Upgrade to Pro — share decks privately, control downloads, hide ads and more …

DataMapper 2 - an object mapping toolkit

Piotr Solnica
September 29, 2012

DataMapper 2 - an object mapping toolkit

An introduction to the DataMapper 2 stack.

Piotr Solnica

September 29, 2012
Tweet

More Decks by Piotr Solnica

Other Decks in Programming

Transcript

  1. Hi I’m solnic! • Software Consultant • DataMapper Core Team

    • Blog at solnic.eu • Hack at github.com/solnic • Tweet @_solnic_ Monday, October 1, 12
  2. 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
  3. 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
  4. Active Record in Rails (User.methods.sort - Object.methods.sort).count # => 391

    391 new public class methods... Monday, October 1, 12
  5. • Persistence • Validations • Life cycle hooks • Typecasting

    • Mass-assignment security • ...and much much more! ActiveRecord in Rails Monday, October 1, 12
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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
  33. 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
  34. 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
  35. 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
  36. 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