moulin_rouge 0.0.1.alpha

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ CHANGELOG
2
+ =========
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
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.
@@ -0,0 +1,215 @@
1
+ Moulin Rouge
2
+ ============
3
+
4
+ **Moulin Rouge** is a DSL to manage your permissions and groups of access outside the [CanCan](https://github.com/ryanb/cancan) Ability class. It will help you organize and declare your permissions with has much ruby files you judge necessary, that are automatically pushed to CanCan authorization system. It is also decoupled from the role system.
5
+
6
+ There are a bunch of examples bellow to show you how to implement.
7
+
8
+ Installation
9
+ ------------
10
+
11
+ Add the gem to your Gemfile:
12
+
13
+ gem 'moulin_rouge'
14
+
15
+ And install it
16
+
17
+ bundle install
18
+
19
+ Run the generator to install the roles and permissions structure:
20
+
21
+ rails g moulinrouge:install
22
+
23
+ Generate a permission file:
24
+
25
+ rails g moulinrouge:permission <name>
26
+
27
+ Add your permissions to newly created file:
28
+
29
+ ```ruby
30
+ role :name do
31
+ can :read, :something
32
+ end
33
+ ```
34
+
35
+ By default your `current_user` method should respond to `is?`, but you can change this method to match your role system at the configuration.
36
+
37
+ Usage
38
+ -----
39
+
40
+ First of all, you have to accept that the role registering belongs to the ruby code. Organizing this ruby files it's a really important issue to any large application.
41
+
42
+ When you run:
43
+
44
+ bundle g moulinrouge:install
45
+
46
+ This will create the following folder structure:
47
+
48
+ root
49
+ | app/
50
+ | | permissions/
51
+ | config/
52
+ | | initalizers
53
+ | | | moulin_rouge.rb
54
+
55
+ ### Defining roles ###
56
+
57
+ All your permission files will be stored inside the `app/permissions` folder. Just create a ruby file inside and the definitions will be automatically defined.
58
+
59
+ ```ruby
60
+ role :superuser do
61
+ can :manage, :all
62
+ end
63
+
64
+ role :editors do
65
+ can :manage, Articles
66
+ end
67
+
68
+ role :authors do
69
+ can :manage, Article do |article|
70
+ article.user_id == current_user.id
71
+ end
72
+ end
73
+ ```
74
+
75
+ Note that the `can` method is the **same** for defining abilities in CanCan, and will be passed as they are on the Ability class. See [Defining Abilities](https://github.com/ryanb/cancan/wiki/defining-abilities) for more information on what you `can` do.
76
+
77
+ Also, the others CanCan methods are avaliable (`cannot`, `can?`, `cannot?`) and will act like expected.
78
+
79
+ ### Groups ###
80
+
81
+ A group is an easy way to organize your permissions, no matter where file the definition is. All groups with the same name, will have their abilities and permissions nested together.
82
+
83
+ The group will delegate all abilities defined into, to their childrens, so any children role or group will have the abilities defined in the parent. Also the group is not an avaliable role, they only serve has namespaces.
84
+
85
+ ```ruby
86
+ group :marketing do
87
+ can :read, Dashboard
88
+
89
+ role :manager do
90
+ can :manage, Proposal
91
+ end
92
+
93
+ role :salesman do
94
+ can :manage, Proposal, :user_id => current_user.id
95
+ end
96
+ end
97
+ ```
98
+
99
+ To avoid name conflicts, whenever you have a nested roles or groups, their name will be prefixed with the parent name separeted by a `_` underscore just like they were namespaced.
100
+
101
+ Following the example above, will generate two roles:
102
+
103
+ ```ruby
104
+ MoulinRouge::Permission.list
105
+ # => [:marketing_manager, :marketing_salesman]
106
+ # => :marketing_manager => can :read, Dashboard, can :manage, Proposal
107
+ # => :marketing_salesman => can :read, Dashboard, can :manage, Proposal, :user_id => current_user.id
108
+ ```
109
+
110
+ ### Nested roles ###
111
+
112
+ When you have nested rules, they will act has namespaces, no ability will be shared unless is explicited with the `include` method.
113
+
114
+ ```ruby
115
+ role :marketing do
116
+ can :manage, Proposal
117
+
118
+ role :salesman do
119
+ can :read, Proposal
120
+ end
121
+ end
122
+ ```
123
+
124
+ Following the example above, this will generate two roles with the abilities:
125
+
126
+ ```ruby
127
+ MoulinRouge::Permission.list
128
+ # => [:marketing, :marketing_salesman]
129
+ # => :marketing => can :manage, Proposal
130
+ # => :marketing_salesman => can :read, Proposal
131
+ ```
132
+
133
+ ### Extending ###
134
+
135
+ Many times you want to extend a role from another one, **MoulinRouge** let you `include` the abilities from one role to another, all the abilities from the target will be appended to the caller.
136
+
137
+ Only roles can be included, and if you provide a name that isn't defined, a `RoleNotFound` will be raised. Notice by the example bellow, that you should provide the full name of the role in order to find the correct one.
138
+
139
+ ```ruby
140
+ group :marketing do
141
+ role :admin do
142
+ can :do, :something
143
+ end
144
+ end
145
+
146
+ role :super do
147
+ include :marketing_admin
148
+ end
149
+ ```
150
+
151
+ Configuration
152
+ -------------
153
+
154
+ ```ruby
155
+ MoulinRouge.configure do |config|
156
+ # Cache permissions
157
+ config.cache = Rails.env.production?
158
+ # The search path for permissions
159
+ config.path = 'app/permissions/**/*.rb'
160
+ # The method that will test the permission
161
+ config.test_method = :is?
162
+ # The class of the model
163
+ config.model = User
164
+ # How you like to call the active user model
165
+ config.model_instance = :current_user
166
+ end
167
+ ```
168
+
169
+ Goodies
170
+ -------
171
+
172
+ For those who like I dislikes the `load_and_authorize_resource` method from CanCan, here is provided a cleaner and more flexible solution through `ActionController::Responder`, the `MoulinRouge::CanCan::Responder` bellow there are instruction to activate them.
173
+
174
+ Create the file `lib/application_responder.rb` with the following:
175
+
176
+ ```ruby
177
+ require 'moulin_rouge/cancan/responder'
178
+
179
+ class ApplicationResponder < ActionController::Responder
180
+ include MoulinRouge::CanCan::Responder
181
+ end
182
+ ```
183
+
184
+ And on your `application_controller.rb` just add the responder:
185
+
186
+ ```ruby
187
+ require 'application_responder'
188
+
189
+ class ApplicationController < ActionController::Base
190
+ protect_from_forgery
191
+ self.responder = ApplicationResponder
192
+ ...
193
+ end
194
+ ```
195
+
196
+ More about the `Responder` class:
197
+
198
+ * http://blog.plataformatec.com.br/2009/08/embracing-rest-with-mind-body-and-soul/
199
+ * http://archives.ryandaigle.com/articles/2009/8/6/what-s-new-in-edge-rails-cleaner-restful-controllers-w-respond_with/
200
+
201
+ Thanks
202
+ =======
203
+
204
+ * [Troles](https://github.com/kristianmandrup/trole)
205
+ * [CanTango](https://github.com/kristianmandrup/cantango)
206
+ * [Declarative Authorization](https://github.com/stffn/declarative_authorization)
207
+
208
+ ### A little of history ###
209
+
210
+ Mouling Rouge is a cabaret in Paris best know as the birthplace on modern [CanCan](https://github.com/ryanb/cancan) second to [Wikipedia](http://en.wikipedia.org/wiki/Moulin_Rouge).
211
+
212
+ Credits
213
+ =======
214
+
215
+ **Edson Hilios** <edson (at) hilios (dot) com (dot) br>
data/init.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'moulin_rouge'
2
+
3
+ MoulinRouge.run!
@@ -0,0 +1,44 @@
1
+ Dir[File.expand_path("moulin_rouge/**/*.rb", File.dirname(__FILE__))].each {|f| require f;}
2
+
3
+ module MoulinRouge
4
+ # Returns the global Configuration object.
5
+ def self.configuration
6
+ @configuration ||= MoulinRouge::Configuration.new
7
+ end
8
+
9
+ # Yields the global configuration to a block.
10
+ def self.configure
11
+ yield configuration if block_given?
12
+ end
13
+
14
+ # Create the main permission, import all permission files and
15
+ # define the Ability class require for CanCan
16
+ def self.run!
17
+ self.load!
18
+ # Create the ability class
19
+ Object.const_set 'Ability', Class.new(MoulinRouge::CanCan::Ability) unless Object.const_defined? 'Ability'
20
+ # Change flag
21
+ @@run = true
22
+ end
23
+
24
+ # Import all permission files in the configuration
25
+ def self.load!
26
+ MoulinRouge::Permission.main.import(MoulinRouge.configuration.path)
27
+ end
28
+
29
+ # Returns true if the run! method was called and false oterwise
30
+ def self.run?
31
+ @@run ||= false
32
+ end
33
+
34
+ # Reset all permission and load them again
35
+ def self.reload!
36
+ reset!
37
+ load!
38
+ end
39
+
40
+ # Reset all constants
41
+ def self.reset! #:nodoc:
42
+ MoulinRouge::Permission.reset!
43
+ end
44
+ end
@@ -0,0 +1,25 @@
1
+ require 'cancan'
2
+
3
+ module MoulinRouge
4
+ module CanCan
5
+ class Ability
6
+ include ::CanCan::Ability
7
+ # Define all permissions collect by MoulinRouge
8
+ def initialize(model)
9
+ model = MoulinRouge.configuration.model.new if model.nil? and not MoulinRouge.configuration.model.nil?
10
+ # Reload all permissions if cache is disabled
11
+ MoulinRouge.reload! unless MoulinRouge.configuration.cache
12
+ # Set all permissions in main
13
+ MoulinRouge::Permission.main.abilities.each do |ability|
14
+ ability.send_to(self, model)
15
+ end
16
+ # Set all permissions by roles
17
+ MoulinRouge::Permission.all.each do |role, permission|
18
+ permission.abilities.each do |ability|
19
+ ability.send_to(self, model)
20
+ end if model.send(MoulinRouge.configuration.test_method, role)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,24 @@
1
+ module MoulinRouge
2
+ module CanCan
3
+ class Method
4
+ attr_reader :name, :args, :block
5
+ def initialize(name, *args, &block)
6
+ @name, @args, @block = name, args, block
7
+ end
8
+
9
+ # Send this method to the given object
10
+ def send_to(object, proc_scope = nil)
11
+ # Evaluate any proc in args
12
+ if args.last.is_a?(Hash)
13
+ args.last.each do |key, value|
14
+ if value.is_a?(Proc)
15
+ args.last[key] = value.call(proc_scope || object)
16
+ end
17
+ end
18
+ end
19
+ # Send
20
+ object.send(name, *args, &block)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,7 @@
1
+ module MoulinRouge
2
+ module CanCan
3
+ class Responder # < ActionController::Responder
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ require 'rbconfig'
2
+
3
+ module MoulinRouge
4
+ class Configuration
5
+ attr_accessor :path, :test_method, :cache, :model_instance, :model
6
+
7
+ def initialize
8
+ @path = "app/permissions/**/*.rb"
9
+ @test_method = :is?
10
+ @cache = true
11
+ @model = nil
12
+ @model_instance = :current_user
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ module MoulinRouge
2
+ class ModelDouble
3
+ # Return an block for later evaluation
4
+ def method_missing(name, *args, &block)
5
+ lambda do |scope|
6
+ scope.send(name, *args, &block)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,140 @@
1
+ module MoulinRouge
2
+ class RoleNotFound < Exception; end
3
+ # A wrapper to catch and store the DSL methods
4
+ class Permission
5
+ CANCAN_METHODS = [:can, :cannot, :can?, :cannot?]
6
+ # Returns a string with the given name
7
+ attr_reader :singular_name
8
+
9
+ # Returns a instance of the parent permission.
10
+ # When nil this is the main permission.
11
+ attr_reader :parent
12
+
13
+ # Returns true if it's a group or flase otherwise
14
+ attr_reader :is_a_group
15
+
16
+ # Creates a new permission and evaluate the given block
17
+ # with roles, groups and abilities on this scope.
18
+ # If the parent is a group append all their abilities to self
19
+ def initialize(name, options = {}, &block)
20
+ @singular_name = name
21
+ @parent = options.delete(:parent)
22
+ @is_group = options.delete(:group)
23
+ abilities.concat(parent.abilities) if not parent.nil? and parent.group?
24
+ instance_eval(&block) if block_given?
25
+ # Store this permission
26
+ self.class.add(self) unless parent.nil? or @is_group
27
+ end
28
+
29
+ def method_missing(name, *args, &block)
30
+ return store_method(name, *args, &block) if CANCAN_METHODS.include?(name)
31
+ return MoulinRouge::ModelDouble.new if MoulinRouge.configuration.model_instance == name
32
+ super(name, *args, &block)
33
+ end
34
+
35
+ # Define a new role inside this scope. If exists a role with the
36
+ # same name evaluate the block inside them instead of create a new one
37
+ def role(name, options = {}, &block)
38
+ if children = find(name)
39
+ children.instance_eval(&block)
40
+ children
41
+ else
42
+ childrens << self.class.new(name, options.merge!({:parent => self}), &block)
43
+ childrens.last
44
+ end
45
+ end
46
+
47
+ # Define a group inside this scope
48
+ def group(name, options = {}, &block)
49
+ options.merge!({:group => true})
50
+ role(name, options, &block)
51
+ end
52
+
53
+ # Returns true if is a group
54
+ def group?
55
+ @is_group
56
+ end
57
+
58
+ # Add the given parameters to the authorizations list
59
+ def store_method(name, *args, &block)
60
+ abilities << MoulinRouge::CanCan::Method.new(name, *args, &block)
61
+ abilities.last
62
+ end
63
+
64
+ # Returns an array with all childrens
65
+ def childrens
66
+ @childrens ||= []
67
+ end
68
+
69
+ # Returns an array with all authorizations declared on this permission
70
+ def abilities
71
+ @abilities ||= []
72
+ end
73
+
74
+ # Returns all abilities for this permission.
75
+ # If this is a group, grab the abilities from parent.
76
+ def inherithed_abilities
77
+ abilities.concat(childrens.map(&:inherithed_abilities).flatten).uniq
78
+ end
79
+
80
+ # Execute all files in the given path in the class scope
81
+ def import(path)
82
+ Dir[path].each { |file| instance_eval(File.open(file).read) }
83
+ end
84
+
85
+ # Returns the instance of the children with the given name if exists and nil otherwise
86
+ def find(name)
87
+ childrens.each { |children| return children if children.name == name or children.singular_name == name }
88
+ return nil
89
+ end
90
+
91
+ # Appends all childrens and abilities from one object to another,
92
+ # raises an error if could not found a match.
93
+ def include(name)
94
+ unless from = self.class.all[name]
95
+ raise RoleNotFound
96
+ end
97
+ from.childrens.each { |children| childrens << children.dup }
98
+ from.abilities.each { |ability| abilities << ability.dup }
99
+ end
100
+
101
+ # Returns a symbol with the name appended with the parents separeted by a underscore
102
+ def name
103
+ unless @name
104
+ @name = []
105
+ @name << self.parent.name.to_s if not self.parent.nil? and not self.parent.parent.nil?
106
+ @name << self.singular_name.to_s
107
+ @name = @name.join('_')
108
+ end
109
+ @name.to_sym
110
+ end
111
+
112
+ class << self
113
+ # The instance of the main container, if don't exist create one
114
+ def main
115
+ @main ||= self.new(:main)
116
+ end
117
+ # Returns an array with the name of all roles created
118
+ def list
119
+ @list ||= []
120
+ end
121
+
122
+ # Returns an hash with all permissions defined
123
+ def all
124
+ @all ||= {}
125
+ end
126
+
127
+ # Stores the instance on the all hash and add the name to the list
128
+ def add(instance)
129
+ name = instance.name
130
+ self.all[name] = instance
131
+ self.list << name unless self.list.include?(name)
132
+ end
133
+
134
+ # Reset all constants
135
+ def reset! #:nodoc:
136
+ @main, @list, @all = nil
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,3 @@
1
+ module MoulinRouge
2
+ VERSION = "0.0.1.alpha"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :moulin_rouge do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,3 @@
1
+ role :test do
2
+ can :do, :something
3
+ end
@@ -0,0 +1,89 @@
1
+ require 'spec_helper'
2
+
3
+ describe MoulinRouge::CanCan::Ability do
4
+ let(:test_method) { MoulinRouge.configuration.test_method.to_sym }
5
+ let(:model) { double("model", test_method => true) }
6
+ let(:ability) { MoulinRouge::CanCan::Ability.new(model) }
7
+ let(:permission) { MoulinRouge::Permission.main }
8
+
9
+ before(:each) do
10
+ MoulinRouge::CanCan::Ability.any_instance.stub(:can) { true }
11
+ # The current permission will create the following abilities
12
+ # for each role because of nested rules:
13
+ # => can(:do, :this) # for everybody
14
+ # :role => can(:do, :something)
15
+ # :role_nested => can(:create, :something)
16
+ # :group_nested => can(:read, :something), can(:edit, :something)
17
+ permission.can(:do, :this)
18
+ permission.role(:role) do
19
+ can(:do, :something)
20
+
21
+ role(:nested) do
22
+ can(:create, :something)
23
+ end
24
+ end
25
+ permission.group(:group) do
26
+ can :read, :something
27
+
28
+ role(:nested) do
29
+ can :edit, :something
30
+ end
31
+ end
32
+ end
33
+
34
+ it "includes the CanCan::Ability module" do
35
+ ability.should be_a(CanCan::Ability)
36
+ end
37
+
38
+ describe "#initialize" do
39
+ it "call test_method in model for each permission" do
40
+ model.should_receive(test_method).with(:role)
41
+ model.should_receive(test_method).with(:role_nested)
42
+ model.should_receive(test_method).with(:group_nested)
43
+ ability
44
+ end
45
+
46
+ it "call can method for each ability of the permission" do
47
+ # First all permissions in main
48
+ MoulinRouge::CanCan::Ability.any_instance.should_receive(:can).with(:do, :this).once
49
+ # Please read the expetations explanation at before(:each) method
50
+ MoulinRouge::CanCan::Ability.any_instance.should_receive(:can).with(:do, :something).once
51
+ MoulinRouge::CanCan::Ability.any_instance.should_receive(:can).with(:create, :something).once
52
+ MoulinRouge::CanCan::Ability.any_instance.should_receive(:can).with(:read, :something).once
53
+ MoulinRouge::CanCan::Ability.any_instance.should_receive(:can).with(:edit, :something).once
54
+ ability # Execute
55
+ end
56
+
57
+ it "executes the can method with exactly the same arguments and block that was stored" do
58
+ # Concat permissions from main and from all defined classes
59
+ abilities = permission.abilities + MoulinRouge::Permission.all.values.map(&:abilities)
60
+ abilities.flatten.each do |ability|
61
+ MoulinRouge::CanCan::Ability.any_instance.should_receive(:can).with(*ability.args, &ability.block).at_least(:once)
62
+ end
63
+ ability # Execute
64
+ end
65
+
66
+ it "reloads all permissions when cache is set to false" do
67
+ MoulinRouge.configuration.cache = false
68
+ MoulinRouge.should_receive(:reload!).once
69
+ ability # Execute
70
+ end
71
+
72
+ it "creates a new instance of model if the config is not nil and the initializer argument is also nil" do
73
+ klass = Class.new
74
+ klass.stub(:new) { model }
75
+ # Test
76
+ klass.should_receive(:new).once
77
+ MoulinRouge.configuration.model = klass
78
+ MoulinRouge::CanCan::Ability.new(nil)
79
+ end
80
+
81
+ it "executes any proc on the model object" do
82
+ MoulinRouge::Permission.reset!
83
+ ####
84
+ MoulinRouge::Permission.main.can(:do, :this, :user_id => MoulinRouge::ModelDouble.new.id)
85
+ model.should_receive(:id)
86
+ ability # Execute
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe MoulinRouge::CanCan::Method do
4
+ let(:name) { :can }
5
+ let(:args) { [:one, :two] }
6
+ let(:proc) { Proc.new { :block } }
7
+ let(:object) { double(method.name => true) }
8
+ let(:method) { MoulinRouge::CanCan::Method.new(name, *args, &proc) }
9
+
10
+ describe "#args" do
11
+ it "is a reader attribute" do
12
+ method.args.should eq(args)
13
+ end
14
+ end
15
+
16
+ describe "#block" do
17
+ it "is a reader attribute" do
18
+ method.block.should eq(proc)
19
+ end
20
+ end
21
+
22
+ describe "#name" do
23
+ it "is a reader attribute" do
24
+ method.name.should eq(name)
25
+ end
26
+ end
27
+
28
+ describe "#send_to" do
29
+ it "sends this method to the given object" do
30
+ object.should_receive(method.name).with(*method.args, &method.block)
31
+ method.send_to(object)
32
+ end
33
+
34
+ it "evaluate any arguments that are proc" do
35
+ method = MoulinRouge::CanCan::Method.new(:can, :do, :something, :on => proc)
36
+ method.send_to(object)
37
+ method.args.last[:on].should be(:block)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe MoulinRouge::CanCan::Responder do
4
+
5
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe MoulinRouge::Configuration do
4
+ let(:config) { MoulinRouge::Configuration.new }
5
+
6
+ describe "#path" do
7
+ it "returns a string" do
8
+ config.path.should be_a(String)
9
+ end
10
+ end
11
+
12
+ describe "#path=" do
13
+ it "sets the value" do
14
+ config.path = "test"
15
+ config.path.should eq("test")
16
+ end
17
+ end
18
+
19
+ describe "#test_method" do
20
+ it "returns a symbol" do
21
+ config.test_method.should be_a(Symbol)
22
+ end
23
+ end
24
+
25
+ describe "#test_method=" do
26
+ it "sets the value" do
27
+ config.test_method = :"test"
28
+ config.test_method.should eq(:"test")
29
+ end
30
+ end
31
+
32
+ describe "#cache" do
33
+ it "returns a boolean" do
34
+ config.cache.should be_true
35
+ end
36
+ end
37
+
38
+ describe "#cache=" do
39
+ it "sets the value" do
40
+ config.cache = false
41
+ config.cache.should eq(false)
42
+ end
43
+ end
44
+
45
+ describe "#model_instance" do
46
+ it "returns a boolean" do
47
+ config.model_instance.should be_a(Symbol)
48
+ end
49
+ end
50
+
51
+ describe "#model_instance=" do
52
+ it "sets the value" do
53
+ config.model_instance = :user
54
+ config.model_instance.should eq(:user)
55
+ end
56
+ end
57
+
58
+ describe "#model" do
59
+ it "returns an class or nil" do
60
+ config.model.should be_nil
61
+ end
62
+ end
63
+
64
+ describe "#model=" do
65
+ it "sets the class" do
66
+ klass = Class.new
67
+ config.model = klass
68
+ config.model.should eq(klass)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe MoulinRouge::ModelDouble do
4
+ let(:model) { MoulinRouge::ModelDouble.new }
5
+
6
+ it "returns an proc for every method called in this class" do
7
+ model.id.should be_a(Proc)
8
+ model.abc.should be_a(Proc)
9
+ end
10
+
11
+ it "the proc sends the methods to the given scope with all arguments" do
12
+ object = double(:id => true)
13
+ object.should_receive(:id).with(:this).once
14
+ model.id(:this).call(object)
15
+ end
16
+ end
@@ -0,0 +1,316 @@
1
+ require 'spec_helper'
2
+
3
+ describe MoulinRouge::Permission do
4
+ let(:permission) { MoulinRouge::Permission.new(:name) }
5
+
6
+ describe "#initialize" do
7
+ it "evaluate the groups and authorizations to the class scope" do
8
+ permission = MoulinRouge::Permission.new(:scope) do
9
+ self.class.should == MoulinRouge::Permission
10
+ can :do, :something
11
+ role :for
12
+ end
13
+ end
14
+
15
+ it "append the new instance to #all and #list if it's a role" do
16
+ MoulinRouge::Permission.new(:main) do
17
+ role(:one)
18
+ end
19
+ MoulinRouge::Permission.all.should include(:one)
20
+ MoulinRouge::Permission.list.should include(:one)
21
+ end
22
+
23
+ it "inherits all abilities from parent if this is a group" do
24
+ role = nil
25
+ MoulinRouge::Permission.new(:main) do
26
+ group(:group) do
27
+ can :do, :this
28
+ role = role(:role)
29
+ end
30
+ end
31
+ role.abilities.first.args.should include(:do, :this)
32
+ end
33
+ end
34
+
35
+ describe "#name" do
36
+ it "returns the given name on class initialization" do
37
+ permission.name.should be(:name)
38
+ end
39
+ end
40
+
41
+ describe "#parent" do
42
+ it "returns nil when for main" do
43
+ permission.parent.should be_nil
44
+ end
45
+
46
+ it "returns a instance of the MoulinRouge::Permission" do
47
+ another = MoulinRouge::Permission.new(:another, :parent => permission)
48
+ another.parent.should be_a(MoulinRouge::Permission)
49
+ another.parent.should be(permission)
50
+ end
51
+ end
52
+
53
+ describe "#childrens" do
54
+ it "returns an array" do
55
+ permission.childrens.should be_an(Array)
56
+ end
57
+ end
58
+
59
+ describe "#abilities" do
60
+ it "returns an array" do
61
+ permission.abilities.should be_a(Array)
62
+ end
63
+ end
64
+
65
+ describe "#inherithed_abilities" do
66
+ it "returns all abilities from self and their childrens" do
67
+ permission.can :do, :this
68
+ # First nested level
69
+ one = permission.role(:one) do
70
+ can :do, :one
71
+ end
72
+ # Second nested level
73
+ two = one.role(:two) do
74
+ can :do, :two
75
+ end
76
+ permission.inherithed_abilities.should be_an(Array)
77
+ permission.inherithed_abilities.length.should be(3)
78
+ one.inherithed_abilities.length.should be(2)
79
+ two.inherithed_abilities.length.should be(1)
80
+ end
81
+ end
82
+
83
+ describe "#role" do
84
+ it "returns a new permission with the parent setted to the class that are calling" do
85
+ role = permission.role(:test)
86
+ role.should be_a(MoulinRouge::Permission)
87
+ role.parent.should be(permission)
88
+ end
89
+
90
+ it "adds a new instance on children" do
91
+ permission.childrens.should be_empty
92
+ permission.role(:name)
93
+ permission.childrens.should_not be_empty
94
+ end
95
+
96
+ it "appends the content if the name is already present" do
97
+ permission.role(:test) { can :do, :this }
98
+ # should create one children ...
99
+ permission.childrens.length.should be(1)
100
+ # ... and one ability
101
+ permission.childrens.first.abilities.length.should be(1)
102
+
103
+ permission.role(:test) { can :do, :this }
104
+ # should not create other children ...
105
+ permission.childrens.length.should be(1)
106
+ # ... and append the new ability
107
+ permission.childrens.first.abilities.length.should be(2)
108
+ end
109
+ end
110
+
111
+ describe "#group" do
112
+ it "not add the group name to permission list" do
113
+ permission.group(:test)
114
+ MoulinRouge::Permission.list.should_not include(:test)
115
+ end
116
+ end
117
+
118
+ describe "#group?" do
119
+ it "returns true if is a group and false otherwise" do
120
+ role = permission.role(:role)
121
+ group = permission.group(:group)
122
+ role.group?.should be_false
123
+ group.group?.should be_true
124
+ end
125
+ end
126
+
127
+ describe "#method_missing" do
128
+ let(:args) { [:one, :two] }
129
+ let(:proc) { Proc.new { :block } }
130
+
131
+ context "collect all cancan methods and store under abilities" do
132
+ MoulinRouge::Permission::CANCAN_METHODS.each do |method_name|
133
+ describe "##{method_name}" do
134
+ it "adds a new ability to this permission" do
135
+ permission.abilities.should be_empty
136
+ permission.send(method_name, *args, &proc)
137
+ permission.abilities.should_not be_empty
138
+ end
139
+
140
+ it "stores nil on block attribute when no block is given" do
141
+ permission.send(method_name, *args)
142
+ permission.abilities.first.block.should be_nil
143
+ end
144
+
145
+ it "stores the method" do
146
+ permission.abilities.should be_empty
147
+ permission.send(method_name, *args, &proc)
148
+ permission.abilities.should_not be_empty
149
+
150
+ method = permission.abilities.first
151
+ method.should be_a(MoulinRouge::CanCan::Method)
152
+ # Evaluate the class of the arguments and block
153
+ method.args.should be_a(Array)
154
+ method.block.should be_a(Proc)
155
+ # Just check the value
156
+ method.args.should eq(args)
157
+ method.block.call.should be(:block)
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ it "receives and stores in a proc calls on the model object" do
164
+ ability = nil
165
+ permission.role(:test) do
166
+ ability = can :do, :this, :user_id => current_user.id
167
+ end
168
+ ability.args.last[:user_id].should be_a(Proc)
169
+ end
170
+
171
+ it "raise an error if the method is not registered has a cancan method" do
172
+ lambda { permission.send(:abcdefg, *args, &proc) }.should raise_error(NoMethodError)
173
+ end
174
+ end
175
+
176
+ describe "#import" do
177
+ let(:files_opened) { [] }
178
+
179
+ it "glob all files in the given path and evaluate their content in the class scope" do
180
+ File.stub(:open) { |file| files_opened << file; double("file", :read => "") }
181
+ permission.import(MoulinRouge.configuration.path)
182
+ files_opened.should include(*Dir[MoulinRouge.configuration.path])
183
+ end
184
+
185
+ it "let raise exceptions when there are syntax errors" do
186
+ tests = []
187
+ tests << %|
188
+ # Wrong method name
189
+ roe :name do
190
+ end
191
+ |
192
+ tests << %|
193
+ # Wrong method name
194
+ groups :name do
195
+ end
196
+ |
197
+ tests << %|
198
+ # Wrong method name
199
+ cn :
200
+ |
201
+ tests << %|
202
+ # Wrong method name
203
+ role do
204
+ end
205
+ |
206
+ # Execute them all
207
+ tests.each do |test|
208
+ create_permission test
209
+ lambda { permission.import(MoulinRouge.configuration.path) }.should raise_error
210
+ end
211
+ end
212
+ end
213
+
214
+ describe "#find" do
215
+ it "returns the instance of the permission if there is children with this name and nil otherwise" do
216
+ role = permission.role(:test)
217
+ permission.find(:test).should be(role)
218
+ permission.find(:bad).should be_nil
219
+ end
220
+ end
221
+
222
+ describe "#name" do
223
+ it "returns an symbol containing the name with the parents separeted by a underscore" do
224
+ first_children, second_children = nil
225
+ MoulinRouge::Permission.new(:main) do
226
+ first_children = role(:one) do
227
+ second_children = role(:two)
228
+ end
229
+ end
230
+ first_children.name.should be(:one)
231
+ second_children.name.should be(:one_two)
232
+ end
233
+ end
234
+
235
+ describe "#include" do
236
+ it "appends all childrens and abilities from one object to another" do
237
+ one, another = nil
238
+ one = permission.role(:one) do
239
+ role(:nested)
240
+ can :do, :something
241
+ end
242
+ another = permission.role(:another) do
243
+ include :one
244
+ end
245
+ another.childrens.should_not be_empty
246
+ another.abilities.should_not be_empty
247
+ another.childrens.first.singular_name.should be(:nested)
248
+ another.abilities.first.args.should include(:do, :something)
249
+ end
250
+
251
+ it "raises an error if could not find the requested permission" do
252
+ lambda {
253
+ permission.role(:name) do
254
+ include :not_found
255
+ end
256
+ }.should raise_error(MoulinRouge::RoleNotFound)
257
+ end
258
+ end
259
+
260
+ context "self" do
261
+ describe "#main" do
262
+ it "returns the main MoulinRouge::Permission instance" do
263
+ MoulinRouge::Permission.main.should be_instance_of(permission.class)
264
+ end
265
+ end
266
+
267
+ describe "#list" do
268
+ it "returns an array" do
269
+ MoulinRouge::Permission.list.should be_a(Array)
270
+ end
271
+ end
272
+
273
+ describe "#all" do
274
+ it "returns an hash with all created permissions" do
275
+ MoulinRouge::Permission.all.should be_a(Hash)
276
+ end
277
+
278
+ it "store all permissions instantiated unless the main one" do
279
+ hello_world = permission.role(:hello_world) do
280
+ can :do
281
+ end
282
+ MoulinRouge::Permission.all[hello_world.name].should be(hello_world)
283
+ end
284
+ end
285
+
286
+ describe "#add" do
287
+ it "should append the object instance on Permission.all and Permission.names" do
288
+ object = double(:name => :foo)
289
+ MoulinRouge::Permission.stub(:all) { @all ||= double() }
290
+ MoulinRouge::Permission.stub(:list) { @list ||= double(:include? => false) }
291
+ MoulinRouge::Permission.list.should_receive(:'<<').with(object.name)
292
+ MoulinRouge::Permission.all.should_receive(:'[]=').with(object.name, object)
293
+ MoulinRouge::Permission.add(object)
294
+ end
295
+ end
296
+
297
+ describe "#reset!" do
298
+ it "sets to nil the main, list and all constants" do
299
+ # Create some
300
+ permission.role(:one)
301
+ permission.role(:two)
302
+ MoulinRouge::Permission.all.should_not be_empty
303
+ MoulinRouge::Permission.list.should_not be_empty
304
+ # Apply
305
+ MoulinRouge::Permission.reset!
306
+ # Evaluate constants
307
+ MoulinRouge::Permission.instance_variable_get(:'@main').should be_nil
308
+ MoulinRouge::Permission.instance_variable_get(:'@all').should be_nil
309
+ MoulinRouge::Permission.instance_variable_get(:'@list').should be_nil
310
+ # Evaluate has arrays
311
+ MoulinRouge::Permission.all.should be_empty
312
+ MoulinRouge::Permission.list.should be_empty
313
+ end
314
+ end
315
+ end
316
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe MoulinRouge do
4
+ it "should be a module" do
5
+ MoulinRouge.should be_a(Module)
6
+ end
7
+
8
+ context "self" do
9
+ describe "#configuration" do
10
+ it "returns the same object every time" do
11
+ MoulinRouge.configuration.should equal(MoulinRouge.configuration)
12
+ end
13
+ end
14
+
15
+ describe "#configure" do
16
+ it "yields the current configuration" do
17
+ MoulinRouge.configure do |config|
18
+ config.should eq(MoulinRouge::configuration)
19
+ end
20
+ end
21
+ end
22
+
23
+ describe "#run!" do
24
+ it "calls the load! method" do
25
+ MoulinRouge.should_receive(:load!).once
26
+ MoulinRouge.run!
27
+ end
28
+
29
+ it "creates a class named Ability inherited from MoulinRouge::CanCan::Ability" do
30
+ MoulinRouge.run!
31
+ Object.const_get('Ability').should_not be_nil
32
+ Object.const_get('Ability').ancestors.should include(MoulinRouge::CanCan::Ability)
33
+ end
34
+ end
35
+
36
+ describe "#run?" do
37
+ it "returns true if the run! method was called and false oterwise" do
38
+ MoulinRouge.class_variable_set(:'@@run', false)
39
+ MoulinRouge.run?.should be_false
40
+ MoulinRouge.run!
41
+ MoulinRouge.run?.should be_true
42
+ end
43
+ end
44
+
45
+ describe "#load!" do
46
+ context "(with stubs)" do
47
+ it "call import the config path on the main permission" do
48
+ MoulinRouge::Permission.main.should_receive(:import).with(MoulinRouge.configuration.path).once
49
+ MoulinRouge.load!
50
+ MoulinRouge::Permission.main.should_not be_nil
51
+ end
52
+ end
53
+
54
+ context "(without stubs)" do
55
+ it "evaluate all permissions in the path" do
56
+ MoulinRouge.load!
57
+ MoulinRouge::Permission.main.childrens.should_not be_empty
58
+ end
59
+ end
60
+ end
61
+
62
+ describe "reset!" do
63
+ it "calls the reset! method on Permission" do
64
+ MoulinRouge::Permission.should_receive(:reset!).twice # One in the before(:each) in spec_helper.rb and another here
65
+ MoulinRouge.reset!
66
+ end
67
+ end
68
+
69
+ describe "#reload!" do
70
+ it "Reset all permissions and load them again" do
71
+ MoulinRouge.should_receive(:reset!).twice # One in the before(:each) in spec_helper.rb and another here
72
+ MoulinRouge.should_receive(:load!).once
73
+ MoulinRouge.reload!
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,40 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'rubygems'
5
+ require 'bundler/setup'
6
+ require 'rspec'
7
+
8
+ # Requires supporting ruby files with custom matchers and macros, etc,
9
+ # in spec/support/ and its subdirectories.
10
+ Dir[File.expand_path("spec/support/**/*.rb")].each {|f| require f}
11
+
12
+ # Require all files from the project
13
+ Dir[File.expand_path("lib/**/*.rb")].each {|f| require f}
14
+
15
+ def permission_file
16
+ File.expand_path("spec/fixtures/spec_permission.rb")
17
+ end
18
+
19
+ def create_permission(content)
20
+ f = File.open(permission_file, 'w')
21
+ f.write(content)
22
+ ensure
23
+ f.close
24
+ end
25
+
26
+ RSpec.configure do |config|
27
+ config.mock_with :rspec
28
+ config.before do
29
+ MoulinRouge.configure do |config|
30
+ config.path = File.join(File.expand_path(File.dirname(__FILE__)), "/fixtures/**/*.rb")
31
+ end
32
+ end
33
+ # Reset the MoulinRouge global variables
34
+ # Remove the permission file created by the helper
35
+ config.after(:each) do
36
+ MoulinRouge.reset!
37
+ MoulinRouge.configuration.cache = true
38
+ FileUtils.rm_rf(permission_file) if File.exists?(permission_file)
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: moulin_rouge
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.alpha
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Edson Hilios
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-16 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: cancan
16
+ requirement: &19713720 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *19713720
25
+ - !ruby/object:Gem::Dependency
26
+ name: rdoc
27
+ requirement: &19713280 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *19713280
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &19712860 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *19712860
47
+ - !ruby/object:Gem::Dependency
48
+ name: guard-rspec
49
+ requirement: &19712420 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *19712420
58
+ - !ruby/object:Gem::Dependency
59
+ name: simplecov
60
+ requirement: &19712000 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *19712000
69
+ description: A wrapper to CanCan authorization system, allowing you to define permissions
70
+ in many ruby files. Add new functionality allowing you do more with less.
71
+ email:
72
+ - edson.hilios@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - CHANGELOG.md
78
+ - MIT-LICENSE
79
+ - README.md
80
+ - lib/tasks/moulin_rouge_tasks.rake
81
+ - lib/moulin_rouge.rb
82
+ - lib/moulin_rouge/model_double.rb
83
+ - lib/moulin_rouge/permission.rb
84
+ - lib/moulin_rouge/version.rb
85
+ - lib/moulin_rouge/cancan/responder.rb
86
+ - lib/moulin_rouge/cancan/ability.rb
87
+ - lib/moulin_rouge/cancan/method.rb
88
+ - lib/moulin_rouge/configuration.rb
89
+ - init.rb
90
+ - spec/fixtures/role.rb
91
+ - spec/spec_helper.rb
92
+ - spec/moulin_rouge_spec.rb
93
+ - spec/moulin_rouge/permission_spec.rb
94
+ - spec/moulin_rouge/configuration_spec.rb
95
+ - spec/moulin_rouge/cancan/method_spec.rb
96
+ - spec/moulin_rouge/cancan/responder_spec.rb
97
+ - spec/moulin_rouge/cancan/ability_spec.rb
98
+ - spec/moulin_rouge/model_double_spec.rb
99
+ homepage: https://github.com/hilios/moulin_rouge
100
+ licenses: []
101
+ post_install_message:
102
+ rdoc_options: []
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ! '>'
115
+ - !ruby/object:Gem::Version
116
+ version: 1.3.1
117
+ requirements: []
118
+ rubyforge_project:
119
+ rubygems_version: 1.8.10
120
+ signing_key:
121
+ specification_version: 3
122
+ summary: Organize your CanCan permissions in many files.
123
+ test_files:
124
+ - spec/fixtures/role.rb
125
+ - spec/spec_helper.rb
126
+ - spec/moulin_rouge_spec.rb
127
+ - spec/moulin_rouge/permission_spec.rb
128
+ - spec/moulin_rouge/configuration_spec.rb
129
+ - spec/moulin_rouge/cancan/method_spec.rb
130
+ - spec/moulin_rouge/cancan/responder_spec.rb
131
+ - spec/moulin_rouge/cancan/ability_spec.rb
132
+ - spec/moulin_rouge/model_double_spec.rb