Backstory: Was writing a google calendar api wrapper gem, since I just worked on a project that required it and also there are no existing gems and very few examples available. Needless to say that led to lot of hair pulling. Guess rubyists don’t care about google api. Anyway, I got a prototype up and running using bdd, and was switching over to pry for manual testing. I use lot of let methods in my specs and this one was no exception. One of them was:
let(:token) do
"access_token"
end
I copied and posted that in pry and I got NameError: undefined local variable or method let' for main:Object. Well duh, let is a rspec method. Usually I’d copy and paste the string alone, but I had couple more let methos, so I figured I’d just define let in pry. It seemed simple enough, it takes a symbol as argument and sets that to the value in the block. Stupidly I did:
def let(name)
name = yield
end
Yes, except name is a local variable with scope restricted to just that method body and therefore useless for my purpose. I then changed name to an instance variable and that worked.
def let(name)
@name = yield
end
pry(main)> @name
=> "access_token"
Expect then I’d have to manually change them all and what’s the point in that. Local variable and instance variable is out of question, so the next option is to define them as a method instead.
def let(name, &block)
define_method(name, &block)
end
That looked good, I turned the implicit block into an explicit one with &block and passed that along to define_method. Except I got NoMethodError: undefined method define_method' for main:Object. When pry starts, it starts under main and that has no define_method method. Say you’ve a Post class and you cd into that, then you can do define_method, since that is an object.
pry(main)> class Post
pry(main)* end
pry(main)> cd Post
[8] pry(Post):1> define_method(:hello) {'hi'}
=> #<Proc:0x007f84daa5f2b0@(pry):11 (lambda)>
[9] pry(Post):1> Post.new.hello
=> "hi"
Just blindly I tried calling define_method on Object class and that almost worked.
def let(name, &block)
self.class.define_method(name, &block)
end
=>NoMethodError: private method `define_method' called for Object:Class
So I guess it is a private method, no problem, we can use send instead.
def let(name, &block)
self.class.send(:define_method, name, &block)
end
let(:token) do
'access_token'
end
[10] pry(main)> token
=> "access_token"
And that’s how you define let to work in pry. This is a fairly simple example, but what was interesting to me was the trial and error process. Figured someone else would benefit from it, rather than just the answer.
EDIT:
Add the method to .pryrc in my root dir and its available every time pry starts now. I think pry has some special syntax for adding methods, but plain `def let` works fine, for now.