Metaprogramming to generate list of class-specific attributes

Rubyists:

Here's a test case illustrates the problem. Then we'll have a candidate fix
and some questions.

  class Kozmik
   def self.acts_as_frog(species)
     @@frog_list << species
   end
   @@frog_list = []
   cattr_accessor :frog_list
end

class Toad < Kozmik
   acts_as_frog :bufo
   acts_as_frog :kermit
end

class BullFrog < Kozmik
   acts_as_frog :tree
   acts_as_frog :riparian
end

def test_fun_with_metaclasses
   assert_equal [:tree, :riparian], BullFrog.frog_list
   assert_equal [:bufo, :kermit], Toad.frog_list
end

Suppose we need an 'acts_as_' system to generate some methods for our target
classes. And suppose one method is a list of all the arguments passed to
each acts_as_() in each specific class.

The above test fails because @@ occupies the entire inheritance chain. So
both frog_list() class-methods return [:tree, :riparian, :bufo, :kermit].
The fix seems to be to reach into the meta-class for each derived class:

class Kozmik
   def self.acts_as_frog(species)
     @frog_list ||= []
     @frog_list << species
     self.class.send :attr_accessor, :frog_list
   end
end

Is that as clean and lean as it could be? Is there some way to do this with
even fewer lines?

Next question: Couldn't class Kozmik be a module instead?

···

--
Phlip
http://www.oreilly.com/catalog/9780596510657/
^ assert_xpath

Phlip wrote:
...

class Kozmik
   def self.acts_as_frog(species)
     @frog_list ||=
     @frog_list << species
     self.class.send :attr_accessor, :frog_list
   end
end

Is that as clean and lean as it could be? Is there some way to do this with
even fewer lines?

Next question: Couldn't class Kozmik be a module instead?

Sure...

  module Kozmik
    def acts_as_frog(species)
      @frog_list ||=
      @frog_list << species
      self.class.send :attr_accessor, :frog_list
    end
  end

  class Toad; extend Kozmik
  ...
  end

Or is that cheating somehow?

···

--
        vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Rubyists:

Here is one answering

class Kozmik
   def self.acts_as_frog(species)
     @frog_list ||=
     @frog_list << species
     self.class.send :attr_accessor, :frog_list
   end
end

Is that as clean and lean as it could be? Is there some way to do this with
even fewer lines?

I am not really sure if this works, but couldn't you do:

class Kozmik
   def self.inherited
     module_eval do
       @frog_list =
     end
   end

   def self.acts_as_frog(species)
     @frog_list << species
   end
end

Also, does module_eval only work in a class method? Why can't I do def something; module_eval do puts 5 end; end ?

Ari
-------------------------------------------|
Nietzsche is my copilot

···

On Sep 14, 2007, at 5:00 PM, Phlip wrote:

Joel VanderWerf wrote:

  class Toad; extend Kozmik

extend... different from include, right?

···

--
Phlip

Phlip wrote:

Joel VanderWerf wrote:

  class Toad; extend Kozmik

extend... different from include, right?

If you need it to be #include, you can always call #extend from within #append_features as in ruby-talk:35979.

···

--
        vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407