Depth Merge
Merge is rather useful, but it has the limitation of staying shallow. Here is a simple extension of an implemented depth merge, with one caveat. Passing in true
to delete_nils
will remove the key if the value is nil
.
Install as a Rails plugin:
./script/plugin install http://source.elevatorfight.com/public/merge_extensions
To gut out the meat just grab this file
An example of use:
# Without trimming nils
hash1 = { :a => "foo", :b => { :c => "bar"} }
hash2 = { :b => { :c => "blah"} }
hash1.depth_merge(hash2) #=> { :a => "foo", :b => { :c => "blah"} }
# With trimming nils
hash1 = { :a => "foo", :b => { :c => "bar"} }
hash2 = { :b => nil }
hash1.depth_merge(hash2, true) #=> { :a => "foo" }
Again a very simple utility, however it comes in handy with testing.
# In Model
class Foo < ActiveRecord::Base
validates_presence_of :col1, :col2
end
# In Unit Test
...
def setup
@default_attributes = { :col1 => "foo", :col2 => "bar" }
end
def test_missing_important_fields
# A model with not only col1 == nil, but nil is never passed to the setter
model = create_model(:col1 => nil)
# do some checking on validity
model = create_model(:col2 => nil)
# again checking on validity
end
def test_something_else
model = create_model
end
private
def create_model(opts = {})
SomeModel.new(@default_attributes.depth_merge(opts, true))
end
...
Having a the depth part of the merge many not be handy in that scenario, but in functional tests they can help with composing the get
/post
# In Functional Test
...
def setup
...
@default_params = {
:id => 5,
:foo => {
:col1 => "foo",
:col2 => "bar"
}
}
end
def test_something
post :some_action, params(:foo => { :col1 => nil })
...
end
def test_something_else
post :some_action, params
...
end
private
def params(opts = {})
@default_params.depth_merge(opts, true)
end
...
The premise for this is so tests become more concise, leaving only relevant information. Which in turn make the tests more readable.