【问题标题】:Correct use of concern's constants正确使用关注的常量
【发布时间】:2014-09-18 22:05:32
【问题描述】:

我有一个关注点和两个包含它的类。在关注点中,我定义了一个变量。这是关注点和两个模型:

module UserInstance
  extend ActiveSupport::Concern

  included do

    ACTIVE = 'active'
  end
end

class Guardian < ActiveRecord::Base

  include UserInstance

end

class Student < ActiveRecord::Base

  include UserInstance

end

我收到以下警告:

/app/app/models/concerns/user_instance.rb:12: warning: already initialized constant UserInstance::ACTIVE
/app/app/models/concerns/user_instance.rb:12: warning: previous definition of ACTIVE was here

我猜一旦它加载了一个类(如Guardian),它也会加载常量,并且加载另一个类会尝试再次加载常量,然后给出警告。我怎样才能避免这种情况而不必将常量放在两个模型中?谢谢

【问题讨论】:

    标签: ruby-on-rails-4


    【解决方案1】:

    我刚刚在尝试实现关注点时遇到了同样的错误。

    我也遵循了指南,但包含块中的常量似乎没有错误。但是我的日志向我抛出了与您完全相同的错误。

    经过反复试验,我刚刚从块中删除了常量并将其放在外面,例如:

    module UserInstance
      extend ActiveSupport::Concern
    
      included do
    
      end
    
      ACTIVE = 'active'
    end
    

    这样我仍然可以访问常量,但不再出现错误。我不能 100% 确定这是正确的方法,但它确实有效并且我找不到任何错误,所以我会继续使用它。

    如果这也适用于你,我想现在!

    【讨论】:

    • 是的,即使在 Rails 5 上使用关注点,这似乎也是正确的方法
    • 不是真正的错误,只是一个警告。如果你做对了(例如冻结字符串),你应该对常量的重新定义没有问题。然而,这是在关注点中使用常量的正确方法
    • 如果你想深入了解为什么会这样,请阅读stackoverflow.com/questions/66751918/… - Ruby 块具有词法范围,这意味着当你在 included 块中定义一个常量时,它具有相同的效果作为在它之外声明它。所以你实际上只声明了一个常量,即UserInstance::ACTIVE。由于 ActiveSupport::Concern 会在包含块时调用它,因此您实际上会尝试在每次包含时再次定义它,因此会出现警告。
    • 为了更加清楚,您可以访问Student::ACTIVEGuardian::ACTIVE 的原因并不是因为它们是由包含块在这些类中定义的,并且您有两个单独的常量;如前所述,在 included 块内定义该常量并没有改变它的定义范围,这就是问题本身;当 Ruby 在Student::ACTIVE 的词法范围内找不到常量时,它会沿着继承链向上,并且由于您将关注点包含在模型中,它将被搜索,从而找到UserInstance::ACTIVE 并返回它。
    • 起初这也让我感到困惑,因为在included 块内定义一个常量看起来与在块内定义一个局部变量非常相似;众所周知,“块局部变量”是块“词法作用域”规则的一个例外,这意味着除非它们之前在块外定义过,否则只有块才能看到那些局部变量;然而,这不适用于常量,所以如果你在一个块中定义一个常量,它与在该块之外定义它具有完全相同的效果。
    【解决方案2】:

    使用base 为我解决了这个问题。

    
    module UserInstance
      extend ActiveSupport::Concern
    
      # https://api.rubyonrails.org/v6.0.0/classes/ActiveSupport/Concern.html#method-i-included
      included do |base|
        base.const_set :ACTIVE, "active"
    
        # direct const creation in this included block will attach the const to the concern module, not the including class
        # so if you have multiple classes including the same concern, it will try to difine the same const to the same module
        # ACTIVE = "active" 
    
      end
    
    end
    

    【讨论】:

      【解决方案3】:

      Rails 6,这对我有用。 用例是根据更新的属性来标记记录。

      module MyNameSpace
        module MyModule
          extend ActiveSupport::Concern
      
          module ClassMethods
            def syncable(*attrs)
              cattr_accessor(:attrs_to_sync, default: attrs)
            end
          end
      
          included do
            const_set(:REDIS_KEY, "mynamespace.mymodule.#{table_name}")
      
            after_save :enqueue_for_sync
          end
      
          private
      
          def enqueue_for_sync
            return if (saved_changes.keys.map(&:to_sym) & attrs_to_sync).empty?
      
            Redis.current.rpush(self.class::REDIS_KEY, id)
          end
        end
      end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-09
        • 2015-05-13
        • 2017-10-03
        • 1970-01-01
        • 2018-01-24
        • 1970-01-01
        相关资源
        最近更新 更多