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

Why You Should Never Use an ORM

Why You Should Never Use an ORM

My presentation at RailsConf 2011 on thinking about your interface, your data and your code.

John Nunemaker
PRO

May 17, 2011
Tweet

More Decks by John Nunemaker

Other Decks in Programming

Transcript

  1. Ordered List
    @jnunemaker
    RailsConf Baltimore, MD
    May 17, 2011
    Why You Should Never
    Use an ORM

    View Slide

  2. You crazy man...

    View Slide

  3. View Slide


  4. Albert Einstein
    Any intelligent fool can
    make things bigger, more
    complex, and more
    violent.

    View Slide

  5. Violence
    My Path Of

    View Slide

  6. @gem

    View Slide

  7. HTTParty

    View Slide

  8. HappyMapper

    View Slide

  9. MongoMapper

    View Slide

  10. ToyStore

    View Slide

  11. View Slide

  12. View Slide

  13. Three Points

    View Slide

  14. 1) Interface
    Think About Your

    View Slide

  15. 1) Interface
    Think About Your
    2) Data

    View Slide

  16. 1) Interface
    Think About Your
    2) Data
    3) Code

    View Slide

  17. Your Interface
    Think About

    View Slide


  18. John Nunemaker
    ORMs too often lead to
    interface laziness.

    View Slide

  19. site.memberships.create({
    :user_id => user.id,
    :owner => true,
    })

    View Slide

  20. site.add_owner(user)

    View Slide

  21. membership.update_attributes({
    :state => 1,
    })

    View Slide

  22. membership.update_attributes({
    :state => 'maximized',
    })

    View Slide

  23. user.maximize(site)

    View Slide

  24. content.to_json({
    'a crap ton': 'of options'
    })

    View Slide

  25. ContentPresenter.new(site, date, {
    :page => params['page'],
    }).to_json

    View Slide

  26. class ContentPresenter
    include BasePresenter
    def initialize(site, date, options={})
    @site, @date = site, date
    @options = options
    end
    def page # ...
    def per_page # ...
    def total # ...
    def path # ...
    def prev_path # ...
    def next_path # ...
    def next_page_path # ...
    def prev_page_path # ...
    def paginated # ...
    def content # ...

    View Slide

  27. def path # ...
    def prev_path # ...
    def next_path # ...
    def next_page_path # ...
    def prev_page_path # ...
    def paginated # ...
    def content # ...
    def as_json(options = nil)
    {
    'date' => @date.to_s,
    'prev_path' => prev_path,
    'next_path' => next_path,
    'content' => content,
    'page' => page,
    'per_page' => per_page,
    'total' => total,
    'prev_page_path' => prev_page_path,
    'next_page_path' => next_page_path,
    }
    end
    end

    View Slide

  28. Content.paginate({
    :conditions => {
    :site_id => site.id,
    :date => date,
    },
    :page => params['page'],
    :per_page => 15,
    })

    View Slide

  29. site.content_for_date(date, {
    :page => params['page'],
    })

    View Slide

  30. Your Interface
    Think About

    View Slide

  31. Your Data
    Think About

    View Slide


  32. John Nunemaker
    ORMs sometimes hide
    creative ways you can
    store and retrieve
    your data.

    View Slide

  33. Failure and Triumph
    A Story of

    View Slide

  34. {
    '_id' => 'site_id:2011-03-28',
    '/' => {'v' => 200, 't' => 'Home'},
    '/about/' => {'v' => 50, 't' => 'About'},
    '/foo/' => {'v' => 23, 't' => 'Foo!'},
    }

    View Slide

  35. You say heresy! I say use case...

    View Slide

  36. View Slide

  37. Content.get("#{site.id}:2011-03-28")

    View Slide

  38. View Slide

  39. write data
    ensure_index(site_id, date, path)

    View Slide

  40. read data
    ensure_index(site_id, date, views)

    View Slide

  41. {
    '_id' => BSON::ObjectId.new,
    'sid' => site_id,
    'p' => '/about/',
    'd' => '2011-03-28',
    'v' => 50,
    }

    View Slide

  42. NewContent.for_site_and_date(site, date)

    View Slide

  43. db['c.2011.5']

    View Slide

  44. db['c.2011.5']
    read index
    write index

    View Slide

  45. db['c.2011.4'] => db['c.2011.5']
    read index
    write index

    View Slide

  46. View Slide

  47. /about/

    View Slide

  48. /this/is/my/really/long/
    and/descriptive/path/and/
    of/course/we/need/to/
    have/a/query/string?
    gibberish=true&yunolikesh
    orturls=false#DontForgetT
    oHashTagIt

    View Slide

  49. View Slide

  50. Zlib.crc32('...really long path...')
    762151429

    View Slide

  51. Counts
    Every bit

    View Slide

  52. Integers

    View Slide

  53. Site.create(:state => 'enabled')

    View Slide

  54. class Site
    States = {
    'enabled' => 1,
    'disabled' => 2,
    }
    end
    Site.create({
    :state => Site::States['enabled'],
    })

    View Slide

  55. class Site
    def enabled?
    state == States['enabled']
    end
    def disabled?
    !enabled?
    end
    end

    View Slide

  56. Compression

    View Slide

  57. require 'rsmaz'
    compressed = RSmaz.compress(str)
    decompressed = RSmaz.decompress(compressed)

    View Slide

  58. require 'zlib'
    compressed = Zlib::Deflate.deflate(str)
    decompressed = Zlib::Inflate.inflate(compressed)

    View Slide

  59. require 'msgpack'
    ids = [1,2,3,4,5,6,7,8,9,10]
    compressed = MessagePack.pack(ids)
    decompressed = MessagePack.unpack(compressed)

    View Slide

  60. class Stylesheet
    class RSmazCompressor
    def self.compress(str)
    RSmaz.compress(str)
    end
    def self.decompress(str)
    RSmaz.decompress(str)
    end
    end
    class ZlibCompressor
    def self.compress(str)
    Zlib::Deflate.deflate(str)
    end
    def self.decompress(str)
    Zlib::Deflate.inflate(str)
    end
    end

    View Slide

  61. Compressors = {
    1 => RSmazCompressor,
    2 => ZlibCompressor,
    }
    key :compressor_id, Integer
    key :contents, String
    validates_inclusion_of :compressor_id,
    :within => Compressors.keys
    def contents
    value = read_attribute(:contents)
    compressor.decompress(value)
    end
    def compressor
    Compressors[compressor_id]
    end
    end

    View Slide

  62. Partial Updates

    View Slide

  63. site.atomic_update_attributes(attrs)

    View Slide

  64. Unused Fields

    View Slide

  65. Counts
    Where

    View Slide

  66. Memory/Disk/Network

    View Slide

  67. class SiteMode
    include Scam
    attr_accessor :name
    def password_required?
    id == 2
    end
    def item_cache?
    id == 1
    end
    end

    View Slide

  68. SiteMode.create({
    :id => 1,
    :name => 'live'
    })
    SiteMode.all
    # find by id or string id
    pp SiteMode.find(2)
    pp SiteMode.find('2')

    View Slide

  69. class Plan
    include Toy::Store
    store(:memory, {})
    attribute(:title, String)
    attribute(:price, Integer)
    end

    View Slide

  70. class Asset
    plugin Joint
    attachment :file
    def page_cache(version=nil)
    page_cache_original
    page_cache_version(version)
    end
    end

    View Slide

  71. current_user.sites.map do |site|
    Site.get(site['id'])
    end

    View Slide

  72. ids = current_user.sites.map do |site|
    site['id']
    end
    Site.all(:_id.in => ids)

    View Slide

  73. Go A Long Way
    A Little Ruby Can

    View Slide

  74. Move

    View Slide

  75. Move
    BigintMove

    View Slide

  76. Move
    BigintMove
    MakeYouWannaMove

    View Slide

  77. Move
    BigintMove
    MakeYouWannaMove
    DaMove

    View Slide

  78. Move
    BigintMove
    MakeYouWannaMove
    DaMove
    SmoothMove

    View Slide

  79. Move
    BigintMove
    MakeYouWannaMove
    DaMove
    SmoothMove
    NightMove

    View Slide

  80. Move
    BigintMove
    MakeYouWannaMove
    DaMove
    SmoothMove
    NightMove
    DanceMove

    View Slide

  81. Partition.create({
    :association => :moves,
    :model => Move,
    :first_id => 1,
    :writer => false,
    })

    View Slide

  82. Partition.for_writes.model.create(...)

    View Slide

  83. Partition.since(since_id, last_move_id).map do |p|
    send(p.association).since(since_id)
    end

    View Slide

  84. Your Data
    Think About

    View Slide

  85. Your Code
    Think About

    View Slide


  86. No code is faster
    than no code.

    View Slide

  87. $ bx ruby performance/reads.rb
    Collection#find_one
    0.270231008529663
    Site.first
    0.69925594329834

    View Slide

  88. /track - no
    /content - no
    /referrers - no
    /sites - yes
    /users - yes

    View Slide

  89. class Hit
    def site
    @site ||= begin
    query = {'_id' => site_id}
    options = {:fields => ['tz']}
    collection.find_one(query, options)
    end
    end
    end

    View Slide

  90. write code
    Don’t be afraid to

    View Slide

  91. read code
    Don’t be afraid to

    View Slide

  92. fail
    Don’t be afraid to

    View Slide

  93. Site.find(id)
    Site.create({
    :title => 'RailsTips',
    })
    site.update_attributes(attrs)
    site.to_json

    View Slide

  94. Your Code
    Think About

    View Slide


  95. Albert Einstein
    It takes a touch of genius
    —and a lot of courage—to
    move in the opposite
    direction.

    View Slide

  96. Ordered List
    Thank you!
    [email protected]
    John Nunemaker
    RailsConf Baltimore, MD
    May 17, 2011
    @jnunemaker

    View Slide