Miscellaneous Features

DataMapper comes loaded features, many of which other ORMs require external libraries for.

Single Table Inheritance

Many ORMs support Single Table Inheritance and DataMapper is no different. In order to declare a model for Single Table Inheritance, define a property with the data-type of Types::Discriminator.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person
  include DataMapper::Resource

  property :name, String
  property :job,  String,        :length => 1..255
  property :type, Discriminator
  ...
end

class Male   < Person; end
class Father < Male;   end
class Son    < Male;   end

class Woman    < Person; end
class Mother   < Woman;  end
class Daughter < Woman;  end

When DataMapper sees your type column declared as type Types::Discriminator, it will automatically insert the class name of the object you’ve created and later instantiate that row as that class. It also supports deep inheritance, so doing Woman.all will select all women, mothers, and daughters (and deeper inherited classes if they exist).

Timezone handling

Currently, DataMapper has no builtin support for working with timezones particularly. This means that time properties will always be stored and retrieved in the timezone the datastore is set to. There is no API to explicitly manipulate timezones.

Have a look at dm-zone-types for more elaborate timezone handling support.

Paranoia

Sometimes…most times…you don’t really want to destroy a row in the database, you just want to mark it as deleted so that you can restore it later if need be. This is aptly-named Paranoia and DataMapper has basic support for this baked right in. Just declare a property and assign it a type of Types::ParanoidDateTime or Types::ParanoidBoolean:

1
property :deleted_at, ParanoidDateTime

Multiple Data-Store Connections

DataMapper sports a concept called a context which encapsulates the data-store context in which you want operations to occur. For example, when you setup a connection in getting-started, you were defining a context known as :default

1
  DataMapper.setup(:default, 'mysql://localhost/dm_core_test')

If you supply another context name, you will now have 2 database contexts with their own unique loggers, connection pool, identity map….one default context and one named context.

1
DataMapper.setup(:external, 'mysql://someother_host/dm_core_test')

To use one context rather than another, simply wrap your code block inside a repository call. It will return whatever your block of code returns.

1
2
DataMapper.repository(:external) { Person.first }
# hits up your :external database and retrieves the first Person

This will use your connection to the :external data-store and the first Person it finds. Later, when you call .save on that person, it’ll get saved back to the :external data-store; An object is aware of what context it came from and should be saved back to.

NOTE that currently you must setup a :default repository to work with DataMapper (and to be able to use additional differently named repositories). This might change in the future.

Chained Associations

Say you want to find all of the animals in a zoo, but Animal belongs to Exhibit which belongs to Zoo. Other ORMs solve this problem by providing a means to describe the double JOINs into the retrieval call for Animals. ActiveRecord specifically will let you specify JOINs in a hash-of-hashes syntax which will make most developers throw up a little in their mouths.

DataMapper’s solution is to let you chain association calls:

1
2
zoo = Zoo.first
zoo.exhibits.animals  # retrieves all animals for all exhibits for that zoo

This has great potential for browsing collections of content, like browsing all blog posts’ comments by category or tag. At present, chaining beyond 2 associations is still experimental.