Lazy Loading ..and object proxying shenanigans
Sep 12, 2014
Lazy Loading..and object proxying shenanigans
John Bartonwww.whoisjohnbarton.com
@johnbarton
we’re often* hiring
* a month or two ago, right now, a couple of months from now
The Problem:
To handle ever increasing page load through the
judicious application of view fragment caching...
... without resorting to downright ugly code
For Example:
class PostController < ApplicationController def index @posts = Post.all endend
<% @posts.each do |post| %> <%= post.title %><% end %>
Make it fast:
<% cache 'slow_posts' do %> <% @posts.each do |post| %> <%= post.title %> <% end %><% end %>
What now?
class PostController < ApplicationController def index @posts = Post.all endend
@posts = Post.all is still evaluated ... and it’s expensive
What about?
<% cache 'slow_posts' do %> <% Post.all.each do |post| %> <%= post.title %> <% end %><% end %>
Separation of concerns?
What about?
class PostController < ApplicationController def index unless fragment_exist? 'slow_posts' @posts = Post.all end endend
Separation of concerns?
What About?
class PostController < ApplicationController def index @posts = lazy_load { Post.all } endend
Still not perfect ... but fuck it ... I’ve got a job
to get on with
How?
def lazy_load(&block) LazyLoader.new(&block) end
class LazyLoader instance_methods.each { |m| undef_method m unless m =~ /^__/ } def initialize(&block) @_initializer = block end
protected # pass everything to _target def method_missing(method, *args, &block) _target.send method, *args, &block end
private # on first call will instantiate itself with _initializer block def _target @_target ||= @_initializer.call endend
Gotchas:
... or when I realised I wanted to be a
Smalltalk programmer
<% if @post %> <%= @post.title %><% end %>
class PostController < ApplicationController def show @post = Post.find(params[:id]) endend
<% cache 'slow_post' do %> <% if @post %> <%= @post.title %> <% end %><% end %>
class PostController < ApplicationController def show @post = lazy_load { Post.find(params[:id]) } endend
NoMethodError:undefined method `title'
for nil:NilClass
wtf?
Ruby’s boolean operators don’t ask the objects for their truthyness
Word on the street is Smalltalk has that covered** I don’t know Smalltalk so hopefully I’m not full of shit right here
a = lazy_load { nil } # this is finenil.nil? # => truea.nil? # => true # this is broken but.....# i can fix it by opening up NilClassa == nil # => truenil == a # => false # but what do i do about these?!!nil # => false!!a # => true if a puts "blah!"end # => "blah!"
Two Solutions:
<% cache 'slow_post' do %> <% unless @post.nil? %> <%= @post.title %> <% end %><% end %>
OR
Learn Smalltalk
http://github.com/joho/lazy-loader
we’re pretty frickin rad, come work for us