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

Preferences

This guide covers how to manage and configure preferences within Spree. After reading it, you should be familiar with:

1 Overview

The Spree preference system adds support for easily creating custom preferences for models. It’s simply an adoption of the preferences plugin, and much of the following material is derived from that plugin’s documentation.

This original preferences plugin required another plugin (plugins_plus), which was something like the Spree extensions system. So we just added the preferences functionality to the Spree core, and it can now be used by both the Spree core as well as extensions to store preferences.

The first several sections of this guide describe preferences in a very general way. If you’re just interested in making modifications to the existing preferences, you can skip ahead to the Configuring Spree Preferences section. If you would like a more in-depth understanding of the underlying concepts used by the preference system, please read on.

2 Motivation

Preferences for models within an application, such as for users, represent a common use case. Although the rule of thumb is to keep the number of preferences available to a minimum, sometimes it’s necessary if you want users to be able to disable things like e-mail notifications.

Generally, basic preferences can be accomplished through simple designs, such as additional columns or a bit vector such as that described and implemented by preference_fu.

However, as you find the need for non-binary preferences and the number of preferences becomes unmanageable as individual columns in the database, the next step is often to create a separate preferences table. This is where a more sophisticated preferences system comes in. Spree encapsulates this design by hiding the fact that preferences are stored in a separate table and making it dead-simple to define and manage preferences.

3 Persisting Modifications to Preferences

Although you can change settings from script/console or from the admin interface, you should realize that these settings will be lost should you decide to rebuild your database. You might also be missing these settings in your production server once you promote your code.

You should consider using seed data or initializers to configure your application and to enforce important settings. See the Extensions guide for more details.

4 Usage

4.1 Defining Preferences

You can define preferences for a model within the model itself:

class User < ActiveRecord::Base preference :hot_salsa preference :dark_chocolate, :default => true preference :color, :string preference :favorite_number preference :language, :string, :default => 'English' end

In the above model, five preferences have been defined:

  • hot_salsa
  • dark_chocolate
  • color
  • favorite_number
  • language

For each preference, a data type and default value can be specified. If no data type is given, it’s considered a boolean value. If no default value is given, the default is assumed to be nil.

5 Accessing preferences

Once preferences have been defined for a model, they can be accessed either using the shortcut methods that are generated for each preference or the generic methods that are not specific to a particular preference.

5.1 Shortcut methods

There are several shortcut methods that are generated. They are shown below.

Query methods:

user.prefers_hot_salsa? # => false user.prefers_dark_chocolate? # => false

Reader methods:

user.preferred_color # => nil user.preferred_language # => "English"

Writer methods:

user.prefers_hot_salsa = false # => false user.preferred_language = 'English' # => "English"

5.2 Generic methods

Each shortcut method is essentially a wrapper for the various generic methods shown below:

Query method:

user.prefers?(:hot_salsa) # => false user.prefers?(:dark_chocolate) # => false

Reader method:

user.preferred(:color) # => nil user.preferred(:language) # => "English"

Write method:

user.set_preference(:hot_salsa, false) # => false user.set_preference(:language, "English") # => "English"

5.3 Accessing all preferences

To get the collection of all custom, stored preferences for a particular record, you can access the stored_preferences has_many association which is automatically generated:

user.stored_preferences

In addition to this, you can get a hash of all stored preferences and default preferences, by accessing the preferences helper:

user.preferences # => {"language"=>"English", "color"=>nil}

This hash will contain the value for every preference that has been defined for the model, whether that’s the default value or one that has been previously stored.

A short-hand alternative for preferences is also available:

user.prefs # => {"language"=>"English", "color"=>nil}

6 Grouping preferences

In addition to defining generic preferences for the owning record, you can also group preferences by ActiveRecord objects or arbitrary names. This is best shown through an example:

user = User.find(:first) car = Car.find(:first) user.preferred_color = 'red', car # or you can use user.set_preference(:color, 'red', car) # the generic way

This will create a preference for the color red for the given car. In this way, you can have color preferences for different records.

To access the preference for a particular record, you can use the same accessor methods as before:

user.preferred_color(car) # or use user.preferred(:color, car) # the generic way

In addition to grouping preferences for a particular record, you can also group preferences by name. For example,

user = User.find(:first) user.preferred_color = 'red', 'automobiles' user.preferred_color = 'tan', 'clothing' user.preferred_color('automobiles') # => "red" user.preferred_color('clothing') # => "tan" user.preferences # => {"color"=>nil, "automobiles"=>{"color"=>"red"}, "clothing=>{"color=>"tan"}} user.preferences('automobiles') # => {"color"=>"red"}

7 Saving preferences

Note that preferences are not saved until the owning record is saved. Preferences are treated in a similar fashion to attributes. For example,

user = user.find(:first) user.attributes = {:preferred_color => 'red'} user.save!

Preferences are stored in a separate table called preferences.

8 Configuring Spree Preferences

Up until now we’ve been discussing the general preference system that was adapted to Spree. This has given you a general idea of what types of preference features are theoretically supported. Now, let’s start to look specifically at how Spree is using these preferences for configuration.

8.1 Reading the Current Preferences

At the heart of Spree preferences lies the Spree::Config module. This module provides general access to the configuration settings anywhere in the application. These settings can be accessed from initializers, models, controllers, views, etc. The Spree::Config module returns a singleton instance of the AppConfiguration model which is where the default values for all of the general Spree preferences are defined.

You can access these preferences using a convenient hash notation. To see this in action, just fire up script/console and try the following.

>> Spree::Config[:site_name] => "Spree Demo Site" >> Spree::Config[:default_locale] => "en-US"

The above examples show the default configuration values for these preferences. The defaults themselves are coded within the AppConfiguration model.

class AppConfiguration < Configuration #... snip ... preference :site_name, :string, :default => 'Spree Demo Site' #... snip ... preference :default_locale, :string, :default => 'en-US' #... snip ... end

If you are using the default preferences without any modifications, then there is actually nothing to store in the preferences table of the database. Let’s verify that it is empty.

$ script/dbconsole sqlite> SELECT * FROM preferences;

8.2 Overriding the Default Preferences

The default Spree preferences in AppConfiguration can be changed using the set method of the Spree::Config module. For example to set the default locale for Spain we could do the following:

$ script/console >> Spree::Config.set(:default_locale => "es") => nil >> Spree::Config[:default_locale] => "es"

Here we are changing a preference to something other then the default as specified in AppConfiguration. In this case the preference system will persist the new value in the preferences table.

$ script/dbconsole sqlite> SELECT * FROM preferences; 1|default_locale|1|Configuration|||es|2009-05-04 22:30:17|2009-05-04 22:30:17

Console settings may be lost if the database is rebuilt. See the advice above about preservation of settings.

8.3 Configuration Through the Admin Interface

The Spree admin interface has several different screens where various settings can be configured. For instance, the admin/general_settings URL in your Spree application can be used to configure the values for the site name and the site URL. This is basically equivalent to calling Spree::Config.set(:site_name => "Whatever", :site_url => "http://whatever.com") directly in your Ruby code.

Many of the configuration settings are not editable from an admin screen. This was basically put on hold during the refactoring of the admin interface. Now that things have stabilized we’ll be adding the missing settings in the near future. However, all of the settings can be changed through Ruby code (e.g. in initializers).

Admin interface settings may be lost if the database is rebuilt. See the advice above about preservation of settings. However, you may want to make an exception for sensitive data, i.e. to enter gateway passwords directly and not store them in the code repository.