Slide 1

Slide 1 text

DataMapper 2 an object mapping toolkit RubyShift Conference, Kiev 2012 Monday, October 1, 12

Slide 2

Slide 2 text

Hi I’m solnic! • Software Consultant • DataMapper Core Team • Blog at solnic.eu • Hack at github.com/solnic • Tweet @_solnic_ Monday, October 1, 12

Slide 3

Slide 3 text

Active Record vs Data Mapper Monday, October 1, 12

Slide 4

Slide 4 text

Active Record • Simple pattern • Designed for simple use cases when business logic is not complex (ie CRUD) • No separation between persistence and domain objects • 1:1 mapping between db schema and domain objects Monday, October 1, 12

Slide 5

Slide 5 text

Data Mapper • Advanced system implementing various patterns (Mapper, Unit of Work, Repository) • Strict separation between persistence and domain logic • Designed for cases where mixing persistence concerns with domain logic becomes a problem Monday, October 1, 12

Slide 6

Slide 6 text

Active Record Data Mapper vs Monday, October 1, 12

Slide 7

Slide 7 text

Active Record Data Mapper vs Monday, October 1, 12

Slide 8

Slide 8 text

Active Record Data Mapper & Monday, October 1, 12

Slide 9

Slide 9 text

What went wrong with ActiveRecord in Rails Monday, October 1, 12

Slide 10

Slide 10 text

class User < ActiveRecord::Base end Active Record in Rails Monday, October 1, 12

Slide 11

Slide 11 text

Active Record in Rails (User.methods.sort - Object.methods.sort).count # => 391 391 new public class methods... Monday, October 1, 12

Slide 12

Slide 12 text

ActiveRecord in Rails Does TOO MUCH Monday, October 1, 12

Slide 13

Slide 13 text

• Persistence • Validations • Life cycle hooks • Typecasting • Mass-assignment security • ...and much much more! ActiveRecord in Rails Monday, October 1, 12

Slide 14

Slide 14 text

DataMapper 2 Monday, October 1, 12

Slide 15

Slide 15 text

DataMapper 2 Repository Mapper & Session Domain Model Monday, October 1, 12

Slide 16

Slide 16 text

Domain Model Monday, October 1, 12

Slide 17

Slide 17 text

Domain Model class User attr_reader :id, :name, :age def initialize(attributes) @id, @name, @age = attributes.values_at(:id, :name, :age) end # business logic goes here :) end Monday, October 1, 12

Slide 18

Slide 18 text

Domain Model require './user' require 'rspec' describe User do subject { User.new(id: 1, name: 'Jane', age: 21) } describe '#name' do its(:name) { should eql('Jane') } end describe '#age' do its(:age) { should eql(21) } end end Monday, October 1, 12

Slide 19

Slide 19 text

Mapper Monday, October 1, 12

Slide 20

Slide 20 text

Mapper class User attr_reader :id, :name, :age def initialize(attributes) @id, @name, @age = attributes.values_at(:id, :name, :age) end # business logic goes here :) end Monday, October 1, 12

Slide 21

Slide 21 text

Mapper class UserMapper < DataMapper::Mapper::Relation::Base model User relation_name :users repository :postgres map :id, Integer, :key => true map :name, String, :to => :username map :age, Integer end Monday, October 1, 12

Slide 22

Slide 22 text

Mapper class User include DataMapper::Model attribute :id, Integer attribute :name, String attribute :age, Integer end DataMapper.generate_mapper_for(User) do key :id map :name, :to => :username end Monday, October 1, 12

Slide 23

Slide 23 text

Mapper DataMapper[User].all DataMapper[User].order(:age) DataMapper[User].one(name: 'Jane') DataMapper[User].find(age: 18) Monday, October 1, 12

Slide 24

Slide 24 text

Mapper class Address include DataMapper::Model attribute :id, Integer attribute :street, String attribute :city, String attribute :zipcode, String end class User include DataMapper::Model attribute :id, Integer attribute :name, String attribute :age, Integer attribute :address, Address end Monday, October 1, 12

Slide 25

Slide 25 text

Mapper class Address include DataMapper::Model attribute :id, Integer attribute :street, String attribute :city, String attribute :zipcode, String end class User include DataMapper::Model attribute :id, Integer attribute :name, String attribute :age, Integer attribute :address, Address end a user has an address Monday, October 1, 12

Slide 26

Slide 26 text

Mapper DataMapper.generate_mapper_for(Address) do key :id end DataMapper.generate_mapper_for(User) do key :id map :name, :to => :username has 1, :address, Address end Monday, October 1, 12

Slide 27

Slide 27 text

Mapper DataMapper.generate_mapper_for(Address) do key :id end DataMapper.generate_mapper_for(User) do key :id map :name, :to => :username has 1, :address, Address end OneToOne Relationship Monday, October 1, 12

Slide 28

Slide 28 text

Mapper user = DataMapper[User].include(:address).first user.address.city user.address.street user.address.zipcode Monday, October 1, 12

Slide 29

Slide 29 text

Mapper class Order include DataMapper::Model attribute :id, Integer attribute :product, String end class User include DataMapper::Model attribute :id, Integer attribute :name, String attribute :age, Integer attribute :orders, Array[Order] end Monday, October 1, 12

Slide 30

Slide 30 text

Mapper class Order include DataMapper::Model attribute :id, Integer attribute :product, String end class User include DataMapper::Model attribute :id, Integer attribute :name, String attribute :age, Integer attribute :orders, Array[Order] end a user has orders Monday, October 1, 12

Slide 31

Slide 31 text

Mapper DataMapper.generate_mapper_for(Order) do key :id end DataMapper.generate_mapper_for(User) do key :id map :name, :to => :username has 0..n, :orders, Order end DataMapper[User].include(:orders).all Monday, October 1, 12

Slide 32

Slide 32 text

Mapper DataMapper.generate_mapper_for(Order) do key :id end DataMapper.generate_mapper_for(User) do key :id map :name, :to => :username has 0..n, :orders, Order end DataMapper[User].include(:orders).all OneToMany Relationship Monday, October 1, 12

Slide 33

Slide 33 text

Session Work In Progress... Monday, October 1, 12

Slide 34

Slide 34 text

Session DataMapper.session do |session| user = User.new(name: 'Piotr', age: 28) session.persisted?(user) # => false session.insert(user) session.commit end Monday, October 1, 12

Slide 35

Slide 35 text

Session DataMapper.session do |session| user = User.new(name: 'Piotr', age: 28) session.persisted?(user) # => false session.insert(user) session.commit end is the user persisted? Monday, October 1, 12

Slide 36

Slide 36 text

Session DataMapper.session do |session| user = User.new(name: 'Piotr', age: 28) session.persisted?(user) # => false session.insert(user) session.commit end queue the user to be persisted Monday, October 1, 12

Slide 37

Slide 37 text

Session DataMapper.session do |session| user = User.new(name: 'Piotr', age: 28) session.persisted?(user) # => false session.insert(user) session.commit end write changes to the database Monday, October 1, 12

Slide 38

Slide 38 text

Session DataMapper.session do |session| user = session[User].get(1) user.age = 29 session.dirty?(user) # => true session.update(user) session.commit end Monday, October 1, 12

Slide 39

Slide 39 text

Session DataMapper.session do |session| user = session[User].get(1) user.age = 29 session.dirty?(user) # => true session.update(user) session.commit end fetch the user and track it Monday, October 1, 12

Slide 40

Slide 40 text

Session DataMapper.session do |session| user = session[User].get(1) user.age = 29 session.dirty?(user) # => true session.update(user) session.commit end was the user changed? Monday, October 1, 12

Slide 41

Slide 41 text

Session DataMapper.session do |session| user = session[User].get(1) user.age = 29 session.dirty?(user) # => true session.update(user) session.commit end update the user in the database Monday, October 1, 12

Slide 42

Slide 42 text

Session DataMapper.session do |session| user = User.new(name: 'Piotr', age: 28) address = Address.new( street: 'Street 1', city: 'Krakow', zipcode: '12345' ) user.address = address session.insert(user) session.commit end Monday, October 1, 12

Slide 43

Slide 43 text

Session DataMapper.session do |session| user = User.new(name: 'Piotr', age: 28) address = Address.new( street: 'Street 1', city: 'Krakow', zipcode: '12345' ) user.address = address session.insert(user) session.commit end assign the address to the user Monday, October 1, 12

Slide 44

Slide 44 text

Session DataMapper.session do |session| user = User.new(name: 'Piotr', age: 28) address = Address.new( street: 'Street 1', city: 'Krakow', zipcode: '12345' ) user.address = address session.insert(user) session.commit end queue the user to be persisted Monday, October 1, 12

Slide 45

Slide 45 text

Session DataMapper.session do |session| user = User.new(name: 'Piotr', age: 28) address = Address.new( street: 'Street 1', city: 'Krakow', zipcode: '12345' ) user.address = address session.insert(user) session.commit end write both objects to the database Monday, October 1, 12

Slide 46

Slide 46 text

Repository Monday, October 1, 12

Slide 47

Slide 47 text

Veritas Powerful Relational Algebra Library Monday, October 1, 12

Slide 48

Slide 48 text

Veritas • Created by Dan Kubb in 2010 • Complete support for all relational algebra operations • All operations can be run in-memory • Can be extended to support any kind of a datastore • Can be used with multiple different databases Monday, October 1, 12

Slide 49

Slide 49 text

Repository class User include DataMapper::Model attribute :id, Integer attribute :name, String attribute :age, Integer end DataMapper.generate_mapper_for(User) do key :id map :name, :to => :username end Monday, October 1, 12

Slide 50

Slide 50 text

Repository mapper = DataMapper[User] mapper.relation.class # => Veritas::Relation::Gateway mapper.relation.header.to_set.map { |a| [ a.name, a.type ] } # => [ # [ :id, Veritas::Attribute::Integer ], # [ :username, Veritas::Attribute::String ], # [ :age, Veritas::Attribute::Integer ] # ] mappers wrap veritas gateway relations Monday, October 1, 12

Slide 51

Slide 51 text

Repository mapper = DataMapper[User] relation = Veritas::Relation.new( mapper.relation.header, [ [ 1, 'John', 18 ], [ 2, 'Jane', 21 ] ] ) relation.restrict { |r| r.age.gt(18) }.each do |tuple| puts tuple[:username] # => 'Jane' end building an in-memory relation Monday, October 1, 12

Slide 52

Slide 52 text

Repository user_mapper = DataMapper[User] address_mapper = DataMapper[Address] user_relation = Veritas::Relation.new( user_mapper.relation.header, [ [ 1, 'John', 18 ], [ 2, 'Jane', 21 ] ]) address_relation = Veritas::Relation.new( address_mapper.relation.header, [ [ 1, 1, 'Street 1', '12345' ], [ 2, 2, 'Street 2', '54321' ] ]) joining two relations in-memory Monday, October 1, 12

Slide 53

Slide 53 text

Repository user_address = user_relation.rename(:id => :user_id). join(address_relation) user_address.restrict { |r| r.street.eq('Street 1') }. each do |tuple| puts tuple[:username] # => "John" end joining two relations in-memory Monday, October 1, 12

Slide 54

Slide 54 text

Projects on Github • github.com/dkubb/veritas • github.com/solnic/virtus • github.com/solnic/dm-mapper • github.com/solnic/dm-session Monday, October 1, 12

Slide 55

Slide 55 text

Thank You! Time for questions? Monday, October 1, 12