Every repository with this icon (
Every repository with this icon (
Extension Tutorial
Don’t let the length of this very detailed tutorial discourage you. The later part of the tutorial is dedicated to showing the specific code for our example extension. It goes way beyond the knowledge necessary to create a simple extension.
Generating an Extension
We’re assuming you have already created a new Spree application and run the migrations. If you haven’t done so, create a simple application now.
spree tutorial cd tutorial rake db:bootstrap
Let’s start by building a simple extension. Let’s pretend we want the ability to mark certain products as part of a promotion. We’d like to add an admin interface for marking certain items as being part of the promotion. We’d also like to highlight these products in our store view. This is a great example of how an extension can be used to build on the solid Spree foundation. We’ll be adding our own custom models, views and controllers through the new extension.
Spree comes with generators for creating your new extensions. To create a new extension use
$ script/generate extension <extension_name>
So in the case of our Promotion Manager extension we type
$ script/generate extension PromotionManagerThis command generates the following output
create vendor/extensions/promotion_manager/app/controllers
create vendor/extensions/promotion_manager/app/helpers
create vendor/extensions/promotion_manager/app/models
create vendor/extensions/promotion_manager/app/views
create vendor/extensions/promotion_manager/db/migrate
create vendor/extensions/promotion_manager/lib/tasks
create vendor/extensions/promotion_manager/README
create vendor/extensions/promotion_manager/promotion_manager_extension.rb
create vendor/extensions/promotion_manager/lib/tasks/promotion_manager_extension_tasks.rake
create vendor/extensions/promotion_manager/spec/controllers
create vendor/extensions/promotion_manager/spec/models
create vendor/extensions/promotion_manager/spec/views
create vendor/extensions/promotion_manager/spec/helpers
create vendor/extensions/promotion_manager/Rakefile
create vendor/extensions/promotion_manager/spec/spec_helper.rb
create vendor/extensions/promotion_manager/spec/spec.opts
This creates the promotion manager extension in vendor/extensions. The extension is entirely self contained in that directory. Once we’re done, we can reuse this extension in other Spree applications by simply copying the promotion_manager folder and dropping it into another application’s vendor/extensions directory.
TODO: Add some information about the promotion_extension.rb file and activation once activation feature is in place
Generating a Model
Extensions allow you to add your own models to the application. You can also use extensions to mixin additional functionality into the existing models. For our promotion extension let’s create a brand new promotion model. Note that the field names are optional but if we provide them they will be automatically added to the migration.
$ script/generate extension_model PromotionManager promotion name:string start:date stop:dateThis command generates the following output
exists app/models/ exists spec/models/ create app/models/promotion.rb create spec/models/promotion_spec.rb create vendor/extensions/promotion_manager/db/migrate create db/migrate/001_create_promotions.rb
Reviewing the output of this command you can see that the generator produced the following:
- Model file
- RSpec test
- Database migration
Let’s examine the contents of the migration in our favorite text editor.
class CreatePromotions < ActiveRecord::Migration
def self.up
create_table :promotions do |t|
t.string :name
t.date :start
t.date :stop
t.timestamps
end
end
def self.down
drop_table :promotions
end
end
So we now can see that Spree extensions support their own migrations and the extension_model generator will even help us to create them. Let’s run the migrations. Just run the regular migration task to migrate your project and all of your extensions. This is made possible by the UTC based migration system introduced by Rails 2.1
rake db:migrate
This should output
== 1 CreatePromotions: migrating ============================================== -- create_table(:promotions) -> 0.0025s == 1 CreatePromotions: migrated (0.0030s) =====================================
Of course you can have more then one model in your extension. If we had additional models their migrations would have been run at this point as well. If you decide to make your extension available to the public (via Github or some other mechanism) you can write additional migrations to support new features for your extension in subsequent versions using the command:
script/generate extension_migration <extension_name> <migration_name>
As we mentioned, the previous command migrates all of your extensions. If you want to migrate your extension to a specific version use something like this:
rake spree:extensions:promotion_manager:migrate VERSION=0
Note the use of the familiar VERSION environment variable to specify the version. The version number corresponds to the number of your extension’s migration (file) and not the number in the schema info table. You can also use RAILS_ENV to specify an environment other then the default (which is development.)
Generating a Controller
Now that we have the model setup for our extension we can move onto the controller. To generate a controller use the following command:
script/generate extension_controller PromotionManager admin/promotions
This should generate the following output:
create app/controllers/admin
create app/helpers/admin
create app/views/admin/promotions
create spec/controllers/admin
create spec/helpers/admin
create spec/views/admin/promotions
create spec/controllers/admin/promotions_controller_spec.rb
create spec/helpers/admin/promotions_helper_spec.rb
create app/controllers/admin/promotions_controller.rb
create app/helpers/admin/promotions_helper.rb
Reviewing this output we see that it generated
- Admin::PromotionsController
- Helper for the controller
- RSpec test for the controller
- Folder for the promotion controller views
Routing
Spree extensions have the ability to configure their own routes. Let’s open vendor/extensions/promotion_manager/config/routes.rb and take a closer look.
# Put your extension routes here. # map.namespace :admin do |admin| # admin.resources :whatever # end
Notice how the generator created a placeholder for you to add your routes. Go ahead an uncomment that block of code and change :whatever to :promotions.
map.namespace :admin do |admin|
admin.resources :promotions
end
Activation
TODO: Activation Tabs (once they are supported!)
At this point, you have enough information to get up and running with your own extensions. We haven’t done anything useful with the extension yet but you get the idea of how to create one and the ways in which you can provide your own functionality in a familiar way. The rest of this tutorial will provide some more specifics on how we can make the extension do something useful.
Adding Views into Views
Adding Fields to the User Form
So, as you may have already noticed, you can easily add/overwrite functions in existing models and controllers using an extension. See here for another example of this. I was writing a plugin so you could have your users subscribe to your mailing lists, and I realized, I need a way to add fields to the user form. For example, a check box field that said “Yes, notify me when you release new products”. So, I needed a way to add these fields, but I didn’t want to have to overwrite the whole users form view (which you can do simply by simply overriding it, but I didn’t want to).
Instead, I contributed to spree, and now you can do something like this:
UsersController.class_eval do
before_filter :add_mailing_list_fields
def add_mailing_list_fields
@extension_partials << 'mailing_lists'
end
end
The mailing list partial which is in extension/app/views/users/_mailing_lists.html.erb contains the extra fields I want rendered on the user form.
Adding Admin Configuration Links
If you add a new admin controller, you can link to it from the configuration page by doing this:
Admin::ConfigurationsController.class_eval do
before_filter :add_mailing_list_links, :only => :index
def add_mailing_list_links
@extension_links << {:link => admin_mailing_lists_path, :link_text => t('Mailing Lists'), :description => "Add Mailing Lists your users can opt-in to."}
end
end
Add REST Actions to the Controller
Let’s add some REST actions to the controller. If you are not already aware of the excellent resource_controller plugin, then you should take a look at it when you get a chance. It will save you a massive amount of tedious typing. For now though, we’ll create our REST actions the old fashioned way.
Go ahead and add the REST methods to the controller.
class Admin::PromotionsController < Admin::BaseController
# GET /promotions
# GET /promotions.xml
def index
@promotions = Promotion.find(:all)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @promotions }
end
end
# GET /promotions/1
# GET /promotions/1.xml
def show
@promotion = Promotion.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => @promotion }
end
end
# GET /promotions/new
# GET /promotions/new.xml
def new
@promotion = Promotion.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @promotion }
end
end
# GET /promotions/1/edit
def edit
@promotion = Promotion.find(params[:id])
end
# POST /promotions
# POST /promotions.xml
def create
@promotion = Promotion.new(params[:promotion])
respond_to do |format|
if @promotion.save
flash[:notice] = 'Promotion was successfully created.'
format.html { redirect_to collection_url }
format.xml { render :xml => @promotion, :status => :created, :location => @promotion }
else
format.html { render :action => "new" }
format.xml { render :xml => @promotion.errors, :status => :unprocessable_entity }
end
end
end
# PUT /promotions/1
# PUT /promotions/1.xml
def update
@promotion = Promotion.find(params[:id])
respond_to do |format|
if @promotion.update_attributes(params[:promotion])
flash[:notice] = 'Promotion was successfully updated.'
format.html { redirect_to collection_url }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @promotion.errors, :status => :unprocessable_entity }
end
end
end
# DELETE /promotions/1
# DELETE /promotions/1.xml
def destroy
@promotion = Promotion.find(params[:id])
@promotion.destroy
respond_to do |format|
format.html { redirect_to(promotions_url) }
format.xml { head :ok }
end
end
end
Add access to Spree Base helpers
To gain access to standard spree helper methods in your new controller, add helper 'spree/base' to your controller.
Extension and Repository Naming Conventions
You create a new extension this way:
script/generate extension YourOwnExtension
This is supposed to be stored in a repository called spree-your-own-extension.
And install this way:
script/extension install git://github.com/developer/spree-your-own-extension.git
Using this convention the extension will be placed inside vendor/extensions/your_own_extension, the spree- part will be erased and traces will be substituted by underlines.
[TODO: Finish up tutorial]






