This memoized patterns happens all over the place. One example is in inherited_resources:
I have some similar code, but since I am working on a pure web service project, I'm not actually using any assigns. But the technique is similar. inherited_resource calls out methods that are sensible defaults most of the time. To customize it, you would override one of several of the hooks, such as object, collection, end_of_association_chain. But this looks a lot nicer using the let() syntax:
class ProjectsController < InheritedResources::Base
protected
def collection
@projects ||= end_of_association_chain.paginate(:page => params[:page])
end
end
class ProjectsController < InheritedResources::BaseAnd then I dropped this into app/concerns. This was actually extracted from my (working) Rails 3 web services project:
include Let
let(:collection) { @projects ||= end_of_association_chain.paginate(:page => params[:page]) }
end
# http://gist.github.com/453389I stole this from Rspec 2, then ripped out the documentation and refactored it using ActiveSupport::Concern. I added a line to make the let() bindings protected so it won't show up as an action for the controller. This is probably the behavior you'd want in non-controller anyways.
module Let
extend ActiveSupport::Concern
included do
extend Let::ClassMethods
end
private
def __memoized # :nodoc:
@__memoized ||= {}
end
module ClassMethods
def let(name, &block)
define_method(name) do
__memoized[name] ||= instance_eval(&block)
end
protected(name)
end
end
end
The neatest thing I had found about let() in Rspec was easily making an implicit DSL inside my spec. The fact that I can carry this over to my controller is simply too cool not to share.