Some of the information here has been made redundant by later changes.

Improved Layout Customization

Work has been done to reduce the likelihood of new projects needing to override the default Spree layout template application.html.erb. The title, stylesheets, and logo now can all be customized without creating your own copy of the layout.

New title methods

There are some new methods for manipulating the page title: the title and set_title helper methods in Spree::BaseController.

Use set_title to set a page title either from a controller method, or a view template. You can also override the default_title and title methods in Spree::BaseController for further control.

The title method is used in application.html.erb of the new release, however if you are upgrading and want to take advantage, use this in between your <title> tags of your layout template

And to set the title in a view template:

Customize default stylesheets

Spree::Config+ is a new config option for customizing the stylesheets used by the default application layout. The value ofSpree::Config[:stylesheets]+ is a comma-separated string of stylesheet names without the file extensions. See the customization guide for more information.

If you are upgrading, to take advantage of this use the new stylesheet_tags helper method.

*Spree::Config+ is a new config option for customizing the logo image path.

If you are upgrading, take advantage of this by using the new*logo+ helper method.

Polymorphic Calculators

There has been significant refactoring to the implementation of calculators. Calculators are now polymorphic and belong to calculable. This will have a non trivial impact on your existing store configuration. After upgrading to Spree 0.9.0 you are likely going to have to make several manual adjustments to the existing tax and shipping configurations. Ultimately we feel this is outweighed by the superior design of the new calculator system which will allow for a more modular design.

Many of the existing calculator extensions are not yet updated to support Spree 0.9.0. Please check the extension registry to see which versions are supported. Our goal is to back port most of the useful calculators out there shortly after the release.

All calculators need to implement the following method

 def compute(something=nil)
 …
 end

The calculator is passed an optional “target” on which to base their calculation. This method is expected to return a single numeric value when the calculation is complete. A value of nil should be returned in the event that a charge is not applicable.

Since calculators are now instances of ActiveRecord::Base they can be configured with preferences. Each instance of ShippingMethod is now stored in the database along with the configured values for its preferences. This allows the same calculator (ex. Calculator::FlatRate) to be used with multiple ShippingMethods, and yet each can be configured with different values (ex. different amounts per calculator.)

Calculators are configured using Spree’s flexible preference system. Default values for the preferences are configured through the class definition. For example, the flat rate calculator class definition specifies an amount with a default value of 0.

 class Calculator::FlatRate < Calculator
    preference :amount, :decimal, :default =\> 0
    …
 end

Spree now contains a standard mechanism by which calculator preferences can be edited. The screenshot below shows how the amounts for the flat rate calculator are now editable directly in the admin interface.

Calculators are now stored in a special calculator directory located within app/models. There are several calculators included that meet many of the standard store owner needs. Developers are encouraged to write their own extensions to supply additional functionality or to consider using a third party extension written by members of the Spree community.

Calculators need to be “registered” with Spree in order to be made available in the admin interface for various configuration options. The recommended approach for doing this is via an extension. Custom calculators will typically be written as extensions so you need to add some registration logic to the extension containing the calculator. This will allow the calculator to do a one time registration during the standard extension activation process.

The CalculatorExtenion that is included in the Spree core is a good example of how you can achieve this in your own custom extensions.

 def activate
 [
   Calculator::FlatPercent,
   Calculator::FlatRate,
   Calculator::FlexiRate,
   Calculator::PerItem,
   Calculator::SalesTax,
   Calculator::Vat,
   ].each(&:register)
 end

This calls the register method on the calculators that we intend to register. Spree provides a mechanism for extension authors to specify the operations for which the calculator is intended. For example, a flat rate calculator might be useful for all operations but another calculator may be appropriate only for coupons and not shipping or taxes.

Models that are declared with has_calculator maintains their own set of registered calculators. Currently this includes Coupons, ShippingMethods, ShippingRates and TaxRates. The following example shows how to configure a calculator to make it available for use with Coupons.

  def self.register
     super
     Coupon.register_calculator(self)
  end

Spree automatically configures your calculators for you when using the basic install and/or third party extensions. This discussion is intended to help developers and others interested in understanding the design behind calculators.

Once your calculators have been registered correctly by your extensions, then they will become available as options in the appropriate admin screens.

Simplified Tax Configuration

There are also minor changes to how taxes are configured. You no longer need to specify sales tax or VAT but you do need to choose a calculator type. Tax rates are configured as preferences for the calculator itself.

Your tax rates will be lost when you run the migrations. You will have to recreate them manually in the admin interface.

Unified Adjustment Model

Spree 0.9.0 provides a new flexible system of adjustments associated with orders. The orders table no longer has separate columns for tax_total, ship_total, etc. This information is now captured more generically as an Adjustment. This allows a Spree application to add more then one tax or shipping charge per order as well as to support new types of charges that might be required. For instance, some products for sale (like cell phones) require a separate activation fee.

Adjustments come in two basic flavors: Charges and Credits. From an implementation perspective, they are both modeled in a single database table called adjustments and use the single table inheritance mechanism of Rails. Charges add to the order total, and credits work in the opposite direction.

Orders have one or more adjustments associated with them and each adjustment also belongs to an adjustment source. This allows charges and credits to recalculate themselves when requested. Adjustments are always recalculated before they are saved which includes every time and order is updated. This provides a very flexible system by which an adjustment can determine that it is no longer relevant based on changes in the order.

Consider a coupon that takes $5 off all orders over $20. If the order exceeds the required amount during checkout the coupon will create the proper adjustment. If the customer then decides to edit their cart (before completing checkout) then you will want to make sure that the coupon still qualifies. If the order total drops below the required amount the source of the adjustment (in this case the coupon) will have the ability to remove the adjustment based on its own internal logic.

There are significant changes to the database to support the new adjustment system. The migrations should update your legacy data and create the necessary tax and shipping adjustments for existing orders but you should backup your database before running.

Coupons and Discounts

Spree now supports a flexible coupon system. Coupons in an online store are virtual and can be thought of as “codes” that must be entered during the checkout process. Coupons serve two important functions. First, they determine whether or not they are eligible to be used for the offer in question. Second, they calculate the actual credit/discount that should be applied to the specific order (assuming that the eligibility requirement is satisfied.)

Eligibility

Coupon eligibility is completely customizable on a per coupon basis. Eligibility is determined by the following factors.

  • Start Date - coupons can be configured to be invalid before a specific date
  • Expiration Date - coupons can be configured so that they are not usable passed a certain date
  • Expiration Date - coupons can be configured so that they are not usable passed a certain date
  • Number of Uses - coupons can be restricted to an arbitrary number of uses (typically a single use if there’s a limit at all)
  • Combination - there is an option to restrict specific coupons so that they cannot be combined with other coupons in the same order.

Any other restriction on eligibility is intended to be provided by custom calculators. The compute method has access to the complete order (including shipping and other related information) and can simply return nil if the coupon is not to be applied in a specific situation

The next version of Spree will also provide built in filtering for coupons based on product properties and taxon information. This will provide a standard way to restrict coupons to certain types of products. As a workaround, you can accomplish this by hard coding restrictions in your calculator.

Discount Calculation

The create_discount method in Coupon is responsible for the actual calculation of the credit to be applied for the cooupon. By default, Spree will not allow the credit amount to exceed the item total. The credit adjustment associated with a coupon is subject to recalculation based on changes in the order. This is no different then any other adjustment to the order total (such as shipping or tax charges.)

RESTful Checkout

There have been several minor but crucial changes to the checkout process. The primary motivation for these changes was to improve maintenance of the checkout process and to simplify checkout customization.

Checkout Module has been Replaced by Controller

Prior to the refactoring, much of the checkout logic was contained in lib/checkout.rb. The idea was to isolate this logic from the OrdersController and to make it easier to extend. In this release we have just taken this another step further and made the checkout its own resource.

Changes to the Checkout Partials

The views have been shuffled around a bit due to this refactoring. This shouldn’t affect you too much unless you have an existing Spree application in which you customized some of the checkout partials. For instance, app/views/orders/_billing.html.erb has been moved to app/views/checkouts/_billing.html.erb. So you may need to rename your custom partials if you have any.

Additional Details

For more detailed information on the nature of these changes, please see the relevant commit in Github.

Variant Images

Some changes have been made to allow you to attach images to both the Product model and each individual variant for a product. The Images administration has been relocated from the main product details form to it’s own tab accessible via the right hand side bar on the product details screen.

This new admin interface enables you to select from a drop-down list which object (product or variant) the image represents. Note if a product does not contain any variants then the drop-down is not displayed to ensure that basic implementations are not cluttered with unnecessary administration options.

The front-end product details interface has also been updated to filter the displayed images depending on which variant is selected, and the cart view now displays the image of the selected variant.

Improvements to image handling

We’ve upgraded the paperclip gem to take advantage of recent changes. Paperclip is the library which handles creation of and access to the various formats of image. On top of this, we’re explicitly catching errors in the image creation stage and returning these via the validation mechanism - also adding a more meaningful message in the errors list. This will avoid the silent failures that some people have experienced when they don’t have image magick installed correctly.

Another change is to store the original image’s width and height: this info is sometimes useful when working with a set of images with different ‘shapes’, e.g. where your images might all have a width of 240 but (minor) variation on height. Knowing the height of the original allows you to calculate the max height of your images and thus to create a suitable bounding box.

Finally, note that the processing tools behind paperclip can do many transformations on the images, such as cropping, color adjustment, … - and these can be requested by passing the options to paperclip, or you can run the conversions on a batch of images in advance of loading into Spree. Automatic cropping is particularly useful to make best use of screen area.

Update to SearchLogic

Spree now runs with version 2.1.13 of SearchLogic. It has meant some minor recoding of how searches are set up and paginated, and allowed some of the existing forms to be simplified (by taking advantage of new functionality) and opened the door to more sophistication in selecting products, e.g. for handling faceted search or product groups.

There’s an overview of what the new SearchLogic offers on the Spree blog, and full documentation is at rdoc.info.

Some new named scopes for products

To make it easier to construct sets of products for various uses, we’ve added some more named scopes whichhelp with taxon, property, and availability of option values. The first kind (taxons_id_in_tree and taxons_id_in_tree_any) allows restriction to a set of taxons and their combined descendents. The property scope with_property takes a property object (or its id - the definition uses Rails’ automatic conversion) and an optional argument for uniquifying the table names in complex queries, eg where you are filtering by two distinct properties. This scope does not take a property value: the design is that you add further condition(s) on the value in a subsequent scope. It will handle cases where the property is absent or null for a product. There is a simpler scope with_property_value for simpler cases. The option type scope (with_option, with its prerequisite with_variant_options) follows the pattern of option type object or id, and an optional table name, and is intended as a basis for further conditions on the value of that option type. See lib/product_scopes.rb for the definitions, and see lib/product_filters.rb for examples of their use.

Basic support for filtering of results

It is often useful to cut down the results in a taxon via certain criteria, such as products in a price range or with certain properties, and sometimes you want a set of restrictions selectable via checkboxes etc. Using ideas from SearchLogic version 2, Spree now contains a basic framework for this kind of filtering. You can some basic filtering by visiting /products?taxon=1000 (unless you have overridden the products controller), where it allows you to select zero or more of a taxon’s children and to select some price ranges and product brands.

File lib/product_filters.rb explains the mechanism in detail, with several concrete examples. Basically, a filter definition associates a named scope with a mapping of human readable labels to internal labels. The named scope should be defined to test the relevant product attribute(s), and to convert a set of these internal labels into tests on the attributes. For example, you may want to filter by price range, so should set up labels for price ranges like 0-20, 20-50, 50-100, 100 or more; then define a named scope which maps these into a combined test on the (master) price attribute of products.

The partial app/views/shared/_filters.html.erb displays a checkbox interface for the filters returned by the method applicable_filters for the selected taxon. This method allows you to control which filters are used for some taxon, eg a filter on fabric type may be required for clothing taxons, but not suitable for mugs etc.

To use this framework, you should override and extend lib/product_filters.rb and define a suitable applicable_filters method for taxons. The new named scopes (above) are useful building blocks for adding application-specific filters.

Miscellaneous improvements

Default ship and bill addresses

Spree now saves the last used bill and ship addresses for logged in users and uses these as the defaults in their next checkout. If the ship or bill addresses are edited in checkout, then the old addresses are left unchanged and new addresses saved as the defaults. This is a very simple form of address book.

Extension initializers

It is now possible to include initializers in your extensions. This makes it a lot easier to configure extensions and to make site-specific customizations, and to keep them with the relevant extension code.

Improved handling of requests for invalid objects

If a method object_missing for a controller, Spree will pass all requests for invalid objects to this method. This provides an easy way for applications to add specific handlers for invalid requests. For example, you may wish to direct customers back to the front page. If no method has been defined, Spree will use its default 404 response.

Reduced silent failures in checkout

The checkout code is now more careful about returning and checking results from key operations, and a few more handlers for exceptions and invalid responses have been added. In normal use these should not occur, but they may sometimes occur if you have an error in your database or configuration.

Improvements to Upgrade Process

The rake spree:upgrade task has been eliminated. It turns out there were some crucial flaws that caused issues when the older version of Spree used a different version of Rails or a different version of upgrade.task than the newer version of Spree. The rake task has been replaced by a new gem command:

 spree —update

You can also use the abbreviated form:

 spree —u

After installing a new version of the Spree gem, simply run either one of these commands from inside RAILS_ROOT (your application directory) and your application will be upgraded.

The update process is also now less “destructive” than in previous versions of Spree. Instead of silently replacing crucial files in your application, Spree now checks the content of files it needs to replace, and if the old version differs, it will be saved with a \~ suffix.

This makes it easier to see when and how some file has changed - which is often useful if you need to update a customized version. The update command will also no longer copy the routes.rb file - the original version just loads the core Spree routes file, so has no need to change. (Recall that you can define new routes in your extensions.)