Forum: Ruby Smarter creation of redunant methods

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Caleb T. (Guest)
on 2005-12-16 17:45
(Received via mailing list)
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
Robert K. (Guest)
on 2005-12-16 18:03
(Received via mailing list)
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
unknown (Guest)
on 2005-12-16 18:21
(Received via mailing list)
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
Caleb T. (Guest)
on 2005-12-16 19:06
(Received via mailing list)
> 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.
unknown (Guest)
on 2005-12-16 19:30
(Received via mailing list)
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
Caleb T. (Guest)
on 2005-12-16 22:59
(Received via mailing list)
Thanks - these are some good ideas I hadn't thought of using before!

Caleb
This topic is locked and can not be replied to.