DataMapper als
Alternative zu
ActiveRecord ?
Rails-Konferenz.de 2009
Hussein Morsy & Tanja Otto
1
SalesLentz::DevTeam
2
Über uns
# internes Entwicklerteam von Sales-Lentz
# IBEs für Reisen, Bustickets, Eventtickets
# seit 2006 entwickeln wir mit Ruby on Rails
# Buch Ruby on Rails 2 Galileo Press
# http://railsbuch.de
# http://twitter.com/ajnato
# http://twitter.com/HusseinMorsy
3
DataMapper Intro
require 'rubygems'
require 'dm-core'
DataMapper.setup(:default, "sqlite3://db/shop.sqlite3")
class Shoe
include DataMapper::Resource
property :id, Serial
property :brand, String
property :price, Float
property :available, Boolean
end
Shoe.auto_migrate!
Shoe.create(:brand => "Geox", :price => 52.5)
puts Shoe.first.brand
# => Geox
9
Migrations
10
ActiveRecord Migrations
# Neue Tabelle erstellen
class CreateShoes < ActiveRecord::Migration
def self.up
create_table :shoes do |t|
t.string :brand
t.float :price
t.timestamps
end
end
def self.down
drop_table :shoes
end
end
rake db:migrate
11
ActiveRecord Migrations
# Neues Feld hinzufügen
class AddAvailableToShoes < ActiveRecord::Migration
def self.up
add_column :shoes, :available, :boolean
end
def self.down
remove_column :shoes, :available
end
end
rake db:migrate
12
DataMapper Migrations
# Neue Tabelle erstellen
class Shoe
include DataMapper::Resource
property :id, Serial
property :brand, String
property :price, Float
end
# Tabelle shoes erstellen
# Vorsicht: Daten gehen verloren
Shoe.auto_migrate!
# alle Tabellen erstellen
# Vorsicht: Daten gehen verloren
DataMapper.auto_migrate!
# In Datamapper gibt es auch Migrations, ähnlich wie in ActiveRecord
# siehe dm-migrations
13
DataMapper Migrations
# Neues Feld hinzufügen
class Shoe
include DataMapper::Resource
property :id, Serial
property :brand, String
property :price, Float
property :available, Boolean
end
# Vorsicht: Daten gehen verloren
Shoe.auto_migrate!
14
ActiveRecord Validations
# Model ohne Validierung
class Woman < ActiveRecord::Base
end
24
ActiveRecord Validations
# Model mit Validierung
class Woman < ActiveRecord::Base
validates_presence_of :name
validates_length_of :title, :within => 2..20
validates_format_of :email,
:with => /^([^@s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})$/i
validates_uniqueness_of :email
end
25
DataMapper Validations
# Model ohne Validierung
class Woman
include DataMapper::Resource
property :id, Serial
property :name, String
property :title, String
property :email, String
end
26
DataMapper Validations
# Model mit Validierung
class Woman
include DataMapper::Resource
property :id, Serial
property :name, String, :nullable => false
property :title, String, :length => 2..20
property :email, String, :nullable => false,
:format => :email_address,
:unique => true
end
27
ActiveRecord Keys
# Primary Keys automatisch in Migration
# Immer Integer
37
DataMapper Keys
class Woman
include DataMapper::Resource
property :id, Integer, :serial => true
# ...
end
Woman.get(7)
38
DataMapper Keys
# Kurzform
class Woman
include DataMapper::Resource
property :id, Serial
# ...
end
Woman.get(7)
39
DataMapper Keys
# Natürlicher Key
class Woman
include DataMapper::Resource
property :steuer_identifikationsnummer, :key => true
end
Woman.get("i2930d980324")
40
DataMapper Keys
# Composite Keys
class Woman
include DataMapper::Resource
property :steuer_identifikationsnummer, :key => true
property :pass_nr, :key => true
end
Woman.get("i2930d980324","949823902-X-234s")
41
Search
42
ActiveRecord Search
class Shoe < ActiveRecord::Base
end
# the ActiveRecord-SQL-Style
Shoe.find(:all, :conditions => ["brand=? AND price < ?", "Geox", 130.0])
# brand = Geox
Shoe.all(:conditions => {:brand => "Geox"})
# brand != Geox
Shoe.all(:conditions => ["brand IS NOT ?", "Geox"])
# brand IS NOT NULL
Shoe.all(:conditions => "brand IS NOT NULL")
# brand like "G%"
Shoe.all(:conditions => "brand LIKE G%")
# brand = "Geox" OR brand= "Converse"
Shoe.all(:conditions => {:brand => ["Geox", "Converse"]})
43
DataMapper Search
class Shoe
include DataMapper::Resource
property :id, Serial
property :brand, String
property :price, Float
end
DataMapper.auto_migrate!
# the ActiveRecord-SQL-Style
Shoe.all(:conditions => ["brand=? AND price < ?", "Geox", 130.0])
44
DataMapper Search
# the ActiveRecord-SQL-Style
Shoe.all(:conditions => ["brand=? AND price < ?", "Geox", 130.0])
# the DataMapper-Style
Shoe.all(:brand => "Geox", :price.lt => 130.0)
# brand != Geox
Shoe.all(:brand.not => "Geox")
# brand IS NOT NULL
Shoe.all(:brand.not => nil)
# brand like "G%"
Shoe.all(:brand.like => "G%")
# price <= 60.0
Shoe.all(:price.lte => 60.0)
# brand = "Geox" OR brand= "Converse"
Shoe.all(:brand => ["Geox", "Converse"])
45
DataMapper Named Scope
class Shoe
include DataMapper::Resource
property :id, Serial
property :brand, String
property :price, Float
property :available, Boolean
def self.available
all(:available => true)
end
def self.cheaper(max_price)
all(:price.lt => max_price)
end
end
Shoe.available.cheaper(210.0)
48
Associations
49
ActiveRecord Associations
class Woman
has_many :shoes
end
class Shoe
belongs_to :woman
has_many :categories, :through => :categorisations
end
50
DataMapper Associations
class Woman
include DataMapper::Resource
property :id, Serial
property :name, String
has n, :shoes
end
class Shoe
include DataMapper::Resource
property :id, Serial
property :brand, String
property :price, Float
property :woman_id, Integer
belongs_to :woman
has n,:categories, :through => :categorisations
end
51
Timestamps
52
ActiveRecord Timestamps
# Neue Tabelle erstellen mit einer Migration
class CreateShoes < ActiveRecord::Migration
def self.up
create_table :shoes do |t|
t.string :brand
t.float :price
t.timestamps
end
end
def self.down
drop_table :shoes
end
end
# Model
class Shoe < ActiveRecord::Base
end
my_shoe = Shoe.create(:brand => "demo", :price => 99.9)
puts my_shoe.created_at
puts my_shoe.updated_at
53
DataMapper Timestamps
require 'initdb'
class Shoe
include DataMapper::Resource
property :id, Serial
property :brand, String
property :price, Float
property :created_at, DateTime
property :updated_at, DateTime
end
DataMapper.auto_migrate!
my_shoe = Shoe.create(:brand => "demo", :price => 99.9)
puts my_shoe.created_at
# => nil
54
DataMapper Timestamps
require 'initdb'
require 'dm-timestamps'
class Shoe
include DataMapper::Resource
property :id, Serial
property :title, String
property :price, Float
timestamps :created_at, :updated_at
end
DataMapper.auto_migrate!
my_shoe = Shoe.create(:title => "demo", :price => 99.9)
puts my_shoe.created_at
# => 2009-08-30T04:29:36+00:00
56
DataMapper Timestamps
require 'initdb'
require 'dm-timestamps'
class Shoe
include DataMapper::Resource
property :id, Serial
property :title, String
property :price, Float
timestamps :at
end
DataMapper.auto_migrate!
my_shoe = Shoe.create(:title => "demo", :price => 99.9)
puts my_shoe.created_at
# => 2009-08-30T04:29:36+00:00
57
Multi Databases
58
ActiveRecord Multi Databases
?
59
DataMapper Multi Databases
DataMapper.setup(:default, "sqlite3:///#{Dir.pwd}/db/crm.sqlite3")
DataMapper.setup(:shop, "sqlite3:///#{Dir.pwd}/db/shop.sqlite3")
class Woman
include DataMapper::Resource
property :id, Serial
property :name, String
end
class Shoe
include DataMapper::Resource
def self.default_repository_name
:shop
end
property :id, Serial
property :brand, String
property :price, Float
end
60
Naming Conventions
61
ActiveRecord Naming Conventions
class Shoe < ActiveRecord::Base
set_table_name "tbl_Shoe"
end
class Woman < ActiveRecord::Base
set_table_name "tbl_Woman"
end
62
DataMapper Naming Conventions
class Shoe
include DataMapper::Resource
storage_names[:default] = 'tbl_Shoe'
#....
end
class Woman
include DataMapper::Resource
storage_names[:default] = 'tbl_Woman'
#....
end
63
DataMapper Naming Conventions
repository(:default).adapter.resource_naming_convention =
lambda { |klass| "tbl_#{klass.camel_case}" }
class Shoe
include DataMapper::Resource
# ....
end
class Woman
include DataMapper::Resource
#....
end
64
Legacy Databases
65
ActiveRecord Legacy Databases
?
66
DataMapper Legacy Databases
class Woman
include DataMapper::Resource
storage_names[:default] = 'tbl_woman'
property :id, String, :field => "NR", :key => true
property :firstname, String, :field => "VORNAME"
property :title, String, :field => "ANREDE"
property :email, String, :nullable => false,
:format => :email_address, :unique => true
end
# Im Gegensatz zu ActiveRecord werden nur die angegebenen Felder gelesen
67
Adapter
68
ActiveRecord Adapter
MySQL
PostgreSQL
SQLite
Oracle
69
DataMapper Adapter
MySQL YAML IMAP ...
PostgreSQL
SQLite
Oracle
70
0 comments
Post a comment