Datamapper Railskonferenz 2009

1,316 views

Published on

Dieser Vortrag wurde auf der Railskonferenz 2009 in Offenbach von Hussein Morsy und Tanja Otto gehalten

  • Be the first to comment

Datamapper Railskonferenz 2009

  1. 1. DataMapper als Alternative zu ActiveRecord ? Rails-Konferenz.de 2009 Hussein Morsy & Tanja Otto 1
  2. 2. SalesLentz::DevTeam 2
  3. 3. Ü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
  4. 4. Wer ist ein Rails Newbie ? 4
  5. 5. Dan Kubb – Ruby Hero 5
  6. 6. Wer setzt DataMapper ein ? 6
  7. 7. Intro 7
  8. 8. ActiveRecord Intro require 'rubygems' require 'active_record' ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => "db/shop.sqlite3") # Tabelle manuell oder per Migration erstellen # CREATE TABLE shoes ( # ... # ); class Shoe < ActiveRecord::Base end Shoe.create(:brand => "Geox", :price => 52.5) puts Shoe.first.brand # => Geox 8
  9. 9. 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
  10. 10. Migrations 10
  11. 11. 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
  12. 12. 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
  13. 13. 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
  14. 14. 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
  15. 15. Extended Types 15
  16. 16. DataMapper Extended Types require 'rubygems' require 'dm-core' # erweiterte Datentypen require 'dm-types' class Shoe include DataMapper::Resource # primitive Datentypen # ... # erweiterte Datentypen property :color, Enum[:red, :green, :blue] property :image_path, FilePath end shoe = Shoe.new shoe.color = :red shoe.image_path = "/images/shoes.jpg" shoe.image_path.class # => Pathname shoe.image_path.basename # => shoes.jpg 16
  17. 17. ActiveRecord Extended Types ? 17
  18. 18. Defaults 18
  19. 19. ActiveRecord Defaults # Neue Tabelle erstellen class CreateShoes < ActiveRecord::Migration def self.up create_table :shoes do |t| t.string :brand t.float :price t.float :tax, :default => 19.0 t.timestamps end end def self.down drop_table :shoes end end rake db:migrate 19
  20. 20. DataMapper Defaults # Dynamische Default-Werte class Shoe include DataMapper::Resource property :id, Serial property :brand, String property :price, Float property :tax, Float, :default => 19.0 end DataMapper.auto_migrate! my_shoe = Shoe.create(:brand => "demo", :price => 99.9) puts my_shoe.tax # => 19.0 20
  21. 21. DataMapper Dynamic Defaults # Dynamische Default-Werte class Shoe include DataMapper::Resource property :id, Serial property :brand, String property :price, Float property :tax, Float, :default => 19 property :sale, Float, :default => Proc.new { |r, p| r.price * 0.5 } end DataMapper.auto_migrate! my_shoe = Shoe.create(:brand => "demo", :price => 99.9) puts my_shoe.sale # => 49.95 21
  22. 22. ActiveRecord Dynamic Defaults ? 22
  23. 23. Validations 23
  24. 24. ActiveRecord Validations # Model ohne Validierung class Woman < ActiveRecord::Base end 24
  25. 25. 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
  26. 26. DataMapper Validations # Model ohne Validierung class Woman include DataMapper::Resource property :id, Serial property :name, String property :title, String property :email, String end 26
  27. 27. 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
  28. 28. Identity Map 28
  29. 29. ActiveRecord Identity Map Shoe.first.eql? Shoe.first # => false 29
  30. 30. DataMapper Identity Map Shoe.first.eql? Shoe.first # => true 30
  31. 31. Lazy Loading 31
  32. 32. DataMapper Lazy Loading class Shoe include DataMapper::Resource property :id, Serial property :brand, String property :price, Float property :short_description, String, :length => 255, :lazy => true property :description, Text # default :lazy => true end # SELECT id, brand, price from shoes where id=1 my_shoe = Shoe.get(1) # SELECT short_description from shoes where id=1 my_shoe.short_description # SELECT description from shoes where id=1 my_shoe.description 32
  33. 33. DataMapper Lazy Loading class Shoe include DataMapper::Resource property :id, Serial property :brand, String property :price, Float property :short_description, String, :length => 255, :lazy => [:details] property :description, Text, :lazy => [:details] end # SELECT id, brand, price from shoes where id=1 my_shoe = Shoe.get(1) # SELECT short_description,description from shoes where id=1 my_shoe.short_description # Keine Datenbankabfrage my_shoe.description 33
  34. 34. ActiveRecord Lazy Loading ? 34
  35. 35. ActiveRecord Lazy Loading class Shoe < ActiveRecord::Base end my_shoe = Shoe.all(:select => "id, brand, price").first puts my_shoe.brand my_shoe.description # => Exception puts Shoe.first(:select => "id,description") 35
  36. 36. Keys 36
  37. 37. ActiveRecord Keys # Primary Keys automatisch in Migration # Immer Integer 37
  38. 38. DataMapper Keys class Woman include DataMapper::Resource property :id, Integer, :serial => true # ... end Woman.get(7) 38
  39. 39. DataMapper Keys # Kurzform class Woman include DataMapper::Resource property :id, Serial # ... end Woman.get(7) 39
  40. 40. DataMapper Keys # Natürlicher Key class Woman include DataMapper::Resource property :steuer_identifikationsnummer, :key => true end Woman.get("i2930d980324") 40
  41. 41. 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
  42. 42. Search 42
  43. 43. 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
  44. 44. 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
  45. 45. 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
  46. 46. Named Scope 46
  47. 47. ActiveRecord Named Scope class Shoe < ActiveRecord::Base named_scope :available, :conditions => ['available = ?', true] named_scope :cheaper, lambda { |max_price| { :conditions => ['price < ?', max_price] } } end Shoe.available.cheaper(20) 47
  48. 48. 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
  49. 49. Associations 49
  50. 50. ActiveRecord Associations class Woman has_many :shoes end class Shoe belongs_to :woman has_many :categories, :through => :categorisations end 50
  51. 51. 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
  52. 52. Timestamps 52
  53. 53. 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
  54. 54. 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
  55. 55. DataMapper Timestamps require 'initdb' require 'dm-timestamps' class Shoe include DataMapper::Resource property :id, Serial property :title, String property :price, Float property :created_at, DateTime property :updated_at, DateTime 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 55
  56. 56. 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
  57. 57. 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
  58. 58. Multi Databases 58
  59. 59. ActiveRecord Multi Databases ? 59
  60. 60. 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
  61. 61. Naming Conventions 61
  62. 62. ActiveRecord Naming Conventions class Shoe < ActiveRecord::Base set_table_name "tbl_Shoe" end class Woman < ActiveRecord::Base set_table_name "tbl_Woman" end 62
  63. 63. 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
  64. 64. 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
  65. 65. Legacy Databases 65
  66. 66. ActiveRecord Legacy Databases ? 66
  67. 67. 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
  68. 68. Adapter 68
  69. 69. ActiveRecord Adapter MySQL PostgreSQL SQLite Oracle 69
  70. 70. DataMapper Adapter MySQL YAML IMAP ... PostgreSQL SQLite Oracle 70
  71. 71. Rails 3 71
  72. 72. Fazit 72
  73. 73. one more thing... 73
  74. 74. 74
  75. 75. Solo Division Winner: Jacques Crocker 75
  76. 76. mit DataMapper gemacht! 76
  77. 77. http://github.com/merbjedi/alertme_tv 77
  78. 78. 78

×