lib/preferences.rb in preferences-0.1.4 vs lib/preferences.rb in preferences-0.1.5

- old
+ new

@@ -18,15 +18,39 @@ # u.save! # # u = User.find_by_login('admin') # u.attributes = {:prefers_notifications => true} # u.save! + # + # == Validations + # + # Since the generated accessors for a preference allow the preference to be + # treated just like regular ActiveRecord column attributes, they can also be + # validated against in the same way. For example, + # + # class User < ActiveRecord::Base + # preference :color, :string + # + # validates_presence_of :preferred_color + # validates_inclusion_of :preferred_color, :in => %w(red green blue) + # end + # + # u = User.new + # u.valid? # => false + # u.errors.on(:preferred_color) # => "can't be blank" + # + # u.preferred_color = 'white' + # u.valid? # => false + # u.errors.on(:preferred_color) # => "is not included in the list" + # + # u.preferred_color = 'red' + # u.valid? # => true module Preferences module MacroMethods - # 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. + # 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. # # == Examples @@ -45,47 +69,50 @@ # # == 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 + # * +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. # - # == Generated shortcut methods + # == Generated accessors # # In addition to calling <tt>prefers?</tt> and +preferred+ on a record, you - # can also use the shortcut methods that are generated when a preference is - # defined. For example, + # 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: - # * <tt>prefers_notifications?</tt> - The same as calling <tt>record.prefers?(:notifications)</tt> - # * <tt>prefers_notifications=(value)</tt> - The same as calling <tt>record.set_preference(:notifications, value)</tt> - # * <tt>preferred_notifications</tt> - The same as called <tt>record.preferred(:notifications)</tt> - # * <tt>preferred_notifications=(value)</tt> - The same as calling <tt>record.set_preference(:notifications, value)</tt> + # * <tt>prefers_notifications?</tt> - Whether a value has been specified, i.e. <tt>record.prefers?(:notifications)</tt> + # * <tt>prefers_notifications</tt> - The actual value stored, i.e. <tt>record.prefers(:notifications)</tt> + # * <tt>prefers_notifications=(value)</tt> - Sets a new value, i.e. <tt>record.set_preference(:notifications, value)</tt> + # * <tt>preferred_notifications?</tt> - Whether a value has been specified, i.e. <tt>record.preferred?(:notifications)</tt> + # * <tt>preferred_notifications</tt> - The actual value stored, i.e. <tt>record.preferred(:notifications)</tt> + # * <tt>preferred_notifications=(value)</tt> - Sets a new value, i.e. <tt>record.set_preference(:notifications, value)</tt> # # Notice that there are two tenses used depending on the context of the # preference. Conventionally, <tt>prefers_notifications?</tt> is better - # for boolean preferences, while +preferred_color+ is better for non-boolean - # preferences. + # 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_color? # => true + # 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.prefers_color?(car) # => true + # user.preferred_color?(car) # => true # # user.save! # => true def preference(attribute, *args) unless included_modules.include?(InstanceMethods) class_inheritable_hash :preference_definitions @@ -107,30 +134,32 @@ attribute = attribute.to_s definition = PreferenceDefinition.new(attribute, *args) self.preference_definitions[attribute] = definition self.default_preferences[attribute] = definition.default_value - # Create short-hand helper methods, making sure that the attribute + # Create short-hand accessor methods, making sure that the attribute # is method-safe in terms of what characters are allowed attribute = attribute.gsub(/[^A-Za-z0-9_-]/, '').underscore # Query lookup - define_method("prefers_#{attribute}?") do |*group| - prefers?(attribute, group.first) + define_method("preferred_#{attribute}?") do |*group| + preferred?(attribute, group.first) end + alias_method "prefers_#{attribute}?", "preferred_#{attribute}?" - # Writer - define_method("prefers_#{attribute}=") do |*args| - set_preference(*([attribute] + [args].flatten)) - end - alias_method "preferred_#{attribute}=", "prefers_#{attribute}=" - # Reader define_method("preferred_#{attribute}") do |*group| preferred(attribute, group.first) end + alias_method "prefers_#{attribute}", "preferred_#{attribute}" + # Writer + define_method("preferred_#{attribute}=") do |*args| + set_preference(*([attribute] + [args].flatten)) + end + alias_method "prefers_#{attribute}=", "preferred_#{attribute}=" + definition end end module InstanceMethods @@ -154,11 +183,11 @@ # A user with stored values for a particular group: # user.preferred_color = 'red', 'cars' # user.preferences # => {"language"=>"English", "color"=>nil, "cars"=>{"language=>"English", "color"=>"red"}} # - # Getting preference values for the owning record: + # Getting preference values *just* for the owning record (i.e. excluding groups): # user.preferences(nil) # => {"language"=>"English", "color"=>nil} # # Getting preference values for a particular group: # user.preferences('cars') @@ -167,10 +196,13 @@ if args.empty? group = nil conditions = {} else group = args.first + + # Split the actual group into its different parts (id/type) in case + # a record is passed in group_id, group_type = Preference.split_group(group) conditions = {:group_id => group_id, :group_type => group_type} end # Find all of the stored preferences @@ -187,64 +219,84 @@ preferences[preference.attribute] = preference.value all_preferences end end - # Queries whether or not a value has been specified for the given attribute. - # This is dependent on how the value is type-casted. + # Queries whether or not a value is present for the given attribute. This + # is dependent on how the value is type-casted. # # == Examples # - # user = User.find(:first) - # user.prefers?(:notifications) # => true + # class User < ActiveRecord::Base + # preference :color, :string, :default => 'red' + # end # - # user.prefers(:notifications, 'error') # => true + # user = User.create + # user.preferred(:color) # => "red" + # user.preferred?(:color) # => true + # user.preferred?(:color, 'cars') # => true + # user.preferred?(:color, Car.first) # => true # - # newsgroup = Newsgroup.find(:first) - # user.prefers?(:notifications, newsgroup) # => false - def prefers?(attribute, group = nil) + # user.set_preference(:color, nil) + # user.preferred(:color) # => nil + # user.preferred?(:color) # => false + def preferred?(attribute, group = nil) attribute = attribute.to_s value = preferred(attribute, group) preference_definitions[attribute].query(value) end + alias_method :prefers?, :preferred? - # Gets the preferred value for the given attribute. + # Gets the actual value stored for the given attribute, or the default + # value if nothing is present. # # == Examples # - # user = User.find(:first) - # user.preferred(:color) # => 'red' + # class User < ActiveRecord::Base + # preference :color, :string, :default => 'red' + # end # - # user.preferred(:color, 'cars') # => 'blue' + # user = User.create + # user.preferred(:color) # => "red" + # user.preferred(:color, 'cars') # => "red" + # user.preferred(:color, Car.first) # => "red" # - # car = Car.find(:first) - # user.preferred(:color, car) # => 'black' + # user.set_preference(:color, 'blue') + # user.preferred(:color) # => "blue" def preferred(attribute, group = nil) attribute = attribute.to_s if @preference_values && @preference_values[attribute] && @preference_values[attribute].include?(group) + # Value for this attribute/group has been written, but not saved yet: + # grab from the pending values value = @preference_values[attribute][group] else + # Split the group being filtered group_id, group_type = Preference.split_group(group) + + # Grab the first preference; if it doesn't exist, use the default value preference = stored_preferences.find(:first, :conditions => {:attribute => attribute, :group_id => group_id, :group_type => group_type}) value = preference ? preference.value : preference_definitions[attribute].default_value end value end + alias_method :prefers, :preferred # Sets a new value for the given attribute. The actual Preference record - # is *not* created until the actual record is saved. + # is *not* created until this record is saved. In this way, preferences + # act *exactly* the same as attributes. They can be written to and + # validated against, but won't actually be written to the database until + # the record is saved. # # == Examples # # user = User.find(:first) - # user.set_preference(:notifications, false) # => false + # user.set_preference(:color, 'red') # => "red" # user.save! # - # newsgroup = Newsgroup.find(:first) - # user.set_preference(:notifications, true, newsgroup) # => true + # user.set_preference(:color, 'blue', Car.first) # => "blue" # user.save! def set_preference(attribute, value, group = nil) attribute = attribute.to_s @preference_values ||= {}