<?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: Adam Stomski</title>
    <description>The latest articles on DEV Community by Adam Stomski (@adamstomski).</description>
    <link>https://dev.to/adamstomski</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%2F613259%2F2cfc7d1f-bc1d-47dc-a92f-e2ba74864501.jpg</url>
      <title>DEV Community: Adam Stomski</title>
      <link>https://dev.to/adamstomski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adamstomski"/>
    <language>en</language>
    <item>
      <title>Enhance repository pattern with refinements</title>
      <dc:creator>Adam Stomski</dc:creator>
      <pubDate>Tue, 03 Aug 2021 14:55:03 +0000</pubDate>
      <link>https://dev.to/adamstomski/enhance-repository-pattern-with-refinements-5fn3</link>
      <guid>https://dev.to/adamstomski/enhance-repository-pattern-with-refinements-5fn3</guid>
      <description>&lt;p&gt;Recently I'm spending a lot of time on thinking how to enforce some rules and boundaries in a significant Ruby project, but in a way that can be applied only to specific parts of application. An interesting idea came to my mind today so I went into code and tried it out.&lt;/p&gt;

&lt;p&gt;Disclaimer: This is just an idea, I have not tested it in a production at all (although tested it on a production codebase)&lt;/p&gt;

&lt;h2&gt;
  
  
  Repository pattern
&lt;/h2&gt;

&lt;p&gt;To decouple you persistence layer from domain layer, you can use repositories. Basic repository with ActiveRecord might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostRepository&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;all_published&lt;/span&gt;
    &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;published: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The advantage of this pattern is that you decouple your persistence logic into one place. The problem is (in the context of this post) that you still return ActiveRecord objects, which then can be misused and call persistence methods inside you business logic code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refinements
&lt;/h2&gt;

&lt;p&gt;Refinements were added into Ruby to solve for a global "monkey patching" - extending classes globally. The whole idea can be read in the &lt;a href="https://ruby-doc.org/core-3.0.0/doc/syntax/refinements_rdoc.html"&gt;first paragraphs in the documentation&lt;/a&gt;. A short example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;LoudInteger&lt;/span&gt;
  &lt;span class="n"&gt;refine&lt;/span&gt; &lt;span class="no"&gt;Integer&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;
      &lt;span class="s2"&gt;"hello, &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hello&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; NoMethodError (undefined method `hello' for 11:Integer)&lt;/span&gt;

&lt;span class="n"&gt;using&lt;/span&gt; &lt;span class="no"&gt;LoudInteger&lt;/span&gt;

&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hello&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; "hello, 11!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What I found interesting is that you can enable refinements at the top level and they will be applied for a single file, which I feel might be very useful.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You may activate refinements at top-level, and inside classes and modules. You may not activate refinements in method scope. Refinements are activated until the end of the current class or module definition, or &lt;strong&gt;until the end of the current file if used at the top-level.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I thought it might be interesting to connect those two things together.&lt;/p&gt;

&lt;h2&gt;
  
  
  The domain layer refinement boundary
&lt;/h2&gt;

&lt;p&gt;I've started with a simple module called &lt;code&gt;DomainLayer&lt;/code&gt;. The api I want is very simple - at the beginning of a file that encapsulates domain logic I will call my refinements. When there is persistence method called inside this file, I want it to raise exception. Like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="n"&gt;using&lt;/span&gt; &lt;span class="no"&gt;DomainLayer&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostService&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# boom!&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save!&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;The code to achieve this is actually rather simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;DomainLayer&lt;/span&gt;
  &lt;span class="no"&gt;DomainLayerAccessError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Class&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="no"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;refine&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;DomainLayerAccessError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"don't use persistence methods in domain layer!"&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;a simple test can show that it is working correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failure/Error: raise DomainLayerAccessError, "don't use persistence methods in domain layer!"

DomainLayer::DomainLayerAccessError:
  don't use persistence methods in domain layer!
# ./app/lib/domain_layer.rb:8:in `find'
# ./app/domains/cms/post_service.rb:7:in `publish'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have to use the repository to make it work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="n"&gt;using&lt;/span&gt; &lt;span class="no"&gt;DomainLayer&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostService&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="ss"&gt;post_repository: &lt;/span&gt;&lt;span class="no"&gt;PostRepository&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="vi"&gt;@post_repository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post_repository&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;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;
    &lt;span class="n"&gt;post_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

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

  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:post_repository&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Finished in 0.32838 seconds (files took 0.58 seconds to load)
1 example, 0 failures
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks good, simple and easy to understand.&lt;/p&gt;

&lt;p&gt;I've added the "repository pattern" in the title because of the example, but this pattern obviously can be used to much more than just repositories. I can imagine all the layers to enforce its own rules. Not only layers but, if you keep you domains/subdomains/modules separate, I guess you could enforce what can be used publicly. In fact Shopify for example created a &lt;a href="https://shopify.engineering/enforcing-modularity-rails-apps-packwerk"&gt;specially designed tool just for this problem&lt;/a&gt;. Now we begin to have some language features to support this idea.&lt;/p&gt;

&lt;p&gt;I'm a big fan of these kind of solutions because of its opt-in nature. Just like with &lt;a href="https://github.com/ruby/rbs"&gt;rbs&lt;/a&gt; in separate files, just for most important code, I think there is a place for ideas with refinements for this code as well.&lt;/p&gt;

&lt;p&gt;I will definitely explore more :).&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>ddd</category>
    </item>
    <item>
      <title>ActiveRecord - Value Objects with serialize</title>
      <dc:creator>Adam Stomski</dc:creator>
      <pubDate>Tue, 13 Apr 2021 06:41:46 +0000</pubDate>
      <link>https://dev.to/adamstomski/activerecord-value-objects-with-serialize-28d7</link>
      <guid>https://dev.to/adamstomski/activerecord-value-objects-with-serialize-28d7</guid>
      <description>&lt;p&gt;I love Value Objects. They can improve and clean up your project by insane amount, yet they are simple and easy to understand for almost everyone. Not only easy to understand but actually easy to write as well!&lt;/p&gt;

&lt;p&gt;But wait, most of the Value Object implementations are just Plain Ruby Objects right? What if I use ActiveRecord? &lt;/p&gt;

&lt;p&gt;Doesn't matter if you like it, or just have to live with the legacy written by hardcore Rails developer - ActiveRecord has a way to inject Value Objects. In both cases they will improve your life and domain model of your code. Lets take a look at one of the simplest possibilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  The underestimated serialize
&lt;/h3&gt;

&lt;p&gt;One of the simplest method to use is the &lt;a href="https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html"&gt;serialize&lt;/a&gt; method. All it does it is loading data into object using &lt;code&gt;load&lt;/code&gt; method and dumps the data into db using &lt;code&gt;dump&lt;/code&gt;. Its simple yet powerful mechanism. Lets get some example (implemented as internal class for simplicity):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateMeetings&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;6.0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
    &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:meetings&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;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;integer&lt;/span&gt; &lt;span class="ss"&gt;:time_limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;

      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timestamps&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meeting&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TimeLimit&lt;/span&gt;
    &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Comparable&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;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time_limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;time_limit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time_limit&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;time_limit&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;time_limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@time_limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&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;to_i&lt;/span&gt;
      &lt;span class="n"&gt;time_limit&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;&amp;lt;&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;to_i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&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;minimum?&lt;/span&gt;
      &lt;span class="n"&gt;time_limit&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&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;maximum?&lt;/span&gt;
      &lt;span class="n"&gt;time_limit&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;60&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;seconds&lt;/span&gt;
      &lt;span class="n"&gt;time_limit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seconds&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

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

    &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:time_limit&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;serialize&lt;/span&gt; &lt;span class="ss"&gt;:time_limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;TimeLimit&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 there is implementation of &lt;code&gt;TimeLimit&lt;/code&gt; value object, which allows us to have domain related behaviors (&lt;code&gt;minimum?&lt;/code&gt;, &lt;code&gt;maximum?&lt;/code&gt;, &lt;code&gt;seconds&lt;/code&gt; methods). Its then simply registered into ActiveRecord by using &lt;code&gt;serialize&lt;/code&gt; macro.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;serialize&lt;/span&gt; &lt;span class="ss"&gt;:time_limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;TimeLimit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets see it in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; :004 &amp;gt; meeting = Meeting.new
 :005 &amp;gt; meeting
 =&amp;gt; #&amp;lt;Meeting id: nil, time_limit: #&amp;lt;Meeting::TimeLimit:0x00007f9744443120 @time_limit=1&amp;gt;, created_at: nil, updated_at: nil&amp;gt;
 :006 &amp;gt; meeting.time_limit.minimum?
 =&amp;gt; true 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see its clearly visible that there is separate object used. We can call all the methods on it directly, without any Rails magic involved. Lets try to change the value using setter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; :014 &amp;gt; meeting.time_limit = 5
 :015 &amp;gt; meeting
 =&amp;gt; #&amp;lt;Meeting id: nil, time_limit: #&amp;lt;Meeting::TimeLimit:0x000056020ecfec50 @time_limit=5&amp;gt;, created_at: nil, updated_at: nil&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interesting right? A new object with our value! ActiveRecord is using the &lt;code&gt;load&lt;/code&gt; method to create it for us. Lets do a save then!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; :016 &amp;gt; meeting.save
   (0.1ms)  begin transaction
  Meeting Create (0.5ms)  INSERT INTO "meetings" ("time_limit", "created_at", "updated_at") VALUES (?, ?, ?)  [["time_limit", 5], ["created_at", "2021-04-12 20:32:06.867606"], ["updated_at", "2021-04-12 20:32:06.867606"]]
   (17.9ms)  commit transaction
 =&amp;gt; true
 :017 &amp;gt; meeting
 =&amp;gt; #&amp;lt;Meeting id: 2, time_limit: #&amp;lt;Meeting::TimeLimit:0x000056020ebf89c8 @time_limit=5&amp;gt;, created_at: "2021-04-12 20:32:06", updated_at: "2021-04-12 20:32:06"&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great so it saves a correct value! Turns out it used our &lt;code&gt;dump&lt;/code&gt; method correctly. But this is the same record in memory, lets fetch our newly created meeting from db instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; :024 &amp;gt; meeting = Meeting.last
  Meeting Load (0.2ms)  SELECT "meetings".* FROM "meetings" ORDER BY "meetings"."id" DESC LIMIT ?  [["LIMIT", 1]]
 :025 &amp;gt; meeting
 =&amp;gt; #&amp;lt;Meeting id: 2, time_limit: #&amp;lt;Meeting::TimeLimit:0x000056020b8a3618 @time_limit=5&amp;gt;, created_at: "2021-04-12 20:32:06", updated_at: "2021-04-12 20:32:06"&amp;gt; 
 :026 &amp;gt; meeting.time_limit.seconds
 =&amp;gt; 300 seconds 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is so cool right? Simple yet powerful interface, almost no magic involved. This allows us to create Plain Old Ruby Objects and inject them into our ActiveRecord based entities without problems. No need to spread your &lt;code&gt;maximum&lt;/code&gt; logic all over the place (Service Objects I'm looking at you...), we can encapsulate them and still use our beloved(hated?) models!&lt;/p&gt;




&lt;p&gt;For "get rid of AR" developers: you can use this mechanism to inject a domain modeling into legacy code without problems. Ruby duck typing is your friend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# using ActiveRecord:&lt;/span&gt;
&lt;span class="n"&gt;meeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time_limit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seconds&lt;/span&gt;

&lt;span class="c1"&gt;# using some PORO or w/e:&lt;/span&gt;
&lt;span class="n"&gt;meeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time_limit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seconds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, there is no difference, you can refactor at any point of time, and your code will remain untouched. Isn't that beautiful?&lt;/p&gt;




&lt;p&gt;There is one thing to mention that might catch you off guard: object creation validation. Lets modify our example a bit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TimeLimit&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;

    &lt;span class="no"&gt;InvalidLimitError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Class&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="no"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;)&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;time_limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;InvalidLimitError&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time_limit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="vi"&gt;@time_limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time_limit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

   &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And try to create invalid limit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; :034 &amp;gt; meeting.time_limit = 100
 :035 &amp;gt; meeting.id
 :036 &amp;gt; meeting.time_limit
Traceback (most recent call last):
        3: from app/models/meeting.rb:10:in `load'
        2: from app/models/meeting.rb:10:in `new'
        1: from app/models/meeting.rb:16:in `initialize'
Meeting::TimeLimit::InvalidLimitError (Meeting::TimeLimit::InvalidLimitError)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, ActiveRecord does a lazy loading on serialized attributes, so the &lt;code&gt;load&lt;/code&gt; method is called after first call - &lt;strong&gt;not on the assignment&lt;/strong&gt;. Keep that in mind when writing this kind of code.&lt;/p&gt;




&lt;p&gt;Useful links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html"&gt;serialize documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://martinfowler.com/bliki/ValueObject.html"&gt;Value Object by Martin Fowler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/zverok/good-value-object"&gt;Good Value Object Conventions for Ruby&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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