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
1
module MyStore
2
module Spree
3
module ProductDecorator
4
def self.prepended(base)
5
base.before_validation :strip_whitespaces
6
end
7
​
8
def some_method
9
...
10
end
11
​
12
protected
13
​
14
def strip_whitespaces
15
...
16
end
17
end
18
end
19
end
20
​
21
::Spree::Product.prepend MyStore::Spree::ProductDecorator if ::Spree::Product.included_modules.exclude?(MyStore::Spree::ProductDecorator)
Copied!

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:
1
bundle exec rails g migration CreateVideos url:string product:references
2
bundle exec rails db:migrate
Copied!
Add new model to app/models/videos.rb:
1
class Video < ApplicationRecord
2
belongs_to :product, class_name: 'Spree::Product'
3
end
Copied!
Finally add the association in ProductDecorator in app/models/my_store/spree/product_decorator.rb:
1
module MyStore
2
module Spree
3
module ProductDecorator
4
def self.prepended(base)
5
base.has_many :videos, class_name: 'Video', foreign_key: 'product_id', dependent: :destroy
6
end
7
end
8
end
9
end
10
​
11
::Spree::Product.prepend MyStore::Spree::ProductDecorator if ::Spree::Product.included_modules.exclude?(MyStore::Spree::ProductDecorator)
Copied!

Extending Controllers

Adding a custom action to the ProductsController: app/controllers/my_store/spree/products_controller_decorator.rb
1
module MyStore
2
module Spree
3
module ProductsControllerDecorator
4
def some_action
5
...
6
end
7
end
8
end
9
end
10
​
11
::Spree::ProductsController.prepend MyStore::Spree::ProductsControllerDecorator if ::Spree::ProductsController.included_modules.exclude?(MyStore::Spree::ProductsControllerDecorator)
Copied!
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.
1
module MyStore
2
module Spree
3
module ProductsControllerDecorator
4
def self.prepended(base)
5
base.before_action :load_data, only: :some_action
6
end
7
​
8
def some_action
9
...
10
end
11
end
12
end
13
end
14
​
15
::Spree::ProductsController.prepend MyStore::Spree::ProductsControllerDecorator if ::Spree::ProductsController.included_modules.exclude?(MyStore::Spree::ProductsControllerDecorator)
Copied!
: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.
Last modified 3mo ago