Rails猴子补丁只在某些列上

时间:2014-07-29 13:25:00

标签: ruby-on-rails

我试图只在子类有某个列的情况下包含一个模块, 我在初始化器中执行此操作:

 class ActiveRecord::Base
      def self.inherited(subclass)
        subclass.include(MultiTenancy) if subclass.new.respond_to?(:tenant_id)
      end
    end

我一直收到这个错误:

NoMethodError: undefined method `[]' for nil:NilClass

以下是我要导入的模块:

module MultiTenancy
  class TenantNotSetError < StandardError ; end

  def self.included(model)
    model.class_eval do
      belongs_to :tenant
      validates :tenant_id, presence: true
      default_scope -> {
        raise TenantNotSetError.new unless Tenant.current_tenant
        where(tenant_id: Tenant.current_tenant.id)
      }

      def multi_tenanted?
        true
      end
    end
  end
end

我做错了什么。我可以将模块包含在任何子类中,例如。 Klass.include(MultiTenancy)成功,所以问题在于初始化程序。

下面是堆栈跟踪:

from -e:1:in `<main>'2.1.1 :002 > Klass.all
NoMethodError: undefined method `[]' for nil:NilClass
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activerecord-4.1.4/lib/active_record/relation/delegation.rb:9:in `relation_delegate_class'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activerecord-4.1.4/lib/active_record/relation/delegation.rb:112:in `relation_class_for'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activerecord-4.1.4/lib/active_record/relation/delegation.rb:106:in `create'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activerecord-4.1.4/lib/active_record/model_schema.rb:133:in `table_name='
    from /home/lee/Code/mobifit/app/models/klass.rb:25:in `<class:Klass>'
    from /home/lee/Code/mobifit/app/models/klass.rb:22:in `<top (required)>'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:443:in `load'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:443:in `block in load_file'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:633:in `new_constants_in'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:442:in `load_file'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:342:in `require_or_load'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:480:in `load_missing_constant'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:180:in `const_missing'
    from (irb):2
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/commands/console.rb:90:in `start'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/commands/console.rb:9:in `start'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/commands/commands_tasks.rb:69:in `console'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/commands/commands_tasks.rb:40:in `run_command!'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/commands.rb:17:in `<top (required)>'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:247:in `require'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:247:in `block in require'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:232:in `load_dependency'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:247:in `require'
    from /home/lee/Code/mobifit/bin/rails:8:in `<top (required)>'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:241:in `load'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:241:in `block in load'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:232:in `load_dependency'
    from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:241:in `load'
    from /home/lee/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require'
    from /home/lee/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require'
    from -e:1:in `<main>'2.1.1 :003 > 

1 个答案:

答案 0 :(得分:2)

如果您稍微跟踪堆栈跟踪,您可以看到nil不应该是的类是@relation_delegate_cache类。查看the source,您可以看到这通常会在initialize_relation_delegate_cache方法中初始化,该方法在inherited的{​​{1}}挂钩中调用。

ActiveRecord::Delegation::DelegateCache扩展了这个模块,所以通常一个继承自ActiveRecord::Base的类会调用这个继承的钩子。因此,您的代码存在的问题是,您在ActiveRecord::Base上修补了inherited,这意味着ActiveRecord::Base中的钩子不再被调用。

两条评论:

  1. 这就是为什么猴子补丁是一个坏主意,特别是像活跃记录那样基本和复杂的东西。是否有可能以更直接的方式做你想做的事情?
  2. 如果不是,你应该记下ActiveRecord::Delegation::DelegateCache的工作原理并重写你的猴子补丁以混合你自己的模块,而不是仅仅覆盖ActiveRecord::Delegation::DelegateCache的{​​{1}方法。
  3. (2)中的建议看起来像这样:

    ActiveRecord::Base

    然后你的猴子补丁就变成了

    inherited