datamapper
DESCRIPTION
Learn all about the state of DataMapper.TRANSCRIPT
datamapperthe persistence framework
who am I?
(Booth 501)
DataMapper
datamapper
object relational mapper
like activerecord
like activerecord★ DB adapters★ migrations★ associations★ one-to-one★ one-to-many★ many-to-one
★ many-to-many★ -through★ polymorphic
★ lifecycle events★ sti★ etc.
drops into Rails1.0
(or merb)0.9
DataMapper
Merb Rails
DO.rb YAML IMAP
Application
Adapters
Data Stores
architecture
some caveats
1.0
work in progress0.9
be prepared for code
why did we build it?
identity map
Foo[1]
Foo[1]
Foo
id = 1
...
foo[1] == foo[1]#=> true
loadedset
LoadedSet
foo.all
Foo
id = 1
...
Foo
id = 2
...
Foo
id = 3
...
Foo
id = 4
...
Foo
id = 5
...
Foo
id = 6
...
composite keys
belongs_to :project, :child_key => [:name, :tag]
legacy data
naming conventions(yours)
naming conventionsunderscored
foo::barBaz => foo/bar_baz
naming conventionsunderscoredandpluralizedfoo::barBaz => foo_bar_bazs
naming conventionsunderscoredandpluralizedwithoutmodule
foo::barBaz => bazs
naming conventionsyaml
foo::Bar => foo/bars.yaml
support for multiple databases(even in the same model)
Post.all(:repository => :legacy)
Post.all # :repository => :default
prepared statements1.0
custom types
embedded values1.0
Employment
id<Integer>
person<Person>
start<Date>
end<Date>
salary<Money>
salary_cur: char
salary_amt: dec
end: date
start: date
person_id: int
id: int
Employments
declared propertiesproperty :id, :key => true
robust queries
Child.all( “mother.last_name.like” => “Jane%”, :name.like => “Jim”)
dirty tracking
modularity
unified interface for drivers
•connections
•commands
•readers
•cursors (forward-only)
•quoting
• transactions
Connection Pool
Connection Connection Connection Connection Connection
“select * from foos where id = ?”
Command
create_command
execute_reader Reader next values next ...
execute_reader(12) => select * from foos where id = 12
works today on:mysql, sqlite, postgres
fast, written in C
simple interface for adapters
•read by key
•read by query
•update by key
•update set
•delete
• (optional: transactions)
salesforce adapter in 200 LOC
Errors
Operators
NamingConventions
~/.salesforce
Build Query
Load Result Set
Similar CRUD
Demo
what does it look like?
Zoo.all( :age.gt => 30, :name.not => [“bob”, “jones”])
SELECT “age”, “name”, “description”FROM “zoos”WHERE (“age” > 30) AND (“name” NOT IN (“bob”, “jones”))
Zoo.first.eql? Zoo.first#=> true
Zoo.all.map {|x| x.animals }how many queries?
2
we call thisstrategic eager loading.
lazy loading
include DataMapper::Resource
property :id, Fixnum, :serial => true property :title, String property :body, Text, :lazy => trueend
class Post
posts = Post.all
SELECT “id”, “title”FROM “posts”
return two objectsids 1 and 2
SELECT “body”FROM “posts”WHERE (“id” IN (1,2))
posts.first.body
SELECT “body”FROM “posts”WHERE (“id” IN (1,2))
posts.map{|x| x.body}
lazy loaded grouping
include DataMapper::Resource
property :id, Fixnum, :serial => true property :title, String, :lazy => [:details] property :body, Text, :lazy => [:details]end
class Post
you can go off the golden path
class Post include DataMapper::Resource property :title, String
repository(:legacy) do property :title, String, :field => “T1tLz” endend
Post.all(:repository => :legacy)
repository(:legacy) do Post.allend
Post.all
Post.all(:repository => :default)
repository(:default) do Post.allend
repository(:legacy).adapter. resource_naming_convention = DM:: NamingConventions:: Underscored
naming conventions
AndPluralized
repository(:legacy).adapter. resource_naming_convention = lambda do |klass| “tbl#{klass.camel_case}” end
naming conventions
default repositoryclass Post include DataMapper::Resource def self.default_repository_name :legacy endend
import data
Post.copy(:legacy, :default)
Post.copy(:legacy, :default, :created_at.gt => Date.today - 365)
1.0
import dataclass Post property :title, String
repository(:legacy) do property :title, String, :field => “TIT13” endend
custom types
primitivesTrueclass, string, text, float, fixnum,
bigdecimal, datetime, date, object, class
custom types
class Post include DataMapper::Resource property :title, String property :author, FullName property :details, Csvend
class FullName < DM::Type primitive String size 100
def self.load(str) str.split(“, ”).reverse end
def self.dump(ary) ary.reverse.join(“, ”) endend
class Csv < DM::Type primitive String size 65355
def self.load(str) FasterCSV.parse(value) end
def self.dump(ary) FasterCSV.generate do |csv| ary.each {|line| csv << line} end endend
Post.create!( :title => “New Post”, :author => [“Yehuda”, “Katz”], :metadata => [ [“Some”, “Sample”, “Data”], [“More”, “Sample”, “Data”] ])
Title
Author
Metadata
“New Post”
“Katz, Yehuda”
“Some,Sample,Data\nMore,Sample,Data\n”
in the database
and it’s lazy-parsed
custom stores
stores are uris
mysql://user@localhost
setting up
DataMapper.setup( :default, “mysql://user@localhost”)
database.yml
development: default: adapter: mysql database: app_dev user: person password: sekrit
database.yml
legacy: adapter: sqlite3 database: config/l3g.db
yaml:///fixtures
1.0
ssh+yaml://fixtures.engineyard.com/
fixtures
1+
database.ymltest: default: adapter: yaml database: app_test.db legacy: adapter: yaml database: l3g_test.db
making fixtures
Post.copy(:default, :fixtures)
rake db:copy_fixtures
validations
class Product include DataMapper::Resource property :title, String property :price, String, :nullable => false, :validation_context => :purchaseend
validates_length_of
validates_presence_of
valid_for_purchase?
class Product include DataMapper::Resource property :title, String property :number, String, :format => /\d{3}-[a-zA-Z]{9}-0/end validates_format_of
class Product include DataMapper::Resource property :title, String property :number, String, :format => proc {|n| n.split(“-”).size == 2}
end
validates_format_of
class Product include DataMapper::Resource property :title, String property :number, String, :length => 2..10, :validation_context => :import
end
validates_length_of
valid_for_import?save(:import)
class Product include DataMapper::Resource property :title, String property :number, String,
end valid_for_import?
validates_length_of :number, :in => (2..10), :when => :import
thank you.
any questions?
</railsconf>