Consider using Module#prepend
over Module#class_eval
as it's much cleaner and least invasive method of overriding existing Spree
functionality.
Please, refer to the following articles for additional details about Module#prepend
and method lookup in Ruby:
- https://blog.newrelic.com/2016/12/15/ruby-agent-module-prepend-alias-method-chains/
- https://makandracards.com/makandra/23171-how-ruby-method-lookup-works
- https://stackoverflow.com/questions/4470108/when-monkey-patching-a-method-can-you-call-the-overridden-method-from-the-new-i
Usage example:
module Example # Use your application name as a namespace to avoid collisions with decorators from other sources
module ProductDecorator
# This is the place to define custom associations, delegations, scopes and other ActiveRecord stuff
def self.prepended(base)
base.has_many :comments, dependent: :destroy
base.scope :sellable, -> { base.where(...).order(...) }
base.delegate :something, to: :something
base.singleton_class.prepend ClassMethods
end
# Overriding an existing method
def shipping_category
# We can call original method by using `super`
super || ::Spree::Category.find_or_create_by(name: 'Alternate')
end
# Additional instance method
def int_price
price.to_i
end
# Class methods
module ClassMethods
def ransackable_associations
%w(...)
end
end
end
end
Spree::Product.prepend Example::ProductDecorator
product.int_price #=> 15
product.shipping_category.name #=> 'Default'
Spree::Product.ransackable_associations #=> [.....]
Same technique could be applied to controllers and other classes.