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.

E13c31390e0369fcd5972292ce0e7b92?s=128

John Nunemaker
PRO

May 17, 2011
Tweet

Transcript

  1. Ordered List @jnunemaker RailsConf Baltimore, MD May 17, 2011 Why

    You Should Never Use an ORM
  2. You crazy man...

  3. None
  4. “ Albert Einstein Any intelligent fool can make things bigger,

    more complex, and more violent.
  5. Violence My Path Of

  6. @gem

  7. HTTParty

  8. HappyMapper

  9. MongoMapper

  10. ToyStore

  11. None
  12. None
  13. Three Points

  14. 1) Interface Think About Your

  15. 1) Interface Think About Your 2) Data

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

  17. Your Interface Think About

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

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

  20. site.add_owner(user)

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

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

  23. user.maximize(site)

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

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

  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 # ...
  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
  28. Content.paginate({ :conditions => { :site_id => site.id, :date => date,

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

  30. Your Interface Think About

  31. Your Data Think About

  32. “ John Nunemaker ORMs sometimes hide creative ways you can

    store and retrieve your data.
  33. Failure and Triumph A Story of

  34. { '_id' => 'site_id:2011-03-28', '/' => {'v' => 200, 't'

    => 'Home'}, '/about/' => {'v' => 50, 't' => 'About'}, '/foo/' => {'v' => 23, 't' => 'Foo!'}, }
  35. You say heresy! I say use case...

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

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

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

  41. { '_id' => BSON::ObjectId.new, 'sid' => site_id, 'p' => '/about/',

    'd' => '2011-03-28', 'v' => 50, }
  42. NewContent.for_site_and_date(site, date)

  43. db['c.2011.5']

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

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

  46. None
  47. /about/

  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

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

  51. Counts Every bit

  52. Integers

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

  54. class Site States = { 'enabled' => 1, 'disabled' =>

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

    !enabled? end end
  56. Compression

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

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

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

    MessagePack.unpack(compressed)
  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
  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
  62. Partial Updates

  63. site.atomic_update_attributes(attrs)

  64. Unused Fields

  65. Counts Where

  66. Memory/Disk/Network

  67. class SiteMode include Scam attr_accessor :name def password_required? id ==

    2 end def item_cache? id == 1 end end
  68. SiteMode.create({ :id => 1, :name => 'live' }) SiteMode.all #

    find by id or string id pp SiteMode.find(2) pp SiteMode.find('2')
  69. class Plan include Toy::Store store(:memory, {}) attribute(:title, String) attribute(:price, Integer)

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

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

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

  73. Go A Long Way A Little Ruby Can

  74. Move

  75. Move BigintMove

  76. Move BigintMove MakeYouWannaMove

  77. Move BigintMove MakeYouWannaMove DaMove

  78. Move BigintMove MakeYouWannaMove DaMove SmoothMove

  79. Move BigintMove MakeYouWannaMove DaMove SmoothMove NightMove

  80. Move BigintMove MakeYouWannaMove DaMove SmoothMove NightMove DanceMove

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

    :writer => false, })
  82. Partition.for_writes.model.create(...)

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

  84. Your Data Think About

  85. Your Code Think About

  86. “ No code is faster than no code.

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

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

    - yes /users - yes
  89. class Hit def site @site ||= begin query = {'_id'

    => site_id} options = {:fields => ['tz']} collection.find_one(query, options) end end end
  90. write code Don’t be afraid to

  91. read code Don’t be afraid to

  92. fail Don’t be afraid to

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

  94. Your Code Think About

  95. “ Albert Einstein It takes a touch of genius —and

    a lot of courage—to move in the opposite direction.
  96. Ordered List Thank you! john@orderedlist.com John Nunemaker RailsConf Baltimore, MD

    May 17, 2011 @jnunemaker