<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Super Good</title>
    <description>The latest articles on Forem by Super Good (@supergoodsoft).</description>
    <link>https://forem.com/supergoodsoft</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1433%2F42f09267-e7fa-4690-a4fc-31220092caf4.png</url>
      <title>Forem: Super Good</title>
      <link>https://forem.com/supergoodsoft</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/supergoodsoft"/>
    <language>en</language>
    <item>
      <title>Anonymous Struct Literals Might Be Coming To Ruby</title>
      <dc:creator>Jared Norman</dc:creator>
      <pubDate>Tue, 07 Jul 2020 15:38:44 +0000</pubDate>
      <link>https://forem.com/supergoodsoft/anonymous-struct-literals-might-be-coming-to-ruby-j84</link>
      <guid>https://forem.com/supergoodsoft/anonymous-struct-literals-might-be-coming-to-ruby-j84</guid>
      <description>&lt;p&gt;There's been some really interesting discussion in the Ruby community about adding anonymous struct literals to the language. My Japanese isn't very good (I don't speak Japanese), but Koichi Sasada and Matz had a little Twitter exchange about the idea and that's now turned into &lt;a href="https://bugs.ruby-lang.org/issues/16986"&gt;a proposal&lt;/a&gt; on the Ruby tracker where there's been further discussion.&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o8wi9oUJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/EbV1Z9VUwAII7MK.png" alt="unknown tweet media content"&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--70KBapKb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1146121561/QR_Code_normal.jpg" alt="_ko1 profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        _ko1
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @_ko1
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      &lt;a href="https://twitter.com/yukihiro_matz"&gt;@yukihiro_matz&lt;/a&gt; just idea なんですが、無名Structを簡単に作る仕組みをいれるのはどうでしょうか。同じキーセットなら、同じ無名 Struct が返るような感じで。 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      07:30 AM - 25 Jun 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1276055259241046016" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1276055259241046016" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      3
      &lt;a href="https://twitter.com/intent/like?tweet_id=1276055259241046016" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      14
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  What's a Struct?
&lt;/h2&gt;

&lt;p&gt;Ruby provides &lt;a href="https://ruby-doc.org/core-2.7.1/Struct.html"&gt;Structs&lt;/a&gt; as a convenient way to create classes that group together a set of fields without forcing you to explicitly define a class with all the accessor methods.&lt;/p&gt;

&lt;p&gt;For example, if you needed to pass around a representation of a dog but didn't need any additional behaviour you might do 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="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Dog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Struct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:breed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Dog&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Roxie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"whippet-cross"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;struct Dog name="Roxie", breed="whippet-cross", age=4&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Roxie"&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;breed&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"whippet-cross"&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;age&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Roxie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"whippet-cross"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;struct Dog name="Roxie", breed="whippet-cross", age=5&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;roxie2&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Because instances of Structs are equal if their attributes are all equal, it makes them a great solution for defining &lt;a href="https://martinfowler.com/bliki/ValueObject.html"&gt;value objects&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the proposal add?
&lt;/h2&gt;

&lt;p&gt;There's some disagreement on the syntax, but the idea is to add a syntax for defining anonymous instances of Structs in-line. Today, you might write this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;roxie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Struct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:breed&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;"Roxie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"whippet-cross"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With this proposal you'd be able to simply write something like this instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;roxie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"Roxie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;breed: &lt;/span&gt;&lt;span class="s2"&gt;"whippet-cross"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This would create an instance of an anonymous Struct class without having to explicitly define and instantiate the class.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is that useful?
&lt;/h2&gt;

&lt;p&gt;There are actually a number of benefits to this, but let's start with why anonymous Structs are useful regardless of the syntax.&lt;/p&gt;

&lt;h3&gt;
  
  
  Structs avoid typos
&lt;/h3&gt;

&lt;p&gt;In Ruby we often use hashes for passing around things like configuration options and other structured data. Structs allow us to do this without allowing arbitrary properties.&lt;/p&gt;

&lt;p&gt;In our dog example above, &lt;code&gt;roxie.breeed = "pug"&lt;/code&gt; will cause an error because we spelled "breed" incorrectly and the Struct doesn't have a member called "breeed". If were using a Hash instead then we'd be able to accidentally assign &lt;code&gt;roxie[:breeed] = "pug"&lt;/code&gt; without any errors, accidentally creating a new typoed key in the Hash.&lt;/p&gt;

&lt;p&gt;There are many cases where you need arbitrary properties, but for those cases you can use OpenStruct or a Hash. When accepting configuration from a user or any other situation where you know the possible fields in advance Structs work great.&lt;/p&gt;

&lt;h3&gt;
  
  
  Anonymous Structs communicate intent
&lt;/h3&gt;

&lt;p&gt;Writing code that communicates its own intent clearly is useful for helping future developers understand and modify that code. Using an anonymous Struct rather than a Hash or OpenStruct communicates to the person reading the code that the fields the object is defined with are the &lt;em&gt;only&lt;/em&gt; fields that object supports.&lt;/p&gt;

&lt;h3&gt;
  
  
  Structs are simpler to access
&lt;/h3&gt;

&lt;p&gt;This is admittedly a small improvement, but it's simpler to type &lt;code&gt;roxie.breed&lt;/code&gt; than &lt;code&gt;roxie[:breed]&lt;/code&gt;. In this example &lt;code&gt;roxie&lt;/code&gt; doesn't represent an arbitrary mapping of keys to values, but a dog, so if we're following conventional object-oriented design practices, then &lt;code&gt;roxie&lt;/code&gt; should be an object that responds to messages about itself.&lt;/p&gt;

&lt;p&gt;Using Structs over Hashes also means that refactoring the code to use a real Class won't mean you need to change everywhere its data is accessed.&lt;/p&gt;

&lt;h3&gt;
  
  
  OpenStruct is slow
&lt;/h3&gt;

&lt;p&gt;OpenStruct is another class in the standard library that provides the ability to create objects that are basically like Structs, but are "open" to creating new attributes like Hashes are.&lt;/p&gt;

&lt;p&gt;This is extremely useful if you need to support arbitrary attributes on the object, but if you don't you're just opening yourself up to the same issue as using a Hash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;OpenStruct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"Roxie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;breed: &lt;/span&gt;&lt;span class="s2"&gt;"whippet-cross"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;OpenStruct name="Roxie", breed="whippet-cross"&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;breeed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"french bulldog"&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"french bulldog"&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;OpenStruct name="Roxie", breed="whippet-cross", breeed="french bulldog"&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Additionally, while OpenStructs are definitely great when you need their "open" functionality, they are much slower than regular Structs. Koichi Sasada &lt;a href="https://bugs.ruby-lang.org/issues/16986#Performance"&gt;notes this&lt;/a&gt; in the performance section of his proposal.&lt;/p&gt;

&lt;h2&gt;
  
  
  But it says in the proposal that Hashes are faster!
&lt;/h2&gt;

&lt;p&gt;One interesting detail that the proposal brings up is that Hashes are currently faster than Structs for this kind of usage. This is true, but Hashes have seen plenty of optimization because of how heavily we rely on them.&lt;/p&gt;

&lt;p&gt;You would think that Structs would be faster than Hashes since their behaviour is simpler. Well, that's not the case but Koichi Sasada is confident that it &lt;em&gt;could&lt;/em&gt; be the case with some optimization.&lt;/p&gt;

&lt;p&gt;This proposal also enables a whole new optimization that is only briefly mentioned:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Thanks to this spec, we can specify anonymous Struct classes at compile time. We don't need to find or create Struct classes at runtime.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because the attributes of the Structs is known at &lt;em&gt;parse&lt;/em&gt; time rather than &lt;em&gt;run&lt;/em&gt; time, it is possible to define the anonymous classes at compile time so that using this syntax incurs only the overhead of instantiating the anonymous class without having to define it too.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does it mean for you?
&lt;/h2&gt;

&lt;p&gt;For now, not much. We can't use this in our programs yet, but we are free to use anonymous Structs in our code without the special syntax. If you're creating value objects, want to limit the attributes on your object, or just want to make refactoring easier, this isn't a bad idea.&lt;/p&gt;

&lt;p&gt;That said, keep an eye on this proposal. Switching to this syntax in your applications when it becomes available could have performance benefits, save on typos, and make your code clearer.&lt;/p&gt;

&lt;p&gt;What do you think about this new syntax? Let me know &lt;a href="https://twitter.com/SuperGoodJared/status/1280509476861288449"&gt;on Twitter!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Working Around ActiveRecord Callbacks</title>
      <dc:creator>Jared Norman</dc:creator>
      <pubDate>Tue, 10 Dec 2019 16:05:10 +0000</pubDate>
      <link>https://forem.com/supergoodsoft/working-around-activerecord-callbacks-pj0</link>
      <guid>https://forem.com/supergoodsoft/working-around-activerecord-callbacks-pj0</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was original posted &lt;a href="https://supergood.software/working-around-activerecord-callbacks/"&gt;over here on the Super Good Software website&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Working on applications that overuse ActiveRecord callbacks can be painful. Saving or updating any given record might cause a cascade of API calls and business logic that's totally irrelevant to what you're trying accomplish. I've got a great trick for working around troublesome callbacks by allowing you to easily prevent them from running as needed.&lt;/p&gt;

&lt;p&gt;I strongly believe that business logic shouldn't be implemented using ActiveRecord callbacks. Callbacks are great for data normalization and caching computed values. Sending e-mails, making API calls, and other side-effects should be implemented using some other programming pattern that untangles the logic from your persistence layer. Excessive use of callbacks leads to slow test suites, brittle systems, and unintended changes.&lt;/p&gt;

&lt;p&gt;I run into many applications with complex third-party API integrations that are fueled by these cascades of &lt;code&gt;after_save&lt;/code&gt;, &lt;code&gt;after_create&lt;/code&gt;, and &lt;code&gt;after_commit&lt;/code&gt; callbacks and are difficult to understand and debug. Ideally I'd love to untangle these messes and pull out the business logic into classes that can easily be understood and tested separately from the persistence layer, but I don't always have the time to do that.&lt;/p&gt;

&lt;p&gt;Imagine this situation: you've got an &lt;code&gt;Address&lt;/code&gt; model with a &lt;code&gt;before_save&lt;/code&gt; callback that fetches and sets the latitude and longitude for that address. You're on a deadline and don't have the time to refactor every location in the code where you create an address. The callback is also slowing down your test suite because it's making slow requests out to the geolocation API every time you create an address. What's more, you've found some locations in the app where you create addresses and already know the coordinates. In these scenarios you don't need to do the lookup, but the lookup is being performed anyway.&lt;/p&gt;

&lt;p&gt;You could address the test suite speed issue by using something like VCR, but with a few hundred tests creating addresses, that'll generate &lt;em&gt;a ton&lt;/em&gt; of cassettes and doesn't solve the problem of the unnecessary API calls anyway.&lt;/p&gt;

&lt;p&gt;There's a solution that alleviates both these problems with minimal effort. Let's say our address class looks 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;Address&lt;/span&gt;
  &lt;span class="n"&gt;before_save&lt;/span&gt; &lt;span class="ss"&gt;:set_geolocation&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;set_geolocation&lt;/span&gt;
    &lt;span class="c1"&gt;# Hit some API or something...&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;What we can do is add an &lt;code&gt;attr_accessor&lt;/code&gt; to control whether we want to perform geolocation and then condition the callback on that attribute. (&lt;code&gt;attr_accessor :disable_geolocation&lt;/code&gt; is a handy shorthand for defining a &lt;code&gt;disable_geolocation&lt;/code&gt; reader method and a &lt;code&gt;disable_geolocation=&lt;/code&gt; writer method.)&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;Address&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:disable_geolocation&lt;/span&gt;
  &lt;span class="n"&gt;before_save&lt;/span&gt; &lt;span class="ss"&gt;:set_geolation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;unless: :disable_geolocation&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;set_geolocation&lt;/span&gt;
    &lt;span class="c1"&gt;# Hit some API or something...&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;Now when updating or creating our addresses we can pass in this attribute to control whether geolocation is performed:&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;# This one will still cause geolocation:&lt;/span&gt;
&lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="ss"&gt;line1: &lt;/span&gt;&lt;span class="s2"&gt;"910 Government St"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;city: &lt;/span&gt;&lt;span class="s2"&gt;"Victoria"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;province: &lt;/span&gt;&lt;span class="s2"&gt;"British Columbia"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;country: &lt;/span&gt;&lt;span class="s2"&gt;"Canada"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Here we prevent geolocation from running:&lt;/span&gt;
&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="ss"&gt;disable_geolocation: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;line1: &lt;/span&gt;&lt;span class="s2"&gt;"1328 Douglas St"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This works because ActiveRecord methods like &lt;code&gt;update&lt;/code&gt; and &lt;code&gt;create&lt;/code&gt; basically just assign the values you pass in, so it doesn't matter that &lt;code&gt;disable_geolocation&lt;/code&gt; isn't backed by a database column. This also means that you can update your factory definitions:&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;FactoryBot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="ss"&gt;:address&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"Jardo Namron"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;sequence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:street_address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; Fake St."&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"Vancouver"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;province&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"British Columbia"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;country&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"Canada"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;disable_geolocation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When you create addresses using the factory you won't get geolocation by default, but can opt in as needed.&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;# No geolocation!&lt;/span&gt;
&lt;span class="n"&gt;address1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;FactoryBot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Yes geolocation!&lt;/span&gt;
&lt;span class="n"&gt;address2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;FactoryBot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;disable_geolocation: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This pattern comes in really handy when you don't have the time to make the larger architectural changes to remove the offending callbacks altogether. It's definitely a hack; externally controlling an object's behaviour like this is an antipattern by my standards, but it extends well to more complex situations and cleanly addresses the immediate problem, so I hope you find it useful.&lt;/p&gt;

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