acts_as_having_settings.rb 2.25 KB
# declare missing types
module ActiveRecord
  module Type
    class Symbol < Value
      def cast_value value
        value.to_sym
      end
    end
    class Array < Value
      def cast_value value
        ::Array.wrap(value)
      end
    end
    class Hash < Value
      def cast_value value
        h = ::Hash[value]
        h.symbolize_keys!
        h
      end
    end
  end
end

module ActsAsHavingSettings

  def self.type_cast value, type
    # do not cast nil
    return value if value.nil?
    type.send :cast_value, value
  end

  module ClassMethods

    def acts_as_having_settings(*args)
      options = args.last.is_a?(Hash) ? args.pop : {}
      field = (options[:field] || :settings).to_sym

      serialize field, Hash
      class_attribute :settings_field
      self.settings_field = field

      class_eval do
        def settings_field
          self[self.class.settings_field] ||= Hash.new
        end

        def setting_changed? setting_field
          setting_field = setting_field.to_sym
          changed_settings = self.changes[self.class.settings_field]
          return false if changed_settings.nil?

          old_setting_value = changed_settings.first.nil? ? nil : changed_settings.first[setting_field]
          new_setting_value = changed_settings.last[setting_field]
          old_setting_value != new_setting_value
        end
      end

      settings_items *args
    end

    def settings_items *names

      options = names.extract_options!
      default = options[:default]
      type = options[:type]
      type = if type.present? then ActiveRecord::Type.const_get(type.to_s.camelize.to_sym).new else nil end

      names.each do |setting|
        # symbolize key
        setting = setting.to_sym

        define_method setting do
          h = send self.class.settings_field
          val = h[setting]
          # translate default value if it is used
          if not val.nil? then val elsif default.is_a? String then gettext default else default end
        end

        define_method "#{setting}=" do |value|
          h = send self.class.settings_field
          h[setting] = if type then ActsAsHavingSettings.type_cast value, type else value end
        end
      end
    end

  end

end

ActiveRecord::Base.send(:extend, ActsAsHavingSettings::ClassMethods)