simon_says 0.2.0 → 0.3.0.alpha.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +25 -12
- data/lib/simon_says/roleable.rb +35 -7
- data/lib/simon_says/version.rb +1 -1
- data/test/simon_says/roleable_test.rb +23 -4
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c6bf629f417e1bd66bf820ee52c333aacfe0e25a
|
4
|
+
data.tar.gz: 340919dff4421094ec54d4b75e0446b54f522a10
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c2f679703c9bd5fb566ee181498131d069c6ad5e3de83957cb2e8a8a3777a6c194589a399a58e4272906c1e8e89f309101ad23103f1b5e515e7d747edc0c9055
|
7
|
+
data.tar.gz: 1aae317daf655c2dcbcf425ef2eeeff492212003a4256501f72c5278239aeb8f57b861051a407d8f35af5467e8f2f10466695e9a5eeaec887c3d050c02129ef4
|
data/README.md
CHANGED
@@ -12,7 +12,7 @@ Rails that works great with devise!
|
|
12
12
|
|
13
13
|
### Installation
|
14
14
|
|
15
|
-
SimonSays can be installed via your Gemfile
|
15
|
+
SimonSays can be installed via your Gemfile.
|
16
16
|
|
17
17
|
```ruby
|
18
18
|
gem 'simon_says'
|
@@ -22,21 +22,23 @@ gem 'simon_says'
|
|
22
22
|
|
23
23
|
SimonSays consists of two parts:
|
24
24
|
|
25
|
-
1. A [Roleable](#roleable)
|
26
|
-
on User models or on join through models.
|
27
|
-
2. An [Authorizer](#authorizer)
|
28
|
-
to controllers for finding and authorizing
|
25
|
+
1. A [Roleable](#roleable) module mixin which provides a way to define
|
26
|
+
roles on User models or on join through models.
|
27
|
+
2. An [Authorizer](#authorizer) module mixin which provides a
|
28
|
+
declarative API to controllers for finding and authorizing resources.
|
29
29
|
|
30
30
|
#### Roleable
|
31
31
|
|
32
32
|
First, we need to define some roles on a model. Roles are stored as an
|
33
33
|
integer and [bitmasking](https://en.wikipedia.org/wiki/Mask_(computing))
|
34
|
-
is used to determine the roles assigned for
|
34
|
+
is used to determine the roles assigned for given record. SimonSays
|
35
35
|
provides a generator for creating a new migration for this required
|
36
36
|
attribute:
|
37
37
|
|
38
38
|
```bash
|
39
|
+
rails g model User # if and only if this model does not yet exist
|
39
40
|
rails g active_record:simon_says User
|
41
|
+
rails db:migrate
|
40
42
|
```
|
41
43
|
|
42
44
|
Now we can define some roles in our User model. For example:
|
@@ -51,14 +53,16 @@ end
|
|
51
53
|
# > User.new.roles
|
52
54
|
# => []
|
53
55
|
|
54
|
-
# > u = User.
|
55
|
-
#
|
56
|
+
# > u = User.create(roles: %i[add edit])
|
57
|
+
# => #<User ...>
|
56
58
|
# > u.roles
|
57
59
|
# => [:add, :edit]
|
58
60
|
# > u.has_add?
|
59
61
|
# => true
|
60
62
|
# > u.has_delete?
|
61
63
|
# => false
|
64
|
+
# > u.update roles: %i[delete add edit]
|
65
|
+
# > u.save # save record with roles_mask of 7
|
62
66
|
```
|
63
67
|
|
64
68
|
The attribute name can be customized by using the `:as` option as seen
|
@@ -103,11 +107,20 @@ end
|
|
103
107
|
# => [:download, :edit, :delete]
|
104
108
|
```
|
105
109
|
|
106
|
-
|
107
|
-
|
108
|
-
|
110
|
+
`Roleable` also creates two scopes that can be used to find records that
|
111
|
+
have a given set roles. Using the default attribute name, the two scopes
|
112
|
+
generated would be `with_roles` and `with_all_roles`. Both methods
|
113
|
+
accept one or more role symbols as its arguments. The first scope,
|
114
|
+
`with_roles`, will find any record with one or more the supplied roles.
|
115
|
+
The second scope, `with_all_roles` will only find record that have all
|
116
|
+
of the supplied roles.
|
117
|
+
|
118
|
+
It is useful to note the various dynamically generated methods as well
|
119
|
+
the `ROLES` constant, which is used in the Permission example. Take a
|
120
|
+
look at the `Roleable`
|
109
121
|
[source code](https://github.com/SimplyBuilt/SimonSays/blob/master/lib/simon_says/roleable.rb)
|
110
|
-
to see how
|
122
|
+
to see how methods and scopes are dynamically generated with
|
123
|
+
`has_roles`.
|
111
124
|
|
112
125
|
#### Authorizer
|
113
126
|
|
data/lib/simon_says/roleable.rb
CHANGED
@@ -71,11 +71,11 @@ module SimonSays
|
|
71
71
|
args.compact!
|
72
72
|
args.map!(&:to_sym)
|
73
73
|
|
74
|
-
self
|
74
|
+
self.#{name}_mask = (args & #{const}).map { |i| 2 ** #{const}.index(i) }.sum
|
75
75
|
end
|
76
76
|
|
77
77
|
def #{name}
|
78
|
-
#{const}.reject { |i| ((#{name}_mask || 0) & 2 ** #{const}.index(i)).zero? }
|
78
|
+
#{const}.reject { |i| ((#{name}_mask || 0) & 2 ** #{const}.index(i)).zero? }.tap(&:freeze)
|
79
79
|
end
|
80
80
|
|
81
81
|
def has_#{name}?(*args)
|
@@ -93,12 +93,40 @@ module SimonSays
|
|
93
93
|
RUBY_EVAL
|
94
94
|
end
|
95
95
|
|
96
|
-
#
|
97
|
-
#
|
98
|
-
|
99
|
-
|
100
|
-
|
96
|
+
# Try to declare ActiveRecord scopes or Sequel subsets for finding
|
97
|
+
# records with a given set of roles
|
98
|
+
|
99
|
+
scope_method = respond_to?(:scope) ? :scope : respond_to?(:subset) ? :subset : nil
|
100
|
+
|
101
|
+
if scope_method
|
102
|
+
send scope_method, "with_#{name}", ->(*args) {
|
103
|
+
clause = "#{name}_mask & ?"
|
104
|
+
values = Roleable.roles2ints(roles, *args)
|
105
|
+
|
106
|
+
query = where(clause, values.shift)
|
107
|
+
query = query.or(where(clause, values.shift)) until values.empty?
|
108
|
+
query
|
109
|
+
}
|
110
|
+
|
111
|
+
send scope_method, "with_all_#{name}", ->(*args) {
|
112
|
+
clause = "#{name}_mask & ?"
|
113
|
+
values = Roleable.roles2ints(roles, *args)
|
114
|
+
|
115
|
+
query = where(clause, values.shift)
|
116
|
+
query = query.where(clause, values.shift) until values.empty?
|
117
|
+
query
|
118
|
+
}
|
119
|
+
end
|
101
120
|
end
|
102
121
|
end
|
122
|
+
|
123
|
+
def self.roles2ints(defined_roles, *args)
|
124
|
+
values = args.map do |arg|
|
125
|
+
index = defined_roles.index(arg)
|
126
|
+
index ? 2 ** index : nil
|
127
|
+
end
|
128
|
+
|
129
|
+
values.tap(&:flatten!)
|
130
|
+
end
|
103
131
|
end
|
104
132
|
end
|
data/lib/simon_says/version.rb
CHANGED
@@ -132,15 +132,34 @@ class RoleableTest < ActiveSupport::TestCase
|
|
132
132
|
test "named scope with_roles" do
|
133
133
|
assert_equal [2, 1], [
|
134
134
|
Membership.with_roles(:download).count,
|
135
|
-
Membership.with_roles(:delete).count
|
135
|
+
Membership.with_roles(:edit, :delete).count,
|
136
136
|
]
|
137
137
|
end
|
138
138
|
|
139
139
|
test "named scope with_access" do
|
140
|
-
assert_equal [2,
|
141
|
-
Admin.with_access(:marketing).count,
|
140
|
+
assert_equal [2, 3, 4], [
|
142
141
|
Admin.with_access(:content).count,
|
143
|
-
Admin.with_access(:
|
142
|
+
Admin.with_access(:content, :marketing).count,
|
143
|
+
Admin.with_access(:content, :marketing, :support).count
|
144
|
+
]
|
145
|
+
end
|
146
|
+
|
147
|
+
test "named scope with_all_roles" do
|
148
|
+
memberships(:mb1).update roles: %i[download fork edit]
|
149
|
+
memberships(:mb2).update roles: %i[download fork]
|
150
|
+
|
151
|
+
assert_equal [2, 0], [
|
152
|
+
Membership.with_all_roles(:download, :fork).count,
|
153
|
+
Membership.with_all_roles(:download, :fork, :edit, :delete).count
|
154
|
+
]
|
155
|
+
end
|
156
|
+
|
157
|
+
test "named scope with_all_access" do
|
158
|
+
Admin.create(access: %i[content marketing])
|
159
|
+
|
160
|
+
assert_equal [2, 1], [
|
161
|
+
Admin.with_all_access(:content, :marketing).count,
|
162
|
+
Admin.with_all_access(:content, :marketing, :support).count
|
144
163
|
]
|
145
164
|
end
|
146
165
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simon_says
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0.alpha.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Coyne
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2018-10-26 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -231,9 +231,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
231
231
|
version: '0'
|
232
232
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
233
233
|
requirements:
|
234
|
-
- - "
|
234
|
+
- - ">"
|
235
235
|
- !ruby/object:Gem::Version
|
236
|
-
version:
|
236
|
+
version: 1.3.1
|
237
237
|
requirements: []
|
238
238
|
rubyforge_project:
|
239
239
|
rubygems_version: 2.6.13
|