在Ruby中将参数传递给包含的模块?

时间:2010-03-22 08:07:33

标签: ruby dynamic module eval

我希望能够实现像ruby这样的所有优秀插件,以便你可以这样做:

acts_as_commentable
has_attached_file :avatar

但我有一个约束:

  

该辅助方法只能包含一个模块;它无法定义任何变量或方法。

原因是因为,我希望选项哈希定义类似type的东西,并且可以将其转换为20个不同的“主力”模块之一,所有这些都可以归结为像这样的行:

def dynamic_method(options = {})
 include ("My::Helpers::#{options[:type].to_s.camelize}").constantize(options)
end

那些'workhorses'会处理选项,例如:

has_many "#{options[:something]}"

这是结构的样子,我想知道你是否知道拼图中缺少的部分:

# 1 - The workhorse, encapsuling all dynamic variables
module My::Module
  def self.included(base)
    base.extend ClassMethods
    base.class_eval do
      include InstanceMethods
    end
  end

  module InstanceMethods
    self.instance_eval %Q?
      def #{options[:my_method]}
        "world!"
      end
    ?
  end

  module ClassMethods
  end
end

# 2 - all this does is define that helper method
module HelperModule
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods

    def dynamic_method(options = {})
      # don't know how to get options through!
      include My::Module(options)
    end

  end
end

# 3 - send it to active_record
ActiveRecord::Base.send(:include, HelperModule)

# 4 - what it looks like
class TestClass < ActiveRecord::Base
  dynamic_method :my_method => "hello"
end

puts TestClass.new.hello #=> "world!"

%Q?我不完全确定如何使用,但我基本上只是想以某种方式能够将该帮助方法中的options哈希值传递给主力模块。那可能吗?这样,主力模块可以定义各种功能,但我可以在运行时将变量命名为。

3 个答案:

答案 0 :(得分:3)

我正在研究类似的东西,基于我曾经看过的一些聪明的红宝石代码,但现在再也找不到了。我的东西几乎正在工作,但是在你的模块中它会混淆像self.included和self.extended这样的东西,因为有些明显的原因可以解决,但我不喜欢它会有多复杂得到。我认为还有一个我没有想到的技巧会让这项工作更加完美。

所以这可能适用于您,也可能不适合您,但我正在尝试的是根据您的选项动态创建的匿名模块的想法。 (我认为'type'可能是一个保留词,但我不确定,所以让我们说“选项”)。看看这个想法:


Module HelperModule
   def[](options)
      Module.new do 
        include HelperModule
        define_method(:options) { options }
      end
   end

   def options
     raise TypeError.new("You need to instantiate this module with [], man!")
   end
end

obj = Object.new
obj.extend(HelperModule)
obj.options => raises TypeError

obj = Object.new
obj.extend(HelperModule[ :my_option => "my_option" ]
obj.options => { my_option => "my_option }
有点整洁,对吧?但对我来说还不够好,因为你从HelperModule [options]获得的实际模块没有原始HelperModule中的self.included和self.extended。我想我可以在anon模块中定义self.included和self.extended,但我不喜欢代码混淆。我也不喜欢在匿名模块中明确说“包含HelperModule”,我宁愿它像“自我”一样,除了“自我”并不代表那里正确的东西,所以这不起作用。

啊,等等,猜猜我刚才发现:如果这种一般方法看起来应该适合你,它可以由paramix gem提供: http://rubyworks.github.com/paramix/

答案 1 :(得分:2)

您可能希望查看Modularity gem,它正是您想要的。

答案 2 :(得分:0)

ActionModel gem可以帮到你。我在我的项目中使用它。