$30 off During Our Annual Pro Sale. View Details »

Beyond the ORM - RuLu Conf 2012

Piotr Solnica
June 24, 2012
1.2k

Beyond the ORM - RuLu Conf 2012

Piotr Solnica

June 24, 2012
Tweet

Transcript

  1. Beyond the ORM
    an introduction to DataMapper 2
    RuLu 2012
    Sunday, June 24, 12

    View Slide

  2. Piotr Solnica
    • codebenders.com
    • solnic.eu
    • github.com/solnic
    • @_solnic_
    Sunday, June 24, 12

    View Slide

  3. 4 Facts about This Talk
    • It has 2 sections: “ActiveRecord
    Retrospective” and “DataMapper 2”
    • It has 2 obligatory pictures of a cat
    • It has only 1 quote from Martin
    Fowler’s PoEAA book
    • It has only 16 code examples
    Sunday, June 24, 12

    View Slide

  4. ActiveRecord
    Retrospective
    Sunday, June 24, 12

    View Slide

  5. ActiveRecord
    Pattern Facts
    Sunday, June 24, 12

    View Slide

  6. Active Record Pattern Facts
    • Simple ORM Pattern
    • Designed for simple usecases where the
    business domain is simple. ie. basic
    CRUD
    • Mixes together data & behavior
    • 1:1 mapping between database schema &
    objects (means tight coupling with the
    db)
    Sunday, June 24, 12

    View Slide

  7. ActiveRecord
    in Rails
    Sunday, June 24, 12

    View Slide

  8. class User < ActiveRecord::Base
    end
    Active Record in Rails
    Sunday, June 24, 12

    View Slide

  9. Active Record in Rails
    (User.methods.sort - Object.methods.sort).count
    # => 391
    391
    new public class methods...
    Sunday, June 24, 12

    View Slide

  10. WHAT??!! I’m perfectly fine!!!
    Sunday, June 24, 12

    View Slide

  11. • Persistence
    • Validations
    • Associations
    • Life cycle hooks
    • Typecasting
    • Mass-assignment security
    • ...and much much more!
    Active Record
    retrospective
    Sunday, June 24, 12

    View Slide

  12. Active Record
    retrospective
    • Slow test suites
    • Lack of real unit tests
    • God objects
    • Code hard to reuse and extend
    • Code hard to refactor
    Sunday, June 24, 12

    View Slide

  13. I don’t think I can take it Anymore!
    Sunday, June 24, 12

    View Slide

  14. I don’t care if
    we can create a
    blog in 15
    minutes
    Sunday, June 24, 12

    View Slide

  15. but I do care if
    we can work on a
    project for 15
    months and keep
    it in a good
    shape
    Sunday, June 24, 12

    View Slide

  16. DataMapper 2
    Sunday, June 24, 12

    View Slide

  17. DataMapper 2
    “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, June 24, 12

    View Slide

  18. DataMapper 2
    Repository
    Mapper & Session
    Domain Model
    Validation Serialization Extensions
    Sunday, June 24, 12

    View Slide

  19. Domain Model
    Sunday, June 24, 12

    View Slide

  20. Domain Model
    class City
    attr_reader :name, :lat, :lng
    def initialize(attributes)
    @name, @lat, @lng = attributes.values_at(
    :name, :lat, :lng
    )
    end
    end
    Sunday, June 24, 12

    View Slide

  21. Domain Model
    class CityMapper < DataMapper::Mapper
    map :name, :lat, :lng
    end
    Sunday, June 24, 12

    View Slide

  22. Domain Model
    city = City.new(
    name: 'Kraków',
    lat: 123456.789,
    lng: 987654.321
    )
    Sunday, June 24, 12

    View Slide

  23. Domain Model
    class GeoLocation
    include Virtus::ValueObject
    attribute :lat, Float
    attribute :lng, Float
    end
    class City
    include Virtus
    attribute :name, String
    attribute :location, GeoLocation
    end
    Sunday, June 24, 12

    View Slide

  24. Domain Model
    class CityMapper < DataMapper::Mapper
    map :name
    map :location, to: [ :lat, :lng ]
    end
    Sunday, June 24, 12

    View Slide

  25. Session
    Sunday, June 24, 12

    View Slide

  26. Session
    class Person
    include Virtus
    attribute :name, String
    attribute :city, City
    end
    Sunday, June 24, 12

    View Slide

  27. Session
    class PersonMapper < DataMapper::Mapper
    map :name
    has 1, :city
    end
    Sunday, June 24, 12

    View Slide

  28. Session
    city = City.new(
    name: 'Kraków',
    location: {
    lat: 123456789.123,
    lng: 987654321.987
    }
    )
    person = Person.new(name: 'Piotr')
    Sunday, June 24, 12

    View Slide

  29. Session
    DataMapper.session do |session|
    session.track(city)
    session.track(person)
    person.city = city
    session.insert(person)
    session.commit
    end
    Sunday, June 24, 12

    View Slide

  30. Repository
    Sunday, June 24, 12

    View Slide

  31. Repository
    Repository
    Relation
    Optimizer
    Datastore Adapter
    Sunday, June 24, 12

    View Slide

  32. Repository
    • 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
    Sunday, June 24, 12

    View Slide

  33. Repository
    relation = Veritas::Relation.new([
    [ :id, Integer ], [ :name, String ], [ :color, String ] ],
    [
    [ 1, 'Nut', 'Red' ],
    [ 2, 'Bolt', 'Green' ],
    [ 3, 'Screw', 'Blue' ],
    [ 4, 'Screw', 'Red' ],
    [ 5, 'Cam', 'Blue' ],
    [ 6, 'Cog', 'Red' ],
    ]
    )
    Sunday, June 24, 12

    View Slide

  34. Repository
    # projection
    new_relation = relation.project([ :id ])
    # removal
    new_relation = relation.remove([ :name ])
    # rename
    new_relation = relation.rename(id: :other_id,
    name: :other_name)
    # restriction
    new_relation = relation.restrict { |r|
    r.color.eq('Red').or(r.color.eq('Blue'))
    }
    # natural join
    new_relation = relation.join(other)
    Sunday, June 24, 12

    View Slide

  35. Repository
    include Veritas
    adapter = Adapter::DataObjects.new(
    "postgres://localhost/test")
    header = [ [ :id, Integer ], [ :name,
    String ], [ :color, String ] ]
    relation = Relation::Base.new(
    'items', header)
    gateway = Relation::Gateway.new(
    adapter, relation)
    Sunday, June 24, 12

    View Slide

  36. Repository
    new_relation = gateway.restrict { |r|
    r.color.eq('Red').or(r.color.eq('Blue'))
    }
    new_relation.to_a
    # SELECT "id", "name", "color"
    # FROM items
    # WHERE (
    # "color" = 'Red' OR "color" = 'Blue'
    # )
    Sunday, June 24, 12

    View Slide

  37. What’s Done
    Sunday, June 24, 12

    View Slide

  38. What’s Done
    • Veritas
    • Veritas DataObject Adapter for PostgreSQL
    • Veritas Optimizer
    • Virtus (Domain Model features)
    Sunday, June 24, 12

    View Slide

  39. Work in Progress
    Sunday, June 24, 12

    View Slide

  40. Work in Progress
    • “write” support for Veritas
    • Mapper
    • Session/Unit of Work
    Sunday, June 24, 12

    View Slide

  41. Work in Progress
    • Validations
    • Migrations
    • Constraints
    • Extension Interfaces
    • More Veritas adapters (sqlite, MySQL,
    MongoDB, Riak etc.)
    Sunday, June 24, 12

    View Slide

  42. Development
    Process
    Sunday, June 24, 12

    View Slide

  43. Development Process
    • All “levels” of API are treated with the
    same amount of care & love
    • Using metric tools to verify code quality
    • Mutation testing with Heckle
    • 100% YARD documentation coverage
    Sunday, June 24, 12

    View Slide

  44. Virtus Project
    on CodeClimate
    Sunday, June 24, 12

    View Slide

  45. Resources
    • https://github.com/datamapper/dm-
    core/wiki/Roadmap
    • https://github.com/dkubb/veritas
    • https://github.com/dkubb/veritas-do-
    adapter
    • https://github.com/solnic/virtus
    • https://github.com/solnic/dm-mapper
    Sunday, June 24, 12

    View Slide

  46. THANKS!
    Sunday, June 24, 12

    View Slide