<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Miguel</title>
    <description>The latest articles on DEV Community by Miguel (@mickeytgl).</description>
    <link>https://dev.to/mickeytgl</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%2F75558%2F4970180c-a0ce-4449-9c4b-b29381acc181.png</url>
      <title>DEV Community: Miguel</title>
      <link>https://dev.to/mickeytgl</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mickeytgl"/>
    <language>en</language>
    <item>
      <title>How to take advantage of Ruby blocks</title>
      <dc:creator>Miguel</dc:creator>
      <pubDate>Wed, 29 May 2019 23:46:19 +0000</pubDate>
      <link>https://dev.to/mickeytgl/how-to-take-advantage-of-ruby-blocks-2o4o</link>
      <guid>https://dev.to/mickeytgl/how-to-take-advantage-of-ruby-blocks-2o4o</guid>
      <description>&lt;p&gt;Blocks in ruby are a very powerful aspect of the language. They are often less used in when compared to other features of the language and it is something not everyone is comfortable using them, and much less writing code that consumes blocks. So let's start with a small introduction:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are blocks?&lt;/strong&gt;&lt;br&gt;
Essentially, a method with no name. It is a piece of code, delimited either by &lt;code&gt;{ }&lt;/code&gt; or by &lt;code&gt;do... end&lt;/code&gt;. That we are able to call in a certain context For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# The following syntaxes are equivalent. They will both execute the block twice.&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;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"foo"&lt;/span&gt;
&lt;span class="k"&gt;end&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;times&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"foo"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;A more complicated example: Let's assume that for a user masquerading gem, you want a method in which you can pass in a user, grant them all permissions, run some code with those permissions in place, and then reset the old permissions once the code has finished running. This is where blocks shine. A very simple test suite for this would be something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;UserImpersonator&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"grants permissions while inside the block"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

    &lt;span class="no"&gt;UserImpersonator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grant_all_permissions&lt;/span&gt; &lt;span class="n"&gt;valid_user&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valid_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:admin&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_true&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valid_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:admin&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_false&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;We first need a &lt;code&gt;UserImpersonator&lt;/code&gt; class and a &lt;code&gt;grant_all_permissions&lt;/code&gt; method that accepts the user, and the &lt;strong&gt;block&lt;/strong&gt; of code you want to run while you have all permissions in place.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;UserImpersonator&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;grant_all_permissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&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;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&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;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&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;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;begin&lt;/span&gt;
      &lt;span class="n"&gt;cache_old_permissions&lt;/span&gt;
      &lt;span class="n"&gt;assign_all_permissions&lt;/span&gt;
      &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
    &lt;span class="k"&gt;ensure&lt;/span&gt;
      &lt;span class="n"&gt;reset_permissions&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;Let's break up the code above: &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;amp;block&lt;/code&gt; on the &lt;code&gt;self.grant_all_permissions&lt;/code&gt; method definition lets the method know that it can expect to be passed in a block. This will instantiate a new &lt;code&gt;UserImpersonator&lt;/code&gt; object and call the run method on it, forwarding the &lt;code&gt;&amp;amp;block&lt;/code&gt; argument.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;run&lt;/code&gt; method's first two lines after the &lt;code&gt;begin&lt;/code&gt; keyword are the set up, we want to cache the old permissions so that we can reset them later and assign the new ones. After this, we want to actually run the block that was passed in, and after that, we want to reset the permissions back, regardless of whether or not an error was raised somewhere in between, hence the &lt;code&gt;ensure&lt;/code&gt; keyword. &lt;/p&gt;

&lt;p&gt;There you have it, we've just created a method that accepts a block and runs it wherever we want, this is certainly useful, but we can go deeper&lt;/p&gt;

&lt;p&gt;Let's try now passing a specific context into our block, or block variables, if you have used rails, even for a bit, then you have probably seen this when you iterate through your database records, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&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;user&lt;/span&gt;&lt;span class="o"&gt;|&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;update&lt;/span&gt; &lt;span class="ss"&gt;admin: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The block variable is what is between the pipes, &lt;code&gt;user&lt;/code&gt; in this case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New example:&lt;/strong&gt; Let's imagine we're running a restaurant, and we would like to calculate the cost of an order. Since an order can be made out of different items, we want to be able to add as much or as little items within the context of that order. Again, starting with the tests, we could have an expectation like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Order&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"allows to make operations"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cost&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;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_taco&lt;/span&gt;
      &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_taco&lt;/span&gt;
      &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_guacamole&lt;/span&gt;
      &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_beer&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="mi"&gt;44&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;To make the test above pass, one possible implementation that would make the test above to pass would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;Order&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;cost&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="no"&gt;Actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Actions&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
      &lt;span class="vi"&gt;@cost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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_taco&lt;/span&gt;
      &lt;span class="vi"&gt;@cost&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;10&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_guacamole&lt;/span&gt;
      &lt;span class="vi"&gt;@cost&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;6&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_beer&lt;/span&gt;
      &lt;span class="vi"&gt;@cost&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;18&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;Things to note:&lt;/p&gt;

&lt;p&gt;1) The block variable, or &lt;code&gt;c&lt;/code&gt; in the case of the test is an instance of the &lt;code&gt;Actions&lt;/code&gt; class, which means it has access to the &lt;code&gt;add_taco&lt;/code&gt;, &lt;code&gt;add_guacamole&lt;/code&gt; and &lt;code&gt;add_beer&lt;/code&gt; methods&lt;/p&gt;

&lt;p&gt;2) You don't need to specify &lt;code&gt;&amp;amp;block&lt;/code&gt; as an parameter in the &lt;code&gt;cost&lt;/code&gt; method since you are using &lt;code&gt;yield&lt;/code&gt; inside of the method&lt;/p&gt;

&lt;p&gt;3) &lt;code&gt;yield&lt;/code&gt; makes a block an &lt;strong&gt;OPTIONAL&lt;/strong&gt; parameter, which means that you COULD call cost and pass no block at all. This however, will fail and complain with a&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;LocalJumpError &lt;span class="o"&gt;(&lt;/span&gt;no block given &lt;span class="o"&gt;(&lt;/span&gt;yield&lt;span class="o"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To solve this, a handy little method called &lt;code&gt;block_given?&lt;/code&gt; allows you to verify if a block was passed in. So you could refactor the &lt;code&gt;cost&lt;/code&gt; method to handle that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&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;cost&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="no"&gt;Actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;block_given?&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I really hope that this deep dive into the more fun and out there features of ruby was useful. Personally, I find it enlightening to find out how the inner workings of the tools I use very often within &lt;strong&gt;RSpec&lt;/strong&gt; or &lt;strong&gt;factory_bot&lt;/strong&gt; fit together step by step&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>The good and bad of ActiveRecord callbacks</title>
      <dc:creator>Miguel</dc:creator>
      <pubDate>Thu, 28 Feb 2019 01:02:38 +0000</pubDate>
      <link>https://dev.to/mickeytgl/the-good-and-bad-of-activerecord-callbacks-p4a</link>
      <guid>https://dev.to/mickeytgl/the-good-and-bad-of-activerecord-callbacks-p4a</guid>
      <description>&lt;p&gt;Callbacks inside your rails controllers have sparked a lot of controversy. On one side, you have DHH advocating for the use of callbacks to &lt;a href="https://www.youtube.com/watch?v=m1jOWu7woKM"&gt;manage auxiliary complexity&lt;/a&gt;, and on the other hand, you have people that call it &lt;a href="https://medium.com/planet-arkency/the-biggest-rails-code-smell-you-should-avoid-to-keep-your-app-healthy-a61fd75ab2d3"&gt;The biggest Rails code smell you should avoid&lt;/a&gt;. In this post I would like to analyze both sides of the argument and present an example of how callbacks were implemented in one area of our codebase and how it can benefit or hurt us. &lt;/p&gt;

&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;p&gt;1) &lt;strong&gt;It allows to put complexity off in the side lanes:&lt;/strong&gt; This allows developers to focus on the main flow of the logic in a straightforward way when we want to get a high-level understanding of the logic. Most of the time, if you are looking at your &lt;code&gt;create&lt;/code&gt; method, you care about how the &lt;code&gt;create&lt;/code&gt; action is being handled, not about the &lt;code&gt;:welcome_email&lt;/code&gt; that is being sent out or the &lt;code&gt;:notification&lt;/code&gt; that goes out to a receiver.&lt;/p&gt;

&lt;p&gt;2) &lt;strong&gt;It "just works":&lt;/strong&gt; Having callbacks that trigger side effects like sending out a notification, or triggering a background job allows allows your controller actions to be way more readable at first glance, since you don't have to figure out everything that is going on inside of it. And because you don't have to understand every intricacy of the flow, you can make changes more quickly in your app, assuming you follow the default path, this is the principle of convention over configuration. DHH actually frames this well in his 2018 RailsConf &lt;a href="https://www.youtube.com/watch?v=zKyv-IGvgGE&amp;amp;t=6s"&gt;keynote&lt;/a&gt; as "Conceptual Compression".&lt;/p&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;p&gt;1) &lt;strong&gt;Unwanted side effects:&lt;/strong&gt; Side effects that work are great when they do, but especially painful and hard to deal with when they don't. Following your normal flow might lead you to think a certain value is coming back but then getting something completely different is certainly frustrating.&lt;/p&gt;

&lt;p&gt;2) &lt;strong&gt;Hard to opt out of:&lt;/strong&gt; This is something that even DHH recognizes as a possible culprit on why callbacks have such a bad reputation, since this can easily come back to bite many developers in heavy callback scenarios. To counter this, he introduced the concept of &lt;code&gt;supressed&lt;/code&gt; in the Basecamp codebase.&lt;/p&gt;

&lt;p&gt;3) &lt;strong&gt;Tightly coupled actions:&lt;/strong&gt; This is the one in particular I find most dangerous, using callbacks means that you are not able to test the callback actions on their own, since you need to call &lt;code&gt;create&lt;/code&gt; or &lt;code&gt;update&lt;/code&gt; on your object first which will set a cascade of callbacks. As an extreme, but very real example, you wouldn't be able to test your &lt;code&gt;#send_receipt&lt;/code&gt; method without triggering &lt;code&gt;#charge_credit_card&lt;/code&gt; which could go very wrong, very fast. &lt;/p&gt;

&lt;h3&gt;
  
  
  Example implementation:
&lt;/h3&gt;

&lt;p&gt;Here is an example of our &lt;code&gt;MatchOpportunities#update&lt;/code&gt; action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;CatchOpportunitiesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;

  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:set_catch_opportunity&lt;/span&gt;
  &lt;span class="o"&gt;...&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;if&lt;/span&gt; &lt;span class="vi"&gt;@catch_opportunity&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="n"&gt;catch_opportunity_params&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;catch_opportunity_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:interested_in_fish&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;catch_opportunity_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:fisherman_profile_up_to_date&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
          &lt;span class="n"&gt;post_lack_of_interest_survey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;catch_opportunity_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:response_text&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="vi"&gt;@catch_opportunity&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="s1"&gt;'fisherman_profile_up_to_update'&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;catch_opportunity_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:interested_in_fish&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;post_lack_of_interest_survey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;catch_opportunity_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:response_text&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="vi"&gt;@catch_opportunity&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="s1"&gt;'survey_results'&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="no"&gt;MatchOpportunityMailer&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;catch_opportunity: &lt;/span&gt;&lt;span class="vi"&gt;@catch_opportunity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fisherman_response_email&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deliver_now&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="vi"&gt;@catch_opportunity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="vi"&gt;@catch_opportunity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;422&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;As you can see, this is A LOT of responsibilities for the &lt;code&gt;#update&lt;/code&gt; method alone, leveraging the power of ActiveRecord callbacks, the new code would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;CatchOpportunitiesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;

  &lt;span class="n"&gt;after_action&lt;/span&gt; &lt;span class="ss"&gt;:send_email_with_fisherman_response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="ss"&gt;only: :update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;if: :profile_response_complete?&lt;/span&gt;
  &lt;span class="n"&gt;after_action&lt;/span&gt; &lt;span class="ss"&gt;:post_fisherman_response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: :update&lt;/span&gt;

  &lt;span class="o"&gt;...&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;if&lt;/span&gt; &lt;span class="vi"&gt;@catch_opportunity&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="n"&gt;catch_opportunity_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="vi"&gt;@catch_opportunity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="vi"&gt;@catch_opportunity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;422&lt;/span&gt;
    &lt;span class="k"&gt;end&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;post_fisherman_response&lt;/span&gt;
      &lt;span class="c1"&gt;# handle posting to external service&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;send_email_with_fisherman_response&lt;/span&gt;
      &lt;span class="c1"&gt;# handles email delivery&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;profile_response_complete?&lt;/span&gt;
      &lt;span class="c1"&gt;# decides if callback should be performed at all.&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The second version of this snippet looks way more readable, just the fact that we managed to eliminate a double nested conditional is a big win, and although it is true that the &lt;code&gt;#update&lt;/code&gt; method gives you no hint whatsoever that an email will get sent out, ultimately, that work is not hidden, it lets you know at the top of the file what action will be performed and where. Granted, this might not be the most perfect, beautiful, easy-to-test possible form of this code, but it's certainly an improvement on the original.&lt;/p&gt;

&lt;p&gt;The fact that DHH, the creator of the Rails framework is in favor of using callbacks (mindfully), should be no surprise to anyone, since the rails framework, and the language ruby itself known to have a lot of &lt;em&gt;magic&lt;/em&gt; (Implicit logic that allows you to get up and running faster, but hides complexity making the inner working of things not so obvious)&lt;/p&gt;

&lt;h3&gt;
  
  
  Personal Thoughts
&lt;/h3&gt;

&lt;p&gt;There are some arguments against callbacks I didn't find very compelling. For example, stating that explicit code is always inherently better than implicit code is a bit of an overstatement in my opinion. Although some places like functional languages hold this idea as a very core part of their philosophy, Both Ruby and Rails are famously known for having a lot of syntactic sugar and embedded into it, and it's exactly that that helps people get up and running with them quickly.&lt;/p&gt;

&lt;p&gt;I do see how this can be easily abused, and I find that the fact that it introduces tightly coupled actions that cannot run one without the other, and violating the &lt;a href="https://de.wikipedia.org/wiki/Single-Responsibility-Prinzip"&gt;Single Responsibility Principle&lt;/a&gt; of the controller, (which should only be concerned with persisting the data to the database). Charging a user's credit card when trying to test sending an email is an extreme, but real example.&lt;/p&gt;

&lt;p&gt;At the end of the day, I see this as a display of Ruby's values: A very powerful tool, just as &lt;a href="https://stackoverflow.com/questions/5626193/what-is-monkey-patching"&gt;monkey patching&lt;/a&gt;, that gives you the freedom to build very powerful software, but can also screw you over very is used incorrectly. I don't believe it should be avoided at all costs, but like with most things, it has it's tradeoffs, so approach with caution.&lt;/p&gt;

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