One day, when I was explaining how the routing > params > controllers > views
work,
I wanted to keep things as simple as possible,
so I needed a way to flexibly “mock” AR objects.
And the JavaScript-like objects would be a good-enough solution for it.
let obj = {}
obj.some_field
// undefined - N.B. no error raised
obj.some_field = "some value"
obj.some_field
// "some value" - JS "magic"
In contrast, the Ruby’s Object does not allow it.
The nearest Ruby’s type to flexibly fake a model is Hash
.
But you need to use those [...]
square brackets with key names
obj = {}
obj[:some_field]
# nil
obj[:some_field] = "some value"
obj[:some_field]
# "some value"
which breaks the controller and views code that expects it to be like this
<%= user.name %>
# and not
<%= user[:name] %>
It turns out that the Ruby on Rails already has this JS-like functionality.
Enter the ActiveSupport::OrderedOptions < Hash
obj = ActiveSupport::OrderedOptions.new
obj.some_field
# nil
obj.some_field = "some value"
obj.some_field
# "some value"
And it has a “bonus”. If you need it to raise an exception when the value is blank, just add a bang !
obj.some_empty_field!
# => raises KeyError: :some_empty_field is blank
Also, it has a useful “sibling” ActiveSupport::InheritableOptions
which allows to initialize from other such objects or Hash
-es.
obj = ActiveSupport::InheritableOptions.new({ some_field: "some value" })
obj.some_field
# "some value"
obj.some_empty_field
# nil
Yay 🎉
Disclaimer: I know that it is best to fake the AR models by introducing step by step all of the parts of the ActiveModel into a PORO. But for a person that’s new to the Rails it would be an unnecessary mental (over)loading at this stage. And the person already new the JavaScript 😅 so the choice was obvious.