Smarter creation of redunant methods


#1

I have some methods in a class that look like this:

def h_coil(chan)
raise NoDataError if @h_coils[chan].nil?
@h_coils[chan]
end

def i_coil(chan)
raise NoDataError if @i_coils[chan].nil?
@i_coils[chan]
end

and so on and so forth…

I’ve been refactoring a bit to cut down on the redunancy in the code,
and
I’m wondering your thoughts on the best way of doing this, considering
that all methods have the same “signature”.

Is define_method the smartest way?

pseudo code off the top of my head, probably has errors:

def create_coil_method(name)
define_method(name) do |chan|
iv = ("@" + name.to_s + “s”).to_sym
val = instance_variable_get(iv)
raise NoDataError if val.nil?
val[chan]
end
end

create_coil_method(:h_coil)
create_coil_method(:i_coil)

Thanks,
Caleb


#2

Caleb T. wrote:

end

and so on and so forth…

I’ve been refactoring a bit to cut down on the redunancy in the code,
and I’m wondering your thoughts on the best way of doing this,
considering that all methods have the same “signature”.

Is define_method the smartest way?

You are recreating functionality that’s already there (assuming coils
collections are arrays or hashes):

h={1=>2}
=> {1=>2}

h.fetch 1
=> 2

h.fetch 2
IndexError: key not found
from (irb):7:in `fetch’
from (irb):7
from :0

a=[1]
=> [1]

a.fetch 0
=> 1

a.fetch 1
IndexError: index 1 out of array
from (irb):16:in `fetch’
from (irb):16
from :0

IMHO it’s not worth while to use meta programming in this case but YMMV.
If you use meta programming then I’d probably use a way that makes
sure
the member is there, properly initialized and the getter defined. I’d
probably create a module whose initialize will create member variables
and
which when included will define the create_coil for the including
class…

create_coil_method(:h_coil)
create_coil_method(:i_coil)

Of course you can combine both techniques and save the exception
throwing
part.

Kind regards

robert

#3

On Sat, 17 Dec 2005, Caleb T. wrote:

end

create_coil_method(:i_coil)
you can certainly use metaprogramming here, but you could accomplish the
same
effect with far less code:

 harp:~ > cat a.rb
 class Coil
   class NoDataError < StandardError; end

   attr "coils"

   def initialize
     @coils =
       Hash::new{|h,k| h[k] = Hash::new{|h,k| raise NoDataError, 

“chan=#{ k }”}}
end
end

 coil = Coil::new
 chan = 1
 coil.coils["z"][chan] = 42

 p coil.coils["z"][chan]

 p coil.coils["x"][chan]



 harp:~ > ruby a.rb
 42
 a.rb:8:in `initialize': chan=1 (Coil::NoDataError)
         from a.rb:18

regards.

-a


#4

On Sat, 17 Dec 2005, Caleb T. wrote:

you can certainly use metaprogramming here, but you could accomplish the
same
effect with far less code:

My issue is that I’ve got other methods in the class that use these
particular instance variables, and instead of refactoring all of them in
one fell swoop I’m trying to go in smaller chunks. Mostly because I’m
working on the “production” system (long story), so if break anything I
need to be able to roll it back quickly.

then something like

class Coil
class Error < StandardError; end

 class << self
   def coil_attr *list
     list.flatten.each do |a|
       module_eval <<-code
         def #{ a }_coil chan
           @#{ a }_coil[chan] or raise Error, "chan=\#{ chan }"
         end
       code
     end
   end
 end

 coil_attr %w( a b c x y z )

end

hth.

-a


#5

Thanks - these are some good ideas I hadn’t thought of using before!

Caleb


#6

you can certainly use metaprogramming here, but you could accomplish the
same
effect with far less code:

My issue is that I’ve got other methods in the class that use these
particular instance variables, and instead of refactoring all of them in
one fell swoop I’m trying to go in smaller chunks. Mostly because I’m
working on the “production” system (long story), so if break anything I
need to be able to roll it back quickly.