Make macros DRY

Hey,

I have lots of problems to refactor RSpec macros and make them DRY.
Right now I’m stuck in such a situation for some role macros.

In my controller tests, I want to write:

  1. it_should_grant_access(:super_admin, :to => :countries)
  2. it_should_grant_access(:admin, :to => :countries, :only =>
    [:index, :show])
  3. it_should_not_grant_access(:user, :to => :countries)

These three rules should make sure that:

  1. Super admin should be granted access to all default actions in
    the countries controller
  2. Admin should be granted access to index and show, but not to new,
    create, edit, update or destroy
  3. User should not be granted access to any of the default actions

Since I want to be using this from many places, I used a module:

module RoleMacros
DEFAULT_ACTIONS =
[:index, :show, :new, :create, :edit, :update, :destroy]

def it_should_grant_access(role, options)

  # ...

  if except.present?
    actions = DEFAULT_ACTIONS - Array(except)
  elsif only.present?
    actions = Array(only)
  else
    actions = DEFAULT_ACTIONS
  end

  # ...

end

def it_not_should_grant_access(role, options)
  # ...
end

end

As you see from the example code there are some stuff I need to do
before I can do the actual testing.

My question is how to solve this in a DRY way so that I can use the
set up code in the “not” case as well?

Another problem is that when I specify that some role should have
access to some actions, it also means that the role should not have
access to all but those actions. This means that I must have each
request test in separate so that I can call them. But I’m not sure how
to do this.

Thanks!

On Dec 21, 2010, at 2:11 PM, rejeep wrote:

  1. it_should_not_grant_access(:user, :to => :countries)

    elsif only.present?

end
end

As you see from the example code there are some stuff I need to do
before I can do the actual testing.

My question is how to solve this in a DRY way so that I can use the
set up code in the “not” case as well?

Just use the Extract Method refactoring:

def it_should_grant_access(role, options)
actions_from(options).each do |action|
# …
end
end

def it_not_should_grant_access(role, options)
actions_from(options).each do |action|
# …
end
end

def actions_from(options)
if options[:except]
DEFAULT_ACTIONS - options[:except]
elsif options[:only]
options[:only]
else
DEFAULT_ACTIONS
end
end

Another problem is that when I specify that some role should have
access to some actions, it also means that the role should not have
access to all but those actions. This means that I must have each
request test in separate so that I can call them. But I’m not sure how
to do this.

Just use one macro:

def it_should_grant_access(role, options)
actions_from(options).each do |action|
# … specify grant access
end
(DEFAULT_ACTIONS - actions_from(options)).each do |action|
# … specify deny access
end
end

HTH,
David

Thanks!


rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users

Cheers,
David

Thanks for your reply David!

My solution ended up something like this:

def it_should_grant_access(role, options)
request_index = proc do
get :index

  response.success?
end

# ...

actions_from(options).each do |action|
  request = instance_eval(&eval("request_#{action}"))
  request.should be_true, "#{role} should have been granted access

to #{action}, but was not"
end

# ...

end

Not perfect, but it works.

Btw, I solved the “not-case” by flipping only and except and then just
call it_should_grant_access.

Thanks!