Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Why You Should Never Use an ORM
Search
John Nunemaker
PRO
May 17, 2011
Programming
10k
61
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
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
More Decks by John Nunemaker
See All by John Nunemaker
Remote First: Building Distributed Teams that Win
jnunemaker
PRO
1
220
AI: The stuff that nobody shows you
jnunemaker
PRO
10
1.1k
Atom
jnunemaker
PRO
10
5.2k
MongoDB for Analytics
jnunemaker
PRO
11
1.2k
Addicted to Stable
jnunemaker
PRO
32
2.9k
MongoDB for Analytics
jnunemaker
PRO
21
2.3k
MongoDB for Analytics
jnunemaker
PRO
16
30k
Why NoSQL?
jnunemaker
PRO
10
1.1k
Don't Repeat Yourself, Repeat Others
jnunemaker
PRO
7
3.6k
Other Decks in Programming
See All in Programming
APNsからLive Activityを開始する話
yumnumm
0
150
AIに “答え“を書かせるのを やめてみた
___akari11
0
110
見えないものを探る要求要件定義に必要な基本的思考 / invisible-requirement-thinking
minodriven
15
7.9k
難しいけど、読めた。- OSSの入口に立った話。
sts11142
0
140
wkhtmltopdfの次どうするか問題2026
willnet
2
1.9k
Androidだけじゃない、DroidKaigiで広がった私の技術スタック
akkeylab
0
120
Agentic Software Factoryに、すごく賢いIF文を。 / super smart IF statement into the Agentic Software Factory.
rkaga
8
2k
SREの越境 / SRE Collaboration
y0hgi
2
300
Streamlitで実現する自然言語データアプリ開発
ayumu_yamaguchi
1
340
個人開発者よ、Honoを使え(おじさんver)
tanahiro2010
2
120
月間100万人のサービス刷新で、 技術選定をHonoに振り切った話
yutobara
7
5k
contenteditable と日本語入力に向き合う
colorful12
0
170
Featured
See All Featured
Between Models and Reality
mayunak
4
490
Chasing Engaging Ingredients in Design
codingconduct
0
350
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
Agile that works and the tools we love
rasmusluckow
331
22k
Information Architects: The Missing Link in Design Systems
soysaucechin
1
1.2k
Technical Leadership for Architectural Decision Making
baasie
3
600
Optimising Largest Contentful Paint
csswizardry
37
4k
The Power of CSS Pseudo Elements
geoffreycrofte
82
6.6k
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
740
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
3
3.9k
Context Engineering - Making Every Token Count
addyosmani
9
1.2k
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
450
Transcript
Ordered List @jnunemaker RailsConf Baltimore, MD May 17, 2011 Why
You Should Never Use an ORM
You crazy man...
None
“ Albert Einstein Any intelligent fool can make things bigger,
more complex, and more violent.
Violence My Path Of
@gem
HTTParty
HappyMapper
MongoMapper
ToyStore
None
None
Three Points
1) Interface Think About Your
1) Interface Think About Your 2) Data
1) Interface Think About Your 2) Data 3) Code
Your Interface Think About
“ John Nunemaker ORMs too often lead to interface laziness.
site.memberships.create({ :user_id => user.id, :owner => true, })
site.add_owner(user)
membership.update_attributes({ :state => 1, })
membership.update_attributes({ :state => 'maximized', })
user.maximize(site)
content.to_json({ 'a crap ton': 'of options' })
ContentPresenter.new(site, date, { :page => params['page'], }).to_json
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 # ...
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
Content.paginate({ :conditions => { :site_id => site.id, :date => date,
}, :page => params['page'], :per_page => 15, })
site.content_for_date(date, { :page => params['page'], })
Your Interface Think About
Your Data Think About
“ John Nunemaker ORMs sometimes hide creative ways you can
store and retrieve your data.
Failure and Triumph A Story of
{ '_id' => 'site_id:2011-03-28', '/' => {'v' => 200, 't'
=> 'Home'}, '/about/' => {'v' => 50, 't' => 'About'}, '/foo/' => {'v' => 23, 't' => 'Foo!'}, }
You say heresy! I say use case...
None
Content.get("#{site.id}:2011-03-28")
None
write data ensure_index(site_id, date, path)
read data ensure_index(site_id, date, views)
{ '_id' => BSON::ObjectId.new, 'sid' => site_id, 'p' => '/about/',
'd' => '2011-03-28', 'v' => 50, }
NewContent.for_site_and_date(site, date)
db['c.2011.5']
db['c.2011.5'] read index write index
db['c.2011.4'] => db['c.2011.5'] read index write index
None
/about/
/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
None
Zlib.crc32('...really long path...') 762151429
Counts Every bit
Integers
Site.create(:state => 'enabled')
class Site States = { 'enabled' => 1, 'disabled' =>
2, } end Site.create({ :state => Site::States['enabled'], })
class Site def enabled? state == States['enabled'] end def disabled?
!enabled? end end
Compression
require 'rsmaz' compressed = RSmaz.compress(str) decompressed = RSmaz.decompress(compressed)
require 'zlib' compressed = Zlib::Deflate.deflate(str) decompressed = Zlib::Inflate.inflate(compressed)
require 'msgpack' ids = [1,2,3,4,5,6,7,8,9,10] compressed = MessagePack.pack(ids) decompressed =
MessagePack.unpack(compressed)
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
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
Partial Updates
site.atomic_update_attributes(attrs)
Unused Fields
Counts Where
Memory/Disk/Network
class SiteMode include Scam attr_accessor :name def password_required? id ==
2 end def item_cache? id == 1 end end
SiteMode.create({ :id => 1, :name => 'live' }) SiteMode.all #
find by id or string id pp SiteMode.find(2) pp SiteMode.find('2')
class Plan include Toy::Store store(:memory, {}) attribute(:title, String) attribute(:price, Integer)
end
class Asset plugin Joint attachment :file def page_cache(version=nil) page_cache_original page_cache_version(version)
end end
current_user.sites.map do |site| Site.get(site['id']) end
ids = current_user.sites.map do |site| site['id'] end Site.all(:_id.in => ids)
Go A Long Way A Little Ruby Can
Move
Move BigintMove
Move BigintMove MakeYouWannaMove
Move BigintMove MakeYouWannaMove DaMove
Move BigintMove MakeYouWannaMove DaMove SmoothMove
Move BigintMove MakeYouWannaMove DaMove SmoothMove NightMove
Move BigintMove MakeYouWannaMove DaMove SmoothMove NightMove DanceMove
Partition.create({ :association => :moves, :model => Move, :first_id => 1,
:writer => false, })
Partition.for_writes.model.create(...)
Partition.since(since_id, last_move_id).map do |p| send(p.association).since(since_id) end
Your Data Think About
Your Code Think About
“ No code is faster than no code.
$ bx ruby performance/reads.rb Collection#find_one 0.270231008529663 Site.first 0.69925594329834
/track - no /content - no /referrers - no /sites
- yes /users - yes
class Hit def site @site ||= begin query = {'_id'
=> site_id} options = {:fields => ['tz']} collection.find_one(query, options) end end end
write code Don’t be afraid to
read code Don’t be afraid to
fail Don’t be afraid to
Site.find(id) Site.create({ :title => 'RailsTips', }) site.update_attributes(attrs) site.to_json
Your Code Think About
“ Albert Einstein It takes a touch of genius —and
a lot of courage—to move in the opposite direction.
Ordered List Thank you!
[email protected]
John Nunemaker RailsConf Baltimore, MD
May 17, 2011 @jnunemaker