Module: Preferences::MacroMethods

Defined in:
lib/preferences.rb

Instance Method Summary collapse

Instance Method Details

#preference(name, *args) ⇒ Object

Defines a new preference for all records in the model. By default, preferences are assumed to have a boolean data type, so all values will be typecasted to true/false based on ActiveRecord rules.

Configuration options:

  • :default - The default value for the preference. Default is nil.

  • :group_defaults - Defines the default values to use for various groups. This should map group_name -> defaults. For ActiveRecord groups, use the class name.

Examples

The example below shows the various ways to define a preference for a particular model.

class User < ActiveRecord::Base
  preference :notifications, :default => false
  preference :color, :string, :default => 'red', :group_defaults => {:car => 'black'}
  preference :favorite_number, :integer
  preference :data, :any # Allows any data type to be stored
end

All preferences are also inherited by subclasses.

Associations

After the first preference is defined, the following associations are created for the model:

  • stored_preferences - A collection of all the custom preferences specified for a record. This will not include default preferences unless they have been explicitly set.

Named scopes

In addition to the above associations, the following named scopes get generated for the model:

  • with_preferences - Finds all records with a given set of preferences

  • without_preferences - Finds all records without a given set of preferences

In addition to utilizing preferences stored in the database, each of the above scopes also take into account the defaults that have been defined for each preference.

Example:

User.with_preferences(:notifications => true)
User.with_preferences(:notifications => true, :color => 'blue')

# Searching with group preferences
car = Car.find(:first)
User.with_preferences(car => {:color => 'blue'})
User.with_preferences(:notifications => true, car => {:color => 'blue'})

Generated accessors

In addition to calling prefers? and preferred on a record, you can also use the shortcut accessor methods that are generated when a preference is defined. For example,

class User < ActiveRecord::Base
  preference :notifications
end

…generates the following methods:

  • prefers_notifications? - Whether a value has been specified, i.e. record.prefers?(:notifications)

  • prefers_notifications - The actual value stored, i.e. record.prefers(:notifications)

  • prefers_notifications=(value) - Sets a new value, i.e. record.write_preference(:notifications, value)

  • prefers_notifications_changed? - Whether the preference has unsaved changes

  • prefers_notifications_was - The last saved value for the preference

  • prefers_notifications_change - A list of [original_value, new_value] if the preference has changed

  • prefers_notifications_will_change! - Forces the preference to get updated

  • reset_prefers_notifications! - Reverts any unsaved changes to the preference

…and the equivalent preferred methods:

  • preferred_notifications?

  • preferred_notifications

  • preferred_notifications=(value)

  • preferred_notifications_changed?

  • preferred_notifications_was

  • preferred_notifications_change

  • preferred_notifications_will_change!

  • reset_preferred_notifications!

Notice that there are two tenses used depending on the context of the preference. Conventionally, prefers_notifications? is better for accessing boolean preferences, while preferred_color is better for accessing non-boolean preferences.

Example:

user = User.find(:first)
user.prefers_notifications?         # => false
user.prefers_notifications          # => false
user.preferred_color?               # => true
user.preferred_color                # => 'red'
user.preferred_color = 'blue'       # => 'blue'

user.prefers_notifications = true

car = Car.find(:first)
user.preferred_color = 'red', car   # => 'red'
user.preferred_color(car)           # => 'red'
user.preferred_color?(car)          # => true

user.save!  # => true


154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/preferences.rb', line 154

def preference(name, *args)
  unless included_modules.include?(InstanceMethods)
    class_attribute :preference_definitions
    self.preference_definitions = {}

    has_many :stored_preferences, as: :owner, class_name: "Preference", dependent: :delete_all

    after_save :update_preferences

    # Named scopes
    scope :with_preferences, lambda { |preferences| build_preference_scope(preferences) }
    scope :without_preferences, lambda { |preferences| build_preference_scope(preferences, true) }

    extend Preferences::ClassMethods
    include Preferences::InstanceMethods
  end

  # Create the definition
  name = name.to_s
  definition = PreferenceDefinition.new(name, *args)
  preference_definitions[name] = definition

  # Create short-hand accessor methods, making sure that the name
  # is method-safe in terms of what characters are allowed
  name = name.gsub(/[^A-Za-z0-9_-]/, "").underscore

  # Query lookup
  define_method("preferred_#{name}?") do |*group|
    preferred?(name, group.first)
  end
  alias_method "prefers_#{name}?", "preferred_#{name}?"

  # Reader
  define_method("preferred_#{name}") do |*group|
    preferred(name, group.first)
  end
  alias_method "prefers_#{name}", "preferred_#{name}"

  # Writer
  define_method("preferred_#{name}=") do |*args|
    write_preference(*args.flatten.unshift(name))
  end
  alias_method "prefers_#{name}=", "preferred_#{name}="

  # Changes
  define_method("preferred_#{name}_changed?") do |*group|
    preference_changed?(name, group.first)
  end
  alias_method "prefers_#{name}_changed?", "preferred_#{name}_changed?"

  define_method("preferred_#{name}_was") do |*group|
    preference_was(name, group.first)
  end
  alias_method "prefers_#{name}_was", "preferred_#{name}_was"

  define_method("preferred_#{name}_change") do |*group|
    preference_change(name, group.first)
  end
  alias_method "prefers_#{name}_change", "preferred_#{name}_change"

  define_method("preferred_#{name}_will_change!") do |*group|
    preference_will_change!(name, group.first)
  end
  alias_method "prefers_#{name}_will_change!", "preferred_#{name}_will_change!"

  define_method("reset_preferred_#{name}!") do |*group|
    reset_preference!(name, group.first)
  end
  alias_method "reset_prefers_#{name}!", "reset_preferred_#{name}!"

  definition
end