module ActsAsPreferenced module Store # This store holds preferences in a serialized field class Field < Base def initialize( klass, field, translation_scope = nil ) super( klass, translation_scope ) @field = field @klass.send( :define_method, "preferences" ) do prefs = read_attribute(field) unless prefs prefs = HashWithIndifferentAccess.new write_attribute(field, prefs) end return prefs end @klass.send( :define_method, "preferences=" ) do |new_prefs| self.class.all_preferences.each do |pref| # 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) new_prefs[pref.name] ||= false if pref.default == true end write_attribute(field, new_prefs) end end def preference( name, options = {} ) options.assert_valid_keys( :section, :label, :choices, :default ) pref = Preference.new( self, name, options ) if options[:choices] # Choice @klass.send( :define_method, "#{name}" ) do # This symbolizes strings and leave other values (eg. numbers) # intact value = preferences[name].is_a?( String ) ? preferences[name].to_sym : preferences[name] value || pref.default end else # Boolean @klass.send( :define_method, "#{name}?" ) do # We check for nil to allow "false" here preferences[name].nil? ? pref.default : preferences[name] end end @klass.send( :define_method, "#{name}=" ) do |value| if pref.value_valid?( value ) preferences[name] = value else raise ArgumentError, "Value #{value} invalid for preference #{pref.name}" end end end end end end