Lazy Loading and Object Proxying Shenangians

Preview:

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 views

Citation preview

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

Recommended