lazy loading and object proxying shenangians
Post on 12-Sep-2014
1.494 views
DESCRIPTION
A presentation delivered to the Ruby on Rails Oceania Melbourne group's October meet up.Covers how to cleaning organise controller code with object proxying to play well with fragment caching in the viewsTRANSCRIPT
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