Sunday, June 03, 2007

ActiveProperty



I'm a Ruby on Rails developer and I was missing some functionalities in the area of property and configuration of applications. In my previous job as a Java developer, I used property files to define environment specific values. When moving from development to test or production changing the property files would configure file paths and so on.

RoR has a very powerfull testframework with fixtures so most of the property functionalities are not necessary anymore, because you can use the fixtures to do that. One area I couldn't get configured without the use of some kind of properties, paths for file modifications.
RoR has several environment.rb files to set some of the differences between production, test and development, but that is not very intuitive to me. That's why I wanted to use an ActiveRecord to read and store my properties.

The ActiveProperty was born..

The main reason why I wanted something like an ActiveProperty is because:
  1. I wanted a clear distinction between production properties and test properties.

  2. By storing these properties as ActiveRecord, we can use fixtures to make sure we can test everything without have to write testspecific stuff like "path = '/../mocks/test/' if RAILS_ENV == 'test' " in controllers and models.

  3. Using the a scaffold controller on the ActiveProperty gives you an easy interface to maintain the properties.


For test purposes these properties can be simply overridden by the active_properties.yml fixture
This is especially handy when working with files. Production paths can be loaded with migrations and functional tests use values from the fixture.

By superclassing ActiveProperty, you can create multiple scopes for your properties.

Usage:
Superclass this class and set your scope fe ApplicationProperty
  • ApplicationProperty.spoon -> "no key found"
  • ApplicationProperty.spoon = "There is no spoon" -> "There is no spoon"
  • ApplicationProperty.spoon -> "There is no spoon"
Author: Jeroen Knoops
Company: ViCi BV - Eindhoven

ActiveProperty application: ActiveProperty.zip

active_property.rb:


# ActiveProperty.
#
# This class can be used to store properties. The main reason why I made this is because
# I wanted a clear distinction between production properties and test properties.
# By storing these properties as ActiveRecord, we can use fixtures.
#
# For test purposes these properties can be simply overridden by the active_properties.yml fixture
# This is especially handy when working with files. Production paths can be loaded with migrations and functional tests use values from the fixture.
#
# By superclassing ActiveProperty, you can create multiple scopes for your properties.

# Usage:
# Superclass this class and set your scope fe ApplicationProperty
# * ApplicationProperty.spoon -> "no key found"
# * ApplicationProperty.spoon = "There is no spoon" -> "There is no spoon"
# * ApplicationProperty.spoon -> "There is no spoon"
#
# Maintaining the properties can be done by scaffolding the ActiveProperty model.
#
# Author: Jeroen Knoops jeroen@vici-nl.com
# Company: ViCi BV - Eindhoven

class ActiveProperty < ActiveRecord::Base

@scope = self.name

class << self

def method_missing(method_id, *args)
begin
super(method_id, args)
rescue
method_id_string = method_id.id2name
if method_id_string[method_id_string.length - 1] == 61
result = set_value(@scope, method_id_string[0...-1], args[0])
else
result = get_value(@scope, method_id.id2name)
end
result || "No active property found in scope #{@scope} for: #{method_id}"
end
end

private

def get_value(scope, key)
ap = find(:first, :conditions => ["`key` = ? and `scope` = ?", key, scope])
if ap
return ap.value
end
return nil
end

def set_value(scope, key, value)
ap = find(:first, :conditions => ["`key` = ? and `scope` = ?", key, scope])
if ap
ap.value = value
ap.save!
else
self.create!(:scope => scope,
:key => key,
:value => value)
end
end
end
end