<?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>DEV Community: appaloosa.io</title>
    <description>The latest articles on DEV Community by appaloosa.io (@appaloosastore).</description>
    <link>https://dev.to/appaloosastore</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%2Fuser%2Fprofile_image%2F131288%2Fe2562dec-ee0b-4853-a513-2e2f4f427ac0.jpg</url>
      <title>DEV Community: appaloosa.io</title>
      <link>https://dev.to/appaloosastore</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/appaloosastore"/>
    <language>en</language>
    <item>
      <title>Active Record, Sidekiq, pools and threads</title>
      <dc:creator>appaloosa.io</dc:creator>
      <pubDate>Thu, 07 Feb 2019 12:54:01 +0000</pubDate>
      <link>https://dev.to/appaloosastore/active-record-sidekiq-pools-and-threads-18d5</link>
      <guid>https://dev.to/appaloosastore/active-record-sidekiq-pools-and-threads-18d5</guid>
      <description>&lt;p&gt;At Appaloosa, we use Sidekiq a lot. In fact, we use it for all the background jobs we need. We also use a database (yes, this sounds crazy, I know.) with Active Record as ORM. Sometimes, Sidekiq jobs need to do stuff in our database, this looks like a normal situation, until…&lt;/p&gt;

&lt;h2&gt;
  
  
  The recurring error…
&lt;/h2&gt;

&lt;p&gt;Until, this error pops many times:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;could not obtain a connection from the pool within 5.000 seconds (waited 5.002 seconds); all pooled connections were in use (ActiveRecord::ConnectionTimeoutError)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And it pops many times, every day, at the same hour. Something like late at night, so yes, let’s try to understand and fix it.&lt;/p&gt;

&lt;h2&gt;
  
  
  But how can this happen?
&lt;/h2&gt;

&lt;p&gt;Ok so, after checking all our monitoring tools we figured out that the error pops inside a Sidekiq job. And then, with more than 20 tabs opened on the browser (obviously, many were the same, opened from different sources, at different time), we found the theory and theoretically, the solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Theory
&lt;/h3&gt;

&lt;p&gt;First of all, we have Active Record on one side with its proper configuration file. In this &lt;code&gt;config/database.yml&lt;/code&gt; file, you can find something like:&lt;/p&gt;


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


&lt;p&gt;Remember the error? &lt;code&gt;could not obtain a connection from the pool&lt;/code&gt;. It must have something to do with that &lt;code&gt;pool&lt;/code&gt; configuration!&lt;/p&gt;

&lt;p&gt;Ok let’s check it. &lt;a href="https://guides.rubyonrails.org/v5.1/configuring.html#database-pooling" rel="noopener noreferrer"&gt;The official documentation&lt;/a&gt; tells us that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Active Record database connections are managed by ActiveRecord::ConnectionAdapters::ConnectionPool which ensures that a connection pool synchronizes the amount of thread access to a limited number of database connections.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you try to use more connections than are available, Active Record will block you and wait for a connection from the pool. If it cannot get a connection, a timeout error similar to that given below will be thrown.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here it is! This is what is happening:&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj98zs9jjjkddyzwuwi1q.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj98zs9jjjkddyzwuwi1q.gif" alt="That’s what must happen here!"&gt;&lt;/a&gt;&lt;em&gt;That’s what must happen here!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ok, then, what about the other side? How is Sidekiq managing its threads to prevent this error? &lt;a href="https://github.com/mperham/sidekiq/wiki/Advanced-Options#concurrency" rel="noopener noreferrer"&gt;The official wiki&lt;/a&gt; is pretty straightforward:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can tune the amount of concurrency in your sidekiq process. By default, one sidekiq process creates &lt;strong&gt;10 threads&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, it creates as many threads as we mentioned on our &lt;code&gt;config/sidekiq.yml&lt;/code&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38eugc3a2s623ecnhwgx.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38eugc3a2s623ecnhwgx.gif" alt="concurrency must be the key!"&gt;&lt;/a&gt;&lt;em&gt;concurrency must be the key!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There is also a little note on the wiki:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that ActiveRecord has a connection pool which needs to be properly configured in config/database.yml to work well with heavy concurrency. Set the pool setting to something close or equal to the number of threads&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;THAT’S IT! If we have Sidekiq concurrency value equal to Active Record pool value we can’t have more threads than the accepted by Active Record, and no more errors. Voilà! Let’s check those values on our project, something should not be right.&lt;/p&gt;


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


&lt;p&gt;and&lt;/p&gt;


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


&lt;p&gt;??&lt;/p&gt;

&lt;h3&gt;
  
  
  So… is everything ok?
&lt;/h3&gt;

&lt;p&gt;Ok, now things are really strange. On our configurations files we already have 3 for concurrency and 3 for pool fields, then, according to documentations, everything should work and we should not have any issues… But we have one! Then, I don’t know how but somewhere, at a time, 3 looks not to be equal to 3.&lt;/p&gt;

&lt;p&gt;Let’s double check everything, somewhere one of those value has to be overridden.&lt;/p&gt;

&lt;p&gt;Of course, the answer was NOPE.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let’s investigate 🕵🏻‍♀️
&lt;/h3&gt;

&lt;p&gt;As the error occurred each day at the same hour, we looked at the piece of code running at that moment and checked the logs.&lt;/p&gt;

&lt;p&gt;We analyzed 40 lines that occured in less than 10 seconds. The result is a beautiful file named &lt;code&gt;sidekiq logs sherlock&lt;/code&gt; with many background colors and others for the text. And we found something interesting…&lt;/p&gt;

&lt;h3&gt;
  
  
  Can we reproduce it?
&lt;/h3&gt;

&lt;p&gt;Of course, we can try. Let’s create a tiny Rails app with a database and Sidekiq. Let’s have two configuration files with the same configuration, as we have seen before:&lt;/p&gt;


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


&lt;p&gt;Now we can create a worker:&lt;/p&gt;


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


&lt;p&gt;This is a simple worker, doing nothing but calling a service which is going to do some stuff. For this service, we tried to reproduce the code we have, but this code is calling some other gem. To avoid complexity, we try to simplify it but keeping the main behavior.&lt;/p&gt;


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


&lt;p&gt;Try to run your worker and look at the result…&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ruby-2.5.3/gems/activerecord-5.2.1.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:199:in `block in wait_poll’: could not obtain a connection from the pool within 5.000 seconds (waited 5.010 seconds); all pooled connections were in use (ActiveRecord::ConnectionTimeoutError)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We are not sure that is exactly mimicking our behavior as the code uses transitive dependencies that are creating threads. But it looks like this and we ran into the same issue.&lt;/p&gt;

&lt;p&gt;And it makes sense. Remember, we said that for each process Active Record assigns &lt;code&gt;x&lt;/code&gt; connections depending on the &lt;code&gt;pool&lt;/code&gt; value. Each connection is made by one thread. If our Sidekiq &lt;code&gt;concurrency&lt;/code&gt; value is equal to the &lt;code&gt;pool&lt;/code&gt; value, we should not have any problem, unless, something is creating a new thread and calling the database. That’s the case on our example above with the &lt;code&gt;MyService&lt;/code&gt; class!&lt;/p&gt;

&lt;h2&gt;
  
  
  The why!
&lt;/h2&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs8mxle63lslducwgahn0.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs8mxle63lslducwgahn0.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In fact, it’s possible to do async job here but we need to be sure that we are not dealing with database, or at least, it’s not creating too much connections. In our case, we are inside a deep loop, which leads to too many database calls. 💥&lt;/p&gt;

&lt;p&gt;If you don’t want to have some headaches, be careful on what you put inside your workers 🙂&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%2F6000%2F0%2AtWDf4h0UaPtfbnQD" 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%2F6000%2F0%2AtWDf4h0UaPtfbnQD" alt="Photo by [Ayo Ogunseinde](https://unsplash.com/@armedshutter?utm_source=medium&amp;amp;utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral)"&gt;&lt;/a&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@armedshutter?utm_source=medium&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Ayo Ogunseinde&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;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Thank you for reading! 🙏
&lt;/h2&gt;

&lt;p&gt;You should subscribe to our blog to be notified whenever a new article is available! 👋&lt;/p&gt;

&lt;p&gt;This article was written by &lt;a href="http://twitter.com/ammelanie" rel="noopener noreferrer"&gt;Mélanie Araujo Martins&lt;/a&gt; of &lt;a href="https://www.appaloosa-store.com/" rel="noopener noreferrer"&gt;Appaloosa&lt;/a&gt;’s dev team.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>sidekiq</category>
      <category>activerecord</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Test your iOS application UI</title>
      <dc:creator>appaloosa.io</dc:creator>
      <pubDate>Wed, 23 Jan 2019 15:12:54 +0000</pubDate>
      <link>https://dev.to/appaloosastore/test-your-ios-application-ui-je5</link>
      <guid>https://dev.to/appaloosastore/test-your-ios-application-ui-je5</guid>
      <description>&lt;h2&gt;
  
  
  Introduction ✍️
&lt;/h2&gt;

&lt;p&gt;In this article, we will explain first how writing UI Test using Xcode, then how we added visual diff and finally how we configured in Bitrise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why testing the User Interface (UI)?
&lt;/h3&gt;

&lt;p&gt;For production release, it is painful to test every single use case and be sure there is no regression. You can either forget to test one flow or think that your change will not impact other parts of the code… But sometimes, it does 😣. This is what happened at Appaloosa for our iOS application, where some errors slipped under the radars. For example, when iOS 11 was released, we added constraints in the navigation bar, but this feature was not available in prior versions and the app crashed in iOS 10.&lt;/p&gt;

&lt;p&gt;It was quickly identified and fixed, yet that alert encouraged us to have a stronger process to prevent such issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  UI Test and Unit Test
&lt;/h3&gt;

&lt;p&gt;With unit tests, you test each small piece of code independently and focus on the logical aspects of the code.&lt;/p&gt;

&lt;p&gt;With UI tests you simulate user interactions and user flows. Let’s take the example of a user trying to log in your application. It will test the different interactions the user has with our application during the whole process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup 📚
&lt;/h2&gt;

&lt;p&gt;Go to your project on Xcode, click “+” button (see the image below) and add a iOS UI Testing bundle. A new target will be created as well.&lt;/p&gt;

&lt;p&gt;You can also add it when you create your project on Xcode.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Sample project
&lt;/h2&gt;

&lt;p&gt;Here is an example project. We want to test multiple user flows and be sure they all work as expected.&lt;/p&gt;

&lt;p&gt;You can find the complete code on &lt;a href="https://github.com/eelbeze/uiTesting"&gt;github&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;The goal is to make sure our login page shows the right views during the different steps. So we will need to access the different views of the page to evaluate their behaviour during the test.&lt;/p&gt;

&lt;p&gt;You have multiple ways to retrieve an element of your application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You can use the recording button in Xcode, it will generate automatically your code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can add an accessibility identifier on your view to access it more easily.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;You can write your code manually, in that case, you need to know how to access an element. You can print the detailed view hierarchy of the visible screen using the following command in LLDB:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;(lldb) po print(app.debugDescription)&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I prefer this last method, the code is more readable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing your first UI test
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Login with invalid credentials:
&lt;/h3&gt;

&lt;p&gt;If a user enters invalid credentials, we expect the application to show an error message:&lt;/p&gt;

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

&lt;p&gt;Let’s see how this translates in code.&lt;/p&gt;


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


&lt;h3&gt;
  
  
  The user wants to display his application list:
&lt;/h3&gt;


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


&lt;p&gt;Here we are forced to wait for the response of our fake server. We need to tell Xcode to wait until the application finished rendering the view. If we don’t, our test will fail since the view component being targeted has not yet appeared.&lt;/p&gt;

&lt;p&gt;To force Xcode to wait, I used an extension to XCTestCase:&lt;/p&gt;


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


&lt;h3&gt;
  
  
  The user wants to see the details of an application:
&lt;/h3&gt;

&lt;p&gt;Starting from Xcode 9, we can’t compare strings longer than 128 characters anymore so we are forced to use a predicate:&lt;/p&gt;


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


&lt;p&gt;✌️Now you can add UI tests for various user flows!&lt;/p&gt;

&lt;h3&gt;
  
  
  Visual Diff
&lt;/h3&gt;

&lt;p&gt;UI tests can’t be used to detect design changes (eg: different text size or text color)&lt;/p&gt;

&lt;p&gt;At Appaloosa, the frontend team faced the same issue and, after some research, they found a solution for visual regression test called &lt;a href="https://percy.io/"&gt;Percy&lt;/a&gt;. So I looked for a similar solution for iOS.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: &lt;a href="https://twitter.com/KamiKillertO"&gt;Benjamin&lt;/a&gt;, our front end expert wrote an article about &lt;a href="https://medium.com/appaloosa-store-engineering/better-ui-testing-in-ember-5d272b0a5cf3"&gt;Percy&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Snap.swift 🎨
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/skyweb07/Snap.swift"&gt;Snap.swift&lt;/a&gt; is a library that can compare a referent image (a screenshot) to the rendered UIView. This library was inspired by a &lt;a href="https://github.com/facebookarchive/ios-snapshot-test-case/"&gt;facebook library&lt;/a&gt;, which is now maintained by &lt;a href="https://github.com/uber/ios-snapshot-test-case/"&gt;Uber&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This library is usually used for unit tests, so I had to adapt my UI tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup:
&lt;/h3&gt;

&lt;p&gt;The first change I needed to make is to hide the status bar. If you don’t do this, your tests will fail every time.&lt;/p&gt;

&lt;p&gt;To do this, I added the following line of code:&lt;/p&gt;


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


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

&lt;p&gt;We then need to generate screenshots for every view in the app using the following command:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;isRecording = true&lt;/em&gt; in your setup method of your UI test.&lt;/p&gt;

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

&lt;p&gt;If something changes on the view, Snap.swift will generate two folders:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Diff&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Failure&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;the diff image will show the difference between the failed image and the expected image.&lt;/p&gt;

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

&lt;p&gt;We now have a functional setup to run UI tests locally, and we want to integrate it to our CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bitrise 🤖
&lt;/h2&gt;

&lt;p&gt;At appaloosa, we use &lt;a href="https://www.bitrise.io/"&gt;Bitrise&lt;/a&gt; for Continuous Integration. Below you can see the flows we use when launching a new production deployment. All flows need to be run successfully for the deployment to be completed.&lt;/p&gt;

&lt;p&gt;In order to avoid regression within the application, we decided to run our UITest flow on different screen sizes and iOS versions. At the time of writing, the flow is tested against the iPad, the iPhone 7, the iPhone 6 Plus and the iPhone X on iOS from version 10 to 12.&lt;/p&gt;

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

&lt;p&gt;When UI tests fail, we need to retrieve the screenshots associated to the failed tests (generated by Snap.swift).&lt;/p&gt;

&lt;p&gt;To do this, we added a Bitrise flow: &lt;em&gt;Deploy to Bitrise.io — Apps, Logs, Artifacts&lt;/em&gt;.&lt;/p&gt;

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

&lt;p&gt;This is also used to create new screenshots or update existing screenshots when a view changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations ⏳
&lt;/h2&gt;

&lt;p&gt;Every time the design of the app changes, we need to force tests to fail in order to retrieve the new “valid” screenshots as the new point of reference. This goes against the principles of Continuous Integration but we did not find a suitable alternative (yet).&lt;/p&gt;

&lt;h2&gt;
  
  
  Thank you for reading! 🙏
&lt;/h2&gt;

&lt;p&gt;If you have any questions or suggestions, please leave a comment we will be glad to answer it!&lt;/p&gt;

&lt;p&gt;👋 You should subscribe to our blog to be notified whenever a new article is available!&lt;/p&gt;

&lt;p&gt;This article was written by &lt;a href="https://twitter.com/grisouchoux"&gt;Elise El Beze&lt;/a&gt; of &lt;a href="https://www.appaloosa-store.com/"&gt;Appaloosa&lt;/a&gt;’s dev team.&lt;/p&gt;

</description>
      <category>ios</category>
      <category>uitesting</category>
      <category>testing</category>
      <category>visualdiff</category>
    </item>
    <item>
      <title>Demystifying git rebase: A workshop</title>
      <dc:creator>appaloosa.io</dc:creator>
      <pubDate>Wed, 23 Jan 2019 14:57:28 +0000</pubDate>
      <link>https://dev.to/appaloosastore/demystifying-git-rebase-a-workshop-3b21</link>
      <guid>https://dev.to/appaloosastore/demystifying-git-rebase-a-workshop-3b21</guid>
      <description>&lt;p&gt;Practical guide to learn about the git rebase command with a poem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZxlaveVZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2Agg5mhWR_VUm_sJVwztB-Uw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZxlaveVZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2Agg5mhWR_VUm_sJVwztB-Uw.png" alt="Image credit: **professortocat_v2 from [**GitHub Octodex](https://octodex.github.com/)"&gt;&lt;/a&gt;&lt;em&gt;Image credit: **professortocat_v2 from &lt;a href="https://octodex.github.com/"&gt;**GitHub Octodex&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;git rebase is the process of reapplying commits on top of another base tip.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Birth of an idea
&lt;/h3&gt;

&lt;p&gt;The git rebase command is quite complex and after several discussions during team meetings we discovered that some of us did not know how to use it properly. We were often avoiding the git rebase command out of fear, since this command can lead to all kinds of trouble if not used properly.&lt;/p&gt;

&lt;p&gt;Some of the team members are working with git GUI clients, such as &lt;a href="https://www.sourcetreeapp.com/"&gt;SourceTree&lt;/a&gt; or &lt;a href="https://www.gitkraken.com/"&gt;GitKraken&lt;/a&gt;, to work with a visual representation of their repositories. Some don’t know advanced use of some commands.&lt;/p&gt;

&lt;p&gt;So, we decided it would be a good idea to organize a workshop to learn this all together since most of us had knowledge gaps on this similar subject.&lt;/p&gt;

&lt;p&gt;I really wanted this to be less scholar and more fun, so I volunteered to create a hands-on workshop for the team.&lt;/p&gt;

&lt;p&gt;I thought about using a poem. “The road not taken”, by &lt;a href="https://fr.wikipedia.org/wiki/Robert_Frost"&gt;Robert Frost&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Two roads diverged in a yellow wood,&lt;br&gt;
And sorry I could not travel both&lt;br&gt;
And be one traveler, long I stood&lt;br&gt;
And looked down one as far as I could&lt;br&gt;
To where it bent in the undergrowth;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Then took the other, as just as fair,&lt;br&gt;
And having perhaps the better claim,&lt;br&gt;
Because it was grassy and wanted wear;&lt;br&gt;
Though as for that the passing there&lt;br&gt;
Had worn them really about the same,&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;And both that morning equally lay&lt;br&gt;
In leaves no step had trodden black.&lt;br&gt;
Oh, I kept the first for another day!&lt;br&gt;
Yet knowing how way leads on to way,&lt;br&gt;
I doubted if I should ever come back.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I shall be telling this with a sigh&lt;br&gt;
Somewhere ages and ages hence:&lt;br&gt;
Two roads diverged in a wood, and I — &lt;br&gt;
I took the one less traveled by,&lt;br&gt;
And that has made all the difference.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Preparation
&lt;/h3&gt;

&lt;p&gt;I simulated the work of a team contributing to the same poem, like so:&lt;/p&gt;

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

&lt;p&gt;Each branch corresponded to a poem’s paragraph or a small part.&lt;/p&gt;

&lt;p&gt;The different branches contained bad commits (in red), and commits to rearrange (in yellow).&lt;/p&gt;

&lt;p&gt;The objective of the workshop was to recompose the full poem branch after branch, using the git rebase command. Commands, such as the &lt;a href="https://git-scm.com/docs/git-cherry-pick"&gt;git cherry-pick&lt;/a&gt; command, were forbidden.&lt;/p&gt;

&lt;p&gt;When creating the workshop, one commit corresponded to only one line of the poem.&lt;/p&gt;

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

&lt;p&gt;“P1/L3” was the line of the poem from paragraph 1 line 3: “And be one traveler, long I stood”.&lt;/p&gt;

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

&lt;p&gt;The objective was to make it easier to resolve conflicts. They often occur when changes are made to the same line of a file.&lt;/p&gt;

&lt;p&gt;To resolve a merge conflict, we usually have to choose which changes to incorporate, so I indicated the number of the line for each line of the poem (see below).&lt;/p&gt;

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

&lt;p&gt;The poem was an excuse to use the git rebase command. The workshop was not focused on conflict resolutions.&lt;/p&gt;

&lt;h3&gt;
  
  
  The workshop
&lt;/h3&gt;

&lt;p&gt;Each member of the dev team set up their environment, cloning the repository:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/coralieco/workshop-git-rebase"&gt;https://github.com/coralieco/workshop-git-rebase&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This workshop is a practical guide to train on a complex command using git rebase. Everyone was allowed to read the &lt;a href="https://git-scm.com/docs/git-rebase"&gt;documentation&lt;/a&gt; or to ask help live. It was not a competition, but the workshop had to be completed in one hour or less :)&lt;/p&gt;

&lt;p&gt;We started the workshop together, rebasing the first paragraph as a first example. This was to make sure everyone understood the rules and the methods to go along with the rest of the workshop.&lt;/p&gt;

&lt;p&gt;Then, everybody had to complete the workshop. Using the git rebase command, and more particularly the — interactive option.&lt;/p&gt;

&lt;p&gt;From &lt;a href="https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History"&gt;documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;With the interactive rebase tool, you can then stop after each commit you want to modify and change the message, add files, or do whatever you wish. You can run rebase interactively by adding the -i option to git rebase. You must indicate how far back you want to rewrite commits by telling the command which commit to rebase onto.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is one option I particularly liked, so I suggested to the team to use it to rebase the poem into paragraph 4 without rebasing it into the parent paragraph (see diagram below) : git rebase -onto&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--10qF35-W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2740/0%2Ai9JwwphI6bCxTR9R" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--10qF35-W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2740/0%2Ai9JwwphI6bCxTR9R" alt="[git rebase documentation](https://git-scm.com/docs/git-rebase))"&gt;&lt;/a&gt;&lt;em&gt;&lt;a href="https://git-scm.com/docs/git-rebase"&gt;git rebase documentation&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As there was different levels of progression in the team, I corrected one paragraph every 10–15 minutes approximately, so everyone could follow.&lt;/p&gt;

&lt;p&gt;At the end, I sent a detailed &lt;a href="https://github.com/coralieco/workshop-git-rebase#corrections"&gt;correction&lt;/a&gt; of the commands I used to reconstitute the poem. Some of the dev team members felt the need to redo the workshop at home in order to really benefit from this exercise.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Git rebase interactive gives us the opportunity to alter individual commits.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Git rebase is great to maintain a linear project history, clean and meaningful, we must not be afraid to use it :)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Practical workshops are really successful to learn a new subject: we were not petrified to use it on Appaloosa project.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, we were all happy of the format. We all enjoyed spending this time together and we would like to continue organizing similar workshops in the future.&lt;/p&gt;

&lt;p&gt;Thanks to Appaloosa Team!&lt;br&gt;
This article was written by &lt;a href="https://twitter.com/cco_app"&gt;Coralie Collignon&lt;/a&gt; of &lt;a href="https://www.appaloosa-store.com/"&gt;Appaloosa&lt;/a&gt;’s dev team.&lt;/p&gt;

</description>
      <category>git</category>
    </item>
    <item>
      <title>How to slowly replace your site with a new one made with EmberJS</title>
      <dc:creator>appaloosa.io</dc:creator>
      <pubDate>Wed, 23 Jan 2019 14:53:55 +0000</pubDate>
      <link>https://dev.to/appaloosastore/how-to-slowly-replace-your-site-with-a-new-one-made-with-emberjs-3m66</link>
      <guid>https://dev.to/appaloosastore/how-to-slowly-replace-your-site-with-a-new-one-made-with-emberjs-3m66</guid>
      <description>&lt;p&gt;As you may know (if not, read our articles 😉) at Appaloosa we recently release our new frontend to our customers (read &lt;a href="https://medium.com/@appaloosastore/we-have-a-new-emberjs-front-end-c7246e76cdbd"&gt;We have a new EmberJS front end&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;More than two years ago, we started the work on this new frontend which is a complete redesign of our old massive frontend (lot of features/pages). At the time there’s still works to do, the new admin does not cover the global spectrum of our old admin. But we decided to release to our customers, because it’s a nice interface with a better UX and it’s easier to update/maintain.&lt;/p&gt;

&lt;p&gt;For the parts that are not available (yet), we redirect our users to our old/legacy admin.&lt;/p&gt;

&lt;p&gt;This new frontend is build with EmberJS and today we decided to show you how we manage the transition from our new admin to our old admin. We decided to share our solution, because we think it’s a good one that might help people that need to add the same behavior.&lt;/p&gt;

&lt;p&gt;But first let’s have a small look of how it looks in real.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BD2tURYJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/6696/1%2AWNatcWzH_t8pabQ7_uqbFQ.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BD2tURYJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/6696/1%2AWNatcWzH_t8pabQ7_uqbFQ.gif" alt="Looks great, isn’t it ?"&gt;&lt;/a&gt;&lt;em&gt;Looks great, isn’t it ?&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why a redirection modal?
&lt;/h3&gt;

&lt;p&gt;Before dealing with the redirection, let’s create a nice modal. The modal is very important because it inform the user about what’s happening. Without the modal, the user would be lost. Imagine being are on a nice looking interface and suddenly ending up on another website with a very different and old-looking design 😱.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to make a redirection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s start with some HTML&lt;/p&gt;


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


&lt;p&gt;Now that we have a modal, let’s have some CSS to make a good looking modal.&lt;/p&gt;


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


&lt;p&gt;Now that we have a nice modal, let’s make the redirection. To make it easy to add/remove redirection in our templates we’ve decided to go with a service and use a simple html link.&lt;/p&gt;

&lt;p&gt;Let’s have a look to the redirection service.&lt;/p&gt;


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


&lt;p&gt;This service is pretty simple, it consist on 3 actions :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;redirect&lt;/strong&gt;: This action setup the redirection, by saving the request url, and displaying the redirection modal.&lt;br&gt;
&lt;strong&gt;proceedRedirection&lt;/strong&gt;: This action triggers the redirection to the requested page&lt;br&gt;
&lt;strong&gt;cancelRedirection&lt;/strong&gt;: Cancel the redirection. Cleanup the redirection service by erasing the saved url and close the redirection popup.&lt;/p&gt;

&lt;p&gt;It also expose displayModal that is use to display the modal when needed.&lt;/p&gt;

&lt;p&gt;Because with Ember you can specify the target for an action. Adding a redirection in the app is as simple as adding a link tag in a template (component or route). The only requirement is to inject the service.&lt;/p&gt;


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


&lt;p&gt;As you can see it’s pretty easy to add such redirection service the only tricky part is to detect back navigation and close the redirection popup. Fortunately since Ember 2.15 there’s a router service you can use (see the release note &lt;a href="https://emberjs.com/blog/2017/09/01/ember-2-15-released.html#toc_public-router-service-phase-1"&gt;here&lt;/a&gt;). Using this service you can easily detect back navigation by observing the router’s current route. So let’s change the previous code a little bit.&lt;/p&gt;


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


&lt;p&gt;Now we’re able to detect any “back” navigation action and close the modal automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Doing more than redirection
&lt;/h3&gt;

&lt;p&gt;In this article we share how to create a simple redirection which opens a popup, it’s just a basic simple example there’s more you can do.&lt;/p&gt;

&lt;p&gt;For example, at Appaloosa we send some analytics when a user is redirected. That way we prioritise the transfer of a feature from the old admin to the new one, because we know what feature our users need the most.&lt;/p&gt;

&lt;p&gt;With the same code you can also add an input field inside the redirect popup asking for a feedback, or rate their experiences… is up to you 😉&lt;/p&gt;




&lt;p&gt;👋 You should subscribe to our blog to be notified whenever a new article is available!&lt;/p&gt;

&lt;p&gt;This article was written by &lt;a href="https://twitter.com/KamiKillertO"&gt;Benjamin JEGARD &lt;/a&gt;of &lt;a href="https://www.appaloosa-store.com/"&gt;Appaloosa&lt;/a&gt;’s dev team.&lt;br&gt;
&lt;em&gt;PS: There’s a live example of this redirection service&lt;a href="https://ember-twiddle.com/9a6f90e2ea95743b5f246d600b3c644d"&gt; here&lt;/a&gt;&lt;/em&gt; 🤩.&lt;/p&gt;

</description>
      <category>ember</category>
    </item>
    <item>
      <title>Ruby memory, ActiveRecord and Draper</title>
      <dc:creator>appaloosa.io</dc:creator>
      <pubDate>Wed, 23 Jan 2019 14:42:23 +0000</pubDate>
      <link>https://dev.to/appaloosastore/ruby-memory-activerecord-and-draper-1j8i</link>
      <guid>https://dev.to/appaloosastore/ruby-memory-activerecord-and-draper-1j8i</guid>
      <description>&lt;p&gt;At Appaloosa we often process CSV. When we process thousands of CSV lines of users that need to be added to (or removed from) a group, we are seeing a huge increase in memory. Sometimes the Heroku dynos cannot manage it.&lt;/p&gt;

&lt;p&gt;We will explain how we jump from this when processing 4_000 users (look at blue line but also time):&lt;/p&gt;

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

&lt;p&gt;To that:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  memory profiler
&lt;/h2&gt;

&lt;p&gt;We used &lt;a href="https://github.com/tmm1/stackprof"&gt;stackprof&lt;/a&gt;, &lt;a href="https://github.com/SamSaffron/memory_profiler"&gt;memory_profiler&lt;/a&gt;. We look at tool like &lt;a href="https://github.com/jlfwong/speedscope"&gt;speedscope&lt;/a&gt; for stackprof results and always it point at heavy use of ActiveRecord. Also we noticed in “Allocated String Report” of memory_profiler a string allocated multiples times.&lt;/p&gt;

&lt;p&gt;With 4000 users:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Allocated String Report
-----------------------------------
   1619100  "decorated?"
   1619100  .rvm/gems/ruby-2.5.3@app/gems/activerecord-5.2.1/lib/active_record/attribute_methods.rb:279
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  "decorated?" where does it come from?
&lt;/h2&gt;

&lt;p&gt;It’s instantiated here: &lt;code&gt;.rvm/gems/ruby-2.5.3@app/gems/activerecord-5.2.1/lib/active_record/attribute_methods.rb:281&lt;/code&gt;&lt;/p&gt;


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



&lt;p&gt;If we add a &lt;code&gt;puts caller&lt;/code&gt; when &lt;code&gt;"decorated?"&lt;/code&gt; is instantiated we have this:&lt;/p&gt;


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


&lt;p&gt;First we didn’t understand how we can jump from Draper to &lt;code&gt;active_record/associations/collection_association.rb&lt;/code&gt; like:&lt;/p&gt;


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


&lt;p&gt;The code on Draper &lt;a href="https://github.com/drapergem/draper/blob/ed62f5555148b997eeb9feb365dd3dffcdb3d903/lib/draper/decoratable/equality.rb#L21"&gt;part&lt;/a&gt; is:&lt;/p&gt;


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


&lt;p&gt;Then the code in &lt;code&gt;active_record/associations/collection_association.rb:281&lt;/code&gt; looks like:&lt;/p&gt;


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


&lt;p&gt;We jump from &lt;code&gt;index = @target.index(record)&lt;/code&gt; to &lt;code&gt;Draper::Decoratable::Equality.==&lt;/code&gt; 🤔.&lt;/p&gt;

&lt;h2&gt;
  
  
  But how?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/christophemaximin"&gt;Christophe Maximin&lt;/a&gt; (thanks a lot, Christophe) came with this answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;(Wild guess by just reading the AR code) Because if the same user (same primary key) is already in users, it just replaces that old object with the new object you’re passing with &amp;lt;&amp;lt;, which is why it needs to know the index of the old one. And to know if records are the same, it needs to call ==.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want a full example of how it is called, you can use &lt;code&gt;[TracePoint]&lt;/code&gt;(&lt;a href="https://ruby-doc.org/core-2.5.3/TracePoint.html"&gt;https://ruby-doc.org/core-2.5.3/TracePoint.html&lt;/a&gt;). Without and with having &lt;code&gt;==&lt;/code&gt; in Integer:&lt;/p&gt;


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


&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Tracepoint “Establishes proc as the handler for tracing” calls &lt;code&gt;:call:&lt;/code&gt; for call to Ruby method &lt;code&gt;:c_call:&lt;/code&gt; for a call to a C-language routine&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What happens on a real app?
&lt;/h2&gt;

&lt;p&gt;To be sure we were not testing a side effect of our app, we created a &lt;a href="https://github.com/benoittgt/ar_and_draper_allocation/tree/with_draper_and_concat"&gt;new rails app&lt;/a&gt;, with 2 models, Draper installed. Then &lt;a href="https://github.com/benoittgt/ar_and_draper_allocation/blob/with_draper_and_concat/lib/core_extensions.rb"&gt;we monkey patched &lt;code&gt;add_to_target&lt;/code&gt;&lt;/a&gt; for ActiveRecord.&lt;/p&gt;


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


&lt;p&gt;We put the code to reproduce the concatenation in a &lt;a href="https://github.com/benoittgt/ar_and_draper_allocation/blob/with_draper_and_concat/lib/tasks/concat_records.rake"&gt;rake task&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If we add two records &lt;code&gt;rails concat_records:comments[2]&lt;/code&gt; on the second record we have this interesting result:&lt;/p&gt;


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


&lt;p&gt;We can see that we are calling &lt;code&gt;index&lt;/code&gt;, then &lt;code&gt;Draper::Decoratable::Equality&lt;/code&gt; (that override &lt;code&gt;==&lt;/code&gt;), then &lt;code&gt;ActiveRecord:Core&lt;/code&gt;, etc...&lt;/p&gt;

&lt;p&gt;The issue is when the array of record we concat contains lot’s of ActiveRecord objects. Every time we add one element, we redo all of those calls. Also Draper in is &lt;code&gt;Equality&lt;/code&gt; method add more calls, and one of the call adds &lt;code&gt;:decorated?&lt;/code&gt; string. All of this chain have a cost.&lt;/p&gt;

&lt;p&gt;Let’s measure it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring allocation with Draper, without Draper, without concatenation
&lt;/h2&gt;

&lt;h2&gt;
  
  
  With Draper (&lt;a href="https://github.com/benoittgt/ar_and_draper_allocation"&gt;branch&lt;/a&gt;)
&lt;/h2&gt;

&lt;p&gt;Draper gem installed. No decorators defined but &lt;code&gt;==&lt;/code&gt; is overridden by Draper as we can see.&lt;/p&gt;


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



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


&lt;p&gt;If we run a measure with &lt;code&gt;memory_profiler&lt;/code&gt;: &lt;code&gt;rails concat_records:comments[2000]&lt;/code&gt;. We get:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Total allocated: 327.93 MB (4862043 objects)

Total retained:  17.67 MB (172370 objects)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And we can see also:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Allocated String Report

-----------------------------------

1999000  "decorated?"

1999000  .rvm/gems/ruby-2.5.3/gems/activerecord-5.2.1/lib/active_record/attribute_methods.rb:282
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Without Draper (&lt;a href="https://github.com/benoittgt/ar_and_draper_allocation/tree/no_draper"&gt;branch&lt;/a&gt;)
&lt;/h2&gt;

&lt;p&gt;First we don’t have any more the very present allocated string &lt;code&gt;"decorated?"&lt;/code&gt;. First allocated string is &lt;code&gt;””&lt;/code&gt; with 40185 occurrences. That’s a gain of 1 958 815 strings.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Allocated String Report

-----------------------------------

40185  ""

24000  .rvm/gems/ruby-2.5.3/gems/activesupport-5.2.1/lib/active_support/tagged_logging.rb:23
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If we run a measure with &lt;code&gt;memory_profiler&lt;/code&gt; &lt;code&gt;rails&lt;/code&gt; &lt;code&gt;concat_records:comments[2000]&lt;/code&gt;. We get:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Total allocated: 247.97 MB (2863011 objects)

Total retained:  17.67 MB (172370 objects)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It’s little bit better.&lt;/p&gt;
&lt;h2&gt;
  
  
  Without concat but with Draper (&lt;a href="https://github.com/benoittgt/ar_and_draper_allocation/tree/draper_no_concat"&gt;branch&lt;/a&gt;)
&lt;/h2&gt;

&lt;p&gt;Same as before without Draper we don’t have any more very present allocated string &lt;code&gt;"decorated?"&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Allocated String Report

-----------------------------------

40185  ""

24000  .rvm/gems/ruby-2.5.3/gems/activesupport-5.2.1/lib/active_support/tagged_logging.rb:23
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And…&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Total allocated: 218.73 MB (2377978 objects)

Total retained:  31.95 kB (284 objects)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is a huge gain but we don’t validate that we don’t create a duplicated entry into the join table. But this is something easy to handle.&lt;/p&gt;
&lt;h2&gt;
  
  
  How this can happening?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If their is a &lt;code&gt;distinct&lt;/code&gt; on the association scope 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;&lt;p&gt;Then when ActiveRecord needs to concatenate. It will call &lt;code&gt;@target.index(record)&lt;/code&gt;. If &lt;code&gt;@target&lt;/code&gt; have a lot's of elements this will consume more some CPU and memory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And if Draper is installed this will add extra cost because Draper override &lt;code&gt;==&lt;/code&gt; called from &lt;code&gt;index&lt;/code&gt;. This override will allocate extra strings we don't need (&lt;code&gt;"decorated?"&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Looking in &lt;code&gt;index&lt;/code&gt; plus using Draper is a logarithmic cost for iteration per second and memory. More element you have in &lt;code&gt;"target"&lt;/code&gt; slower it gets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So careful when using &lt;code&gt;distinct&lt;/code&gt; in a scope. Also careful with Draper.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is a diagram of what is happening&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YSm56BZM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AB-2lE_aQCJpVcW1Iv2CRng.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YSm56BZM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AB-2lE_aQCJpVcW1Iv2CRng.png" alt="Diagram of index search and Draper override"&gt;&lt;/a&gt;&lt;em&gt;Diagram of index search and Draper override&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In our code base the patch looks like:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;Thanks to Appaloosa Team, Christophe Maximin, Ryuta Kamizono (kamipo), Nate Berkopec and the Rails Speed Community Slack.&lt;/p&gt;

&lt;p&gt;👋&lt;/p&gt;

&lt;p&gt;You should subscribe to our blog to be notified whenever a new article is available!&lt;/p&gt;

&lt;p&gt;This article was written by &lt;a href="https://www.twitter.com/benoit_tgt"&gt;Benoit Tigeot&lt;/a&gt; of &lt;a href="https://www.appaloosa-store.com/"&gt;Appaloosa&lt;/a&gt;’s dev team.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>activerecord</category>
      <category>draper</category>
    </item>
  </channel>
</rss>
