<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Rails - Rado's Blog]]></title><description><![CDATA[Rails - Rado's Blog]]></description><link>https://blog.rstankov.com/</link><image><url>http://blog.rstankov.com/favicon.png</url><title>Rails - Rado&apos;s Blog</title><link>https://blog.rstankov.com/</link></image><generator>Ghost 1.8</generator><lastBuildDate>Tue, 09 Jun 2026 23:04:12 GMT</lastBuildDate><atom:link href="https://blog.rstankov.com/tag/rails/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[My favourite Ruby on Rails engines]]></title><description><![CDATA[Engines is one of my favorite features in Ruby on Rails that distinguishes it from other web frameworks. Here are some cool ones...]]></description><link>https://blog.rstankov.com/top-8-ruby-on-rails-engines/</link><guid isPermaLink="false">63f9dfb5a71733000255c891</guid><category><![CDATA[ruby]]></category><category><![CDATA[GraphQL]]></category><category><![CDATA[Rails]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Sun, 26 Feb 2023 21:14:12 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1594734349446-4a2f752392d3?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMjc2N3wwfDF8c2VhcmNofDN8fHRyYWluJTIwZW5naW5lfGVufDB8fHx8MTY3NzQzMzg1OA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1594734349446-4a2f752392d3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjc2N3wwfDF8c2VhcmNofDN8fHRyYWluJTIwZW5naW5lfGVufDB8fHx8MTY3NzQzMzg1OA&ixlib=rb-4.0.3&q=80&w=1080" alt="My favourite Ruby on Rails engines"><p><a href="https://guides.rubyonrails.org/engines.html">Ruby on Rails Engines</a> is one of the most powerful and underestimated features. It distinguishes Rails from other web frameworks.</p>
<h2 id="whatengines">What engines?</h2>
<p>Engines are embeddable applications that can be integrated into your Ruby on Rails application. They are distributed as regular Ruby gems with models, controllers, and views.</p>
<p>The UI that comes with engines is incredibly useful, and most engines can be set up within 10 minutes, saving months of integration of microservices and external SaaS tools.</p>
<p>Here are the top eight Ruby on Rails engines:</p>
<ol>
<li><strong>Sidekiq</strong> - background jobs</li>
<li><strong>Blazer</strong> - business intelligence</li>
<li><strong>Lookbook</strong> - UI components previews</li>
<li><strong>Flipper</strong> - Feature toggles</li>
<li><strong>Split</strong> - A/B testing</li>
<li><strong>PGHero</strong> - PostgreSQL monitoring</li>
<li><strong>FastEntry</strong> - cache previewer</li>
<li><strong>GraphiQL</strong> - GraphQL IDE</li>
</ol>
<h3 id="1sidekiq">1) <a href="https://sidekiq.org/">Sidekiq</a></h3>
<p>Sidekiq is the de facto standard for background jobs in Ruby land. The engine provides a UI to monitor what is happening with your background jobs.</p>
<a href="https://rstankov-blog.s3.amazonaws.com/rails_engine_sidekiq.png" style="box-shadow: none;" target="_blank">
<img src="https://rstankov-blog.s3.amazonaws.com/rails_engine_sidekiq.png" alt="My favourite Ruby on Rails engines"></a>
<h3 id="2blazer">2) <a href="https://github.com/ankane/blazer">Blazer</a></h3>
<p>Blazer allows you to write SQL queries and build charts and dashboards from them, making it an excellent tool for business intelligence.</p>
<p>But even if you don't use it as a business intelligence tool. It is a great developer tool. It can help you see what is in your database in local or staging environments.</p>
<a href="https://rstankov-blog.s3.amazonaws.com/rails_engine_blazer.png" style="box-shadow: none;" target="_blank">
<img src="https://rstankov-blog.s3.amazonaws.com/rails_engine_blazer.png" alt="My favourite Ruby on Rails engines"></a>
<h3 id="3lookbook">3) <a href="https://lookbook.build/">Lookbook</a></h3>
<p><em>I found this recently.</em></p>
<p>If you use <a href="https://viewcomponent.org/">ViewComponents</a> to build UI elements instead of <a href="https://guides.rubyonrails.org/action_view_helpers.html">Action View Helpers</a>, you should use it. It builds on top of <a href="https://viewcomponent.org/guide/previews.html">previews</a> and wraps them in <a href="https://storybook.js.org/">Storybook</a> like interface.</p>
<p>However, if you have ever tried to set up <a href="https://storybook.js.org/">Storybook</a> and <a href="https://reactjs.org/">React</a> components, you have spent A LOT of time on it.</p>
<p>This gem takes about 10 minutes to set up, and the rest is just writing simple <a href="https://viewcomponent.org/guide/previews.html">previews</a> Ruby classes.</p>
<a href="https://rstankov-blog.s3.amazonaws.com/rails_engine_lookbook.png" style="box-shadow: none;" target="_blank">
<img src="https://rstankov-blog.s3.amazonaws.com/rails_engine_lookbook.png" alt="My favourite Ruby on Rails engines"></a>
<h3 id="4flipper">4) <a href="https://github.com/jnunemaker/flipper">Flipper</a></h3>
<p>Flipper provides infrastructure and UI for <a href="https://en.wikipedia.org/wiki/Feature_toggle">Feature toggle</a>.</p>
<p>I have <a href="https://blog.rstankov.com/feature-flags-in-react/">written</a> before how to integrate with React - <a href="https://blog.rstankov.com/feature-flags-in-react/">here</a>.</p>
<a href="https://rstankov-blog.s3.amazonaws.com/rails_engine_flipper.png" style="box-shadow: none;" target="_blank">
<img src="https://rstankov-blog.s3.amazonaws.com/rails_engine_flipper.png" alt="My favourite Ruby on Rails engines"></a>
<p>Flipper has a <a href="https://www.flippercloud.io/">paid version</a> with features like permissions and audit history.</p>
<h3 id="5split">5) <a href="https://github.com/splitrb/split">Split</a></h3>
<p><a href="https://github.com/splitrb/split">Split</a> provides infrastructure for <a href="https://en.wikipedia.org/wiki/A/B_testing">A/B testing</a>.</p>
<p>There are a lot of SaaS tools to do so; however, they are slowing your pages, cost a lot and take time integrate.</p>
<a href="https://rstankov-blog.s3.amazonaws.com/rails_engine_split_2.png" style="box-shadow: none;" target="_blank">
<img src="https://rstankov-blog.s3.amazonaws.com/rails_engine_split_2.png" alt="My favourite Ruby on Rails engines"></a>
<h3 id="6pghero">6) <a href="https://github.com/ankane/pghero">PGHero</a></h3>
<p>It provides a performance dashboard <a href="https://www.postgresql.org/">PostgreSQL</a>. It tells you things like - slow queries, unused indexes, table/index sizes, and more.</p>
<p>It comes from ? <a href="https://www.instacart.com">Instacard</a>, which makes also <a href="https://github.com/ankane/blazer">Blazer</a> and <a href="https://www.instacart.com/opensource">other wonderful open source projects</a>.</p>
<a href="https://rstankov-blog.s3.amazonaws.com/rails_engine_pghero.png" style="box-shadow: none;" target="_blank">
<img src="https://rstankov-blog.s3.amazonaws.com/rails_engine_pghero.png" alt="My favourite Ruby on Rails engines"></a>
<h3 id="7fastentry">7) <a href="https://rubygems.org/gems/fastentry">FastEntry</a></h3>
<p>Do you know what is in your cache? <a href="https://rubygems.org/gems/fastentry">FastEntry</a> shows you this. <em>Simple and useful.</em></p>
<p>I have found a secondary use for it in development. I use the same Redis instance for cache, <a href="https://sidekiq.org/">Sidekiq</a>, and application data storage. It shows you everything in this Redis instance.</p>
<a href="https://rstankov-blog.s3.amazonaws.com/rails_engine_fast_entry.png" style="box-shadow: none;" target="_blank">
<img src="https://rstankov-blog.s3.amazonaws.com/rails_engine_fast_entry.png" alt="My favourite Ruby on Rails engines"></a>
<h3 id="8graphiql">8) <a href="https://github.com/rmosolgo/graphiql-rails">GraphiQL</a></h3>
<p><em>Last but not least.</em> <a href="https://github.com/graphql/graphiql">GraphiQL</a> is IDE for <a href="https://graphql.org/">GraphQL</a>. This engine provides quick setup and integration.</p>
<a href="https://rstankov-blog.s3.amazonaws.com/rails_engine_graphiql.png" style="box-shadow: none;" target="_blank">
<img src="https://rstankov-blog.s3.amazonaws.com/rails_engine_graphiql.png" alt="My favourite Ruby on Rails engines"></a>
<h2 id="howtosetupandsecureengines">How to setup and secure engines?</h2>
<p>I mentioned that engines are easy to set up. But talk is cheap. Let me show you the code. ?</p>
<p>Let's install <a href="https://github.com/ankane/blazer">Blazer</a>, as it is one of the more complex ones to set up.</p>
<p><strong>Step 1</strong>: Add to Gemfile</p>
<pre><code>gem &quot;blazer&quot;
</code></pre>
<p><strong>Step 2</strong>: Bundle install &amp; database setup</p>
<pre><code>bundle install

rails generate blazer:install
rails db:migrate
</code></pre>
<p><strong>Step 3</strong>: Mount to the Rails application</p>
<pre><code class="language-ruby"># config/routes.rb
namespace :dev, constraints: Routes::AdminConstraint do
  mount Blazer::Engine, at: '/blazer'
  # ... other engines
end
</code></pre>
<p><em>Note:</em> <code>Routes::AdminConstraint</code> ensures that this namespace is only available for certain users.</p>
<p>A simplified version can look something like:</p>
<pre><code class="language-ruby">module Routes::AdminConstraint
  extend self

  def matches?(request)
    return false if request.session[:user_id].blank?

    User.find(request.session[:user_id]).admin?
  end
end
</code></pre>
<p><strong>In Production</strong>: Have database as ENV variable</p>
<pre><code>ENV[&quot;BLAZER_DATABASE_URL&quot;] = &quot;postgres://user:password@hostname:5432/database&quot;
</code></pre>
<p>That's it, you have a business intelligence tool ?</p>
<h2 id="conclusion">Conclusion</h2>
<p>Engines have saved me a lot of time <em>(and use of external services)</em> over the years. I'm constantly on the lookup for new useful ones  ?</p>
<p>If you have any questions or comments, you can ping me on  <a href="https://twitter.com/rstankov">Twitter</a> or <a href="https://mastodon.social/@rstankov">Mastodon</a>.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Organizing External Services in Rails]]></title><description><![CDATA[Rules I use at Product Hunt to deal and structure external services in Ruby.]]></description><link>https://blog.rstankov.com/handling-external-service-in-rails/</link><guid isPermaLink="false">5f53a4767d3886000468a1a3</guid><category><![CDATA[Rails]]></category><category><![CDATA[product hunt]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Tue, 08 Sep 2020 10:47:24 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1587620931276-d97f425f62b9?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjEyNzY3fQ" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1587620931276-d97f425f62b9?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjEyNzY3fQ" alt="Organizing External Services in Rails"><p>Our applications rarely work in isolation. Gone are the days where your app is just talking to a single database and storing files to local disc. Nowadays, apps talk to many external services like Sentry, Stripe, Twitter, etc.</p>
<p>External services are often added one by one in various places in a system, and it is easy to lose track of them. Because of this, at <a href="http://producthunt.com/">Product Hunt</a> we have strict rules for dealing with external services. This blogs post shows and explains those rules.</p>
<p>Our main goals are to isolate those external services and to make it easy for team members to know why and how services are used.</p>
<h2 id="1keepallservicesinasingleplace">1. Keep all services in a single place</h2>
<p><strong>All</strong> external services live in the <code>app/services/external</code> folder and are namespaced under the <code>External</code> module. All network-related code goes there. This includes calls to microservices we wrote ourselves.</p>
<p>In this way, we can see most of the outcoming network calls from our application just by opening this folder.</p>
<h2 id="2wraptheexternalservicesinafacademodule">2. Wrap the external services in a facade module</h2>
<p><strong>All</strong> external services are accessed via <code>External::[Service]Api</code> <a href="https://en.wikipedia.org/wiki/Facade_pattern">facade</a> module.</p>
<p>The following is our template for such a module.</p>
<pre><code class="language-ruby"># frozen_string_literal: true

# Documentation
#
# - service: [url to service]
# - manage: [url to manage tokens]
# - api: [url to api documentation]
# - gem: [gem we are using (optional)]
# [- ... other links]

module External::[Service]Api
  extend self

  # documentation: [documentation]
  def perform_action(args)
    # use HTTParty or gem for this external service
  end
end
</code></pre>
<p>Usually, the API facades are just a bag of module methods.</p>
<p>Here is an example from our <code>External::UnsplashApi</code>:</p>
<pre><code class="language-ruby"># frozen_string_literal: true

# Documentation
#
# - service: https://unsplash.com/
# - api: https://unsplash.com/documentation
# - gem: https://github.com/unsplash/unsplash_rb
# - portal: https://unsplash.com/oauth/applications

module External::UnsplashApi
  extend self

  def search(query)
    Unsplash::Photo.search(query, 1, 12, 'landscape')
  end

  def track_download(id)
    photo = Unsplash::Photo.find(id)
    photo.track_download
  rescue Unsplash::NotFoundError
    nil
  end
end
</code></pre>
<p>Even when the external service has a gem to access it, the gem is never used outside this module.</p>
<p>If the gem returns an instance of a response object, we try to wrap it with a class defined by us. <em>We started wrapping those response objects quite late.</em></p>
<p>We expose only what we use from the services.</p>
<h2 id="3storingcredentials">3. Storing credentials</h2>
<p>Rails added support for <a href="https://blog.engineyard.com/rails-encrypted-credentials-on-rails-5.2">encrypted credentials</a>. Our newest applications use this mechanism for storing external services secrets.</p>
<p>We use the following commands to work with credentials.</p>
<pre><code>EDITOR=vim rails credentials:edit --environment development
EDITOR=vim rails credentials:edit --environment production
</code></pre>
<p>Credentials are stored in <a href="https://en.wikipedia.org/wiki/YAML">YAML</a>. We always include a link to where a certain credential was taken because it is very irritating to search where token and secret was taken from—especially when dealing with Google APIs.</p>
<pre><code># Taken from: [url to where those credentials are taken]
[sevice_name]_token: [...]
[sevice_name]_secret: [...]
</code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>We are structuring our external services for the following reasons:</p>
<ul>
<li>Know precisely which services are external to the system</li>
<li>Make it evident that we are interacting with an external system where network calls are involved</li>
<li>Have an obvious surface area of external service, where we know what exact features we are using from them</li>
<li>Having a facade makes it easier to add proper logging, caching, and error handling</li>
<li>Simpler upgrade paths of APIs -- if an external gem has a breaking change, it only affects the facade object</li>
</ul>
</div>]]></content:encoded></item><item><title><![CDATA[How Product Hunt Structures GraphQL Mutations]]></title><description><![CDATA[<div class="kg-card-markdown"><p><a href="https://graphql.org/learn/queries/#mutations">Mutations</a> in <a href="https://graphql.org">GraphQL</a> ... mutate data. ?</p>
<p>At the time of this writing, <a href="https://www.producthunt.com/">Product Hunt</a> codebase and related projects have 222 mutations. All of our mutations have the same structure. This enables us not only to have a consistent system but to build a lot of tooling around it to make our</p></div>]]></description><link>https://blog.rstankov.com/how-product-hunt-structures-graphql-mutations/</link><guid isPermaLink="false">5d18ce9e566565000403d575</guid><category><![CDATA[GraphQL]]></category><category><![CDATA[ruby]]></category><category><![CDATA[Rails]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Tue, 02 Jul 2019 12:58:40 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1509966756634-9c23dd6e6815?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjEyNzY3fQ" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1509966756634-9c23dd6e6815?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjEyNzY3fQ" alt="How Product Hunt Structures GraphQL Mutations"><p><a href="https://graphql.org/learn/queries/#mutations">Mutations</a> in <a href="https://graphql.org">GraphQL</a> ... mutate data. ?</p>
<p>At the time of this writing, <a href="https://www.producthunt.com/">Product Hunt</a> codebase and related projects have 222 mutations. All of our mutations have the same structure. This enables us not only to have a consistent system but to build a lot of tooling around it to make our developer's life easier.</p>
<p>Here the <a href="https://www.producthunt.com/">Product Hunt</a> guide on structuring <a href="https://graphql.org/learn/queries/#mutations">GraphQL mutations</a>:</p>
<h2 id="naming">Naming</h2>
<blockquote>
<p>There are only two hard things in Computer Science: cache invalidation and <em>naming things</em><br>
-- Phil Karlton</p>
</blockquote>
<p>We have decided  to name our mutations in the following pattern:</p>
<pre><code>[module][object][action]
</code></pre>
<p>Here are some examples:</p>
<pre><code>CommentCreate
CollectionPostAdd
PostVoteCreate
PostVoteDestroy
PostSubmissionCreate
ShipContactCreate
ShipContactDestroy
</code></pre>
<p>The reason to follow this approach is consistency and easier grouping.  Autocompleting mutations names is even easier because you filter from module to object to the action.</p>
<p><img src="https://s3.amazonaws.com/rstankov-blog/uploads/2019/06/Screenshot-2019-06-30-at-18.07.19.png" alt="How Product Hunt Structures GraphQL Mutations"></p>
<h2 id="mutationshape">Mutation shape</h2>
<p>This is how a typical mutation looks like:</p>
<pre><code class="language-graphql">mutation CollectionPostAdd($input: CollectionPostAddInput!) {  
  response: collectionPostAdd(input: $input) {      
    node {
       id
       ...SomeFragment
    }    
    errors {    
      name  
      messages
    }
  }
}
</code></pre>
<p>It has the following elements:</p>
<ul>
<li>it is <a href="https://relay.dev/docs/en/next/graphql-server-specification">Relay</a> compatible</li>
<li>the result object is <strong>always</strong> named <code>node</code></li>
<li>it always has <code>error</code> array for user input validations</li>
<li>in frontend we <a href="https://graphql.org/learn/queries/#aliases">alias</a> the mutation to “response.”</li>
</ul>
<p>Let look at each one of those elementals:</p>
<h3 id="relaycompatibility">Relay compatibility</h3>
<p>We are using <a href="https://apollographql.com">Apollo</a> in our frontend. However, we try to have <a href="https://relay.dev/docs/en/next/graphql-server-specification">Relay comparable scheme</a>, just in case this situation changes.</p>
<p>In order for a <a href="https://relay.dev/docs/en/next/graphql-server-specification#mutations">mutation</a> to be <a href="https://relay.dev/docs/en/next/graphql-server-specification">Relay compatible</a>, it is required to have a field named <code>clientMutationId</code> and accepted all of its arguments as  <code>inputs</code> variable. <code>inputs</code> contains all arguments.</p>
<p>We tend to use <code>clientMutationId</code> when we don’t care about the mutation result. For example - <code>TrackEventCreate</code> .</p>
<p><code>inputs</code> is more interesting. It is an <a href="https://graphql.org/learn/schema/#input-types">input type</a>, containing all values needed for the mutation.</p>
<p>It makes it very easy to work with Forms. Because we have a single argument object to hold the form state, we can just pass this object to the mutation. When we add/remove arguments from a given mutation, we don't have to change the mutation call at all.</p>
<p>I really like this concept.</p>
<h3 id="nodefield">Node field</h3>
<p>We noticed that most mutations operate on a single object. Because <a href="https://relay.dev/docs/en/next/graphql-server-specification">Relay</a>  uses the term <code>node</code> a lot <em>(example - <a href="https://relay.dev/docs/en/next/graphql-server-specification#connections">connections</a>)</em>. We decide to name the object returned from the mutation - <code>node</code>. This makes it easier for the frontend to handle it since it knows what to expect and where to look for it. Plus backend mutation can just return an object, making defining mutations in the backend easier.</p>
<p>In some rare occasions, we might need a second object to be returned from mutation. We support this.</p>
<h3 id="errorsfield">Errors field</h3>
<p>There are a lot of debates on how to handle mutation errors. Should the <a href="https://graphql.github.io/graphql-spec/June2018/#sec-Errors">GraphQL error system</a> be used, or should we return an array of error objects?</p>
<p>I prefer returning error objects. I split the errors into two buckets based on their cause:</p>
<ol>
<li>those caused by system error</li>
<li>those caused by a user input error</li>
</ol>
<p>In <a href="https://www.producthunt.com/">Product Hunt</a>, we handle system errors with <a href="https://graphql.github.io/graphql-spec/June2018/#sec-Errors">GraphQL system error</a>, and we handle user input errors with error objects. Because of this, all our mutations have <code>error</code> fields, which return an array of <code>Error</code> objects:</p>
<pre><code class="language-graphql">type Error {
  name: String!
  messages: [String!]!
}
</code></pre>
<p>Our <a href="https://blog.rstankov.com/how-product-hunt-structures-graphql-mutations/#basemutation"><code>BaseMutation</code></a> knows how to return it and our <a href="https://blog.rstankov.com/how-product-hunt-structures-graphql-mutations/#formmutation"><code>Form.Mutation</code></a> knows how to map the errors to its input fields.</p>
<p>We use the name <code>base</code> for errors which can’t be attached to a particular field. Example: &quot;you posted too many posts for today&quot;.</p>
<p>I have written about this before ? <a href="http://blog.rstankov.com/graphql-mutations-and-form-errors/">here</a>.</p>
<h3 id="responsealias">Response alias</h3>
<p>We <a href="https://graphql.org/learn/queries/#aliases">alias</a> the mutation field name to <code>response</code>, so we can simplify our frontend code:</p>
<pre><code class="language-js">const response = await mutation({ variables: $input });


responseNode(response)
responseErrors(resoonse)
</code></pre>
<p>Notice that this code applies to every mutation we have.</p>
<h2 id="backendtooling">Backend tooling</h2>
<h3 id="basemutation">BaseMutation</h3>
<p>If you are working by schema first, enforcing those rules will be quite painful.</p>
<p>In <a href="https://www.producthunt.com/">Product Hunt</a>, we use the revolver first approach to GraphQL development. We have a lot of built-in tools on top of <a href="https://graphql-ruby.org/">GraphQL ruby gem</a>.</p>
<p>One of those tools is <code>BaseMutation</code>. Every mutation in our system uses it.</p>
<p>Here is an example:</p>
<pre><code class="language-ruby">module Graph::Mutations
  class CollectionPostAdd &lt; BaseMutation
    argument_record :collection, Collection, authorize: :edit
    argument_record :post, Post
    argument :description, String, required: false

    returns Graph::Types::CollectionPostType

    def perform(collection:, post:, description: nil)
      Collections.collect(
        current_user: current_user,
        collection: collection, 
        post: post, 
        description: description
      )
    end
  end
end
</code></pre>
<ul>
<li>it generates consistent mutations.</li>
<li>it can fetch records</li>
<li>it handles authorization</li>
<li>it knows how to map returns to <code>node</code> field</li>
<li>it knows how to handle raised errors from <a href="https://rubyonrails.org/">Ruby on Rails</a>, validation, authorization and similar</li>
</ul>
<p>The developer just has to think about:</p>
<ul>
<li>what is your input</li>
<li>what are you returning</li>
<li>what are the authorization rules</li>
<li>implement</li>
</ul>
<p>All the mechanics are handled by this class.</p>
<p>This is how we define mutations:</p>
<pre><code class="language-ruby">module Graph::Types
  class MutationType &lt; Types::BaseObject
    # we have this small helper to reduce the noise when defining mutations
    def self.mutation_field(mutation)
      field mutation.name.demodulize.underscore, mutation: mutation
    end

    mutation_field Graph::Mutations::CommentCreate
    mutation_field Graph::Mutations::CollectionPostAdd
    mutation_field Graph::Mutations::PostVoteCreate
    mutation_field Graph::Mutations::PostVoteDestroy
    mutation_field Graph::Mutations::PostSubmissionCreate
    mutation_field Graph::Mutations::ShipContactCreate
    mutation_field Graph::Mutations::ShipContactDestroy

    # ...
  end
end
</code></pre>
<h2 id="frontendtooling">Frontend tooling</h2>
<p>Having all wiring in the backend makes it very easy to build tooling for frontend. I already mentioned <code>responseNode</code> and <code>responseErrors</code>. We have them, but in reality, we use <code>Form.Mutation</code> and <code>MuttationButton</code> more.</p>
<h3 id="formmutation">Form.Mutation</h3>
<p>I have written and spoken about this before. <a href="http://blog.rstankov.com/graphql-mutations-and-form-errors/">Here</a> is a link to a post about it, and <a href="https://speakerdeck.com/rstankov/how-not-to-hate-your-life-when-dealing-with-forms">here</a> is a link to a presentation about forms in general  (here is a <a href="https://www.youtube.com/watch?v=kIi9OV338c4&amp;list=PLOEJ0eNOcZeosJfuT0dRcRWvNz4pru7SD&amp;index=7&amp;t=0s">recording</a>)</p>
<pre><code class="language-jsx">&lt;Form.Mutation onSubmit={onComplete}&gt;
  &lt;Form.Field name=&quot;title&quot; /&gt;
  &lt;Form.Field name=&quot;email&quot; control=&quot;email&quot; /&gt;
  &lt;Form.Field name=&quot;description&quot; control=&quot;textarea&quot; /&gt;
  &lt;Form.Field name=&quot;length&quot; control=&quot;select&quot; options={LENGTH_OPTIONS} /&gt;
  &lt;Form.Field name=&quot;level&quot; control=&quot;radioGroup&quot; options={LEVEL_OPTIONS} /&gt;
  &lt;Form.Field name=&quot;speakers&quot; control={SpeakersInput} /&gt;
  &lt;Form.Submit /&gt;
&lt;/Form.Mutation&gt;
</code></pre>
<p>This form handles:</p>
<ul>
<li>provides consistent UI for forms</li>
<li>map fields and arguments</li>
<li>knows how to pass inputs to mutation and interpret results</li>
<li>protects against double submit</li>
<li>handles validation errors and map them to fields</li>
<li>handles the successful submission and passes <code>node</code> to <code>onSubmit</code> handler</li>
</ul>
<h3 id="mutationbutton">MutationButton</h3>
<p>The second main way mutations are triggered by buttons. We have a React component name <code>MutationButton</code>:</p>
<pre><code class="language-js">function LikeButton({ post, onLike }) {
  const mutation = post.isLiked ? DESTROY_MUTATION : CREATE_MUTATION;
  const optimistic = post.isLiked ? optimisticDestroy : optimisticCreate;

  return (
    &lt;MutationButton
      requireLogin={true}
      mutation={mutation}
      input={{ postId: post.id }}
      optimisticResponse={optimistic(post)}
      onMutate={onLike}
      active={post.isLiked}
      icon={&lt;Like Icon /&gt;
      label={post.likesCount}
    /&gt;
  );
}
</code></pre>
<p>This  button handles:</p>
<ul>
<li>triggering the mutation with right arguments</li>
<li>protects against double click</li>
<li>handle optimistic updates</li>
<li>handles clicks from not logged in users</li>
<li>knows how to interpret the result of the mutations</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>Having all those structure and tooling around mutations helps the developers to focus on business logic and not worry about mechanics. This is a big win in my book.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Improve ActiveRecord Polymorphic Associations]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Polymorphic associations in ActiveRecord are very useful. Especially for attachable models like Comments, Attachments, Likes. However, sometimes we define them too loosely.</p>
<p>Here is an example of a Comment model. Its subject is polymorphic, allowing us to add comments to any ActiveRecord model.</p>
<pre><code class="language-ruby">class Comment &lt; ApplicationRecord
  belongs_to :subject,</code></pre></div>]]></description><link>https://blog.rstankov.com/allowed-class-names-in-activerecord-polymorphic-associations/</link><guid isPermaLink="false">5cb4ba58daac5f00047f7743</guid><category><![CDATA[Rails]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Thu, 18 Apr 2019 11:09:20 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1529465230221-a0d10e46fcbb?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjEyNzY3fQ" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1529465230221-a0d10e46fcbb?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjEyNzY3fQ" alt="Improve ActiveRecord Polymorphic Associations"><p>Polymorphic associations in ActiveRecord are very useful. Especially for attachable models like Comments, Attachments, Likes. However, sometimes we define them too loosely.</p>
<p>Here is an example of a Comment model. Its subject is polymorphic, allowing us to add comments to any ActiveRecord model.</p>
<pre><code class="language-ruby">class Comment &lt; ApplicationRecord
  belongs_to :subject, polymorphic: true, inverse_of: :comments
end
</code></pre>
<p>Two things are bothering me with this code:</p>
<ol>
<li>What are all possible subject types?</li>
<li>Any model can have comments. What if I don't want that?</li>
</ol>
<p>To address my worries, I explicitly list and validate all allowed classes.</p>
<pre><code class="language-ruby">class Comment &lt; ApplicationRecord
   SUBJECT_TYPES = [Post, Message, Category, Discussion::Thread]
   
   belongs_to :subject, polymorphic: true, inverse_of: :comments

   validates :subject_type, inclusion: { in: SUBJECT_TYPES.map(&amp;:name) } 
</code></pre>
<p>This solves my issues.</p>
<p>I need this functionality basically everywhere where I have a polymorphic association. So, I moved it to &quot;ApplicationRecord&quot;.</p>
<pre><code class="language-ruby">class ApplicationRecord &lt; ActiveRecord::Base
  self.abstract_class = true

  class &lt;&lt; self
    def belongs_to_polymorphic(name, allowed_classes:, **options)
      belongs_to name, polymorphic: true, **options
      validates &quot;#{name}_type&quot;, inclusion: { in: allowed_classes.map(&amp;:name) }
      define_singleton_method(&quot;#{name}_types&quot;) { allowed_classes }
    end
  end
end
</code></pre>
<p>All ActiveRecord models inherit from &quot;ApplicationRecord&quot;:</p>
<pre><code class="language-ruby">class Comment &lt; ApplicationRecord
  belongs_to_polymorphic :subject, allowed_classes: [Post, Message, Category, Discussion::Thread]
end
</code></pre>
<p>When I extracted this. I noticed another pattern, I almost always do with polymorphic associations. Adding named scopes for each of the corresponding models.</p>
<pre><code class="language-ruby">class Comment &lt; ApplicationRecord
  belongs_to_polymorphic :subject, allowed_classes: [Post, Message, Category, Discussion::Thread]

  scope :with_subject, -&gt;(subject_class) { where(subject_type: subject_class.name) }
  scope :with_subject_post, -&gt; { with_subject(Post) }
  scope :with_subject_message, -&gt; { with_subject(Message) }
  scope :with_subject_category, -&gt; { with_subject(Category) }
end
</code></pre>
<p>Since we already have <code>ApplicationRecord.belongs_to_polymorphic</code> we can add this functionality there.</p>
<pre><code class="language-ruby">class ApplicationRecord &lt; ActiveRecord::Base
  self.abstract_class = true

  class &lt;&lt; self
    def belongs_to_polymorphic(name, allowed_classes:, **options)
      belongs_to name, polymorphic: true, **options
      validates &quot;#{name}_type&quot;, inclusion: { in: allowed_classes.map(&amp;:name) }
      define_singleton_method(&quot;#{name}_types&quot;) { allowed_classes }
      
      # generates a generic finder method
      define_singleton_method(&quot;with_#{name}&quot;) do |type|
        type = case type
               when Class then type.name
               when String then type
               else type.class.name
               end
        where(&quot;#{name}_type&quot; =&gt; type)
      end

      # generates scope for each allowed class
      allowed_classes.each do |model|
        scope &quot;with_#{name}_#{model.name.underscore.tr('/', '_')}&quot;, -&gt; { where(&quot;#{name}_type&quot; =&gt; model.name) }
      end
    end
  end
end
</code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>Having <code>belongs_to_polymorphic</code> cleaned up a lot of my models.</p>
<ul>
<li>I can clearly see what models are used as a subject for the relationship</li>
<li>I have validations to ensure the proper models are being used</li>
<li>I have a standardly named scope and finder methods</li>
</ul>
</div>]]></content:encoded></item><item><title><![CDATA[Dealing with N+1 in GraphQL  (Part 2)]]></title><description><![CDATA[How to deal with N+1 queries in GraphQL within Ruby on Rails project when Active Record's build in preload is not enough.  Part 2 of the series.]]></description><link>https://blog.rstankov.com/dealing-with-n-1-in-graphql-part-2/</link><guid isPermaLink="false">5ba781a4c5e5f80004aaa753</guid><category><![CDATA[ruby]]></category><category><![CDATA[Rails]]></category><category><![CDATA[GraphQL]]></category><category><![CDATA[product hunt]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Wed, 26 Sep 2018 11:23:07 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1534972195531-d756b9bfa9f2?ixlib=rb-0.3.5&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjEyNzY3fQ&amp;s=1498b73397f58f328ab3e68086ee9cfb" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1534972195531-d756b9bfa9f2?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjEyNzY3fQ&s=1498b73397f58f328ab3e68086ee9cfb" alt="Dealing with N+1 in GraphQL  (Part 2)"><p>In <a href="https://blog.rstankov.com/dealing-with-n-1-with-graphql-part-1">Part 1</a>, I was showing how at <a href="https://www.producthunt.com">Product Hunt</a> we use <code>GraphQL::Batch</code> to solve N+1 when loading associations.</p>
<p>Those are the obvious first candidates for optimization in the GraphQL world.<br>
But the N+1 queries is hidden in a lot of places.</p>
<p>For example, let's say we have the following query:</p>
<pre><code class="language-graphql">query {
  posts(date: 'today') {
    id
    name
    isVoted
  }
}
</code></pre>
<p>This <code>isVoted</code>  returns whether the current user (viewer) have voted for this post.</p>
<p>The naive implementation would be something like:</p>
<pre><code class="language-ruby">class Graph::Types::PostType &lt; GraphQL::Schema::Object
  field :id, ID, null: false
  # ..
  field :is_voted, function: Graph::Resolvers::IsVotedResolver.new
end

class Graph::Resolvers::IsVotedResolver &lt; GraphQL::Function
  type !types.Boolean

  def call(post, _args, ctx)
    # handle not-logged in users
    return false unless ctx.current_user?

    # check if user have voted for a post
    ctx.current_user.votes.where(post_id: post.id).exists?
  end
end
</code></pre>
<p>This generates the following queries:</p>
<pre><code class="language-sql">SELECT * FROM posts WHERE DATE(featured_at) = {date}
SELECT * FROM votes WHERE post_id={post.id} and user_id={user_id}
SELECT * FROM votes WHERE post_id={post.id} and user_id={user_id}
SELECT * FROM votes WHERE post_id={post.id} and user_id={user_id}
SELECT * FROM votes WHERE post_id={post.id} and user_id={user_id}
SELECT * FROM votes WHERE post_id={post.id} and user_id={user_id}
SELECT * FROM votes WHERE post_id={post.id} and user_id={user_id}
SELECT * FROM votes WHERE post_id={post.id} and user_id={user_id}
-- ...
</code></pre>
<p>Those are N+1. But there isn't a build in solution in <a href="https://rubyonrails.org/">Rails</a> for those. Because of this, they are often unaddressed.</p>
<p>The solution we have in <a href="http://www.producthunt.com">Product Hunt</a> for this problem as of most of N+1 is to uses <a href="https://github.com/Shopify/graphql-batch"><code>GraphQL::Batch</code></a>:</p>
<pre><code class="language-ruby">class Graph::Resolvers::IsVotedResolver &lt; GraphQL::Function
  type !types.Boolean

  def call(post, _args, ctx)
    # handle not-logged in users
    return false unless ctx.current_user?

    # `.for` ensures we get the same loader instance
    loader = VotesLoader.for(ctx.current_user)
    # `.load` adds the post id to the list of things to load
    loader.load(post.id)
    # return a future promise for this particular post load
    loader
  end

  class VotesLoader &lt; GraphQL::Batch::Loader
    def initialize(user)
      @user = user
    end
    
    # this gets called after all `isVoted` were collected
    def perform(post_ids)
      # THE TRICK: load with one query only the voted post id from votes, not all posts
      voted_post_ids = @user.votes.where(post_id: post_ids).pluck(:post_id)

      # fulfill the future promises
      post_ids.each do |post_id|
        fulfill post_id, voted_post_ids.include?(post_id)
      end
    end
  end
end
</code></pre>
<p>This now generates only two queries.</p>
<pre><code class="language-sql">SELECT * FROM posts WHERE DATE(featured_at) = {date}
SELECT post_id FROM votes WHERE post_id IN ({post_ids})
</code></pre>
<p>This problem exists with normal <a href="https://rubyonrails.org/">Rails</a> applications. It isn't GraphQL specific. But I have rarely seen this being addressed. With  <code>GraphQL::Batch</code> we can elegantly solve this problem.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Dealing with N+1 in GraphQL  (Part 1)]]></title><description><![CDATA[How to deal with N+1 queries in GraphQL within Ruby on Rails project. Part 1 of the series.]]></description><link>https://blog.rstankov.com/dealing-with-n-1-with-graphql-part-1/</link><guid isPermaLink="false">5b5606ebdd3b93000404cd80</guid><category><![CDATA[ruby]]></category><category><![CDATA[Rails]]></category><category><![CDATA[GraphQL]]></category><category><![CDATA[product hunt]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Tue, 07 Aug 2018 12:19:41 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1484417894907-623942c8ee29?ixlib=rb-0.3.5&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjEyNzY3fQ&amp;s=676bfb6a4b21f736d765c193ea250e09" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1484417894907-623942c8ee29?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjEyNzY3fQ&s=676bfb6a4b21f736d765c193ea250e09" alt="Dealing with N+1 in GraphQL  (Part 1)"><p>When people see GraphQL their first question - what about N+1 problems.</p>
<p>This is a legit question.  In <a href="https://www.producthunt.com/">Product Hunt</a> as in every GraphQL powered project, we have the same issues.</p>
<p>For example, let's say we have the following query:</p>
<pre><code class="language-graphql">query {
  posts(date: 'today') {
    id
    name
    slug
    topics {
      id
      name
      slug
    }
  }
}
</code></pre>
<p>And the following type definition for a post:</p>
<pre><code class="language-ruby">class Graph::Types::PostType &lt; GraphQL::Schema::Object
  field :id, ID, null: false
  field :name, String, null: false
  field :slug, String, null: false
  field :topics, [Graph::Types::TopicType], null: false
end
</code></pre>
<p>This would result the following queries:</p>
<pre><code class="language-sql">SELECT * FROM posts WHERE DATE(featured_at) = {date}
SELECT * FROM topics JOIN post_topics WHERE post_id = {post_id}
SELECT * FROM topics JOIN post_topics WHERE post_id = {post_id}
SELECT * FROM topics JOIN post_topics WHERE post_id = {post_id}
SELECT * FROM topics JOIN post_topics WHERE post_id = {post_id}
SELECT * FROM topics JOIN post_topics WHERE post_id = {post_id}
SELECT * FROM topics JOIN post_topics WHERE post_id = {post_id}
-- ...
</code></pre>
<p>All those N+1s SQL queries ?</p>
<p>We don't only have additional queries but when a couple of posts have the same topics we load those multiple times.</p>
<p>The best solution, I have found to this problem so far was to use <a href="https://github.com/Shopify/graphql-batch"><code>GraphQL::Batch</code></a> gem from Shopify.</p>
<p>Adding the gem to your project is simple.</p>
<pre><code class="language-ruby">class Graph::Schema &lt;&lt; GraphQL::Schema
  # ...

  use GraphQL::Batch

  # ...
end
</code></pre>
<p>The way batching works: Instead of executing the query immediately, it returns an instance of <code>GraphQL::Batch::Loader</code>. The same instance of a loader object is returned for a given field leaf. After all fields for a given GraphQL query leaf are collected, then the <code>perform</code> method of the loader is called with all requested records. <code>preform</code> then must load and return for each record the requested data.</p>
<p>Here is how are going to solve the N+1 for topics with <a href="https://github.com/Shopify/graphql-batch"><code>GraphQL::Batch</code></a>.</p>
<p>First, we change the post type to use a new resolver.</p>
<pre><code class="language-ruby">class Graph::Types::PostType &lt; GraphQL::Schema::Object
  # ...

  field :topics, [Graph::Types::TopicType], null: false, function: Graph::Resolvers::Posts::TopicsResolver.new
end
</code></pre>
<p>Define the resolver itself:</p>
<pre><code class="language-ruby">class Graph::Resolvers::Posts::TopicsResolver &lt; GraphQL::Function
  def call(post, _args, _ctx)
    # `.for` makes sure we return the same loader instance
    # so all leaves, so we can group data
    loader = TopicsLoader.for
    # adds a post to the list of posts to be loaded
    loader.load(post)
    # returns the loader, not the actual topics
    # this gets transformed into topics afterward
    loader
  end

  # Loaders represent promises and mechanism to 
  # postpone loading until we have all posts in the list
  class TopicsLoader &lt; GraphQL::Batch::Loader
    # perform called with all the posts
    def perform(posts)
      # this is the built-in active record mechanism to 
      # preload associations into a group of records
      # association are loaded with the minimum amount of queries
      # if a couple of posts have same topics they would be loaded once
      ::ActiveRecord::Associations::Preloader.new.preload(posts, :topics)
      
      posts.each do |post|
        # returns topics for every post in the list
        fulfill post, post.topics
      end
    end
  end
end
</code></pre>
<p>Now we have a lot less queries ?</p>
<pre><code class="language-sql">SELECT * FROM posts WHERE DATE(featured_at) = {date}
SELECT * FROM topics JOIN post_topics WHERE post_id IN ({post_ids})
</code></pre>
<p>Also, each topic is loaded only once ?</p>
<p>Since loading associations is a very typical problem, I have created a generic <a href="https://gist.github.com/RStankov/48070003a31d71a66f57a237e27d5865"><code>AssociationLoader</code></a>. It can be found in this <a href="https://gist.github.com/RStankov/48070003a31d71a66f57a237e27d5865">gist</a>.</p>
<pre><code class="language-ruby">class Graph::Types::PostType &lt; GraphQL::Schema::Object
  # ...

  field :topics, [Graph::Types::TopicType], null: false, function: Graph::Resolvers::AssociationResolver.new(:topics)
end
</code></pre>
</div>]]></content:encoded></item><item><title><![CDATA[Introducing KittyEvents]]></title><description><![CDATA[Implements the publish/subscribe pattern using ActiveJob. You setup your events and list the subscribers for them. When an event is triggered, KittyEvents will fanout the event to each of your subscribers.]]></description><link>https://blog.rstankov.com/introducing-kittyevents/</link><guid isPermaLink="false">598468bfaf8dad5ca316eb6a</guid><category><![CDATA[ruby]]></category><category><![CDATA[open_source]]></category><category><![CDATA[Rails]]></category><category><![CDATA[product hunt]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Mon, 20 Feb 2017 19:08:45 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1489389944381-3471b5b30f04?ixlib=rb-0.3.5&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;s=64e35eedb964d117543506bea852008f" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1489389944381-3471b5b30f04?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&s=64e35eedb964d117543506bea852008f" alt="Introducing KittyEvents"><p>During the Christmas break me and <a href="https://twitter.com/mscccc">Mike</a> were discussing a new feature at <a href="https://www.producthunt.com">Product Hunt</a>. The feature required scheduling an <a href="http://guides.rubyonrails.org/active_job_basics.html">ActiveJobs</a> when a user signs up, votes or submits a comment.</p>
<p>There is <code>SignUp</code> object which handles user registration. So scheduling a new background job there is quite simple:</p>
<pre><code class="language-ruby">module SignUp
  # ... handle user sign up

  def after_sign_up(user)
    WelcomeEmailWorker.perform_later(user)
    WelcomeTweetWorker.perform_later(user)
    SyncProfileImageWorker.perform_later(user)
    NewFancyFeatureWorker.perform_later(user) # &lt;- new worker
  end
end
</code></pre>
<p>Unfortunately <code>after_sign_up</code> method was becoming quite large ☹️<br>
Now imagine having to add <code>NewFancyFeatureWorker</code> to 10 other places ?</p>
<p>Those issues pushed us to create a simple wrapper around <a href="http://guides.rubyonrails.org/active_job_basics.html">ActiveJobs</a>, which we call <a href="https://github.com/producthunt/KittyEvents">KittyEvents</a>.</p>
<p>Now in <code>SignUp</code> there is just one trigger for an &quot;event&quot;:</p>
<pre><code class="language-ruby">module SignUp
  # ... handle user sign up

  def after_sign_up(user)
    ApplicationEvents.trigger(:user_signup, user)
  end
end
</code></pre>
<p>And there is a central place, where events are mapped to  <a href="http://guides.rubyonrails.org/active_job_basics.html">ActiveJobs Workers</a>:</p>
<pre><code class="language-ruby"># config/initializers/application_events.rb
module ApplicationEvents
  extend KittyEvents

  event :user_signup, [
    WelcomeEmailWorker,
    WelcomeTweetWorker,
    SyncProfileImageWorker,
    NewFancyFeatureWorker, # &lt;- new worker
  ]

  # ... other events
end
</code></pre>
<p>When an event is triggered, all <a href="http://guides.rubyonrails.org/active_job_basics.html">ActiveJobs Workers</a> for this events are scheduled and executed.</p>
<p>Another bonus, is when using <a href="https://github.com/producthunt/KittyEvents">KittyEvents</a>, you only make a single Redis call to trigger any number of events. This shaves off precious milliseconds when using <a href="https://github.com/producthunt/KittyEvents">KittyEvents</a> in request.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Rails 5 Features, I'm Excited About]]></title><description><![CDATA[I'm more excited about the smaller new features in Rails 5, which would not change my life, but will make it a bit easier. Here is a list of them.]]></description><link>https://blog.rstankov.com/rails-5-features-im-excited-about/</link><guid isPermaLink="false">598468bfaf8dad5ca316eb5e</guid><category><![CDATA[Rails]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Tue, 12 Jul 2016 16:48:21 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1499673610122-01c7122c5dcb?ixlib=rb-0.3.5&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;s=362212e79fa42563e2d45ab0d9ced35d" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1499673610122-01c7122c5dcb?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&s=362212e79fa42563e2d45ab0d9ced35d" alt="Rails 5 Features, I'm Excited About"><p><a href="http://weblog.rubyonrails.org/2016/6/30/Rails-5-0-final/">Ruby on Rails 5</a> has arrived. Though, I don't care much about the <a href="https://github.com/rails/rails/tree/master/actioncable">big</a> <a href="https://github.com/turbolinks/turbolinks">features</a>. I'm more excited about the smaller features, which would not change my life, but will make it a bit easier.</p>
<h3 id="apimodeimprovements">API mode improvements</h3>
<p>Most of my work in Rails projects, these days is just using Rails as an api endpoint. There are lots of improvements.</p>
<p>I'm really glad we finally got <a href="http://api.rubyonrails.org/classes/ActionController/API.html">ActionController::API</a>.</p>
<h6 id="easierconversionoferrorstojson">Easier conversion of errors to JSON</h6>
<p>Returning consistent and usable error messages is vital when designing and API.</p>
<p>Previously getting usable validation errors from rails was quite painful. Since you can only have:</p>
<pre><code class="language-ruby">user.errors.messages # =&gt; {:email=&gt;[&quot;can't be blank&quot;]}
</code></pre>
<p>This error message is it quite hard for clients. In several projects, I have defined a json locale where things like &quot;can't be blank&quot; is just &quot;blank&quot; and used that for the api.</p>
<p>But this is no more! Since <a href="http://api.rubyonrails.org/classes/ActiveModel/Errors.html#method-i-add">errors.details</a> exists now:</p>
<pre><code class="language-ruby">user.errors.details # =&gt; {:email=&gt;[{:error=&gt;:blank}]}
</code></pre>
<h6 id="betterexceptionsindevelopment">Better exceptions in development</h6>
<p>Speaking about errors. There is a new option <a href="https://github.com/rails/rails/pull/20831">debug_exception_response_format</a>:</p>
<pre><code># config/environments/development.rb
config.debug_exception_response_format = :api
</code></pre>
<p>You will get exception information in JSON format during development, instead of HTML.</p>
<h3 id="activejobimprovements">ActiveJob improvements</h3>
<p>In a previous post - <a href="https://blog.rstankov.com/activejobretry/">Retrying ActiveJob</a>, there was a glue code depending on  a Rails 5 feature - <a href="http://api.rubyonrails.org/classes/ActiveJob/Core.html#method-i-deserialize">ActiveJob::Base#deserialize</a>. Now that can be removed.</p>
<p>Also having the <a href="https://github.com/rails/rails/pull/19034">ApplicationJob</a> makes including the <code>ActiveJobRetriesCount</code> from the post easier:</p>
<pre><code class="language-ruby">class ApplicationJob &lt; ActiveJob::Base
  include ActiveJobRetriesCount
end
</code></pre>
<h3 id="activerecordimprovements">ActiveRecord improvements</h3>
<ul>
<li><a href="http://guides.rubyonrails.org/active_record_migrations.html">better and more stable migrations</a>,</li>
<li><a href="http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-or">where.or</a></li>
<li><a href="https://github.com/rails/rails/pull/12162">ApplicationRecord</a>.</li>
<li><a href="https://github.com/rails/rails/blob/8c752c7ac739d5a86d4136ab1e9d0142c4041e58/activerecord/lib/active_record/attributes.rb">New Attributes API</a></li>
</ul>
<h4 id="andevenmore">And even more</h4>
<p>There is, even more, stuff like <a href="http://api.rubyonrails.org/classes/ActionController/Redirecting.html#method-i-redirect_back">redirect_back</a> or <a href="http://api.rubyonrails.org/classes/ActiveModel/AttributeAssignment.html#method-i-assign_attributes">ActiveModel::AttributeAssignment</a>. But for more information I encourage you to check:</p>
<ul>
<li><a href="http://blog.bigbinary.com/categories/Rails-5">BigBinary Rails 5 coverage</a></li>
<li><a href="http://guides.rubyonrails.org/5_0_release_notes.html">The official Ruby on Rails release notes</a></li>
</ul>
<p>Overall I think this is a solid release.</p>
<p>I have heard on <a href="http://bikeshed.fm/">The Bike Shed</a> that there are discussions about changing the release process and to smaller and more frequent release cycle. I think this would be great. Especially since the features, I'm mostly excited usually are smaller and don't require a major version change.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Retrying ActiveJob]]></title><description><![CDATA[One missing feature from ActiveJob is the ability to retry when a job fails. Here is how to implement it.]]></description><link>https://blog.rstankov.com/activejobretry/</link><guid isPermaLink="false">598468bfaf8dad5ca316eb63</guid><category><![CDATA[Rails]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Sun, 15 May 2016 16:11:54 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1477244075012-5cc28286e465?ixlib=rb-0.3.5&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;s=650281f5f6da101b6954a53645bf6501" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1477244075012-5cc28286e465?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&s=650281f5f6da101b6954a53645bf6501" alt="Retrying ActiveJob"><p>I like <a href="http://guides.rubyonrails.org/active_job_basics.html">ActiveJob</a>. One missing feature is the ability to retry when a job fails. Fortunately, this is quite easy to add:</p>
<pre><code class="language-ruby">module ActiveJobRetriesCount
  extend ActiveSupport::Concern

  included do
    attr_accessor :retries_count
  end

  def initialize(*arguments)
    super
    @retries_count ||= 0
  end

  def deserialize(job_data)
    super
    @retries_count = job_data['retries_count'] || 0
  end

  def serialize
    super.merge('retries_count' =&gt; retries_count || 0)
  end

  def retry_job(options)
    @retries_count = (retries_count || 0) + 1
    super(options)
  end
end
</code></pre>
<pre><code class="language-ruby">class ImageDownloader::Worker &lt; ActiveJob::Base
  include ActiveJobRetriesCount

  rescue_from ImageDownloader::TimeoutError do |exception|
    if exception.code == 0
      # just retry the download in 5 minutes
      # tolarate up to 5 failures
      retry_job wait: 5.minutes if retries_count &gt; 5
    else
      fail exception
    end
  end

  def perform(url)
    ImageDownloader.download(url)
  end
end
</code></pre>
<p>Here is a simple test for the <code>ActiveJobRetriesCount</code>.</p>
<pre><code class="language-ruby">require 'spec_helper'

describe ActiveJobRetriesCount do
  class RetriesTestClass
  end

  class RetriesTestError &lt; StandardError
  end

  class RetriesTestWorker &lt; ActiveJob::Base
    include ActiveJobRetriesCount

    rescue_from RetriesTestError do
      retry_job wait: 5.minutes if retries_count &lt; 5
    end

    def perform
      RetriesTestClass.do_something(retries_count)
    end
  end

  it 'handles retries counts', active_job: :inline do
    allow(RetriesTestClass).to receive(:do_something).and_raise RetriesTestError

    expect { RetriesTestWorker.perform_later }.not_to raise_error

    expect(RetriesTestClass).to have_received(:do_something).with(5)
    expect(RetriesTestClass).not_to have_received(:do_something).with(6)
  end
end
</code></pre>
<p>If you are not on Rails 5, you would need the following code:</p>
<pre><code class="language-ruby">if ActiveJob::Base.method_defined?(:deserialize)
  fail 'This no longer needed.'
else
  module ActiveJob
    class Base
      def self.deserialize(job_data)
        job = job_data['job_class'].constantize.new
        job.deserialize(job_data)
        job
      end

      def deserialize(job_data)
        self.job_id               = job_data['job_id']
        self.queue_name           = job_data['queue_name']
        self.serialized_arguments = job_data['arguments']
      end
    end
  end
end
</code></pre>
<p>For more advanced features – check out <a href="https://github.com/gocardless/activejob-retry">ActiveJob::Retry</a>.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Custom Routes in Rails]]></title><description><![CDATA[How to generate custom routes helper in Ruby on Rails.]]></description><link>https://blog.rstankov.com/custom-routes-in-rails/</link><guid isPermaLink="false">598468bfaf8dad5ca316eb5f</guid><category><![CDATA[Rails]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Tue, 02 Jun 2015 18:20:42 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1503252947848-7338d3f92f31?ixlib=rb-0.3.5&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;s=3a8488c11fc131ab9ad5bdb2bd3a22af" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1503252947848-7338d3f92f31?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&s=3a8488c11fc131ab9ad5bdb2bd3a22af" alt="Custom Routes in Rails"><p>In the life of almost all Rails applications, comes the moment where custom routes are needed. Usually, the custom routes are just convenience methods like these:</p>
<pre><code class="language-ruby">def post_path(post)
  date_post_path post.date_slug, post.slug
end

def product_url
  date_post_url post.date_slug, post.slug
end
</code></pre>
<p>In this way, you decouple the route generation from your objects. Also, if you are doing some system route update and you want to keep the old routes around - this is the way.</p>
<p>Usually, I create a module <code>CustomPaths</code> and put those methods there. But in recent years, <code>Rails.application.routes.url_helpers</code> is showing up in more and more places like - background jobs, presenters, serializers and so. This complicates things.</p>
<p>At <a href="http://www.producthunt.com/">Product Hunt</a> we needed some custom paths. So, me and <a href="https://mikecoutermarsh.com/">Mike Coutermarsh</a> created the following object:</p>
<pre><code class="language-ruby"># =&gt; routes.rb
module Routes
  # Simple `extend self` won't work here,
  # due to the way Rails implement `url_helpers`
  class &lt;&lt; self
    include Rails.application.routes.url_helpers
    include Routes::CustomPaths
  end

  include Rails.application.routes.url_helpers
  include Routes::CustomPaths

  def default_url_options
    Rails.application.routes.default_url_options
  end
end

# =&gt; routes/custom_paths.rb
module Routes
  module CustomPaths
    # ... all your custom route methods
  end
end
</code></pre>
<p>It behaves the same way as <code>Rails.application.routes.url_helpers</code> plus the custom routes.</p>
<pre><code class="language-ruby"># in can be called directly
Routes.root_path
Routes.product_path(product)

# or included in another object
class ObjectWhoNeedsRoutes
  include Routes
end

ObjectWhoNeedsRoutes.new.root_path
ObjectWhoNeedsRoutes.new.product_path
</code></pre>
<p>I added <code>Routes</code> to my <a href="https://github.com/RStankov/toolbox/blob/master/routes_helper.md">toolbox</a> with install instructions and tests.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Rails Anti-Pattern: Setting View Variables In Before Actions]]></title><description><![CDATA[In a lot of Rails project I'm seeing view assignments from before actions. I don't like this pattern.]]></description><link>https://blog.rstankov.com/rails-anti-pattern-setting-view-variables-in-before-actions/</link><guid isPermaLink="false">598468bfaf8dad5ca316eb66</guid><category><![CDATA[Rails]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Sun, 29 Mar 2015 05:53:55 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1496181133206-80ce9b88a853?ixlib=rb-0.3.5&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;s=29828cc604bb2bad9be5533ee06faab0" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1496181133206-80ce9b88a853?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&s=29828cc604bb2bad9be5533ee06faab0" alt="Rails Anti-Pattern: Setting View Variables In Before Actions"><p>I see a lot of the following pattern in <a href="http://rubyonrails.org/">Rails</a> projects:</p>
<pre><code class="language-ruby">class ProductsController &lt; ApplicationController
  before_action :assign_post, only: %i(show edit)
  
  def index
    @posts = Post.paginate(params[:page])
  end
  
  def show
  end
  
  def edit
  end
  
  private
  
  def assign_post
    @post = Post.find(params[:id])
  end
end
</code></pre>
<p>Most people do this, because for them <code>@post = Post.find(params[:id])</code> is duplication. <em>Having <code>assign_post</code> is more <a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself">DRY</a></em>.</p>
<p>I don't buy the duplication argument. The definition for <a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself">DRY</a> is:</p>
<blockquote>
<p>“Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.&quot;</p>
</blockquote>
<p>Unfortunately, most people confused it with - &quot;<em>I don't want to have the same sequence of characters more than once</em>&quot;. <a href="https://twitter.com/dchelimsky">David Chelimsky</a> have an excellent  <a href="http://confreaks.tv/videos/rubyconf2010-maintaining-balance-while-reducing-duplication">talk</a> on that subject.</p>
<p>But, my biggest issue with the approach is that it &quot;hides&quot; all variables passed to the view. Especially if there are <code>only</code> or <code>except</code> passed to <code>before_action</code>. Then you have to become an interpreter to figure out what gets into the view. I can recall several situations when during a code review I found code passing unwanted variables to a view.</p>
<p>I prefer to think the following, when I read a controller:</p>
<blockquote>
<p>ok, <code>show</code> sends <code>@post</code> and <code>@comments</code> to the view</p>
</blockquote>
<p>Instead of:</p>
<blockquote>
<p>So, if <code>show</code> is called then <code>assign_posts</code> is called before it, which sets <code>@posts</code>, then <code>show</code> is not included in this other action filter, but is not excluded for this third one...</p>
</blockquote>
<p>Also, it is easier for a developer to spot, that too many variables are passed to the view if all of them are listed together.</p>
<p>Because of that, I prefer to use the following pattern:</p>
<pre><code class="language-ruby">class ProductsController &lt; ApplicationController
  def show
    @post = find_post
  end
  
  def edit
    @post = find_post
  end
  
  private
  
  def find_post
    Post.find(params[:id])
  end
end
</code></pre>
<p>In this way, the duplication is moved into finder methods. So if I have to add a scope like <code>visible</code> to <code>Post.find</code>, I just need to apply it in <code>find_post</code>.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Introducing MiniForm]]></title><description><![CDATA[Introducing my newest gem - MiniForm. It is group of helpers for dealing with form objects and nested forms.]]></description><link>https://blog.rstankov.com/introducing-miniform/</link><guid isPermaLink="false">598468bfaf8dad5ca316eb69</guid><category><![CDATA[open_source]]></category><category><![CDATA[ruby]]></category><category><![CDATA[Rails]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Mon, 02 Mar 2015 03:23:26 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1461632830798-3adb3034e4c8?ixlib=rb-0.3.5&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;s=2a2103abf46489c584928a10cb189f84" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1461632830798-3adb3034e4c8?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&s=2a2103abf46489c584928a10cb189f84" alt="Introducing MiniForm"><p>My last blog post - <a href="https://blog.rstankov.com/update_as_design/">Dealing with form objects</a>, got me thinking about form objects. I also had a 12 hours flight to San Francisco, where I was meeting my teammates at <a href="http://www.producthunt.com/">Product Hunt</a>.</p>
<p>During that flight, I went back and checked all of my custom form implementations. I gathered them together in a folder and started combining and extracting the common parts. By the end of the flight I almost had what now is <a href="https://github.com/RStankov/MiniForm">MiniForm</a>. (I had to change its name several times since I'm not very good with names and most of the decent names are not available).</p>
<p><a href="https://github.com/RStankov/MiniForm">MiniForm</a> allows the following code:</p>
<pre><code class="language-ruby"> class RegistrationForm  
  include ActiveModel::Model

  attr_reader :user, :account

  attr_accessor :first_name, :last_name, :email, :name, :plan, :terms_of_service

  # user validation
  validates :first_name, presence: true
  validates :last_name, presence: true
  validates :email, presence: true, email: true

  # account validation
  validates :account_name, presence: true

  # form custom validation
  validates :plan, inclusion: {in AccountPlan::VALUES}
  validates :terms_of_service, acceptance: true

  # ensure uniqueness
  validate :ensure_unique_user_email
  validate :ensure_unique_account_name

  def initialize
    @user    = User.new
    @account = Account.new owner: @user
  end

  def update(attributes)
    attributes.each do |name, value|
      public_send &quot;#{name}=&quot;, value
    end

    if valid? &amp; user.update(user_attributes) &amp;&amp; account.update(account_attributes)
      account.users &lt;&lt; user
      AccountWasCreated.perform_later(account)
    else
      false
    end
  end

  private

  def ensure_unique_user_email
    errors.add :email, 'already taken' if User.where(email: email).any?
  end

  def ensure_unique_account_name
    errors.add :name, 'already taken' if Account.where(name: name).any?
  end

  def user_attributes
    {first_name: first_name, last_name: last_name, email: email}
  end

  def account_attributes
    {plan: plan, name: name}
  end
end 
</code></pre>
<p>To become:</p>
<pre><code class="language-ruby">class RegistrationForm  
  include MiniForm::Model

  # `model` delegates attributes to models 
  # and copies the validations from them
  model :user, attributes: %i(first_name last_name email), save: true
  model :account, attributes: %i(name plan), save: true

  attributes :terms_of_service

  validates :plan, inclusion: {in AccountPlan::VALUES}
  validates :terms_of_service, acceptance: true

  def initialize
    @user    = User.new
    @account = Account.new owner: @user
  end

  # `update` calls `perform` if model is valid
  # and models are saved
  def perform
    account.users &lt;&lt; user
    AccountWasCreated.perform_later(account)
  end
end
</code></pre>
</div>]]></content:encoded></item><item><title><![CDATA[Dealing With Form Objects in Rails]]></title><description><![CDATA[Tutorial about handling complicated nested forms with Ruby on Rails.]]></description><link>https://blog.rstankov.com/update_as_design/</link><guid isPermaLink="false">598468bfaf8dad5ca316eb60</guid><category><![CDATA[Rails]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Sun, 15 Feb 2015 10:47:47 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1468070454955-c5b6932bd08d?ixlib=rb-0.3.5&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;s=5fff45437f6abc76dc00a6ce653be545" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1468070454955-c5b6932bd08d?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&s=5fff45437f6abc76dc00a6ce653be545" alt="Dealing With Form Objects in Rails"><p>Let's say - we have the following nice clean model for our new application:</p>
<pre><code class="language-ruby">class Label &lt; ActiveRecord::Base
  belogns_to :user, required: true
  
  validates :name, presence: true, uniquness: true
end
</code></pre>
<p>Several months later, we have the requirement to add <code>quote</code> and <code>description</code> to <code>Label</code> and those attributes should be mandatory. So we update <code>Label</code>:</p>
<pre><code class="language-ruby">class Label &lt; ActiveRecord::Base
  belogns_to :user, required: true

  validates :name, presence: true, uniquness: true

  validates :quote, :description, presence: true
 end
</code></pre>
<p>Now we have a big problem. All of our previous <code>Label</code> records are invalid. Because those new attributes didn't exist an hour ago. Also <code>quote</code> and <code>description</code> are not the kind of fields we add good default values.</p>
<p>So we revert this change and start thinking about a better way to handle the situation.</p>
<p>When a little more thought is put into this - we find that we need this validation to be applied in two places - <code>my/labels/create</code> and <code>my/labels/update</code> pages.</p>
<p>One pattern I have seen used for dealing with this is the following:</p>
<pre><code class="language-ruby">class Label &lt; ActiveRecord::Base
  belogns_to :user, required: true

  validates :name, presence: true, uniquness: true

  validates :quote, presence: true, if: :validating_as_designer?
  validates :description, presence: true, if: :validating_as_designer?

  def update_as_designer(attributes = {})
    @validating_as_designer = true

    update_attributes(attributes).tap do
      @validating_as_designer = false
    end
  end

  private

  def validating_as_designer?
    @validating_as_designer
  end
end
</code></pre>
<p>In a newish model, this approach doesn't look so bad.</p>
<p>But this becomes unmaintainable easy. The main issue here is that we are adding form specific logic into a domain model. Now this model knows about certain pages. In my experience, such things tend to happen for the core modals of the application, which are bigger by nature.</p>
<p>Also, I have seen situations where a new developer enters the project, see that <code>update_as_designer</code> is used and decided that &quot;this is the way they do stuff here&quot; and adds <code>update_as_admin</code>.</p>
<p>So what is a better way to deal with the issue? I tend to favor the extraction of <a href="http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/">form objects</a>:</p>
<pre><code class="language-ruby">class DesignerForm
  include ActiveModel::Model

  attr_accessor :label, :user, :name, :quote, :description

  validates :user, presence: true
  validates :name, presence: true
  validates :quote, presence: true
  validates :description, presence: true

  def initialize(label = Label.new)
    @label = label
  end

  def persisted?
    @label.persisted?
  end

  def update(attributes)
    attributes.each do |name, value|
      public_send &quot;#{name}=&quot;, value
    end

    if valid?
      @label.update attributes
    else
      false
    end
  end
end
</code></pre>
<p>That sure looks too complicated! I can understand why people decide to use the &quot;nice short method&quot; instead of this big bulky object. But this is just our starting point.</p>
<p>Let's apply some minor modules; I use in almost every project I'm working on.</p>
<ul>
<li><a href="https://github.com/RStankov/toolbox/blob/master/active_model_attributes.md">ActiveModel::Attributes</a></li>
<li><a href="https://github.com/RStankov/toolbox/blob/master/active_model_validations_valid_validor.md">ActiveModel::Validations::ValidValidator</a></li>
</ul>
<pre><code class="language-ruby">class DesignerForm
  include ActiveModel::Model
  include ActiveModel::Attributes

  attr_reader :label

  delegate :persisted?, to: label
  
  # ActiveModel::Attributes method:
  # share attributes between form object and label
  attributes :user, :name, :quote, :description, delegate: :label
  
  # ValidValidator:
  # runs label validations and copies the errors to the form
  validates :label, valid: true

  # this is form specific validations we need
  validates :quote, presence: true
  validates :description, presence: true

  def initialize(label = Label.new)
    @label = label
  end

  def update(attributes)
    # ActiveModel::Attributes method
    self.attributes = attributes

    if valid?
      @label.save!
      true
    else
      false
    end
  end
end
</code></pre>
<p>That is a lot better. Still not perfect. In my experience after having 2-3 form objects in the application - more and more common functionally is extracted. In the end, most of the form objects become something like following:</p>
<pre><code class="language-ruby">class DesignerForm
  include BaseForm
  
  model :label, attributes: %i(user name quote description) 

  validates :quote, presence: true
  validates :description, presence: true
end
</code></pre>
<p>I don't start with <code>BaseForm</code> because every project has slightly different requirements for handling forms.</p>
<p>p.s. As expected - there a <a href="https://rubygems.org/search?utf8=%E2%9C%93&amp;query=form+object+">several gems</a> which provide nice apis for form objects. I don't use any of them.</p>
</div>]]></content:encoded></item></channel></rss>