can_opener 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/Gemfile +4 -0
- data/README.markdown +68 -0
- data/Rakefile +7 -0
- data/can_opener.gemspec +19 -0
- data/lib/can_opener.rb +35 -0
- data/lib/can_opener/ability.rb +16 -0
- data/lib/can_opener/version.rb +3 -0
- data/spec/can_opener_spec.rb +141 -0
- data/spec/spec_helper.rb +4 -0
- metadata +77 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
can-opener
|
2
|
+
==========
|
3
|
+
|
4
|
+
Split up your CanCan Ability by allowing you to easily create abilities in separate classes which you reference in your main ability model. This is mainly useful if you want to break down your abilities into smaller classes for organizational purposes.
|
5
|
+
|
6
|
+
Usage
|
7
|
+
-----
|
8
|
+
|
9
|
+
gem install can_opener
|
10
|
+
|
11
|
+
Declare ability classes than inherit from CanOpener::Ability, and implement the `abilities` public instance method. By default, the method will have access to the the local `user` as per typical use in CanCan.
|
12
|
+
|
13
|
+
require 'can_opener'
|
14
|
+
|
15
|
+
class AdminAbility < CanOpener::Ability
|
16
|
+
def abilities
|
17
|
+
if user.admin?
|
18
|
+
can :manage, :all
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class SupportAbility < CanOpener::Ability
|
24
|
+
def abilities
|
25
|
+
if user.support?
|
26
|
+
can :read, :all
|
27
|
+
can :write, :all
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class BannedAbility < CanOpener::Ability
|
33
|
+
def abilities
|
34
|
+
if user.banned?
|
35
|
+
cannot do |action, object_class, object|
|
36
|
+
true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
Then declare your Ability class, but include `CanOpener` rather than `CanCan::Abilities`
|
43
|
+
|
44
|
+
class Ability
|
45
|
+
include CanOpener
|
46
|
+
|
47
|
+
configure_abilities do |c|
|
48
|
+
c.add ReaderAbility
|
49
|
+
c.add SupportAbility
|
50
|
+
c.add AdminAbility, BannedAbility
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
Remember that CanCan processes abilities in a top down fashion, so add your general abilities up top, and then things you want to override everything (like banning a user) at the bottom. You can add line by line or multiple on one line if you wish.
|
55
|
+
|
56
|
+
### But I want to check the IP Address, Project, etc, in the ability
|
57
|
+
|
58
|
+
Just override your Ability#initialize and setup your accessors to be used in the `CanOpener::Ability` subclasses.
|
59
|
+
|
60
|
+
Why?
|
61
|
+
----
|
62
|
+
|
63
|
+
A couple times when I used CanCan, my abilities definition method got a little long, and I wanted a simple way to break it up for readability.
|
64
|
+
|
65
|
+
Todo
|
66
|
+
----
|
67
|
+
|
68
|
+
* Perhaps a generator for CanOpener::Ability subclasses.
|
data/Rakefile
ADDED
data/can_opener.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "can_opener/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "can_opener"
|
7
|
+
s.version = CanOpener::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Brendon Murphy"]
|
10
|
+
s.email = ["xternal1+github@gmail.com"]
|
11
|
+
s.homepage = "http://github.com/bemurphy/can-opener"
|
12
|
+
s.summary = %q{Split up your CanCan Ability}
|
13
|
+
s.description = %q{Split up your CanCan Ability by allowing you to easily create abilities in separate classes which you reference in your main ability model. This is mainly useful if you want to break down your abilities into smaller classes for organizational purposes.'}
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {spec}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
end
|
data/lib/can_opener.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
require "cancan"
|
3
|
+
require "can_opener/ability"
|
4
|
+
|
5
|
+
module CanOpener
|
6
|
+
include CanCan::Ability
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
base.extend ClassMethods
|
10
|
+
class << base;
|
11
|
+
attr_accessor :ability_classes
|
12
|
+
protected :ability_classes=
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def configure_abilities(&block)
|
18
|
+
yield self
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def add(*klasses)
|
24
|
+
self.ability_classes ||= []
|
25
|
+
self.ability_classes.concat Array(klasses)
|
26
|
+
self.ability_classes.uniq!
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(user)
|
31
|
+
self.class.ability_classes.each do |ability_class|
|
32
|
+
ability_class.new(self, user)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module CanOpener
|
2
|
+
class Ability
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def_delegators :@base, *CanCan::Ability.public_instance_methods
|
6
|
+
|
7
|
+
attr_accessor :user
|
8
|
+
protected :user=
|
9
|
+
|
10
|
+
def initialize(base, user)
|
11
|
+
@base = base
|
12
|
+
self.user = user
|
13
|
+
abilities
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require "cancan/matchers"
|
3
|
+
|
4
|
+
class AdminAbility < CanOpener::Ability
|
5
|
+
def abilities
|
6
|
+
if user.admin?
|
7
|
+
can :manage, :all
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class SupportAbility < CanOpener::Ability
|
13
|
+
def abilities
|
14
|
+
if user.support?
|
15
|
+
can :read, :all
|
16
|
+
can :write, :all
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class ReaderAbility < CanOpener::Ability
|
22
|
+
def abilities
|
23
|
+
can :read, :all
|
24
|
+
alias_action :read, :to => :speed_read
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class BannedAbility < CanOpener::Ability
|
29
|
+
def abilities
|
30
|
+
if user.banned?
|
31
|
+
cannot do |action, object_class, object|
|
32
|
+
true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Ability
|
39
|
+
include CanOpener
|
40
|
+
|
41
|
+
configure_abilities do |c|
|
42
|
+
c.add ReaderAbility
|
43
|
+
c.add SupportAbility
|
44
|
+
c.add AdminAbility
|
45
|
+
c.add BannedAbility
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe CanOpener do
|
50
|
+
def user_double(opts = {})
|
51
|
+
double(:user,
|
52
|
+
{ :admin? => false, :support? => false, :banned? => false }.merge(opts)
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
let(:admin_user) { user_double(:admin? => true) }
|
57
|
+
let(:support_user) { user_double(:support? => true) }
|
58
|
+
let(:reader_user) { user_double }
|
59
|
+
let(:banned_user) { user_double(:admin? => true, :banned? => true) }
|
60
|
+
|
61
|
+
describe "loading abilities" do
|
62
|
+
before(:each) { @ability = Ability.new(admin_user) }
|
63
|
+
|
64
|
+
it "should have the abilities loaded in the expected order" do
|
65
|
+
Ability.ability_classes.should == [ReaderAbility, SupportAbility, AdminAbility, BannedAbility]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "checking abilities" do
|
70
|
+
context "for the admin user" do
|
71
|
+
before(:each) { @ability = Ability.new(admin_user) }
|
72
|
+
|
73
|
+
it "should allow management" do
|
74
|
+
@ability.should be_able_to(:manage, :foo)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should allow reading" do
|
78
|
+
@ability.should be_able_to(:read, :foo)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should allow writing" do
|
82
|
+
@ability.should be_able_to(:write, :foo)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "for the support user" do
|
87
|
+
before(:each) { @ability = Ability.new(support_user) }
|
88
|
+
|
89
|
+
it "should not allow management" do
|
90
|
+
@ability.should_not be_able_to(:manage, :foo)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should allow reading" do
|
94
|
+
@ability.should be_able_to(:read, :foo)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should not allow writing" do
|
98
|
+
@ability.should be_able_to(:write, :foo)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context "for the reader user" do
|
103
|
+
before(:each) { @ability = Ability.new(reader_user) }
|
104
|
+
|
105
|
+
it "should not allow management" do
|
106
|
+
@ability.should_not be_able_to(:manage, :foo)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should allow reading" do
|
110
|
+
@ability.should be_able_to(:read, :foo)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should not allow writing" do
|
114
|
+
@ability.should_not be_able_to(:write, :foo)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "for the banned user" do
|
119
|
+
before(:each) { @ability = Ability.new(banned_user) }
|
120
|
+
|
121
|
+
it "should not allow management" do
|
122
|
+
@ability.should_not be_able_to(:manage, :foo)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should not allow reading" do
|
126
|
+
@ability.should_not be_able_to(:read, :foo)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should not allow writing" do
|
130
|
+
@ability.should_not be_able_to(:write, :foo)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "aliasing actions" do
|
136
|
+
it "should still work" do
|
137
|
+
ability = Ability.new(reader_user)
|
138
|
+
ability.should be_able_to(:read, :foo)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: can_opener
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Brendon Murphy
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-01-20 00:00:00 -08:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Split up your CanCan Ability by allowing you to easily create abilities in separate classes which you reference in your main ability model. This is mainly useful if you want to break down your abilities into smaller classes for organizational purposes.'
|
23
|
+
email:
|
24
|
+
- xternal1+github@gmail.com
|
25
|
+
executables: []
|
26
|
+
|
27
|
+
extensions: []
|
28
|
+
|
29
|
+
extra_rdoc_files: []
|
30
|
+
|
31
|
+
files:
|
32
|
+
- .gitignore
|
33
|
+
- Gemfile
|
34
|
+
- README.markdown
|
35
|
+
- Rakefile
|
36
|
+
- can_opener.gemspec
|
37
|
+
- lib/can_opener.rb
|
38
|
+
- lib/can_opener/ability.rb
|
39
|
+
- lib/can_opener/version.rb
|
40
|
+
- spec/can_opener_spec.rb
|
41
|
+
- spec/spec_helper.rb
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: http://github.com/bemurphy/can-opener
|
44
|
+
licenses: []
|
45
|
+
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
hash: 3
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
hash: 3
|
66
|
+
segments:
|
67
|
+
- 0
|
68
|
+
version: "0"
|
69
|
+
requirements: []
|
70
|
+
|
71
|
+
rubyforge_project:
|
72
|
+
rubygems_version: 1.3.7
|
73
|
+
signing_key:
|
74
|
+
specification_version: 3
|
75
|
+
summary: Split up your CanCan Ability
|
76
|
+
test_files: []
|
77
|
+
|