Business Logic

Overview

It is highly recommended to use Dependencies and Extensions first rather than to apply patches to Spree Core. Still if you don't find those to be efficient you can pretty much overwrite any part of Spree following this guide.

Extending Classes

All of Spree's business logic (models, controllers, helpers, etc) can easily be extended or overridden to meet your exact requirements using standard Ruby idioms.

Standard practice for including such changes in your application or extension is to create a file within the relevant app/models/spree or app/controllers/spree directory with the original class name with _decorator appended.

Extending Models

Adding a custom method to the Product model: app/models/my_store/spree/product_decorator.rb

module MyStore
module Spree
module ProductDecorator
def self.prepended(base)
base.before_validation :strip_whitespaces
end
​
def some_method
...
end
​
protected
​
def strip_whitespaces
...
end
end
end
end
​
::Spree::Product.prepend MyStore::Spree::ProductDecorator if ::Spree::Product.included_modules.exclude?(MyStore::Spree::ProductDecorator)

Adding new associations to existing models

Assume you want to add a new model called Video associated to Spree::Product. Let's start with creating a database migration:

bundle exec rails g migration CreateVideos url:string product:references
bundle exec rails db:migrate

Add new model to app/models/videos.rb:

class Video < ApplicationRecord
belongs_to :product, class_name: 'Spree::Product'
end

Finally add the association in ProductDecorator in app/models/my_store/spree/product_decorator.rb:

module MyStore
module Spree
module ProductDecorator
def self.prepended(base)
base.has_many :videos, class_name: 'Video', foreign_key: 'product_id', dependent: :destroy
end
end
end
end
​
::Spree::Product.prepend MyStore::Spree::ProductDecorator if ::Spree::Product.included_modules.exclude?(MyStore::Spree::ProductDecorator)

Extending Controllers

Adding a custom action to the ProductsController: app/controllers/my_store/spree/products_controller_decorator.rb

module MyStore
module Spree
module ProductsControllerDecorator
def some_action
...
end
end
end
end
​
::Spree::ProductsController.prepend MyStore::Spree::ProductsControllerDecorator if ::Spree::ProductsController.included_modules.exclude?(MyStore::Spree::ProductsControllerDecorator)

The exact same format can be used to redefine an existing method.

Accessing Product Data

If you extend the Products controller with a new method, you may very well want to access product data in that method. You can do so by using the :load_data before_action.

module MyStore
module Spree
module ProductsControllerDecorator
def self.prepended(base)
base.before_action :load_data, only: :some_action
end
​
def some_action
...
end
end
end
end
​
::Spree::ProductsController.prepend MyStore::Spree::ProductsControllerDecorator if ::Spree::ProductsController.included_modules.exclude?(MyStore::Spree::ProductsControllerDecorator)

:load_data will use params[:id] to lookup the product by its permalink.

Replacing Models or Controllers

If your customizations are so large that you overwrite the majority of a given Model or Controller we recommend to drop the _decorator pattern and overwrite the Model or Controller completely in your project. This will make future Spree upgrades easier.