Slide 1

Slide 1 text

Beyond the ORM an introduction to DataMapper 2 Sunday, March 11, 12

Slide 2

Slide 2 text

Piotr Solnica • codebenders.com • solnic.eu • github.com/solnic • @_solnic_ Sunday, March 11, 12

Slide 3

Slide 3 text

Before I start talking about DataMapper... Sunday, March 11, 12

Slide 4

Slide 4 text

Active Record ...the pattern Sunday, March 11, 12

Slide 5

Slide 5 text

“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

Slide 6

Slide 6 text

“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

Slide 7

Slide 7 text

“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

Slide 8

Slide 8 text

When should I use Active Record? Sunday, March 11, 12

Slide 9

Slide 9 text

“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

Slide 10

Slide 10 text

“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

Slide 11

Slide 11 text

Data Mapper ...the pattern Sunday, March 11, 12

Slide 12

Slide 12 text

“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

Slide 13

Slide 13 text

“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

Slide 14

Slide 14 text

When should I use Data Mapper? Sunday, March 11, 12

Slide 15

Slide 15 text

“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

Slide 16

Slide 16 text

“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

Slide 17

Slide 17 text

Datamapper 2 Sunday, March 11, 12

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

Work in progress! we’re probably ~50% done Sunday, March 11, 12

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

New core library • The mapper • Query API • Unit of Work • Interfaces for 3rd-party plugins Sunday, March 11, 12

Slide 22

Slide 22 text

veritas Sunday, March 11, 12

Slide 23

Slide 23 text

• 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

Slide 24

Slide 24 text

veritas & github api Sunday, March 11, 12

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

Veritas new_relation = members_relation.restrict do |r| r.login.eq('dkubb').or(r.login.eq('solnic')) end new_relation.each do |member| puts member[:login] end # dkubb # solnic Sunday, March 11, 12

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

joining data from two different datastores? Sunday, March 11, 12

Slide 32

Slide 32 text

Veritas PostgreSQL + Github API Sunday, March 11, 12

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

Veritas adapter = Veritas::Adapter::DataObjects.new( 'postgres://localhost/test' ) users_header = [ [ :id, Integer ], [ :username, String ], [ :country, String ] ] Sunday, March 11, 12

Slide 35

Slide 35 text

Veritas users_relation = Veritas::Relation::Gateway.new( adapter, Veritas::Relation::Base.new('users', users_header) ) Sunday, March 11, 12

Slide 36

Slide 36 text

Veritas # # SELECT "id", "username", “country” # FROM "users" # users_relation.each do |user| puts "#{user[:id]} #{user[:username]}" end Sunday, March 11, 12

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

Virtus Sunday, March 11, 12

Slide 41

Slide 41 text

Virtus • Attributes • Coercions • Embedded Values • Embedded Collections • Value Objects Sunday, March 11, 12

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

Virtus library = Library.new(:books => [ { :title => 'Some Book', :author => 'John Doe' } ]) library.books.class # => Set library.books.first.inspect # => # Sunday, March 11, 12

Slide 45

Slide 45 text

Virtus Virtus::Coercion::Object.to_integer("1") # => 1 Virtus::Coercion::Object.to_string(1) # => "1" Virtus::Coercion::Object.to_array(1) # => [ 1 ] Sunday, March 11, 12

Slide 46

Slide 46 text

Virtus Virtus::Coercion::String.to_date("2012/03/10") # => # Virtus::Coercion::Date.to_string(Date.new(2012,3,10)) # => "2012-03-10" Virtus::Coercion::Integer.to_float(1) # => 1.0 Sunday, March 11, 12

Slide 47

Slide 47 text

missing pieces... Sunday, March 11, 12

Slide 48

Slide 48 text

missing pieces • Mapper • Session / Unit of Work • Database schema reflection • Migrations • Constraints • Veritas Adapters Sunday, March 11, 12

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

Resources • https://github.com/dkubb/veritas • https://github.com/solnic/virtus • https://github.com/emmanuel/aequitas • https://github.com/datamapper/dm-core/ wiki/Roadmap Sunday, March 11, 12

Slide 52

Slide 52 text

thank you! Sunday, March 11, 12

Slide 53

Slide 53 text

questions? Sunday, March 11, 12