Hello,
I am having trouble to mock the chaining of named_scope in the
controller, also I would like to use will_paginate.
def index
@things = Thing.allowed_for(@current_user).available.paginate :page =>
params[:page]
end
¿How to do it?
Thanks
Juanma C.
Maybe “utilization” is not correct an english word.
I’m not sure.
I would change the words in the subject
Juanma C.
On Fri, Sep 12, 2008 at 5:20 AM, Juanma C. [email protected]
wrote:
Thanks
You’re basically just going to have to chain some stubs.
@available = stub(“available things”, :paginate => [:thing])
@allowed_for = stub(“allowed things”, :available => @available)
Thing.stub!(:allowed_for).and_return @allowed_for
I would probably wrap that chain up in a method though, that expresses
what you want and makes it easier to stub
class Thing
def self.available_to(user)
allowed_for(user).available
end
end
This makes your test become
@available = stub(“available things”, :paginate => [:thing])
Thing.stub!(:available_to).and_return @available
and to mock it, you can do
Thing.should_receive(:allowed_for).with(mock_user).and_return @available
Pat
Very clear.
Thank you very much Pat!
This trick to spec a named_scope method works very well. And it cleans
up the chain mess in the controller.
Here you go:
It’s not easy to test for the following named_scopes:
Thing.method1(@current_user).method2.method3 :page =>
params[:page]
I tried:
Thing.should_receive(:method1)…
Thing.should_receive(:method2)…
Thing.should_receive(:method3)
But testing for these three method calls didn’t work. I have an error as
“could not evaluate nil.method2”. That’s because of the way Rails treats
chained named_scopes, each method only exists in the context of its
surrounding scopes. method1 doesn’t anything when followed by method2,
it’s because rails dynamically creates a method that would be called
something like method1_and_method2 if you know what I’m sayin’.
Therefore a workaround is to define an instance method:
class Thing
def self.method1_and_method2(arg)
method1(arg).method2
end
end
Then in the controller, you would call: Thing.method1_and_method2
Now you can spec that easily with:
Thing.should_receive(:method1_and_method2).with(…)
And it works.
On 4 Nov 2008, at 21:48, Fernando P. wrote:
This trick to spec a named_scope method works very well. And it cleans
up the chain mess in the controller.
Which trick is that?
Sorry if this sounds a bit patronising, but it does help if you quote
the relevant bits of a post you’re replying to, to give some context
to your message.
cheers,
Matt
Fernando P. [email protected] writes:
Therefore a workaround is to define an instance method:
class Thing
def self.method1_and_method2(arg)
method1(arg).method2
end
end
I’m inclined to call this an improvement rather than a workaround
Pat
I dunno … creating a bunch of joining methods seems to be an awfully
disjoint way to deal with something that is, admittedly, designed to
laugh
in the face of the Law of Demeter.
I’ve been handling chains through some heavy use of null_object stubs
…
but, I’ve been doing that sort of manually. Could be rolled up, at
least.
I’m kind of spitballing something that might be somewhat less manual …
foo.stub_chains :method_1, :method_2 do |chain|
end
def stub_chains(*args)
hades = stub(“chain”, :null_object => true)
args.each do|chain|
hades.stub!(chain).and_return(hades)
self.stub!(chain).and_return(hades)
end
end
This would more or less let named_scope be used like it’s intended in
Rails,
with one not-so-little exception – I have to know what gets called
last, so
it can return a real value. For me, in practice, that’s been .paginate,
so
it’s not been a huge issue, but that’s a definite gotchya
WDYT?
Right. Example passed a block, but stub_chains didn’t call it. Pretend
there’s a yield(hades) in there somewhere.
“Chris F.” [email protected] writes:
def stub_chains(*args)
hades = stub(“chain”, :null_object => true)
args.each do|chain|
hades.stub!(chain).and_return(hades)
self.stub!(chain).and_return(hades)
end
end
Yeah, that’s cool. In regards to Demeter, one of my former coworkers
argued that if you don’t gain much by that encapsulation, you should
default to following the ontological structure. Makes a lot of sense to
me.
Pat