<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Runtime Revolution</title>
    <description>The latest articles on Forem by Runtime Revolution (@runtime-revolution).</description>
    <link>https://forem.com/runtime-revolution</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F416%2Fbe110712-687c-4a6d-97ba-fdcc5f264ee2.png</url>
      <title>Forem: Runtime Revolution</title>
      <link>https://forem.com/runtime-revolution</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/runtime-revolution"/>
    <language>en</language>
    <item>
      <title>Connecting React Native to localhost</title>
      <dc:creator>Andre Nunes</dc:creator>
      <pubDate>Tue, 28 Apr 2020 08:44:21 +0000</pubDate>
      <link>https://forem.com/runtime-revolution/connecting-react-native-to-localhost-f88</link>
      <guid>https://forem.com/runtime-revolution/connecting-react-native-to-localhost-f88</guid>
      <description>&lt;p&gt;This is something that happens to me on every React Native project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;How can I make a request to my local API (that runs on localhost)&lt;/em&gt;&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;To be able to do a request to your local machine you have to do two things.&lt;/p&gt;

&lt;p&gt;First, you must use your &lt;strong&gt;machine IP&lt;/strong&gt; as the base URL for the request.&lt;/p&gt;

&lt;p&gt;To discover your IP, just type this command on your console and look for the &lt;strong&gt;inet&lt;/strong&gt; that looks like &lt;strong&gt;192.168.1.200&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ifconfig | grep inet

---

**inet** 192.168.1.200 netmask ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that you will need to update your Android and iOS configurations.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Android&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;One of the problems may be related to &lt;strong&gt;CLEARTEXT HTTP,&lt;/strong&gt; which protects apps from accidental usage of cleartext traffic.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Starting with Android 9 (API level 28), cleartext support is disabled by default.&lt;/p&gt;

&lt;p&gt;* from &lt;a href="https://android-developers.googleblog.com/2018/04/protecting-users-with-tls-by-default-in.html"&gt;Android Developers&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which means that Android can only make a request to &lt;strong&gt;HTTPS&lt;/strong&gt; sources. So we need to allow our application to use &lt;strong&gt;HTTP&lt;/strong&gt;  sources.&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;AndroidManifest.xml&lt;/strong&gt; file, we need to add these lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;manifest&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;uses-permission&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.permission.INTERNET"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;application&lt;/span&gt;
    &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;".MainApplication"&lt;/span&gt;
    &lt;span class="na"&gt;android:usesCleartextTraffic=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;  
    &lt;span class="na"&gt;android:networkSecurityConfig=&lt;/span&gt;&lt;span class="s"&gt;"@xml/network\_security\_config"&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;activity&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;".MainActivity"&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      ...
    &lt;span class="nt"&gt;&amp;lt;/activity&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/application&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/manifest&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will allow our application to connect to &lt;strong&gt;EVERY HTTP&lt;/strong&gt; source. But we want to allow only your machine’s IP. So to do that we will need to create the &lt;strong&gt;network_security_config.xml&lt;/strong&gt; file in the folder &lt;strong&gt;main/res/xml&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;network-security-config&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;domain-config&lt;/span&gt; &lt;span class="na"&gt;cleartextTrafficPermitted=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;domain&lt;/span&gt; &lt;span class="na"&gt;includeSubdomains=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; localhost &lt;span class="nt"&gt;&amp;lt;/domain&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;domain&lt;/span&gt; &lt;span class="na"&gt;includeSubdomains=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 10.0.2.2 &lt;span class="nt"&gt;&amp;lt;/domain&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;domain&lt;/span&gt; &lt;span class="na"&gt;includeSubdomains=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 192.168.1.200 &lt;span class="nt"&gt;&amp;lt;/domain&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;base-config&lt;/span&gt; &lt;span class="na"&gt;cleartextTrafficPermitted=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/domain-config&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/network-security-config&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file restricts the &lt;strong&gt;HTTP&lt;/strong&gt; domains that our app can communicate with. The IP &lt;strong&gt;10.0.2.2&lt;/strong&gt; is an alias for the emulator to &lt;strong&gt;127.0.0.1,&lt;/strong&gt; and &lt;strong&gt;192.168.1.200&lt;/strong&gt; is our machine’s IP.&lt;/p&gt;

&lt;p&gt;After that we need to set the domain url on the Android emulator. So open &lt;strong&gt;Dev Settings by&lt;/strong&gt; clicking &lt;strong&gt;CMD + M&lt;/strong&gt; and then click on &lt;strong&gt;Debug server host &amp;amp; port for device.&lt;/strong&gt; On this modal we add our machine’s IP &lt;strong&gt;192.168.1.200.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2_LHMXNH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/718/1%2A_bb-PXxubZrZTV2fsILL9Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2_LHMXNH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/718/1%2A_bb-PXxubZrZTV2fsILL9Q.png" alt="" width="718" height="1072"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;iOS&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;On the iOS side we will have the same issue, we need to allow our app to do requests to *&lt;em&gt;HTTP *&lt;/em&gt; sources.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By default, iOS will block any request that’s not encrypted using &lt;a href="https://hosting.review/web-hosting-glossary/#12"&gt;SSL&lt;/a&gt;. If you need to fetch from a cleartext URL (one that begins with http) you will first need to &lt;a href="https://reactnative.dev/docs/integration-with-existing-apps#test-your-integration"&gt;add an App Transport Security exception&lt;/a&gt;. If you know ahead of time what domains you will need access to, it is more secure to add exceptions only for those domains; if the domains are not known until runtime you can &lt;a href="https://reactnative.dev/docs/integration-with-existing-apps#app-transport-security"&gt;disable ATS completely&lt;/a&gt;. Note however that from January 2017, &lt;a href="https://forums.developer.apple.com/thread/48979"&gt;Apple's App Store review will require reasonable justification for disabling ATS&lt;/a&gt;. See &lt;a href="https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW33"&gt;Apple's documentation&lt;/a&gt; for more information.  &lt;/p&gt;

&lt;p&gt;* from &lt;a href="https://reactnative.dev/docs/network"&gt;React Native&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To do that we will need to update our &lt;strong&gt;Info.plist&lt;/strong&gt; file and add the &lt;strong&gt;NSAppTransportSecurity&lt;/strong&gt; key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;NSAppTransportSecurity&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;NSExceptionDomains&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;localhost&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;NSTemporaryExceptionAllowsInsecureHTTPLoads&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On iOS we don’t need to define our machine’s IP because it translates &lt;strong&gt;localhost&lt;/strong&gt; to that.&lt;/p&gt;

&lt;p&gt;After that all we need to do is re-install our app on the emulator and it’s done, with this you can now make requests to your local API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This should only be used on local development environment, don’t commit this to your production app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended links&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.android.com/training/articles/security-config"&gt;https://developer.android.com/training/articles/security-config&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW33"&gt;https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW33&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://reactnative.dev/docs/network"&gt;https://reactnative.dev/docs/network&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;I’m a software developer from Portugal, currently working at&lt;/em&gt; &lt;a href="https://www.runtime-revolution.com/"&gt;&lt;em&gt;Runtime Revolution&lt;/em&gt;&lt;/a&gt; &lt;em&gt;as a front-end and mobile developer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.runtime-revolution.com/"&gt;Runtime Revolution&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/anunes9"&gt;anunes9 - Overview&lt;/a&gt;&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>todayilearned</category>
      <category>testing</category>
      <category>api</category>
    </item>
    <item>
      <title>Golang DNS Resolving</title>
      <dc:creator>Luís Costa</dc:creator>
      <pubDate>Thu, 23 Apr 2020 10:04:33 +0000</pubDate>
      <link>https://forem.com/runtime-revolution/golang-dns-resolving-1ao3</link>
      <guid>https://forem.com/runtime-revolution/golang-dns-resolving-1ao3</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A9NA3QJOTTXQeCQRp1IAFqQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A9NA3QJOTTXQeCQRp1IAFqQ.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lately, I have been writing Golang (more commonly known as just Go, which is what I’ll be using from here on out) at work, and so far I have been enjoying it a lot! This is a small TIL (&lt;em&gt;Today I Learned&lt;/em&gt;) about making Go correctly communicate with services in the local machine like, for instance, Redis, Postgres, or Kafka.&lt;/p&gt;

&lt;p&gt;In my particular situation, the project I am working on is running on my machine, however, some of the needed services (Redis, Kafka) are running inside a Docker container. For instance, Redis is accessible via the hostname redis, and likewise for Kafka. Initially, I thought that simply adding the following entries to my etc/hosts files would be enough for the name resolution to work properly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1 redis kafka
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, the code was still throwing errors because it could not connect to redis or Kafka. Turns out that Go has &lt;a href="https://golang.org/pkg/net/#hdr-Name_Resolution" rel="noopener noreferrer"&gt;two ways to perform address name resolution, depending on the operating system it is running on&lt;/a&gt;. On OS X (which is my case), Golang uses a different name resolution library called cgo, which is a bit different than the default name resolver. You can see which name resolver your Go program is using by setting the flag GODEBUG with the value netdns=2. Here’s a sample output for my program:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GODEBUG=netdns=1 go run main.go
&amp;gt; go package net: hostLookupOrder(redis) = cgo
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;On OSX, applications are not allowed to make direct DNS requests&lt;/strong&gt;. So, let’s try running the program with the pure Go name resolver:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GODEBUG=netdns=go+2 go run main.go
&amp;gt; go package net: GODEBUG setting forcing use of Go’s resolver go 
&amp;gt; package net: hostLookupOrder(redis) = files,dns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(The + sign is used to tell Go to force a specific name resolver and to show debugging information).&lt;/p&gt;

&lt;p&gt;And it works! Since the pure Go name resolver is used, Go accesses local files in the machine to perform name resolutions such as &lt;a href="https://github.com/golang/go/blob/d32ec38f1cfe69ed2a553272f6c8f425c59577f2/src/net/conf.go#L100" rel="noopener noreferrer"&gt;/etc/resolv.conf&lt;/a&gt; and &lt;a href="https://github.com/golang/go/blob/7bb33179cadf072403b2f1d8f8210c5ae414d135/src/net/dnsclient_unix.go#L526" rel="noopener noreferrer"&gt;/etc/hosts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Nowadays I work at&lt;/em&gt; &lt;a href="http://www.runtime-revolution.com/" rel="noopener noreferrer"&gt;&lt;em&gt;Runtime Revolution&lt;/em&gt;&lt;/a&gt; &lt;em&gt;as a full-stack software developer. Working here has been, and continues to be, a great learning experience. I’ve matured professionally as a developer, as well as a human being.&lt;/em&gt;&lt;/p&gt;




</description>
      <category>dns</category>
      <category>go</category>
      <category>tips</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Remotees At Work</title>
      <dc:creator>B Jacquet</dc:creator>
      <pubDate>Tue, 14 Apr 2020 07:31:00 +0000</pubDate>
      <link>https://forem.com/runtime-revolution/remotees-at-work-2mdb</link>
      <guid>https://forem.com/runtime-revolution/remotees-at-work-2mdb</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Re·mo·tees&lt;/strong&gt; (rĭ-mōt-ēs): A person hired to work for another from outside the office, usually from home.❊&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For the past month &lt;a href="https://www.runtime-revolution.com/"&gt;Runtime Revolution&lt;/a&gt; has been working full remote, for reasons the whole world is aware of. Going remote is such a game changer at Runtime, and to me personally, that I immediately wanted to blog about it. This time ‘round I twisted things a bit and asked some of our readers (fellow co-workers) to share their experiences on (forced) remote work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QIZP35Rk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AP0_WdasLJ7BthRuCJPB8hA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QIZP35Rk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AP0_WdasLJ7BthRuCJPB8hA.jpeg" alt=""&gt;&lt;/a&gt;All you need for remote work. Photo by &lt;a href="https://medium.com/u/256a7e3ac5bc"&gt;Ana Gonçalves&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this post I’m joined by &lt;a href="https://dev.to@sbg_ana"&gt;Ana Gonçalves&lt;/a&gt;, &lt;a href="https://medium.com/u/7c54fb105c1e"&gt;João Ribeiro&lt;/a&gt;, and &lt;a href="https://medium.com/u/499c0dd781c5"&gt;Maria Luís Duro&lt;/a&gt;. &lt;strong&gt;Ana&lt;/strong&gt; is a Team Leader and has been with Runtime Revolution for four years. During that time she has contributed to ten different projects! &lt;strong&gt;João&lt;/strong&gt; is another Team Leader and he has been with Runtime for two years. &lt;strong&gt;Maria&lt;/strong&gt; is our IT Recruiter. She’s hiring! 😉&lt;/p&gt;

&lt;h4&gt;
  
  
  Are you working alone at home? Are you enjoying it?
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Ana:&lt;/strong&gt; &lt;em&gt;“Me and my partner are both working from home right now, we work in separate rooms because we have meetings quite often. We need separate spaces so we don’t speak over each other. Sometimes we notify each other if there’s going to be a meeting so that the other doesn’t make too much noise. Other than that we basically work as if we were alone and try not to interact much during the day, apart from lunch and snack times. It can be nice to work without the usual hustle of the office and with my cat’s occasional company, but human interaction is also missed. Overall, working from home has its pros and cons. For now I’m enjoying it but I’m afraid I might get over it soon.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;João:&lt;/strong&gt; “&lt;em&gt;I’m not alone, since my girlfriend is also remote during this time.&lt;br&gt;&lt;br&gt;
We made some ground rules before starting this period and one of them is to work in separate rooms in the house, since we have this possibility.&lt;br&gt;&lt;br&gt;
These two “offices” were also set up in rooms that we don’t use for our off-work time, which means we can disconnect and still be in the living-room / kitchen / bedroom without the work office feeling. I can say that this new experience is definitely different from the normal routine, it’s better in some aspects but at the same time, worse in others. I think I’ve been able to focus more easily than in a busy day at the office, since I don’t get interrupted as often.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maria:&lt;/strong&gt; &lt;em&gt;“I’m currently “in quarantine” alongside my parents. The three of us are working but in separate spaces since we all have jobs that require calls or video chats. For now it’s all going well and we try to respect each other’s schedules. We eat lunch together to maintain interaction, have coffee breaks, and try to keep a fairly normal life. Since this is a particular situation in which we all need to stay at home I don’t feel my personal life is any more involved in my day-to-day than it was before. Because we’re not used to spending all hours of the day with each other sometimes we get a bit annoyed but it’s a feeling that goes away fairly quickly.”&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Now that you don’t commute, what do you use that time for?
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cn6_FTeK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2ALWOU_B7O42LYbTUxhS7HzA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cn6_FTeK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2ALWOU_B7O42LYbTUxhS7HzA.jpeg" alt=""&gt;&lt;/a&gt;João’s less is more setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ana:&lt;/strong&gt; &lt;em&gt;“I usually spend about 2 hours a day to commute and I actually used to read during that time but now I don’t read as much. Now I try to be mindful with my time so I don’t fall into the trap of working more than usual. I use the extra time to bake (I like making my own sourdough bread and the extra time is very much appreciated), tidying up, or use the extra hour in the evening to do some yoga.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;João:&lt;/strong&gt; &lt;em&gt;“In the morning I get up at the same time as I did before, I just get ready a lot more slowly and the rush part of my day is not here anymore. I use that time to catch up on the news (“real” television news), calmly eat my breakfast, and sometimes still read a bit as I used to do on the train.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maria:&lt;/strong&gt; &lt;em&gt;“Usually in the morning I go for a run outside and come home to continue some workout exercises I would otherwise do at the gym. I save the end of my day to play video games (I am revisiting some oldies like Pokemon and Kirby!)”&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  How did you, and your team, adapt to the long distance?
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Ana:&lt;/strong&gt; &lt;em&gt;“We as a team work from an office but we work remotely with the product team. We have standups twice a week with them, so that interaction hasn’t really changed that much. We added a daily standup internally so we see and talk to each other regularly and PRs [Pull Requests — Ed.] are usually published in a Slack channel, as we did before. We also created a Zoom link that we called “Virtual Kitchen” so anyone from the team can join after lunch and during breaks to talk to each other about whatever they want, as we would do if we were in the office.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;João:&lt;/strong&gt; “&lt;em&gt;In my project we’re divided into different teams, inside the project, so in my team we started doing two daily check-ins, one in the morning and one near the end of the day. I feel this keeps the team connected even though there might not be that many updates in each one, but we get to see each other and also talk a little bit. Apart from that nothing much changed, if you need help with something you talk to who you think might help you and schedule a call, or simply write a message in your channel and get some responses to it. For all the people in the project we also started doing some “Watercooler Calls”, twice a day you can get together with others and just talk about “nothing”, which is great to keep the mental sanity!”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maria:&lt;/strong&gt; &lt;em&gt;“We adapted pretty well. We talk everyday and maintain our weekly meetings. Personally I feel the response time isn’t so immediate but at the same time it doesn’t have a particularly adverse effect in our work. I believe that now more so than before, all my questions are answered. Because of this change, working from home has made us more aware that we need to maintain all channels of communication open and do business as usual.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rIWEf7A4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Aw8opCUlojnbPdMzV3bgS8g.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rIWEf7A4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Aw8opCUlojnbPdMzV3bgS8g.jpeg" alt=""&gt;&lt;/a&gt;Maria’s minimalist office, in the dinning room.&lt;/p&gt;

&lt;h4&gt;
  
  
  Name one thing that is absolutely indispensable to have at your desk for remote work
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Ana:&lt;/strong&gt; &lt;em&gt;“Tea and headphones are very much indispensable to me, having wireless headphones is even cooler because I can walk around the house and still hear whatever is being said in the “Virtual Kitchen”.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;João:&lt;/strong&gt; “&lt;em&gt;Water, ANC Headphones and hand cream to keep my hands from drying!”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maria: “&lt;/strong&gt; &lt;em&gt;My desk is pretty basic, since I’m working in the living room (where we eat haha). I only have a notebook, a pen, and a bottle of water to keep me hydrated :)”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---rCrpq8L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A80VPYZ9abBRsdhTbuHX14g.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---rCrpq8L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A80VPYZ9abBRsdhTbuHX14g.jpeg" alt=""&gt;&lt;/a&gt;Ana’s Virtual Kitchen.&lt;/p&gt;

&lt;p&gt;I echo the sentiments expressed above by my co-workers. I’m also at home with someone who’s doing remote work and we also work in separate rooms. I wonder why do we seek a quiet place to work, when at the office we would be working in an open space with multiple distractions? Why do we seek the shift from busy open space to quiet alone room?&lt;br&gt;&lt;br&gt;
As for the added “bonus” time, I use it differently. Oddly enough I find my concentration levels varying throughout the day and I tend to compensate by spending my commute time on working hours. But that is not the norm and so far I was able to catch up seasons one and two of Westworld’s TV Series.&lt;br&gt;&lt;br&gt;
Runtime Revolution’s culture and values are spread throughout its teams and their projects. But that doesn’t restrict them in adopting different routines for remote work. During this time I’ve worked with teams relying mostly on group text messaging and occasional voice calls (through Slack), and other teams opting for an always on group voice call with occasional text (through Discord). Though apparently similar solutions, I find voice to be less disruptive than text. As soon as I know the discussion doesn’t concern me, I find it easier to ignore human voice. That doesn’t happen with notification beeps and flashing icons luring me to text waiting to be read.&lt;/p&gt;

&lt;p&gt;I want to thank &lt;a href="https://dev.to@sbg_ana"&gt;Ana Gonçalves&lt;/a&gt;, &lt;a href="https://medium.com/u/7c54fb105c1e"&gt;João Ribeiro&lt;/a&gt;, and &lt;a href="https://medium.com/u/499c0dd781c5"&gt;Maria Luís Duro&lt;/a&gt; for taking part on this post and sharing their insights. The Q&amp;amp;A interviews were conducted safely on Slack and no COVID-19 virus was spread through them.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;❊&lt;/em&gt;&lt;/strong&gt; Remotees &lt;em&gt;is word I made up. The title is a pun on Peter Seibel’s wonderful book&lt;/em&gt; &lt;a href="http://www.codersatwork.com/"&gt;Coders At Work&lt;/a&gt;&lt;em&gt;. Read it!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;I work at&lt;/em&gt; &lt;a href="http://www.runtime-revolution.com/"&gt;&lt;em&gt;Runtime Revolution&lt;/em&gt;&lt;/a&gt; &lt;em&gt;as Full Stack Rails developer. We help startups kickoff their business and reach the next level.&lt;/em&gt;&lt;/p&gt;




</description>
      <category>remote</category>
      <category>productivity</category>
      <category>interview</category>
      <category>tips</category>
    </item>
    <item>
      <title>How to start with React Native</title>
      <dc:creator>Andre Nunes</dc:creator>
      <pubDate>Tue, 17 Mar 2020 11:15:04 +0000</pubDate>
      <link>https://forem.com/runtime-revolution/how-to-start-with-react-native-1cbe</link>
      <guid>https://forem.com/runtime-revolution/how-to-start-with-react-native-1cbe</guid>
      <description>&lt;p&gt;In this article, I’ll tell you what you need to bootstrap a React Native app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---inbmZUw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2A4hJESi7DgtPFHRfu" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---inbmZUw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2A4hJESi7DgtPFHRfu" alt=""&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/@monicasauro?utm_source=medium&amp;amp;utm_medium=referral"&gt;Monica Sauro&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;So what is React Native?&lt;/strong&gt;
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;React Native combines the best parts of native development with React, a best-in-class JavaScript library for building user interfaces. (…)&lt;/p&gt;

&lt;p&gt;* from &lt;a href="https://reactnative.dev/"&gt;React Native&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;React Native allows you to create Android and iOS applications with the same code base, that behave nearly the same using all the resources of React with a little tweak.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up Your Development Environment
&lt;/h3&gt;

&lt;p&gt;The installation process that is described on the React Native site is well documented and easy to follow, but here is the short version. I use a MacBook Pro as my development machine so the commands will be for macOS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installation process&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install &lt;a href="https://developer.apple.com/xcode/"&gt;Xcode&lt;/a&gt; and &lt;a href="https://developer.android.com/studio"&gt;Android Studio&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Install &lt;a href="https://nodejs.org/en/"&gt;node&lt;/a&gt; and &lt;a href="https://facebook.github.io/watchman/docs/nodejs.html"&gt;watchman&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Install Java SDK&lt;/li&gt;
&lt;/ol&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Install Xcode command line tools&lt;/li&gt;
&lt;/ol&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Open Xcode, then choose the &lt;strong&gt;Preferences…&lt;/strong&gt; menu.&lt;br&gt;&lt;br&gt;
Go to &lt;strong&gt;Locations&lt;/strong&gt; panel and install the tools by selecting the most recent version in the &lt;strong&gt;Command Line Tools&lt;/strong&gt; dropdown.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install CocoaPods&lt;/li&gt;
&lt;/ol&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Install &lt;a href="https://github.com/jhen0409/react-native-debugger"&gt;react-native-debugger&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And you are ready to go.&lt;/p&gt;

&lt;p&gt;Now what you need to do is bootstrap your application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;React Native has a built-in command line interface, which you can use to generate a new project. You can access it without installing anything globally using npx, which ships with Node.js. (…)&lt;/p&gt;

&lt;p&gt;* from &lt;a href="https://reactnative.dev/"&gt;React Native&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;After you create your app you can choose to run the app on iOS or Android.&lt;/p&gt;

&lt;p&gt;To open the app on iOS just run this command, it will open the iOS simulator and install the app.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;To open the app on Android you will need to create a virtual device on Android Studio, open the virtual device and run the following command.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And there you go, the app is running!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V23bFAZR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/249/1%2AWANqxyiI6zLXB981kE_g0A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V23bFAZR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/249/1%2AWANqxyiI6zLXB981kE_g0A.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Debugging
&lt;/h3&gt;

&lt;p&gt;To use &lt;strong&gt;react-native-debugger&lt;/strong&gt; you need to enable remote JS debugging on the simulator option:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On iOS press &lt;strong&gt;&lt;em&gt;CMD + D&lt;/em&gt;&lt;/strong&gt; and click on &lt;strong&gt;Debug JS Remotely&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;On Android press &lt;strong&gt;&lt;em&gt;CMD + P&lt;/em&gt;&lt;/strong&gt;  .&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A nice feature of React Native is Hot Reloading, which allows you to see the code changes in real time without having to restart the app.&lt;/p&gt;

&lt;p&gt;To enable this option, open the simulator options and click on &lt;strong&gt;Enable Hot Reloading&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EpdPDlpJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/760/1%2ArA2F8oNEpGILBvujuuhTtg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EpdPDlpJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/760/1%2ArA2F8oNEpGILBvujuuhTtg.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point you have your React Native environment set and ready to start coding some apps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recommended links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://reactnative.dev/"&gt;React Native&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/react-native-community"&gt;React Native Community&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jondot/awesome-react-native"&gt;Awesome React Native&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/microsoft/vscode-react-native"&gt;VSCode React Native&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/anunes9"&gt;anunes9 - Overview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you have any questions or comments, don’t hesitate to reach out. I’d be happy to help you. Thanks for reading!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I’m a software developer from Portugal, currently working at&lt;/em&gt; &lt;a href="https://www.runtime-revolution.com/"&gt;&lt;em&gt;Runtime Revolution&lt;/em&gt;&lt;/a&gt; &lt;em&gt;as a front-end and mobile developer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.runtime-revolution.com/"&gt;Runtime Revolution&lt;/a&gt;&lt;/p&gt;




</description>
      <category>softwaredevelopment</category>
      <category>reactnative</category>
      <category>ios</category>
      <category>android</category>
    </item>
    <item>
      <title>Starting with AWS DynamoDB using Python</title>
      <dc:creator>João Gomes</dc:creator>
      <pubDate>Tue, 10 Mar 2020 10:08:07 +0000</pubDate>
      <link>https://forem.com/runtime-revolution/starting-with-aws-dynamodb-using-python-1blm</link>
      <guid>https://forem.com/runtime-revolution/starting-with-aws-dynamodb-using-python-1blm</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IJY8LWLB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/680/1%2AqKw_ICEQ8Aw202wAEroxhw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IJY8LWLB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/680/1%2AqKw_ICEQ8Aw202wAEroxhw.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Amazon &lt;strong&gt;DynamoDB&lt;/strong&gt; is a &lt;em&gt;key-value and document database that delivers single-digit millisecond performance at any scale.&lt;/em&gt; &lt;a href="https://aws.amazon.com/dynamodb/"&gt;https://aws.amazon.com/dynamodb/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This guide is meant to provide a base, or a quick start, for interfacing with AWS DynamoDB using Python, its contents are based on the repo &lt;a href="https://github.com/5thempire/aws-dynamodb"&gt;https://github.com/5thempire/aws-dynamodb&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The chosen examples are meant to illustrate the breadth of options available, even though they’re not the typical use case.&lt;/p&gt;

&lt;p&gt;We are going to create two tables: one for storing URLs and their corresponding domain, and another for storing logs, which have a timestamp, a log status and a message. The URLs data model will be using a key-value configuration, while the logs data model will use a document configuration.&lt;/p&gt;

&lt;p&gt;First things first, let’s define the data models.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key-Value — URL data model
&lt;/h3&gt;

&lt;p&gt;The key-value configuration is a very simple implementation, where the only requirement is to specify the key or index. In DynamoDB this is set under the attribute definition and the key schema.&lt;/p&gt;

&lt;p&gt;The attribute definition stores the name of the keys and their corresponding type.&lt;/p&gt;

&lt;p&gt;The key schema defines the type of primary keys we’re using, in DynamoDB they can be one of two types, hash or range. We could have one of each, but for the current example we’ll only be using hash, leaving out range for you to play with.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Document — Log data model
&lt;/h3&gt;

&lt;p&gt;The document configuration is a bit more complex. To query the log documents we’ll use two indexes, one will be the timestamp and the other the status of the log, the latter of which is set as a global secondary index.&lt;/p&gt;

&lt;p&gt;The global secondary index also includes a projection attribute. This configuration is very important, since it defines what will be retrieved when we query using the secondary index. In our definition we’re retrieving the complete document.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Python DynamoDB base class
&lt;/h3&gt;

&lt;p&gt;This approach has one base class to interact with DynamoDB, which is not meant to be used on its own, but to provide a solid base for the table specific definitions. These specificities are set by overriding the abstract methods for get, put, update and remove.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Python URL class
&lt;/h3&gt;

&lt;p&gt;This class overrides the base class by defining what’s specific to the domain data model interaction.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Python Log class
&lt;/h3&gt;

&lt;p&gt;This class overrides the base class by defining what’s specific to the log data model interaction, but also adds filtering methods. There are two filters defined:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;filter_by_timestamp_status&lt;/strong&gt; : which uses both timestamp and status keys to filter results&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;filter_by_status&lt;/strong&gt; : which filters by a specific status (i.e.: INFO)&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;Don’t forget there are a few options out there that provide the typical level of abstraction a database model normally requires. However, sometimes a more hands on approach is preferred.&lt;/p&gt;

&lt;p&gt;For those who want to experience a running example visit &lt;a href="https://github.com/5thempire/aws-dynamodb"&gt;https://github.com/5thempire/aws-dynamodb&lt;/a&gt;. There you’ll find the samples mentioned here along with the corresponding set of tests, that can be used as a basis for further experiments.&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html"&gt;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://amazon-dynamodb-labs.com/"&gt;https://amazon-dynamodb-labs.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;At&lt;/em&gt; &lt;a href="http://www.runtime-revolution.com/"&gt;&lt;em&gt;Runtime Revolution&lt;/em&gt;&lt;/a&gt; &lt;em&gt;we take our craft seriously and always go the extra mile to deliver a reliable, maintainable, and testable product. Do you have a project to move forward or a product you’d like to launch? We would love to help you!&lt;/em&gt;&lt;/p&gt;




</description>
      <category>python</category>
      <category>softwareengineering</category>
      <category>aws</category>
      <category>dynamodb</category>
    </item>
    <item>
      <title>Tech Recruiters are crybabies! 😭😭😭</title>
      <dc:creator>Fausto Nunes</dc:creator>
      <pubDate>Wed, 26 Feb 2020 15:59:22 +0000</pubDate>
      <link>https://forem.com/runtime-revolution/tech-recruiters-are-crybabies-2335</link>
      <guid>https://forem.com/runtime-revolution/tech-recruiters-are-crybabies-2335</guid>
      <description>&lt;p&gt;Yap. I said it. Let the flame war begin! 🔥&lt;/p&gt;

&lt;p&gt;If you’re on LinkedIn and you’re, in some way or another, in the Tech industry, I’m sure you have a few (or a lot!) of recruiters on your network.&lt;/p&gt;

&lt;p&gt;Recruiters are a strange group of people that search for Developers and other professionals to fill up a vacancy (or more) in a specific company that needs a person with that set of skills. Some recruiters forget they’re dealing with actual people. You know, fellow human beings with families, friends, desires, fears, expectations and demands. Sometimes it’s hard not to think of recruiters as cartoon villains, reducing everyone to a number in a body shop, especially if they’re working for shady outsourcing companies with an evil mastermind as CEO.&lt;/p&gt;

&lt;p&gt;You’ve probably seen recruiters whining on Linked In about how a candidate did this and that or stopped replying at some stage of the recruitment process. There are rude people everywhere, but seriously, can you blame them? I dare to say, most of the time they had good reasons to be rude or they simply reflected the way they were treated in the first place.&lt;/p&gt;

&lt;p&gt;Here’s a list of things most recruiters do that make no sense and reinforce the stereotype:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sending the same old template to everyone, no matter the seniority, project, tech stack, etc.;&lt;/li&gt;
&lt;li&gt;Not knowing or not giving details about the project or company they’re hiring for;&lt;/li&gt;
&lt;li&gt;Assuming everyone in Tech must love Star Wars — Darth Vader/Stormtrooper image “Apply now!”, “Join the dark side”, Yoda image “Join the force!”. &lt;em&gt;You know what I’m talking about… ughhh&lt;/em&gt; 😒;&lt;/li&gt;
&lt;li&gt;Having 5 recruiters from the same company hitting you up with the same message in the same week;&lt;/li&gt;
&lt;li&gt;Ghosting after you had an interview with them;&lt;/li&gt;
&lt;li&gt;Not providing feedback;&lt;/li&gt;
&lt;li&gt;Saying they have THE perfect opportunity for you, when they don’t even know you;&lt;/li&gt;
&lt;li&gt;Calling you during your working hours, knowing it might get you into trouble;&lt;/li&gt;
&lt;li&gt;Saying someone referred you, when you know it’s bullshit;&lt;/li&gt;
&lt;li&gt;Creating fake job ads just to put you on their database for future opportunities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just to be perfectly clear, I am a recruiter and I’ve had some of these things happen to me when I was job hunting and it sucked!&lt;/p&gt;

&lt;p&gt;I try to use what I learned from that process to be better at this job. Do you remember that old saying: “treat others the way you want to be treated”? The same applies here.&lt;/p&gt;

&lt;p&gt;Some companies do put recruiters through a lot of pressure, unreasonable deadlines and objectives, outdated tools and give them no space to think about what they’re doing and no room for improvement (Numbers, numbers, numbers!). Believe me, I’ve been through it and whining about candidates on LinkedIn isn’t going to make things better.&lt;/p&gt;

&lt;p&gt;If that’s your case, I suggest you find a better company to work for, because if they don’t treat you right, it’s way harder to do the right thing. Besides, you’ll be on the other end of the recruitment process again. It’s a great time to learn how candidates think and feel when you are the candidate.&lt;/p&gt;

&lt;p&gt;We’re not reinventing the wheel here:&lt;/p&gt;

&lt;p&gt;It all comes down to respect and treating people fairly.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I follow a strict no bullshit approach to HR and I’m glad Runtime-Revolution supports it. If you are interested in who we are and what we do, make sure to reach out! I’ll make sure you get a clear picture of our way of doing things.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>techrecruitment</category>
      <category>recruitment</category>
      <category>employerbranding</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>4 quick tips to improve your communication skills</title>
      <dc:creator>Nuno Tomás</dc:creator>
      <pubDate>Tue, 10 Dec 2019 15:31:19 +0000</pubDate>
      <link>https://forem.com/runtime-revolution/quick-tips-to-improve-your-communication-skills-nn6</link>
      <guid>https://forem.com/runtime-revolution/quick-tips-to-improve-your-communication-skills-nn6</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GLvKGCgW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Am2PfxmWSRngFUwDFfhDM_g.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GLvKGCgW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Am2PfxmWSRngFUwDFfhDM_g.jpeg" alt=""&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/@galen_crout?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Galen Crout&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/communication?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the essential skills that you’ll need is how to communicate all the things related to your work. I am constantly baffled by the lack of education/preparation that college provides for one of the most critical assets you can have, regardless of your chosen career path. Be it Sales, Software Development, Customer Support, you name it, strong communication skills help amplify your strengths. In this article, I’ll be mostly approaching things based on a Software Development area perspective, since it’s what I’m familiar with, but the same lessons can be applied to any other area.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start by listening
&lt;/h3&gt;

&lt;p&gt;You cannot answer if you don’t know the question. Or rather, you &lt;em&gt;can&lt;/em&gt;, but the odds of getting it right are minuscule at best. I’ve said it before, and I’ll say it again, when programming something, you have to start by understanding the problem, not the solution. When it comes to communication, you have to do the same thing. Open your eyes and ears and listen. Know how people communicate, learn what the problem is, and understand what they are expecting. With that info, adapt your communication in a way that best suits the people and context involved.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Adapt the message to the listener
&lt;/h3&gt;

&lt;p&gt;People talk to others as if they possess the same information, in terms of context and knowledge, as they do, I’ve seen it happen many times. This inevitably ends with either having to constantly repeat the same thing, or a half-hearted acknowledgment where the listener acquiesces to something they didn’t fully understand. In all likelihood, they will even need to pass the message along, resulting in a process strikingly similar to playing the broken telephone game in a foreign language.&lt;/p&gt;

&lt;p&gt;You’ll need to get a sense of how the listener is involved, how much they know and what their perspective on the matter is. You then explain the context and the impact of the solution with simple and understandable terms. It’s possible that your message still won’t get through the first time, if so, try to arrange new ways/metaphors (possibly something more closely incorporating their knowledge and experience).&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The right amount of words
&lt;/h3&gt;

&lt;p&gt;It is difficult to understand what the right amount of words is. You always want to express your ideas perfectly, and that means you don’t want to talk too much nor too little. When you talk too much, you might throw people off if they’re not able to follow your line of reasoning, or they might plain get tired and nod off. When you talk too little, people might not grasp the full picture and fill the blanks with the wrong info. Yes, the Goldilocks principle is ever-present in communication.&lt;/p&gt;

&lt;p&gt;It’s never easy, but you should strive to be concise and straightforward with your message. One thing that helps me a lot is to write it down, get some fresh air, and then try to see with fresh eyes, as if I were a client seeing it for the first time.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Feedback cycle
&lt;/h3&gt;

&lt;p&gt;Picture the following: A problem comes up at 4 pm when the client notifies you. By 5 pm, everything is back up and operational. The way you communicate can dramatically change how a situation is perceived. Check out the following examples illustrating two distinct approaches to handle this same scenario. Notice that the time taken to deploy the fix is the same in both instances, and it takes exactly 1 hour to have everything working again. If you put yourself in the client’s shoes, how would you react to Example 1 vs. Example 2?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5XHf_b1z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1010/1%2AUbyMP3Mp_VeRSRHgSX6BSQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5XHf_b1z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1010/1%2AUbyMP3Mp_VeRSRHgSX6BSQ.png" alt=""&gt;&lt;/a&gt;Example 1&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CU78_iVC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1012/1%2AP9l06tbf4h56WHB7HX56HQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CU78_iVC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1012/1%2AP9l06tbf4h56WHB7HX56HQ.png" alt=""&gt;&lt;/a&gt;Example 2&lt;/p&gt;

&lt;p&gt;It’s all about how you manage expectations. The client has a big problem in their hands, and the best way to work through it is by helping them follow along with your process. In the second example, I assure you that the person on the other end will be much more at ease. By knowing where we’re at and having a better view of what’s going on, should they need to report it to someone else, the level of the aggravation felt will be greatly diminished.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Communication is hard because one size doesn’t fit all. Even if you know the listener, the context in which you’re delivering your message can influence the way you communicate. There’s no easy process, but there are simple principles to follow. And if you follow them and adapt, you will make your day to day a lot easier.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you have any comments or feedback on this subject, I’d love to hear from you!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>softskills</category>
      <category>communication</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Saving data inside the browser’s storage — a summary</title>
      <dc:creator>João Chitas</dc:creator>
      <pubDate>Mon, 19 Aug 2019 20:11:02 +0000</pubDate>
      <link>https://forem.com/runtime-revolution/saving-data-inside-the-browser-s-storage-a-summary-1b4m</link>
      <guid>https://forem.com/runtime-revolution/saving-data-inside-the-browser-s-storage-a-summary-1b4m</guid>
      <description>&lt;h3&gt;
  
  
  Saving data inside the browser’s storage — a summary
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m94xmI0Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2Akz4cKjgM2rdsLISh" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m94xmI0Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2Akz4cKjgM2rdsLISh" alt=""&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/@samuelzeller?utm_source=medium&amp;amp;utm_medium=referral"&gt;Samuel Zeller&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every time you visit a website you may have noticed that on repeated visits there are some elements that no longer appear or load faster. It could be cache working its magic or maybe there is something inside your browser that triggers these behaviors.&lt;/p&gt;

&lt;p&gt;A very simple example: modals asking for your consent about the use of cookies. After the EU approved the GDPR regulation, many websites started showing a modal warning the user that, to improve their experience navigating the page, they use &lt;em&gt;cookies.&lt;/em&gt; Accepting the use of these needs to be stored somewhere so that when you visit the site for a second time, it would behave in a different way (in this case, the consent modal would no longer appear). This could be stored inside a server database but that would be complex. Instead, the browser has its own storage and &lt;em&gt;cookies&lt;/em&gt; are just one storage type. Your choice is then stored inside your browser in the &lt;em&gt;cookies&lt;/em&gt; store.&lt;/p&gt;

&lt;p&gt;With this post, I’ll try to explain to you, in a very brief way, the different types of browser storage that you can use while providing some code snippets to illustrate their usage. In the end, I’ll list references to posts with more details about this topic!&lt;/p&gt;

&lt;h3&gt;
  
  
  Cookies
&lt;/h3&gt;

&lt;p&gt;Good old &lt;em&gt;cookie.&lt;/em&gt; This is probably the most famous browser storage. Everyone who surfs the internet has heard about it. From this &lt;a href="http://www.whatarecookies.com/"&gt;link&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Each cookie is effectively a small lookup table containing pairs of (key, data) values — for example (firstname, John) (lastname, Smith). Once the cookie has been read by the code on the server or client computer, the data can be retrieved and used to customise the web page appropriately.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is easy to create or get a &lt;em&gt;cookie&lt;/em&gt;. The document element contains an API to manipulate them. Here are some code examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// get all cookies
cookies = document.cookie;

// add a cookie
// must be a string with the format "key=value"
document.cookie = "pokemon=bulbasaur";
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Local Storage
&lt;/h3&gt;

&lt;p&gt;Local Storage is a type of web storage that stores data as a key/value pair structure and without expiration date. Even if the user ends their session, the data inside local storage will not disappear. The value stored can only be a string and if the user wants to store an object, they will need to &lt;em&gt;stringify it&lt;/em&gt; when storing and &lt;em&gt;JSON parse it&lt;/em&gt; when retrieving the value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// get the storage
pokedex = window.localStorage;

// insert into store
pokedex.setItem('1', 'Bulbasaur');
pokedex.setItem('25', 'Pikachu');

// get from store
let pikachu = pokedex.getItem('25');
console.log(pikachu); // _Pikachu_

// remove from store
pokedex.removeItem('1');
let bulbasaur = pokedex.getItem('1');
console.log(bulbasaur); // _null_

// clear the store _aka_ reset pokedex
pokedex.clear();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Session Storage
&lt;/h3&gt;

&lt;p&gt;Session storage is very similar to Local Storage. The main difference is that after the page session ends, the data inside the Session Storage is cleared. Both Local Storage and Session Storage have the same API. Just type pokedex = window.sessionStorage to access it.&lt;/p&gt;

&lt;h3&gt;
  
  
  IndexedDB
&lt;/h3&gt;

&lt;p&gt;IndexedDB is a more complex type of storage, useful for storing larger amounts of data and complex objects. The API is much more complicated to use, compared to the ones provided by Cookies and Local/Session Storages. Because of that, there are libraries that make it easier to use. Like the other types of storage, you can store and retrieve elements using a &lt;strong&gt;key&lt;/strong&gt;. For that, you’ll need to specify the database schema, open a connection and manipulate the data with &lt;strong&gt;transactions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here is a snippet of a very simple flow for using the browser IndexedDB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// init IndexedDB
let request= window.indexedDB.open('POKEDEX', 1);

// to create an new object store, you must define
// a function to onupgradeneeded
request.onupgradeneeded = function(event) {
  let db = request.result;
  let store = db.createObjectStore('POKEMONS', { keyPath: 'pk'});
};

// store on DB
request= window.indexedDB.open('POKEDEX', 1);
request.onsuccess = function(event) {
  let db = request.result;
  let transaction = db.transaction('POKEMONS', 'readwrite');

  let store = transaction.objectStore('POKEMONS');

  store. **put** ({
   pk: 'pikachu',
   value: {
     number: 25,
     name: 'Pikachu',
     moves: {
       "move\_1": "Volt Tackle",
       "move\_2": "Tail Whip"
     }
   }
  });

  transaction.oncomplete = function() {
    db.close();
  }
}

// get object from the store
request= window.indexedDB.open('POKEDEX', 1);
request.onsuccess = function(event) {
  let db = request.result;
  let transaction = db.transaction('POKEMONS', 'read');
  let store = transaction.objectStore('POKEMONS');

  let pokemon = store.get('pikachu');
  pokemon.onsuccess = function(event) {
    console.log(pokemon.result.value);
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Because this is a complex process, even for small examples like the one above, there are libraries, like &lt;a href="https://dexie.org/"&gt;dexie.js&lt;/a&gt;, that help you use IndexedDB.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;As you can see, browsers provide a variety of tools to store data for very specific cases. There is also another one called Web SQL but this one has been deprecated for a quite few years.&lt;/p&gt;

&lt;p&gt;This is just a very small summary of those tools so please, take a look at some references below for more information.&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API"&gt;IndexedDB API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage"&gt;Window.sessionStorage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage#Syntax"&gt;Window.localStorage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.whatarecookies.com/"&gt;What are Cookies - Computer Cookies - What is a Cookie&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nolanlawson.com/tag/indexeddb/"&gt;indexeddb | Read the Tea Leaves&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Pokémon © 2002–2019 Pokémon. © 1995–2019 Nintendo/Creatures Inc./GAME FREAK inc. TM, ® and Pokémon character names are trademarks of Nintendo.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;No copyright or trademark infringement is intended in using Pokémon content on this post.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;I’m currently a Ruby-on-Rails developer at Runtime Revolution that still likes to play the good old Pokemon games for the GameBoy. Go check our&lt;/em&gt; &lt;a href="https://www.runtime-revolution.com/?source=post_page---------------------------"&gt;&lt;em&gt;website&lt;/em&gt;&lt;/a&gt; &lt;em&gt;to know us more.&lt;/em&gt;&lt;/p&gt;




</description>
      <category>web</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>browsers</category>
    </item>
    <item>
      <title>Organize your translations using multiple locales files</title>
      <dc:creator>João Chitas</dc:creator>
      <pubDate>Mon, 06 May 2019 14:56:53 +0000</pubDate>
      <link>https://forem.com/runtime-revolution/organize-your-translations-using-multiple-locales-files-2kg4</link>
      <guid>https://forem.com/runtime-revolution/organize-your-translations-using-multiple-locales-files-2kg4</guid>
      <description>&lt;p&gt;Of course, this contains an example with Pokémon!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sql4z1Hc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AlwT3xnIqSotzB1Yj" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sql4z1Hc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AlwT3xnIqSotzB1Yj" alt=""&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/@kylejglenn?utm_source=medium&amp;amp;utm_medium=referral"&gt;Kyle Glenn&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Internationalization of a website's content is always something to have in mind. Although many countries have a second language, not everyone knows how to speak or write it. In my country, Portugal, only the latest couple of generations understand English, at least in a way that they can use it on a daily basis. Some older generations didn’t even know how to read and write in Portuguese; illiteracy in our country was a major concern.&lt;/p&gt;

&lt;p&gt;Because of these type of things, a website (in my opinion) should have at least 2 languages a user can select from, or enough languages to reach as many users as it can.&lt;/p&gt;

&lt;p&gt;Hard-coded text in an application is somewhat of a bad practice, but in a Rails application we can find inside the configs/locales a file with the name en.yml. This file is a place where we can set strings that may appear in different places, instead of hard coding them. But, they don’t need to be only in English. You can create other translations files for different languages for instance: fr.yml and es.yml (French and Spanish, respectively).&lt;/p&gt;

&lt;p&gt;To change which locale you are using, you can set I18n.locale with the language code, or even set the default language with I18n.default_locale.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I18n.locale = :pt

I18n.deafaul\_locale = :es
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Okay but, now imagine this case: you are a Pokémon fan like me and you would like to make a website with all the information about them (names, types, moves…). Yes, you could save everything in a database, but that’s not the point, I don’t want you to be that smart.&lt;/p&gt;

&lt;p&gt;Start by adding info about the first 151 inside en.yml… It is already a lot of information, so you’ll have a very big file. OK, now create a locales file for Portuguese, pt.yml. Now you have all the info for a Portuguese native to read.&lt;/p&gt;

&lt;p&gt;I said to start with the first 151… Now add the ones from the second generation and you get info about 251 Pokémon. Oh, but in generation two, the generation one Pokémon can learn new moves, so you’ll have to update that info going through the whole file.&lt;/p&gt;

&lt;p&gt;This is a big edge case but I think you already know where I want to go with this… In the future, changing a locale file could be hard to manage if you don’t structure it right from the beginning.&lt;/p&gt;

&lt;p&gt;If only we could organize them a little bitbetter.&lt;/p&gt;

&lt;p&gt;Well, you can! Rails lets you organize your translations in a way that’s much more suitable to your project. You can create multiple locales files for the same language and spread them into different folders, in a structure that makes sense to you or the project scope. This example was taken from the Ruby on Rails &lt;a href="https://guides.rubyonrails.org/i18n.html#organization-of-locale-files"&gt;Guides&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;|-defaults
|---es.rb
|---en.rb
|-models
|---book
|-----es.rb
|-----en.rb
|-views
|---defaults
|-----es.rb
|-----en.rb
|---books
|-----es.rb
|-----en.rb
|---users
|-----es.rb
|-----en.rb
|---navigation
|-----es.rb
|-----en.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;(The file's extension is in &lt;em&gt;ruby&lt;/em&gt; but you can use _yml _inside.)&lt;/p&gt;

&lt;p&gt;To use a structure like this, you need to let Rails know where to look for the files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# config/application.rb

config.i18n.load\_path += Dir[Rails.root.join(_'config'_, _'locales'_, _'\*\*'_, _'\*.{rb,yml}'_)]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Going back to our example, we can now think of a possible structure to organize our Pokémon’s information. Here’s an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;|-pokemons
|--models
|---en.yml
|---pt.yml
|--views
|---en.yml
|---pt.yml
|-moves
|--models
|---en.yml
|---pt.yml
|--...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Or you can do something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;|-models
|--pokemons
|---en.yml
|---pt.yml
|--moves
|---en.yml
|---pt.yml
|--...
|-views
|--homepage
|---en.yml
|---pt.yml
|--...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And then, each file will be something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# config/locales/models/en.yml

en:
 models:
  pokemon:
   name: Name
   type: Type
   moves: Moves
...

# config/locales/models/pt.yml

pt:
 models:
  pokemon:
   name: Nome
   type: Tipo
   moves: Ataques
...

# config/locales/views/homepage/pt.yml

pt:
 views:
  homepage:
   title: Pokémon e Companhia
   main\_section: Vou apanhá-los todos!
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Always scope the translations by the name of the folder, to avoid overrides.&lt;/p&gt;

&lt;p&gt;With a structure like this, it is now easier for you or any developer on your project to add, change or remove a translation for a specific language. This is great if you need to refactor some code and delete some legacy references in the project. Trust me, refactoring a big and unique translations file is not a pleasant task at all.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pokémon © 2002–2018 Pokémon. © 1995–2018 Nintendo/Creatures Inc./GAME FREAK inc. TM, ® and Pokémon character names are trademarks of Nintendo.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No copyright or trademark infringement is intended in using Pokémon content on this post.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;I’m currently a Ruby-on-Rails developer at Runtime Revolution that still likes to play the good old Pokemon games for the GameBoy. Go check our&lt;/em&gt; &lt;a href="https://www.runtime-revolution.com/"&gt;&lt;em&gt;website&lt;/em&gt;&lt;/a&gt; &lt;em&gt;to know us more. Oh, and we’re hiring!&lt;/em&gt;&lt;/p&gt;




</description>
      <category>i18n</category>
      <category>rails</category>
      <category>locale</category>
      <category>pokemon</category>
    </item>
    <item>
      <title>To merge or to rebase: that is the question</title>
      <dc:creator>Diogo Leitão</dc:creator>
      <pubDate>Tue, 16 Apr 2019 15:17:50 +0000</pubDate>
      <link>https://forem.com/runtime-revolution/to-merge-or-to-rebase-that-is-the-question-1jai</link>
      <guid>https://forem.com/runtime-revolution/to-merge-or-to-rebase-that-is-the-question-1jai</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v4TtskRS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AB8N2nz2sDy_-AM2k" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v4TtskRS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AB8N2nz2sDy_-AM2k" alt=""&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/@garciasaldana_?utm_source=medium&amp;amp;utm_medium=referral"&gt;Pablo García Saldaña&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s one of the eternal questions that ignite all dev discussions, on par with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tabs vs Spaces&lt;/li&gt;
&lt;li&gt;Statically typed vs Dynamically typed&lt;/li&gt;
&lt;li&gt;Han Solo vs Greedo&lt;/li&gt;
&lt;li&gt;Jacob vs Edward&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To make things simpler, I’m going to use an analogy. Let’s step away from code and programming for a while.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integrating a paper research team
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;You recently integrated a group of people who are working on a big article or thesis. As you can imagine, you won’t do all the work and neither will the rest of your team. The work will be divided into fairly equal parts, so that each member of the team can focus on their branch of work.&lt;/li&gt;
&lt;li&gt;A given member of your team is the lead researcher. This means that their decisions and comments carry more weight; in case of a draw, they are the ones who have the final say. Therefore, we can say that your lead researcher’s work is the source of your truth.&lt;/li&gt;
&lt;li&gt;You have been assigned a new chapter on the research. You Google some existing papers on the subject (based on the main research document), find some images, plot some graphs, draw some conclusions, etc. A few days later, the lead researcher says some other colleague finished their part of the work and that piece has been merged into the main document (which, as I mentioned earlier, is your source of truth).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_MNqlUho--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2Aj1P7vX5MnDq89jQa" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_MNqlUho--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2Aj1P7vX5MnDq89jQa" alt=""&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/@jontyson?utm_source=medium&amp;amp;utm_medium=referral"&gt;Jon Tyson&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How should you continue your work from now on? How will you incorporate the newest additions into the research you’ve done so far? What if it clashes with what you currently have? Since the main document is your source of truth, you know it was approved by your peers and by the lead researcher, so you have to do you work starting from there!&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding new information / Getting updates
&lt;/h3&gt;

&lt;p&gt;How you think it will happen:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_oLgUiXO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/320/1%2AYD5uNhM9JRtwXNnFiy0oQw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_oLgUiXO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/320/1%2AYD5uNhM9JRtwXNnFiy0oQw.gif" alt=""&gt;&lt;/a&gt;Zipper gif @ &lt;a href="https://giphy.com/gifs/cgi-FS2GiqcNGT292"&gt;&lt;/a&gt;&lt;a href="https://giphy.com/gifs/cgi-FS2GiqcNGT292"&gt;https://giphy.com/gifs/cgi-FS2GiqcNGT292&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How it will most likely happen (with fewer deaths and injuries, hopefully):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Kmlh8ZD3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/480/1%2AkHykMUvIrwn2w8pXGdYPfg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Kmlh8ZD3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/480/1%2AkHykMUvIrwn2w8pXGdYPfg.gif" alt=""&gt;&lt;/a&gt;Git Merge GIF @ &lt;a href="https://tenor.com/view/git-merge-gif-5920259"&gt;&lt;/a&gt;&lt;a href="https://tenor.com/view/git-merge-gif-5920259"&gt;https://tenor.com/view/git-merge-gif-5920259&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before the &lt;em&gt;YOU’RE A REBASE PURIST&lt;/em&gt; accusations start pouring in, let me say that git merge has its advantages (of course it does!), the same way that git rebasehas its disadvantages. People who’ve been through it know that solving merge conflicts is easier and a lot less cumbersome when you use git merge.&lt;/p&gt;

&lt;p&gt;However, what I want to focus is not which one is better, but rather which should be used in which situations.&lt;/p&gt;

&lt;p&gt;Going back to the research paper analogy, once you get the updated version of the main document, &lt;em&gt;rebasing&lt;/em&gt; your work on it will produce a more coherent work. If you were to merge everything together, you’d probably end up with clashing definitions and mixed up pages (if you started your work on page 20, and the latest document has 23 pages, your work does not belong on page 20 — the same way your commits will be spliced in-between the main branch’s commits).&lt;/p&gt;

&lt;p&gt;Furthermore, when checking the log, we will see changes on both sides (main branch and our branch). If we place our changes on top of the most recent commit, there’ll be a straight line detailing in which order everything was done, and so we end up with a more coherent and clearer log history (and we don’t have to read through all the Merge branch develop into dl/feat-123/example commit messages as well).&lt;/p&gt;

&lt;h3&gt;
  
  
  Merging your research back into the main document
&lt;/h3&gt;

&lt;p&gt;Now that you have your work sorted out and cleaned up, it’s time for the final review before &lt;em&gt;merging&lt;/em&gt; it into the main document. By doing this, the moment your work was added to the main research will stand out, because everyone will know when what you did was introduced.&lt;/p&gt;

&lt;h3&gt;
  
  
  Back to code
&lt;/h3&gt;

&lt;p&gt;I think that most of what I explained before was self-explanatory. Either way, here’s a quick recap, with code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You’re added to a repo&lt;/li&gt;
&lt;li&gt;Someone assigns you a feature (let’s assume the main branch is develop)&lt;/li&gt;
&lt;li&gt;Someone updates develop, so you should rebase your branch&lt;/li&gt;
&lt;li&gt;Once you’re finished, open a pull request, get your code reviewed, and merge it into develop&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Summing everything up
&lt;/h3&gt;

&lt;p&gt;If in the future you have any doubts about whether you should merge or rebase, remember the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always rebase from parent branch: this way your work will always be on top of the most up-to-date version of the work (and you avoid having multiple merge commits, keeping you branch history cleaner and clearer)&lt;/li&gt;
&lt;li&gt;Always merge into parent branch: when you merge your branch, you will know when that was done, since there will be a merge commit stating it!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether it’s frontend, backend, mobile or DevOps, at Runtime Revolution we have a spot for you!&lt;/p&gt;




</description>
      <category>git</category>
      <category>gitflow</category>
      <category>productivity</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Grids, why I love them, and why you should not run away from them</title>
      <dc:creator>André Bártolo</dc:creator>
      <pubDate>Wed, 10 Apr 2019 14:34:21 +0000</pubDate>
      <link>https://forem.com/runtime-revolution/grids-why-i-love-them-and-why-you-should-not-run-away-from-them-33i5</link>
      <guid>https://forem.com/runtime-revolution/grids-why-i-love-them-and-why-you-should-not-run-away-from-them-33i5</guid>
      <description>&lt;h3&gt;
  
  
  Grids, why I love them and why you should not run away from them
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AY2z8-QC22TWkr4ystde2lw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AY2z8-QC22TWkr4ystde2lw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am a designer, a UX/UI designer to be precise, and I love grids. But wait, which designer doesn’t? Okay, I know designers are not all the same and perhaps not all designers love grids, but we have to admit that in general designers do love their grids and there’s a reason for that. Grids are cool and very useful, this post is all about grids, why I love them, and why developers should have a better understanding of them.&lt;/p&gt;

&lt;p&gt;Grids are used to design all kinds of things: logos, books, furniture, posters, flyers, websites, apps, webapps, icons, and so on. Whenever possible, I start with a grid, no matter what kind of project it is. It can be just what you need to break free from that fear of a blank sheet in front of you, and to start realizing all the potential stored in that [brand new] canvas.&lt;/p&gt;

&lt;p&gt;Grids bring order; they help you decide sizes, proportions, and the placement of elements. Grids bring clarity and consistency; they help you collaborate and speed up production. A grid always works with, rather than against, the designer and the developer.&lt;/p&gt;

&lt;p&gt;Grids are gradually shifting from mere helpers to an important part of the whole work process. If you are a developer you might think &lt;em&gt;“Okay cool, this guy loves grids and the framework I love has a grid system, problem solved!”&lt;/em&gt; but what if I ask you to work with a grid that was specially designed for that project? You would have to develop that grid, and make it responsive and understand how it works across all platforms and have more work. Why? Because development frameworks take a “&lt;em&gt;one size fits all”&lt;/em&gt; approach when it comes to grids and sometimes it’s okay to work with but that approach generally doesn’t work. For one reason or another I am never fully satisfied with the grids that most development frameworks provide, because they demand compromises I am not willing to make.&lt;/p&gt;

&lt;p&gt;Grids are not static! These days, &lt;em&gt;dynamic&lt;/em&gt; is a word we use a lot, and with good reason; things change all the time! Design needs to be fluid, to use more relative values, not absolute ones. Design needs to adapt to content, not dictate it. Design needs to accommodate, not disarrange.&lt;/p&gt;

&lt;p&gt;In my process I take two things into consideration when preparing a digital grid. First one is &lt;strong&gt;screen sizes aren’t standard&lt;/strong&gt; , and the second one is &lt;strong&gt;different projects have different needs.&lt;/strong&gt; These starting points warn us that things need to adapt, breakpoints are not always going to be the same, the number of columns may vary, their size, the gutter between each column, if the grid has margins or not, all may vary.&lt;/p&gt;

&lt;p&gt;Now, why do developers need to better understand grids? Because if you take part in this process there is as much in it for you as there is for a designer. You will gain independence, and will not need to constantly ask for changes. You will be faster in your work. Your code will be cleaner and tidier, in order to accommodate what the grid asks, and you will collaborate more, leaving time to what’s important. All this because you understood the grid, the structure, and how things move and evolve.&lt;/p&gt;

&lt;p&gt;If you reached this far in this post, I will assume you are interested in grids (Who isn’t!?). So let me introduce you to some types of grids:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Responsive grid&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A4hgqhH3-ygPopMbjOrkV4Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A4hgqhH3-ygPopMbjOrkV4Q.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The responsive grid is a grid scheme designed that uses around 12 columns and is complemented with gutters and margins. As the screen becomes smaller, the responsive grid adapts by losing columns in order to fit the screen, so for example in a 3 breakpoint scheme it would be: desktop 12 columns, tablet 8 columns and mobile 4 columns.&lt;/p&gt;

&lt;p&gt;Responsive grids are great at adding order and balance and a great resource for less complex project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Fluid grid&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AzfLeeWw4inzDEe1SaehJ6A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AzfLeeWw4inzDEe1SaehJ6A.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fluid grids are similar to the regular responsive grid but these scale with browser size. There are several ways to do this, and one of my favorites is nested grids where smaller grids are nested into bigger ones, also fluid grids usually use relative values instead of absolute.&lt;/p&gt;

&lt;p&gt;Fluid grids usually produce better mechanics for responsiveness since they are more agile and they prove to be a better option for more complex projects or heavy content projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. 8-point grid&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AeGM1q0tPlRI5RZb8lEgIZg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AeGM1q0tPlRI5RZb8lEgIZg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This system should be used in conjunction with another system (a fluid system for example), because the primary goal of this 8-point system is to create a consistent ratio of measurement (all sizes, paddings and spacings should be divisible by 8).&lt;/p&gt;

&lt;p&gt;8-point grid systems are a great tool for teams because they create an easy system of communication between designers and developers (no fussing over pixels). A developer can easily eyeball an 8pt increment instead of having to measure each time.&lt;/p&gt;

&lt;p&gt;There are plenty more grids to explore but these three are a good start, they can lead to some huge improvements in your work and help you in complex projects. Understand that when someone puts time and effort into a grid, it is usually not just because that person loves grids, but more so that they have studied the problem and have developed something that could help everyone. It’s not only about the user, it’s also about you.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I am UX/UI designer at Runtime Revolution who is quirky and loves a good laugh that has wonderful brain to memorize idiot things and forget important ones. I love all kinds of design and I’m all about making questions and finding solutions.&lt;/em&gt;&lt;/p&gt;




</description>
      <category>love</category>
      <category>grid</category>
      <category>ui</category>
      <category>ux</category>
    </item>
    <item>
      <title>Building a Slack App using Ruby and Sinatra</title>
      <dc:creator>Nuno Namorado</dc:creator>
      <pubDate>Mon, 11 Mar 2019 14:43:08 +0000</pubDate>
      <link>https://forem.com/runtime-revolution/building-a-slack-app-using-ruby-and-sinatra-2ie4</link>
      <guid>https://forem.com/runtime-revolution/building-a-slack-app-using-ruby-and-sinatra-2ie4</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2A10lZcKeJOYAjJbW9" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2A10lZcKeJOYAjJbW9"&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/@morrec?utm_source=medium&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Morre Christophe&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My team at Runtime Revolution uses an in-house app for team management and other day-to-day chores. Recently, I was asked to integrate &lt;a href="https://api.slack.com/slash-commands" rel="noopener noreferrer"&gt;&lt;em&gt;Slack’s Slash Commands&lt;/em&gt;&lt;/a&gt; into it.&lt;/p&gt;

&lt;p&gt;Being new to Ruby I accepted the challenge with added motivation and drive. This was a good side project to improve my Ruby skills.&lt;/p&gt;

&lt;p&gt;From the start, I wanted to create a small standalone app that could be easily integrated with our existing Rails application. There are a couple of web frameworks out there. Some offer Rails-like features, &lt;a href="http://hanamirb.org/" rel="noopener noreferrer"&gt;&lt;em&gt;Hanami&lt;/em&gt;&lt;/a&gt; &lt;em&gt;and&lt;/em&gt; &lt;a href="http://padrinorb.com/" rel="noopener noreferrer"&gt;&lt;em&gt;Padrino&lt;/em&gt;&lt;/a&gt;, just to name a few. However, none offered the small footprint and modularity that &lt;a href="http://sinatrarb.com" rel="noopener noreferrer"&gt;&lt;em&gt;Sinatra&lt;/em&gt;&lt;/a&gt; implies. So I went with Sinatra.&lt;/p&gt;

&lt;p&gt;Providing a simple yet powerful DSL, Sinatra enables the creation of web applications with ease. Using community created modules we are able to quickly build a fully-fledged API server with authentication, logging, etc. or a small HTTP module for other Ruby applications or services.&lt;/p&gt;

&lt;p&gt;In this article, I will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;give a brief overview of &lt;em&gt;Slash Commands&lt;/em&gt; and &lt;em&gt;Actions&lt;/em&gt;, as well as the concept behind interactive messages;&lt;/li&gt;
&lt;li&gt;guide you through the creation and configuration of a Slack application;&lt;/li&gt;
&lt;li&gt;build a “Surf forecasting” Sinatra application for receiving and sending Slack messages;&lt;/li&gt;
&lt;li&gt;show how to deploy an app using Heroku service or embed it in a Rails application.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1. Slash Commands and Actions
&lt;/h3&gt;

&lt;p&gt;Slack apps can do more than just sending simple text messages. Using the &lt;em&gt;Slash Commands&lt;/em&gt; and &lt;em&gt;Interactive Components&lt;/em&gt; (buttons, menus, date pickers, popups, etc.), apps are able to effectively create more engaging experiences.&lt;/p&gt;

&lt;p&gt;The diagram below provides a basic outline of the typical flow using interactive messages. Note that the interactions can be triggered by a variety of sources found within the API provided by Slack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F512%2F1%2A7sqqRAtDN49QUDkuMlhQuA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F512%2F1%2A7sqqRAtDN49QUDkuMlhQuA.png"&gt;&lt;/a&gt;Slack interactions overview. Taken from &lt;a href="https://api.slack.com/messaging/interactivity" rel="noopener noreferrer"&gt;Slack docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the context of this article, the origin will be a user action triggered by a command_._ The app will then send an enriched message containing buttons, which allow the user to select one of several outcomes presented.&lt;/p&gt;

&lt;p&gt;Slack API documentation can be somewhat confusing when referring to actions because almost everything is an action. However, when it comes to configuring a Slack app, as we will see further along, there is a clear separation between &lt;em&gt;Slash Commands&lt;/em&gt; and &lt;em&gt;Interactive Components.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What are &lt;em&gt;Slash commands?&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Slash commands,&lt;/em&gt; as the name states, are commands that the user can execute by writing with a leading / followed by the name of the command. A known example of this is the /remind.&lt;/p&gt;

&lt;p&gt;Slack apps can register new Slash Commands, which will instruct Slack to send an HTTP POST request to the receiving server. The app server will act accordingly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The string &lt;em&gt;/&amp;lt;name&amp;gt;&lt;/em&gt; is what identifies the command and all words that follow will be the command parameters.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What are &lt;em&gt;Actions?&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Actions are events triggered when the user selects &lt;a href="https://api.slack.com/actions" rel="noopener noreferrer"&gt;a menu action&lt;/a&gt; or clicks any interactive element on a message (buttons, selects, date pickers, etc.). Slack apps can register up to 5 actions in the &lt;em&gt;Interactive Components&lt;/em&gt; page or have unlimited actions if they use interactive elements on messages.&lt;/p&gt;

&lt;p&gt;Every action is identified by its respective &lt;em&gt;callback_id,&lt;/em&gt; and every time the user triggers one a different HTTP POST will be sent to the receiving server.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Creating and configuring a Slack Application
&lt;/h3&gt;

&lt;p&gt;There are a couple of things to consider when creating a Slack application for sending and receiving interactive messages. We will have to set specific auth permissions, activate the &lt;em&gt;Interactive Components&lt;/em&gt; feature, and configure &lt;em&gt;Slash Commands.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  2.1. Create the app
&lt;/h4&gt;

&lt;p&gt;Navigate to &lt;a href="https://api.slack.com/apps" rel="noopener noreferrer"&gt;https://api.slack.com/apps&lt;/a&gt; and press the “&lt;em&gt;Create New App”&lt;/em&gt; button. Add a name and assign it to a workspace of your choice.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.2. Configuring Slash Commands
&lt;/h4&gt;

&lt;p&gt;Having created the app, go to &lt;em&gt;Slash Commands&lt;/em&gt; page and press “&lt;em&gt;Create New Command&lt;/em&gt;”. Fill the form. Set the &lt;em&gt;Request URL&lt;/em&gt; with a dummy URL, we’ll come back to this page later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F983%2F1%2AQioZF-Wdui3SVT8-9xgtvg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F983%2F1%2AQioZF-Wdui3SVT8-9xgtvg.png"&gt;&lt;/a&gt;Command form&lt;/p&gt;

&lt;h4&gt;
  
  
  2.3. Activating Interactive Components
&lt;/h4&gt;

&lt;p&gt;Go to the &lt;em&gt;Interactive Components&lt;/em&gt; page and switch “on” the feature. Again, set the &lt;em&gt;Request URL&lt;/em&gt; with a dummy URL. This will be updated later, with the actual endpoint. Hit “Save changes” on the bottom.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F998%2F1%2AqSx3UUdWaXGmVDpEq4Kauw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F998%2F1%2AqSx3UUdWaXGmVDpEq4Kauw.png"&gt;&lt;/a&gt;Interactive components configuration&lt;/p&gt;

&lt;h4&gt;
  
  
  2.4. Set permissions
&lt;/h4&gt;

&lt;p&gt;Going to &lt;em&gt;OAuth &amp;amp; Permissions&lt;/em&gt; page_,_ in the Scopes section, search for “&lt;strong&gt;Add slash commands and add actions to messages (and view related content)&lt;/strong&gt;” and save.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F705%2F1%2AnlQLIqqxS86GXp1KlhL_1A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F705%2F1%2AnlQLIqqxS86GXp1KlhL_1A.png"&gt;&lt;/a&gt;App permissions&lt;/p&gt;

&lt;h4&gt;
  
  
  2.5. Installing
&lt;/h4&gt;

&lt;p&gt;After following the previous steps, we are ready to install the app in the selected workspace. Go to the &lt;em&gt;Install App&lt;/em&gt; page and trigger the installation.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Building the app
&lt;/h3&gt;

&lt;p&gt;We are going to build an application that will give us the current surf forecast for a given place in the world. We will use &lt;a href="https://www.windguru.cz/1060" rel="noopener noreferrer"&gt;Windguru’s website&lt;/a&gt; to compile the necessary information. For added fun, let’s also include the location map of the spot, so it’s easier to interpret the information given by the variables. We will use &lt;a href="https://docs.mapbox.com/api/maps/#static" rel="noopener noreferrer"&gt;Mapbox’s static maps API&lt;/a&gt; for that.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; :&lt;/p&gt;

&lt;p&gt;During the creation of this article and while coding the example app, I identified some common behaviours for proccessing and sending Slack messages. So, I decided to create a simple Sinatra module and provide it as a gem (&lt;a href="https://github.com/nunonamorado/sinatra-slack" rel="noopener noreferrer"&gt;sinatra-slack&lt;/a&gt;). All examples shown are using this gem.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  3.1. Interactions
&lt;/h4&gt;

&lt;p&gt;The app will have two types of interactions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the user uses the command /surf today carcavelos for example, to retrieve the forecast information. If the provided variable, &lt;em&gt;spot_name&lt;/em&gt;, is enough to pinpoint the desired surf spot, the forecast is returned to the user.&lt;/li&gt;
&lt;li&gt;If during the first interaction, the search result is ambiguous, the app will send in return a message with multiple buttons one per each surf spot result, up to a limit of 5. This will allow the user to continue the flow.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F835%2F1%2AANzly1NRuP5hOJxYHEvkhg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F835%2F1%2AANzly1NRuP5hOJxYHEvkhg.gif"&gt;&lt;/a&gt;Interaction 1&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F835%2F1%2A5azSmb5HsTcXeaMjF3d25A.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F835%2F1%2A5azSmb5HsTcXeaMjF3d25A.gif"&gt;&lt;/a&gt;Interaction 2&lt;/p&gt;

&lt;h4&gt;
  
  
  3.2. Requirements
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Ruby environment correctly configured;&lt;/li&gt;
&lt;li&gt;code from repo — &lt;a href="https://github.com/nunonamorado/slash_command_article" rel="noopener noreferrer"&gt;Slash Command Article&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;a working Slack App with a configured /surf command and Interactive Components activated.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ngrok.com/download" rel="noopener noreferrer"&gt;ngrok tool&lt;/a&gt; to tunnel all Slack requests to your local dev environment.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.mapbox.com/" rel="noopener noreferrer"&gt;Mapbox account&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3.3. Local development
&lt;/h4&gt;

&lt;p&gt;Slack needs to send requests to valid HTTP endpoints, so we will have to set up some HTTP forwarding using ngrok.&lt;/p&gt;

&lt;p&gt;Download the tool from their website, create a free account. When logged in, start the forwarding requests by running,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./ngrok http 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;this will start the server and you are now able to receive requests on your dev machine.&lt;/p&gt;

&lt;p&gt;Go back to Slack App’s configurations page, in the /surf command configuration paste the ngrok endpoint to the &lt;em&gt;Request URL&lt;/em&gt; input and do the same for the &lt;em&gt;Interactive Components&lt;/em&gt; section.&lt;/p&gt;

&lt;p&gt;The final URLs should look something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;https://&amp;lt;ngrok-domain&amp;gt;/slack/commands&lt;/li&gt;
&lt;li&gt;https://&amp;lt;ngrok-domain&amp;gt;/slack/actions&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  3.4. Starting the app
&lt;/h4&gt;

&lt;p&gt;Before starting the application, set the environment variables&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MAPBOX_API_TOKEN — create one in the &lt;a href="https://account.mapbox.com/" rel="noopener noreferrer"&gt;Mapbox’s account page&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;SLACK_SIGNING_SECRET — in the &lt;em&gt;Basic Information&lt;/em&gt; page, copy the secret from the &lt;em&gt;Credentials&lt;/em&gt; section.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;either using the .env file or the command line and start the app by running,&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bundle exec rackup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; To use .env, install &lt;a href="https://github.com/bkeepers/dotenv" rel="noopener noreferrer"&gt;dotenv&lt;/a&gt; gem.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  3.5. Project overview
&lt;/h4&gt;

&lt;p&gt;The S_urforecaster_ app consists of the following files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;app.rb — your main app file. This is where the Sinatra application will be configured;&lt;/li&gt;
&lt;li&gt;config.ru— this is the Rack configuration file;&lt;/li&gt;
&lt;li&gt;lib/surforecaster.rb — helper class to fetch data from Windguru’s API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;app.rb&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The code below contains comments for some relevant parts. However, a couple of things to notice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The class must inherit from Sinatra::Base ;&lt;/li&gt;
&lt;li&gt;To use the Slack app specific DSL, register the Sinatra::Slack module;&lt;/li&gt;
&lt;li&gt;All requests from Slack need to be verified. By setting the :slack_secret option, the module will verify each request sent to both commands_endpoint and actions_endpoint. For more information about the Slack verification process, &lt;a href="https://api.slack.com/docs/verifying-requests-from-slack" rel="noopener noreferrer"&gt;see their docs&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;The quick_reply parameter in both command and action definitions lets you set the text that the server will send back to Slack, as soon as it receives the command or action request. This is not necessary, but it’s a good practice to let the user know that their request was correctly received and is being processed.&lt;/li&gt;
&lt;li&gt;Under the hood, the module is using Sinatra::Async to process the requests asynchronously. Slack requires that all command and action requests be answered within a 3-second interval, so we need a mechanism to process the request in the background for longer tasks. We will then have to respond with HTTP Post, to the response_url sent in the original request.&lt;/li&gt;
&lt;li&gt;Command and Action signature definitions use the Mustermann string matching definitions. Looking at the &lt;em&gt;command&lt;/em&gt; signature definition /surf *granularity :spot_name, the &lt;strong&gt;*&lt;/strong&gt; allows catching the first word in a non-greedy way, leaving the rest of the words for the &lt;em&gt;spot_name&lt;/em&gt; variable. To know more about it, &lt;a href="https://github.com/sinatra/mustermann/blob/master/mustermann/README.md#-sinatra-pattern" rel="noopener noreferrer"&gt;go here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;config.ru&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We will use the rackup command to start the Sinatra application. The config.ru file contains all the necessary options for the app to run. Notice the topmost comment, this is where you can set all command-line options which rackupwill pick up.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;lib/surforecaster.rb&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is just a basic ruby class for fetching forecast information. It contains 3 methods that will be used in different stages of the command and action flows.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  3.6. Adding interactivity to messages
&lt;/h4&gt;

&lt;p&gt;As described above, Slack responses can be simple text messages or more complex, with rich text, images and other actionable elements like buttons and menus.&lt;/p&gt;

&lt;p&gt;For &lt;em&gt;Surforecaster&lt;/em&gt; app, we will need to send both messages with images and buttons.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To send messages with interactive components, two message properties are required — &lt;em&gt;callback_id&lt;/em&gt; and &lt;em&gt;fallback&lt;/em&gt;. In the example below, we are using a helper method to build a slack message and pass along the &lt;em&gt;callback_id&lt;/em&gt; value. If you look closely, we are setting the same name as the action signature defined in the app.rb, using the string interpolation to set the &lt;em&gt;callback_id&lt;/em&gt; according to the current command &lt;em&gt;granularity&lt;/em&gt; (that was sent by the command)&lt;em&gt;.&lt;/em&gt; This is a nice way of passing around values between Slack and the app, without having to cache it somewhere else.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;To send messages with images, you just have to set the property &lt;em&gt;image_url&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  4. Deploying
&lt;/h3&gt;

&lt;p&gt;The final step of this exercise is deploying the app we just built. As previously stated, Sinatra apps can be deployed as a standalone app or embeded in a Rails application.&lt;/p&gt;

&lt;h4&gt;
  
  
  4.1. Standalone mode
&lt;/h4&gt;

&lt;p&gt;We will be deploying &lt;em&gt;Surforecaster&lt;/em&gt; using &lt;a href="https://www.heroku.com" rel="noopener noreferrer"&gt;Heroku&lt;/a&gt; and &lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are a couple of things you need to do first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://signup.heroku.com" rel="noopener noreferrer"&gt;create an account on Heroku&lt;/a&gt; or use an existing one;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://devcenter.heroku.com/articles/heroku-cli#download-and-install" rel="noopener noreferrer"&gt;download and install Heroku CLI&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create a new app in Heroku using,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku create \&amp;lt;app-name\&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then, we need to set your app stack to container. Run the command,&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku stack:set container -a \&amp;lt;app-name\&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Set the values for the two environment variables (SLACK_SIGNING_SECRET and MAPBOX_API_TOKEN) either in the app settings page, c_onfig vars_ section or using Heroku CLI with,&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku config:set SLACK\_SIGNING\_SECRET -a \&amp;lt;app-name\&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, let’s look at these 2 files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;heroku.yml — this is the manifest file with the definition of the Heroku app;&lt;/li&gt;
&lt;li&gt;Dockerfile — contains the instructions for building the app image that will be deployed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;heroku.yml&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;build.docker.web tells Heroku to build the Dockerfile at the root of the project;&lt;/li&gt;
&lt;li&gt;run.web command is executed when launching the container. Heroku doesn’t let us define a static port number, so we have to set it through an environment variable, $PORT.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Dockerfile&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inherits from an official Ruby image;&lt;/li&gt;
&lt;li&gt;installs necessary dependencies missing from the image;&lt;/li&gt;
&lt;li&gt;copies the app files to the working directory.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We will be using the &lt;a href="https://devcenter.heroku.com/articles/github-integration" rel="noopener noreferrer"&gt;Github-Heroku integration&lt;/a&gt; for automatic deployments, so that every time a code change is pushed, Heroku will build and deploy the application. However, you could also choose to deploy using &lt;a href="https://devcenter.heroku.com/articles/git" rel="noopener noreferrer"&gt;Heroku Git&lt;/a&gt; or &lt;a href="https://devcenter.heroku.com/articles/container-registry-and-runtime" rel="noopener noreferrer"&gt;Container Registry&lt;/a&gt; methods.&lt;/p&gt;

&lt;h4&gt;
  
  
  4.2. Embedding in a Rails App
&lt;/h4&gt;

&lt;p&gt;Another way of deploying this app is to embed it in an existing Rails app. You can do this in two ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changing the config.ru file to something like this:&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;or by using Rails routes:&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Last step
&lt;/h3&gt;

&lt;p&gt;After the deployment is complete, copy&amp;amp;paste the new URL to the Slack app configuration pages and that’s it! Go to the Slack workplace and start “commanding” stuff.&lt;/p&gt;

&lt;h3&gt;
  
  
  Future work
&lt;/h3&gt;

&lt;p&gt;By creating Slack Apps with Interactive Components and Slash Commands we can accomplish really useful integrations that can simplify any company’s processes.&lt;/p&gt;

&lt;p&gt;Keep in mind that we’ve barely scratched the surface in terms of interactive elements, we can still experiment further with select menus, date pickers, popups, confirmation messages, etc.&lt;/p&gt;

&lt;p&gt;During the making of this article, I created a Sinatra module accessible through the &lt;a href="https://rubygems.org/gems/sinatra-slack" rel="noopener noreferrer"&gt;“sinatra-slack” gem&lt;/a&gt;. It’s still not production ready and there are a lot of interactive component helpers missing.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I work at&lt;/em&gt; &lt;a href="http://www.runtime-revolution.com/" rel="noopener noreferrer"&gt;&lt;em&gt;Runtime Revolution&lt;/em&gt;&lt;/a&gt; &lt;em&gt;as a Full-stack developer currently focusing on Ruby. My background is .Net and C#, with incursions into Angular and React.&lt;/em&gt;&lt;/p&gt;




</description>
      <category>sinatra</category>
      <category>slack</category>
      <category>heroku</category>
      <category>ruby</category>
    </item>
  </channel>
</rss>
