Redis Application Configuration Class

Posted on 18 Sep 2014 by Eric Oestrich

At work I needed to make a configuration class that worked well with ActiveAdmin, which meant implementing a lot of the ActiveModel modules. Below is what I came up with. It was pretty overkill for this project, but I like what came out nonetheless.

One nice thing I want to point out is that I'm passing in a redis instance, in this case the default is the AppContainer.redis. This lets you easily set up a Redis::Namespace for the configuration class, and the class itself doesn't have to know how to do that.

To define new attributes you use the attribute class method. It lets you set a default adn a type. The type is only special for :integer at the moment, but it's not hard to add more.

class Configuration
  extend ActiveModel::Naming
  include ActiveModel::Conversion
  include ActiveModel::Dirty
  include ActiveModel::Serialization
  include ActiveModel::Serializers::JSON

  def self.reset!(redis = AppContainer.redis)
    attributes.each do |attribute|
      redis.del("config_#{attribute}")
    end
  end

  def self.attributes
    @attributes ||= []
  end

  def self.attribute(name, default: default, type: type)
    attributes << name

    define_attribute_methods name

    define_method(name) do
      attr = @attributes[name] || default

      case type
      when :integer
        attr.to_i
      else
        attr
      end
    end
    define_method("#{name}=") do |value|
      send("#{name}_will_change!") unless value == @attributes[name]
      @attributes[name] = value
    end
  end

  attribute :timeout, default: 1500, type: :integer

  def initialize(redis = AppContainer.redis)
    @redis = redis
    @attributes = {}
    load_attributes
  end

  def attributes
    attrs = self.class.attributes.map do |attribute|
      [attribute, send(attribute)]
    end
    Hash[attrs]
  end

  def update(attributes)
    attributes.each do |attribute, value|
      send("#{attribute}=", value)
    end
    save
  end

  def save
    changes.each do |attribute, (old, new)|
      @redis.set("config_#{attribute}", new)
    end
    changes_applied
    true
  end

  private

  def load_attributes
    self.class.attributes.each do |attribute|
      @attributes[attribute] = @redis.get("config_#{attribute}")
    end
  end
end

To use it:

config = Configuration.new
config.timeout
# => 1500
config.timeout = 1600
config.changed?
# => true
config.changes
# => { "timeout" => [1500, 1600] }
config = Configuration.new
config.timeout
# => 1600
config.update("timeout" => 1000)
config.timeout
# => 1000
comments powered by Disqus
Eric Oestrich
I am:
All posts
Creative Commons License
This site's content is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License unless otherwise specified. Code on this site is licensed under the MIT License unless otherwise specified.