In this brief post I want to report a brief experiece regarding a Ruby 2.0+ feature.
A few weeks ago I wanted to enforce the name of the arguments accepted when initializing a simple PORO.
The object itself was pretty simple and its purpouse was to abstract the access and add some behavior to another one. Guess what, the encapsulated object was an Hash.
Thanks to the fact that I was on Ruby 2.0+ (specifically the last stable, i.e. 2.3.0) I immediately looked at keyword arguments.
This feature is already well known and there are actually many blog posts that describe how to use it. My favorite article is probably Justin’s post.
I’ll introduce some code I’ll explain later:
class Resource
attr_reader :type, :id
def initialize(resource_type:, resource_id:)
@type = resource_type
@id = resource_id
end
%i[name description].each do |method|
define_method(method) { instance.send method }
end
def instance
@instance ||= type.constantize.find(id)
end
def permitted?
permitted_types.include? type
end
def not_permitted?
!permitted?
end
def permitted_types
self.class.permitted_types
end
def self.permitted_types
%w[NewsItem Gate Course Document Gallery Video Survey Content]
end
end
In this example I’ve created a simple PORO which can be used to interact with different ActiveRecord resources
such as NewsItem, Gate, Course, Document Gallery, Video, Survey and Content.
I wrote the code with some specs and so I felt I had my back somehow guarded.
Despite that, when I put the object in use, I stumbled in a banal ArgumentError
. The code was actually a simple initialization of my object: Resource.new home_block_params
I knew that home_block_params
was an Hash
and I also knew that it was composed by the expected key and value pairs, i.e. resource_type
and resource_id
.
You will probably have already realized what I clumsily missed: the nature of the Hash
keys.
They were indeed strings while they had to be symbols!
I wrote some specs (just for fun ;)) to clear the things up:
describe '#initialize' do
it 'raises an ArgumentError if no resource_type and resource_id arguments has been specified' do
expect do
described_class.new
end.to raise_error(ArgumentError)
end
it 'raises an ArgumentError if resource_type and resource_id arguments has been specified as strings' do
expect do
described_class.new('resource_type' => 'News', 'resource_id' => 1)
end.to raise_error(ArgumentError)
end
it 'does not raise any error if resource_type and resource_id arguments has been specified as strings' do
expect do
permitted_home_block_resource
end.not_to raise_error
end
end
The quick solution in my case was to rely on Hash#symbolize_keys
. Unfortunately this is something given by Ruby On Rails.
Anyway the take home message here is to properly refresh your memory when dealing with some midly new language features and to always consider the nature of Hash
objects keys!
Leave a Reply