DataMapper
From Docunext Technology Wiki
Contents |
DataMapper Summary
A Ruby object relational mapper (ORM).
My Experience with DataMapper
I've only really scratched the surface this morning, but its really awesome!! Thankfully, its simpler than DBIx::Class.
While most of what I'm learning about DataMapper is very impressive, one of the plugins is truly remarkable: dm-rest-adapter. This allows DataMapper to use HTTP as its data store protocol! Incredible, and just happens to be something I've wanted to try out for awhile.
Sample Code
Here's a very rough rendition of the accounts table from PBooks in DataMapper format:
class Account include DataMapper::Resource property :id,Serial property :name,String property :account_type_id,Integer property :description,Text property :creation_datetime,DateTime property :account_number,Integer property :parent_account_id,Integer property :hide,Boolean property :group_id,Integer end
While interesting, I'm really interesting in these features:
- Associations
- Hooks
DataMapper Basics
@myconf = Conf.new(:conf => data) @myconf.save
This is where things get sort of interesting to me, especially in the context of PBooks.
DataMapper Associations
The base object in PBooks is the journal entry. Each journal entry must have at least two journal entry amounts: one debit, and one credit, and journal entry amounts must refer to a specific journal entry.
In MySQL, the PBooks data model enforces this through foreign key constraints. If you try to create a journal entry amount that relates to a non-existent entry, MySQL will throw an error. However, enforcing the higher level logic about journal entries and the journal entry amounts is, as far as I know, too complicated for SQL. PBooks currently enforces the requirement through XSL, and not very well at that as one might imagine.
With DataMapper, it appears that the higher level object constraints are managed with validators. Nexista has many built-in validators, and I never thought to add support for custom validators. That's something that needs to be done!
DataMapper Validators
Concepts like validators bring DataMapper to a new level. It not only maps data and objects together, it really helps developers model objects, too. Is there such a thing as an object modeler?
DataMapper Limits
With Sinatra:
get '/raw/test/entries/:offset' do
content_type 'application/xml', :charset => 'utf-8'
@myentries = Entry.all(:limit => 4, :offset => params[:offset].to_i)
builder :'xml/entries_test'
end
DataMapper Hooks
These are really cool too!
DataMapper Serialization
I just discovered an awesome extension to DataMapper: dm-serializer. It provides the ability to serialize DataMapper objects to XML, JSON, YAML and even CSV.
I had one fumble when trying it out - how to include object associations. Thankfully, its quite possible. To do this, the :methods symbol is used:
get '/raw/jsontest' do
#content_type :json
content_type 'text/plain'
@myentries = Entry.all
@myentries.to_json(:methods => [:credits,:debits])
end
This produces awesome results:
[
{
o id: 82
o memorandum: null
o status: null
o fiscal_period_id: null
o
credits: [
{
# id: 158
# entry_id: 82
# type: "Credit"
# amount: 12323
# account_id: null
# memorandum: null
# currency_id: null
}
]
debits: [
{
# id: 159
# entry_id: 82
# type: "Debit"
# amount: 12323
# account_id: null
# memorandum: null
# currency_id: null
}
]
}
Honestly, this is really amazing. (Output is made readable in Iceweasel thanks to jsonview).
Unfortunately, I was unable to get the XML serializer to work with the associations yet. This code:
get '/raw/xmltest' do
content_type 'application/xml'
@myentries = Entry.all
@myentries.to_xml(:methods => [:credits,:debits])
end
produces:
<entry> <id type="datamapper::types::serial">94</id> <memorandum>sfdasdf</memorandum> <status type="integer"/> <fiscal_period_id type="integer"/> <credits>#<Credit:0xb753831c></credits> <debits>#<Debit:0xb74c9638></debits> </entry>
Yup, the source code of the JSON serializer includes:
# Note: if you want to include a whole other model via relation, use :methods
# comments.to_json(:relationships=>{:user=>{:include=>[:first_name],:methods=>[:age]}})
# add relationships
# TODO: This needs tests and also needs to be ported to #to_xml and #to_yaml