module Decor
Decor
provides a simple way to define multiple representations of an object.
This is useful for when you want to retain multiple versions of your objects while providing a consistent interface between versions.
For example:
class Company < ActiveRecord::Base include Decor # the first version, customers are dependent on certain artifacts # like industry_id and industry version "v1" do INDUSTRIES = {1 => "Farm", 2 => "Software"} def industry INDUSTRIES[industry_id] end def as_json(*_) super(:only => [:id, :name, :industry_id], :methods => [:industry]) end end # switch to using industry standard codes, cleans up the name version "v2" do SIC_CODES = {...} # use the cleaned name for the name instead def name name_cleaned end def industry SIC_DOES[sic_code] end def as_json(*_) super(:only => [:id, :name, :sic_code], :methods => [:industry]) end end end
In our API, for instance, we can then define a single endpoint for both versions:
get "/api/:version/companies/:id.json" do Company.find(params[:id]).for(version).to_json end
This helps us keep our models fat and our “controllers” skinny. This also helps unit testing the versions of your API.
Further details can be found on [Github](github.com/mtodd/decor/).
Public Class Methods
Decor
is a mixin. Include it into your class and then use the `version` class methods in the class body to define your versions, then use the `for` instance method on your objects to use a specific version.
For example:
class Model include Decor version "v1" do # implement specifics for this version here end end model = Model.new.for("v1")
# File lib/decor.rb, line 78 def self.included(target) target.send(:extend, ClassMethods) class << target; attr_accessor :versions; end target.versions = {} end
Public Instance Methods
An object will be told to behave like the version specified.
Options provide additional values in the context of the verions.
class Model include Decor version "v1" do def versioned? true end end def versioned? false end end model = Model.new model. versioned? #=> false model.for("v1").versioned? #=> true
An optional context can be supplied which will make external resources available for specific functions in your versions. For example:
class User include Decor version "v1" do def display_name "%s (%s)" % [name, band.display_name] end end end user = User.find(id).for("v1", :band => external_band) user.display_name #=> "Dan Auerbach (The Black Keys)"
Lastly, it's possible to pass in a `:module` option which will override the module already defined for the version specified (making the `version` passed in almost meaningless).
module Specialized # special considerations here end Model.new.for("v1", :module => Specialized)
# File lib/decor.rb, line 201 def for(version, options = {}) version_module = self.version_module_for(version, options) decorator = Class.new(Base).new(self, version, options) decorator.send(:extend, version_module) decorator end
Handles finding the module defined for the `version` specified, or overriding with the `:module` option.
See `for` for details.
# File lib/decor.rb, line 213 def version_module_for(version, options = {}) return options.delete(:module) if options.key?(:module) return self.class.versions[version] if self.class.versions.key?(version) self.class.const_get(version.upcase) end