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

Extensions

This guide covers the use of extensions and their role in customizing and enhancing the standard Spree installation. After reading this guide you should learn:

1 Overview

Spree supports 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 missing functionality in their application. 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.

2 Overriding the Spree Defaults

Custom extensions should be placed in the vendor/extensions directory of your project. Extensions placed in this directory will be automatically available to your application. What does an extension allow you to customize? The short answer is pretty much everything.

  • Models
  • Views
  • Controllers
  • Routes
  • Migrations
  • Rake tasks

Some of the more obvious possibilities would be to override the default view or controller used by Spree. Most stores will need to customize the UI and you can use extensions to do this. You can also use an extension to mixin functionality into an existing controller or view. Since extensions have their own migrations you can modify existing Spree tables or create new ones in your database. The possibilities are almost endless.

3 Directory Structure

Extensions have a directory structure that should look very familiar to you. They follow essentially the same layout as a standard Rails app.

app controllers helpers models views config db migrate seeds.rb lib spec controllers helpers models spec.opts spec_helper views

So even though your Spree application will not have an app directory, your extensions will. If your primary goal is to simply customize Spree for a specific business then you could just create a single extension and put all of your custom stuff in there. If you would like to reuse stuff between your own projects, or even share them with the Spree community, then you will want to organize your work logically into multiple self-contained extensions.

4 Site Extension

When you use the spree command to create a new application (a gem installation), it will automatically create a site extension for you. This is where you should put all of the code to customize your site views, etc. This is essentially what you would normally put into the app directory of a standard Rails application. It may seem a little bit strange to put this type of content into an extension (as opposed to a top level directory) but this process makes upgrading to newer versions of Spree considerably easier.

Technically there is nothing special about the site extension. It is just a naming convention. Every Spree application will likely need one so we have come up with a standard name for this concept. You could call this extension whatever you wish. You could also break the contents of the site extension into as many extensions as you wish (although there aren’t many usecases for this.)

5 I18n

Extensions can be used to provide custom locales for your extension. For instance, if you wanted to use the en-GB locale but there were a few translations that you disagreed with and wanted to change. You could createa a new locale called en-GB-custom and start by copying over the existing locale files.

app config locales en-GB-custom.yml en-GB-custom_rails.yml db lib spec

Suppose you wanted to change the appearance of the term “City / Town” to “Hamlet.” Simply edit your custom locale file in your extension as follows

... city: Hamlet ...

Typically you would save your custom locales in the site extension but you might also want to create it as its own extension if you were planning on building more then one Spree store and you wished to reuse your custom locale.

If there is a translation error or missing translation information in one of the existing Spree locales, you should fork the Spree project in GitHub and provide a patch. See contributing to spree for more details.

6 Tutorial

There is a very detailed extensions tutorial that will go through a detailed example of how to create your own extension.

7 Extensions Directory

There are numerous extensions that have been written for Spree. We’ve attempted to consolidate the list of known extensions in the extensions directory for your convenience. Extensions are listed by author, tag as well as supported Spree versions.

One thing to keep in mind is that extensions are often the result of a generous donation from one or more of our users. A typical extension reflects the code needed to make the extension work at a particular moment (and for a particular version of Spree.) Consequently, not all extensions will work with all versions of Spree. These extensions are still often very useful starting points for addressing your own specific needs. Many times the extension requires only a minor tweak to make it working with the latest version of Spree. Other times, the extension is not listed as being compatible with a particular version of Spree only because it has yet to be tested. There may not even be any issues at all.

8 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.

9 Extension Migrations

Extensions have a special migration script that allows you to add migrations with the appropriate timestamp to your extension.

script/generate extension_migration <extension_name> <migration_name>

The standard rake command for running Rails migrations will run all of the Spree migrations as well your extension migrations.

rake db:migrate

There are currently some known issues with migrating DOWN using extension migrations. Since down migrations are rarely useful in the real world, this problem is likely to remain unresolved for a while.

Extensions can also populate seed data in a way similar to how a standard rails app. Seed data is data that is needed by the application in order for it to work properly. This is a fairly specialized case and it is not the same as sample data (which is optional.)

rake db:seed

For more information on Rails migrations in general, please see the excellent rails guide on this subject.

10 Extension Load Order and Initializers

Put something like the following inside the initializer block in the file config/environment.rb:

config.extensions = [:all, :site]

Where :all means all extensions available and not specified in that array. Those “all” extensions will follow a predefined order then, first Spree’s internal extensions will be loaded in alphabetical order then external extensions will be loaded in alphabetical order.

You can even make something be loaded first too:

config.extensions = [:first_extension, :all, :site]

10.1 Extension Initializers

You can put initializers in extensions, and these are the recommended way to configure certain settings.

Initializers go in the config/initializers directory in an extension, and should be .rb files. Such extensions are loaded in extension order (as above) then by alphabetical order within an extension – and they will all be loaded after the main project initialization is done.

So, if the site extension is loaded last, then +site/config/initializers/beta.rb" is run before site/config/initializers/gamma.rb, and other_ext/config/initializers/alpha.rb will be loaded before both of these (because it is from an earier extension).

11 Creating Your Own Extension

11.1 Generate the Extension

Spree comes with a handy generator so you can easily create your own custom extension. Lets suppose we want to create a “Foo” extension.

script/generate extension Foo

This will create a series of folders and files, including test_extension.rb, which will define a FooExtension class.

Do not use spaces or dashes when creating a new extension. If there is more then one word to the extension (such as “Foo Bar”) use script/generate extension FooBar instead.

11.2 Making the Extension Available on GitHub

Once you are satisfied that your extension is running properly, feel free to share it with others through GitHub. There is just a simple naming convention that you should follow in order to make things easier for everyone to use it.

If your extension class is named FooBar and it is defined in foo_bar_extension.rb, you should name your GitHub project spree-foo-bar. Notice how there are no spaces or underscores in the GitHub project name and that it is prefixed with spree. The underscores in the filename are replaced with ‘-’ and the word ‘extension’ does not appear anywhere in the GitHub project name.

It is important that you observe the suggested naming conventions when adding your extension to GitHub, otherwise, people will not be able to automatically install it with the special script/extension install command.

11.3 Publishing Your Extension in the Spree Extension Registry

Once your extension is complete and you have published it to GitHub, its time to let everyone else know about it. The best way to do this is to add your extension to the official extension registry.

12 Tips and Tricks

12.1 Adding Fields to Product or Variants

You may be interested in adding one or more fields to variant and / or product models using an extension. The Variant model has a class method called additional_fields which exposes an array of hash objects, which you can add to from your extension to provide any extra fields that you might need. It’s important to note that you should always append or add to this array as you might overwrite fields added from another extension.

Each field added to this array automatically gets presented on the relevant add / edit admin interfaces, but you still need to create a migration to added these fields to the relevant tables. This is a better approach to simply overriding the admin views in your extension, because it enables multiple extensions to add columns to the product/variant model without having the clash of views.

12.2 Variant#additional_fields

From your extension class you can add to the variant and / or product models by adding a hash for each value, like:

Variant.additional_fields += [ {:name => 'Foo', :only => [:variant], :format => "%.2f"}, {:name => 'Bar', :only => [:product], :format => "%.2f"}, {:name => 'Buzz', :populate => [:line_items]} ]

In this example the extension would be adding the following fields:

  • Foo – will be added to the variant model (and formatted to two decimal points)
  • Bar – will be added to the product model (and formatted to two decimal points)
  • Buzz – will be added to both the variant and product models. It will also populate the buzz attribute on the _line_item_ model when the product/variant is added to an order(cart).

The supported parameters for the config hash are:

  • :name – The name of the field to be added, this should match the column added to table (column name will be lowercase)
  • :only – Defines which models the field has been added to. If no :only key is present it’s presumed to be added to both Variant and Product
  • :format – An sprintf compatible string to be used to format the value.
  • :populate – If [:line_items] is passed to the :populate key, the value will be updated on the line_item when the product / variant is added to an order (i.e. cart). This can be helpful if your field changes frequently, but you’d like to record the value at the time of purchase (like cost price for example).

12.3 Modifying the Checkout Workflow

You may wish to change the checkout workflow for your specific application. Extensions are a logical place to do this. Typically you would override the checkout in your site extension. Please see the guide on the checkout process for more detailed information.

12.4 Using Migrations for Custom Configurations

Spree comes with all sorts of default configurations that you will likely want to customize. Many of these values can be changed now through admin screens (those that can’t be changed will be able to shortly once those screens are written.) Once a preference is changed from its default, the value is stored in the database.

It is possible to use a migration to setup these preferences so that you can set the preferences of your site automatically. This is handy when migrating a new series of configurations from a tested development environment to production. You no longer have to remember to manually change these settings, just run the migrations like normal.

For example, the default country in Spree is United States. If you wanted the default country to be Ireland you could add the following to your site extension:

class AddCustomConfigurations < ActiveRecord::Migration def self.up Spree::Config.set(:default_country_id => 96) end def self.down end end

12.5 Adding a tab to the Admin screens

If your extension needs to have a section in the admin screens, you can add a tab to the admin panel from the activate method in your extension file.

Generate an extension admin controller:

script/generate extension_controller foo admin/foos

Add a route to your config/routes.rb file:

map.namespace :admin do |admin| admin.resources :foos end

Add your localization file

You’ll need to create at the least, a localization file for en-US that contains your extension name. A minimal locale file would contain the string the tab helper will use to render the tab label. You may want to include other translations for your particular admin needs.

config/locales/en-US.yml

--- en-US: foos: "Foos" delete_foo: "Delete Foo" edit_foo: "Edit Foo" transmorgify_foo: "Turn Foo into a T-Rex"

Once you’ve got the parts you need set up, you can tie your admin screen into the main panel by editing foo_extension.rb and adding the following to the activate method:

foo_extension.rb

def activate Admin::BaseController.class_eval do before_filter :add_foos_tab def add_foos_tab @extension_tabs << [ :foos ] end end end

The array that is appended to the extension_tabs attribute is passed in the call to the tab helper. You can pass in options to the tab helper like:

... @extension_tabs << [ :foos, { :label => "Some other tab label", :route => :mapped_path_name } ] ...

Any option supported by the tabs helper can be passed in the hash argument.

By default, the route is created by concatenating “admin_” with the first element of the array. However, if you override the route (as above) and are making use of the ‘admin’ namespace, you will need to include “admin_” as a part of your route argument.

12.6 Make your extension helper avaliable to all views (helpful if you’re using it in a layout override)

foo_extension.rb

def activate Spree::BaseController.class_eval do helper FooHelper end end