Mocking named_scope utilization

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 :slight_smile:

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