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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6c3b6bd7983eb136f044c2653430143493637871
4
- data.tar.gz: a66d00fe12a96f48e8865a61aaf61f145feb5007
3
+ metadata.gz: c6bf629f417e1bd66bf820ee52c333aacfe0e25a
4
+ data.tar.gz: 340919dff4421094ec54d4b75e0446b54f522a10
5
5
  SHA512:
6
- metadata.gz: 284e3a2a74a36d1ae52aa78849a347f38071b4c5838dc43b60cdba794fd9bed021054941b44b44d28744511a3af57eb6764108ae087b7bf1d17e96b03f1f1e8a
7
- data.tar.gz: dea0bc94572281d6b505ef4a3eb2ed0fb502e916483e122cf19f34444dfaf12947e374065495f2b2edd3c8a0e370b45a5454a1075c56daec896f642a02ae451e
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 or using Ruby gems directly.
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) concern which provides a way to define access roles
26
- on User models or on join through models.
27
- 2. An [Authorizer](#authorizer) concern which provides a declarative API
28
- to controllers for finding and authorizing model resources.
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 that model. SimonSays
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.new(roles: %i[add edit])
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
- It is useful to note the dynamically generated `has_` methods as shown
107
- in the User model as well the `ROLES` constant which is used in the
108
- Permission example. Take a look at the `Roleable`
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 features are dynamically generated with `has_roles`.
122
+ to see how methods and scopes are dynamically generated with
123
+ `has_roles`.
111
124
 
112
125
  #### Authorizer
113
126
 
@@ -71,11 +71,11 @@ module SimonSays
71
71
  args.compact!
72
72
  args.map!(&:to_sym)
73
73
 
74
- self[:#{name}_mask] = (args & #{const}).map { |i| 2 ** #{const}.index(i) }.sum
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
- # Declare a scope for finding records with a given role set
97
- # TODO support an array roles (must match ALL)
98
- scope "with_#{name}", ->(role) {
99
- where("(#{name}_mask & ?) > 0", 2**roles.index(role.to_sym))
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
@@ -1,3 +1,3 @@
1
1
  module SimonSays
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0.alpha.1'
3
3
  end
@@ -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, 2, 2], [
141
- Admin.with_access(:marketing).count,
140
+ assert_equal [2, 3, 4], [
142
141
  Admin.with_access(:content).count,
143
- Admin.with_access(:support).count
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.2.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: 2017-10-07 00:00:00.000000000 Z
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: '0'
236
+ version: 1.3.1
237
237
  requirements: []
238
238
  rubyforge_project:
239
239
  rubygems_version: 2.6.13