Idiomatic way to merge "multivalued" options

I’m looking for a nice and idiomatic way to merge options hashes. The
usual ways for scalar-valued options are

def my_method(options = {})
options = {
:foo => ‘something’,
:bar => ‘else’
}.update(options)

end

and

def my_method(options = {})
options = options.reverse_merge(
:foo => ‘something’,
:bar => ‘else’
)

end

The cases I’m interested in are different. For illustration, let’s take
a helper method that inserts a text field into the page and adds the
class attribute “special”. In particular, it should not overwrite
existing class attribute options, only add a new one.

def my_special_text_field1(object, field, options = {})
if options[:class]
options[:class] += ’ special’
else
options[:class] = ‘special’
end
text_field(object, field, options)
end

That’s rather clumsy. How about this, then

def my_special_text_field2(object, field, options = {})
options[:class] =
(String(options[:class]).split(/\s+/) | [‘special’]) * ’ ’
text_field(object, field, options)
end

Ugh. Well, it has the added advantage that duplicates are not added.
Alas, the code is not very obvious. But with a bit of encapsulation and
class opening that can be rectified.

def my_special_text_field3(object, field, options = {})
text_field(object, field,
options.merge_multivalued(:class => ‘special’))
end

class Hash
def merge_multivalued(other_hash)
merge_arrays(other_hash) do |old_value, other_array|
(String(old_value).split(/\s+/) | other_array) * ’ ’
end
end

private

def merge_arrays(other_hash, &block)
  # add values from other_hash to already present values
  merged_old = inject({}) do |memo, (key, value)|
    if other_value = other_hash[key]
      other_array = other_value.kind_of?(Array) ?
        other_value : other_value.split(/\s+/)
      memo[key] = block[value, other_array]
    else
      memo[key] = value
    end
    memo
  end
  # add new values from other_hash
  other_hash.merge(merged_old)
end

end

That does look a bit over-engineered. Here’s a minimalist (and slightly
rogueish) alternative.

def my_special_text_field4(object, field, options = {})
options[:class] = (Array(options[:class]) | [‘special’]) * ’ ’
text_field(object, field, options)
end

After all this, I still can’t say that I’m fully satisfied with any of
the options. I’m looking forward to suggestions.

Michael


Michael S.
mailto:[email protected]
http://www.schuerig.de/michael/

Michael S. wrote:

I’m looking for a nice and idiomatic way to merge options hashes.

The Ruby newsgroup deserves to work on questions like this.

news:comp.lang.ruby


Phlip
Redirecting... ← NOT a blog!!!

Doesn’t it depend on which direction you decide to conduct the merge?
E.g.:

existing_options.merge(new_options)

will overwrite existing keys (which is normally what you want if
existing
options specify defaults). However:

new_options.merge(existing_options)

will cause any keys present in new_options to be overwritten by those in
existing_options (if they exist).

I don’t know if this answers the question, but I hope it helps…

Michael S. wrote:

}.update(options)
...
else
text_field(object, field, options)

  # add values from other_hash to already present values
  # add new values from other_hash

end


View this message in context:
http://www.nabble.com/-Rails--Idiomatic-way-to-merge-"multivalued"-options-tf3133390.html#a8682416
Sent from the RubyOnRails Users mailing list archive at Nabble.com.