Skip to content
Browse files

Merged upstream master and bumped rspec to 3.0.0 (release)

  • Loading branch information...
2 parents 0a20095 + 968be2c commit 531c01799fc413a6e6c96745943830437034e946 @andypike andypike committed Jun 11, 2014
View
12 .gitignore
@@ -1,5 +1,15 @@
+.DS_Store
+.idea/*
*.swp
**/*.swp
*.gem
-Gemfile.lock
.bundle
+
+gemfiles/*.lock
+Gemfile.lock
+
+.rvmrc
+.rbenv-version
+.ruby-version
+.ruby-gemset
+/tmp
View
1 .ruby-version
@@ -1 +0,0 @@
-1.9.3
View
10 .travis.yml
@@ -7,10 +7,18 @@ rvm:
- ree
- jruby
- rbx
+gemfile:
+ - gemfiles/activerecord_3.0.gemfile
+ - gemfiles/activerecord_3.1.gemfile
+ - gemfiles/activerecord_3.2.gemfile
+ - gemfiles/datamapper_1.x.gemfile
+ - gemfiles/mongoid_2.x.gemfile
+ - gemfiles/sequel_3.x.gemfile
+services:
+ - mongodb
matrix:
allow_failures:
- rvm: rbx
notifications:
recipients:
- bryan@bryanrite.com
- - ryan@railscasts.com
View
73 Appraisals
@@ -0,0 +1,73 @@
+appraise "activerecord_3.0" do
+ gem "activerecord", "~> 3.0.20", :require => "active_record"
+ gem 'activesupport', '~> 3.0.20', :require => 'active_support/all'
+ gem "meta_where"
+
+ gemfile.platforms :jruby do
+ gem "activerecord-jdbcsqlite3-adapter"
+ gem "jdbc-sqlite3"
+ end
+
+ gemfile.platforms :ruby, :mswin, :mingw do
+ gem "sqlite3"
+ end
+end
+
+appraise "activerecord_3.1" do
+ gem "activerecord", "~> 3.1.0", :require => "active_record"
+
+ gemfile.platforms :jruby do
+ gem "activerecord-jdbcsqlite3-adapter"
+ gem "jdbc-sqlite3"
+ end
+
+ gemfile.platforms :ruby, :mswin, :mingw do
+ gem "sqlite3"
+ end
+end
+
+appraise "activerecord_3.2" do
+ gem "activerecord", "~> 3.2.0", :require => "active_record"
+
+ gemfile.platforms :jruby do
+ gem "activerecord-jdbcsqlite3-adapter"
+ gem "jdbc-sqlite3"
+ end
+
+ gemfile.platforms :ruby, :mswin, :mingw do
+ gem "sqlite3"
+ end
+end
+
+appraise "datamapper_1.x" do
+ gem 'activesupport', '~> 3.0', :require => 'active_support/all'
+ gem "dm-core", "~> 1.0.2"
+ gem "dm-sqlite-adapter", "~> 1.0.2"
+ gem "dm-migrations", "~> 1.0.2"
+end
+
+appraise "mongoid_2.x" do
+ gem 'activesupport', '~> 3.0', :require => 'active_support/all'
+ gem "mongoid", "~> 2.0.0"
+
+ gemfile.platforms :ruby, :mswin, :mingw do
+ gem "bson_ext", "~> 1.1"
+ end
+
+ gemfile.platforms :jruby do
+ gem "mongo", "~> 1.9.2"
+ end
+end
+
+appraise "sequel_3.x" do
+ gem "sequel", "~> 3.47.0"
+ gem 'activesupport', '~> 3.0', :require => 'active_support/all'
+
+ gemfile.platforms :jruby do
+ gem "jdbc-sqlite3"
+ end
+
+ gemfile.platforms :ruby, :mswin, :mingw do
+ gem "sqlite3"
+ end
+end
View
32 CHANGELOG.rdoc
@@ -1,4 +1,34 @@
-Master
+Develop
+
+
+1.8.2 (June 5th, 2014)
+
+* Fix cancancan#75 - More specific hash-like object check. (bryanrite)
+
+
+1.8.1 (May 27th, 2014)
+
+* Fix cancancan#67 - Sequel tests are run properly for JRuby. (bryanrite)
+
+* Fix cancancan#68 - Checks for hash-like objects in subject better. (bryanrite)
+
+
+1.8.0 (May 8th, 2014)
+
+* Feature cancan#884 - Add a Sequel model adapter (szetobo)
+
+* Feature cancancan#3 - Permit "can?" check multiple subjects (cefigueiredo)
+
+* Feature cancancan#29 - Add ability to use a String that will get instance_eval'd or a Proc that will get called as the parameter method option for strong_parameter santization (svoop)
+
+* Feature cancancan#48 - Define a CanCanCan module. Even though it is not used, it is standard practice to define the module, and helpful for determining between CanCanCan and CanCan for external libraries.
+
+
+1.7.1 (March 19th, 2014)
+
+* Fix ryanb/cancan#992 - Remove Rails 4 deprecations for scoped (thejchap & hitendrasingh)
+
+* Fix cancancan#16 - RSpec expectations are not explicitly required in RSpec > 2.13 (justinaiken & bryanrite)
1.7.0 (February 19th, 2014)
View
22 CONTRIBUTING.md
@@ -1,11 +1,23 @@
-### Please read before contributing
+## Contributing to CanCanCan
-1) If you have any questions about CanCan, search the [Wiki](https://github.com/bryanrite/cancancan/wiki) or use [Stack Overflow](http://stackoverflow.com/questions/tagged/cancancan). Do not post questions here.
+### Reporting an Issue
-2) If you find a security bug, **DO NOT** submit an issue here. Please send an e-mail to [bryan@bryanrite.com](mailto:bryan@bryanrite.com) instead.
+1. If you have any questions about CanCanCan, search the [Wiki](https://github.com/bryanrite/cancancan/wiki), use [Stack Overflow](http://stackoverflow.com/questions/tagged/cancancan), or [our mailing list](https://groups.google.com/forum/#!forum/cancancan). Do not post questions here.
-3) Do a small search on the issues tracker before submitting your issue to see if it was already reported / fixed. In case it was not, create your report including Rails and CanCan versions. If you are getting exceptions, please include the full backtrace.
+1. If you find a security bug, **DO NOT** submit an issue here. Please send an e-mail to [bryan@bryanrite.com](mailto:bryan@bryanrite.com) instead.
-That's it! The more information you give, the more easy it becomes for us to track it down and fix it. Ideal scenario would be adding the issue to CanCan test suite or to a sample application.
+1. Do a small search on the issues tracker before submitting your issue to see if it was already reported / fixed.
+
+1. Create your report including Rails and CanCanCan versions. If you are getting exceptions, please include the full backtrace.
+
+That's it! The more information you give, the more easy it becomes for us to track it down and fix it. Ideal scenario would be adding the issue to CanCanCan test suite or to a sample application.
+
+### Adding new Features or Bugfixes
+
+CanCanCan uses a [git-flow](http://nvie.com/posts/a-successful-git-branching-model/) development model. The latest "released" version of CanCanCan, the latest gem version, can always be found on `master`, while the next version or nightly is on `develop`.
+
+Please make sure you have test coverage for anything you add or fix!
+
+Please add a CHANGELOG entry with any relevant tags for issues, pull-requests, and authors.
Thanks!
View
20 Gemfile
@@ -1,23 +1,3 @@
source "https://rubygems.org"
-case ENV["MODEL_ADAPTER"]
-when nil, "active_record"
- # Sqlite for CRuby, Rubinius, including Windows and RubyInstaller
- gem "sqlite3", :platform => [:ruby, :mswin, :mingw]
- # Sqlite for JRuby
- gem 'activerecord-jdbcsqlite3-adapter', :platform => :jruby
- gem "activerecord", '~> 3.0.9', :require => "active_record"
- gem "with_model", "~> 0.2.5"
- gem "meta_where"
-when "data_mapper"
- gem "dm-core", "~> 1.0.2"
- gem "dm-sqlite-adapter", "~> 1.0.2"
- gem "dm-migrations", "~> 1.0.2"
-when "mongoid"
- gem "bson_ext", "~> 1.1"
- gem "mongoid", "~> 2.0.0.beta.20"
-else
- raise "Unknown model adapter: #{ENV["MODEL_ADAPTER"]}"
-end
-
gemspec
View
36 README.rdoc
@@ -1,7 +1,8 @@
= CanCanCan
{<img src="https://badge.fury.io/rb/cancancan.png" alt="Gem Version" />}[http://badge.fury.io/rb/cancancan]
-{<img src="https://travis-ci.org/bryanrite/cancancan.png" alt="Build Status" />}[https://travis-ci.org/bryanrite/cancancan]
-{<img src="https://codeclimate.com/github/bryanrite/cancancan.png?" alt="Code Climate" />}[https://codeclimate.com/github/bryanrite/cancancan]
+{<img src="https://travis-ci.org/CanCanCommunity/cancancan.png?branch=master" alt="Build Status" />}[https://travis-ci.org/CanCanCommunity/cancancan]
+{<img src="https://codeclimate.com/github/CanCanCommunity/cancancan.png" />}[https://codeclimate.com/github/CanCanCommunity/cancancan]
+{<img src="http://inch-ci.org/github/CanCanCommunity/cancancan.png" alt="Inline docs" />}[http://inch-ci.org/github/CanCanCommunity/cancancan]
Wiki[https://github.com/bryanrite/cancancan/wiki] | RDocs[http://rdoc.info/projects/ryanb/cancan] | Screencast[http://railscasts.com/episodes/192-authorization-with-cancan]
@@ -21,7 +22,7 @@ Any help is greatly appreciated, feel free to submit pull-requests or open issue
In <b>Rails 3 and 4</b>, add this to your Gemfile and run the +bundle+ command.
- gem 'cancancan', '~> 1.7'
+ gem 'cancancan', '~> 1.8'
In <b>Rails 2</b>, add this to your environment.rb file.
@@ -34,7 +35,7 @@ Alternatively, you can install it as a plugin.
== Getting Started
-CanCan expects a +current_user+ method to exist in the controller. First, set up some authentication (such as Authlogic[https://github.com/binarylogic/authlogic] or Devise[https://github.com/plataformatec/devise]). See {Changing Defaults}[https://github.com/bryanrite/cancan/wiki/changing-defaults] if you need different behavior.
+CanCanCan expects a +current_user+ method to exist in the controller. First, set up some authentication (such as Authlogic[https://github.com/binarylogic/authlogic] or Devise[https://github.com/plataformatec/devise]). See {Changing Defaults}[https://github.com/bryanrite/cancancan/wiki/changing-defaults] if you need different behavior.
=== 1. Define Abilities
@@ -52,7 +53,7 @@ In Rails 2.3, just add a new class in <tt>app/models/ability.rb</tt> with the fo
end
end
-See {Defining Abilities}[https://github.com/bryanrite/cancan/wiki/defining-abilities] for details.
+See {Defining Abilities}[https://github.com/bryanrite/cancancan/wiki/defining-abilities] for details.
=== 2. Check Abilities & Authorization
@@ -97,6 +98,8 @@ By default, CanCan will try to sanitize the input on <tt>:create</tt> and <tt>:u
Additionally, <tt>load_and_authorize_resource</tt> can now take a <tt>param_method</tt> option to specify a custom method in the controller to run to sanitize input.
+You can associate the <tt>param_method</tt> option with a symbol corresponding to the name of a method that will get called:
+
class ArticlesController < ApplicationController
load_and_authorize_resource param_method: :my_sanitizer
@@ -114,7 +117,15 @@ Additionally, <tt>load_and_authorize_resource</tt> can now take a <tt>param_meth
params.require(:article).permit(:name)
end
end
-
+
+You can also use a string that will be evaluated in the context of the controller using <tt>instance_eval</tt> and needs to contain valid Ruby code. This does come in handy when using a PermittedParams class as suggested in Railscast 371:
+
+ load_and_authorize_resource param_method: 'permitted_params.article'
+
+Finally, it's possible to associate <tt>param_method</tt> with a Proc object which will be called with the controller as the only argument:
+
+ load_and_authorize_resource param_method: Proc.new { |c| c.params.require(:article).permit(:name) }
+
See {Strong Parameters}[https://github.com/bryanrite/cancancan/wiki/Strong-Parameters] for more information.
=== 3. Handle Unauthorized Access
@@ -153,9 +164,18 @@ This will raise an exception if authorization is not performed in an action. If
== Questions or Problems?
-If you have any issues with CanCan which you cannot find the solution to in the documentation[https://github.com/bryanrite/cancancan/wiki], please add an {issue on GitHub}[https://github.com/bryanrite/cancancan/issues] or fork the project and send a pull request.
+If you have any issues with CanCan which you cannot find the solution to in the documentation[https://github.com/bryanrite/cancancan/wiki] or our mailing list: http://groups.google.com/group/cancancan, please add an {issue on GitHub}[https://github.com/bryanrite/cancancan/issues] or fork the project and send a pull request.
+
+
+== Development
+
+Cancancan uses {appraisals}[https://github.com/thoughtbot/appraisal] to test the code base against multiple versions of rails, as well as the different model adapters.
+
+When first developing, you may need to run <tt>bundle install</tt> and then <tt>appraisal install</tt>, to install the different sets.
+
+You can then run all appraisal files (like CI does), with <tt>appraisal rake</tt> or just run a specific set <tt>appraisal activerecord_3.0 rake</tt>.
-To get the specs running you should call +bundle+ and then +rake+. See the {spec/README}[https://github.com/bryanrite/cancancan/blob/master/spec/README.rdoc] for more information.
+See the {CONTRIBUTING}[https://github.com/CanCanCommunity/cancancan/blob/develop/CONTRIBUTING.md] and {spec/README}[https://github.com/bryanrite/cancancan/blob/master/spec/README.rdoc] for more information.
== Special Thanks
View
10 Rakefile
@@ -1,19 +1,9 @@
require "bundler/gem_tasks"
-require 'rubygems'
-require 'rake'
require 'rspec/core/rake_task'
desc "Run RSpec"
RSpec::Core::RakeTask.new do |t|
t.verbose = false
end
-desc "Run specs for all adapters"
-task :spec_all do
- %w[active_record data_mapper mongoid].each do |model_adapter|
- puts "MODEL_ADAPTER = #{model_adapter}"
- system "rake spec MODEL_ADAPTER=#{model_adapter}"
- end
-end
-
task :default => :spec
View
19 cancancan.gemspec
@@ -8,19 +8,24 @@ Gem::Specification.new do |s|
s.version = CanCan::VERSION
s.authors = ["Bryan Rite", "Ryan Bates"]
s.email = "bryan@bryanrite.com"
- s.homepage = "http://github.com/bryanrite/cancancan"
+ s.homepage = "https://github.com/CanCanCommunity/cancancan"
s.summary = "Simple authorization solution for Rails."
s.description = "Continuation of the simple authorization solution for Rails which is decoupled from user roles. All permissions are stored in a single location."
s.platform = Gem::Platform::RUBY
s.license = "MIT"
- s.files = Dir["{lib,spec}/**/*", "[A-Z]*", "init.rb"] - ["Gemfile.lock"]
- s.require_path = "lib"
+ s.files = `git ls-files`.split($/)
+ s.test_files = `git ls-files -- Appraisals {spec,features,gemfiles}/*`.split($/)
+ s.executables = `git ls-files -- bin/*`.split($/).map{ |f| File.basename(f) }
+ s.require_paths = ["lib"]
- s.add_development_dependency 'rspec', '~> 3.0.0.beta2'
- s.add_development_dependency 'rails', '~> 3.0.9'
- s.add_development_dependency 'supermodel', '~> 0.1.4'
+ s.required_ruby_version = Gem::Requirement.new(">= 1.8.7")
+ s.required_rubygems_version = ">= 1.3.4"
+
+ s.add_development_dependency 'bundler', '~> 1.3'
+ s.add_development_dependency 'rake', '~> 10.1.1'
+ s.add_development_dependency 'rspec', '~> 3.0.0'
+ s.add_development_dependency 'appraisal', '>= 1.0.0'
s.rubyforge_project = s.name
- s.required_rubygems_version = ">= 1.3.4"
end
View
18 gemfiles/activerecord_3.0.gemfile
@@ -0,0 +1,18 @@
+# This file was generated by Appraisal
+
+source "https://rubygems.org"
+
+gem "activerecord", "~> 3.0.20", :require => "active_record"
+gem "activesupport", "~> 3.0.20", :require => "active_support/all"
+gem "meta_where"
+
+platforms :jruby do
+ gem "activerecord-jdbcsqlite3-adapter"
+ gem "jdbc-sqlite3"
+end
+
+platforms :ruby, :mswin, :mingw do
+ gem "sqlite3"
+end
+
+gemspec :path => "../"
View
16 gemfiles/activerecord_3.1.gemfile
@@ -0,0 +1,16 @@
+# This file was generated by Appraisal
+
+source "https://rubygems.org"
+
+gem "activerecord", "~> 3.1.0", :require => "active_record"
+
+platforms :jruby do
+ gem "activerecord-jdbcsqlite3-adapter"
+ gem "jdbc-sqlite3"
+end
+
+platforms :ruby, :mswin, :mingw do
+ gem "sqlite3"
+end
+
+gemspec :path => "../"
View
16 gemfiles/activerecord_3.2.gemfile
@@ -0,0 +1,16 @@
+# This file was generated by Appraisal
+
+source "https://rubygems.org"
+
+gem "activerecord", "~> 3.2.0", :require => "active_record"
+
+platforms :jruby do
+ gem "activerecord-jdbcsqlite3-adapter"
+ gem "jdbc-sqlite3"
+end
+
+platforms :ruby, :mswin, :mingw do
+ gem "sqlite3"
+end
+
+gemspec :path => "../"
View
10 gemfiles/datamapper_1.x.gemfile
@@ -0,0 +1,10 @@
+# This file was generated by Appraisal
+
+source "https://rubygems.org"
+
+gem "activesupport", "~> 3.0", :require => "active_support/all"
+gem "dm-core", "~> 1.0.2"
+gem "dm-sqlite-adapter", "~> 1.0.2"
+gem "dm-migrations", "~> 1.0.2"
+
+gemspec :path => "../"
View
16 gemfiles/mongoid_2.x.gemfile
@@ -0,0 +1,16 @@
+# This file was generated by Appraisal
+
+source "https://rubygems.org"
+
+gem "activesupport", "~> 3.0", :require => "active_support/all"
+gem "mongoid", "~> 2.0.0"
+
+platforms :ruby, :mswin, :mingw do
+ gem "bson_ext", "~> 1.1"
+end
+
+platforms :jruby do
+ gem "mongo", "~> 1.9.2"
+end
+
+gemspec :path => "../"
View
16 gemfiles/sequel_3.x.gemfile
@@ -0,0 +1,16 @@
+# This file was generated by Appraisal
+
+source "https://rubygems.org"
+
+gem "sequel", "~> 3.47.0"
+gem "activesupport", "~> 3.0", :require => "active_support/all"
+
+platforms :jruby do
+ gem "jdbc-sqlite3"
+end
+
+platforms :ruby, :mswin, :mingw do
+ gem "sqlite3"
+end
+
+gemspec :path => "../"
View
1 lib/cancan.rb
@@ -12,3 +12,4 @@
require 'cancan/model_adapters/active_record_adapter' if defined? ActiveRecord
require 'cancan/model_adapters/data_mapper_adapter' if defined? DataMapper
require 'cancan/model_adapters/mongoid_adapter' if defined?(Mongoid) && defined?(Mongoid::Document)
+require 'cancan/model_adapters/sequel_adapter' if defined? Sequel
View
28 lib/cancan/ability.rb
@@ -29,6 +29,13 @@ module Ability
#
# can? :create, @category => Project
#
+ # You can also pass multiple objects to check. You only need to pass a hash
+ # following the pattern { :any => [many subjects] }. The behaviour is check if
+ # there is a permission on any of the given objects.
+ #
+ # can? :create, {:any => [Project, Rule]}
+ #
+ #
# Any additional arguments will be passed into the "can" block definition. This
# can be used to pass more information about the user's request for example.
#
@@ -54,12 +61,16 @@ module Ability
#
# Also see the RSpec Matchers to aid in testing.
def can?(action, subject, *extra_args)
- match = relevant_rules_for_match(action, subject).detect do |rule|
- rule.matches_conditions?(action, subject, extra_args)
- end
+ subject = extract_subjects(subject)
+
+ match = subject.map do |subject|
+ relevant_rules_for_match(action, subject).detect do |rule|
+ rule.matches_conditions?(action, subject, extra_args)
+ end
+ end.compact.first
+
match ? match.base_behavior : false
end
-
# Convenience method which works the same as "can?" but returns the opposite value.
#
# cannot? :destroy, @project
@@ -272,6 +283,15 @@ def expanded_actions
@expanded_actions ||= {}
end
+ # It translates to an array the subject or the hash with multiple subjects given to can?.
+ def extract_subjects(subject)
+ subject = if subject.kind_of?(Hash) && subject.key?(:any)
+ subject[:any]
+ else
+ [subject]
+ end
+ end
+
# Given an action, it will try to find all of the actions which are aliased to it.
# This does the opposite kind of lookup as expand_actions.
def aliases_for_action(action)
View
8 lib/cancan/controller_resource.rb
@@ -221,7 +221,11 @@ def name
def resource_params
if param_actions.include?(@params[:action].to_sym) && params_method.present?
- return @controller.send(params_method)
+ return case params_method
+ when Symbol then @controller.send(params_method)
+ when String then @controller.instance_eval(params_method)
+ when Proc then params_method.call(@controller)
+ end
elsif @options[:class]
params_key = extract_key(@options[:class])
return @params[params_key] if @params[params_key]
@@ -236,7 +240,7 @@ def resource_params_by_namespaced_name
def params_method
params_methods.each do |method|
- return method if @controller.respond_to?(method, true)
+ return method if (method.is_a?(Symbol) && @controller.respond_to?(method, true)) || method.is_a?(String) || method.is_a?(Proc)
end
nil
end
View
8 lib/cancan/matchers.rb
@@ -1,4 +1,12 @@
rspec_module = defined?(RSpec::Core) ? 'RSpec' : 'Spec' # for RSpec 1 compatability
+
+if rspec_module == 'RSpec'
+ require 'rspec/core'
+ require 'rspec/expectations'
+else
+ ActiveSupport::Deprecation.warn("RSpec v1 will not be supported in the CanCanCan >= 2.0.0")
+end
+
Kernel.const_get(rspec_module)::Matchers.define :be_able_to do |*args|
match do |ability|
ability.can?(*args)
View
4 lib/cancan/model_adapters/active_record_adapter.rb
@@ -97,7 +97,7 @@ def joins
def database_records
if override_scope
- @model_class.scoped.merge(override_scope)
+ @model_class.where(nil).merge(override_scope)
elsif @model_class.respond_to?(:where) && @model_class.respond_to?(:joins)
mergeable_conditions = @rules.select {|rule| rule.unmergeable? }.blank?
if mergeable_conditions
@@ -106,7 +106,7 @@ def database_records
@model_class.where(*(@rules.map(&:conditions))).includes(joins)
end
else
- @model_class.scoped(:conditions => conditions, :joins => joins)
+ @model_class.all(:conditions => conditions, :joins => joins)
end
end
View
87 lib/cancan/model_adapters/sequel_adapter.rb
@@ -0,0 +1,87 @@
+module CanCan
+ module ModelAdapters
+ class SequelAdapter < AbstractAdapter
+ def self.for_class?(model_class)
+ model_class <= Sequel::Model
+ end
+
+ def self.find(model_class, id)
+ model_class[id]
+ end
+
+ def self.override_condition_matching?(subject, name, value)
+ value.kind_of?(Hash)
+ end
+
+ def self.matches_condition?(subject, name, value)
+ obj = subject.send(name)
+ if obj.nil?
+ false
+ else
+ value.each do |k, v|
+ if v.kind_of?(Hash)
+ return false unless self.matches_condition?(obj, k, v)
+ elsif obj.send(k) != v
+ return false
+ end
+ end
+ end
+ end
+
+ def database_records
+ if @rules.size == 0
+ @model_class.where('1=0')
+ else
+ # only need to process can rules if there are no can rule with empty conditions
+ rules = @rules.reject { |rule| rule.base_behavior && rule.conditions.empty? }
+ rules.reject! { |rule| rule.base_behavior } if rules.count < @rules.count
+
+ can_condition_added = false
+ rules.reverse.inject(@model_class.dataset) do |records, rule|
+ normalized_conditions = normalize_conditions(rule.conditions)
+ if rule.base_behavior
+ if can_condition_added
+ records.or normalized_conditions
+ else
+ can_condition_added = true
+ records.where normalized_conditions
+ end
+ else
+ records.exclude normalized_conditions
+ end
+ end
+ end
+ end
+
+ private
+
+ def normalize_conditions(conditions, model_class = @model_class)
+ return conditions unless conditions.kind_of? Hash
+ conditions.inject({}) do |result_hash, (name, value)|
+ if value.kind_of? Hash
+ value = value.dup
+ association_class = model_class.association_reflection(name).associated_class
+ nested = value.inject({}) do |nested, (k, v)|
+ if v.kind_of?(Hash)
+ value.delete(k)
+ nested_class = association_class.association_reflection(k).associated_class
+ nested[k] = nested_class.where(normalize_conditions(v, association_class))
+ else
+ nested[k] = v
+ end
+ nested
+ end
+ result_hash[name] = association_class.where(nested)
+ else
+ result_hash[name] = value
+ end
+ result_hash
+ end
+ end
+ end
+ end
+end
+
+Sequel::Model.class_eval do
+ include CanCan::ModelAdditions
+end
View
53 lib/cancan/rule.rb
@@ -99,31 +99,19 @@ def matches_subject_class?(subject)
# override_matching_for_conditions?(subject, conditions) and
# matches_conditions_hash?(subject, conditions)
def matches_conditions_hash?(subject, conditions = @conditions)
- if conditions.empty?
- true
- else
- if model_adapter(subject).override_conditions_hash_matching? subject, conditions
- model_adapter(subject).matches_conditions_hash? subject, conditions
- else
- conditions.all? do |name, value|
- if model_adapter(subject).override_condition_matching? subject, name, value
- model_adapter(subject).matches_condition? subject, name, value
- else
- attribute = subject.send(name)
- if value.kind_of?(Hash)
- if attribute.kind_of?(Array) || attribute.kind_of?(ActiveRecord::Relation)
- attribute.any? { |element| matches_conditions_hash? element, value }
- else
- !attribute.nil? && matches_conditions_hash?(attribute, value)
- end
- elsif !value.is_a?(String) && value.kind_of?(Enumerable)
- value.include? attribute
- else
- attribute == value
- end
- end
- end
+ return true if conditions.empty?
+ adapter = model_adapter(subject)
+
+ if adapter.override_conditions_hash_matching?(subject, conditions)
+ return adapter.matches_conditions_hash?(subject, conditions)
+ end
+
+ conditions.all? do |name, value|
+ if adapter.override_condition_matching?(subject, name, value)
+ return adapter.matches_condition?(subject, name, value)
end
+
+ condition_match?(subject.send(name), value)
end
end
@@ -143,5 +131,22 @@ def call_block_with_all(action, subject, extra_args)
def model_adapter(subject)
CanCan::ModelAdapters::AbstractAdapter.adapter_class(subject_class?(subject) ? subject : subject.class)
end
+
+ def condition_match?(attribute, value)
+ case value
+ when Hash then hash_condition_match?(attribute, value)
+ when String then attribute == value
+ when Enumerable then value.include?(attribute)
+ else attribute == value
+ end
+ end
+
+ def hash_condition_match?(attribute, value)
+ if attribute.kind_of?(Array) || (defined?(ActiveRecord) && attribute.kind_of?(ActiveRecord::Relation))
+ attribute.any? { |element| matches_conditions_hash?(element, value) }
+ else
+ !attribute.nil? && matches_conditions_hash?(attribute, value)
+ end
+ end
end
end
View
2 lib/cancan/version.rb
@@ -1,3 +1,3 @@
module CanCan
- VERSION = "1.7.0"
+ VERSION = "1.8.2"
end
View
3 lib/cancancan.rb
@@ -1 +1,4 @@
require 'cancan'
+
+module CanCanCan
+end
View
23 spec/README.rdoc
@@ -5,24 +5,23 @@
To run the specs first run the +bundle+ command to install the necessary gems and the +rake+ command to run the specs.
bundle
- rake
-The specs currently require Ruby 1.8.7. Ruby 1.9.2 support will be coming soon.
+Then run the appraisal command to install all the necssary test sets:
+ appraisal install
-== Model Adapters
+You can then run all test sets:
+
+ appraisal rake
-CanCan offers separate specs for different model adapters (such as Mongoid and Data Mapper). By default it will use Active Record but you can change this by setting the +MODEL_ADAPTER+ environment variable before running. You can run the +bundle+ command with this as well to ensure you have the installed gems.
+Or individual ones:
- MODEL_ADAPTER=data_mapper bundle
- MODEL_ADAPTER=data_mapper rake
+ appraisal rails_3.0 rake
-The different model adapters you can specify are:
+A list of the tests is in the +Appraisal+ file.
-* active_record (default)
-* data_mapper
-* mongoid
+The specs support Ruby 1.8.7+
-You can also run the +spec_all+ rake task to run specs for each adapter.
+== Model Adapters
- rake spec_all
+The model adapter ENV setting has been removed and replaced with the +Appraisal+ file.
View
32 spec/cancan/ability_spec.rb
@@ -147,36 +147,53 @@
expect(@ability.can?(:update, 123)).to be(false)
end
+ it "checks if there is a permission for any of given subjects" do
+ @ability.can :update, [String, Range]
+ expect(@ability.can?(:update, {:any => ["foo", 1..3]})).to be(true)
+ expect(@ability.can?(:update, {:any => [1..3, "foo"]})).to be(true)
+ expect(@ability.can?(:update, {:any => [123, "foo"]})).to be(true)
+ expect(@ability.can?(:update, {:any => [123, 1.0]})).to be(false)
+ end
+
it "supports custom objects in the rule" do
@ability.can :read, :stats
expect(@ability.can?(:read, :stats)).to be(true)
expect(@ability.can?(:update, :stats)).to be(false)
expect(@ability.can?(:read, :nonstats)).to be(false)
+ expect(@ability.can?(:read, {:any => [:stats, :nonstats]})).to be(true)
+ expect(@ability.can?(:read, {:any => [:nonstats, :neitherstats]})).to be(false)
end
it "checks ancestors of class" do
@ability.can :read, Numeric
expect(@ability.can?(:read, Integer)).to be(true)
expect(@ability.can?(:read, 1.23)).to be(true)
expect(@ability.can?(:read, "foo")).to be(false)
+ expect(@ability.can?(:read, {:any => [Integer, String]})).to be(true)
end
it "supports 'cannot' method to define what user cannot do" do
@ability.can :read, :all
@ability.cannot :read, Integer
expect(@ability.can?(:read, "foo")).to be(true)
expect(@ability.can?(:read, 123)).to be(false)
+ expect(@ability.can?(:read, {:any => ["foo", "bar"]})).to be(true)
+ expect(@ability.can?(:read, {:any => [123, "foo"]})).to be(false)
+ expect(@ability.can?(:read, {:any => [123, 456]})).to be(false)
end
it "passes to previous rule, if block returns false or nil" do
@ability.can :read, :all
@ability.cannot :read, Integer do |int|
int > 10 ? nil : ( int > 5 )
end
+
expect(@ability.can?(:read, "foo")).to be(true)
expect(@ability.can?(:read, 3)).to be(true)
expect(@ability.can?(:read, 8)).to be(false)
expect(@ability.can?(:read, 123)).to be(true)
+ expect(@ability.can?(:read, {:any => [123, 8]})).to be(true)
+ expect(@ability.can?(:read, {:any => [8, 9]})).to be(false)
end
it "always returns `false` for single cannot definition" do
@@ -216,22 +233,31 @@
@ability.can :read, Integer do |int, x|
int > x
end
+
expect(@ability.can?(:read, 2, 1)).to be(true)
expect(@ability.can?(:read, 2, 3)).to be(false)
+ expect(@ability.can?(:read, {:any => [4, 5]}, 3)).to be(true)
+ expect(@ability.can?(:read, {:any => [2, 3]}, 3)).to be(false)
end
it "uses conditions as third parameter and determine abilities from it" do
@ability.can :read, Range, :begin => 1, :end => 3
+
expect(@ability.can?(:read, 1..3)).to be(true)
expect(@ability.can?(:read, 1..4)).to be(false)
expect(@ability.can?(:read, Range)).to be(true)
+ expect(@ability.can?(:read, {:any => [1..3, 1..4]})).to be(true)
+ expect(@ability.can?(:read, {:any => [1..4, 2..4]})).to be(false)
end
it "allows an array of options in conditions hash" do
@ability.can :read, Range, :begin => [1, 3, 5]
+
expect(@ability.can?(:read, 1..3)).to be(true)
expect(@ability.can?(:read, 2..4)).to be(false)
expect(@ability.can?(:read, 3..5)).to be(true)
+ expect(@ability.can?(:read, {:any => [2..4, 3..5]})).to be(true)
+ expect(@ability.can?(:read, {:any => [2..4, 2..5]})).to be(false)
end
it "allows a range of options in conditions hash" do
@@ -310,17 +336,23 @@ class A; include B; end
it "checks permissions through association when passing a hash of subjects" do
@ability.can :read, Range, :string => {:length => 3}
+
expect(@ability.can?(:read, "foo" => Range)).to be(true)
expect(@ability.can?(:read, "foobar" => Range)).to be(false)
expect(@ability.can?(:read, 123 => Range)).to be(true)
+ expect(@ability.can?(:read, {:any => [{"foo" => Range}, {"foobar" => Range}]})).to be(true)
+ expect(@ability.can?(:read, {:any => [{"food" => Range}, {"foobar" => Range}]})).to be(false)
end
it "checks permissions correctly when passing a hash of subjects with multiple definitions" do
@ability.can :read, Range, :string => {:length => 4}
@ability.can [:create, :read], Range, :string => {:upcase => 'FOO'}
+
expect(@ability.can?(:read, "foo" => Range)).to be(true)
expect(@ability.can?(:read, "foobar" => Range)).to be(false)
expect(@ability.can?(:read, 1234 => Range)).to be(true)
+ expect(@ability.can?(:read, {:any => [{"foo" => Range}, {"foobar" => Range}]})).to be(true)
+ expect(@ability.can?(:read, {:any => [{"foo.bar" => Range}, {"foobar" => Range}]})).to be(false)
end
it "allows to check ability on Hash-like object" do
View
968 spec/cancan/controller_resource_spec.rb
@@ -1,554 +1,626 @@
require "spec_helper"
describe CanCan::ControllerResource do
+ let(:ability) { Ability.new(nil) }
+ let(:params) { HashWithIndifferentAccess.new(:controller => "models") }
+ let(:controller_class) { Class.new }
+ let(:controller) { controller_class.new }
+
before(:each) do
- @params = HashWithIndifferentAccess.new(:controller => "projects")
- @controller_class = Class.new
- @controller = @controller_class.new
- @ability = Ability.new(nil)
- allow(@controller).to receive(:params) { @params }
- allow(@controller).to receive(:current_ability) { @ability }
- allow(@controller_class).to receive(:cancan_skipper) { {:authorize => {}, :load => {}} }
- end
+ class Model
+ attr_accessor :name
+
+ def initialize(attributes={})
+ attributes.each do |attribute, value|
+ send("#{attribute}=", value)
+ end
+ end
+ end
- it "loads the resource into an instance variable if params[:id] is specified" do
- project = Project.create!
- @params.merge!(:action => "show", :id => project.id)
- resource = CanCan::ControllerResource.new(@controller)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(project)
+ allow(controller).to receive(:params) { params }
+ allow(controller).to receive(:current_ability) { ability }
+ allow(controller_class).to receive(:cancan_skipper) { {:authorize => {}, :load => {}} }
end
- it "does not load resource into an instance variable if already set" do
- @params.merge!(:action => "show", :id => "123")
- @controller.instance_variable_set(:@project, :some_project)
- resource = CanCan::ControllerResource.new(@controller)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(:some_project)
- end
+ context "on build actions" do
+ before :each do
+ params.merge!(:action => "new")
+ end
- it "loads resource for namespaced controller" do
- project = Project.create!
- @params.merge!(:controller => "admin/projects", :action => "show", :id => project.id)
- resource = CanCan::ControllerResource.new(@controller)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(project)
- end
+ it "builds a new resource with attributes from current ability" do
+ ability.can(:create, Model, :name => "from conditions")
+ resource = CanCan::ControllerResource.new(controller)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model).name).to eq("from conditions")
+ end
+
+ it "overrides initial attributes with params" do
+ params.merge!(:model => {:name => "from params"})
+ ability.can(:create, Model, :name => "from conditions")
+ resource = CanCan::ControllerResource.new(controller)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model).name).to eq("from params")
+ end
- it "attempts to load a resource with the same namespace as the controller when using :: for namespace" do
- module MyEngine
- class Project < ::Project; end
+ it "builds a resource when on custom new action even when params[:id] exists" do
+ params.merge!(:action => "build", :id => "123")
+ allow(Model).to receive(:new) { :some_model }
+ resource = CanCan::ControllerResource.new(controller, :new => :build)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(:some_model)
end
- project = MyEngine::Project.create!
- @params.merge!(:controller => "MyEngine::ProjectsController", :action => "show", :id => project.id)
- resource = CanCan::ControllerResource.new(@controller)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(project)
+ it "only authorizes :show action on parent resource" do
+ model = Model.new
+ allow(Model).to receive(:find).with("123") { model }
+
+ params.merge!(:model_id => 123)
+ allow(controller).to receive(:authorize!).with(:show, model) { raise CanCan::AccessDenied }
+ resource = CanCan::ControllerResource.new(controller, :model, :parent => true)
+ expect { resource.load_and_authorize_resource }.to raise_error(CanCan::AccessDenied)
+ end
end
- # Rails includes namespace in params, see issue #349
- it "creates through the namespaced params" do
- module MyEngine
- class Project < ::Project; end
+ context "on create actions" do
+ before :each do
+ params.merge!(:action => 'create')
end
- @params.merge!(:controller => "MyEngine::ProjectsController", :action => "create", :my_engine_project => {:name => "foobar"})
- resource = CanCan::ControllerResource.new(@controller)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project).name).to eq("foobar")
- end
+ # Rails includes namespace in params, see issue #349
+ it "creates through the namespaced params" do
+ module MyEngine
+ class Model < ::Model; end
+ end
- it "loads resource for namespaced controller when using '::' for namespace" do
- project = Project.create!
- @params.merge!(:controller => "Admin::ProjectsController", :action => "show", :id => project.id)
- resource = CanCan::ControllerResource.new(@controller)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(project)
- end
+ params.merge!(:controller => "MyEngine::ModelsController", :my_engine_model => {:name => "foobar"})
+ resource = CanCan::ControllerResource.new(controller)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model).name).to eq("foobar")
+ end
- it "has the specified nested resource_class when using / for namespace" do
- module Admin
- class Dashboard; end
+ it "builds a new resource with hash if params[:id] is not specified" do
+ params.merge!(:model => {:name => "foobar"})
+ resource = CanCan::ControllerResource.new(controller)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model).name).to eq("foobar")
end
- @ability.can(:index, "admin/dashboard")
- @params.merge!(:controller => "admin/dashboard", :action => "index")
- resource = CanCan::ControllerResource.new(@controller, :authorize => true)
- expect(resource.send(:resource_class)).to eq(Admin::Dashboard)
- end
- it "builds a new resource with hash if params[:id] is not specified" do
- @params.merge!(:action => "create", :project => {:name => "foobar"})
- resource = CanCan::ControllerResource.new(@controller)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project).name).to eq("foobar")
- end
+ it "builds a new resource for namespaced model with hash if params[:id] is not specified" do
+ module Sub
+ class Model < ::Model; end
+ end
- it "builds a new resource for namespaced model with hash if params[:id] is not specified" do
- @params.merge!(:action => "create", 'sub_project' => {:name => "foobar"})
- resource = CanCan::ControllerResource.new(@controller, :class => ::Sub::Project)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project).name).to eq("foobar")
- end
+ params.merge!('sub_model' => {:name => "foobar"})
+ resource = CanCan::ControllerResource.new(controller, :class => ::Sub::Model)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model).name).to eq("foobar")
+ end
- it "builds a new resource for namespaced controller and namespaced model with hash if params[:id] is not specified" do
- @params.merge!(:controller => "Admin::SubProjectsController", :action => "create", 'sub_project' => {:name => "foobar"})
- resource = CanCan::ControllerResource.new(@controller, :class => Project)
- resource.load_resource
- expect(@controller.instance_variable_get(:@sub_project).name).to eq("foobar")
- end
+ it "builds a new resource for namespaced controller and namespaced model with hash if params[:id] is not specified" do
+ params.merge!(:controller => "Admin::SubModelsController", 'sub_model' => {:name => "foobar"})
+ resource = CanCan::ControllerResource.new(controller, :class => Model)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@sub_model).name).to eq("foobar")
+ end
- it "builds a new resource with attributes from current ability" do
- @params.merge!(:action => "new")
- @ability.can(:create, Project, :name => "from conditions")
- resource = CanCan::ControllerResource.new(@controller)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project).name).to eq("from conditions")
- end
+ it "does not build record through has_one association with :singleton option because it can cause it to delete it in the database" do
+ category = Class.new
+ allow_any_instance_of(Model).to receive('category=').with(category)
+ allow_any_instance_of(Model).to receive('category') { category }
- it "overrides initial attributes with params" do
- @params.merge!(:action => "new", :project => {:name => "from params"})
- @ability.can(:create, Project, :name => "from conditions")
- resource = CanCan::ControllerResource.new(@controller)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project).name).to eq("from params")
- end
+ params.merge!(:model => {:name => "foobar"})
- it "builds a collection when on index action when class responds to accessible_by" do
- allow(Project).to receive(:accessible_by).with(@ability, :index) { :found_projects }
- @params[:action] = "index"
- resource = CanCan::ControllerResource.new(@controller, :project)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to be_nil
- expect(@controller.instance_variable_get(:@projects)).to eq(:found_projects)
- end
+ controller.instance_variable_set(:@category, category)
+ resource = CanCan::ControllerResource.new(controller, :through => :category, :singleton => true)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model).name).to eq("foobar")
+ expect(controller.instance_variable_get(:@model).category).to eq(category)
+ end
- it "does not build a collection when on index action when class does not respond to accessible_by" do
- @params[:action] = "index"
- resource = CanCan::ControllerResource.new(@controller)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to be_nil
- expect(@controller.instance_variable_defined?(:@projects)).to be_falsy
- end
+ it "builds record through has_one association with :singleton and :shallow options" do
+ params.merge!(:model => {:name => "foobar"})
+ resource = CanCan::ControllerResource.new(controller, :through => :category, :singleton => true, :shallow => true)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model).name).to eq("foobar")
+ end
- it "does not use accessible_by when defining abilities through a block" do
- allow(Project).to receive(:accessible_by).with(@ability) { :found_projects }
- @params[:action] = "index"
- @ability.can(:read, Project) { |p| false }
- resource = CanCan::ControllerResource.new(@controller)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to be_nil
- expect(@controller.instance_variable_defined?(:@projects)).to be_falsy
+ context "with a strong parameters method" do
+ it "accepts and uses the specified symbol for santitizing input" do
+ params.merge!(:controller => "model")
+ allow(controller).to receive(:resource_params).and_return(:resource => 'params')
+ allow(controller).to receive(:model_params).and_return(:model => 'params')
+ allow(controller).to receive(:create_params).and_return(:create => 'params')
+ allow(controller).to receive(:custom_params).and_return(:custom => 'params')
+ resource = CanCan::ControllerResource.new(controller, {:param_method => :custom_params})
+ expect(resource.send("resource_params")).to eq(:custom => 'params')
+ end
+
+ it "accepts the specified string for sanitizing input" do
+ params.merge!(:controller => "model")
+ resource = CanCan::ControllerResource.new(controller, {:param_method => "{:custom => 'params'}"})
+ expect(resource.send("resource_params")).to eq(:custom => 'params')
+ end
+
+ it "accepts the specified proc for sanitizing input" do
+ params.merge!(:controller => "model")
+ resource = CanCan::ControllerResource.new(controller, {:param_method => Proc.new { |c| {:custom => 'params'}}})
+ expect(resource.send("resource_params")).to eq(:custom => 'params')
+ end
+
+ it "prefers to use the create_params method for santitizing input" do
+ params.merge!(:controller => "model")
+ allow(controller).to receive(:resource_params).and_return(:resource => 'params')
+ allow(controller).to receive(:model_params).and_return(:model => 'params')
+ allow(controller).to receive(:create_params).and_return(:create => 'params')
+ allow(controller).to receive(:custom_params).and_return(:custom => 'params')
+ resource = CanCan::ControllerResource.new(controller)
+ expect(resource.send("resource_params")).to eq(:create => 'params')
+ end
+
+ it "prefers to use the <model_name>_params method for santitizing input if create is not found" do
+ params.merge!(:controller => "model")
+ allow(controller).to receive(:resource_params).and_return(:resource => 'params')
+ allow(controller).to receive(:model_params).and_return(:model => 'params')
+ allow(controller).to receive(:custom_params).and_return(:custom => 'params')
+ resource = CanCan::ControllerResource.new(controller)
+ expect(resource.send("resource_params")).to eq(:model => 'params')
+ end
+
+ it "prefers to use the resource_params method for santitizing input if create or model is not found" do
+ params.merge!(:controller => "model")
+ allow(controller).to receive(:resource_params).and_return(:resource => 'params')
+ allow(controller).to receive(:custom_params).and_return(:custom => 'params')
+ resource = CanCan::ControllerResource.new(controller)
+ expect(resource.send("resource_params")).to eq(:resource => 'params')
+ end
+ end
end
- it "does not authorize single resource in collection action" do
- @params[:action] = "index"
- @controller.instance_variable_set(:@project, :some_project)
- allow(@controller).to receive(:authorize!).with(:index, Project) { raise CanCan::AccessDenied }
- resource = CanCan::ControllerResource.new(@controller)
- expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
- end
+ context "on collection actions" do
+ before :each do
+ params[:action] = 'index'
+ end
- it "authorizes parent resource in collection action" do
- @params[:action] = "index"
- @controller.instance_variable_set(:@category, :some_category)
- allow(@controller).to receive(:authorize!).with(:show, :some_category) { raise CanCan::AccessDenied }
- resource = CanCan::ControllerResource.new(@controller, :category, :parent => true)
- expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
- end
+ it "builds a collection when on index action when class responds to accessible_by" do
+ allow(Model).to receive(:accessible_by).with(ability, :index) { :found_models }
- it "performs authorization using controller action and loaded model" do
- @params.merge!(:action => "show", :id => "123")
- @controller.instance_variable_set(:@project, :some_project)
- allow(@controller).to receive(:authorize!).with(:show, :some_project) { raise CanCan::AccessDenied }
- resource = CanCan::ControllerResource.new(@controller)
- expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
- end
+ resource = CanCan::ControllerResource.new(controller, :model)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to be_nil
+ expect(controller.instance_variable_get(:@models)).to eq(:found_models)
+ end
- it "performs authorization using controller action and non loaded model" do
- @params.merge!(:action => "show", :id => "123")
- allow(@controller).to receive(:authorize!).with(:show, Project) { raise CanCan::AccessDenied }
- resource = CanCan::ControllerResource.new(@controller)
- expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
- end
+ it "does not build a collection when on index action when class does not respond to accessible_by" do
+ resource = CanCan::ControllerResource.new(controller)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to be_nil
+ expect(controller.instance_variable_defined?(:@models)).to be(false)
+ end
- it "calls load_resource and authorize_resource for load_and_authorize_resource" do
- @params.merge!(:action => "show", :id => "123")
- resource = CanCan::ControllerResource.new(@controller)
- expect(resource).to receive(:load_resource)
- expect(resource).to receive(:authorize_resource)
- resource.load_and_authorize_resource
- end
+ it "does not use accessible_by when defining abilities through a block" do
+ allow(Model).to receive(:accessible_by).with(ability) { :found_models }
- it "does not build a single resource when on custom collection action even with id" do
- @params.merge!(:action => "sort", :id => "123")
- resource = CanCan::ControllerResource.new(@controller, :collection => [:sort, :list])
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to be_nil
- end
+ ability.can(:read, Model) { |p| false }
+ resource = CanCan::ControllerResource.new(controller)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to be_nil
+ expect(controller.instance_variable_defined?(:@models)).to be(false)
+ end
- it "loads a collection resource when on custom action with no id param" do
- allow(Project).to receive(:accessible_by).with(@ability, :sort) { :found_projects }
- @params[:action] = "sort"
- resource = CanCan::ControllerResource.new(@controller)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to be_nil
- expect(@controller.instance_variable_get(:@projects)).to eq(:found_projects)
- end
+ it "does not authorize single resource in collection action" do
+ allow(controller).to receive(:authorize!).with(:index, Model) { raise CanCan::AccessDenied }
+ resource = CanCan::ControllerResource.new(controller)
- it "builds a resource when on custom new action even when params[:id] exists" do
- @params.merge!(:action => "build", :id => "123")
- allow(Project).to receive(:new) { :some_project }
- resource = CanCan::ControllerResource.new(@controller, :new => :build)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(:some_project)
- end
+ expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
+ end
- it "does not try to load resource for other action if params[:id] is undefined" do
- @params[:action] = "list"
- resource = CanCan::ControllerResource.new(@controller)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to be_nil
- end
+ it "authorizes parent resource in collection action" do
+ controller.instance_variable_set(:@category, :some_category)
+ allow(controller).to receive(:authorize!).with(:show, :some_category) { raise CanCan::AccessDenied }
- it "is a parent resource when name is provided which doesn't match controller" do
- resource = CanCan::ControllerResource.new(@controller, :category)
- expect(resource).to be_parent
- end
+ resource = CanCan::ControllerResource.new(controller, :category, :parent => true)
+ expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
+ end
- it "does not be a parent resource when name is provided which matches controller" do
- resource = CanCan::ControllerResource.new(@controller, :project)
- expect(resource).to_not be_parent
- end
+ it "has the specified nested resource_class when using / for namespace" do
+ module Admin
+ class Dashboard; end
+ end
+ ability.can(:index, "admin/dashboard")
+ params.merge!(:controller => "admin/dashboard")
+ resource = CanCan::ControllerResource.new(controller, :authorize => true)
+ expect(resource.send(:resource_class)).to eq(Admin::Dashboard)
+ end
- it "is parent if specified in options" do
- resource = CanCan::ControllerResource.new(@controller, :project, {:parent => true})
- expect(resource).to be_parent
- end
+ it "does not build a single resource when on custom collection action even with id" do
+ params.merge!(:action => "sort", :id => "123")
- it "does not be parent if specified in options" do
- resource = CanCan::ControllerResource.new(@controller, :category, {:parent => false})
- expect(resource).to_not be_parent
- end
+ resource = CanCan::ControllerResource.new(controller, :collection => [:sort, :list])
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to be_nil
+ end
- it "has the specified resource_class if 'name' is passed to load_resource" do
- class Section
+ it "loads a collection resource when on custom action with no id param" do
+ allow(Model).to receive(:accessible_by).with(ability, :sort) { :found_models }
+ params[:action] = "sort"
+ resource = CanCan::ControllerResource.new(controller)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to be_nil
+ expect(controller.instance_variable_get(:@models)).to eq(:found_models)
end
- resource = CanCan::ControllerResource.new(@controller, :section)
- expect(resource.send(:resource_class)).to eq(Section)
- end
+ it "loads parent resource through proper id parameter" do
+ model = Model.new
+ allow(Model).to receive(:find).with("1") { model }
- it "loads parent resource through proper id parameter" do
- project = Project.create!
- @params.merge!(:controller => "categories", :action => "index", :project_id => project.id)
- resource = CanCan::ControllerResource.new(@controller, :project)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(project)
- end
+ params.merge!(:controller => "categories", :model_id => 1)
+ resource = CanCan::ControllerResource.new(controller, :model)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(model)
+ end
- it "loads resource through the association of another parent resource using instance variable" do
- @params.merge!(:action => "show", :id => "123")
- category = double(:projects => {})
- @controller.instance_variable_set(:@category, category)
- allow(category.projects).to receive(:find).with("123") { :some_project }
- resource = CanCan::ControllerResource.new(@controller, :through => :category)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(:some_project)
+ it "authorizes nested resource through parent association on index action" do
+ controller.instance_variable_set(:@category, category = double)
+ allow(controller).to receive(:authorize!).with(:index, category => Model) { raise CanCan::AccessDenied }
+ resource = CanCan::ControllerResource.new(controller, :through => :category)
+ expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
+ end
end
- it "loads resource through the custom association name" do
- @params.merge!(:action => "show", :id => "123")
- category = double(:custom_projects => {})
- @controller.instance_variable_set(:@category, category)
- allow(category.custom_projects).to receive(:find).with("123") { :some_project }
- resource = CanCan::ControllerResource.new(@controller, :through => :category, :through_association => :custom_projects)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(:some_project)
- end
+ context "on instance read actions" do
+ before :each do
+ params.merge!(:action => "show", :id => "123")
+ end
- it "loads resource through the association of another parent resource using method" do
- @params.merge!(:action => "show", :id => "123")
- category = double(:projects => {})
- allow(@controller).to receive(:category) { category }
- allow(category.projects).to receive(:find).with("123") { :some_project }
- resource = CanCan::ControllerResource.new(@controller, :through => :category)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(:some_project)
- end
+ it "loads the resource into an instance variable if params[:id] is specified" do
+ model = Model.new
+ allow(Model).to receive(:find).with("123") { model }
- it "does not load through parent resource if instance isn't loaded when shallow" do
- project = Project.create!
- @params.merge!(:action => "show", :id => project.id)
- resource = CanCan::ControllerResource.new(@controller, :through => :category, :shallow => true)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(project)
- end
+ resource = CanCan::ControllerResource.new(controller)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(model)
+ end
- it "raises AccessDenied when attempting to load resource through nil" do
- project = Project.create!
- @params.merge!(:action => "show", :id => project.id)
- resource = CanCan::ControllerResource.new(@controller, :through => :category)
- expect {
+ it "does not load resource into an instance variable if already set" do
+ controller.instance_variable_set(:@model, :some_model)
+ resource = CanCan::ControllerResource.new(controller)
resource.load_resource
- }.to raise_error(CanCan::AccessDenied) { |exception|
- expect(exception.action).to eq(:show)
- expect(exception.subject).to eq(Project)
- }
- expect(@controller.instance_variable_get(:@project)).to be_nil
- end
+ expect(controller.instance_variable_get(:@model)).to eq(:some_model)
+ end
- it "authorizes nested resource through parent association on index action" do
- @params.merge!(:action => "index")
- @controller.instance_variable_set(:@category, category = double)
- allow(@controller).to receive(:authorize!).with(:index, category => Project) { raise CanCan::AccessDenied }
- resource = CanCan::ControllerResource.new(@controller, :through => :category)
- expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
- end
+ it "loads resource for namespaced controller" do
+ model = Model.new
+ allow(Model).to receive(:find).with("123") { model }
+ params.merge!(:controller => "admin/models")
- it "loads through first matching if multiple are given" do
- @params.merge!(:action => "show", :id => "123")
- category = double(:projects => {})
- @controller.instance_variable_set(:@category, category)
- allow(category.projects).to receive(:find).with("123") { :some_project }
- resource = CanCan::ControllerResource.new(@controller, :through => [:category, :user])
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(:some_project)
- end
+ resource = CanCan::ControllerResource.new(controller)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(model)
+ end
- it "finds record through has_one association with :singleton option without id param" do
- @params.merge!(:action => "show", :id => nil)
- category = double(:project => :some_project)
- @controller.instance_variable_set(:@category, category)
- resource = CanCan::ControllerResource.new(@controller, :through => :category, :singleton => true)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(:some_project)
- end
+ it "attempts to load a resource with the same namespace as the controller when using :: for namespace" do
+ module MyEngine
+ class Model < ::Model; end
+ end
- it "does not build record through has_one association with :singleton option because it can cause it to delete it in the database" do
- @params.merge!(:action => "create", :project => {:name => "foobar"})
- category = Category.new
- @controller.instance_variable_set(:@category, category)
- resource = CanCan::ControllerResource.new(@controller, :through => :category, :singleton => true)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project).name).to eq("foobar")
- expect(@controller.instance_variable_get(:@project).category).to eq(category)
- end
+ model = MyEngine::Model.new
+ allow(MyEngine::Model).to receive(:find).with("123") { model }
- it "finds record through has_one association with :singleton and :shallow options" do
- project = Project.create!
- @params.merge!(:action => "show", :id => project.id)
- resource = CanCan::ControllerResource.new(@controller, :through => :category, :singleton => true, :shallow => true)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(project)
- end
+ params.merge!(:controller => "MyEngine::ModelsController")
+ resource = CanCan::ControllerResource.new(controller)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(model)
+ end
- it "builds record through has_one association with :singleton and :shallow options" do
- @params.merge!(:action => "create", :project => {:name => "foobar"})
- resource = CanCan::ControllerResource.new(@controller, :through => :category, :singleton => true, :shallow => true)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project).name).to eq("foobar")
- end
+ it "loads resource for namespaced controller when using '::' for namespace" do
+ model = Model.new
+ allow(Model).to receive(:find).with("123") { model }
- it "only authorizes :show action on parent resource" do
- project = Project.create!
- @params.merge!(:action => "new", :project_id => project.id)
- allow(@controller).to receive(:authorize!).with(:show, project) { raise CanCan::AccessDenied }
- resource = CanCan::ControllerResource.new(@controller, :project, :parent => true)
- expect { resource.load_and_authorize_resource }.to raise_error(CanCan::AccessDenied)
- end
+ params.merge!(:controller => "Admin::ModelsController")
+ resource = CanCan::ControllerResource.new(controller)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(model)
+ end
- it "loads the model using a custom class" do
- project = Project.create!
- @params.merge!(:action => "show", :id => project.id)
- resource = CanCan::ControllerResource.new(@controller, :class => Project)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(project)
- end
+ it "performs authorization using controller action and loaded model" do
+ controller.instance_variable_set(:@model, :some_model)
+ allow(controller).to receive(:authorize!).with(:show, :some_model) { raise CanCan::AccessDenied }
- it "loads the model using a custom namespaced class" do
- project = Sub::Project.create!
- @params.merge!(:action => "show", :id => project.id)
- resource = CanCan::ControllerResource.new(@controller, :class => ::Sub::Project)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(project)
- end
+ resource = CanCan::ControllerResource.new(controller)
+ expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
+ end
+
+ it "performs authorization using controller action and non loaded model" do
+ allow(controller).to receive(:authorize!).with(:show, Model) { raise CanCan::AccessDenied }
+ resource = CanCan::ControllerResource.new(controller)
+ expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
+ end
+
+ it "calls load_resource and authorize_resource for load_and_authorize_resource" do
+ resource = CanCan::ControllerResource.new(controller)
+ expect(resource).to receive(:load_resource)
+ expect(resource).to receive(:authorize_resource)
+ resource.load_and_authorize_resource
+ end
+
+ it "loads resource through the association of another parent resource using instance variable" do
+ category = double(:models => {})
+ controller.instance_variable_set(:@category, category)
+ allow(category.models).to receive(:find).with("123") { :some_model }
+ resource = CanCan::ControllerResource.new(controller, :through => :category)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(:some_model)
+ end
+
+ it "loads resource through the custom association name" do
+ category = double(:custom_models => {})
+ controller.instance_variable_set(:@category, category)
+ allow(category.custom_models).to receive(:find).with("123") { :some_model }
+ resource = CanCan::ControllerResource.new(controller, :through => :category, :through_association => :custom_models)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(:some_model)
+ end
+
+ it "loads resource through the association of another parent resource using method" do
+ category = double(:models => {})
+ allow(controller).to receive(:category) { category }
+ allow(category.models).to receive(:find).with("123") { :some_model }
+ resource = CanCan::ControllerResource.new(controller, :through => :category)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(:some_model)
+ end
+
+ it "does not load through parent resource if instance isn't loaded when shallow" do
+ model = Model.new
+ allow(Model).to receive(:find).with("123") { model }
+
+ resource = CanCan::ControllerResource.new(controller, :through => :category, :shallow => true)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(model)
+ end
+
+ it "raises AccessDenied when attempting to load resource through nil" do
+ resource = CanCan::ControllerResource.new(controller, :through => :category)
+ expect {
+ resource.load_resource
+ }.to raise_error(CanCan::AccessDenied) { |exception|
+ expect(exception.action).to eq(:show)
+ expect(exception.subject).to eq(Model)
+ }
+ expect(controller.instance_variable_get(:@model)).to be_nil
+ end
+
+ it "loads through first matching if multiple are given" do
+ category = double(:models => {})
+ controller.instance_variable_set(:@category, category)
+ allow(category.models).to receive(:find).with("123") { :some_model }
+
+ resource = CanCan::ControllerResource.new(controller, :through => [:category, :user])
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(:some_model)
+ end
+
+ it "finds record through has_one association with :singleton option without id param" do
+ params.merge!(:id => nil)
+
+ category = double(:model => :some_model)
+ controller.instance_variable_set(:@category, category)
+ resource = CanCan::ControllerResource.new(controller, :through => :category, :singleton => true)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(:some_model)
+ end
+
+ it "does not try to load resource for other action if params[:id] is undefined" do
+ params.merge!(:action => 'list', :id => nil)
+ resource = CanCan::ControllerResource.new(controller)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to be_nil
+ end
+
+ it "finds record through has_one association with :singleton and :shallow options" do
+ model = Model.new
+ allow(Model).to receive(:find).with("123") { model }
+
+ resource = CanCan::ControllerResource.new(controller, :through => :category, :singleton => true, :shallow => true)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(model)
+ end
+
+ it "loads the model using a custom class" do
+ model = Model.new
+ allow(Model).to receive(:find).with("123") { model }
+
+ resource = CanCan::ControllerResource.new(controller, :class => Model)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(model)
+ end
+
+ it "loads the model using a custom namespaced class" do
+ module Sub
+ class Model < ::Model; end
+ end
+
+ model = Sub::Model.new
+ allow(Sub::Model).to receive(:find).with("123") { model }
+
+ resource = CanCan::ControllerResource.new(controller, :class => ::Sub::Model)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(model)
+ end
- it "authorizes based on resource name if class is false" do
- @params.merge!(:action => "show", :id => "123")
- allow(@controller).to receive(:authorize!).with(:show, :project) { raise CanCan::AccessDenied }
- resource = CanCan::ControllerResource.new(@controller, :class => false)
- expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
+ it "authorizes based on resource name if class is false" do
+ allow(controller).to receive(:authorize!).with(:show, :model) { raise CanCan::AccessDenied }
+ resource = CanCan::ControllerResource.new(controller, :class => false)
+ expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
+ end
+
+ it "loads and authorize using custom instance name" do
+ model = Model.new
+ allow(Model).to receive(:find).with("123") { model }
+
+ allow(controller).to receive(:authorize!).with(:show, model) { raise CanCan::AccessDenied }
+ resource = CanCan::ControllerResource.new(controller, :instance_name => :custom_model)
+ expect { resource.load_and_authorize_resource }.to raise_error(CanCan::AccessDenied)
+ expect(controller.instance_variable_get(:@custom_model)).to eq(model)
+ end
+
+ it "loads resource using custom ID param" do
+ model = Model.new
+ allow(Model).to receive(:find).with("123") { model }
+
+ params.merge!(:the_model => 123)
+ resource = CanCan::ControllerResource.new(controller, :id_param => :the_model)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(model)
+ end
+
+ # CVE-2012-5664
+ it "always converts id param to string" do
+ params.merge!(:the_model => { :malicious => "I am" })
+ resource = CanCan::ControllerResource.new(controller, :id_param => :the_model)
+ expect(resource.send(:id_param).class).to eq(String)
+ end
+
+ it "should id param return nil if param is nil" do
+ params.merge!(:the_model => nil)
+ resource = CanCan::ControllerResource.new(controller, :id_param => :the_model)
+ expect(resource.send(:id_param)).to be_nil
+ end
+
+ it "loads resource using custom find_by attribute" do
+ model = Model.new
+ allow(Model).to receive(:name).with('foo') { model }
+
+ params.merge!(:action => "show", :id => "foo")
+ resource = CanCan::ControllerResource.new(controller, :find_by => :name)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(model)
+ end
+
+ it "allows full find method to be passed into find_by option" do
+ model = Model.new
+ allow(Model).to receive(:find_by_name).with('foo') { model }
+
+ params.merge!(:action => "show", :id => "foo")
+ resource = CanCan::ControllerResource.new(controller, :find_by => :find_by_name)
+ resource.load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(model)
+ end
end
- it "loads and authorize using custom instance name" do
- project = Project.create!
- @params.merge!(:action => "show", :id => project.id)
- allow(@controller).to receive(:authorize!).with(:show, project) { raise CanCan::AccessDenied }
- resource = CanCan::ControllerResource.new(@controller, :instance_name => :custom_project)
- expect { resource.load_and_authorize_resource }.to raise_error(CanCan::AccessDenied)
- expect(@controller.instance_variable_get(:@custom_project)).to eq(project)
+ context "on update actions" do
+ before :each do
+ params.merge!(:action => 'update')
+ end
+
+ context "with a strong parameters method" do
+ it "only calls the santitize method with actions matching param_actions" do
+ allow(controller).to receive(:resource_params).and_return(:resource => 'params')
+ resource = CanCan::ControllerResource.new(controller)
+ allow(resource).to receive(:param_actions) { [:create] }
+
+ expect(controller).not_to receive(:send).with(:resource_params)
+ resource.send("resource_params")
+ end
+
+ it "uses the proper action param based on the action" do
+ allow(controller).to receive(:create_params).and_return(:create => 'params')
+ allow(controller).to receive(:update_params).and_return(:update => 'params')
+ resource = CanCan::ControllerResource.new(controller)
+ expect(resource.send("resource_params")).to eq(:update => 'params')
+ end
+ end
end
- it "loads resource using custom ID param" do
- project = Project.create!
- @params.merge!(:action => "show", :the_project => project.id)
- resource = CanCan::ControllerResource.new(@controller, :id_param => :the_project)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(project)
+ it "is a parent resource when name is provided which doesn't match controller" do
+ resource = CanCan::ControllerResource.new(controller, :category)
+ expect(resource).to be_parent
end
- # CVE-2012-5664
- it "always converts id param to string" do
- @params.merge!(:action => "show", :the_project => { :malicious => "I am" })
- resource = CanCan::ControllerResource.new(@controller, :id_param => :the_project)
- expect(resource.send(:id_param).class).to eq(String)
+ it "does not be a parent resource when name is provided which matches controller" do
+ resource = CanCan::ControllerResource.new(controller, :model)
+ expect(resource).to_not be_parent
end
- it "should id param return nil if param is nil" do
- @params.merge!(:action => "show", :the_project => nil)
- resource = CanCan::ControllerResource.new(@controller, :id_param => :the_project)
- expect(resource.send(:id_param)).to be_nil
+ it "is parent if specified in options" do
+ resource = CanCan::ControllerResource.new(controller, :model, {:parent => true})
+ expect(resource).to be_parent
end
- it "loads resource using custom find_by attribute" do
- project = Project.create!(:name => "foo")
- @params.merge!(:action => "show", :id => "foo")
- resource = CanCan::ControllerResource.new(@controller, :find_by => :name)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(project)
+ it "does not be parent if specified in options" do
+ resource = CanCan::ControllerResource.new(controller, :category, {:parent => false})
+ expect(resource).to_not be_parent
end
- it "allows full find method to be passed into find_by option" do
- project = Project.create!(:name => "foo")
- @params.merge!(:action => "show", :id => "foo")
- resource = CanCan::ControllerResource.new(@controller, :find_by => :find_by_name)
- resource.load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(project)
+ it "has the specified resource_class if 'name' is passed to load_resource" do
+ class Section; end
+ resource = CanCan::ControllerResource.new(controller, :section)
+ expect(resource.send(:resource_class)).to eq(Section)
end
it "raises ImplementationRemoved when adding :name option" do
expect {
- CanCan::ControllerResource.new(@controller, :name => :foo)
+ CanCan::ControllerResource.new(controller, :name => :foo)
}.to raise_error(CanCan::ImplementationRemoved)
end
it "raises ImplementationRemoved exception when specifying :resource option since it is no longer used" do
expect {
- CanCan::ControllerResource.new(@controller, :resource => Project)
+ CanCan::ControllerResource.new(controller, :resource => Model)
}.to raise_error(CanCan::ImplementationRemoved)
end
it "raises ImplementationRemoved exception when passing :nested option" do
expect {
- CanCan::ControllerResource.new(@controller, :nested => :project)
+ CanCan::ControllerResource.new(controller, :nested => :model)
}.to raise_error(CanCan::ImplementationRemoved)
end
it "skips resource behavior for :only actions in array" do
- allow(@controller_class).to receive(:cancan_skipper) { {:load => {nil => {:only => [:index, :show]}}} }
- @params.merge!(:action => "index")
- expect(CanCan::ControllerResource.new(@controller).skip?(:load)).to be(true)
- expect(CanCan::ControllerResource.new(@controller, :some_resource).skip?(:load)).to be_falsy
- @params.merge!(:action => "show")
- expect(CanCan::ControllerResource.new(@controller).skip?(:load)).to be(true)
- @params.merge!(:action => "other_action")
- expect(CanCan::ControllerResource.new(@controller).skip?(:load)).to be_falsy
+ allow(controller_class).to receive(:cancan_skipper) { {:load => {nil => {:only => [:index, :show]}}} }
+ params.merge!(:action => "index")
+ expect(CanCan::ControllerResource.new(controller).skip?(:load)).to be(true)
+ expect(CanCan::ControllerResource.new(controller, :some_resource).skip?(:load)).to be(false)
+ params.merge!(:action => "show")
+ expect(CanCan::ControllerResource.new(controller).skip?(:load)).to be(true)
+ params.merge!(:action => "other_action")
+ expect(CanCan::ControllerResource.new(controller).skip?(:load)).to be_falsey
end
it "skips resource behavior for :only one action on resource" do
- allow(@controller_class).to receive(:cancan_skipper) { {:authorize => {:project => {:only => :index}}} }
- @params.merge!(:action => "index")
- expect(CanCan::ControllerResource.new(@controller).skip?(:authorize)).to be_falsy
- expect(CanCan::ControllerResource.new(@controller, :project).skip?(:authorize)).to be(true)
- @params.merge!(:action => "other_action")
- expect(CanCan::ControllerResource.new(@controller, :project).skip?(:authorize)).to be_falsy
+ allow(controller_class).to receive(:cancan_skipper) { {:authorize => {:model => {:only => :index}}} }
+ params.merge!(:action => "index")
+ expect(CanCan::ControllerResource.new(controller).skip?(:authorize)).to be(false)
+ expect(CanCan::ControllerResource.new(controller, :model).skip?(:authorize)).to be(true)
+ params.merge!(:action => "other_action")
+ expect(CanCan::ControllerResource.new(controller, :model).skip?(:authorize)).to be_falsey
end
it "skips resource behavior :except actions in array" do
- allow(@controller_class).to receive(:cancan_skipper) { {:load => {nil => {:except => [:index, :show]}}} }
- @params.merge!(:action => "index")
- expect(CanCan::ControllerResource.new(@controller).skip?(:load)).to be_falsy
- @params.merge!(:action => "show")
- expect(CanCan::ControllerResource.new(@controller).skip?(:load)).to be_falsy
- @params.merge!(:action => "other_action")
- expect(CanCan::ControllerResource.new(@controller).skip?(:load)).to be(true)
- expect(CanCan::ControllerResource.new(@controller, :some_resource).skip?(:load)).to be_falsy
+ allow(controller_class).to receive(:cancan_skipper) { {:load => {nil => {:except => [:index, :show]}}} }
+ params.merge!(:action => "index")
+ expect(CanCan::ControllerResource.new(controller).skip?(:load)).to be_falsey
+ params.merge!(:action => "show")
+ expect(CanCan::ControllerResource.new(controller).skip?(:load)).to be_falsey
+ params.merge!(:action => "other_action")
+ expect(CanCan::ControllerResource.new(controller).skip?(:load)).to be(true)
+ expect(CanCan::ControllerResource.new(controller, :some_resource).skip?(:load)).to be(false)
end
it "skips resource behavior :except one action on resource" do
- allow(@controller_class).to receive(:cancan_skipper) { {:authorize => {:project => {:except => :index}}} }
- @params.merge!(:action => "index")
- expect(CanCan::ControllerResource.new(@controller, :project).skip?(:authorize)).to be_falsy
- @params.merge!(:action => "other_action")
- expect(CanCan::ControllerResource.new(@controller).skip?(:authorize)).to be_falsy
- expect(CanCan::ControllerResource.new(@controller, :project).skip?(:authorize)).to be(true)
+ allow(controller_class).to receive(:cancan_skipper) { {:authorize => {:model => {:except => :index}}} }
+ params.merge!(:action => "index")
+ expect(CanCan::ControllerResource.new(controller, :model).skip?(:authorize)).to be_falsey
+ params.merge!(:action => "other_action")
+ expect(CanCan::ControllerResource.new(controller).skip?(:authorize)).to be(false)
+ expect(CanCan::ControllerResource.new(controller, :model).skip?(:authorize)).to be(true)
end
it "skips loading and authorization" do
- allow(@controller_class).to receive(:cancan_skipper) { {:authorize => {nil => {}}, :load => {nil => {}}} }
- @params.merge!(:action => "new")
- resource = CanCan::ControllerResource.new(@controller)
+ allow(controller_class).to receive(:cancan_skipper) { {:authorize => {nil => {}}, :load => {nil => {}}} }
+ params.merge!(:action => "new")
+ resource = CanCan::ControllerResource.new(controller)
expect { resource.load_and_authorize_resource }.not_to raise_error
- expect(@controller.instance_variable_get(:@project)).to be_nil
- end
-
- context "with a strong parameters method" do
-
- it "only calls the santitize method with actions matching param_actions" do
- @params.merge!(:controller => "project", :action => "update")
- allow(@controller).to receive(:resource_params).and_return(:resource => 'params')
- allow(@controller).to receive(:send)
- resource = CanCan::ControllerResource.new(@controller)
- allow(resource).to receive(:param_actions).and_return([:create])
- expect(@controller).to_not have_received(:send).with(:resource_params)
-
- resource.send("resource_params")
- end
-
- it "uses the specified option for santitizing input" do
- @params.merge!(:controller => "project", :action => "create")
- allow(@controller).to receive(:resource_params).and_return(:resource => 'params')
- allow(@controller).to receive(:project_params).and_return(:model => 'params')
- allow(@controller).to receive(:create_params).and_return(:create => 'params')
- allow(@controller).to receive(:custom_params).and_return(:custom => 'params')
- resource = CanCan::ControllerResource.new(@controller, {:param_method => :custom_params})
- expect(resource.send("resource_params")).to eq(:custom => 'params')
- end
-
- it "prefers to use the create_params method for santitizing input" do
- @params.merge!(:controller => "project", :action => "create")
- allow(@controller).to receive(:resource_params).and_return(:resource => 'params')
- allow(@controller).to receive(:project_params).and_return(:model => 'params')
- allow(@controller).to receive(:create_params).and_return(:create => 'params')
- allow(@controller).to receive(:custom_params).and_return(:custom => 'params')
- resource = CanCan::ControllerResource.new(@controller)
- expect(resource.send("resource_params")).to eq(:create => 'params')
- end
-
- it "uses the proper action param based on the action" do
- @params.merge!(:controller => "project", :action => "update")
- allow(@controller).to receive(:create_params).and_return(:create => 'params')
- allow(@controller).to receive(:update_params).and_return(:update => 'params')
- resource = CanCan::ControllerResource.new(@controller)
- expect(resource.send("resource_params")).to eq(:update => 'params')
- end
-
- it "prefers to use the <model_name>_params method for santitizing input if create is not found" do
- @params.merge!(:controller => "project", :action => "create")
- allow(@controller).to receive(:resource_params).and_return(:resource => 'params')
- allow(@controller).to receive(:project_params).and_return(:model => 'params')
- allow(@controller).to receive(:custom_params).and_return(:custom => 'params')
- resource = CanCan::ControllerResource.new(@controller)
- expect(resource.send("resource_params")).to eq(:model => 'params')
- end
-
- it "prefers to use the resource_params method for santitizing input if create or model is not found" do
- @params.merge!(:controller => "project", :action => "create")
- allow(@controller).to receive(:resource_params).and_return(:resource => 'params')
- allow(@controller).to receive(:custom_params).and_return(:custom => 'params')
- resource = CanCan::ControllerResource.new(@controller)
- expect(resource.send("resource_params")).to eq(:resource => 'params')
- end
+ expect(controller.instance_variable_get(:@model)).to be_nil
end
end
View
87 spec/cancan/inherited_resource_spec.rb
@@ -1,60 +1,71 @@
require "spec_helper"
describe CanCan::InheritedResource do
+ let(:ability) { Ability.new(nil) }
+ let(:params) { HashWithIndifferentAccess.new(:controller => "models") }
+ let(:controller_class) { Class.new }
+ let(:controller) { controller_class.new }
+
before(:each) do
- @params = HashWithIndifferentAccess.new(:controller => "projects")
- @controller_class = Class.new
- @controller = @controller_class.new
- @ability = Ability.new(nil)
- allow(@controller).to receive(:params).and_return(@params)
- allow(@controller).to receive(:current_ability) { @ability }
- allow(@controller_class).to receive(:cancan_skipper) { {:authorize => {}, :load => {}} }
+ class Model
+ attr_accessor :name
+
+ def initialize(attributes={})
+ attributes.each do |attribute, value|
+ send("#{attribute}=", value)
+ end
+ end
+ end
+
+ allow(controller).to receive(:params) { params }
+ allow(controller).to receive(:current_ability) { ability }
+ allow(controller_class).to receive(:cancan_skipper) { {:authorize => {}, :load => {}} }
end
- it "show loads resource through @controller.resource" do
- @params.merge!(:action => "show", :id => 123)
- allow(@controller).to receive(:resource) { :project_resource }
- CanCan::InheritedResource.new(@controller).load_resource
- expect(@controller.instance_variable_get(:@project)).to eq(:project_resource)
+ it "show loads resource through controller.resource" do
+ params.merge!(:action => "show", :id => 123)
+ allow(controller).to receive(:resource) { :model_resource }
+ CanCan::InheritedResource.new(controller).load_resource
+ expect(controller.instance_variable_get(:@model)).to eq(:model_resource)
end
- it "new loads through @controller.build_resource" do
- @params[:action] = "new"
- allow(@controller).to receive(:build_resource) { :project_resource }