Sublcassing a method? Shared API for methods

Hello,

I have this problem.

An old and huge existing (web) API.

It goes like this:

def foo(i, css_class = '', html_id = '', css_style = '')

The full API is much larger and it involves many methods.

It worked for many years but lately I tried to adapt it
by making the API to some of these methods more flexible.

Like:

foo('Hello world',
  :css_class => 'pentagon',
  :css_style => 'border: 3px solid green',
  :id        => 'test1'
)

Ok, as you can see, a hash as second parameter, and not
all keys are matched 1:1. :id should be :html_id, but
I want flexibility so I can use both :id and :html_id

The thing is - I’d have to modify about 100 different
methods ad-hoc to allow the above.

Is it possible to instead subclass a method from a
base method? Because I am just copy/pasting right
now into all those different methods that they
must respond to a hash as second argument, if a
hash was provided, and it feels like a bad thing
when I have to copy/paste.

You can use alias:

#This is your current method:

def foo(i, css_class = ‘’, html_id = ‘’, css_style = ‘’)
puts “i = #{i.inspect}”
puts “css_class = #{css_class.inspect}”
puts “html_id = #{html_id.inspect}”
puts “css_style = #{css_style.inspect}”
end

alias old_foo foo #Alias your current method

#Now do some preparations:

hash = Hash.new do |this_hash, non_existent_key|
non_existent_key.to_s
end

ALTERNATE_PARAM_NAMES = hash.merge({
:class => ‘css_class’,
:id => ‘html_id’,
:style => ‘css_style’,
})

PARAM_ORDER = [‘css_class’, ‘html_id’, ‘css_style’]

#Redefine your method:

def foo(text, hash)
hash_with_normalized_key_names = {}

hash.each do |key, val|
normalized_key = ALTERNATE_PARAM_NAMES[key]
hash_with_normalized_key_names[normalized_key] = val
end

ordered_param_values = PARAM_ORDER.map do |param|
hash_with_normalized_key_names[param]
end

old_foo(text, *ordered_param_values) #Call the old method
end

#Here is a test to see if it works:

foo(‘Hello world’,
:css_style => ‘border: 3px solid green’,
:id => ‘test1’,
:css_class => ‘pentagon’,
)

–output:–
i = “Hello world”
css_class = “pentagon”
html_id = “test1”
css_style = “border: 3px solid green”

In ruby 2.1, you can make it simpler if you make a slight change to your
definition of foo:

#Use the new style default params:

def foo(i, css_class: ‘’, html_id: ‘’, css_style: ‘’)
puts “i = #{i.inspect}”
puts “css_class = #{css_class.inspect}”
puts “html_id = #{html_id.inspect}”
puts “css_style = #{css_style.inspect}”
end

alias old_foo foo

hash = Hash.new do |this_hash, non_existent_key|
non_existent_key.to_sym #CHANGE HERE
end

ALTERNATE_PARAM_NAMES = hash.merge({
:class => :css_class,
:id => :html_id,
:style => :css_style,
})

def foo(text, hash)
hash_with_normalized_key_names = {}

hash.each do |key, val|
normalized_key = ALTERNATE_PARAM_NAMES[key]
hash_with_normalized_key_names[normalized_key] = val
end

old_foo(text, hash_with_normalized_key_names)
end

foo(‘Hello world’,
‘css_style’ => ‘border: 3px solid green’, #Can use string keys
:id => ‘test1’,
:css_class => ‘pentagon’,
)