active_restrictors 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,14 @@
1
+ == v0.2.0
2
+ * Removed any remaining Rails 2 support
3
+ * Added new parameter structure for defining restrictors
4
+ * Added parameter mapping to allow old restrictor definitions to continue to function
5
+ * Renamed :basic restrictor to :basic_user
6
+ * Added two new restrictor types: basic_model and :implicit
7
+ * Cleaned up restrictor functionality from both model directions
8
+ * Updated #full_restrictors to provide all restrictors
9
+ * Updated view helpers to provide all restrictors
10
+ * Updated form building to use proper *_ids naming scheme
11
+
1
12
  == v0.1.1
2
13
  * Fixed class eval blocks
3
14
  * Fixed hash variable naming
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :rubygems
2
+
3
+ gem 'activerecord', '~> 3.0'
4
+ gem 'rake'
5
+ gem 'minitest'
6
+ gem "sqlite3", :platform => [:ruby, :mswin, :mingw]
7
+ gem "jdbc-sqlite3", :platform => :jruby
8
+ gem "activerecord-jdbcsqlite3-adapter", :platform => :jruby
data/Gemfile.lock ADDED
@@ -0,0 +1,39 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activemodel (3.1.3)
5
+ activesupport (= 3.1.3)
6
+ builder (~> 3.0.0)
7
+ i18n (~> 0.6)
8
+ activerecord (3.1.3)
9
+ activemodel (= 3.1.3)
10
+ activesupport (= 3.1.3)
11
+ arel (~> 2.2.1)
12
+ tzinfo (~> 0.3.29)
13
+ activerecord-jdbc-adapter (1.2.1)
14
+ activerecord-jdbcsqlite3-adapter (1.2.1)
15
+ activerecord-jdbc-adapter (~> 1.2.1)
16
+ jdbc-sqlite3 (~> 3.7.2)
17
+ activesupport (3.1.3)
18
+ multi_json (~> 1.0)
19
+ arel (2.2.1)
20
+ builder (3.0.0)
21
+ i18n (0.6.0)
22
+ jdbc-sqlite3 (3.7.2)
23
+ minitest (2.10.0)
24
+ multi_json (1.0.4)
25
+ rake (0.9.2.2)
26
+ sqlite3 (1.3.5)
27
+ tzinfo (0.3.31)
28
+
29
+ PLATFORMS
30
+ java
31
+ ruby
32
+
33
+ DEPENDENCIES
34
+ activerecord (~> 3.0)
35
+ activerecord-jdbcsqlite3-adapter
36
+ jdbc-sqlite3
37
+ minitest
38
+ rake
39
+ sqlite3
data/README.rdoc CHANGED
@@ -1,6 +1,8 @@
1
1
  == ActiveRestrictors
2
2
 
3
- Chainable ActiveRecord restriction chaining.
3
+ Chainable ActiveRecord restrictions.
4
+
5
+ {<img src="https://secure.travis-ci.org/chrisroberts/active_restrictors.png" />}[http://travis-ci.org/chrisroberts/active_restrictors]
4
6
 
5
7
  === Overview
6
8
 
@@ -47,10 +49,12 @@ Now, suppose a User should only be allowed to to see a Fubar instance if the Fub
47
49
 
48
50
  add_restrictor(:permissions,
49
51
  :enabled => lambda{ User.current_user.fubars_enabled? },
50
- :value => :name,
51
- :multiple => true,
52
- :default_view_all => true,
53
- :user_values_only => lambda{ User.current_user }
52
+ :views => {
53
+ :value => :name,
54
+ :multiple => true,
55
+ :default_view_all => true,
56
+ :user_values_only => lambda{ User.current_user }
57
+ }
54
58
  )
55
59
  end
56
60
 
@@ -95,7 +99,25 @@ The second is on Fubar instances:
95
99
  %td= "#{pair.first}:"
96
100
  %td= pair.last
97
101
 
98
- == Advanced Restrictor
102
+ == Restrictor Types
103
+
104
+ === Basic User
105
+
106
+ - TODO
107
+
108
+ === Basic Model
109
+
110
+ - TODO
111
+
112
+ === Implicit
113
+
114
+ - TODO
115
+
116
+ === Full
117
+
118
+ - TODO
119
+
120
+ == Custom Restrictors
99
121
 
100
122
  === User custom
101
123
 
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.pattern = 'spec/*_spec.rb'
5
+ end
@@ -9,7 +9,8 @@ Gem::Specification.new do |s|
9
9
  s.email = 'chrisroberts.code@gmail.com'
10
10
  s.homepage = 'http://bitbucket.org/chrisroberts/active_restrictors'
11
11
  s.description = 'Restrictors for Models'
12
+ s.add_dependency 'activerecord', '~> 3.0'
12
13
  s.require_path = 'lib'
13
14
  s.extra_rdoc_files = ['README.rdoc', 'CHANGELOG.rdoc']
14
- s.files = %w(README.rdoc CHANGELOG.rdoc active_restrictors.gemspec) + Dir.glob("{lib}/**/*")
15
+ s.files = Dir.glob('**/*')
15
16
  end
@@ -3,60 +3,86 @@ module ActiveRestrictor
3
3
 
4
4
  # name:: Name of restrictor. For non-basic types this must be the name of the association.
5
5
  # opts:: Options hash. Valid options:
6
- # :type:: Should be set to :basic when applying simple condition only
7
- # :condition:: Condition string applied to User
8
- # :class:: Class restriction is based on
9
- # :value:: Attribute name of value to display
10
- # :multiple:: Allow user to select multiple items for restriction
11
- # :include_blank:: Include blank option in restriction selection
12
- # :user_custom:: Block that returns User scope (passed: User scope, self instance)
13
- # :user_association:: Name of association on user
14
- # :model_custom:: Block that returns Model scope (passed: self instance, User instance)
6
+ # :type:: :full, :implicit, :basic_model, :basic_user
7
+ # :class:: Class restriction is based on if not guessable
15
8
  # :enabled:: If restrictor is enabled. Must be boolean value or a callable object that returns boolean value
16
- # :default_view_all:: If user has not been assigned a restrictor, they see all unless set to false
17
- # :user_values_only:: User instance. Set this if you only want values set against user to be selectable
9
+ # :scope:: Scope or callable block returning scope
10
+ # :views =>
11
+ # :value:: Attribute name of value to display
12
+ # :multiple:: Multiple assignments allowed
13
+ # :include_blank:: Allow assignments to be unset
14
+ # :user_values_only:: User instance. Set this if you only want values set against user to be selectable
15
+ # :id:: Value method for selection (defaults to :id)
16
+ # :user_association:: Name of association on user (if different from restrictor name)
17
+ # :user_custom:: Block that returns User scope (passed: User scope, self instance) - Used when generic scope building is lacking
18
+ # :model_custom:: Block that returns Model scope (passed: self instance, User instance) - Used when generic scope building is lacking
19
+ # :default_allowed_all:: If source instance has no restriction assigned, it is viewable
20
+ # :default_view_all:: Alias for :default_allowed_all
18
21
  # Adds restrictions to Model
19
- # NOTE: Basic type signifies that condition is forced without
20
- # user interaction. This means no select options will be used
21
- # as basic restrictors are never seen by the user. Basic restrictors
22
- # are applied directly against the user model.
22
+ # NOTE: Basic types are run directly against the model culminating in a count to see if it is valid (ex for :basic_user: User.where(:allowed_to_do_stuff => true))
23
+ # Implicit type is run directly against the source model. (ex: Fubar.includes(:feebar).where(:feebars => {:user_id => User.current_user.id}))
24
+ # Full is a full restrictor using join tables and provides view helpers for management
23
25
  def add_restrictor(name, opts={})
24
26
  self.restrictors ||= []
25
- new_opts = {:name => name, :id => :id, :enabled => true, :type => :full, :include => []}.merge(opts)
26
- new_opts[:include] = [new_opts[:include]] unless new_opts[:include].is_a?(Array)
27
- new_opts[:include].push(name) unless new_opts[:include].map(&:to_s).include?(name.to_s)
28
- self.restrictors.push(new_opts)
29
- if(new_opts[:type] == :full)
30
- self.class_eval do
31
- # This just creates a helper that will grab activerecord
32
- # instance from passed in IDs for applied restriction
33
- alias_method "original_#{name}=".to_sym, "#{name}=".to_sym
34
- define_method("#{name}=") do |args|
35
- args = (args.is_a?(Array) ? args : Array(args)).find_all{|arg|arg.present?}
36
- new_args = []
37
- ids = []
38
- args.each do |item|
39
- if(item.is_a?(ActiveRecord::Base) || (defined?(ActiveRecord::Relation) && item.is_a?(ActiveRecord::Relation)))
40
- new_args << item
41
- else
42
- ids << item.to_i
43
- end
44
- end
45
- new_args += new_opts[:class].find(ids) unless ids.empty?
46
- self.send("original_#{name}=".to_sym, new_args)
47
- end
27
+ new_opts = {:name => name, :enabled => true, :type => :full, :scope => self.scoped, :default_allowed_all => false}.merge(opts)
28
+ new_opts[:views] ||= {}
29
+ new_opts = map_deprecated_hash(new_opts)
30
+ new_opts[:views][:id] ||= :id
31
+ new_opts[:class] = restrictor_class(new_opts)
32
+ if(new_opts[:type] == :full && new_opts[:views][:value].blank?)
33
+ if(new_opts[:class].column_names.include?('name'))
34
+ new_opts[:views][:value] = :name
35
+ else
36
+ raise 'Value must be defined for association to generate views'
48
37
  end
49
38
  end
39
+ self.restrictors.push(new_opts)
40
+ end
41
+
42
+ # TODO: Add in proper mapping plus scope building
43
+ def map_deprecated_hash(hsh)
44
+ hsh[:type] = :basic_user if hsh[:type] == :basic
45
+ hsh[:scope] = self.class._restrictor_custom_user_class.where(hsh.delete(:condition)) if hsh[:condition].present?
46
+ [:value, :multiple, :include_blank, :user_values_only, :id].each do |v_opt|
47
+ hsh[:views][v_opt] = hsh.delete(v_opt) if hsh[v_opt].present?
48
+ end
49
+ hsh[:default_allowed_all] = hsh.delete(:default_view_all) if hsh.has_key?(:default_view_all)
50
+ hsh
50
51
  end
51
52
 
52
- # Returns restrictors not of type :basic
53
- def full_restrictors
54
- self.restrictors.find_all{|restrictor| restrictor[:type] != :basic && check_enabled(restrictor[:enabled]) == true}
53
+ # Returns restrictors of type: :full
54
+ # NOTE: Returns enabled by default. Provide :include_disabled to get all
55
+ def full_restrictors(*args)
56
+ self.restrictors.find_all do |restrictor|
57
+ restrictor[:type] == :full &&
58
+ (args.include?(:include_disabled) || check_enabled(restrictor[:enabled]) == true)
59
+ end
55
60
  end
56
61
 
57
- # Returns restrictors of type :basic
58
- def basic_restrictors
59
- self.restrictors.find_all{|restrictor| restrictor[:type] == :basic && check_enabled(restrictor[:enabled]) == true}
62
+ # Returns restrictors of type :basic_model
63
+ # NOTE: Returns enabled by default. Provide :include_disabled to get all
64
+ def basic_model_restrictors(*args)
65
+ self.restrictors.find_all do |restrictor|
66
+ restrictor[:type] == :basic_model &&
67
+ (args.include?(:include_disabled) || check_enabled(restrictor[:enabled]) == true)
68
+ end
69
+ end
70
+
71
+ # Returns restrictors of type :basic_user
72
+ # NOTE: Returns enabled by default. Provide :include_disabled to get all
73
+ def basic_user_restrictors(*args)
74
+ self.restrictors.find_all do |restrictor|
75
+ restrictor[:type] == :basic_user &&
76
+ (args.include?(:include_disabled) || check_enabled(restrictor[:enabled]) == true)
77
+ end
78
+ end
79
+
80
+ # Returns restrictors of type :implicit
81
+ def implicit_restrictors(*args)
82
+ self.restrictors.find_all do |restrictor|
83
+ restrictor[:type] == :implicit &&
84
+ check_enabled(restrictor[:enabled]) == true
85
+ end
60
86
  end
61
87
 
62
88
  # Returns all restrictors that are currently in enabled state
@@ -70,8 +96,34 @@ module ActiveRestrictor
70
96
  if(hash[:class].present?)
71
97
  hash[:class]
72
98
  else
73
- self.relect_on_association(hash[:name]).klass
99
+ if(hash[:type] == :basic_user)
100
+ _restrictor_custom_user_class
101
+ elsif(hash[:type] == :basic_model)
102
+ self
103
+ else
104
+ n = self.reflect_on_association(hash[:name]).try(:klass)
105
+ if(n.blank?)
106
+ raise "Failed to location association for restrictor. Given: #{hash[:name]}. Please check assocation name."
107
+ end
108
+ n
109
+ end
110
+ end
111
+ end
112
+
113
+ def restrictor_user_scoping
114
+ user_scope = _restrictor_custom_user_class.scoped
115
+ basic_user_restrictors.each do |restrictor|
116
+ user_scope = user_scope.merge(restrictor[:scope].respond_to?(:call) ? restrictor[:scope].call : restrictor[:scope])
74
117
  end
118
+ user_scope
119
+ end
120
+
121
+ def restrictor_klass_scoping
122
+ klass_scope = self.scoped
123
+ (implicit_restrictors + basic_model_restrictors).each do |restrictor|
124
+ klass_scope = klass_scope.merge(restrictor[:scope].respond_to?(:call) ? restrictor[:scope].call : restrictor[:scope])
125
+ end
126
+ klass_scope
75
127
  end
76
128
 
77
129
  private
@@ -89,6 +141,7 @@ module ActiveRestrictor
89
141
  false
90
142
  end
91
143
  end
144
+
92
145
  end
93
146
 
94
147
  module InstanceMethods
@@ -100,13 +153,26 @@ module ActiveRestrictor
100
153
  end
101
154
 
102
155
  # Returns restrictors not of type :basic
103
- def full_restrictors
104
- self.class.full_restrictors
156
+ # NOTE: Returns enabled by default. Provide :include_disabled to get all
157
+ def full_restrictors(*args)
158
+ self.class.full_restrictors(*args)
159
+ end
160
+
161
+ # Returns restrictors of type :basic_user
162
+ # NOTE: Returns enabled by default. Provide :include_disabled to get all
163
+ def basic_user_restrictors(*args)
164
+ self.class.basic_user_restrictors(*args)
165
+ end
166
+
167
+ # Returns restrictors of type :basic_model
168
+ # NOTE: Returns enabled by default. Provide :include_disabled to get all
169
+ def basic_model_restrictors(*args)
170
+ self.class.basic_model_restrictors(*args)
105
171
  end
106
172
 
107
- # Returns restrictors of type :basic
108
- def basic_restrictors
109
- self.class.basic_restrictors
173
+ # Returns restrictors of type :implict
174
+ def implicit_restrictors
175
+ self.class.implicit_restrictors
110
176
  end
111
177
 
112
178
  # Returns all restrictors taht are currently in enabled status
@@ -114,31 +180,43 @@ module ActiveRestrictor
114
180
  self.class.enabled_restrictors
115
181
  end
116
182
 
183
+ # Grabs customizable user class
184
+ def _restrictor_custom_user_class
185
+ self.class._restrictor_custom_user_class
186
+ end
187
+
117
188
  # Returns User scope with all restrictors applied
118
189
  def allowed_users
119
- user_scope = User.scoped
120
- enabled_restrictors.each do |restrictor|
121
- next if restrictor[:user_custom]
122
- if(restrictor[:include].is_a?(ActiveRecord::Relation))
123
- user_scope = user_scope.merge(restrictor[:include])
124
- elsif(restrictor[:include].present?)
125
- user_scope = user_scope.joins(restrictor[:include])
126
- end
127
- if(restrictor[:condition].is_a?(ActiveRecord::Relation))
128
- user_scope = user_scope.merge(restrictor[:condition])
129
- elsif(restrictor[:condition].respond_to?(:call))
130
- user_scope = user_scope.merge(restrictor[:condition].call)
131
- elsif(restrictor[:condition].present?)
132
- user_scope = user_scope.where(restrictor[:condition])
190
+ klass_scope = self.class.restrictor_klass_scoping.where(:id => self.id)
191
+ user_scope = self.class.restrictor_user_scoping
192
+ if(klass_scope.count > 0)
193
+ full_restrictors.each do |restrictor|
194
+ user_scope = user_scope.includes(restrictor[:name])
195
+ unless(restrictor[:user_custom].present?)
196
+ rtable_name = restrictor[:table_name] || restrictor[:class].table_name
197
+ r_scope = self.send(restrictor[:name]).scoped.select("#{rtable_name}.id")
198
+ r_count = self.send(restrictor[:name]).scoped.select("count(*)")
199
+ # this next bit gets rid of the association_name.* rails insists upon and any extra cruft
200
+ r_scope.arel.ast.cores.first.projections.delete_if{|item| item != "#{rtable_name}.id"}
201
+ r_count.arel.ast.cores.first.projections.delete_if{|item| item != "count(*)"}
202
+ user_scope = user_scope.where(
203
+ "#{rtable_name}.id IN (#{r_scope.to_sql})#{
204
+ " OR (#{r_count.to_sql}) = 0" if restrictor[:default_allowed_all]
205
+ }"
206
+ )
207
+ else
208
+ user_scope = restrictor[:user_custom].call(user_scope, self)
209
+ end
133
210
  end
134
- unless(restrictor[:type] == :basic)
135
- user_scope = user_scope.where("#{restrictor[:table_name] || restrictor_class(restrictor).table_name}.id IN (#{self.send(restrictor[:name]).scoped.select(:id).to_sql})")
211
+ if(Array(implicit_restrictors).size > 0)
212
+ if(r = _restrictor_custom_user_class.reflect_on_all_associations.detect{|r| r.klass == self.class})
213
+ user_scope = user_scope.includes(r.name).merge(klass_scope)
214
+ end
136
215
  end
216
+ user_scope
217
+ else
218
+ user_scope.where('null')
137
219
  end
138
- if((methods = enabled_restrictors.find_all{|res| res[:user_custom]}).size > 0)
139
- user_scope = methods.inject(user_scope){|result,func| func.call(result, self)}
140
- end
141
- user_scope
142
220
  end
143
221
  end
144
222
 
@@ -151,14 +229,28 @@ module ActiveRestrictor
151
229
  extend ClassMethods
152
230
  include InstanceMethods
153
231
 
232
+ unless(base.singleton_methods.map(&:to_sym).include?(:_restrictor_custom_user_class))
233
+ class << self
234
+ # This can be overridden for customization
235
+ def _restrictor_custom_user_class
236
+ User
237
+ end
238
+ end
239
+ end
154
240
  scope :allowed_for, lambda{|*args|
155
241
  user = args.detect{|item|item.is_a?(User)}
156
- where("#{table_name}.id IN (#{user.send("allowed_#{base.name.tableize}").select(:id).to_sql})")
242
+ if(user.present?)
243
+ r_scope = user.send("allowed_#{base.name.tableize}").select("#{base.table_name}.id")
244
+ r_scope.arel.ast.cores.first.projections.delete_if{|item| item != "#{base.table_name}.id"}
245
+ where("#{table_name}.id IN (#{r_scope.to_sql})")
246
+ else
247
+ where('null')
248
+ end
157
249
  }
158
250
  end
159
251
  end
160
252
  # Patch up the user to provide restricted methods
161
- ([User] + User.descendants).compact.each do |user_klass|
253
+ ([klass._restrictor_custom_user_class] + klass._restrictor_custom_user_class.descendants).compact.each do |user_klass|
162
254
  user_klass.class_eval do
163
255
  # This patches a method onto the User instance to
164
256
  # provide access to the allowed instance of the model
@@ -168,45 +260,31 @@ module ActiveRestrictor
168
260
  define_method("allowed_#{klass.name.tableize}") do
169
261
  # First we perform a basic check against the User to see
170
262
  # if this user instance is even allowed by default
171
- user_scope = User.scoped
172
- klass.basic_restrictors.each do |restriction|
173
- if(restriction[:condition].is_a?(ActiveRecord::Relation))
174
- user_scope = user_scope.merge(restriction[:condition])
175
- elsif(restriction[:condition].respond_to?(:call))
176
- user_scope = user_scope.merge(restriction[:condition].call)
177
- elsif(restriction[:condition].present?)
178
- user_scope = user_scope.where(restriction[:condition])
179
- end
180
- if(restriction[:include].is_a?(ActiveRecord::Relation))
181
- user_scope = user_scope.merge(restriction[:include])
182
- elsif(restriction[:include].present?)
183
- user_scope = user_scope.join(restriction[:include])
184
- end
185
- end
263
+ user_scope = klass.restrictor_user_scoping.where(:id => self.id)
186
264
  if(user_scope.count > 0)
187
- scope = klass.scoped
265
+ scope = klass.restrictor_klass_scoping
188
266
  klass.full_restrictors.each do |restrictor|
189
- next if restrictor[:model_custom].present?
190
- rtable_name = restrictor[:table_name] || klass.restrictor_class(restrictor).table_name
191
- r_scope = self.send(restrictor[:name]).scoped.select("#{rtable_name}.id")
192
- r_scope.arel.ast.cores.first.projections.delete_at(0) # gets rid of the association_name.* rails insists upon
193
- if(restrictor[:default_view_all])
194
- scope = scope.includes(restrictor[:name]) if restrictor[:name].present?
267
+ scope = scope.includes(restrictor[:name])
268
+ if(restrictor[:scope].present?)
269
+ scope = scope.merge(restrictor[:scope].respond_to?(:call) ? restrictor[:scope].call : restrictor[:scope])
270
+ end
271
+ unless(restrictor[:model_custom].present?)
272
+ rtable_name = restrictor[:table_name] || restrictor[:class].table_name
273
+ r_scope = self.send(restrictor[:user_association] || restrictor[:name]).scoped.select("#{rtable_name}.id") # This gives us valid joiners!
274
+ # this next bit gets rid of the association_name.* rails insists upon and any extra cruft
275
+ r_scope.arel.ast.cores.first.projections.delete_if{|item| item != "#{rtable_name}.id"}
276
+ scope = scope.where(
277
+ "#{rtable_name}.id IN (#{r_scope.to_sql})#{
278
+ " OR #{rtable_name}.id IS NULL" if restrictor[:default_allowed_all]
279
+ }"
280
+ )
195
281
  else
196
- scope = scope.joins(restrictor[:name]) if restrictor[:name].present?
282
+ scope = restrictor[:model_custom].call(scope, self)
197
283
  end
198
- scope = scope.where(
199
- "#{rtable_name}.id IN (#{r_scope.to_sql})#{
200
- " OR #{rtable_name}.id IS NULL" if restrictor[:default_view_all]
201
- }"
202
- )
203
- end
204
- if((methods = klass.restrictors.map{|res| res[:model_custom]}.compact).size > 0)
205
- scope = methods.inject(scope){|result,func| func.call(result, self)}
206
284
  end
207
285
  scope
208
286
  else
209
- klass.where('false')
287
+ klass.where('null')
210
288
  end
211
289
  end
212
290
  end
@@ -1,57 +1,65 @@
1
1
  module ActiveRestrictors
2
2
  module View
3
+
3
4
  # obj:: Instance with restrictors enabled
5
+ # args:: argument hash :
6
+ # :val_join:: string to join values with
7
+ # :include_disabled:: includes all restrictors
4
8
  # val_join:: String to join restictor values together
5
9
  # Provides array of enabled restrictors in the form of:
6
10
  # [[restrictor_name_label, string_of_restriction_values]]
7
- def display_full_restrictors(obj, val_join = '<br />')
11
+ def display_full_restrictors(obj, *args)
12
+ arg_h = args.last.is_a?(Hash) ? args.last : {}
13
+ res = []
14
+ if(args.size == 1 && args.first.is_a?(String))
15
+ val_join = args.first
16
+ else
17
+ val_join = arg_h[:val_join] || '<br />'
18
+ end
8
19
  if(obj.class.respond_to?(:full_restrictors))
9
- obj.class.full_restrictors.map do |restrictor|
10
- [
11
- label(obj.class.name.camelize, restrictor[:name]),
12
- obj.send(restrictor[:name]).map(&restrictor[:value].to_sym).join(val_join).html_safe
13
- ]
20
+ r_args = arg_h[:include_disabled] ? [:include_disabled] : []
21
+ res = obj.class.full_restrictors(*r_args).map do |restrictor|
22
+ [label(obj.class.name.camelize, restrictor[:name]),
23
+ obj.send(restrictor[:name]).map(&restrictor[:views][:value].to_sym).join(val_join).html_safe]
14
24
  end
15
- else
16
- []
17
25
  end
18
- end
19
-
20
- def display_custom_restrictors(klass)
21
- # Stub for now
22
- []
26
+ res
23
27
  end
24
28
 
25
29
  # obj:: Instance with restrictors enabled
26
30
  # form:: Form object to attach restrictor fields to
31
+ # args:: Argument hash ->
32
+ # :include_disabled:: includes all restrictors
27
33
  # Provides form items for restrictors in an array of format:
28
34
  # [[restrictor_name_label, form_selection_string]]
29
- def edit_full_restrictors(obj, form)
35
+ def edit_full_restrictors(obj, form, args={})
36
+ r_args = args[:include_disabled] ? [:include_disabled] : []
30
37
  if(obj.class.respond_to?(:full_restrictors))
31
- obj.class.full_restrictors.map do |restrictor|
32
- if(restrictor[:user_values_only])
33
- if(restrictor[:user_values_only].respond_to?(:call))
34
- user = restrictor[:user_values_only].call
38
+ obj.class.full_restrictors(*r_args).map do |restrictor|
39
+ if(restrictor[:views][:user_values_only])
40
+ if(restrictor[:views][:user_values_only].respond_to?(:call))
41
+ user = restrictor[:views][:user_values_only].call
35
42
  if(user)
36
43
  values = user.send(restrictor[:name].to_sym).find(:all, :order => restrictor[:value])
37
44
  else
38
- values = restrictor[:user_values_only].send(restrictor[:name].to_sym).find(:all, :order => restrictor[:value])
45
+ values = restrictor[:views][:user_values_only].send(restrictor[:name].to_sym).find(:all, :order => restrictor[:views][:value])
39
46
  end
40
47
  end
41
48
  end
42
- values = restrictor[:class].find(:all, :order => restrictor[:value]) unless values
49
+ @_restrictor_inflector_helper ||= Class.send(:include, ActiveSupport::Inflector).new
50
+ values = restrictor[:class].find(:all, :order => restrictor[:views][:value]) unless values
43
51
  [
44
52
  form.label(restrictor[:name]),
45
53
  form.collection_select(
46
- restrictor[:name],
54
+ "#{@_restrictor_inflector_helper.singularize(restrictor[:name])}_ids",
47
55
  values,
48
- restrictor[:id],
49
- restrictor[:value],
56
+ restrictor[:views][:id],
57
+ restrictor[:views][:value],
50
58
  {
51
- :include_blank => restrictor[:include_blank],
52
- :selected => Array(obj.send(restrictor[:name])).map(&restrictor[:id].to_sym)
59
+ :include_blank => restrictor[:views][:include_blank],
60
+ :selected => Array(obj.send(restrictor[:name])).map(&restrictor[:views][:id].to_sym)
53
61
  },
54
- :multiple => restrictor[:multiple]
62
+ :multiple => restrictor[:views][:multiple]
55
63
  )
56
64
  ]
57
65
  end
@@ -59,11 +67,6 @@ module ActiveRestrictors
59
67
  []
60
68
  end
61
69
  end
62
-
63
- def edit_custom_restrictors(klass)
64
- # Stub for now
65
- []
66
- end
67
70
  end
68
71
  end
69
72
 
@@ -13,5 +13,5 @@ module ActiveRestrictors
13
13
  end
14
14
  end
15
15
 
16
- VERSION = Version.new('0.1.1')
16
+ VERSION = Version.new('0.2.0')
17
17
  end
@@ -0,0 +1,116 @@
1
+ require 'active_record'
2
+ require 'active_record/migration'
3
+ require 'benchmark'
4
+ class ModelSetup < ActiveRecord::Migration
5
+ def self.up
6
+ create_table :users do |t|
7
+ t.string :username
8
+ t.boolean :active
9
+ end
10
+ create_table :fubars do |t|
11
+ t.string :name
12
+ t.boolean :enabled
13
+ t.integer :user_id
14
+ end
15
+ create_table :permissions do |t|
16
+ t.string :name
17
+ end
18
+ create_table :groups do |t|
19
+ t.string :name
20
+ end
21
+ create_table :user_permissions do |t|
22
+ t.integer :user_id
23
+ t.integer :permission_id
24
+ end
25
+ create_table :fubar_permissions do |t|
26
+ t.integer :fubar_id
27
+ t.integer :permission_id
28
+ end
29
+ create_table :user_groups do |t|
30
+ t.integer :group_id
31
+ t.integer :user_id
32
+ end
33
+ create_table :fubar_groups do |t|
34
+ t.integer :group_id
35
+ t.integer :fubar_id
36
+ end
37
+ end
38
+ end
39
+
40
+ ModelSetup.up
41
+
42
+ class User < ActiveRecord::Base
43
+ cattr_accessor :current_user
44
+ has_many :user_permissions, :dependent => :destroy
45
+ has_many :permissions, :through => :user_permissions
46
+ has_many :user_groups, :dependent => :destroy
47
+ has_many :groups, :through => :user_groups
48
+ has_many :fubars, :dependent => :destroy
49
+ end
50
+
51
+ class Permission < ActiveRecord::Base
52
+ has_many :user_permissions, :dependent => :destroy
53
+ has_many :users, :through => :user_permissions
54
+ has_many :fubar_permissions, :dependent => :destroy
55
+ has_many :fubars, :through => :fubar_permissions
56
+ end
57
+
58
+ class Group < ActiveRecord::Base
59
+ has_many :user_groups, :dependent => :destroy
60
+ has_many :users, :through => :user_groups
61
+ has_many :fubar_groups, :dependent => :destroy
62
+ has_many :fubars, :through => :fubar_groups
63
+ end
64
+
65
+ class Fubar < ActiveRecord::Base
66
+ include ActiveRestrictor
67
+ has_many :fubar_permissions, :dependent => :destroy
68
+ has_many :permissions, :through => :fubar_permissions
69
+ has_many :fubar_groups, :dependent => :destroy
70
+ has_many :groups, :through => :fubar_groups
71
+ belongs_to :user
72
+ end
73
+
74
+ class UserPermission < ActiveRecord::Base
75
+ belongs_to :user
76
+ belongs_to :permission
77
+ end
78
+
79
+ class FubarPermission < ActiveRecord::Base
80
+ belongs_to :fubar
81
+ belongs_to :permission
82
+ end
83
+
84
+ class FubarGroup < ActiveRecord::Base
85
+ belongs_to :fubar
86
+ belongs_to :group
87
+ end
88
+
89
+ class UserGroup < ActiveRecord::Base
90
+ belongs_to :user
91
+ belongs_to :group
92
+ end
93
+
94
+ def scrub_all
95
+ Fubar.restrictors.clear
96
+ UserGroup.delete_all
97
+ FubarGroup.delete_all
98
+ UserPermission.delete_all
99
+ FubarPermission.delete_all
100
+ User.current_user = nil
101
+ Fubar.update_all(:user_id => nil)
102
+ end
103
+
104
+ # Setup our test models
105
+ 10.times do |i|
106
+ User.create(:username => "user_#{i}", :active => i % 2 == 0)
107
+ Fubar.create(:name => "fubar_#{i}", :enabled => i % 2 == 0)
108
+ end
109
+
110
+ 5.times do |i|
111
+ Permission.create(:name => "permission_#{i}")
112
+ end
113
+
114
+ 5.times do |i|
115
+ Group.create(:name => "group_#{i}")
116
+ end
@@ -0,0 +1,386 @@
1
+ require File.join(File.expand_path(File.dirname(__FILE__)), 'setup')
2
+
3
+ describe ActiveRestrictor do
4
+ # Check for proper setup at the start
5
+ describe 'after suite setup' do
6
+ it 'should have properly setup models' do
7
+ assert_equal 10, User.count
8
+ assert_equal 10, Fubar.count
9
+ assert_equal 5, Group.count
10
+ assert_equal 5, Permission.count
11
+ refute_equal User.count, User.where(:active => true).count
12
+ end
13
+ end
14
+
15
+
16
+ #!!!! basic_user type restrictions
17
+ describe 'when basic user restrictor only allows active users' do
18
+ before do
19
+ Fubar.class_eval do
20
+ add_restrictor(:active,
21
+ :type => :basic_user,
22
+ :scope => User.where(:active => true)
23
+ )
24
+ end
25
+ end
26
+ after do
27
+ scrub_all
28
+ end
29
+ it 'should only allow active users access to fubars' do
30
+ u = User.where(:active => true).first
31
+ assert u.allowed_fubars.count > 0
32
+ u = User.where(:active => false).first
33
+ assert_equal 0, u.allowed_fubars.count
34
+ end
35
+ it 'should only show active users from fubars' do
36
+ Fubar.all.each do |fubar|
37
+ fubar.allowed_users.each do |user|
38
+ assert user.active
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ describe 'when basic user restrictor provide enabled block' do
45
+ before do
46
+ Fubar.class_eval do
47
+ add_restrictor(:active,
48
+ :type => :basic_user,
49
+ :scope => User.where(:active => true),
50
+ :enabled => lambda{ User.current_user.nil? || !User.current_user.username == 'user_1' }
51
+ )
52
+ end
53
+ u = User.find_by_username('user_1')
54
+ u.active = false
55
+ u.save!
56
+ end
57
+ after do
58
+ scrub_all
59
+ end
60
+ it 'should not allow fubars for user_1 when current_user is unset' do
61
+ assert_equal 0, User.find_by_username('user_1').allowed_fubars.count
62
+ end
63
+ it 'should allow fubars for user_1 when current_user is set' do
64
+ u = User.find_by_username('user_1')
65
+ User.current_user = u
66
+ assert u.allowed_fubars.count > 0
67
+ end
68
+ end
69
+
70
+ #!!! basic model type restrictions
71
+ describe 'when basic model restrictor only allows enabled fubars' do
72
+ before do
73
+ Fubar.class_eval do
74
+ add_restrictor(:enabled,
75
+ :type => :basic_model,
76
+ :scope => Fubar.where(:enabled => true)
77
+ )
78
+ end
79
+ end
80
+ after do
81
+ scrub_all
82
+ end
83
+ it 'should only allow users to access enabled fubars' do
84
+ User.all.each do |user|
85
+ user.allowed_fubars.each do |fubar|
86
+ assert fubar.enabled?
87
+ end
88
+ end
89
+ end
90
+ it 'should not provide users from disabled fubars' do
91
+ assert Fubar.where(:enabled => false).count > 0
92
+ Fubar.where(:enabled => false).all.each do |fubar|
93
+ assert_equal 0, fubar.allowed_users.count
94
+ end
95
+ end
96
+ end
97
+
98
+ #!!! implicit model type restrictions
99
+ describe 'when implict restrictor only allows fubars belonging to users' do
100
+ before do
101
+ Fubar.class_eval do
102
+ add_restrictor(:user,
103
+ :type => :implicit,
104
+ :scope => lambda{ where(:user_id => User.current_user.id) }
105
+ )
106
+ end
107
+ u = User.find_by_username("user_1")
108
+ 3.times do |i|
109
+ f = Fubar.find_by_name("fubar_#{i}")
110
+ f.user_id = u.id
111
+ f.save!
112
+ end
113
+ User.current_user = u
114
+ end
115
+ after do
116
+ scrub_all
117
+ end
118
+ it 'should show 3 fubars for user_1' do
119
+ assert_equal 3, User.find_by_username('user_1').fubars.count
120
+ end
121
+ it 'should show no fubars for users not user_1' do
122
+ User.where("username != 'user_1'").all.each do |user|
123
+ assert_equal 0, user.fubars.count
124
+ end
125
+ end
126
+ it 'should only show allowed users as assigned users' do
127
+ Fubar.all.each do |fubar|
128
+ if(fubar.user_id.nil?)
129
+ assert_equal 0, fubar.allowed_users.count
130
+ else
131
+ assert_equal 1, fubar.allowed_users.count
132
+ assert_equal 'user_1', fubar.allowed_users.first.username
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ #!!! full type restrictions
139
+ describe 'when full permission restrictor is applied' do
140
+ before do
141
+ Fubar.class_eval do
142
+ add_restrictor(:permissions,
143
+ :type => :full
144
+ )
145
+ end
146
+ 3.times do |i|
147
+ perm = Permission.find_by_name("permission_#{i}")
148
+ user = User.find_by_username("user_#{i}")
149
+ fubar = Fubar.find_by_name("fubar_#{i}")
150
+ user.permissions << perm
151
+ fubar.permissions << perm
152
+ end
153
+ end
154
+ after do
155
+ scrub_all
156
+ end
157
+ it 'should only allow users with matching permissions' do
158
+ 3.times do |i|
159
+ user = User.find_by_username("user_#{i}")
160
+ fubar = Fubar.find_by_name("fubar_#{i}")
161
+ assert_equal 1, user.allowed_fubars.count
162
+ assert_equal 1, fubar.allowed_users.count
163
+ assert_equal "fubar_#{i}", user.allowed_fubars.first.name
164
+ assert_equal "user_#{i}", fubar.allowed_users.first.username
165
+ end
166
+ end
167
+ it 'should not allow users without matching permissions' do
168
+ 7.times do |i|
169
+ i = i + 3
170
+ user = User.find_by_username("user_#{i}")
171
+ fubar = Fubar.find_by_name("fubar_#{i}")
172
+ assert_equal 0, user.allowed_fubars.count
173
+ assert_equal 0, fubar.allowed_users.count
174
+ end
175
+ end
176
+ end
177
+
178
+ #!!! Stacked full restrictions
179
+ describe 'when full permission and full group restrictors are applied' do
180
+ before do
181
+ Fubar.class_eval do
182
+ add_restrictor(:permissions,
183
+ :type => :full
184
+ )
185
+ add_restrictor(:groups,
186
+ :type => :full
187
+ )
188
+ end
189
+ 4.times do |i|
190
+ perm = Permission.find_by_name("permission_#{i}")
191
+ user = User.find_by_username("user_#{i}")
192
+ group = Group.find_by_name("group_#{i}")
193
+ fubar = Fubar.find_by_name("fubar_#{i}")
194
+ if(i % 2 == 0 || i == 3)
195
+ user.permissions << perm
196
+ fubar.permissions << perm
197
+ end
198
+ if(i % 2 == 1 || i == 3)
199
+ user.groups << group
200
+ fubar.groups << group
201
+ end
202
+ end
203
+ end
204
+ after do
205
+ scrub_all
206
+ end
207
+
208
+ it 'should be setup correctly' do
209
+ 3.times do |i|
210
+ u = User.find_by_username("user_#{i}")
211
+ if(u.groups.count > 0)
212
+ assert_equal(1, u.groups.count)
213
+ assert_equal(0, u.permissions.count)
214
+ elsif(u.permissions.count > 0)
215
+ assert_equal(1, u.permissions.count)
216
+ assert_equal(0, u.groups.count)
217
+ else
218
+ flunk "User should have one group or one permission assigned"
219
+ end
220
+ end
221
+ assert_equal 1, User.find_by_username('user_3').groups.count
222
+ assert_equal 1, User.find_by_username('user_3').permissions.count
223
+ assert_equal 0, User.find_by_username('user_4').groups.count
224
+ assert_equal 0, User.find_by_username('user_5').permissions.count
225
+ end
226
+ it 'should allow not allow users with single matching permission or group only' do
227
+ 3.times do |i|
228
+ user = User.find_by_username("user_#{i}")
229
+ assert_equal(0, user.allowed_fubars.count)
230
+ end
231
+ end
232
+ it 'should allow users with both matching permission and group' do
233
+ assert_equal(1, User.find_by_username('user_3').allowed_fubars.count)
234
+ end
235
+ it 'should not allow users with no matching permission or group' do
236
+ assert_equal(0, User.find_by_username('user_4').allowed_fubars.count)
237
+ end
238
+ end
239
+
240
+ describe 'when full permission and full group restrictors are applied using default_view_all' do
241
+ before do
242
+ Fubar.class_eval do
243
+ add_restrictor(:permissions,
244
+ :type => :full,
245
+ :default_view_all => true
246
+ )
247
+ add_restrictor(:groups,
248
+ :type => :full,
249
+ :default_view_all => true
250
+ )
251
+ end
252
+ 4.times do |i|
253
+ perm = Permission.find_by_name("permission_#{i}")
254
+ user = User.find_by_username("user_#{i}")
255
+ group = Group.find_by_name("group_#{i}")
256
+ fubar = Fubar.find_by_name("fubar_#{i}")
257
+ if(i % 2 == 0 || i == 3)
258
+ user.permissions << perm
259
+ fubar.permissions << perm
260
+ end
261
+ if(i % 2 == 1 || i == 3)
262
+ user.groups << group
263
+ fubar.groups << group
264
+ end
265
+ end
266
+ end
267
+ after do
268
+ scrub_all
269
+ end
270
+
271
+ it 'should only provide fubars without assigned groups and permissions to users without assigned groups and permissions' do
272
+ User.includes(:groups, :permissions).where(:groups => {:id => nil}, :permissions => {:id => nil}).each do |user|
273
+ assert_equal(6, user.allowed_fubars.count)
274
+ user.allowed_fubars.each do |fubar|
275
+ assert_equal(0, fubar.groups.count)
276
+ assert_equal(0, fubar.permissions.count)
277
+ end
278
+ assert_equal(0, user.groups.count)
279
+ assert_equal(0, user.permissions.count)
280
+ end
281
+ end
282
+ it 'should provide fubars with with matching group or permissions and fubars without assigned groups or permissions' do
283
+ 4.times do |i|
284
+ user = User.find_by_username("user_#{i}")
285
+ assert_equal(7, user.allowed_fubars.count)
286
+ user.allowed_fubars.each do |fubar|
287
+ if(fubar.groups.count > 0)
288
+ assert_equal fubar.groups.first, user.groups.first
289
+ end
290
+ if(fubar.permissions.count > 0)
291
+ assert_equal fubar.permissions.first, user.permissions.first
292
+ end
293
+ end
294
+ end
295
+ end
296
+ it 'should allow all users when no permission and groups are defined' do
297
+ 6.times do |i|
298
+ fubar = Fubar.find_by_name("fubar_#{i + 4}")
299
+ assert_equal(10, fubar.allowed_users.count)
300
+ end
301
+ end
302
+ it 'should only allow users with matching permission or group' do
303
+ 4.times do |i|
304
+ fubar = Fubar.find_by_name("fubar_#{i}")
305
+ assert_equal(1, fubar.allowed_users.count)
306
+ end
307
+ end
308
+ end
309
+
310
+ #!!! Stacked mixed restrictions
311
+ describe 'when basic and full restrictors are applied' do
312
+ before do
313
+ Fubar.class_eval do
314
+ add_restrictor(:enabled,
315
+ :type => :basic_user,
316
+ :scope => User.where(:active => true)
317
+ )
318
+ add_restrictor(:enabled,
319
+ :type => :basic_model,
320
+ :scope => where(:enabled => true)
321
+ )
322
+ add_restrictor(:permissions,
323
+ :type => :full
324
+ )
325
+ end
326
+ 4.times do |i|
327
+ user = User.find_by_username("user_#{i}")
328
+ perm = Permission.find_by_name("permission_#{i}")
329
+ fubar = Fubar.find_by_name("fubar_#{i}")
330
+ user.permissions << perm
331
+ fubar.permissions << perm
332
+ end
333
+ end
334
+ after do
335
+ scrub_all
336
+ end
337
+
338
+ it "should have disabled and enabled users and fubars setup" do
339
+ users = 4.times.map{|i| User.find_by_username("user_#{i}")}
340
+ fubars = 4.times.map{|i| Fubar.find_by_name("fubar_#{i}")}
341
+ assert users.detect(&:active?), 'Expecting active users'
342
+ assert users.detect{|u| !u.active?}, 'Expecting inactive users'
343
+ assert fubars.detect(&:enabled?), 'Expecting enabled fubars'
344
+ assert fubars.detect{|f| !f.enabled?}, 'Expecting disabled fubars'
345
+ end
346
+ it "should not show any allowed users when fubar is disabled" do
347
+ fubar = Fubar.where(:name => 4.times.map{|i|"fubar_#{i}"}, :enabled => false).first
348
+ assert fubar, "Expecting Fubar instance to be found"
349
+ assert_equal 0, fubar.allowed_users.count
350
+ end
351
+ it "should only show active users when fubar is enabled" do
352
+ fubar = Fubar.where(:name => 4.times.map{|i|"fubar_#{i}"}, :enabled => true).first
353
+ assert fubar, "Expecting Fubar instance to be found"
354
+ assert fubar.allowed_users.count > 0, 'Expecting allowed users to be found'
355
+ fubar.allowed_users.each do |user|
356
+ assert user.active?, 'Expecting user to be active'
357
+ end
358
+ end
359
+ it "should not show any allowed fubars when user is inactive" do
360
+ user = User.where(:username => 4.times.map{|i|"user_#{i}"}, :active => false).first
361
+ assert user, "Expecting User instance to be found"
362
+ assert_equal(0, user.allowed_fubars.count)
363
+ end
364
+ it "should only show enabled fubars when user is active" do
365
+ user = User.where(:username => 4.times.map{|i|"user_#{i}"}, :active => true).first
366
+ assert user, "Expecting User instance to be found"
367
+ assert user.allowed_fubars.count > 0, 'Expecting allowed fubars to be found'
368
+ user.allowed_fubars.each do |fubar|
369
+ assert fubar.enabled?, 'Expecting fubar to be enabled'
370
+ end
371
+ end
372
+ it "should only show enabled fubars with same permission as user" do
373
+ User.where(:username => 4.times.map{|i|"user_#{i}"}, :active => true).all.each do |user|
374
+ assert user.active?, 'Expecting active user'
375
+ assert user.allowed_fubars.count > 0, 'Expecting user to have allowed fubars'
376
+ user.allowed_fubars.each do |fubar|
377
+ assert fubar.enabled?, 'Expecting Fubar to be enabled'
378
+ assert_equal fubar.permissions.first, user.permissions.first
379
+ end
380
+ end
381
+ end
382
+ end
383
+
384
+ # TODO: model_custom / user_custom
385
+ # TODO: more complex stacking
386
+ end
data/spec/setup.rb ADDED
@@ -0,0 +1,17 @@
1
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
2
+ require 'rubygems'
3
+ require 'bundler/setup'
4
+ require 'active_record'
5
+ if(RUBY_PLATFORM == 'java')
6
+ require 'jdbc/sqlite3'
7
+ else
8
+ require 'sqlite3'
9
+ end
10
+ require 'active_restrictors'
11
+ require 'minitest/autorun'
12
+
13
+ ActiveRecord::Base.establish_connection(
14
+ :adapter => 'sqlite3',
15
+ :database => ':memory:'
16
+ )
17
+ load File.join(File.expand_path(File.dirname(__FILE__)), 'model_definitions.rb')
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_restrictors
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
9
- - 1
10
- version: 0.1.1
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Chris Roberts
@@ -15,10 +15,24 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-01-09 00:00:00 -08:00
18
+ date: 2012-01-20 00:00:00 -08:00
19
19
  default_executable:
20
- dependencies: []
21
-
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: activerecord
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 7
30
+ segments:
31
+ - 3
32
+ - 0
33
+ version: "3.0"
34
+ type: :runtime
35
+ version_requirements: *id001
22
36
  description: Restrictors for Models
23
37
  email: chrisroberts.code@gmail.com
24
38
  executables: []
@@ -29,13 +43,19 @@ extra_rdoc_files:
29
43
  - README.rdoc
30
44
  - CHANGELOG.rdoc
31
45
  files:
46
+ - Gemfile.lock
47
+ - spec/restrictor_spec.rb
48
+ - spec/setup.rb
49
+ - spec/model_definitions.rb
32
50
  - README.rdoc
33
- - CHANGELOG.rdoc
34
- - active_restrictors.gemspec
51
+ - Gemfile
35
52
  - lib/active_restrictors/active_restrictor_views.rb
36
53
  - lib/active_restrictors/version.rb
37
54
  - lib/active_restrictors/active_restrictor.rb
38
55
  - lib/active_restrictors.rb
56
+ - active_restrictors.gemspec
57
+ - CHANGELOG.rdoc
58
+ - Rakefile
39
59
  has_rdoc: true
40
60
  homepage: http://bitbucket.org/chrisroberts/active_restrictors
41
61
  licenses: []