canable 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +22 -0
- data/LICENSE +20 -0
- data/README.rdoc +122 -0
- data/Rakefile +45 -0
- data/lib/canable.rb +77 -0
- data/specs.watchr +47 -0
- data/test/helper.rb +38 -0
- data/test/test_ables.rb +51 -0
- data/test/test_canable.rb +26 -0
- data/test/test_cans.rb +81 -0
- data/test/test_enforcers.rb +32 -0
- metadata +110 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 John Nunemaker
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
= Canable
|
2
|
+
|
3
|
+
Simple permissions that I have used on my last several projects so I figured it was time to abstract and wrap up into something more easily reusable.
|
4
|
+
|
5
|
+
== Cans
|
6
|
+
|
7
|
+
Whatever class you want all permissions to run through should include Canable::Cans.
|
8
|
+
|
9
|
+
class User
|
10
|
+
include MongoMapper::Document
|
11
|
+
include Canable::Cans
|
12
|
+
end
|
13
|
+
|
14
|
+
This means that an instance of a user automatically gets can methods for the default REST actions: can_view?(resource), can_create?(resource), can_update?(resource), can_destroy?(resource).
|
15
|
+
|
16
|
+
== Ables
|
17
|
+
|
18
|
+
Each of the can methods simply calls the related "able" method (viewable, creatable, updatable, destroyable) for the action (view, create, update, delete). Canable comes with defaults for this methods that you can then override as makes sense for your permissions.
|
19
|
+
|
20
|
+
class Article
|
21
|
+
include MongoMapper::Document
|
22
|
+
include Canable::Ables
|
23
|
+
end
|
24
|
+
|
25
|
+
Including Canable::Ables adds the able methods to the class including it. In this instance, article now has viewable_by?(user), creatable_by?(user), updatable_by?(user) and destroyable_by?(user).
|
26
|
+
|
27
|
+
Lets say an article can be viewed and created by anyone, but only updated or destroyed by the user that created the article. To do that, you could leave viewable_by? and creatable_by? alone as they default to true and just override the other methods.
|
28
|
+
|
29
|
+
class Article
|
30
|
+
include MongoMapper::Document
|
31
|
+
include Canable::Ables
|
32
|
+
userstamps! # adds creator and updater
|
33
|
+
|
34
|
+
def updatable_by?(user)
|
35
|
+
creator == user
|
36
|
+
end
|
37
|
+
|
38
|
+
def destroyable_by?(user)
|
39
|
+
updatable_by?(user)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
Lets look at some sample code now:
|
44
|
+
|
45
|
+
john = User.create(:name => 'John')
|
46
|
+
steve = User.create(:name =. 'Steve')
|
47
|
+
|
48
|
+
ruby = Article.new(:title => 'Ruby')
|
49
|
+
john.can_create?(ruby) # true
|
50
|
+
steve.can_create?(ruby) # true
|
51
|
+
|
52
|
+
ruby.creator = john
|
53
|
+
ruby.save
|
54
|
+
|
55
|
+
john.can_view?(ruby) # true
|
56
|
+
steve.can_view?(ruby) # true
|
57
|
+
|
58
|
+
john.can_update?(ruby) # true
|
59
|
+
steve.can_update?(ruby) # false
|
60
|
+
|
61
|
+
john.can_destroy?(ruby) # true
|
62
|
+
steve.can_destroy?(ruby) # false
|
63
|
+
|
64
|
+
Now we can implement our permissions for each resource and then always check whether a user can or cannot do something. This makes it all really easy to test. Next, how would you use this in the controller.
|
65
|
+
|
66
|
+
== Enforcers
|
67
|
+
|
68
|
+
class ApplicationController
|
69
|
+
include Canable::Enforcers
|
70
|
+
end
|
71
|
+
|
72
|
+
Including Canable::Enforcers adds an enforce permission method for each of the actions defined (by default view/create/update/destroy). It is the same thing as doing this for each Canable action:
|
73
|
+
|
74
|
+
class ApplicationController
|
75
|
+
include Canable::Enforcers
|
76
|
+
|
77
|
+
delegate :can_view?, :to => :current_user
|
78
|
+
helper_method :can_view? # so you can use it in your views
|
79
|
+
hide_action :can_view?
|
80
|
+
|
81
|
+
private
|
82
|
+
def enforce_view_permission(resource)
|
83
|
+
raise Canable::Transgression unless can_view?(resource)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
Which means you can use it like this:
|
88
|
+
|
89
|
+
class ArticlesController < ApplicationController
|
90
|
+
def show
|
91
|
+
@article = Article.find!(params[:id])
|
92
|
+
enforce_view_permission(@article)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
If the user can_view? the article, all is well. If not, a Canable::Transgression is raised which you can decide how to handle (show 404, slap them on the wrist, etc.).
|
97
|
+
|
98
|
+
== Adding Your Own Actions
|
99
|
+
|
100
|
+
You can add your own actions like this:
|
101
|
+
|
102
|
+
Canable.add(:publish, :publishable)
|
103
|
+
|
104
|
+
The first parameter is the can method (ie: can_publish?) and the second is the able method (ie: publishable_by?).
|
105
|
+
|
106
|
+
== Review
|
107
|
+
|
108
|
+
So, lets review: cans go on user model, ables go on everything, you override ables in each model where you want to enforce permissions, and enforcers go after each time you find or initialize an object in a controller. Bing, bang, boom.
|
109
|
+
|
110
|
+
== Note on Patches/Pull Requests
|
111
|
+
|
112
|
+
* Fork the project.
|
113
|
+
* Make your feature addition or bug fix.
|
114
|
+
* Add tests for it. This is important so I don't break it in a
|
115
|
+
future version unintentionally.
|
116
|
+
* Commit, do not mess with rakefile, version, or history.
|
117
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
118
|
+
* Send me a pull request. Bonus points for topic branches.
|
119
|
+
|
120
|
+
== Copyright
|
121
|
+
|
122
|
+
Copyright (c) 2010 John Nunemaker. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
require File.dirname(__FILE__) + '/lib/canable'
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'jeweler'
|
8
|
+
Jeweler::Tasks.new do |gem|
|
9
|
+
gem.name = "canable"
|
10
|
+
gem.summary = %Q{Simple permissions that I have used on my last several projects so I figured it was time to abstract and wrap up into something more easily reusable.}
|
11
|
+
gem.description = %Q{Simple permissions that I have used on my last several projects so I figured it was time to abstract and wrap up into something more easily reusable.}
|
12
|
+
gem.email = "nunemaker@gmail.com"
|
13
|
+
gem.homepage = "http://github.com/jnunemaker/canable"
|
14
|
+
gem.authors = ["John Nunemaker"]
|
15
|
+
gem.version = Canable::Version
|
16
|
+
gem.add_development_dependency "shoulda", "2.10.2"
|
17
|
+
gem.add_development_dependency "mongo_mapper", "0.7"
|
18
|
+
gem.add_development_dependency "mocha", "0.9.8"
|
19
|
+
gem.add_development_dependency "yard", ">= 0"
|
20
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
21
|
+
end
|
22
|
+
Jeweler::GemcutterTasks.new
|
23
|
+
rescue LoadError
|
24
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
Rake::TestTask.new(:test) do |test|
|
29
|
+
test.libs << 'lib' << 'test'
|
30
|
+
test.ruby_opts << '-rubygems'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
task :test => :check_dependencies
|
36
|
+
task :default => :test
|
37
|
+
|
38
|
+
begin
|
39
|
+
require 'yard'
|
40
|
+
YARD::Rake::YardocTask.new
|
41
|
+
rescue LoadError
|
42
|
+
task :yardoc do
|
43
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
44
|
+
end
|
45
|
+
end
|
data/lib/canable.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
module Canable
|
2
|
+
Version = '0.1'
|
3
|
+
|
4
|
+
# Module that holds all the can_action? methods.
|
5
|
+
module Cans; end
|
6
|
+
|
7
|
+
# Module that holds all the [method]able_by? methods.
|
8
|
+
module Ables; end
|
9
|
+
|
10
|
+
# Module that holds all the enforce_[action]_permission methods for use in controllers.
|
11
|
+
module Enforcers
|
12
|
+
def self.included(controller)
|
13
|
+
controller.class_eval do
|
14
|
+
Canable.actions.each do |can, able|
|
15
|
+
delegate "can_#{can}?", :to => :current_user
|
16
|
+
helper_method "can_#{can}?" if controller.respond_to?(:helper_method)
|
17
|
+
hide_action "can_#{can}?" if controller.respond_to?(:hide_action)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Exception that gets raised when permissions are broken for whatever reason.
|
24
|
+
class Transgression < StandardError; end
|
25
|
+
|
26
|
+
# Default actions to an empty hash.
|
27
|
+
@actions = {}
|
28
|
+
|
29
|
+
# Returns hash of actions that have been added.
|
30
|
+
# {:view => :viewable, ...}
|
31
|
+
def self.actions
|
32
|
+
@actions
|
33
|
+
end
|
34
|
+
|
35
|
+
# Adds an action to actions and the correct methods to can and able modules.
|
36
|
+
#
|
37
|
+
# @param [Symbol] can_method The name of the can_[action]? method.
|
38
|
+
# @param [Symbol] resource_method The name of the [resource_method]_by? method.
|
39
|
+
def self.add(can, able)
|
40
|
+
@actions[can] = able
|
41
|
+
add_can_method(can, able)
|
42
|
+
add_able_method(able)
|
43
|
+
add_enforcer_method(can)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
def self.add_can_method(can, able)
|
48
|
+
Cans.module_eval <<-EOM
|
49
|
+
def can_#{can}?(resource)
|
50
|
+
return false if resource.blank?
|
51
|
+
resource.#{able}_by?(self)
|
52
|
+
end
|
53
|
+
EOM
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.add_able_method(able)
|
57
|
+
Ables.module_eval <<-EOM
|
58
|
+
def #{able}_by?(user)
|
59
|
+
true
|
60
|
+
end
|
61
|
+
EOM
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.add_enforcer_method(can)
|
65
|
+
Enforcers.module_eval <<-EOM
|
66
|
+
def enforce_#{can}_permission(resource)
|
67
|
+
raise Canable::Transgression unless can_#{can}?(resource)
|
68
|
+
end
|
69
|
+
private :enforce_#{can}_permission
|
70
|
+
EOM
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
Canable.add(:view, :viewable)
|
75
|
+
Canable.add(:create, :creatable)
|
76
|
+
Canable.add(:update, :updatable)
|
77
|
+
Canable.add(:destroy, :destroyable)
|
data/specs.watchr
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
def growl(title, msg, img)
|
2
|
+
%x{growlnotify -m #{ msg.inspect} -t #{title.inspect} --image ~/.watchr/#{img}.png}
|
3
|
+
end
|
4
|
+
|
5
|
+
def form_growl_message(str)
|
6
|
+
results = str.split("\n").last
|
7
|
+
if results =~ /[1-9]\s(failure|error)s?/
|
8
|
+
growl "Test Results", "#{results}", "fail"
|
9
|
+
elsif results != ""
|
10
|
+
growl "Test Results", "#{results}", "pass"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def run(cmd)
|
15
|
+
puts(cmd)
|
16
|
+
output = ""
|
17
|
+
IO.popen(cmd) do |com|
|
18
|
+
com.each_char do |c|
|
19
|
+
print c
|
20
|
+
output << c
|
21
|
+
$stdout.flush
|
22
|
+
end
|
23
|
+
end
|
24
|
+
form_growl_message output
|
25
|
+
end
|
26
|
+
|
27
|
+
def run_test_file(file)
|
28
|
+
run %Q(ruby -I"lib:test" -rubygems #{file})
|
29
|
+
end
|
30
|
+
|
31
|
+
def run_all_tests
|
32
|
+
run "rake test"
|
33
|
+
end
|
34
|
+
|
35
|
+
watch('test/helper\.rb') { system('clear'); run_all_tests }
|
36
|
+
watch('test/test_.*\.rb') { |m| system('clear'); run_test_file(m[0]) }
|
37
|
+
watch('lib/.*') { |m| system('clear'); run_all_tests }
|
38
|
+
|
39
|
+
# Ctrl-\
|
40
|
+
Signal.trap('QUIT') do
|
41
|
+
puts " --- Running all tests ---\n\n"
|
42
|
+
run_all_tests
|
43
|
+
end
|
44
|
+
|
45
|
+
# Ctrl-C
|
46
|
+
Signal.trap('INT') { abort("\n") }
|
47
|
+
|
data/test/helper.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
gem 'mocha', '0.9.8'
|
4
|
+
gem 'shoulda', '2.10.2'
|
5
|
+
gem 'mongo_mapper', '0.7'
|
6
|
+
|
7
|
+
require 'mocha'
|
8
|
+
require 'shoulda'
|
9
|
+
require 'mongo_mapper'
|
10
|
+
|
11
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
12
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
13
|
+
require 'canable'
|
14
|
+
|
15
|
+
class Test::Unit::TestCase
|
16
|
+
end
|
17
|
+
|
18
|
+
def Doc(name=nil, &block)
|
19
|
+
klass = Class.new do
|
20
|
+
include MongoMapper::Document
|
21
|
+
set_collection_name "test#{rand(20)}"
|
22
|
+
|
23
|
+
if name
|
24
|
+
class_eval "def self.name; '#{name}' end"
|
25
|
+
class_eval "def self.to_s; '#{name}' end"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
klass.class_eval(&block) if block_given?
|
30
|
+
klass.collection.remove
|
31
|
+
klass
|
32
|
+
end
|
33
|
+
|
34
|
+
test_dir = File.expand_path(File.dirname(__FILE__) + '/../tmp')
|
35
|
+
FileUtils.mkdir_p(test_dir) unless File.exist?(test_dir)
|
36
|
+
|
37
|
+
MongoMapper.connection = Mongo::Connection.new('127.0.0.1', 27017, {:logger => Logger.new(test_dir + '/test.log')})
|
38
|
+
MongoMapper.database = 'test'
|
data/test/test_ables.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class AblesTest < Test::Unit::TestCase
|
4
|
+
context "Class with Canable::Ables included" do
|
5
|
+
setup do
|
6
|
+
klass = Doc do
|
7
|
+
include Canable::Ables
|
8
|
+
end
|
9
|
+
|
10
|
+
@resource = klass.new
|
11
|
+
@user = mock('user')
|
12
|
+
end
|
13
|
+
|
14
|
+
should "default viewable_by? to true" do
|
15
|
+
assert @resource.viewable_by?(@user)
|
16
|
+
end
|
17
|
+
|
18
|
+
should "default creatable_by? to true" do
|
19
|
+
assert @resource.creatable_by?(@user)
|
20
|
+
end
|
21
|
+
|
22
|
+
should "default updatable_by? to true" do
|
23
|
+
assert @resource.updatable_by?(@user)
|
24
|
+
end
|
25
|
+
|
26
|
+
should "default destroyable_by? to true" do
|
27
|
+
assert @resource.destroyable_by?(@user)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "Class that overrides an able method" do
|
32
|
+
setup do
|
33
|
+
klass = Doc do
|
34
|
+
include Canable::Ables
|
35
|
+
|
36
|
+
def viewable_by?(user)
|
37
|
+
user.name == 'John'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
@resource = klass.new
|
42
|
+
@john = mock('user', :name => 'John')
|
43
|
+
@steve = mock('user', :name => 'Steve')
|
44
|
+
end
|
45
|
+
|
46
|
+
should "use the overriden method and not default to true" do
|
47
|
+
assert @resource.viewable_by?(@john)
|
48
|
+
assert ! @resource.viewable_by?(@steve)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestCanable < Test::Unit::TestCase
|
4
|
+
context "Canable" do
|
5
|
+
should "have view action by default" do
|
6
|
+
assert_equal :viewable, Canable.actions[:view]
|
7
|
+
end
|
8
|
+
|
9
|
+
should "have create action by default" do
|
10
|
+
assert_equal :creatable, Canable.actions[:create]
|
11
|
+
end
|
12
|
+
|
13
|
+
should "have update action by default" do
|
14
|
+
assert_equal :updatable, Canable.actions[:update]
|
15
|
+
end
|
16
|
+
|
17
|
+
should "have destroy action by default" do
|
18
|
+
assert_equal :destroyable, Canable.actions[:destroy]
|
19
|
+
end
|
20
|
+
|
21
|
+
should "be able to add another action" do
|
22
|
+
Canable.add(:publish, :publishable)
|
23
|
+
assert_equal :publishable, Canable.actions[:publish]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/test/test_cans.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class CansTest < Test::Unit::TestCase
|
4
|
+
context "Class with Canable::Cans included" do
|
5
|
+
setup do
|
6
|
+
klass = Doc do
|
7
|
+
include Canable::Cans
|
8
|
+
end
|
9
|
+
|
10
|
+
@user = klass.create(:name => 'John')
|
11
|
+
end
|
12
|
+
|
13
|
+
context "can_view?" do
|
14
|
+
should "be true if resource is viewable_by?" do
|
15
|
+
resource = mock('resource', :viewable_by? => true)
|
16
|
+
assert @user.can_view?(resource)
|
17
|
+
end
|
18
|
+
|
19
|
+
should "be false if resource is not viewable_by?" do
|
20
|
+
resource = mock('resource', :viewable_by? => false)
|
21
|
+
assert ! @user.can_view?(resource)
|
22
|
+
end
|
23
|
+
|
24
|
+
should "be false if resource is blank" do
|
25
|
+
assert ! @user.can_view?(nil)
|
26
|
+
assert ! @user.can_view?('')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "can_create?" do
|
31
|
+
should "be true if resource is creatable_by?" do
|
32
|
+
resource = mock('resource', :creatable_by? => true)
|
33
|
+
assert @user.can_create?(resource)
|
34
|
+
end
|
35
|
+
|
36
|
+
should "be false if resource is not creatable_by?" do
|
37
|
+
resource = mock('resource', :creatable_by? => false)
|
38
|
+
assert ! @user.can_create?(resource)
|
39
|
+
end
|
40
|
+
|
41
|
+
should "be false if resource is blank" do
|
42
|
+
assert ! @user.can_create?(nil)
|
43
|
+
assert ! @user.can_create?('')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "can_update?" do
|
48
|
+
should "be true if resource is updatable_by?" do
|
49
|
+
resource = mock('resource', :updatable_by? => true)
|
50
|
+
assert @user.can_update?(resource)
|
51
|
+
end
|
52
|
+
|
53
|
+
should "be false if resource is not updatable_by?" do
|
54
|
+
resource = mock('resource', :updatable_by? => false)
|
55
|
+
assert ! @user.can_update?(resource)
|
56
|
+
end
|
57
|
+
|
58
|
+
should "be false if resource is blank" do
|
59
|
+
assert ! @user.can_update?(nil)
|
60
|
+
assert ! @user.can_update?('')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "can_destroy?" do
|
65
|
+
should "be true if resource is destroyable_by?" do
|
66
|
+
resource = mock('resource', :destroyable_by? => true)
|
67
|
+
assert @user.can_destroy?(resource)
|
68
|
+
end
|
69
|
+
|
70
|
+
should "be false if resource is not destroyable_by?" do
|
71
|
+
resource = mock('resource', :destroyable_by? => false)
|
72
|
+
assert ! @user.can_destroy?(resource)
|
73
|
+
end
|
74
|
+
|
75
|
+
should "be false if resource is blank" do
|
76
|
+
assert ! @user.can_destroy?(nil)
|
77
|
+
assert ! @user.can_destroy?('')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class EnforcersTest < Test::Unit::TestCase
|
4
|
+
context "Including Canable::Enforcers in a class" do
|
5
|
+
setup do
|
6
|
+
klass = Class.new do
|
7
|
+
include Canable::Enforcers
|
8
|
+
attr_accessor :current_user, :article
|
9
|
+
|
10
|
+
def show
|
11
|
+
enforce_view_permission(article)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
@article = mock('article')
|
16
|
+
@user = mock('user')
|
17
|
+
@controller = klass.new
|
18
|
+
@controller.article = @article
|
19
|
+
@controller.current_user = @user
|
20
|
+
end
|
21
|
+
|
22
|
+
should "not raise error if can" do
|
23
|
+
@user.expects(:can_view?).with(@article).returns(true)
|
24
|
+
assert_nothing_raised { @controller.show }
|
25
|
+
end
|
26
|
+
|
27
|
+
should "raise error if cannot" do
|
28
|
+
@user.expects(:can_view?).with(@article).returns(false)
|
29
|
+
assert_raises(Canable::Transgression) { @controller.show }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: canable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.1"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- John Nunemaker
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-27 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: shoulda
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - "="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.10.2
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: mongo_mapper
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0.7"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: mocha
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - "="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.9.8
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: yard
|
47
|
+
type: :development
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
description: Simple permissions that I have used on my last several projects so I figured it was time to abstract and wrap up into something more easily reusable.
|
56
|
+
email: nunemaker@gmail.com
|
57
|
+
executables: []
|
58
|
+
|
59
|
+
extensions: []
|
60
|
+
|
61
|
+
extra_rdoc_files:
|
62
|
+
- LICENSE
|
63
|
+
- README.rdoc
|
64
|
+
files:
|
65
|
+
- .document
|
66
|
+
- .gitignore
|
67
|
+
- LICENSE
|
68
|
+
- README.rdoc
|
69
|
+
- Rakefile
|
70
|
+
- lib/canable.rb
|
71
|
+
- specs.watchr
|
72
|
+
- test/helper.rb
|
73
|
+
- test/test_ables.rb
|
74
|
+
- test/test_canable.rb
|
75
|
+
- test/test_cans.rb
|
76
|
+
- test/test_enforcers.rb
|
77
|
+
has_rdoc: true
|
78
|
+
homepage: http://github.com/jnunemaker/canable
|
79
|
+
licenses: []
|
80
|
+
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options:
|
83
|
+
- --charset=UTF-8
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: "0"
|
91
|
+
version:
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: "0"
|
97
|
+
version:
|
98
|
+
requirements: []
|
99
|
+
|
100
|
+
rubyforge_project:
|
101
|
+
rubygems_version: 1.3.5
|
102
|
+
signing_key:
|
103
|
+
specification_version: 3
|
104
|
+
summary: Simple permissions that I have used on my last several projects so I figured it was time to abstract and wrap up into something more easily reusable.
|
105
|
+
test_files:
|
106
|
+
- test/helper.rb
|
107
|
+
- test/test_ables.rb
|
108
|
+
- test/test_canable.rb
|
109
|
+
- test/test_cans.rb
|
110
|
+
- test/test_enforcers.rb
|