<?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: fedeagripa</title>
    <description>The latest articles on Forem by fedeagripa (@fedeagripa).</description>
    <link>https://forem.com/fedeagripa</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%2F396790%2F510e9cbe-0d74-4948-92e1-d60cdb87ff9c.png</url>
      <title>Forem: fedeagripa</title>
      <link>https://forem.com/fedeagripa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/fedeagripa"/>
    <language>en</language>
    <item>
      <title>Rails review on 2023</title>
      <dc:creator>fedeagripa</dc:creator>
      <pubDate>Tue, 03 Jan 2023 09:47:55 +0000</pubDate>
      <link>https://forem.com/fedeagripa/rails-review-on-2023-1akn</link>
      <guid>https://forem.com/fedeagripa/rails-review-on-2023-1akn</guid>
      <description>&lt;p&gt;As Ruby on Rails 7 is the latest version of the popular web application framework, and it includes a number of new features and improvements. Here are some of the key features that you can look forward to when using Rails 7:&lt;/p&gt;

&lt;p&gt;Action Mailbox: This new feature allows you to route incoming emails to controller-like mailboxes, where you can process them and take further action. This makes it easy to build applications that interact with email in a more automated way.&lt;/p&gt;

&lt;p&gt;Action Text: This feature provides rich text editing capabilities for your applications, making it easy to create and edit formatted text. It uses the Trix editor, which is a lightweight and flexible WYSIWYG editor.&lt;/p&gt;

&lt;p&gt;Multiple DB Support: Rails 7 now supports working with multiple databases in a single application, which can be useful in certain situations where you need to scale horizontally.&lt;/p&gt;

&lt;p&gt;Parallel Testing: This feature allows you to run your test suite in parallel, which can significantly reduce the time it takes to run your tests.&lt;/p&gt;

&lt;p&gt;Action Cable Testing: Action Cable is a feature that allows you to build real-time features into your applications, and Rails 7 now includes support for testing these features.&lt;/p&gt;

&lt;p&gt;Custom Action Options: You can now define custom options for your controller actions, which can be used to specify additional behavior or requirements.&lt;/p&gt;

&lt;p&gt;Time Zone Support: Rails 7 includes improved support for working with time zones, including the ability to set a default time zone for your application.&lt;/p&gt;

&lt;p&gt;Overall, Rails 7 includes a number of new features and improvements that make it easier to build and maintain high-quality applications. If you're using an earlier version of Rails, upgrading to Rails 7 is definitely worth considering.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>AWS Bad decisions: impossible to migrate</title>
      <dc:creator>fedeagripa</dc:creator>
      <pubDate>Tue, 22 Feb 2022 17:08:58 +0000</pubDate>
      <link>https://forem.com/fedeagripa/aws-bad-decisions-impossible-to-migrate-inf</link>
      <guid>https://forem.com/fedeagripa/aws-bad-decisions-impossible-to-migrate-inf</guid>
      <description>&lt;p&gt;&lt;strong&gt;TLTR: Due to so many restrictions AWS is generating a downtime of 3-4+ hours in my production site due to needed maintenance/migration.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm in a situation where as a founder I need to transition my AWS infrastructure from my personal account to a company domain one. You may say this is only my problem, but let say you need to migrate a bucket or a site from one account to other, now that sounds more casual, right?&lt;/p&gt;

&lt;p&gt;Well, the thing is my infrastructure is a static web site hosted in AWS in a bucket. On top of that I have CloudFront &amp;amp; Cloudflare so my guess for a production site was that last two were going to be the problematic ones.&lt;/p&gt;

&lt;p&gt;Unfortunately Amazon made the amazing decision to tight buckets to the creator account and don't provide any way to migrate or provide others ownership. On top of that they have the terrible restriction that bucket names are unique worldwide, so on top of I need to create buckets with the domain name to work properly now I'm completely blocked.&lt;/p&gt;

&lt;p&gt;The only solution Amazon support provides me is to create a new bucket with a silly name in the new account, erase the old one and wait an unknown amount of hours for replicas to see the bucket was erased. So that means I will be working 3+ hours on a super simple migration and the production site will be down with all the implications that have for a content delivery company.&lt;/p&gt;

&lt;p&gt;Thoughts? My thinking is that AWS is making so many wrong decisions and they are taking the wrong track about how to provide service to tech companies.&lt;/p&gt;

&lt;p&gt;They need to provide flexibility just as they claim internally&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Rails 7 - Sneak Peek [part1]</title>
      <dc:creator>fedeagripa</dc:creator>
      <pubDate>Fri, 03 Sep 2021 20:58:06 +0000</pubDate>
      <link>https://forem.com/fedeagripa/rails-7-sneak-peek-part1-2j71</link>
      <guid>https://forem.com/fedeagripa/rails-7-sneak-peek-part1-2j71</guid>
      <description>&lt;h3&gt;
  
  
  New method
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# replaces the common pattern of finding sibling objects without one self:&lt;/span&gt;
&lt;span class="n"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;excluding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;another_post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Lets see how we can use it
&lt;/h3&gt;

&lt;p&gt;A simple example for this shows can you simply filter other posts in your usually built onboarding project and it seems quite cool&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;simliar_posts&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;excluding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Lets actually think if it useful in a common place
&lt;/h3&gt;

&lt;p&gt;But wait a second... we have a really messy implementation of &lt;code&gt;BROADCAST&lt;/code&gt; in ActionCable where we need to filter our own sent messages everytime, and this new friend may look promising to use.&lt;br&gt;
What if we implement a &lt;code&gt;MULTICAST&lt;/code&gt;, or even add a parameter to our usual &lt;code&gt;broadcast_to&lt;/code&gt; method in ActionCable making it look like this &lt;a href="https://github.com/rails/rails/blob/a44fbb5dcacd3281116f7d9881a25e8f08f729a4/actioncable/lib/action_cable/server/broadcasting.rb#L42"&gt;source&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;exclude_self: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"[ActionCable] Broadcasting to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;broadcasting&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;truncate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;broadcasting: &lt;/span&gt;&lt;span class="n"&gt;broadcasting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;message: &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;coder: &lt;/span&gt;&lt;span class="n"&gt;coder&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"broadcast.action_cable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;encoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;coder&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;coder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exclude_self&lt;/span&gt;
      &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pubsub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;broadcast&lt;/span&gt; &lt;span class="n"&gt;broadcasting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoded&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pubsub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;broadcast&lt;/span&gt; &lt;span class="n"&gt;broadcasting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoded&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

</description>
      <category>rails</category>
      <category>programming</category>
      <category>opensource</category>
      <category>news</category>
    </item>
    <item>
      <title>ActiveStorage - default conf is wrong?</title>
      <dc:creator>fedeagripa</dc:creator>
      <pubDate>Fri, 14 May 2021 17:20:26 +0000</pubDate>
      <link>https://forem.com/fedeagripa/activestorage-default-conf-is-wrong-2j2c</link>
      <guid>https://forem.com/fedeagripa/activestorage-default-conf-is-wrong-2j2c</guid>
      <description>&lt;p&gt;Lately I been working a lot with ActiveStorage and I found that every-time I try to edit my content (images, videos, etc) they are overwritten O_o, yes WHAT?&lt;/p&gt;

&lt;p&gt;But if you just change your config to:&lt;br&gt;
&lt;code&gt;config.active_storage.replace_on_assign_to_many = false&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now you have what I understand as a normal behaviour, you can do a standard Update according CRUD. &lt;/p&gt;

&lt;p&gt;I believe this was done like this just because it was simple, but do you consider it wrong also?&lt;/p&gt;

</description>
      <category>rails</category>
      <category>discuss</category>
      <category>activestorage</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Reports performance improvement</title>
      <dc:creator>fedeagripa</dc:creator>
      <pubDate>Thu, 20 Aug 2020 14:42:18 +0000</pubDate>
      <link>https://forem.com/fedeagripa/reports-performance-improvement-p2g</link>
      <guid>https://forem.com/fedeagripa/reports-performance-improvement-p2g</guid>
      <description>&lt;p&gt;Are you tired of fighting with the performance of your reports and overloading your database? &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/q1MeAPDDMb43K/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/q1MeAPDDMb43K/giphy.gif" alt="Fight"&gt;&lt;/a&gt;&lt;br&gt;
Well, here I bring you a really simple Rails 6 solution!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use new Rails6 Multiple Databases configuration&lt;br&gt;
To do so you can check my other &lt;a href="https://dev.to/fedeagripa/rails-6-multiple-databases-337k"&gt;post&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Switch your reports to use the replica database&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As an extra you can also use &lt;code&gt;readonly_slow&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connected_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;database: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;readonly_slow: :replica&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# runs a long query while connected to the +replica+ using the readonly_slow role.&lt;/span&gt;
  &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_a_huge_query&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Voila, that's all!
&lt;img src="https://i.giphy.com/media/13CoXDiaCcCoyk/giphy.gif" alt="voila"&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>database</category>
      <category>sql</category>
    </item>
    <item>
      <title>Rails 6 - Multiple Databases</title>
      <dc:creator>fedeagripa</dc:creator>
      <pubDate>Wed, 19 Aug 2020 19:19:34 +0000</pubDate>
      <link>https://forem.com/fedeagripa/rails-6-multiple-databases-337k</link>
      <guid>https://forem.com/fedeagripa/rails-6-multiple-databases-337k</guid>
      <description>&lt;p&gt;Rails 6.0 came up with this amazing enhancement, but for most people, I'm aware they think that is a new feature.&lt;br&gt;
In my opinion, both are correct, because the actual state of multiple databases before rails 6.0 was not even useful to consider it a completed feature.&lt;/p&gt;

&lt;p&gt;Let's dive into Rails &amp;lt; 6 state, Rails 6 introduced changes and the current roadmap.&lt;/p&gt;
&lt;h2&gt;
  
  
  Rails 5 state
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/Gir0ioTOwzjdC/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/Gir0ioTOwzjdC/giphy.gif" alt="Past" width="245" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ActiveRecord supports multiple databases, but Rails &amp;lt; 6 doesn't provide a way to manage them.&lt;br&gt;
As said you needed to handle everything (yes, everything) in a really manual way, having to create your own tasks for &lt;code&gt;seeds&lt;/code&gt;, &lt;code&gt;db:prepare&lt;/code&gt;, &lt;code&gt;db:migrate&lt;/code&gt;, etc.&lt;br&gt;
Then you needed to create a sort of generator to handle your secondary database migrations following conventions. And finally, as if you hadn't done enough custom work, create an initializer to config your connection to the new database.&lt;/p&gt;

&lt;p&gt;So yes, ActiveRecord supported multiple databases, but it was a complete and real mess you didn't want to get into unless really necessary.&lt;/p&gt;
&lt;h2&gt;
  
  
  Rails 6.0 state
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3o7WTxQ7RFErPLfOqk/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3o7WTxQ7RFErPLfOqk/giphy.gif" alt="Now" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  New stuff
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Multiple primary databases and a replica for each&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is amazing, now you can easiliy configure it in your &lt;code&gt;database.yml&lt;/code&gt; like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="ss"&gt;development:
  primary:
    database: &lt;/span&gt;&lt;span class="n"&gt;my_primary_database&lt;/span&gt;
    &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;
    &lt;span class="ss"&gt;adapter: &lt;/span&gt;&lt;span class="n"&gt;sqlite3&lt;/span&gt;
  &lt;span class="ss"&gt;primary_replica:
    database: &lt;/span&gt;&lt;span class="n"&gt;my_primary_database&lt;/span&gt;
    &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;root_readonly&lt;/span&gt;
    &lt;span class="ss"&gt;adapter: &lt;/span&gt;&lt;span class="n"&gt;sqlite3&lt;/span&gt;
    &lt;span class="ss"&gt;replica: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="ss"&gt;animals:
    database: &lt;/span&gt;&lt;span class="n"&gt;my_animals_database&lt;/span&gt;
    &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;animals_root&lt;/span&gt;
    &lt;span class="ss"&gt;adapter: &lt;/span&gt;&lt;span class="n"&gt;sqlite3&lt;/span&gt;
    &lt;span class="ss"&gt;migrations_paths: &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;animals_migrate&lt;/span&gt;
  &lt;span class="ss"&gt;animals_replica:
    database: &lt;/span&gt;&lt;span class="n"&gt;my_animals_database&lt;/span&gt;
    &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;animals_readonly&lt;/span&gt;
    &lt;span class="ss"&gt;adapter: &lt;/span&gt;&lt;span class="n"&gt;sqlite3&lt;/span&gt;
    &lt;span class="ss"&gt;replica: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check how you can define your migration paths for each database, and how seem to have the power to have multiple adapters (still working on a POC for this assumption).&lt;br&gt;
An example migration with this would be: &lt;code&gt;rails g migration CreateCats name:string --database animals&lt;/code&gt; with this output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  Running via Spring preloader &lt;span class="k"&gt;in &lt;/span&gt;process 97210
    invoke  active_record
    create    db/animals_migrate/20200819181625_create_cats.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Automatic connection switching for the model you're working with&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You just need to activate it in your middleware for automatic switching:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active_record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;database_selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;delay: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seconds&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active_record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;database_resolver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Middleware&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DatabaseSelector&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Resolver&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active_record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;database_resolver_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Middleware&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DatabaseSelector&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Resolver&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Session&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also manually decide your connection context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connected_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;role: :reading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# all code in this block will be connected to the reading role&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Automatic swapping between the primary and replica depending on the HTTP verb and recent writes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As we are used to in Rails, this comes standardized:&lt;/p&gt;

&lt;p&gt;“If the application is receiving a POST, PUT, DELETE, or PATCH request the application will automatically write to the primary. For the specified time after the write, the application will read from the primary. For a GET or HEAD request, the application will read from the replica unless there was a recent write.“&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rails tasks for creating, dropping, migrating, and interacting with the multiple databases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First big comment, the usual &lt;code&gt;rails db:create&lt;/code&gt; will now create both databases, as so the usual &lt;code&gt;rails db:migrate&lt;/code&gt; will migrate both databases too.&lt;br&gt;
You need to specify which database you want to work with like &lt;code&gt;rails db:create:primary&lt;/code&gt; or &lt;code&gt;rails db:migrate:secondary&lt;/code&gt; if you want to just modify one.&lt;/p&gt;

&lt;p&gt;So for your custom tasks you may need to also specify the desired database you are working with.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pending ones
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Sharding&lt;br&gt;
Right now there are some gems to accomplish this, so until is integrated onto Rails we should keep using gems (no real deal with it)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Joining across clusters&lt;br&gt;
This is what Rails state:&lt;br&gt;
“Applications cannot join across databases. Rails 6.1 will support using has_many relationships and creating 2 queries instead of joining, but Rails 6.0 will require you to split the joins into 2 selects manually.“&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So we just need to wait for rails 6.1!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Load balancing replicas&lt;br&gt;
This seems an interesting feature, so I hope to see upcoming news for this.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dumping schema caches for multiple databases&lt;br&gt;
Right now this is what is stated:&lt;br&gt;
“If you use a schema cache and multiple databases you'll need to write an initializer that loads the schema cache from your app. This wasn't an issue we could resolve in time for Rails 6.0 but hope to have it in a future version soon“&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So seems like a trivial issue to solve in upcoming versions&lt;/p&gt;

&lt;h2&gt;
  
  
  Rails 6.1 upcoming state
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/l46CaUs3XUKHaPmak/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l46CaUs3XUKHaPmak/giphy.gif" alt="Upcoming" width="480" height="270"&gt;&lt;/a&gt;&lt;br&gt;
As you may know, rails 6.1 is almost there so I will double-check what was stated to be released for this new version, and what is actually going to be released&lt;/p&gt;

&lt;p&gt;Nice open PRs right now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/rails/rails/pull/36886"&gt;Infer migrations_paths from database name&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rails/rails/pull/38721"&gt;Sharding + configuration DSL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the summary would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sharding is now a WIP thing and we may hope to see it live in Rails 6.1&lt;/li&gt;
&lt;li&gt;Still waiting for the Joining across clusters (or I missed it in my review)&lt;/li&gt;
&lt;li&gt;Still waiting for load balancing&lt;/li&gt;
&lt;li&gt;Schema caches are now a thing!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Some personal ideas
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/CLkW1CgQA5xwA/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/CLkW1CgQA5xwA/giphy.gif" alt="Dreams" width="400" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm a huge fan of non-relational databases, so I would love to see some mixins of MongoDB + Postgres for example. And with this introduced changes I think that the road is prepared enough to start working on a way to properly work with it.&lt;br&gt;
My doubts are more into "Should ActiveRecord really support non-relational databases?" or "Is something more rails work outside ActiveRecord?". Leave your thoughts and I will be more than happy to chat over it :)&lt;/p&gt;

</description>
      <category>rails</category>
      <category>database</category>
      <category>beginners</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Full Stack developer</title>
      <dc:creator>fedeagripa</dc:creator>
      <pubDate>Fri, 24 Jul 2020 21:30:14 +0000</pubDate>
      <link>https://forem.com/fedeagripa/full-stack-developer-37gb</link>
      <guid>https://forem.com/fedeagripa/full-stack-developer-37gb</guid>
      <description>&lt;p&gt;Is this an actual role nowadays? Are they real or just unicorns? When are you really full-stack?&lt;/p&gt;

&lt;p&gt;We just released a podcast chapter about this at Rootstrap &lt;a href="https://open.spotify.com/episode/7xpHCOPO9Nm1QrCoRgrRN9?si=xHzYmLnaQWeJ_UuMvHXmWg"&gt;Full Stack podcast&lt;/a&gt; - Friendly spoiler, is in Spanish :)&lt;/p&gt;

&lt;p&gt;Besides being a company podcast, we want it to be developer friendly (from developers to developers)&lt;br&gt;
I will be happy to hear any opinion/feedback/experiences :)&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>javascript</category>
      <category>discuss</category>
      <category>podcast</category>
    </item>
    <item>
      <title>GROWTH - Data-Driven Decisions</title>
      <dc:creator>fedeagripa</dc:creator>
      <pubDate>Fri, 24 Jul 2020 19:42:25 +0000</pubDate>
      <link>https://forem.com/fedeagripa/growth-data-driven-decisions-4nh5</link>
      <guid>https://forem.com/fedeagripa/growth-data-driven-decisions-4nh5</guid>
      <description>&lt;p&gt;Have you ever said one of these tricky phrases?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I'm sure this feature is going to rock!&lt;/li&gt;
&lt;li&gt;We need this feature before going live, it is a must!&lt;/li&gt;
&lt;li&gt;Users will use this for sure!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Well, if you found yourself saying any of those without any numbers that support you, you may probably be making a lot of assumptions.&lt;br&gt;
So is like you are playing roulette, due to lacking data to prove your hypothesis.&lt;br&gt;
Give growth a try! And find out how to move smarter with your development team along with really learning what your users want!&lt;/p&gt;

&lt;h2&gt;
  
  
  What's GROWTH?
&lt;/h2&gt;

&lt;p&gt;I see growth as a way of working around data analysis based decisions. So first let's explain a bit what data analysis is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Data analysis is defined as a process of cleaning, transforming, and modeling data to discover useful information for business decision-making. The purpose of Data Analysis is to extract useful information from data and taking the decision based upon the data analysis&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You may see that the word &lt;code&gt;data&lt;/code&gt; came up a lot, that's not because I want to be redundant but because I want to emphasize it as a really important concept. Nowadays the more data you have the more power you have to make better decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to start?
&lt;/h2&gt;

&lt;p&gt;As I mentioned before, start gathering data, it doesn't need to be super high-quality data, you will start learning from your users and your business reality and improve step by step.&lt;br&gt;
A simple step you can take at a really low cost is adding some forms and analytics to your landing page. This way you will start getting feedback from users and also start knowing your user's interactions and interests.&lt;/p&gt;

&lt;p&gt;First, see an initial approach on how you should approach your changes:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fencrypted-tbn0.gstatic.com%2Fimages%3Fq%3Dtbn%253AANd9GcRPmFzScUp3fnyzQ2vspcC4s_ejH-0LL5Cs_A%26usqp%3DCAU" 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%2Fencrypted-tbn0.gstatic.com%2Fimages%3Fq%3Dtbn%253AANd9GcRPmFzScUp3fnyzQ2vspcC4s_ejH-0LL5Cs_A%26usqp%3DCAU" alt="build measure learn"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will start building something as small as possible to lessen the number of mistakes you can make.&lt;br&gt;
Then you measure it by testing it with users since they are the only ones with the truth on whether it will succeed or not.&lt;br&gt;
Finally, learn from your measures and keep building, but now with a clearer idea of what users want.&lt;/p&gt;

&lt;p&gt;To plan what to build you can follow this simple process, also to manage each specific change.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1200%2F1%2ApLN8zszWI4hhhqf0g01C8g.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%2Fmiro.medium.com%2Fmax%2F1200%2F1%2ApLN8zszWI4hhhqf0g01C8g.jpeg" alt="hypothesis process"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write down your hypothesis based on your business goals&lt;/li&gt;
&lt;li&gt;Plan tests on your product to validate your hypothesis&lt;/li&gt;
&lt;li&gt;Measure&lt;/li&gt;
&lt;li&gt;Verify your numbers against what you thought&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most important step at the end is to make decisions based on your learnings, adjusting your upcoming features/hypothesis based on learnings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;h4&gt;
  
  
  First and most usual mistake: assuming you know how users will behave
&lt;/h4&gt;

&lt;p&gt;You will probably be wrong, instead of that write a hypothesis and try to prove it. It can be as simple as "I want to prove that I get more sales if I place the BUY button at the top with a brighter color" or more complex like "I think users are tempted to buy if I offer discounts after 10 minutes of looking at items in my store"&lt;/p&gt;

&lt;h4&gt;
  
  
  Misread data
&lt;/h4&gt;

&lt;p&gt;Actually is really easy to fall on this error, as you have an idea in your mind and naturally, you want it to end up with that being truth.&lt;br&gt;
So my proposal for this is to again write down your ideas based on hypothesis and set up expected metrics for each of them, so you can properly check against something objectively.&lt;/p&gt;

&lt;p&gt;Here are some tips to prevent falling into misreading data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write down your findings, don't make decisions on the go (just reading data)&lt;/li&gt;
&lt;li&gt;Do multiple rounds of data check (like reading something multiple times before releasing)

&lt;ul&gt;
&lt;li&gt;First round: check the more granular data&lt;/li&gt;
&lt;li&gt;Second one: try to detect patterns or contradictions between previous deductions&lt;/li&gt;
&lt;li&gt;Third one: take into account the "big picture", you are analyzing data and users under a certain context, so think about how your context may compromise your data&lt;/li&gt;
&lt;li&gt;Last one: remember you are evaluating a subset of your universe, so consider you may have some deviation and so don't make absolute decisions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  Think you have finished
&lt;/h4&gt;

&lt;p&gt;This is a continuous delivery process that never ends. User preferences changes and you can always detect new opportunities or tends to look at data.&lt;br&gt;
I see this as a simple process that prevents you to take wrong decisions, so you only can win from using it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do I need an engineer?
&lt;/h2&gt;

&lt;p&gt;Actually you don't, but having one will provide you a different perspective from a structured data result mindset. Let's see what does this means...&lt;br&gt;
You as a product owner, sales manager, investor, etc. will probably be the one with the most knowledge about users and your product, but, do you know how to measure your product performance and record your user's interactions over your app? Probably not.&lt;br&gt;
So here is where an engineer can help you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding metrics on your app&lt;/li&gt;
&lt;li&gt;Recording user journeys to provide you a better understanding of their experience&lt;/li&gt;
&lt;li&gt;Crossing information from different inputs (social media, ads, etc)&lt;/li&gt;
&lt;li&gt;Generating reports&lt;/li&gt;
&lt;li&gt;Processing data so you can take more time on your business instead of the product development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And honestly depending on the level of expertise of the engineer I can continue providing good reasons to work with one of them.&lt;br&gt;
So my recommendation for you is to look for a well-rounded engineer, not just a developer. That will help you look at your business from more points of view.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Mindset
&lt;/h4&gt;

&lt;p&gt;Be curious, be analytical, and try to validate as much as possible as you introduce changes.&lt;/p&gt;

&lt;h4&gt;
  
  
  Changes
&lt;/h4&gt;

&lt;p&gt;Changes are good! Make them as much as possible so you learn more, but keep them organized so it doesn't become anarchic. &lt;/p&gt;

&lt;h4&gt;
  
  
  Organization
&lt;/h4&gt;

&lt;p&gt;"Diversity is a source of wealth" we all know that, so the only problem to deal with is how to work with other roles while keeping an open mindset.&lt;br&gt;
Invest some time explaining your team what you want and why. People understanding what they are doing is way more effective.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>dataanalysis</category>
      <category>discuss</category>
      <category>startup</category>
    </item>
    <item>
      <title>ActionText, bringing rich text content management to Rails Core</title>
      <dc:creator>fedeagripa</dc:creator>
      <pubDate>Wed, 10 Jun 2020 20:31:26 +0000</pubDate>
      <link>https://forem.com/fedeagripa/actiontext-bringing-rich-text-content-management-to-rails-core-5cm1</link>
      <guid>https://forem.com/fedeagripa/actiontext-bringing-rich-text-content-management-to-rails-core-5cm1</guid>
      <description>&lt;p&gt;ActionText is a new feature developed in Rails 6 that aims to solve managing rich text content on apps on an easy way following conventions. At first sight, this may seem like a trivial and insignificant feature, but you will see that inside of it you have some exciting things that may do your coding easier.&lt;/p&gt;

&lt;p&gt;The purpose of this post is to introduce this new feature, explaining the specific case it was build for and the provide a personal opinion about the potential of it.&lt;/p&gt;

&lt;p&gt;“Action Text brings rich text content and editing to Rails. It includes the Trix editor that handles everything from formatting to links to quotes to lists to embedded images and galleries. The rich text content generated by the Trix editor is saved in its own RichText model that’s associated with any existing Active Record model in the application. Any embedded images (or other attachments) are automatically stored using Active Storage and associated with the included RichText model.” [1]&lt;/p&gt;

&lt;h1&gt;
  
  
  But… what actually is this all about?
&lt;/h1&gt;

&lt;p&gt;The most straightforward implementation (and what it was first build for) is to manage content on web apps as you are used in Word (or Open Office) :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Mk2EMUvk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/proxy/2g1byzHLBjzZq0-LIL5mizU6YvuzFLcNbIbpNOoWzhVOx2WrQBnZMiEqQnAdgKJxgqKKhX7E9tHb5vf7TwTo02vMJNfiEEwG0VTDFwz7OGhUQAorZ3j2O5lMkA" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Mk2EMUvk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/proxy/2g1byzHLBjzZq0-LIL5mizU6YvuzFLcNbIbpNOoWzhVOx2WrQBnZMiEqQnAdgKJxgqKKhX7E9tHb5vf7TwTo02vMJNfiEEwG0VTDFwz7OGhUQAorZ3j2O5lMkA" alt="Markdown editor" width="500" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This may seem trivial at first sight, but trust me you will have a lot of headaches with the many cases you should consider and the time it will consume. With ActionText you will just spend 10 minutes.&lt;br&gt;
&lt;a href="https://i.giphy.com/media/U24Ize3546YqEdlJ95/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/U24Ize3546YqEdlJ95/giphy.gif" alt="doubt" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  How do I implement it?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/ule4vhcY1xEKQ/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/ule4vhcY1xEKQ/giphy.gif" alt="code" width="480" height="480"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Use ActiveStorage variant&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'image_processing'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need image_processing to deal with blobs automatically, this is another amazing thing provided here!. After running  bundle this will generate 3 new tables: action_text_rich_texts, active_storage_attachments, active_storage_blobs.&lt;/p&gt;

&lt;p&gt;Your model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;has_rich_text&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running migrations this will just generate a string column on your model, but don’t worry, it will use the previous tables too.&lt;/p&gt;

&lt;p&gt;Your controller&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;post_params&lt;/span&gt;
  &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your view&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"field"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s it, you have an app capable of storing chat messages, posts, images and any other rich text implementation you want with just a few lines.&lt;/p&gt;

&lt;h1&gt;
  
  
  And… what happens if I’m just a backend developer?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Gvc5fY7r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i0.wp.com/www.rootstrap.com/blog/wp-content/uploads/2018/12/screen-shot-2018-12-06-at-11-04-16.png%3Fw%3D352%26ssl%3D1" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gvc5fY7r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i0.wp.com/www.rootstrap.com/blog/wp-content/uploads/2018/12/screen-shot-2018-12-06-at-11-04-16.png%3Fw%3D352%26ssl%3D1" alt="BE dev" width="352" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will tend to think that there is just value in has_rich_text :content, because if you actually try to expose the content param your Front-end developer will have to generate something like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hVgW1WSY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i2.wp.com/www.rootstrap.com/blog/wp-content/uploads/2018/12/screen-shot-2018-12-05-at-16-08-55.png%3Fw%3D669%26ssl%3D1" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hVgW1WSY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i2.wp.com/www.rootstrap.com/blog/wp-content/uploads/2018/12/screen-shot-2018-12-05-at-16-08-55.png%3Fw%3D669%26ssl%3D1" alt="data-attachment" width="669" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The solution provided up to now to solve this is to make your API accept attachments markup in canonicalized form  (e.g. ) instead of the full Trix-compatible markup (like above). To achieve this you would need Basecamp Doc&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/12NUbkX6p4xOO4/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/12NUbkX6p4xOO4/giphy.gif" alt="magic" width="275" height="252"&gt;&lt;/a&gt;&lt;br&gt;
This is a handy feature if you are building a full-stack Rails app, and for API development you would need to ask your Front-end developer to provide some specific information, anyway, it seems extremely useful as you are dealing with all content types automatically then.&lt;/p&gt;

&lt;p&gt;Last but not less important, this is a feature under development yet so there will be some changes and improvements in the near future.&lt;/p&gt;

&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;p&gt;[1] &lt;a href="https://github.com/rails/actiontext"&gt;https://github.com/rails/actiontext&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>Online payments made SIMPLE - How to work with Stripe</title>
      <dc:creator>fedeagripa</dc:creator>
      <pubDate>Tue, 09 Jun 2020 16:22:41 +0000</pubDate>
      <link>https://forem.com/fedeagripa/online-payments-made-simple-how-to-work-with-stripe-34p2</link>
      <guid>https://forem.com/fedeagripa/online-payments-made-simple-how-to-work-with-stripe-34p2</guid>
      <description>&lt;h1&gt;
  
  
  &lt;strong&gt;Online payments made SIMPLE - How to work with Stripe&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;In this blog post, you’ll learn how to start working with Stripe and quickly have fully functioning online payments in your apps.&lt;br&gt;
&lt;a href="https://i.giphy.com/media/xTiTnqUxyWbsAXq7Ju/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/xTiTnqUxyWbsAXq7Ju/giphy.gif" alt="Money" width="398" height="478"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  1) Why Stripe?
&lt;/h2&gt;

&lt;p&gt;Pros&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Easy to implement and use&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fast to develop, so your client will be happy&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solves most of your usual payment problems, so you don't lose time or clients (even worst)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Amazing dashboard with a lot of capabilities so your clients financial team can work along with you&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expensive (high % fee)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  2) INSTALLATION
&lt;/h2&gt;

&lt;p&gt;This post assumes you already created a Stripe account and so you have access to dashboard and its configuration.&lt;br&gt;
&lt;a href="https://i.giphy.com/media/xT5LMQPcARLvQuknaU/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/xT5LMQPcARLvQuknaU/giphy.gif" alt="Installation" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  RAILS
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Add these two gems:

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/stripe/stripe-ruby"&gt;Stripe&lt;/a&gt; to achieve the integration&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/stripe-ruby-mock/stripe-ruby-mock"&gt;Stripe Testing&lt;/a&gt; to test your integration, you don't want to end up writing lots of mocking classes, right?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Configure your keys &amp;amp; version from the Stripe dashboard
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/intializers/stripe.rb&lt;/span&gt;
&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stripe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;publishable_key: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'STRIPE_PUBLISHABLE_KEY'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="ss"&gt;secret_key: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'STRIPE_SECRET_KEY'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="no"&gt;Stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:secret_key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  REACT
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Add this package &lt;a href="https://github.com/stripe/react-stripe-elements"&gt;Stripe&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Configure your App to use the same api key as for rails (make sure it is the same, as you start moving between envs you may forget it). Remember that there is a testing key and a live one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add an env file to store your keys&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt;

&lt;span class="nx"&gt;STRIPE_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pk_test_TYooMQauvdEDq54NiTphI7jx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add your Stripe wrapper&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Elements&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;StripeProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-stripe-elements&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;withStripe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WrappedComponent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Stripe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;StripeProvider&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripe_key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Elements&lt;/span&gt;
        &lt;span class="nx"&gt;fonts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{[{&lt;/span&gt;
          &lt;span class="na"&gt;cssSrc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://fonts.googleapis.com/css?family=Roboto:300,300i,400,500,600&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;}]}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;WrappedComponent&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Elements&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/StripeProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Stripe&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;withStripe&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3) START USING PAYMENTS WITH STRIPE
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/l0IyjiXOXTX6Yemsg/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l0IyjiXOXTX6Yemsg/giphy.gif" alt="Start" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  CREDIT CARDS
&lt;/h3&gt;

&lt;h5&gt;
  
  
  REACT - DO YOURSELF A FAVOR AND USE THE EXISTING COMPONENT
&lt;/h5&gt;

&lt;p&gt;I'm not a fan of reinventing the wheel by any means, the design these components provide is more than enough for 99% of the apps you will be building. But if you insist, be prepared to spend 2 weeks dealing with details instead of 2 days.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;CardNumberElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;CardExpiryElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;CardCVCElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;injectStripe&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-stripe-elements&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;uuid&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uuid/v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/* Your other imports for a usual form */&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;BillingForm&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;cardInputKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onSubmit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;submitBilling&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shopId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialValues&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toJS&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="cm"&gt;/* AT THIS POINT THE CC IS CREATED AT STRIPE AND YOU NEED TO TELL YOUR BACKEND ABOUT IT */&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decamelizeKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;SubmissionError&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;_error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/* HERE WE WERE SUBMITING AND LENDING THE INFO TO THE BACKEND */&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;submitBilling&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shopId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;camelizeKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;initialValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* all your consts */&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;....&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p-3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="cm"&gt;/* The rest of your user profile form */&lt;/span&gt;
        &lt;span class="cm"&gt;/* CC real info */&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;col-lg-3 offset-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-control-label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Card&lt;/span&gt; &lt;span class="nx"&gt;On&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;brand&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;last4&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ending in &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;last4&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-control-label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Card&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CardNumberElement&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`cardNumber&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cardInputKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-control&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-control-label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Expiry&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CardExpiryElement&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`cardExpiry&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cardInputKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-control wd-80&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-control-label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;CVC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CardCVCElement&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`cardCvc&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cardInputKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-control wd-80&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/form&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;injectStripe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reduxForm&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;})(&lt;/span&gt;&lt;span class="nx"&gt;BillingForm&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  RAILS - DON'T TRY TO STORE ALL THE INFO (IT'S ILEGAL)
&lt;/h5&gt;

&lt;p&gt;You will tend to store more credit card info that you need. The only info that you need to store in your database (for basic usage) is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;customer_id&lt;/code&gt; : Stripe customer identifier that you will store in your User for example&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;card_id&lt;/code&gt; : Stripe card identifier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;token_id&lt;/code&gt; you will get from your frontend is a short lived token that is only needed for an atomic operation.&lt;/p&gt;

&lt;p&gt;Add a &lt;code&gt;customer_id&lt;/code&gt; field to your user (or Shop in next example).&lt;br&gt;
Add a &lt;code&gt;card_id&lt;/code&gt; to your user (or Shop in next example).&lt;/p&gt;

&lt;p&gt;Now take this service example (Shopify page example):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/services/stripe_service.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'stripe'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StripeService&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StripeException&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;StandardError&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:shop&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@shop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shop&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_card&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;create_customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;
    &lt;span class="n"&gt;card&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;source: &lt;/span&gt;&lt;span class="n"&gt;token_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;shop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;card_id: &lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;credit_card_info&lt;/span&gt;
    &lt;span class="n"&gt;card&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stripe_token&lt;/span&gt;
    &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_credit_card&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;card&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;source: &lt;/span&gt;&lt;span class="n"&gt;token_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
    &lt;span class="n"&gt;card_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
    &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;card_id&lt;/span&gt;
    &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
    &lt;span class="n"&gt;shop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;card_id: &lt;/span&gt;&lt;span class="n"&gt;card_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;customer&lt;/span&gt;
    &lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;customer_id&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;

    &lt;span class="vi"&gt;@customer&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;Stripe&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;customer_params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;description: &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;shop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="vi"&gt;@customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Stripe&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;shop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;customer_id: &lt;/span&gt;&lt;span class="vi"&gt;@customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this simple controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/api/v1/credit_cards_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Api&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;V1&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreditCardsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Api&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;V1&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ApiController&lt;/span&gt;
      &lt;span class="n"&gt;helper_method&lt;/span&gt; &lt;span class="ss"&gt;:shop&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
        &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;StripeService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="vi"&gt;@card&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;credit_card_info&lt;/span&gt;
        &lt;span class="vi"&gt;@customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;customer&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
        &lt;span class="no"&gt;StripeService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shop&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;add_card&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customer_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;token_id&lt;/span&gt;
        &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="ss"&gt;:no_content&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;
        &lt;span class="k"&gt;begin&lt;/span&gt;
          &lt;span class="no"&gt;StripeService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shop&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update_credit_card&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;StripeService&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;StripeException&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render_not_found&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="ss"&gt;:no_content&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="kp"&gt;private&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;shop&lt;/span&gt;
        &lt;span class="vi"&gt;@shop&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="n"&gt;current_shop&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;token_json&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:token&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;token_id&lt;/span&gt;
        &lt;span class="n"&gt;token_json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;email&lt;/span&gt;
        &lt;span class="n"&gt;token_json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;customer_name&lt;/span&gt;
        &lt;span class="n"&gt;token_json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's all! You can start charging your users now!&lt;br&gt;
&lt;a href="https://i.giphy.com/media/3o6wO4lpSaVQn1J8aI/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3o6wO4lpSaVQn1J8aI/giphy.gif" alt="Cash" width="480" height="360"&gt;&lt;/a&gt;&lt;br&gt;
All fraud detections and customer service actions can be managed directly from Stripe's dashboard.&lt;br&gt;
&lt;a href="https://i.giphy.com/media/ND6xkVPaj8tHO/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/ND6xkVPaj8tHO/giphy.gif" alt="Fraud" width="334" height="310"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  SUBSCRIPTIONS
&lt;/h3&gt;

&lt;p&gt;To create a subscription you need to define it, then create a product in Stripe (this last one is really clear looking at the dashboard, so I'm not going to explain it)&lt;/p&gt;
&lt;h5&gt;
  
  
  CREATING THE SUBSCRIPTION
&lt;/h5&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/subscription.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Subscription&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:purchase_plan&lt;/span&gt; &lt;span class="c1"&gt;# this can be optional if you have annual or monthly plans for example&lt;/span&gt;
  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:subscription_items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;dependent: :destroy&lt;/span&gt; &lt;span class="c1"&gt;# I'm going to explain this later&lt;/span&gt;

  &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'define_your_possible_statuses'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In that model you will store attributes like: &lt;code&gt;expires_at&lt;/code&gt;, &lt;code&gt;type&lt;/code&gt; or even &lt;code&gt;provider&lt;/code&gt; if later you want to extend to other providers like PayPal or Apple Pay&lt;/p&gt;

&lt;p&gt;Finally to create them on Stripe is quite simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/services/stripe_service.rb&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_subscription&lt;/span&gt;
  &lt;span class="no"&gt;Stripe&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="ss"&gt;customer: &lt;/span&gt;&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;plan: &lt;/span&gt;&lt;span class="n"&gt;subs_plan_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# this is the id of your plan (eg: monthly, annual, etc)&lt;/span&gt;
    &lt;span class="ss"&gt;coupon: &lt;/span&gt;&lt;span class="n"&gt;discount_code&lt;/span&gt; &lt;span class="c1"&gt;# if you have any (check COUPONS section below to understand them in more detail)&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  COUPONS
&lt;/h3&gt;

&lt;p&gt;Coupons are the abstract concept of &lt;code&gt;30% off&lt;/code&gt; for example, when you apply that coupon to a user that's called a &lt;code&gt;discount&lt;/code&gt;.&lt;br&gt;
So you should define some discounts on Stripe and store their ids in your database to apply them to users.&lt;br&gt;
There are two types of coupons &lt;code&gt;percentage&lt;/code&gt; &amp;amp; &lt;code&gt;fixed amount&lt;/code&gt;, and any of them can be one time only or have the capability to be applied multiple times. So when you try to apply a coupon to a subscription, for example, remember that it can fail if you reached the maximum usage number.&lt;/p&gt;

&lt;p&gt;Another useful case that is worth mentioning is to apply a coupon to a user, this means that they will have a positive balance for any future invoice (be careful if you charge users with multiple products)&lt;/p&gt;

&lt;h3&gt;
  
  
  SUBSCRIPTION ITEMS
&lt;/h3&gt;

&lt;p&gt;These are your billing items, so for the case of a web subscription, you will just have 1 subscription item. For specific cases like an amazon cart or any complicated use case (where you have multiple items being added to purchase) is where you have to start considering adding some specific logic to your app.&lt;br&gt;
I won't get really into detail about this, I just wanted to show the general concept behind this, maybe I will write more in detail in a future post.&lt;/p&gt;

&lt;h5&gt;
  
  
  RENEWALS
&lt;/h5&gt;

&lt;p&gt;Don't overthink it, there is a webhook for most of your use cases. But for this specific need you can configure the following events:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;customer.subscription.updated&lt;br&gt;
This event happens every time a susbscription is updated according to this &lt;a href="https://stripe.com/docs/billing/subscriptions/change"&gt;documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;customer.subscription.deleted&lt;br&gt;
As simple as it sounds, it tells you when a subscription is canceled so you can take the actions needed in your app (possibly disable the associated account)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;invoice.payment_succeeded&lt;br&gt;
This is a really important one! It tells us when payment is actually accepted by the credit card provider (some times there can be fraud or the payment could get declined)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  WEBHOOKS
&lt;/h3&gt;

&lt;p&gt;There are a lot of them and they will solve most of your problems, the only downcase is the headache trying to understand which exactly to use.&lt;br&gt;
I'm sorry to disappoint you if you reached here trying to answer this question but up to now I only know &lt;a href="https://stripe.com/docs/api/events/types"&gt;this page&lt;/a&gt; that explains the different existing webhooks and what they do. The other option is when you go to create a webhook from the developer's Stripe dashboard, they explain a bit more in detail what each event does.&lt;/p&gt;

&lt;h2&gt;
  
  
  4) SPECIAL RECOMMENDATIONS FOR FURTHER PAYMENT IMPLEMENTATION
&lt;/h2&gt;

&lt;p&gt;Keep these Stripe documentation pages as your friends:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stripe.com/docs/api/"&gt;Devs api&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stripe.com/docs/api/events/types"&gt;Events types&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometimes there are two or even three ways of solving a problem, so consider this and take your time to analyze each requirement properly before you start coding.&lt;/p&gt;

&lt;h2&gt;
  
  
  5) CONCLUSIONS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/hoxn9Vo7BW3RTxKRod/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/hoxn9Vo7BW3RTxKRod/giphy.gif" alt="Easy" width="384" height="480"&gt;&lt;/a&gt;&lt;br&gt;
You can easily add online payments to your app and test it in just 1 week (or so), that's amazing! The other amazing thing is that you can start managing most of the daily based situations like fraud of disputes just from the dashboard (you don't need to keep coding).&lt;/p&gt;

&lt;p&gt;The difficult part of this is when you start adding more concrete and detailed transactions and supporting multiple transfer types (like bank account transfers instead of just Visa or MasterCard). So if you liked this post and want to know more don't hesitate to leave some comments asking for it! or even text me :)&lt;/p&gt;

</description>
      <category>rails</category>
      <category>react</category>
      <category>ecommerce</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Active Admin 2FA with OneLogin</title>
      <dc:creator>fedeagripa</dc:creator>
      <pubDate>Mon, 08 Jun 2020 23:40:00 +0000</pubDate>
      <link>https://forem.com/fedeagripa/active-admin-2fa-with-onelogin-1l3p</link>
      <guid>https://forem.com/fedeagripa/active-admin-2fa-with-onelogin-1l3p</guid>
      <description>&lt;h1&gt;
  
  
  &lt;strong&gt;ActiveAdmin 2FA with OneLogin&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;In this blog post, you’ll learn how to configure two factor authentication with OneLogin for your admin panel.&lt;br&gt;
&lt;a href="https://i.giphy.com/media/3WdUt1PX5mlSo/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3WdUt1PX5mlSo/giphy.gif" alt="2FA" width="370" height="322"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;1) Some key concepts to security&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;SLO (Single log out): SLO is a process that allows users to be logged out in one place and spread it over multiple applications&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SSO (Single sign in): SSO is a process that allows users to authenticate into multiple services after logging into a primary service&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Callback URL: It is a local URL in your app where a third party auth provider sends you auth data like confirmations or logout requests&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;2) How to start implementing two factor authentication?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;First take a look at this lovely &lt;a href="https://github.com/apokalipto/devise_saml_authenticatable"&gt;gem&lt;/a&gt;, you will notice that all the examples are for the &lt;code&gt;User&lt;/code&gt; class, but don't worry we will show some examples for admin specifically&lt;/p&gt;

&lt;p&gt;The specific lines you need to add are just a few ones:&lt;/p&gt;

&lt;p&gt;First add the devise &lt;code&gt;saml_authenticable&lt;/code&gt; module from our choosen gem, this will inject most of the needed capabilities.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/admin_user.rb&lt;/span&gt;

&lt;span class="n"&gt;devise&lt;/span&gt; &lt;span class="ss"&gt;:recoverable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:rememberable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:trackable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:validatable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:lockable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:saml_authenticatable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then tell &lt;code&gt;saml_authenticable&lt;/code&gt; module which SAML fields you want to map to your model ones&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/attribute-map.yml&lt;/span&gt;

&lt;span class="s2"&gt;"urn:mace:dir:attribute-def:email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"email"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://i.giphy.com/media/xUOxfh6ZM75efM3Bqo/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/xUOxfh6ZM75efM3Bqo/giphy.gif" alt="Almost done" width="480" height="270"&gt;&lt;/a&gt;&lt;br&gt;
Add some configuration to your devise initializer to configure the communication between your third party provider and devise.&lt;br&gt;
You can customize the named routes generated in case of named route collisions with other Devise modules or libraries. Set the saml_route_helper_prefix to a string that will be appended to the named route.&lt;br&gt;
If saml_route_helper_prefix = 'saml' then the new_user_session route becomes new_saml_user_session&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/initializers/devise.rb&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saml_route_helper_prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'saml'&lt;/span&gt;
&lt;span class="n"&gt;callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;development?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'http://localhost:3000'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'SAML_CALLBACK_ADDRESS'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# SAML configuration&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saml_create_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saml_update_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saml_default_user_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:email&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saml_session_index_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:session_index&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saml_use_subject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;idp_settings_adapter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saml_configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertion_consumer_service_url&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/admin/saml/auth"&lt;/span&gt;
  &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertion_consumer_service_binding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"&lt;/span&gt;
  &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name_identifier_format&lt;/span&gt;             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"urn:oasis:names:tc:SAML:2.0:nameid-format:transient"&lt;/span&gt;
  &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;issuer&lt;/span&gt;                             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/admin/saml/metadata"&lt;/span&gt;
  &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authn_context&lt;/span&gt;                      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;idp_slo_target_url&lt;/span&gt;                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://company.onelogin.com/trust/saml2/http-redirect/slo/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;development?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'1234'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'SLO_TARGET'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;idp_sso_target_url&lt;/span&gt;                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://company.onelogin.com/trust/saml2/http-post/sso/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;development?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'you_sso_string'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'SSO_TARGET'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;idp_cert_fingerprint&lt;/span&gt;               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;development?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'your_cert_fingerprint'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'IDP_CERT_FINGERPRINT'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;idp_cert_fingerprint_algorithm&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'http://www.w3.org/2000/09/xmldsig#sha256'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we need to modify devise to use our new login strategy. To achieve this we will modify session management so it redirects to our third party provider like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/admin_users/sessions_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;AdminUsers&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SessionsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SessionsController&lt;/span&gt;
    &lt;span class="c1"&gt;# As you are overwriting devise session controller you need this to allow to login with user &amp;amp; pass (dev mode)&lt;/span&gt;
    &lt;span class="n"&gt;prepend_before_action&lt;/span&gt; &lt;span class="ss"&gt;:require_no_authentication&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="s1"&gt;'active_admin_logged_out'&lt;/span&gt;
    &lt;span class="n"&gt;helper&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ActiveAdmin&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ViewHelpers&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;development?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test?&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="ss"&gt;:new_saml_admin_user_session&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;super&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/initializers/active_admin_devise.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ActiveAdmin&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Devise&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;controllers&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;sessions: &lt;/span&gt;&lt;span class="s2"&gt;"admin_users/sessions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;passwords: &lt;/span&gt;&lt;span class="s2"&gt;"active_admin/devise/passwords"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;unlocks: &lt;/span&gt;&lt;span class="s2"&gt;"active_admin/devise/unlocks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;registrations: &lt;/span&gt;&lt;span class="s2"&gt;"active_admin/devise/registrations"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;confirmations: &lt;/span&gt;&lt;span class="s2"&gt;"active_admin/devise/confirmations"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;controllers_for_filters&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;AdminUsers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SessionsController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="no"&gt;SessionsController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="no"&gt;PasswordsController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="no"&gt;UnlocksController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="no"&gt;RegistrationsController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="no"&gt;ConfirmationsController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://i.giphy.com/media/loGszmIU4hu8aLKmiG/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/loGszmIU4hu8aLKmiG/giphy.gif" alt="Is that all?" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well.... not really, sorry for getting you excited ¯\&lt;em&gt;(ツ)&lt;/em&gt;/¯&lt;/p&gt;

&lt;p&gt;As you can see in the initializer there are some conditionals for development, that's because there is no dev out there that wants to do a 2FA every time they want to access ActiveAdmin locally.&lt;br&gt;
So we still need to add some code to conditionally enable 2FA depending on the environment, and a flag (because we don't want to block access to admin if something happens to OneLogin also)&lt;/p&gt;

&lt;p&gt;We need to check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Usual admin login page works in development&lt;/li&gt;
&lt;li&gt;Turning the feature off uses the old admin login and it works&lt;/li&gt;
&lt;li&gt;How logout works&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To perform the first two points I ended up adding the next lines to our overridden sessions_controller, seems that they are lost from super as you override it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/admin_users/sessions_controller.rb&lt;/span&gt;

&lt;span class="n"&gt;prepend_before_action&lt;/span&gt; &lt;span class="ss"&gt;:require_no_authentication&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;prepend_before_action&lt;/span&gt; &lt;span class="ss"&gt;:allow_params_authentication!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: :create&lt;/span&gt;
&lt;span class="n"&gt;prepend_before_action&lt;/span&gt; &lt;span class="ss"&gt;:verify_signed_out_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: :destroy&lt;/span&gt;
&lt;span class="n"&gt;prepend_before_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"devise.skip_timeout"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How logout works&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At this point you have 3 options to logout:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;destroy your app session&lt;/li&gt;
&lt;li&gt;destroy saml session&lt;/li&gt;
&lt;li&gt;force logout from your third party auth partner&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this specific case, we needed to perform the first one only, because logging someone out of OneLogin means they will be logged out of a lot of apps, and that was not desired.&lt;br&gt;
Actually it was a pain in the neck to do this, because &lt;code&gt;devise_saml_authenticable&lt;/code&gt; gem adds routes using &lt;code&gt;class_eval&lt;/code&gt; approach directly to &lt;code&gt;Devise&lt;/code&gt; engine, leaving you with almost no way to configure which routes you really want or not. You will be asking yourself &lt;code&gt;Why would I like to remove a route?&lt;/code&gt;, well... that's because at this point you have 2 &lt;code&gt;admin/logout&lt;/code&gt; routes in your app, and we know this is not a good practice at all.&lt;br&gt;
I ended up with this solution as the "cleanest":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/initializers/devise.rb&lt;/span&gt;

&lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Routing&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Mapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kp"&gt;protected&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;devise_saml_authenticatable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saml_route_helper_prefix&lt;/span&gt;
      &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saml_route_helper_prefix&lt;/span&gt;
      &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="ss"&gt;:session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="ss"&gt;controller: &lt;/span&gt;&lt;span class="n"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:saml_sessions&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;path: &lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="ss"&gt;:new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;path: &lt;/span&gt;&lt;span class="s1"&gt;'saml/sign_in'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as: &lt;/span&gt;&lt;span class="s2"&gt;"new_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;path: &lt;/span&gt;&lt;span class="s1"&gt;'saml/auth'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as: &lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;
        &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="ss"&gt;:metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;path: &lt;/span&gt;&lt;span class="s1"&gt;'saml/metadata'&lt;/span&gt;
        &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="ss"&gt;:idp_sign_out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;path: &lt;/span&gt;&lt;span class="s1"&gt;'saml/idp_sign_out'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as: &lt;/span&gt;&lt;span class="s2"&gt;"idp_destroy_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;via: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="ss"&gt;:session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="ss"&gt;controller: &lt;/span&gt;&lt;span class="n"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:saml_sessions&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;path: &lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="ss"&gt;:new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;path: &lt;/span&gt;&lt;span class="s1"&gt;'saml/sign_in'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as: &lt;/span&gt;&lt;span class="s1"&gt;'new'&lt;/span&gt;
        &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;path: &lt;/span&gt;&lt;span class="s1"&gt;'saml/auth'&lt;/span&gt;
        &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="ss"&gt;:metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;path: &lt;/span&gt;&lt;span class="s1"&gt;'saml/metadata'&lt;/span&gt;
        &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="ss"&gt;:idp_sign_out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;path: &lt;/span&gt;&lt;span class="s1"&gt;'saml/idp_sign_out'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;via: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can add last lines at the end of your devise initializer, or even better create a new initializer that run after devise one.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;3) Conclusions&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/bQEVifmPjIBkA/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/bQEVifmPjIBkA/giphy.gif" alt="Yas" width="480" height="219"&gt;&lt;/a&gt;&lt;br&gt;
A project that was estimated to last over a month or so, ends up tacking only 2 weeks because we found a great gem that solves most of our problems (besides some gem implementations not being done in the best way).&lt;br&gt;
Work does not end here, besides sharing this I'm planning to contributing back to this awesome gem to improve its quality and support missing points we mentioned. And my goal with this post besides showing how to solve a security problem is to encourage others to contribute back when you see you can do it, without contributions like this, this gem wouldn't exist and neither would this post.&lt;/p&gt;

</description>
      <category>security</category>
      <category>rails</category>
      <category>ruby</category>
      <category>coding</category>
    </item>
  </channel>
</rss>
