1

这里的交互有点复杂,所以请耐心等待。我正在与 Spree 合作。Spree 在其一些模型中使用了 delegate_belongs_to,包括“Spree::Variant”。'delegate_belongs_to :product, :available_on (...)' 在原始类主体中被调用。

我希望变体能够有自己的 available_on 日期。delegate_belongs_to 像这样注入自己:

module DelegateBelongsTo
  extend ActiveSupport::Concern
  module ClassMethods
    #...
    def delegate_belongs_to(association, *attrs)     
      #...
    end
  end
end

ActiveRecord::Base.send :include, DelegateBelongsTo  

我不希望重写整个变体类来删除这个参数。这是我最近的尝试之一:

Spree::Variant.class_eval do
  class << self
    alias_method :original_dbt, :delegate_belongs_to

    def delegate_belongs_to(association, *attrs)
      attrs.delete [:available_on]
      original_dbt(association, attrs)
    end
  end

  attr_accessible :available_on
  #...
end

我已经尝试了许多变体。我不确定是不是因为它在 class_eval 中,如果执行顺序有问题,或者什么,但我似乎无法覆盖这个方法。我在这里不明白什么?

谢谢。

4

5 回答 5

1

我通常在 lib 中执行此操作,因此我确信我的更改会在所有初始化程序之后进行评估。

  1. 允许加载 lib 文件application.rb

    # ...
    module App
      class Application < Rails::Application
        # ...        
        config.autoload_paths += %W(#{config.root}/lib)
        # ...
        require 'spree_variants'
      end
    end
    
  2. 创建lib/spree_variants.rb包含内容的文件

    require 'spree_core'
    
    module SpreeOldPriceProducts
      class Engine < Rails::Engine
        def self.activate
          Variant.class_eval do
            alias_method :original_dbt, :delegate_belongs_to
    
            def delegate_belongs_to(association, *attrs)
              attrs.delete [:available_on]
              original_dbt(association, attrs)
            end
          end
        end
    
        config.to_prepare &method(:activate).to_proc
      end
    end
    

大约 2 个月前,我用 rails 3.0.9 和 spree_core 0.60.1 做过类似的事情,所以我的回答对你来说可能没用,但也许会给你一些指导。

于 2012-07-19T20:55:54.350 回答
1

我最终只是在我自己的 app/models 目录中覆盖了 Spree::Variant。

为了删除一个参数,我真的不希望这样做,但问题是狂欢模型似乎在实例化时被加载到内存中,并且由于 Ruby 具有可执行的类主体,原始的 delegate_belongs_to 调用被触发(并且触发一些副作用),然后我才能将其关闭。

该方法正在被覆盖,我可以确认,但到那时为时已晚。我尝试设置初始化程序来解决这个问题并没有成功。

据我所知,任何面临类似情况的人都必须更换整个班级。如果有人有更好的解决方案,我想暂时保留这个问题。感谢您的回复。

于 2012-07-20T01:10:38.317 回答
1

这不是一个真正的答案,我指出我已经尝试过

我不确定这会有多大帮助,但我拿了你的代码并简化了一点,看看覆盖该方法是否可行,它确实有效,这意味着你的方法是正确的,如果你直接覆盖该类,那么它将调用新方法。

module DelegateBelongsTo

  module ClassMethods
    def delegate_belongs_to(association, *attrs)     
      p "METHOD INSIDE MODULE"
    end
  end

  def self.included(base)
    base.extend(ClassMethods)
  end
end

module Spree
  class Variant
    include DelegateBelongsTo

    def self.some_method
      delegate_belongs_to("foo", "bar")
    end
  end
end

Spree::Variant.some_method  #METHOD INSIDE MODULE


Spree::Variant.class_eval do
  class << self
    alias_method :original_dbt, :delegate_belongs_to
    def delegate_belongs_to(association, *attrs)
       p "OVERWRITTEN METHOD"
      original_dbt(association, *attrs)
    end
 end
end

Spree::Variant.some_method # "OVERWRITTEN METHOD", "METHOD INSIDE MODULE"
于 2012-07-19T20:48:52.233 回答
1

我认为我没有完全理解您的问题,但是每当我看到有人使用 class_eval 或 alias_method 时,我认为必须有更好的方法。您是否尝试过覆盖类中的方法并仅调用 super?

class MyModel < ActiveRecord::Base
  def self.delegate_belongs_to(association, *attrs)
    attrs.delete [:available_on]
    super(association, attrs)
  end
end
于 2012-07-19T21:02:12.783 回答
0

对于正在寻找可行解决方案的任何人,删除 getter 和 setter 将恢复为 ActiveRecord 关联。

`DATETIME_add_to_variant.rb`:
class AddToVariant < ActiveRecord::Migration
    def change
        add_column :spree_variants, :description, :text
        add_column :spree_variants, :available_on, :datetime
    end
end

`variant_decorator.rb`:
Spree::Variant.class_eval do
    remove_method :description
    remove_method :description=

    remove_method :available_on
    remove_method :available_on=
end
于 2015-07-26T02:04:36.963 回答