[rspec-users] Controller spec: testing that scope is set

David Chelimsky dchelimsky at gmail.com
Sun Apr 19 19:09:58 EDT 2009


On Sun, Apr 19, 2009 at 5:41 PM, Michael Schuerig <michael at schuerig.de> wrote:
> On Sunday 19 April 2009, Zach Dennis wrote:
>> On Sun, Apr 19, 2009 at 2:09 PM, Michael Schuerig
> <michael at schuerig.de> wrote:
>> > On Sunday 19 April 2009, Zach Dennis wrote:
>> >> On Sun, Apr 19, 2009 at 12:27 PM, Michael Schuerig
>> >
>> > <michael at schuerig.de> wrote:
>> >> > In a Rails controller I set the scope on a model class in an
>> >> > around filter. I have defined expectations on the model classes,
>> >> > and ideally, I would add a further expectation for the scope. Is
>> >> > this already possible in some way? How would I go about adding
>> >> > support a scope expectation?
>> >>
>> >> How are you setting the said scope?
>> >
>> > In an around filter. However, I don't want to test the around
>> > filter mechanism, it might as well be rack middleware instead.
>>
>> Sorry, I don't know what scope means to you in your app. Can you
>> share your around_filter?
>
> Oops, sorry, I assumed the concept from ActiveRecord would be familiar.

It *is* familiar, but setting a model scope from the controller
violates the widely-accepted guideline of skinny controllers and fat
models. I'm guessing that's why Zach wasn't sure what you were talking
about.

> If you know ActiveRecord::Base#with_scope that's really all there is. A
> scope, within a block or through a proxy, defines options that are
> merged with the arguments to #find et al. This merging happens behind
> the scenes, therefore the scoped options are effective, but don't show
> up as arguments anywhere.
>
> I'm using this in conjunction with a generic query representation
> (inspired by JSON Query) that is map through a combination of Rack
> middleware and generated around_filters, see below for a glimpse.
>
> Michael
>
>
> class PeopleController < ApplicationController
>  include QueryScope
>
>  query_scope :only => :index do
>    # Only allow to filter and order by the
>    # virtual name attribute.
>    # This attribute is mapped onto the real
>    # firstname and lastname attributes.
>    allow     :name
>    condition :name =>
>      "LOWER(firstname || ' ' || lastname) :op LOWER(?)"
>    order     :name => "lastname :dir, firstname :dir"
>  end
>  ...
>
> Somewhere in QueryScope
>
> def query_scope(options = {}, &config_block)
>  model_class = extract_resource!(options)
>  builder = QueryScopeBuilder.new(config_block)
>  around_filter(options) do |controller, action|
>    req = builder.build_request_conditioner(controller.request)
>    controller.instance_variable_set(:@offset_limit, req.offset_limit)
>    model_class.send(:with_scope, :find => req.find_options, &action)
>  end
> end

Unless I'm mistaken, it is code like this outside models that was the
underlying motivation for adding named scopes to active record. The
reason it is problematic is that it tends to result in a lot of
duplication outside the models, and makes the controllers really hard
to understand.

Consider this alternative:

describe PeopleController do
  describe "GET index" do
    it "assigns a list of all people filtered by virtual name attributes" do
      people = [mock_model(Person)]
      Person.stub!(:all).and_return(people)
      people.should_receive(:with_virtual_names).and_return(people)
      get :index
    end
  end
end

class PeopleController
  def index
    @people = PeopleController.all.with_virtual_names
  end
end

Now you can specify what with_virtual_names means in a model spec.
This is easier and far less invasive than trying to specify that
specific filters are applied from the controller spec.

HTH,
David

>
> --
> Michael Schuerig
> mailto:michael at schuerig.de
> http://www.schuerig.de/michael/
>
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>


More information about the rspec-users mailing list