module ActsAsPreferenced module Store class Association < Base attr_reader :association def initialize(klass, assoc, translation_scope = nil) super( klass, translation_scope ) @association = assoc @klass.send( :define_method, "preferences" ) do prefs = send( preference_config.association ) || send( "build_#{preference_config.association}" ) return prefs end @klass.send( :define_method, "preferences=" ) do |new_prefs| pref_object = send( preference_config.association ) || send( "build_#{preference_config.association}" ) self.class.all_preferences.each do |pref| old_value = pref_object.send( pref.name ) new_value = new_prefs[pref.name] if new_value stringified_value = new_value.is_a?( Symbol ) ? new_value.to_s : new_value new_prefs[pref.name] = stringified_value else # Hack to allow boolean preferences to be set to false. This looks # wrong from model point of view (if you mass assign preferences, # absent boolean ones will be set to false even though default is # true), but it works well for forms. # TODO: find a way to make it work properly both for defaults and # forms (if at all possible) if pref.default == true || old_value == true new_prefs[pref.name] = false end end end pref_object.attributes = new_prefs end @klass.send( :define_method, "save_preferences" ) do pref_object = send( preference_config.association ) pref_object.save! if pref_object end @klass.before_save :save_preferences end def preference( name, options = {} ) options.assert_valid_keys( :section, :label, :choices, :default ) pref = Preference.new( self, name, options ) method_name = options[:choices] ? name.to_s : "#{name}?" @klass.send( :define_method, method_name ) do value = preferences[name] value = pref.default if value.nil? value &&= value.to_sym if value.is_a?( String ) value end @klass.send( :define_method, "#{name}=" ) do |value| if pref.value_valid?( value ) preferences[name] = (value.is_a?( Symbol ) ? value.to_s : value) else raise ArgumentError, "Value #{value} invalid for preference #{pref.name}" end end end end end end