More at spreecommerce.com: Features | Support | Blog | Demo | Community | Download

Customization Overview

This guide introduces the extension mechanism used to add functionality to Spree and to customize it to a new site design. It discusses the basic ideas behind extensions.

1 Customizing Spree

Spree contains a powerful extension mechanism. This allows us to focus on our mission of building a solid foundation that addresses 90% of the commerce problem. Extensions address the remaining 10%. Extensions provide a logical and consistent way for Spree users to supply any required extra functionality in their application, or to completely change the appearance of their site. Extensions are designed in a way that should be highly intuitive to the experienced Rails programmer. They also provide a convenient way for users to develop and share extensions with other members of the Spree community.

The Spree extension mechanism was based extensively on the work of the Radiant community. The Radiant CMS project has a very similar design philosophy (“no fluff”) and has already solved the problem in a very elegant fashion.

There’s a growing number of extensions in the Spree Extension Registry for you to install, or you can try out work in progress by looking on source repositories (like github.com). As theme support develops, we expect to have access to a wide set of themes too.

Creating and installing extensions is simple. All extensions live in vendor/extensions/ in your project directory. There’s a generator for creating a new extension, and a mechanism for installing existing extensions from the internet, or you can copy extensions manually.

A very good way to learn about these ideas is to experiment with real code! The Spree Demo is a good place to start (wait for updates)

2 What is an extension?

Each extension is like a mini Rails project, with similar directory structure, which allows you to add new files OR to override existing files. You can add or override pretty much anything.

In fact, part of Spree is itself implemented with extensions (which we call core extensions): this is functionality that we’ve been able to abstract out of the ‘kernel’, such as the payment gateway support.

2.1 Extension load order

Extensions are loaded in a particular order. This is essential so that overrides work in a predictable way. The default order is to load the :localization and :theme_default core extensions first, then all other extensions (core and local) in alphabetical order.

You can modify this order by adding a file config/preinitializer.rb containing something like the following. The :all symbol represents everything else not otherwise loaded, taken in alphabetical order from the core extensions and the extensions in your vendor/extensions directory. The other symbols are just the names of the relevant extension directories. So this example loads the default theme first, the site extension last, and loads extension ‘foo’ before the remainder. (This pattern is useful if ‘foo’ sets up something that a later extension requires.)

SPREE_EXTENSIONS_LOAD_ORDER = [:localization, :theme_default, :foo, :all, :site]

2.2 Hooks and overrides

It’s important to understand the key principles of these ideas in order to appreciate how extensions work together and what they might contain. Further detail is given in the extensions guide.

When Spree needs a file, e.g. app/views/products/show.html (which controls presentation of a single product’s information), it looks for the file in the Spree core directory then in the extension directories in the declared load order. It uses the file which is latest in the load order. So, if there was a version of this file in the :site extension, it would mask or take priority over any other versions. (Note: some kinds of file don’t override like this - we’ll explain the special cases in the extension guide) In this way, later-loaded extensions can take control of most aspects of an application, and make unlimited changes to the default functionality or appearance of results.

However, replacing whole files just to change a small number of lines is not good practice. It can lead to a maintenance nightmare, for one thing. We also find that developers often want to make just minor changes or additions to certain components of Spree, e.g. small tweaks to how a product is described. To support this, Spree has a ‘hook’ system. This allows developers to implement their changes just by supplying small blocks of code. When a hook is rendered in a view, the attached code blocks are run (in order of declaration) and the results inserted into the result. This removes the need to duplicate whole files.

2.3 Core extensions

You will sometimes see mention of core extensions: these are extensions that core Spree uses to add certain functionality which can be separated from the ‘inner core’ and is not necessary in all applications. (See: the extension mechanism is powerful enough for this too.) The current extensions shipped with Spree are:

  • :theme_default - the standard “look and feel” of Spree
  • :localization - provides the ‘language bar’ and ability to change the language for messages
  • :calculators - various functionality for computing charges, coupons, etc
  • :overview_dashboard - various information about recent orders and activity for the admin pages
  • :api - allows secure remote access to some of the site’s information
  • :payment_gateway - supports processing of payments through a number of gateways

The core extensions are included in the Spree gem. They are organized as extensions for those developers wishing to use the Spree source code as a starting point and remove functionality that is not needed. We have a long term goal of making Spree more modular in a way that similar is similar to how Rails does things now with ActiveRecord, etc.)

2.4 A ‘theme’ extension

The default content and appearance of Spree pages is entirely provided by a core extension called :theme_default. This contains the views with related helpers, css/less, images, and javascript. We’ve pulled this code into an extension to make it clear which files control the presentation, and to encourage development of alternative themes.

Developers can modify the appearance by overriding the theme files in their own extensions. If extensive modifications are required, we suggest that the view files and related components should be packaged into a stand-alone theme extension. We hope that developers will, in time, make their themes available (for free or otherwise), as is done with other tools.

2.5 The ‘site’ extension

Each Spree project is created with a ‘site’ extension. This extension is often used to pull together elements of the other extensions and provide highly site-specific functionality - mail settings and site ‘brand’ elements are good examples. To promote reuse and simplify maintenance, we suggest that you keep the site extensions simple, e.g. doing most of the customization in your own theme extension.

Technically, there is nothing special about the site extension. It is just a naming convention. Every Spree application will probably need one so we have provided a convenient default. You can rename this extension to whatever you wish or even remove it - just remember to adjust the load order if needed.

See the Spree Demo for a good example of using several extensions and integrating them in the site extension.

3 Installing an Existing Extension

Installing extensions is simple enough since they are typically stored in GitHub. The command is similar to that for installing a Rails plugin:

script/extension install git://github.com/developer/whatever.git

You need to replace developer and whatever with the actual extension author and name respectively.

The Spree Extension Registry contains several useful extensions. You can also search github- - notice the convention that extensions names follow the pattern ‘spree-FOO’.

You can also copy the source code for an extension directly into place (inside vendor/extensions). Symbolic links can also be used if you want to share a single code instance: this can be useful for testing an extension over several sites.