Skip to content

Instantly share code, notes, and snippets.

@mechanicles
Created November 8, 2011 06:50
Show Gist options
  • Save mechanicles/1347183 to your computer and use it in GitHub Desktop.
Save mechanicles/1347183 to your computer and use it in GitHub Desktop.
RSpec : let() it be
Resource: http://jeremy.wordpress.com/2009/11/05/rspec-let-it-be/
RSpec : let() it be
What I’ve found
A few minutes ago, I was watching a great screencast of Corey Haines doing a kata.
I stopped when he was refactoring a few similar assigments. There was something I’ve never seen elsewhere, particularly in the also great RSpec book ; he used the let() method.
Going back and forth a few times, I understood the the method was assigning the result of the given block to an object named after the argument of let().
I googled to see if there was an explanation ; is it a custom helper he wrote, is it built-in RSpec, … ? but « RSpec let() » is returning a lot of results, and none was close to the answer I was looking for.
I then looked at the Rdoc for the RSpec gem ; no luck either.
Finally, I open the whole gem in Textmate and looked for « let » as a word.
I’ve found a use of the method in a spec (RSpec is speced with itself, how great is it?) with a similar scenario. I’ve also found the method declaration, and I have to say that it’s pretty self-explanatory :
1 def let(name, &block)
2 define_method name do
3 @assignments ||= {}
4 @assignments[name] ||= instance_eval(&block)
5 end
6 end
You have to pass a name and a block. It defines a new method (named after what’s in ‘name’). This new method returns the result of the block (whatever it returns) but is memoizes it in a global hash.
You can use the let() method in a describe block, if you want to create a method that is always returning the same value, determined by the result of the passed block. It is something you can do with a traditional before block with a simple variable assignment, but it’d be evaluated each time, though it’s not necessary. A simple variable is also subject to change if it’s not frozen or a constant.
Here, it’s just what it is and only what it needs to be.
What I like
First, I like that some very simple but usefull tools are available. I really sounds like something build after hundreds of times of repeating the same calls, … It’s very concise and DRY.
But I also like very much the way the hash is created if it doesn’t already exist, and the value is set only once,even on subsequent calls of the new method.
The dynamically created method could have been defined this way :
1 define_method name do
2 @assignments = {} if @assignments.nil?
3 if @assignments[name].nil?
4 @assignments[name] = instance_eval(&block)
5 else
6 @assignments[name]
7 end
8 end
but it’s boringly verbose and repetitive. The first way is way more readable and elegant.
If I have remember just a thing from Corey’s screencast, it’s the existence and the use of this beautiful let() method and how well it is written.
About Corey Haines
Corey is a great guy, one of the few that I really admire.
Amongst the thing that I like about him, he’s doing something that I’d really like to do : he’s traveling all over the USA and is offering to share some working (and fun) times for hospitality and friendship.
You can find about what he does on his profile page.
Thanks Corey !
Update 1 : The entire Kata is available on GitHub. There are 3 branches : the first is the start of the kata, with only the setup and the cucumber expectations. The second and third are implementations.
It’s very useful to read the initial state of the Kata and see where it ends, with all the tests and the implementation code.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment