I am looking to do some method chaining. I have the following code:
class MyClass
attr_accessor :handler
def do_a
puts 'i just did a'
self.handler = 'a'
self
end
def do_b_if_a
puts 'i just did b' if handler == 'a'
end
end
So the following works:
irb > test = MyClass.new
=> #<MyClass:0x007fa44ced9a70 @handler=nil>
irb > test.do_a
'i just did a'
irb > test.do_a.do_b_if_a
'i just did a'
'i just did b'
What I DONT want to work is when I call do_a the first time it sets the handler, which means now do_b_if_a can be called at any time. But I only want it to be called when it is chained with do_a, how do I do that?
I think you're tracking state in the wrong place. You'd be better off with something similar to ActiveRecord's query interface, for example:
class MyClass
attr_accessor :handler
def do_a
puts 'i just did a'
with_handler 'a'
end
def do_b_if_a
puts 'i just did b' if handler == 'a'
end
private
def with_handler(h)
o = dup
o.handler = h
o
end
end
That way you always have an instance of MyClass but you have a throw-away copy that keeps track of its history. This is similar to cHao's approach but it doesn't need an extra decorator class as MyClass can decorate itself.
In general, you don't want to care (and in most cases, you don't even get to know) if your methods are called in a certain way. That way lies madness. Magic call sequences make for a hell of a time debugging and testing.
What you could do, though...instead of having do_a
return self
, wrap a decorator around it that defines do_b
, and return the decorator. At that point, your original MyClass
can't do_b
, cause it doesn't know how. But the thingie returned from do_a
can.
(Now, you can still say like a = test.do_a
and then a.do_b
, but you can't really get around that without parsing the Ruby code yourself.)
As cHao said, you shouldn't try to method call patterns. But I can also think of another way to do this:
def do_a
puts "i just did a"
do_b_if_a(true)
end
def do_b_if_a(did_a=false)
puts "i just did b" if did_a
end
do_a # prints "i just did a"
do_b_if_a # does nothing
Sure, you can call do_b_if_a(true)
, but then it just makes it more flexible ;)
Doesn't get you all the way there, but you could define do_b_if_a
as a singleton method that gets added when to_a
is called:
class MyClass
def do_a
puts 'i just did a'
self.tap{|s| def s.do_b_if_a; puts 'i just did b' end}
end
end
You could take a look at Kernel.caller if you want to take certain actions based on the call stack. However, you might get punched in the face by another developer if you don't have a good reason for doing so, lol.