【问题标题】:Ruby on Rails, including a module with argumentsRuby on Rails,包括一个带参数的模块
【发布时间】:2014-11-16 19:15:05
【问题描述】:

有没有办法在包含 ruby​​ 模块时使用参数?我有一个 Assetable 模块,它包含在许多类中。我希望能够即时生成 attr_accessor。

module Assetable
  extend ActiveSupport::Concern

  included do 
    (argument).times do |i| 
      attr_accessor "asset_#{i}".to_sym
      attr_accessible "asset_#{i}".to_sym
    end
  end
end 

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

    有一个技巧:创建一个从模块继承的类,这样你就可以像类一样将任何参数传递给模块。

    class Assetable < Module
      def initialize(num)
        @num = num
      end
    
      def included(base)
        num = @num
    
        base.class_eval do
          num.times do |i|
            attr_accessor "asset_#{i}"
          end
        end
      end
    end
    
    class A
      include Assetable.new(3)
    end
    
    a = A.new
    a.asset_0 = 123
    a.asset_0 # => 123
    

    详细信息写在http://kinopyo.com/en/blog/ruby-include-module-with-arguments,希望对你有用。

    【讨论】:

    • 妈的,儿子!很好的解决方案。
    【解决方案2】:

    包含模块时无法传递参数。接下来最好的事情是定义一个类方法,让您在之后创建所需的内容:

    module Assetable
      extend ActiveSupport::Concern
      module ClassMethods
        def total_assets(number)
          number.times do |i|
            attr_accessor "asset_#{i}"
            attr_accessible "asset_#{i}"
          end
        end
      end
    end
    
    class C
      include Assetable
      total_assets 3
    end
    
    o = C.new
    o.asset_2 = "Some value."
    o.asset_2  #=> "Some value."
    

    在关注点覆盖included 方法时也要小心,因为ActiveSupport::Concern 也使用它。您应该在被覆盖的方法中调用super 以确保正确初始化。

    【讨论】:

    • 仅当您在关注点中定义了included 挂钩时,此实现并非如此。
    • 我注意到 Audited gem 无需使用 'include' 行就可以做到这一点:仅审核::name,你知道这是怎么做的吗?
    【解决方案3】:

    您可以在不污染全局命名空间的情况下生成和包含匿名模块:

    module Assetable
      def self.[](argument)
        Module.new do
          extend ActiveSupport::Concern
    
          included do 
            (argument).times do |i| 
              attr_accessor :"asset_#{i}"
              attr_accessible :"asset_#{i}"
            end
          end
        end
      end
    end
    
    class Foo
      include Assetable[5]
    end
    

    【讨论】:

      【解决方案4】:

      您不能将参数传递给模块。事实上,除了消息发送之外,您不能将参数传递给任何东西。

      所以,你必须使用消息发送:

      module Kernel
        private def Assetable(num)
          @__assetable_cache__ ||= []
          @__assetable_cache__[num] ||= Module.new do
            num.times do |i|
              attr_accessor   :"asset_#{i}"
              attr_accessible :"asset_#{i}"
            end
          end
        end
      end
      
      class Foo
        include Assetable 3
      end
      

      注意:我根本不明白您为什么需要在这里使用ActiveSupport::Concern,但添加回来很容易。

      【讨论】:

      • 谢谢,我已经实现了 Gabriel de Oliveira 的答案,它有效。每种解决方案的优缺点是什么?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-08
      • 1970-01-01
      • 2011-07-03
      • 2010-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多