30 Commits
1.6.5 ... 1.6.7

Author SHA1 Message Date
Ryan Bates
9eebeb2155 releasing 1.6.7 with nested resource fix 2011-10-04 15:04:28 -07:00
Ryan Bates
67a3038628 quick fix to get nested resources working again - closes #482 2011-10-04 15:02:59 -07:00
Ryan Bates
26b40f2b49 releasing version 1.6.6 2011-09-28 17:00:32 -07:00
Ryan Bates
98326394f0 removing jruby/rubinius from travis.yml for now until I figure out why they aren't passing 2011-09-28 16:24:03 -07:00
Patrick Morgan
b3fc5add34 Correct "return cant jump across threads" error when using check_authorization() 2011-09-28 16:05:38 -07:00
Ryan Bates
610d7e3ec4 Merge pull request #425 from skhisma/master
Allow custom IDs to be specified when calling load_resource
2011-09-28 13:44:19 -07:00
Ryan Bates
cfb801ed8d Merge pull request #424 from whilefalse/master
Support for namespaced models and engines
2011-09-28 13:34:30 -07:00
Ryan Bates
0edd310c41 Merge pull request #476 from kirkconnell/with_model-version-fix
Specs fail when running in a freshly installed environment.
2011-09-28 11:57:17 -07:00
kirkconnell
5ab7dea2f0 use version 1.x of with_model to avoid errors in class comparisons 2011-09-20 18:43:39 -06:00
Ryan Bates
916f97fbf3 updating Rails gem dev dependency 2011-07-23 19:53:18 -07:00
Ryan Bates
2be3f98e11 removing 1.9.2 from .travis.yml 2011-07-23 19:33:43 -07:00
Ryan Bates
0343f8f0b5 Merge pull request #427 from bai/master
Add .travis.yml for building cancan on a lovely Travis CI service
2011-07-23 19:31:45 -07:00
Ryan Bates
987dce0dc2 Merge pull request #421 from amc-projects/master
Compatibility fix for rspec-instafail and rspec1
2011-07-23 14:46:37 -07:00
Ryan Bates
e0492f5d75 Merge pull request #426 from manuelmeurer/patch-1
Fixed documentation for skip_load_resource and skip_authorize_resource.
2011-07-23 13:31:03 -07:00
beawesomeinstead
0fc67e4d56 These lines are defaults, should be fine this way 2011-07-21 17:02:12 +03:00
beawesomeinstead
d24ef454e0 Run rake instead of rake test 2011-07-21 15:48:20 +03:00
Manuel Meurer
e561532bab Fixed typos. 2011-07-21 05:12:09 -07:00
beawesomeinstead
1ab4e2d48c The first try to make cancan pass on Travis CI 2011-07-21 14:51:47 +03:00
Geoff Parsons
7937a282a3 :id_param option to load_resource allows specification of the param name to find members 2011-07-20 13:42:24 -04:00
Steven Anderson
d35419fa4c Added the needed camelize to recent patch for engines and namespaced models 2011-07-20 10:03:50 +01:00
Steven Anderson
6c497b8dd9 Added support for engines and namespaced models. 2011-07-20 09:31:53 +01:00
Michael MacDonald
3b33b36de9 Compatibility fix for using cancan with rspec-instafail and rspec1 2011-07-19 10:27:08 +10:00
Ryan Bates
600a3e16a5 Merge pull request #410 from psanford/improve_datamapper_loading
Load datamapper class methods via append_extensions.
2011-07-01 13:01:32 -07:00
psanford
ccd7a94d40 Load datamapper class methods via append_extensions.
This relaxes the previous requirement that cancan has to be loaded
before any models are. append_extensions will apply to all
previously loaded models as well as ones loaded after.
2011-07-01 12:48:20 -07:00
Ryan Bates
8f815c422a Merge pull request #409 from nhocki/patch-1
Make CanCan Default Message a translatable text.
2011-07-01 11:44:23 -07:00
Nicolás Hock Isaza
1c3e61725f Change the i18n default name to :"unauthorized.default" 2011-07-01 13:10:20 -05:00
Nicolás Hock Isaza
71f60bc4ac Adding tests for i18n translation for default messages 2011-06-30 18:16:47 -05:00
Nicolás Hock Isaza
596ad235a9 Make CanCan Default Message a translatable text. Default to the one you had. 2011-06-30 13:22:49 -07:00
Ryan Bates
613ab1c1ab delegating ControllerResource find to model adapter, uses 'get' for DataMapper - closes #373 2011-05-21 13:57:17 -07:00
Ryan Bates
c031f82dd2 allow :find_by option to be full find method name - closes #335 2011-05-19 23:37:36 -04:00
16 changed files with 161 additions and 28 deletions

7
.travis.yml Normal file
View File

@@ -0,0 +1,7 @@
rvm:
- 1.8.7
- ree
notifications:
recipients:
- graf.otodrakula@gmail.com
- ryan@railscasts.com

View File

@@ -1,3 +1,27 @@
1.6.7 (October 4, 2011)
* fixing nested resource problem caused by namespace addition - issue #482
1.6.6 (September 28, 2011)
* correct "return cant jump across threads" error when using check_authorization (thanks codeprimate) - issues #463, #469
* fixing tests in development by specifying with_model version (thanks kirkconnell) - issue #476
* added travis.yml file for TravisCI support (thanks bai) - issue #427
* better support for namespaced models (thanks whilefalse) - issues #424
* adding :id_param option to load_and_authorize_resource (thanks skhisma) - issue #425
* make default unauthorized message translatable text (thanks nhocki) - issue #409
* improving DataMapper behavior (thanks psanford, maxsum-corin) - issue #410, #373
* allow :find_by option to be full find method name - issue #335
1.6.5 (May 18, 2011) 1.6.5 (May 18, 2011)
* pass action and subject through AccessDenied exception when :through isn't found - issue #366 * pass action and subject through AccessDenied exception when :through isn't found - issue #366
@@ -15,7 +39,6 @@
* improve scope merging - issue #328 * improve scope merging - issue #328
1.6.4 (March 29, 2011) 1.6.4 (March 29, 2011)
* Fixed mongoid 'or' error - see issue #322 * Fixed mongoid 'or' error - see issue #322

View File

@@ -3,8 +3,8 @@ source "http://rubygems.org"
case ENV["MODEL_ADAPTER"] case ENV["MODEL_ADAPTER"]
when nil, "active_record" when nil, "active_record"
gem "sqlite3" gem "sqlite3"
gem "activerecord", :require => "active_record" gem "activerecord", '~> 3.0.9', :require => "active_record"
gem "with_model" gem "with_model", '~> 0.1.5'
gem "meta_where" gem "meta_where"
when "data_mapper" when "data_mapper"
gem "dm-core", "~> 1.0.2" gem "dm-core", "~> 1.0.2"

View File

@@ -1,6 +1,6 @@
Gem::Specification.new do |s| Gem::Specification.new do |s|
s.name = "cancan" s.name = "cancan"
s.version = "1.6.5" s.version = "1.6.7"
s.author = "Ryan Bates" s.author = "Ryan Bates"
s.email = "ryan@railscasts.com" s.email = "ryan@railscasts.com"
s.homepage = "http://github.com/ryanb/cancan" s.homepage = "http://github.com/ryanb/cancan"
@@ -10,8 +10,8 @@ Gem::Specification.new do |s|
s.files = Dir["{lib,spec}/**/*", "[A-Z]*", "init.rb"] - ["Gemfile.lock"] s.files = Dir["{lib,spec}/**/*", "[A-Z]*", "init.rb"] - ["Gemfile.lock"]
s.require_path = "lib" s.require_path = "lib"
s.add_development_dependency 'rspec', '~> 2.1.0' s.add_development_dependency 'rspec', '~> 2.6.0'
s.add_development_dependency 'rails', '~> 3.0.0' s.add_development_dependency 'rails', '~> 3.0.9'
s.add_development_dependency 'rr', '~> 0.10.11' # 1.0.0 has respond_to? issues: http://github.com/btakita/rr/issues/issue/43 s.add_development_dependency 'rr', '~> 0.10.11' # 1.0.0 has respond_to? issues: http://github.com/btakita/rr/issues/issue/43
s.add_development_dependency 'supermodel', '~> 0.1.4' s.add_development_dependency 'supermodel', '~> 0.1.4'

View File

@@ -186,7 +186,7 @@ module CanCan
skip_authorize_resource(*args) skip_authorize_resource(*args)
end end
# Skip both the loading behavior of CanCan. This is useful when using +load_and_authorize_resource+ but want to # Skip the loading behavior of CanCan. This is useful when using +load_and_authorize_resource+ but want to
# only do authorization on certain actions. You can pass :only and :except options to specify which actions to # only do authorization on certain actions. You can pass :only and :except options to specify which actions to
# skip the effects on. It will apply to all actions by default. # skip the effects on. It will apply to all actions by default.
# #
@@ -202,7 +202,7 @@ module CanCan
cancan_skipper[:load][name] = options cancan_skipper[:load][name] = options
end end
# Skip both the authorization behavior of CanCan. This is useful when using +load_and_authorize_resource+ but want to # Skip the authorization behavior of CanCan. This is useful when using +load_and_authorize_resource+ but want to
# only do loading on certain actions. You can pass :only and :except options to specify which actions to # only do loading on certain actions. You can pass :only and :except options to specify which actions to
# skip the effects on. It will apply to all actions by default. # skip the effects on. It will apply to all actions by default.
# #
@@ -247,9 +247,9 @@ module CanCan
# #
def check_authorization(options = {}) def check_authorization(options = {})
self.after_filter(options.slice(:only, :except)) do |controller| self.after_filter(options.slice(:only, :except)) do |controller|
return if controller.instance_variable_defined?(:@_authorized) next if controller.instance_variable_defined?(:@_authorized)
return if options[:if] && !controller.send(options[:if]) next if options[:if] && !controller.send(options[:if])
return if options[:unless] && controller.send(options[:unless]) next if options[:unless] && controller.send(options[:unless])
raise AuthorizationNotPerformed, "This action failed the check_authorization because it does not authorize_resource. Add skip_authorization_check to bypass this check." raise AuthorizationNotPerformed, "This action failed the check_authorization because it does not authorize_resource. Add skip_authorization_check to bypass this check."
end end
end end

View File

@@ -100,20 +100,36 @@ module CanCan
if @options[:singleton] && parent_resource.respond_to?(name) if @options[:singleton] && parent_resource.respond_to?(name)
parent_resource.send(name) parent_resource.send(name)
else else
@options[:find_by] ? resource_base.send("find_by_#{@options[:find_by]}!", id_param) : resource_base.find(id_param) if @options[:find_by]
if resource_base.respond_to? "find_by_#{@options[:find_by]}!"
resource_base.send("find_by_#{@options[:find_by]}!", id_param)
else
resource_base.send(@options[:find_by], id_param)
end
else
adapter.find(resource_base, id_param)
end
end end
end end
def adapter
ModelAdapters::AbstractAdapter.adapter_class(resource_class)
end
def authorization_action def authorization_action
parent? ? :show : @params[:action].to_sym parent? ? :show : @params[:action].to_sym
end end
def id_param def id_param
@params[parent? ? :"#{name}_id" : :id] if @options[:id_param]
@params[@options[:id_param]]
else
@params[parent? ? :"#{name}_id" : :id]
end
end end
def member_action? def member_action?
new_actions.include?(@params[:action].to_sym) || @options[:singleton] || (@params[:id] && !collection_actions.include?(@params[:action].to_sym)) new_actions.include?(@params[:action].to_sym) || @options[:singleton] || ( (@params[:id] || @params[@options[:id_param]]) && !collection_actions.include?(@params[:action].to_sym))
end end
# Returns the class used for this resource. This can be overriden by the :class option. # Returns the class used for this resource. This can be overriden by the :class option.
@@ -122,7 +138,7 @@ module CanCan
def resource_class def resource_class
case @options[:class] case @options[:class]
when false then name.to_sym when false then name.to_sym
when nil then name.to_s.camelize.constantize when nil then namespaced_name.to_s.camelize.constantize
when String then @options[:class].constantize when String then @options[:class].constantize
else @options[:class] else @options[:class]
end end
@@ -191,6 +207,12 @@ module CanCan
@name || name_from_controller @name || name_from_controller
end end
def namespaced_name
@name || @params[:controller].sub("Controller", "").singularize.camelize.constantize
rescue NameError
name
end
def name_from_controller def name_from_controller
@params[:controller].sub("Controller", "").underscore.split('/').last.singularize @params[:controller].sub("Controller", "").underscore.split('/').last.singularize
end end

View File

@@ -40,7 +40,7 @@ module CanCan
@message = message @message = message
@action = action @action = action
@subject = subject @subject = subject
@default_message = "You are not authorized to access this page." @default_message = I18n.t(:"unauthorized.default", :default => "You are not authorized to access this page.")
end end
def to_s def to_s

View File

@@ -1,5 +1,5 @@
RSpec = Spec unless defined? RSpec # for RSpec 1 compatability rspec_module = defined?(RSpec::Core) ? 'RSpec' : 'Spec' # for RSpec 1 compatability
RSpec::Matchers.define :be_able_to do |*args| Kernel.const_get(rspec_module)::Matchers.define :be_able_to do |*args|
match do |ability| match do |ability|
ability.can?(*args) ability.can?(*args)
end end

View File

@@ -15,6 +15,11 @@ module CanCan
false # override in subclass false # override in subclass
end end
# Override if you need custom find behavior
def self.find(model_class, id)
model_class.find(id)
end
# Used to determine if this model adapter will override the matching behavior for a hash of conditions. # Used to determine if this model adapter will override the matching behavior for a hash of conditions.
# If this returns true then matches_conditions_hash? will be called. See Rule#matches_conditions_hash # If this returns true then matches_conditions_hash? will be called. See Rule#matches_conditions_hash
def self.override_conditions_hash_matching?(subject, conditions) def self.override_conditions_hash_matching?(subject, conditions)

View File

@@ -5,6 +5,10 @@ module CanCan
model_class <= DataMapper::Resource model_class <= DataMapper::Resource
end end
def self.find(model_class, id)
model_class.get(id)
end
def self.override_conditions_hash_matching?(subject, conditions) def self.override_conditions_hash_matching?(subject, conditions)
conditions.any? { |k,v| !k.kind_of?(Symbol) } conditions.any? { |k,v| !k.kind_of?(Symbol) }
end end
@@ -27,6 +31,4 @@ module CanCan
end # module ModelAdapters end # module ModelAdapters
end # module CanCan end # module CanCan
DataMapper::Model.class_eval do DataMapper::Model.append_extensions(CanCan::ModelAdditions::ClassMethods)
include CanCan::ModelAdditions::ClassMethods
end

View File

@@ -35,6 +35,18 @@ describe CanCan::ControllerResource do
@controller.instance_variable_get(:@project).should == project @controller.instance_variable_get(:@project).should == project
end end
it "should attempt to load a resource with the same namespace as the controller when using :: for namespace" do
module MyEngine
class Project < ::Project; end
end
project = MyEngine::Project.create!
@params.merge!(:controller => "MyEngine::ProjectsController", :action => "show", :id => project.id)
resource = CanCan::ControllerResource.new(@controller)
resource.load_resource
@controller.instance_variable_get(:@project).should == project
end
it "should properly load resource for namespaced controller when using '::' for namespace" do it "should properly load resource for namespaced controller when using '::' for namespace" do
project = Project.create! project = Project.create!
@params.merge!(:controller => "Admin::ProjectsController", :action => "show", :id => project.id) @params.merge!(:controller => "Admin::ProjectsController", :action => "show", :id => project.id)
@@ -185,8 +197,8 @@ describe CanCan::ControllerResource do
it "should load parent resource through proper id parameter" do it "should load parent resource through proper id parameter" do
project = Project.create! project = Project.create!
@params.merge!(:action => "index", :project_id => project.id) @params.merge!(:controller => "categories", :action => "index", :project_id => project.id)
resource = CanCan::ControllerResource.new(@controller, :project, :parent => true) resource = CanCan::ControllerResource.new(@controller, :project)
resource.load_resource resource.load_resource
@controller.instance_variable_get(:@project).should == project @controller.instance_variable_get(:@project).should == project
end end
@@ -328,6 +340,14 @@ describe CanCan::ControllerResource do
@controller.instance_variable_get(:@custom_project).should == project @controller.instance_variable_get(:@custom_project).should == project
end end
it "should load 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
@controller.instance_variable_get(:@project).should == project
end
it "should load resource using custom find_by attribute" do it "should load resource using custom find_by attribute" do
project = Project.create!(:name => "foo") project = Project.create!(:name => "foo")
@params.merge!(:action => "show", :id => "foo") @params.merge!(:action => "show", :id => "foo")
@@ -336,6 +356,14 @@ describe CanCan::ControllerResource do
@controller.instance_variable_get(:@project).should == project @controller.instance_variable_get(:@project).should == project
end end
it "should allow 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
@controller.instance_variable_get(:@project).should == project
end
it "should raise ImplementationRemoved when adding :name option" do it "should raise ImplementationRemoved when adding :name option" do
lambda { lambda {
CanCan::ControllerResource.new(@controller, :name => :foo) CanCan::ControllerResource.new(@controller, :name => :foo)

View File

@@ -32,4 +32,27 @@ describe CanCan::AccessDenied do
@exception.message.should == "Access denied!" @exception.message.should == "Access denied!"
end end
end end
describe "i18n in the default message" do
after(:each) do
I18n.backend = nil
end
it "uses i18n for the default message" do
I18n.backend.store_translations :en, :unauthorized => {:default => "This is a different message"}
@exception = CanCan::AccessDenied.new
@exception.message.should == "This is a different message"
end
it "defaults to a nice message" do
@exception = CanCan::AccessDenied.new
@exception.message.should == "You are not authorized to access this page."
end
it "does not use translation if a message is given" do
@exception = CanCan::AccessDenied.new("Hey! You're not welcome here")
@exception.message.should == "Hey! You're not welcome here"
@exception.message.should_not == "You are not authorized to access this page."
end
end
end end

View File

@@ -1,10 +1,6 @@
if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record" if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
require "spec_helper" require "spec_helper"
RSpec.configure do |config|
config.extend WithModel
end
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:") ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
describe CanCan::ModelAdapters::ActiveRecordAdapter do describe CanCan::ModelAdapters::ActiveRecordAdapter do
@@ -56,6 +52,11 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article).should == CanCan::ModelAdapters::ActiveRecordAdapter CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article).should == CanCan::ModelAdapters::ActiveRecordAdapter
end end
it "should find record" do
article = Article.create!
CanCan::ModelAdapters::ActiveRecordAdapter.find(Article, article.id).should == article
end
it "should not fetch any records when no abilities are defined" do it "should not fetch any records when no abilities are defined" do
Article.create! Article.create!
Article.accessible_by(@ability).should be_empty Article.accessible_by(@ability).should be_empty

View File

@@ -36,6 +36,11 @@ if ENV["MODEL_ADAPTER"] == "data_mapper"
CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article).should == CanCan::ModelAdapters::DataMapperAdapter CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article).should == CanCan::ModelAdapters::DataMapperAdapter
end end
it "should find record" do
article = Article.create
CanCan::ModelAdapters::DataMapperAdapter.find(Article, article.id).should == article
end
it "should not fetch any records when no abilities are defined" do it "should not fetch any records when no abilities are defined" do
Article.create Article.create
Article.accessible_by(@ability).should be_empty Article.accessible_by(@ability).should be_empty

View File

@@ -36,6 +36,11 @@ if ENV["MODEL_ADAPTER"] == "mongoid"
CanCan::ModelAdapters::AbstractAdapter.adapter_class(MongoidProject).should == CanCan::ModelAdapters::MongoidAdapter CanCan::ModelAdapters::AbstractAdapter.adapter_class(MongoidProject).should == CanCan::ModelAdapters::MongoidAdapter
end end
it "should find record" do
project = MongoidProject.create
CanCan::ModelAdapters::MongoidAdapter.find(MongoidProject, project.id).should == project
end
it "should compare properties on mongoid documents with the conditions hash" do it "should compare properties on mongoid documents with the conditions hash" do
model = MongoidProject.new model = MongoidProject.new
@ability.can :read, MongoidProject, :id => model.id @ability.can :read, MongoidProject, :id => model.id

View File

@@ -9,11 +9,15 @@ require 'matchers'
require 'cancan/matchers' require 'cancan/matchers'
RSpec.configure do |config| RSpec.configure do |config|
config.treat_symbols_as_metadata_keys_with_true_values = true
config.filter_run :focus => true
config.run_all_when_everything_filtered = true
config.mock_with :rr config.mock_with :rr
config.before(:each) do config.before(:each) do
Project.delete_all Project.delete_all
Category.delete_all Category.delete_all
end end
config.extend WithModel
end end
class Ability class Ability
@@ -30,4 +34,12 @@ end
class Project < SuperModel::Base class Project < SuperModel::Base
belongs_to :category belongs_to :category
attr_accessor :category # why doesn't SuperModel do this automatically? attr_accessor :category # why doesn't SuperModel do this automatically?
def self.respond_to?(method, include_private = false)
if method.to_s == "find_by_name!" # hack to simulate ActiveRecord
true
else
super
end
end
end end