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

wroc_love.rb-beyond-the-orm-an-introduction-to-...

 wroc_love.rb-beyond-the-orm-an-introduction-to-datamapper-2.pdf

An introduction to the core elements that will be part of the DataMapper 2 project - an implementation of the Data Mapper pattern in Ruby language.

Piotr Solnica

March 11, 2012
Tweet

More Decks by Piotr Solnica

Other Decks in Programming

Transcript

  1. “An object that wraps a row in a database table

    or view, encapsulates the database access, and adds domain logic on that data.” Martin Fowler Sunday, March 11, 12
  2. “An object that wraps a row in a database table

    or view, encapsulates the database access, and adds domain logic on that data.” Martin Fowler Sunday, March 11, 12
  3. “An object that wraps a row in a database table

    or view, encapsulates the database access, and adds domain logic on that data.” Martin Fowler Sunday, March 11, 12
  4. “Active Record is a good choice for domain logic that

    isn't too complex, such as creates, reads, updates and deletes” Martin Fowler Sunday, March 11, 12
  5. “Active Record is a good choice for domain logic that

    isn't too complex, such as creates, reads, updates and deletes” Martin Fowler Sunday, March 11, 12
  6. “A layer of Mappers that moves data between objects and

    a database while keeping them independent of each other and the mapper itself.” Martin Fowler Sunday, March 11, 12
  7. “A layer of Mappers that moves data between objects and

    a database while keeping them independent of each other and the mapper itself.” Martin Fowler Sunday, March 11, 12
  8. “The primary occasion for using Data Mapper is when you

    want the database schema and the object model to evolve independently” Martin Fowler Sunday, March 11, 12
  9. “The primary occasion for using Data Mapper is when you

    want the database schema and the object model to evolve independently” Martin Fowler Sunday, March 11, 12
  10. DataMapper 2 • Data Mapper pattern implementation • Based on

    a general-purpose relational algebra engine • Support different data stores via adapters • A set of independent libraries Sunday, March 11, 12
  11. New Libraries • “Veritas” - the new relational algebra engine

    • “Virtus” - model definition and introspection • “Aequitas” - validations Latin names are actually code-names :) Sunday, March 11, 12
  12. New core library • The mapper • Query API •

    Unit of Work • Interfaces for 3rd-party plugins Sunday, March 11, 12
  13. • 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 • Designed to support per database optimizations Veritas Sunday, March 11, 12
  14. Veritas github = Faraday.new(:url => 'https://api.github.com') members_body = github.get( '/orgs/datamapper/members').body

    commits_body = github.get( '/repos/datamapper/dm-core/commits').body members_json = JSON.parse(members_body) commits_json = JSON.parse(commits_body) Sunday, March 11, 12
  15. Veritas member_header = [ [ :id, Integer ], [ :login,

    String ], [ :gravatar_id, String ], [ :avatar_url, String ] ] commit_header = [ [ :committer, String ], [ :sha, String ], [ :message, String ], [ :date, DateTime ] ] Sunday, March 11, 12
  16. Veritas members_tuple = members_json.map do |member| [ member['id'], member['login'], member['gravatar_id'],

    member['avatar_url'] ] end commits_tuple = commits_json.map do |commit| [ commit['committer']['login'], commit['commit']['tree']['sha'], commit['commit']['message'], commit['committer']['date'] ] end Sunday, March 11, 12
  17. Veritas members_relation = Veritas::Relation::Base.new( 'members', member_header, members_tuple) commits_relation = Veritas::Relation::Base.new(

    'commits', commit_header, commits_tuple) members_relation.each do |member| puts member[:login] end commits_relation.each do |commit| puts commit[:sha] end Sunday, March 11, 12
  18. Veritas new_relation = members_relation.join(commits_relation) do |r| r.login.eq(r.committer) end new_relation.each do

    |member| puts "#{member[:id]} #{member[:login]} #{member[:sha]}" end # 133 dkubb 039c80242eaf4e7adc3775c2739813d1b20b2c3c # 7657 emmanuel e145b4fa7d5e44d983c7e2e990a9a6e4a4f912d3 # 2833 namelessjon becb114f6800e07e2d1941f6751501f43d2174ae # 1066 solnic b951892d442149a74f14480f9a6478a350585e85 # etc... Sunday, March 11, 12
  19. Veritas CREATE TABLE "users" ( "id" SERIAL NOT NULL PRIMARY

    KEY, "username" VARCHAR(50) NOT NULL, "country" VARCHAR(2) NOT NULL ) INSERT INTO "users" ("id", "username", "country") VALUES (1, 'dkubb', 'CA') INSERT INTO "users" ("id", "username", "country") VALUES (2, 'solnic', 'PL') INSERT INTO "users" ("id", "username", "country") VALUES (3, 'john', 'UK') INSERT INTO "users" ("id", "username", "country") VALUES (4, 'jane', 'AU') Sunday, March 11, 12
  20. Veritas adapter = Veritas::Adapter::DataObjects.new( 'postgres://localhost/test' ) users_header = [ [

    :id, Integer ], [ :username, String ], [ :country, String ] ] Sunday, March 11, 12
  21. Veritas # # SELECT "id", "username", “country” # FROM "users"

    # users_relation.each do |user| puts "#{user[:id]} #{user[:username]}" end Sunday, March 11, 12
  22. Veritas new_relation = users_relation.join( members_relation.rename( :login => :username ).remove([ :id

    ]) ) new_relation.each do |user| puts "#{user[:id]} #{user[:username]}" end # 1 dkubb # 2 solnic Sunday, March 11, 12
  23. Veritas restricted = users_relation.restrict do |r| r.country.eq('CA').or(r.country.eq('UK')) end new_relation =

    restricted.join( members_relation.rename( :login => :username).remove([ :id ] ) ) Sunday, March 11, 12
  24. Veritas # First in PostgreSQL: # # SELECT "id", "username",

    "country" FROM "users" # WHERE ("country" = 'US' OR "country" = 'UK') # # *Then in memory* # # join with the members from github on username/login # new_relation.each do |user| puts "#{user[:id]} #{user[:username]} #{user[:country]}" end # 1 dkubb CA Sunday, March 11, 12
  25. Virtus • Attributes • Coercions • Embedded Values • Embedded

    Collections • Value Objects Sunday, March 11, 12
  26. Virtus class User include Virtus attribute :name, String attribute :age,

    Integer end user = User.new(:name => 'John', :age => '29') user.name # => "John" user.age # => 29 Sunday, March 11, 12
  27. Virtus class Book include Virtus attribute :title, String attribute :author,

    String end class Library include Virtus attribute :books, Set[Book] end Sunday, March 11, 12
  28. Virtus library = Library.new(:books => [ { :title => 'Some

    Book', :author => 'John Doe' } ]) library.books.class # => Set library.books.first.inspect # => #<Book:0x0000010884bbc0 @title="Some Book", @author="John Doe"> Sunday, March 11, 12
  29. missing pieces • Mapper • Session / Unit of Work

    • Database schema reflection • Migrations • Constraints • Veritas Adapters Sunday, March 11, 12
  30. missing pieces - mapper https://github.com/solnic/dm-mapper class User attr_reader :id, :name

    def initialize(attributes) @id, @name = attributes.values_at(:id, :name) end class Mapper < DataMapper::VeritasMapper map :id, :type => Integer map :name, :to => :username, :type => String model User name 'users' end end User::Mapper.find(:name => 'John') Sunday, March 11, 12
  31. missing pieces - session https://github.com/solnic/dm-session library = Library.new(:books => [])

    book = Book.new(:title => 'Some Book', :author => 'Jane Doe') library.books << book DataMapper::Session.create do |session| session.insert(library) session.commit end Sunday, March 11, 12