DataMapper @ RubyEnRails2009

Post on 01-Sep-2014

5.848 views 3 download

Tags:

description

An overview of what DataMapper can do and where it will go in the future

Transcript of DataMapper @ RubyEnRails2009

Dirkjan Bussinkhttp://github.com/dbussink

http://twitter.com/dbussink

ORM?

DataMapper 0.10 is ReleasedDataMapper 0.10 is ready for release. We’ve worked on it for the past 11 months, pushed 1250 commits, written 3000+ specs, and fixed 140 tickets in the process.

1.81.9

Windows *

* Sorry, no Windows / 1.9 yet...

JRuby

Dan Kubb

DataMapper 101

class Person include DataMapper::Resource property :id, Serial property :name, String property :age, Integerend

DataMapper.auto_migrate!

DataMapper.auto_upgrade!

class Person

...

property :name, String, :length => 512

...

end

Ruby!

Person.all(:name.like => "%me%")

Identity Map

repository do puts Person.get(1).object_id => 2232427080 puts Person.get(1).object_id => 2232427080end

Pluggable Identity Map

people = Person.all(:name.like => "%me%")~ Query log empty...

p people~ SELECT "id", "name" FROM people

:each

Kicker

Eager loading

people = Person.allpeople.each do |person| p person.addressesend

SELECT "id", "name" FROM people;SELECT "id", "street" FROM addresses WHERE ("person_id" IN (1,2,3))

RDBMS

IMAP

YAML

Multiple repositories

DataMapper.setup(:repo1, "postgres://postgres@localhost/dm")DataMapper.setup(:repo2, "mysql://root@localhost/dm")

repository(:repo1) do p Person.get(1) => #<Person @id=1 @name="Dirkjan" @age=27>end

repository(:repo2) do p Person.get(1) => #<Person @id=1 @name="Dan" @age=35>end

DataMapper.setup(:repo1, "postgres://postgres@localhost/dm")DataMapper.setup(:repo2, "mysql://root@localhost/dm")

get "/" do # Odd / even sharding repo = "repo#{user.id % 2 + 1}".to_sym

repository(repo) do @people = Person.all end

erb :peopleend

Simple sharding

module DataMapper module Adapters

class MyStorageAdapter < AbstractAdapter

def create(resources) end

def read(query) end

def update(attributes, collection) end

def delete(collection) end

end endend

Person.all(:name.like => 'dirk%').update!(:age => 28)

Person.all.destroy!(:age.gte => 100)

Bulk updates

When simple querying is not enough

Complex querying!

Person.all(Person.addresses.street.like => "%street%")

SELECT "people"."id", "people"."name", "people"."age" FROM "people"INNER JOIN "addresses" ON "people"."id" = "addresses"."person_id" WHERE "addresses"."street" LIKE '%street%' GROUP BY "people"."id", "people"."name", "people"."age" ORDER BY "people"."id"

class Person include DataMapper::Resource property :id, Serial property :name, String property :age, Integer

has n, :addresses

def self.named_like_me all(:name.like => "%me%") end

def self.older_than_me all(:age.gt => 27) endend

Person.named_like_me.older_than_me

~ SELECT "id", "name", "age" FROM "people" WHERE ("name" LIKE '%me%' AND "age" > 27) ORDER BY "id"

Query chaining

class Person include DataMapper::Resource property :id, Serial property :name, String property :age, Integer

has n, :addresses

def self.named_like_me all(:name.like => "%me%") end

def self.older_than_me all(:age.gt => 27) endend

Person.named_like_me.older_than_me

~ SELECT "id", "name", "age" FROM "people" WHERE ("name" LIKE '%me%' AND "age" > 27) ORDER BY "id"

Query chaining

Named scopes

in plain Ruby

Subqueries

Person.all(:name.like => '%me%').addresses.all(:street.not => nil)

SELECT "id", "street", "person_id" FROM "addresses" WHERE ( "person_id" IN (SELECT "id" FROM "people" WHERE "name" LIKE '%me%') AND "street" IS NOT NULL ) ORDER BY "id"

Person.all - Person.all(:name => 'Dan Kubb')

SELECT * FROM people WHERE NOT(name = 'Dan Kubb')

[1, 2] & [2, 3] == [2][1, 2] | [2, 3] == [1, 2, 3]

Plugins

Automatic Validations

class Person

...

property :name, String, :nullable => false

end

CREATE TABLE "people" ("id" SERIAL NOT NULL, "name" VARCHAR(50) NOT NULL, "age" INTEGER, PRIMARY KEY("id"))

DataObjects::IntegrityError: ERROR: null value in column "name" violates not-null constraint

Person.create

person = Person.createp person.errors => #<DataMapper::Validate::ValidationErrors:0x101d217a0 @errors={:name=>["Name must not be blank"]}, @resource=#<Person @id=nil @name=nil @age=nil>>

Validations

class Person

...

validates_present :name

end

Timestamps

class Person

...

property :created_at, DateTime property :updated_at, DateTime

end

Adjustments

Person.all.adjust!(:age => 1)

Constraints

class Person

...

has n, :addresses, :constraint => :destroy!

end

ALTER TABLE addressesADD CONSTRAINT addresses_person_id_fkFOREIGN KEY (person_id)REFERENCES peopleON DELETE CASCADEON UPDATE CASCADE

module DataMapper module Types class IPAddress < DataMapper::Type primitive String length 16

def self.load(value, property) IPAddr.new(value) end

def self.dump(value, property) value.to_s end

def self.typecast(value, property) value.kind_of?(IPAddr) ? value : load(value, property) end end endend

class NetworkNode

...

property :ipaddress, IPAddress

end

Custom types

class Address include DataMapper::EmbeddedValue

property :street, Stringend

class Person include DataMapper::Resource

property :id, Serial property :name, String has n, :addressesend

Ambition style queries

User.select { |u| u.id == 1 && u.name == 'Dan Kubb' }

http://github.com/datamapper

http://datamapper.org/

http://groups.google.com/group/datamapper

irc://irc.freenode.net/#datamapper

Questions?