Reputation: 601
The interactions are a bit complicated here, so bear with me. I'm working with Spree. Spree uses delegate_belongs_to in some of its models, including 'Spree::Variant'. 'delegate_belongs_to :product, :available_on (...)' is being called in the original class body.
I would like for variants to be able to have their own available_on date. delegate_belongs_to is injecting itself like so:
module DelegateBelongsTo
extend ActiveSupport::Concern
module ClassMethods
#...
def delegate_belongs_to(association, *attrs)
#...
end
end
end
ActiveRecord::Base.send :include, DelegateBelongsTo
I would prefer not to override the entire variant class to remove this one argument. This is one of my most recent attempts:
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
I've tried a number of variations on this. I'm not sure if it's because it's in a class_eval, if there's some issue with the order of execution, or what, but I can't seem to override this method. What am I failing to understand here?
Thanks.
Upvotes: 2
Views: 5613
Reputation: 88
For anyone looking for a working solution, removing the getter and setter reverts to the ActiveRecord association.
`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
Upvotes: 0
Reputation: 601
I ended up just overriding Spree::Variant in my own app/models directory.
I really would have preferred not to do this for the sake of removing one argument, but the thing is that the spree models seem to get loaded into memory on instantiation, and since Ruby has executable class bodies, the original delegate_belongs_to call is fired (and triggers a number of side effects) before I can switch it out.
The method is being overridden, I can confirm, but it's too late by then. My attempts with setting up an initializer to get around this weren't successful.
As far as I can tell, anyone facing a similar situation just has to replace the entire class. I'd like to leave this question open for a bit in case someone has a better solution. Thanks for the responses.
Upvotes: 1
Reputation: 51717
I don't think I fully understand your problem, but whenever I see someone using class_eval or alias_method, I think there has to be a better way. Have you tried overriding the method in your class and just calling super?
class MyModel < ActiveRecord::Base
def self.delegate_belongs_to(association, *attrs)
attrs.delete [:available_on]
super(association, attrs)
end
end
Upvotes: 1
Reputation: 3761
I usually do it in lib, so I'm sure that my changes are evaluated after all initializers.
allow load lib files in application.rb
# ...
module App
class Application < Rails::Application
# ...
config.autoload_paths += %W(#{config.root}/lib)
# ...
require 'spree_variants'
end
end
create file lib/spree_variants.rb
with content
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
I was done something like that about 2 months ago with rails 3.0.9 and spree_core 0.60.1 so my answer can be useless for you, but maybe gives you some directions.
Upvotes: 1
Reputation: 84180
This isn't really an answer, I am pointing out what I have tried
I am not sure how much this will help, but I took your code and simplified it a bit to see if overwriting the method would work and it does, which means your method is correct and if you overwrite the class directly then it will call the new method.
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"
Upvotes: 1