DataMapper and Sharing Common Properties


Programming in Ruby tutorials and examples

DataMapper and Sharing Common Properties

MaxCDN Content Delivery Network

Firstly I would like to point out that I am not talking about STI (Single Table Inheritance) here. What I am referring to is when you need each of your model classes inheriting a number of properties that need to be stored in each table on a per record basis, much like ActiveRecord does with the its timestamps (updated_at, created_at) properties.

Each of your model classes will need to include a module that defines the common properties. The module can also set out the property default’s and also shared behaviors for your model objects.

require 'dm-core'
require 'dm-validations'
require 'dm-types'

module Shout
  module Record
    #DataMapper::Logger.new(STDOUT, :debug)
    DataMapper.setup(:default, "sqlite:///#{File.dirname(__FILE__)}/../../../db/shout_mouth.db")

    def self.included(base)
      base.class_eval do
        include DataMapper::Resource
        property :id, DataMapper::Property::Serial
        property :is_active, DataMapper::Property::Boolean, :default => true
        property :created_at, DataMapper::Property::DateTime, :default => lambda{ |p,s| DateTime.now}

        #Scope
        def self.all_active
          all(:is_active => true, :order => [ :created_at.desc ])
        end

        #Help
        def created_at_iso8601
          created_at.strftime("%Y%m%dT%H:%M:%S")
        end
      end
    end
  end
end

In the above module I have set three properties that all my classes will include id, is_active and created_at. Also a default all_active scope will be shared amongst all the model objects.

require Dir.pwd + '/app/models/base/shout_record'

class Tag
  include Shout::Record

  property :tag, String, :length => 1000
  has n,   :posts, :through => Resource, :is_active => true, :order => [ :created_at.desc ]

  validates_uniqueness_of :tag

  #Instance Methods
  def permalink
    "#{Blog.url}/tag/#{tag}"
  end

  #Factory Methods Input
  def self.tags_from_array(array)
    array.map{|tag| Tag.first_or_create({:tag => tag.strip}, {:tag => tag.strip})}
  end

  #Factor Methods Output
  def self.usable_active_tags
    all.posts #need to force dynamic creation of the tag_posts class - this does not fire a query
    all_active.all(:tag.not => "page", :post_tags => {:post => { :is_active => true }}, :order => [:tag.asc])
  end

end

Each of the model classes will now need include the module. In the case above the class will now have the following properties id, is_active, created_at and tag. Also the all_active scope defined in the module is now accessible to the model class allowing chaining of methods such as :-

all_active.all(:tag.not => "page")

It’s a great way to keep your model classes DRY and perfect for when you need to change some default behaviors system wide.