views:

74

answers:

1

Assume I have a family of related modules:

module Has_Chocolate
   def has_chocolate?
      true
   end
end

module Has_Cake
   def has_cake?
      true
   end
end

. . .

How would I construct a template module Has_*Something* where Something would be a parameter to the module?

+4  A: 

Modules are constants in their encapsulating context, which for the top level is Kernel. That lets us get the module with const_get. Try this:

module Has_Something
  def has(*items)
    items.each do |item|
      mod = Kernel.const_get("Has_" + item.to_s.capitalize)
      instance_eval { include mod }
    end
  end
end

class Baker
  extend Has_Something
  has :cake
end

class CandyMan
  extend Has_Something
  has :chocolate
end

class ChocolateCake
  extend Has_Something
  has :cake, :chocolate
end

If you prefer include over extend, you can do that, too:

module Has_Something
  def self.included(base)
    base.extend HasTemplate
  end

  module HasTemplate
    def has(*items)
      items.each do |item|
        mod = Kernel.const_get("Has_" + item.to_s.capitalize)
        instance_eval { include mod }
      end
    end
  end
end

class Baker
  include Has_Something
  has :cake
end

class CandyMan
  include Has_Something
  has :chocolate
end

class ChocolateCake
  include Has_Something
  has :cake, :chocolate
end

In either case, this code is the same:

steve = Baker.new
bob = CandyMan.new
delicious = ChocolateCake.new
steve.has_cake? && bob.has_chocolate?  # => true
delicious.has_cake? && delicious.has_chocolate?  #=> true

EDIT:

Based upon your comment, what you're looking for is a way to automatically create methods of the format has_something?. This is even easier to do:

module Has_Something
  def has (*items)
    items.each do |item|
      method_name = ('has_' + item.to_s + '?').to_sym
      send :define_method, method_name do
        true
      end
    end
  end
end

class Baker
  extend Has_Something
  has :cake
end

class CandyMan
  extend Has_Something
  has :chocolate
end

class ChocolateCake
  extend Has_Something
  has :cake, :chocolate
end

steve = Baker.new
bob = CandyMan.new
delicious = ChocolateCake.new

steve.has_cake? && bob.has_chocolate?  # => true
delicious.has_cake? && delicious.has_chocolate?  # => true
Pesto
Thank you.I think I see where you're going here.As far as I understand the code, the 'original' modules *Has_Cake, Has_chocolate, ...* still have to be present.Maybe I was not very clear in the post, but what I'm looking for is a way for *creating* them dynamically, as needed.I tried it like this:module Has_Something def has(*items) items.each do |item| meth = "has_" + item.to_s.downcase + "?" instance_eval {def meth ; return true ; end } end endend...but it doesnt workany suggestions?
3D-Grabber
See the additional solution, which doesn't require the original modules.
Pesto