From be796902bb88df22d17a2b67162eb656cb75e3bb Mon Sep 17 00:00:00 2001 From: Richard Taylor Date: Wed, 7 Dec 2011 14:30:11 +0000 Subject: [PATCH 1/5] fixed deprecation warnings --- lib/preferences.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/preferences.rb b/lib/preferences.rb index 126080e..7a9e7e1 100644 --- a/lib/preferences.rb +++ b/lib/preferences.rb @@ -153,7 +153,7 @@ module MacroMethods # user.save! # => true def preference(name, *args) unless included_modules.include?(InstanceMethods) - class_inheritable_hash :preference_definitions + class_attribute :preference_definitions self.preference_definitions = {} has_many :stored_preferences, :as => :owner, :class_name => 'Preference', :dependent => :destroy From 7d37f9778a52ef6fa0957e14d3bbe44b92ff0a76 Mon Sep 17 00:00:00 2001 From: hoverlover Date: Tue, 3 Apr 2012 10:23:21 -0500 Subject: [PATCH 2/5] Fix undefined method error for class_inheritable_hash method in Rails 3.2. --- lib/preferences.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/preferences.rb b/lib/preferences.rb index 126080e..7a9e7e1 100644 --- a/lib/preferences.rb +++ b/lib/preferences.rb @@ -153,7 +153,7 @@ module MacroMethods # user.save! # => true def preference(name, *args) unless included_modules.include?(InstanceMethods) - class_inheritable_hash :preference_definitions + class_attribute :preference_definitions self.preference_definitions = {} has_many :stored_preferences, :as => :owner, :class_name => 'Preference', :dependent => :destroy From 00e083334a17aa37b129a233241cb5137bd08001 Mon Sep 17 00:00:00 2001 From: hoverlover Date: Sat, 1 Sep 2012 00:06:21 -0500 Subject: [PATCH 3/5] Fix for getting around mass assignment whitelist. --- lib/preferences.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/preferences.rb b/lib/preferences.rb index 7a9e7e1..79f39ce 100644 --- a/lib/preferences.rb +++ b/lib/preferences.rb @@ -586,7 +586,10 @@ def update_preferences preferences.keys.each do |name| # Find an existing preference or build a new one attributes = {:name => name, :group_id => group_id, :group_type => group_type} - preference = find_preferences(attributes).first || stored_preferences.build(attributes) + unless (preference = find_preferences(attributes).first) + preference = stored_preferences.build + attributes.each_pair { |attribute, value| preference[attribute] = value } + end preference.value = preferred(name, group) preference.save! end From c058833c56b5a04d79fb9b3b71d28ea2f6557954 Mon Sep 17 00:00:00 2001 From: kareem mayan Date: Sun, 9 Sep 2012 18:33:57 -0700 Subject: [PATCH 4/5] make attributes attr_accessible Rails 3.0 makes everything protected by default. --- app/models/preference.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/preference.rb b/app/models/preference.rb index 8538721..fa53235 100644 --- a/app/models/preference.rb +++ b/app/models/preference.rb @@ -14,6 +14,8 @@ class Preference < ActiveRecord::Base validates_presence_of :name, :owner_id, :owner_type validates_presence_of :group_type, :if => :group_id? + attr_accessible :name, :group_id, :group_type + class << self # Splits the given group into its corresponding id and type. For simple # primitives, the id will be nil. For complex types, specifically From d0774bb028c1d9cac5c98d815d941f559efdaa08 Mon Sep 17 00:00:00 2001 From: Tomasz Subik Date: Fri, 12 Dec 2014 14:45:29 +0100 Subject: [PATCH 5/5] rails 4 compability --- lib/preferences.rb | 252 ++++++++++++++++++++++----------------------- 1 file changed, 126 insertions(+), 126 deletions(-) diff --git a/lib/preferences.rb b/lib/preferences.rb index 79f39ce..7f6f2f4 100644 --- a/lib/preferences.rb +++ b/lib/preferences.rb @@ -2,46 +2,46 @@ require 'preferences/preference_definition' # Adds support for defining preferences on ActiveRecord models. -# +# # == Saving preferences -# +# # Preferences are not automatically saved when they are set. You must save # the record that the preferences were set on. -# +# # For example, -# +# # class User < ActiveRecord::Base # preference :notifications # end -# +# # u = User.new(:login => 'admin', :prefers_notifications => false) # 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 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 @@ -49,66 +49,66 @@ 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. - # + # # 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. - # + # 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) @@ -118,7 +118,7 @@ module MacroMethods # * 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 @@ -128,107 +128,107 @@ module MacroMethods # * 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 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 => :destroy - + 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) self.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 end - + module ClassMethods #:nodoc: # Generates the scope for looking under records with a specific set of # preferences associated with them. - # + # # Note thate this is a bit more complicated than usual since the preference # definitions aren't in the database for joins, defaults need to be accounted # for, and querying for the the presence of multiple preferences requires @@ -237,7 +237,7 @@ def build_preference_scope(preferences, inverse = false) joins = [] statements = [] values = [] - + # Flatten the preferences for easier processing preferences = preferences.inject({}) do |result, (group, value)| if value.is_a?(Hash) @@ -247,16 +247,16 @@ def build_preference_scope(preferences, inverse = false) end result end - + preferences.each do |(group, preference), value| group_id, group_type = Preference.split_group(group) preference = preference.to_s definition = preference_definitions[preference.to_s] value = definition.type_cast(value) is_default = definition.default_value(group_type) == value - + table = "preferences_#{group_id}_#{group_type}_#{preference}" - + # Since each preference is a different record, they need their own # join so that the proper conditions can be set joins << "LEFT JOIN preferences AS #{table} ON #{table}.owner_id = #{table_name}.#{primary_key} AND " + sanitize_sql( @@ -265,7 +265,7 @@ def build_preference_scope(preferences, inverse = false) "#{table}.group_type" => group_type, "#{table}.name" => preference ) - + if inverse statements << "#{table}.id IS NOT NULL AND #{table}.value " + (value.nil? ? ' IS NOT NULL' : ' != ?') + (!is_default ? " OR #{table}.id IS NULL" : '') else @@ -273,104 +273,104 @@ def build_preference_scope(preferences, inverse = false) end values << value unless value.nil? end - + sql = statements.map! {|statement| "(#{statement})"} * ' AND ' {:joins => joins, :conditions => values.unshift(sql)} end end - + module InstanceMethods def self.included(base) #:nodoc: base.class_eval do alias_method :prefs, :preferences end end - + # Finds all preferences, including defaults, for the current record. If # looking up custom group preferences, then this will include all default # preferences within that particular group as well. - # + # # == Examples - # + # # A user with no stored values: - # + # # user = User.find(:first) # user.preferences # => {"language"=>"English", "color"=>nil} - # + # # A user with stored values for a particular group: - # + # # user.preferred_color = 'red', :cars # user.preferences(:cars) # => {"language=>"English", "color"=>"red"} def preferences(group = nil) preferences = preferences_group(group) - + unless preferences_group_loaded?(group) group_id, group_type = Preference.split_group(group) find_preferences(:group_id => group_id, :group_type => group_type).each do |preference| preferences[preference.name] = preference.value unless preferences.include?(preference.name) end - + # Add defaults preference_definitions.each do |name, definition| preferences[name] = definition.default_value(group_type) unless preferences.include?(name) end end - + preferences.inject({}) do |typed_preferences, (name, value)| typed_preferences[name] = value.nil? ? value : preference_definitions[name].type_cast(value) typed_preferences end end - + # Queries whether or not a value is present for the given preference. # This is dependent on how the value is type-casted. - # + # # == Examples - # + # # class User < ActiveRecord::Base # preference :color, :string, :default => 'red' # end - # + # # user = User.create # user.preferred(:color) # => "red" # user.preferred?(:color) # => true # user.preferred?(:color, 'cars') # => true # user.preferred?(:color, Car.first) # => true - # + # # user.write_preference(:color, nil) # user.preferred(:color) # => nil # user.preferred?(:color) # => false def preferred?(name, group = nil) name = name.to_s assert_valid_preference(name) - + value = preferred(name, group) preference_definitions[name].query(value) end alias_method :prefers?, :preferred? - + # Gets the actual value stored for the given preference, or the default # value if nothing is present. - # + # # == Examples - # + # # class User < ActiveRecord::Base # preference :color, :string, :default => 'red' # end - # + # # user = User.create # user.preferred(:color) # => "red" # user.preferred(:color, 'cars') # => "red" # user.preferred(:color, Car.first) # => "red" - # + # # user.write_preference(:color, 'blue') # user.preferred(:color) # => "blue" def preferred(name, group = nil) name = name.to_s assert_valid_preference(name) - + if preferences_group(group).include?(name) # Value for this group/name has been written, but not saved yet: # grab from the pending values @@ -379,35 +379,35 @@ def preferred(name, group = nil) # Grab the first preference; if it doesn't exist, use the default value group_id, group_type = Preference.split_group(group) preference = find_preferences(:name => name, :group_id => group_id, :group_type => group_type).first unless preferences_group_loaded?(group) - + value = preference ? preference.value : preference_definitions[name].default_value(group_type) preferences_group(group)[name] = value end - + definition = preference_definitions[name] value = definition.type_cast(value) unless value.nil? value end alias_method :prefers, :preferred - + # Sets a new value for the given preference. The actual Preference record # 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.write_preference(:color, 'red') # => "red" # user.save! - # + # # user.write_preference(:color, 'blue', Car.first) # => "blue" # user.save! def write_preference(name, value, group = nil) name = name.to_s assert_valid_preference(name) - + preferences_changed = preferences_changed_group(group) if preferences_changed.include?(name) old = preferences_changed[name] @@ -416,24 +416,24 @@ def write_preference(name, value, group = nil) old = clone_preference_value(name, group) preferences_changed[name] = old if preference_value_changed?(name, old, value) end - + value = convert_number_column_value(value) if preference_definitions[name].number? preferences_group(group)[name] = preference_definitions[name].type_cast(value) - + value end - + # Whether any attributes have unsaved changes. - # + # # == Examples - # + # # user = User.find(:first) # user.preferences_changed? # => false # user.write_preference(:color, 'red') # user.preferences_changed? # => true # user.save # user.preferences_changed? # => false - # + # # # Groups # user.preferences_changed?(:car) # => false # user.write_preference(:color, 'red', :car) @@ -441,18 +441,18 @@ def write_preference(name, value, group = nil) def preferences_changed?(group = nil) !preferences_changed_group(group).empty? end - + # A list of the preferences that have unsaved changes. - # + # # == Examples - # + # # user = User.find(:first) # user.preferences_changed # => [] # user.write_preference(:color, 'red') # user.preferences_changed # => ["color"] # user.save # user.preferences_changed # => [] - # + # # # Groups # user.preferences_changed(:car) # => [] # user.write_preference(:color, 'red', :car) @@ -460,20 +460,20 @@ def preferences_changed?(group = nil) def preferences_changed(group = nil) preferences_changed_group(group).keys end - + # A map of the preferences that have changed in the current object. - # + # # == Examples - # + # # user = User.find(:first) # user.preferred(:color) # => nil # user.preference_changes # => {} - # + # # user.write_preference(:color, 'red') # user.preference_changes # => {"color" => [nil, "red"]} # user.save # user.preference_changes # => {} - # + # # # Groups # user.preferred(:color, :car) # => nil # user.preference_changes(:car) # => {} @@ -485,36 +485,36 @@ def preference_changes(group = nil) changes end end - + # Reloads the pereferences of this object as well as its attributes def reload(*args) #:nodoc: result = super - + @preferences.clear if @preferences @preferences_changed.clear if @preferences_changed - + result end - + private # Asserts that the given name is a valid preference in this model. If it # is not, then an ArgumentError exception is raised. def assert_valid_preference(name) raise(ArgumentError, "Unknown preference: #{name}") unless preference_definitions.include?(name) end - + # Gets the set of preferences identified by the given group def preferences_group(group) @preferences ||= {} @preferences[group.is_a?(Symbol) ? group.to_s : group] ||= {} end - + # Determines whether the given group of preferences has already been # loaded from the database def preferences_group_loaded?(group) preference_definitions.length == preferences_group(group).length end - + # Generates a clone of the current value stored for the preference with # the given name / group def clone_preference_value(name, group) @@ -523,41 +523,41 @@ def clone_preference_value(name, group) rescue TypeError, NoMethodError value end - + # Keeps track of all preferences that have been changed so that they can # be properly updated in the database. Maps group -> preference -> value. def preferences_changed_group(group) @preferences_changed ||= {} @preferences_changed[group.is_a?(Symbol) ? group.to_s : group] ||= {} end - + # Determines whether a preference changed in the given group def preference_changed?(name, group) preferences_changed_group(group).include?(name) end - + # Builds an array of [original_value, new_value] for the given preference. # If the perference did not change, this will return nil. def preference_change(name, group) [preferences_changed_group(group)[name], preferred(name, group)] if preference_changed?(name, group) end - + # Gets the last saved value for the given preference def preference_was(name, group) preference_changed?(name, group) ? preferences_changed_group(group)[name] : preferred(name, group) end - + # Forces the given preference to be saved regardless of whether the value # is actually diferent def preference_will_change!(name, group) preferences_changed_group(group)[name] = clone_preference_value(name, group) end - + # Reverts any unsaved changes to the given preference def reset_preference!(name, group) write_preference(name, preferences_changed_group(group)[name], group) if preference_changed?(name, group) end - + # Determines whether the old value is different from the new value for the # given preference. This will use the typecasted value to determine # equality. @@ -572,17 +572,17 @@ def preference_value_changed?(name, old, value) else value = definition.type_cast(value) end - + old != value end - + # Updates any preferences that have been changed/added since the record # was last saved def update_preferences if @preferences_changed @preferences_changed.each do |group, preferences| group_id, group_type = Preference.split_group(group) - + preferences.keys.each do |name| # Find an existing preference or build a new one attributes = {:name => name, :group_id => group_id, :group_type => group_type} @@ -594,21 +594,21 @@ def update_preferences preference.save! end end - + @preferences_changed.clear end end - + # Finds all stored preferences with the given attributes. This will do a # smart lookup by looking at the in-memory collection if it was eager- # loaded. def find_preferences(attributes) if stored_preferences.loaded? stored_preferences.select do |preference| - attributes.all? {|attribute, value| preference[attribute] == value} + attributes.all? {|attribute, value| preference[attribute] == value} end else - stored_preferences.find(:all, :conditions => attributes) + stored_preferences.where(attributes) end end end