controlist 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/controlist.rb +15 -6
- data/lib/controlist/interceptor.rb +23 -37
- data/lib/controlist/managers/base_manager.rb +2 -2
- data/lib/controlist/managers/thread_based_manager.rb +2 -2
- data/lib/controlist/permission.rb +14 -13
- data/lib/controlist/permissions/advanced_constrain.rb +2 -6
- data/lib/controlist/version.rb +1 -1
- data/test/feature_test.rb +30 -51
- data/test/fixtures/clazz.yml +3 -0
- data/test/test.sqlite3 +0 -0
- data/test/test_helper.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 641ff1fb7c170c5e3d33641eebb63e8d6ab42fcc
|
4
|
+
data.tar.gz: beb6c94f12192612b39df5b868ecc77a999eef68
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 65809c604627384c839c55d55eae91dab922cb6c95999d129bf196647073d0bb5e5c4dd009708f4f5620ce5465d8ca5d3d54c832a5a6a4d07dd004ce47f7704a
|
7
|
+
data.tar.gz: 86817ef4ae15ce9409f099c54a8cf0913c4a2ff6d063fa85c7e3e86535e7005969acad42eda4e8e67e21abfffecb7754757b6a85778ae050e75ef38401c9ec76
|
data/README.md
CHANGED
@@ -59,7 +59,7 @@ Controlist.initialize YourManager #, attribute_proxy: "_val", value_object_proxy
|
|
59
59
|
## Example
|
60
60
|
|
61
61
|
```ruby
|
62
|
-
Controlist.
|
62
|
+
Controlist.permission_manager.set_permission_package(OrderedPackage.new(
|
63
63
|
Controlist::Permission.new(User, READ, true, [
|
64
64
|
SimpleConstrain.new("name", "Tom"),
|
65
65
|
SimpleConstrain.new("name", ["Grade 1", "Grade 2"], relation: "clazz"),
|
data/lib/controlist.rb
CHANGED
@@ -8,10 +8,10 @@ module Controlist
|
|
8
8
|
|
9
9
|
class << self
|
10
10
|
|
11
|
-
attr_accessor :
|
11
|
+
attr_accessor :permission_manager, :attribute_proxy, :value_object_proxy, :logger
|
12
12
|
|
13
|
-
def initialize(
|
14
|
-
@
|
13
|
+
def initialize(permission_manager, config={})
|
14
|
+
@permission_manager = permission_manager
|
15
15
|
@attribute_proxy = config[:attribute_proxy] || "_val"
|
16
16
|
@value_object_proxy = config[:value_object_proxy] || "_value_object"
|
17
17
|
@logger = config[:logger] || Logger.new(STDOUT)
|
@@ -19,14 +19,23 @@ module Controlist
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def skip
|
22
|
-
@
|
23
|
-
yield
|
24
|
-
@
|
22
|
+
@permission_manager.enable_skip
|
23
|
+
result = yield
|
24
|
+
@permission_manager.disable_skip
|
25
|
+
result
|
25
26
|
end
|
26
27
|
|
27
28
|
def is_activerecord3?
|
28
29
|
ActiveRecord::VERSION::MAJOR == 3
|
29
30
|
end
|
31
|
+
|
32
|
+
def debug(*args, &block)
|
33
|
+
logger.debug *args, &block if @logger_enabled
|
34
|
+
end
|
35
|
+
|
36
|
+
def enable_logger
|
37
|
+
@logger_enabled = true
|
38
|
+
end
|
30
39
|
end
|
31
40
|
|
32
41
|
end
|
@@ -56,9 +56,9 @@ module Controlist
|
|
56
56
|
Array(methods).each do |method|
|
57
57
|
ActiveRecord::Persistence.module_eval %Q{
|
58
58
|
def #{method}_with_controlist(*args)
|
59
|
-
|
60
|
-
unless
|
61
|
-
permission_package =
|
59
|
+
permission_manager = Controlist.permission_manager
|
60
|
+
unless permission_manager.skip?
|
61
|
+
permission_package = permission_manager.get_permission_package
|
62
62
|
permissions = permission_package.list_#{operation}[self.class] if permission_package
|
63
63
|
if permissions.blank?
|
64
64
|
raise NoPermissionError
|
@@ -67,7 +67,7 @@ module Controlist
|
|
67
67
|
matched_permission = nil
|
68
68
|
permissions.each do |permission|
|
69
69
|
if permission.match_for_persistence(self, Controlist::Permission::#{operation.upcase})
|
70
|
-
Controlist.
|
70
|
+
Controlist.debug{"Controlist matched to \#{permission.is_allowed ? 'allow' : 'forbid'} \#{permission.inspect}"}
|
71
71
|
if permission.is_allowed
|
72
72
|
passed = true
|
73
73
|
end
|
@@ -76,9 +76,9 @@ module Controlist
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
if passed
|
79
|
-
Controlist.
|
79
|
+
Controlist.debug{"Controlist #{operation} checked: PASSED"}
|
80
80
|
else
|
81
|
-
Controlist.
|
81
|
+
Controlist.debug{"Controlist #{operation} checked: FORBIDDEN"}
|
82
82
|
if matched_permission.nil?
|
83
83
|
raise NoPermissionError
|
84
84
|
else
|
@@ -98,18 +98,9 @@ module Controlist
|
|
98
98
|
def hook_read
|
99
99
|
if Controlist.is_activerecord3?
|
100
100
|
ActiveRecord::QueryMethods.module_eval do
|
101
|
-
def where!(opts, *rest)
|
102
|
-
return if opts.blank?
|
103
|
-
self.where_values += build_where(opts, rest)
|
104
|
-
end
|
105
101
|
def _select!(*value)
|
106
102
|
self.select_values += Array.wrap(value)
|
107
103
|
end
|
108
|
-
def joins!(*args)
|
109
|
-
return if args.compact.blank?
|
110
|
-
args.flatten!
|
111
|
-
self.joins_values += args
|
112
|
-
end
|
113
104
|
end
|
114
105
|
ActiveRecord::IdentityMap.module_eval do
|
115
106
|
def self.enabled?
|
@@ -131,31 +122,26 @@ module Controlist
|
|
131
122
|
end
|
132
123
|
end
|
133
124
|
ActiveRecord::Relation.class_eval do
|
125
|
+
def real_build_arel_with_controlist
|
126
|
+
relation = self
|
127
|
+
permission_manager = Controlist.permission_manager
|
128
|
+
permission_package = permission_manager.get_permission_package
|
129
|
+
permissions = permission_package.list_read[@klass] if permission_package
|
130
|
+
if permissions.blank?
|
131
|
+
relation = self.where("1 != 1")
|
132
|
+
else
|
133
|
+
permissions.each do |permission|
|
134
|
+
relation = permission.handle_for_read relation
|
135
|
+
end
|
136
|
+
end
|
137
|
+
relation.send(:build_arel_without_controlist)
|
138
|
+
end
|
134
139
|
def build_arel_with_controlist
|
135
|
-
|
136
|
-
if
|
140
|
+
permission_manager = Controlist.permission_manager
|
141
|
+
if permission_manager.skip?
|
137
142
|
build_arel_without_controlist
|
138
143
|
else
|
139
|
-
|
140
|
-
raise Controlist::NotReuseableError.new("The relation has built a sql, you can't reuse it, or you can clone it before sql building", self)
|
141
|
-
else
|
142
|
-
@controlist_processing = true
|
143
|
-
permission_provider = Controlist.permission_provider
|
144
|
-
unless permission_provider.skip?
|
145
|
-
permission_package = permission_provider.get_permission_package
|
146
|
-
permissions = permission_package.list_read[@klass] if permission_package
|
147
|
-
if permissions.blank?
|
148
|
-
self.where!("1 != 1")
|
149
|
-
else
|
150
|
-
permissions.each do |permission|
|
151
|
-
permission.handle_for_read self
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
@controlist_processing = false
|
156
|
-
@controlist_done = true
|
157
|
-
build_arel_without_controlist
|
158
|
-
end
|
144
|
+
self.real_build_arel_with_controlist
|
159
145
|
end
|
160
146
|
end
|
161
147
|
alias_method_chain :build_arel, :controlist unless method_defined? :build_arel_without_controlist
|
@@ -16,11 +16,11 @@ module Controlist
|
|
16
16
|
Thread.current[:skip_controlist] == true
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
19
|
+
def enable_skip
|
20
20
|
Thread.current[:skip_controlist] = true
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
23
|
+
def disable_skip
|
24
24
|
Thread.current[:skip_controlist] = nil
|
25
25
|
end
|
26
26
|
|
@@ -10,7 +10,7 @@ module Controlist
|
|
10
10
|
# properties is hash with property and value pair, operation READ only need keys
|
11
11
|
attr_accessor :klass, :operations, :is_allowed, :constrains, :clause, :joins, :properties, :procs_read
|
12
12
|
|
13
|
-
def initialize(klass, operations, is_allowed = true, constrains=nil)
|
13
|
+
def initialize(klass, operations=nil, is_allowed = true, constrains=nil)
|
14
14
|
self.procs_read = []
|
15
15
|
self.klass = klass
|
16
16
|
unless operations.nil?
|
@@ -49,17 +49,14 @@ module Controlist
|
|
49
49
|
|
50
50
|
def handle_for_read(relation)
|
51
51
|
relation._select!(*self.properties.keys) unless self.properties.blank?
|
52
|
-
relation.joins
|
53
|
-
relation.where
|
52
|
+
relation = relation.joins(*self.joins) if self.joins.size > 0
|
53
|
+
relation = relation.where("#{self.clause}") if self.clause
|
54
54
|
unless self.procs_read.blank?
|
55
|
-
# Only support ActiveRecord 4
|
56
|
-
merging_relation = self.klass.unscoped
|
57
55
|
self.procs_read.each do |proc|
|
58
|
-
|
56
|
+
relation = proc.call(relation)
|
59
57
|
end
|
60
|
-
ActiveRecord::Relation::Merger.new(relation, merging_relation).merge
|
61
58
|
end
|
62
|
-
|
59
|
+
relation
|
63
60
|
end
|
64
61
|
|
65
62
|
def match_for_persistence(object, operation)
|
@@ -78,7 +75,7 @@ module Controlist
|
|
78
75
|
break
|
79
76
|
end
|
80
77
|
end
|
81
|
-
Controlist.
|
78
|
+
Controlist.debug{"Controlist #{operation} properties checked: #{properties_matched}"}
|
82
79
|
properties_matched
|
83
80
|
end
|
84
81
|
|
@@ -108,7 +105,7 @@ module Controlist
|
|
108
105
|
inner_matched
|
109
106
|
end
|
110
107
|
end
|
111
|
-
Controlist.
|
108
|
+
Controlist.debug{"Controlist #{operation} constrains checked: #{constrain_matched}"}
|
112
109
|
constrain_matched
|
113
110
|
end
|
114
111
|
|
@@ -127,11 +124,15 @@ module Controlist
|
|
127
124
|
self.constrains = constrains
|
128
125
|
end
|
129
126
|
|
130
|
-
def match_value(
|
127
|
+
def match_value(target, value, operator)
|
131
128
|
if operator.nil?
|
132
|
-
|
129
|
+
if value.is_a? Array
|
130
|
+
value.include? target
|
131
|
+
else
|
132
|
+
target == value
|
133
|
+
end
|
133
134
|
else
|
134
|
-
|
135
|
+
target.send(operator.to_sym, value)
|
135
136
|
end
|
136
137
|
end
|
137
138
|
|
@@ -10,12 +10,8 @@ module Controlist
|
|
10
10
|
self.table_name = hash[:table_name]
|
11
11
|
self.operator = hash[:operator]
|
12
12
|
self.clause = hash[:clause]
|
13
|
-
|
14
|
-
|
15
|
-
else
|
16
|
-
self.proc_read = hash[:proc_read]
|
17
|
-
self.proc_persistence = hash[:proc_persistence]
|
18
|
-
end
|
13
|
+
self.proc_read = hash[:proc_read]
|
14
|
+
self.proc_persistence = hash[:proc_persistence]
|
19
15
|
end
|
20
16
|
|
21
17
|
end
|
data/lib/controlist/version.rb
CHANGED
data/test/feature_test.rb
CHANGED
@@ -20,7 +20,7 @@ class FeatureTest < ActiveSupport::TestCase
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def test_read_constrains
|
23
|
-
Controlist.
|
23
|
+
Controlist.permission_manager.set_permission_package(OrderedPackage.new(
|
24
24
|
Controlist::Permission.new(User, READ, true, [
|
25
25
|
SimpleConstrain.new("name", "Tom"),
|
26
26
|
AdvancedConstrain.new(property: "name", value: ["Grade 1", "Grade 2"], relation: "clazz"),
|
@@ -29,40 +29,34 @@ class FeatureTest < ActiveSupport::TestCase
|
|
29
29
|
SimpleConstrain.new("age", [1,2,3]),
|
30
30
|
SimpleConstrain.new("clazz_id", -> { Clazz.select(:id).map(&:id) }),
|
31
31
|
AdvancedConstrain.new(clause: "age != 100"),
|
32
|
-
|
32
|
+
AdvancedConstrain.new(proc_read: lambda{|relation| relation.order("id DESC").limit(3) })
|
33
33
|
])))
|
34
34
|
|
35
35
|
relation = User.unscoped
|
36
|
-
relation.to_sql
|
37
|
-
|
38
|
-
assert_equal
|
39
|
-
" and (users.age >= 5) and (users.age is null) and (users.age in (1,2,3))" +
|
40
|
-
" and (users.clazz_id in (1,2)) and (age != 100)"], relation.where_values
|
41
|
-
unless Controlist.is_activerecord3?
|
42
|
-
assert_equal 3, relation.limit_value
|
43
|
-
assert_equal ["id DESC"], relation.order_values
|
44
|
-
end
|
36
|
+
sql = relation.to_sql.gsub(/ +/, " ")
|
37
|
+
puts sql
|
38
|
+
assert_equal true, sql.include?("((users.name = 'Tom') and (clazzs.name in ('Grade 1','Grade 2')) and (users.age >= 5) and (users.age is null) and (users.age in (1,2,3)) and (users.clazz_id in (1,2,3)) and (age != 100)) ORDER BY id DESC LIMIT 3")
|
45
39
|
end
|
46
40
|
|
47
41
|
def test_permission_empty
|
48
|
-
Controlist.
|
42
|
+
Controlist.permission_manager.set_permission_package(nil)
|
49
43
|
relation = User.unscoped
|
50
|
-
relation.to_sql
|
51
|
-
assert_equal
|
44
|
+
sql = relation.to_sql
|
45
|
+
assert_equal true, sql.include?("1 != 1")
|
52
46
|
end
|
53
47
|
|
54
48
|
def test_read_constrains_sql_only
|
55
|
-
Controlist.
|
49
|
+
Controlist.permission_manager.set_permission_package(OrderedPackage.new(
|
56
50
|
Controlist::Permission.new(User, READ, true, "age != 100")
|
57
51
|
))
|
58
52
|
relation = User.unscoped
|
59
|
-
relation.to_sql
|
60
|
-
assert_equal
|
53
|
+
sql = relation.to_sql
|
54
|
+
assert_equal true, sql.include?("age != 100")
|
61
55
|
end
|
62
56
|
|
63
57
|
|
64
58
|
def test_read_apply_properties
|
65
|
-
Controlist.
|
59
|
+
Controlist.permission_manager.set_permission_package(OrderedPackage.new(
|
66
60
|
Controlist::Permission.new(User, READ).apply(:name)
|
67
61
|
))
|
68
62
|
|
@@ -70,7 +64,7 @@ class FeatureTest < ActiveSupport::TestCase
|
|
70
64
|
assert_nil relation.first._value_object.clazz_id
|
71
65
|
assert_nil relation.first._val(:clazz_id)
|
72
66
|
assert_raise(ActiveModel::MissingAttributeError) { assert_nil relation.first.clazz_id }
|
73
|
-
Controlist.
|
67
|
+
Controlist.permission_manager.set_permission_package(OrderedPackage.new(
|
74
68
|
Controlist::Permission.new(User, READ)
|
75
69
|
))
|
76
70
|
|
@@ -81,7 +75,7 @@ class FeatureTest < ActiveSupport::TestCase
|
|
81
75
|
end
|
82
76
|
|
83
77
|
def test_update_fail_without_permissions
|
84
|
-
Controlist.
|
78
|
+
Controlist.permission_manager.set_permission_package(OrderedPackage.new(
|
85
79
|
Controlist::Permission.new(User, READ)
|
86
80
|
))
|
87
81
|
user = User.find 1
|
@@ -92,33 +86,32 @@ class FeatureTest < ActiveSupport::TestCase
|
|
92
86
|
end
|
93
87
|
|
94
88
|
def test_persistence_constrains
|
95
|
-
Controlist.
|
89
|
+
Controlist.permission_manager.set_permission_package(OrderedPackage.new(
|
96
90
|
Controlist::Permission.new(Clazz, READ),
|
97
91
|
Controlist::Permission.new(User, READ),
|
98
92
|
Controlist::Permission.new(User, UPDATE, false, AdvancedConstrain.new(property: "name", value: "To", operator: "include?")),
|
99
|
-
Controlist
|
100
|
-
Controlist::Permission.new(User, [UPDATE, DELETE], false, AdvancedConstrain.new(property: "name", value: "Grade 1", relation: "clazz")),
|
93
|
+
Controlist::Permission.new(User, UPDATE, false, AdvancedConstrain.new(proc_persistence: lambda{|object, operation| object.name == "Block"})),
|
94
|
+
Controlist::Permission.new(User, [UPDATE, DELETE], false, AdvancedConstrain.new(property: "name", value: ["Grade 1", "Grade 3"], relation: "clazz")),
|
101
95
|
Controlist::Permission.new(User, UPDATE)
|
102
96
|
))
|
103
97
|
|
104
98
|
user = User.find 3
|
105
99
|
assert_not_equal "Tom", user.name
|
106
100
|
assert_not_equal "Grade 1", user.clazz.name
|
101
|
+
assert_not_equal "Grade 3", user.clazz.name
|
107
102
|
user.name = "Test"
|
108
103
|
assert_equal true, user.save
|
109
104
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
}
|
115
|
-
end
|
105
|
+
assert_raise(Controlist::PermissionForbidden) {
|
106
|
+
user.name = "Block"
|
107
|
+
user.save
|
108
|
+
}
|
116
109
|
|
117
110
|
assert_raise(Controlist::NoPermissionError) {
|
118
111
|
user.destroy
|
119
112
|
}
|
120
113
|
|
121
|
-
Controlist.
|
114
|
+
Controlist.permission_manager.get_permission_package.add_permissions(Controlist::Permission.new(User, DELETE))
|
122
115
|
assert_instance_of User, user.destroy
|
123
116
|
|
124
117
|
assert_raise(Controlist::PermissionForbidden) {
|
@@ -144,7 +137,7 @@ class FeatureTest < ActiveSupport::TestCase
|
|
144
137
|
end
|
145
138
|
|
146
139
|
def test_persistence_apply_properties
|
147
|
-
Controlist.
|
140
|
+
Controlist.permission_manager.set_permission_package(OrderedPackage.new(
|
148
141
|
Controlist::Permission.new(User, READ),
|
149
142
|
Controlist::Permission.new(User, UPDATE, true, SimpleConstrain.new("name", "Tom")).apply(name: "Test", clazz_id: [1, 2]),
|
150
143
|
))
|
@@ -180,13 +173,13 @@ class FeatureTest < ActiveSupport::TestCase
|
|
180
173
|
end
|
181
174
|
|
182
175
|
def test_modify_permissions_on_the_fly
|
183
|
-
Controlist.
|
176
|
+
Controlist.permission_manager.set_permission_package(OrderedPackage.new(
|
184
177
|
Controlist::Permission.new(User, READ),
|
185
178
|
))
|
186
179
|
|
187
180
|
assert_instance_of User, User.find(1)
|
188
181
|
|
189
|
-
package = Controlist.
|
182
|
+
package = Controlist.permission_manager.get_permission_package
|
190
183
|
package.remove_permissions package.permissions.last
|
191
184
|
|
192
185
|
assert_raise(ActiveRecord::RecordNotFound) {
|
@@ -199,34 +192,20 @@ class FeatureTest < ActiveSupport::TestCase
|
|
199
192
|
|
200
193
|
def test_constrains_argument_error
|
201
194
|
assert_raise(ArgumentError) {
|
202
|
-
Controlist.
|
195
|
+
Controlist.permission_manager.set_permission_package(OrderedPackage.new(
|
203
196
|
Controlist::Permission.new(User, READ, true, Object.new)
|
204
197
|
))
|
205
198
|
}
|
206
199
|
end
|
207
200
|
|
208
|
-
def test_relation_not_reuseable
|
209
|
-
assert_raise(Controlist::NotReuseableError) {
|
210
|
-
relation = User.unscoped
|
211
|
-
relation.to_sql
|
212
|
-
relation_new = relation.where("1 = 1")
|
213
|
-
relation_new.to_sql
|
214
|
-
}
|
215
|
-
relation = User.unscoped
|
216
|
-
reuseable_relation = relation.clone
|
217
|
-
relation.to_sql
|
218
|
-
relation_new = reuseable_relation.where("1 = 1")
|
219
|
-
relation_new.to_sql
|
220
|
-
end
|
221
|
-
|
222
201
|
def test_skip
|
223
|
-
Controlist.
|
202
|
+
Controlist.permission_manager.set_permission_package(OrderedPackage.new(
|
224
203
|
Controlist::Permission.new(User, READ, true, "age != 100")
|
225
204
|
))
|
226
205
|
Controlist.skip do
|
227
206
|
relation = User.unscoped
|
228
|
-
relation.to_sql
|
229
|
-
assert_equal
|
207
|
+
sql = relation.to_sql
|
208
|
+
assert_equal "SELECT \"users\".* FROM \"users\"", sql.strip
|
230
209
|
end
|
231
210
|
end
|
232
211
|
|
data/test/fixtures/clazz.yml
CHANGED
data/test/test.sqlite3
CHANGED
Binary file
|
data/test/test_helper.rb
CHANGED
@@ -24,7 +24,7 @@ ActiveRecord::Base.logger = Logger.new(STDOUT)
|
|
24
24
|
|
25
25
|
require 'controlist'
|
26
26
|
require 'controlist/managers/thread_based_manager'
|
27
|
-
#Controlist.initialize Controlist::
|
27
|
+
#Controlist.initialize Controlist::Managers::ThreadBasedManager, attribute_proxy: "_val", value_object_proxy: "_value_object", logger: Logger.new(STDOUT)
|
28
28
|
Controlist.initialize Controlist::Managers::ThreadBasedManager
|
29
29
|
|
30
30
|
unless Controlist.is_activerecord3?
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: controlist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Leon Li
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-07-
|
11
|
+
date: 2015-07-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -193,7 +193,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
193
193
|
version: '0'
|
194
194
|
requirements: []
|
195
195
|
rubyforge_project:
|
196
|
-
rubygems_version: 2.
|
196
|
+
rubygems_version: 2.2.2
|
197
197
|
signing_key:
|
198
198
|
specification_version: 4
|
199
199
|
summary: Fine-grained access control library for Ruby ActiveRecord
|
@@ -206,3 +206,4 @@ test_files:
|
|
206
206
|
- test/models/user.rb
|
207
207
|
- test/test.sqlite3
|
208
208
|
- test/test_helper.rb
|
209
|
+
has_rdoc:
|