role_fu 0.3.1 → 0.4.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/CHANGELOG.md +21 -1
- data/README.md +42 -1
- data/gemfiles/rails_7.2.gemfile.lock +7 -6
- data/gemfiles/rails_8.0.gemfile.lock +7 -6
- data/gemfiles/rails_8.1.gemfile.lock +7 -6
- data/lib/generators/role_fu/abilities_generator.rb +14 -1
- data/lib/generators/role_fu/audit_generator.rb +14 -1
- data/lib/generators/role_fu/role_fu_generator.rb +13 -1
- data/lib/generators/role_fu/templates/abilities_migration.rb.erb +2 -2
- data/lib/generators/role_fu/templates/audit_migration.rb.erb +4 -4
- data/lib/generators/role_fu/templates/migration.rb.erb +5 -5
- data/lib/generators/role_fu/templates/role_fu.rb +7 -0
- data/lib/role_fu/configuration.rb +13 -0
- data/lib/role_fu/resourceable.rb +31 -0
- data/lib/role_fu/roleable.rb +19 -0
- data/lib/role_fu/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1d30d222a87baa0b3a45fa90fb125d0be1b00c0ed6c61f84f494a55e383f9a15
|
|
4
|
+
data.tar.gz: a97948df483b99134fd78003033de2dfa0ab623bc4a7ea798263c7547c3ba093
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d0add7c8ff6d0b02a2eaf3bf74903064a9ad55ea090f67914d0e69baafa4b732425809f210e2ed4627904013b22db2656f019747ed4c079938caab5caee606c2
|
|
7
|
+
data.tar.gz: b0f085cd793cb87574258b066c6a45b786437525a83d1df1bfb46e8bec4c75508e7620b21d78efd39ced57b744f1eb7f387181fb161d728890778ff27313d339
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,24 @@
|
|
|
1
|
-
## [
|
|
1
|
+
## [0.4.0] - 2026-02-08
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
|
|
5
|
+
- **Dynamic Shortcuts**:
|
|
6
|
+
- Call methods like `user.is_admin?` or `user.is_manager?(org)` directly.
|
|
7
|
+
- Configurable pattern (`config.dynamic_shortcuts_pattern`), defaults to `is_%{role}?`.
|
|
8
|
+
- Custom patterns supported (e.g. `in_%{role}_group?`).
|
|
9
|
+
- **Enhanced Resourceable**:
|
|
10
|
+
- Added `has_many :users, through: :roles` association for direct user access (e.g. `org.users`).
|
|
11
|
+
- Added `users_grouped_by_role` to get a hash of users per role.
|
|
12
|
+
- Added `destroy_role(role_name)` for bulk cleanup on a resource.
|
|
13
|
+
- Added convenience methods/aliases: `users_with_roles`, `applied_roles`.
|
|
14
|
+
- **Generator**:
|
|
15
|
+
- Updated install template to include new configuration options.
|
|
16
|
+
|
|
17
|
+
## [0.3.3] - 2026-02-07
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **UUID Support**: Generators now automatically detect and use UUIDs for primary and foreign keys if enabled in the application or model configuration.
|
|
2
22
|
|
|
3
23
|
## [0.3.1] - 2026-02-06
|
|
4
24
|
|
data/README.md
CHANGED
|
@@ -235,8 +235,14 @@ class Organization < ApplicationRecord
|
|
|
235
235
|
end
|
|
236
236
|
|
|
237
237
|
org = Organization.first
|
|
238
|
+
org.users # All users with ANY role (via has_many :through)
|
|
238
239
|
org.users_with_role(:manager)
|
|
239
|
-
org.
|
|
240
|
+
org.users_with_roles # All users with ANY role on this org
|
|
241
|
+
org.available_roles # ["manager", "admin"]
|
|
242
|
+
org.users_grouped_by_role # { "manager" => [user1], "admin" => [user2] }
|
|
243
|
+
|
|
244
|
+
# Cleanup
|
|
245
|
+
org.destroy_role(:manager) # Removes the "manager" role and all its assignments from this org
|
|
240
246
|
|
|
241
247
|
# Scopes
|
|
242
248
|
Organization.with_role(:manager, user)
|
|
@@ -254,9 +260,44 @@ RoleFu.configure do |config|
|
|
|
254
260
|
|
|
255
261
|
# Enable Rolify-style permissive checks (Global roles override resource checks)
|
|
256
262
|
config.global_roles_override = true
|
|
263
|
+
|
|
264
|
+
# Enable dynamic shortcuts (e.g. user.is_admin?)
|
|
265
|
+
# Default is nil (disabled). Recommended: "is_%{role}?"
|
|
266
|
+
config.dynamic_shortcuts_pattern = "is_%{role}?"
|
|
257
267
|
end
|
|
258
268
|
```
|
|
259
269
|
|
|
270
|
+
## Dynamic Shortcuts
|
|
271
|
+
|
|
272
|
+
RoleFu supports dynamic methods for quick role checking if you configure a pattern.
|
|
273
|
+
|
|
274
|
+
**Configuration:**
|
|
275
|
+
|
|
276
|
+
```ruby
|
|
277
|
+
# In config/initializers/role_fu.rb
|
|
278
|
+
RoleFu.configure do |config|
|
|
279
|
+
config.dynamic_shortcuts_pattern = "is_%{role}?"
|
|
280
|
+
end
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Usage:**
|
|
284
|
+
|
|
285
|
+
```ruby
|
|
286
|
+
user.is_admin? # Equivalent to user.has_role?(:admin)
|
|
287
|
+
user.is_manager?(org) # Equivalent to user.has_role?(:manager, org)
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**Custom Pattern:**
|
|
291
|
+
|
|
292
|
+
If you use groups:
|
|
293
|
+
|
|
294
|
+
```ruby
|
|
295
|
+
config.dynamic_shortcuts_pattern = "in_%{role}_group?"
|
|
296
|
+
|
|
297
|
+
# Usage:
|
|
298
|
+
user.in_admin_group? # Equivalent to user.has_role?(:admin)
|
|
299
|
+
```
|
|
300
|
+
|
|
260
301
|
## Custom Aliases (e.g. Groups)
|
|
261
302
|
|
|
262
303
|
If you prefer different terminology (e.g., "Groups" instead of "Roles"), you can alias the methods in your models:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: ..
|
|
3
3
|
specs:
|
|
4
|
-
role_fu (0.
|
|
4
|
+
role_fu (0.4.0)
|
|
5
5
|
activerecord (>= 7.2)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
@@ -104,8 +104,9 @@ GEM
|
|
|
104
104
|
i18n (1.14.8)
|
|
105
105
|
concurrent-ruby (~> 1.0)
|
|
106
106
|
io-console (0.8.2)
|
|
107
|
-
irb (1.
|
|
107
|
+
irb (1.17.0)
|
|
108
108
|
pp (>= 0.6.0)
|
|
109
|
+
prism (>= 1.3.0)
|
|
109
110
|
rdoc (>= 4.0.0)
|
|
110
111
|
reline (>= 0.4.2)
|
|
111
112
|
json (2.18.1)
|
|
@@ -205,7 +206,7 @@ GEM
|
|
|
205
206
|
zeitwerk (~> 2.6)
|
|
206
207
|
rainbow (3.1.1)
|
|
207
208
|
rake (13.3.1)
|
|
208
|
-
rdoc (7.
|
|
209
|
+
rdoc (7.2.0)
|
|
209
210
|
erb
|
|
210
211
|
psych (>= 4.0.0)
|
|
211
212
|
tsort
|
|
@@ -345,7 +346,7 @@ CHECKSUMS
|
|
|
345
346
|
globalid (1.3.0) sha256=05c639ad6eb4594522a0b07983022f04aa7254626ab69445a0e493aa3786ff11
|
|
346
347
|
i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5
|
|
347
348
|
io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
|
|
348
|
-
irb (1.
|
|
349
|
+
irb (1.17.0) sha256=168c4ddb93d8a361a045c41d92b2952c7a118fa73f23fe14e55609eb7a863aae
|
|
349
350
|
json (2.18.1) sha256=fe112755501b8d0466b5ada6cf50c8c3f41e897fa128ac5d263ec09eedc9f986
|
|
350
351
|
language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
|
|
351
352
|
lefthook (2.1.0) sha256=a100dc90139806e62b9aa40cf32e9edf34f6cff10c8b55ebd14a1b0add4b86bf
|
|
@@ -386,10 +387,10 @@ CHECKSUMS
|
|
|
386
387
|
railties (7.2.3) sha256=6eb010a6bfe6f223e783f739ddfcbdb5b88b1f3a87f7739f0a0685e466250422
|
|
387
388
|
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
|
|
388
389
|
rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c
|
|
389
|
-
rdoc (7.
|
|
390
|
+
rdoc (7.2.0) sha256=8650f76cd4009c3b54955eb5d7e3a075c60a57276766ebf36f9085e8c9f23192
|
|
390
391
|
regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4
|
|
391
392
|
reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
|
|
392
|
-
role_fu (0.
|
|
393
|
+
role_fu (0.4.0)
|
|
393
394
|
rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
|
|
394
395
|
rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
|
|
395
396
|
rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: ..
|
|
3
3
|
specs:
|
|
4
|
-
role_fu (0.
|
|
4
|
+
role_fu (0.4.0)
|
|
5
5
|
activerecord (>= 7.2)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
@@ -101,8 +101,9 @@ GEM
|
|
|
101
101
|
i18n (1.14.8)
|
|
102
102
|
concurrent-ruby (~> 1.0)
|
|
103
103
|
io-console (0.8.2)
|
|
104
|
-
irb (1.
|
|
104
|
+
irb (1.17.0)
|
|
105
105
|
pp (>= 0.6.0)
|
|
106
|
+
prism (>= 1.3.0)
|
|
106
107
|
rdoc (>= 4.0.0)
|
|
107
108
|
reline (>= 0.4.2)
|
|
108
109
|
json (2.18.1)
|
|
@@ -201,7 +202,7 @@ GEM
|
|
|
201
202
|
zeitwerk (~> 2.6)
|
|
202
203
|
rainbow (3.1.1)
|
|
203
204
|
rake (13.3.1)
|
|
204
|
-
rdoc (7.
|
|
205
|
+
rdoc (7.2.0)
|
|
205
206
|
erb
|
|
206
207
|
psych (>= 4.0.0)
|
|
207
208
|
tsort
|
|
@@ -341,7 +342,7 @@ CHECKSUMS
|
|
|
341
342
|
globalid (1.3.0) sha256=05c639ad6eb4594522a0b07983022f04aa7254626ab69445a0e493aa3786ff11
|
|
342
343
|
i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5
|
|
343
344
|
io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
|
|
344
|
-
irb (1.
|
|
345
|
+
irb (1.17.0) sha256=168c4ddb93d8a361a045c41d92b2952c7a118fa73f23fe14e55609eb7a863aae
|
|
345
346
|
json (2.18.1) sha256=fe112755501b8d0466b5ada6cf50c8c3f41e897fa128ac5d263ec09eedc9f986
|
|
346
347
|
language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
|
|
347
348
|
lefthook (2.1.0) sha256=a100dc90139806e62b9aa40cf32e9edf34f6cff10c8b55ebd14a1b0add4b86bf
|
|
@@ -382,10 +383,10 @@ CHECKSUMS
|
|
|
382
383
|
railties (8.0.4) sha256=8203d853dcffab4abcdd05c193f101676a92068075464694790f6d8f72d5cb47
|
|
383
384
|
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
|
|
384
385
|
rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c
|
|
385
|
-
rdoc (7.
|
|
386
|
+
rdoc (7.2.0) sha256=8650f76cd4009c3b54955eb5d7e3a075c60a57276766ebf36f9085e8c9f23192
|
|
386
387
|
regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4
|
|
387
388
|
reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
|
|
388
|
-
role_fu (0.
|
|
389
|
+
role_fu (0.4.0)
|
|
389
390
|
rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
|
|
390
391
|
rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
|
|
391
392
|
rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: ..
|
|
3
3
|
specs:
|
|
4
|
-
role_fu (0.
|
|
4
|
+
role_fu (0.4.0)
|
|
5
5
|
activerecord (>= 7.2)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
@@ -103,8 +103,9 @@ GEM
|
|
|
103
103
|
i18n (1.14.8)
|
|
104
104
|
concurrent-ruby (~> 1.0)
|
|
105
105
|
io-console (0.8.2)
|
|
106
|
-
irb (1.
|
|
106
|
+
irb (1.17.0)
|
|
107
107
|
pp (>= 0.6.0)
|
|
108
|
+
prism (>= 1.3.0)
|
|
108
109
|
rdoc (>= 4.0.0)
|
|
109
110
|
reline (>= 0.4.2)
|
|
110
111
|
json (2.18.1)
|
|
@@ -203,7 +204,7 @@ GEM
|
|
|
203
204
|
zeitwerk (~> 2.6)
|
|
204
205
|
rainbow (3.1.1)
|
|
205
206
|
rake (13.3.1)
|
|
206
|
-
rdoc (7.
|
|
207
|
+
rdoc (7.2.0)
|
|
207
208
|
erb
|
|
208
209
|
psych (>= 4.0.0)
|
|
209
210
|
tsort
|
|
@@ -343,7 +344,7 @@ CHECKSUMS
|
|
|
343
344
|
globalid (1.3.0) sha256=05c639ad6eb4594522a0b07983022f04aa7254626ab69445a0e493aa3786ff11
|
|
344
345
|
i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5
|
|
345
346
|
io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
|
|
346
|
-
irb (1.
|
|
347
|
+
irb (1.17.0) sha256=168c4ddb93d8a361a045c41d92b2952c7a118fa73f23fe14e55609eb7a863aae
|
|
347
348
|
json (2.18.1) sha256=fe112755501b8d0466b5ada6cf50c8c3f41e897fa128ac5d263ec09eedc9f986
|
|
348
349
|
language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
|
|
349
350
|
lefthook (2.1.0) sha256=a100dc90139806e62b9aa40cf32e9edf34f6cff10c8b55ebd14a1b0add4b86bf
|
|
@@ -384,10 +385,10 @@ CHECKSUMS
|
|
|
384
385
|
railties (8.1.2) sha256=1289ece76b4f7668fc46d07e55cc992b5b8751f2ad85548b7da351b8c59f8055
|
|
385
386
|
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
|
|
386
387
|
rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c
|
|
387
|
-
rdoc (7.
|
|
388
|
+
rdoc (7.2.0) sha256=8650f76cd4009c3b54955eb5d7e3a075c60a57276766ebf36f9085e8c9f23192
|
|
388
389
|
regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4
|
|
389
390
|
reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
|
|
390
|
-
role_fu (0.
|
|
391
|
+
role_fu (0.4.0)
|
|
391
392
|
rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
|
|
392
393
|
rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
|
|
393
394
|
rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
|
|
@@ -22,7 +22,7 @@ module RoleFu
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def create_abilities_migration
|
|
25
|
-
migration_template "abilities_migration.rb.erb", "db/migrate/role_fu_create_permissions.rb"
|
|
25
|
+
migration_template "abilities_migration.rb.erb", "db/migrate/role_fu_create_permissions.rb"
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def inject_into_role_model
|
|
@@ -56,6 +56,19 @@ module RoleFu
|
|
|
56
56
|
def migration_version
|
|
57
57
|
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" if Rails::VERSION::MAJOR >= 5
|
|
58
58
|
end
|
|
59
|
+
|
|
60
|
+
def uuid_enabled?
|
|
61
|
+
options[:primary_key_type] == "uuid" ||
|
|
62
|
+
Rails.configuration.generators.options.dig(:active_record, :primary_key_type) == :uuid ||
|
|
63
|
+
role_has_uuid_pk?
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def role_has_uuid_pk?
|
|
67
|
+
role_cname = RoleFu.configuration.role_class_name
|
|
68
|
+
klass = role_cname.safe_constantize
|
|
69
|
+
return false unless klass&.table_exists?
|
|
70
|
+
klass.columns_hash[klass.primary_key]&.type == :uuid
|
|
71
|
+
end
|
|
59
72
|
end
|
|
60
73
|
end
|
|
61
74
|
end
|
|
@@ -18,7 +18,7 @@ module RoleFu
|
|
|
18
18
|
desc "" # Empty desc to avoid duplication at the bottom
|
|
19
19
|
|
|
20
20
|
def create_audit_migration
|
|
21
|
-
migration_template "audit_migration.rb.erb", "db/migrate/role_fu_create_audits.rb"
|
|
21
|
+
migration_template "audit_migration.rb.erb", "db/migrate/role_fu_create_audits.rb"
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def create_model
|
|
@@ -30,6 +30,19 @@ module RoleFu
|
|
|
30
30
|
def migration_version
|
|
31
31
|
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" if Rails::VERSION::MAJOR >= 5
|
|
32
32
|
end
|
|
33
|
+
|
|
34
|
+
def uuid_enabled?
|
|
35
|
+
options[:primary_key_type] == "uuid" ||
|
|
36
|
+
Rails.configuration.generators.options.dig(:active_record, :primary_key_type) == :uuid ||
|
|
37
|
+
user_has_uuid_pk?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def user_has_uuid_pk?
|
|
41
|
+
user_cname = RoleFu.configuration.user_class_name || "User"
|
|
42
|
+
klass = user_cname.safe_constantize
|
|
43
|
+
return false unless klass&.table_exists?
|
|
44
|
+
klass.columns_hash[klass.primary_key]&.type == :uuid
|
|
45
|
+
end
|
|
33
46
|
end
|
|
34
47
|
end
|
|
35
48
|
end
|
|
@@ -33,7 +33,7 @@ module RoleFu
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def copy_role_fu_migration
|
|
36
|
-
migration_template "migration.rb.erb", "db/migrate/role_fu_create_#{table_name}.rb"
|
|
36
|
+
migration_template "migration.rb.erb", "db/migrate/role_fu_create_#{table_name}.rb"
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def inject_role_fu_into_user_model
|
|
@@ -52,6 +52,18 @@ module RoleFu
|
|
|
52
52
|
def migration_version
|
|
53
53
|
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" if Rails::VERSION::MAJOR >= 5
|
|
54
54
|
end
|
|
55
|
+
|
|
56
|
+
def uuid_enabled?
|
|
57
|
+
options[:primary_key_type] == "uuid" ||
|
|
58
|
+
Rails.configuration.generators.options.dig(:active_record, :primary_key_type) == :uuid ||
|
|
59
|
+
user_has_uuid_pk?
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def user_has_uuid_pk?
|
|
63
|
+
klass = user_cname.safe_constantize
|
|
64
|
+
return false unless klass&.table_exists?
|
|
65
|
+
klass.columns_hash[klass.primary_key]&.type == :uuid
|
|
66
|
+
end
|
|
55
67
|
end
|
|
56
68
|
end
|
|
57
69
|
end
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
class RoleFuCreatePermissions < ActiveRecord::Migration<%= migration_version %>
|
|
4
4
|
def change
|
|
5
|
-
create_table :permissions do |t|
|
|
6
|
-
t.references :role, null: false, index: true
|
|
5
|
+
create_table :permissions<%= ", id: :uuid" if uuid_enabled? %> do |t|
|
|
6
|
+
t.references :role, null: false, index: true<%= ", type: :uuid" if uuid_enabled? %>
|
|
7
7
|
t.string :action, null: false
|
|
8
8
|
t.jsonb :conditions, default: {}
|
|
9
9
|
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
class RoleFuCreateAudits < ActiveRecord::Migration<%= migration_version %>
|
|
4
4
|
def change
|
|
5
|
-
create_table :role_assignment_audits do |t|
|
|
6
|
-
t.references :user, index: true
|
|
7
|
-
t.references :role, index: true
|
|
8
|
-
t.references :role_assignment, index: true
|
|
5
|
+
create_table :role_assignment_audits<%= ", id: :uuid" if uuid_enabled? %> do |t|
|
|
6
|
+
t.references :user, index: true<%= ", type: :uuid" if uuid_enabled? %>
|
|
7
|
+
t.references :role, index: true<%= ", type: :uuid" if uuid_enabled? %>
|
|
8
|
+
t.references :role_assignment, index: true<%= ", type: :uuid" if uuid_enabled? %>
|
|
9
9
|
t.string :operation, null: false # INSERT, UPDATE, DELETE
|
|
10
10
|
t.string :whodunnit
|
|
11
11
|
t.jsonb :meta_snapshot
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
class RoleFuCreate<%= table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
|
|
4
4
|
def change
|
|
5
|
-
create_table :<%= table_name %> do |t|
|
|
5
|
+
create_table :<%= table_name %><%= ", id: :uuid" if uuid_enabled? %> do |t|
|
|
6
6
|
t.string :name
|
|
7
|
-
t.references :resource, polymorphic: true, index: true
|
|
7
|
+
t.references :resource, polymorphic: true, index: true<%= ", type: :uuid" if uuid_enabled? %>
|
|
8
8
|
|
|
9
9
|
t.timestamps
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
create_table :<%= table_name.singularize %>_assignments do |t|
|
|
13
|
-
t.references :<%= user_cname.underscore %>, index: true
|
|
14
|
-
t.references :<%= table_name.singularize %>, index: true
|
|
12
|
+
create_table :<%= table_name.singularize %>_assignments<%= ", id: :uuid" if uuid_enabled? %> do |t|
|
|
13
|
+
t.references :<%= user_cname.underscore %>, index: true<%= ", type: :uuid" if uuid_enabled? %>
|
|
14
|
+
t.references :<%= table_name.singularize %>, index: true<%= ", type: :uuid" if uuid_enabled? %>
|
|
15
15
|
t.datetime :expires_at, index: true
|
|
16
16
|
t.jsonb :meta, default: {}
|
|
17
17
|
|
|
@@ -9,4 +9,11 @@ RoleFu.configure do |config|
|
|
|
9
9
|
|
|
10
10
|
# The class name of your RoleAssignment model
|
|
11
11
|
# config.role_assignment_class_name = "RoleAssignment"
|
|
12
|
+
|
|
13
|
+
# Enable Rolify-style permissive checks (Global roles override resource checks)
|
|
14
|
+
# config.global_roles_override = false
|
|
15
|
+
|
|
16
|
+
# Enable dynamic shortcuts (e.g. user.is_admin?)
|
|
17
|
+
# Default is "is_%{role}?". Set to nil to disable.
|
|
18
|
+
# config.dynamic_shortcuts_pattern = "is_%{role}?"
|
|
12
19
|
end
|
|
@@ -3,12 +3,25 @@
|
|
|
3
3
|
module RoleFu
|
|
4
4
|
class Configuration
|
|
5
5
|
attr_accessor :user_class_name, :role_class_name, :role_assignment_class_name, :global_roles_override
|
|
6
|
+
attr_reader :dynamic_shortcuts_pattern, :dynamic_shortcuts_regex
|
|
6
7
|
|
|
7
8
|
def initialize
|
|
8
9
|
@user_class_name = "User"
|
|
9
10
|
@role_class_name = "Role"
|
|
10
11
|
@role_assignment_class_name = "RoleAssignment"
|
|
11
12
|
@global_roles_override = false
|
|
13
|
+
self.dynamic_shortcuts_pattern = "is_%{role}?"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def dynamic_shortcuts_pattern=(pattern)
|
|
17
|
+
@dynamic_shortcuts_pattern = pattern
|
|
18
|
+
if pattern.nil? || pattern.strip.empty?
|
|
19
|
+
@dynamic_shortcuts_regex = nil
|
|
20
|
+
else
|
|
21
|
+
parts = pattern.split("%{role}", -1)
|
|
22
|
+
regex_string = "^#{parts.map { |part| Regexp.escape(part) }.join("(?<role>.+)")}$"
|
|
23
|
+
@dynamic_shortcuts_regex = Regexp.new(regex_string)
|
|
24
|
+
end
|
|
12
25
|
end
|
|
13
26
|
end
|
|
14
27
|
|
data/lib/role_fu/resourceable.rb
CHANGED
|
@@ -9,6 +9,11 @@ module RoleFu
|
|
|
9
9
|
as: :resource,
|
|
10
10
|
dependent: :destroy,
|
|
11
11
|
class_name: RoleFu.configuration.role_class_name
|
|
12
|
+
|
|
13
|
+
has_many :users,
|
|
14
|
+
through: :roles,
|
|
15
|
+
class_name: RoleFu.configuration.user_class_name,
|
|
16
|
+
source: :users
|
|
12
17
|
end
|
|
13
18
|
|
|
14
19
|
module ClassMethods
|
|
@@ -48,12 +53,15 @@ module RoleFu
|
|
|
48
53
|
alias_method "users_with_any_#{singular}", :users_with_any_role
|
|
49
54
|
alias_method "users_with_all_#{plural}", :users_with_all_roles
|
|
50
55
|
alias_method "users_with_#{plural}", :users_with_roles
|
|
56
|
+
alias_method "users_grouped_by_#{singular}", :users_grouped_by_role
|
|
51
57
|
alias_method "available_#{plural}", :available_roles
|
|
58
|
+
alias_method "applied_#{plural}", :applied_roles
|
|
52
59
|
alias_method "has_#{singular}?", :has_role?
|
|
53
60
|
alias_method "count_users_with_#{singular}", :count_users_with_role
|
|
54
61
|
alias_method "user_has_#{singular}?", :user_has_role?
|
|
55
62
|
alias_method "add_#{singular}_to_user", :add_role_to_user
|
|
56
63
|
alias_method "remove_#{singular}_from_user", :remove_role_from_user
|
|
64
|
+
alias_method "destroy_#{singular}", :destroy_role
|
|
57
65
|
end
|
|
58
66
|
end
|
|
59
67
|
|
|
@@ -61,6 +69,29 @@ module RoleFu
|
|
|
61
69
|
roles
|
|
62
70
|
end
|
|
63
71
|
|
|
72
|
+
def destroy_role(role_name)
|
|
73
|
+
roles.where(name: role_name.to_s).destroy_all
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def users_grouped_by_role
|
|
77
|
+
RoleFu.role_class.table_name
|
|
78
|
+
|
|
79
|
+
# Efficiently load roles and their associated users
|
|
80
|
+
# 1. Get all roles for this resource
|
|
81
|
+
# 2. Preload users for these roles
|
|
82
|
+
|
|
83
|
+
role_scope = roles.includes(:users)
|
|
84
|
+
|
|
85
|
+
results = {}
|
|
86
|
+
role_scope.each do |role|
|
|
87
|
+
results[role.name] ||= []
|
|
88
|
+
results[role.name].concat(role.users)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Deduplicate users if a user has the same role multiple times (unlikely with distinct but safer)
|
|
92
|
+
results.transform_values(&:uniq)
|
|
93
|
+
end
|
|
94
|
+
|
|
64
95
|
def users_with_role(role_name)
|
|
65
96
|
role_table = RoleFu.role_class.table_name
|
|
66
97
|
RoleFu.user_class.joins(:roles)
|
data/lib/role_fu/roleable.rb
CHANGED
|
@@ -209,8 +209,27 @@ module RoleFu
|
|
|
209
209
|
query.distinct
|
|
210
210
|
end
|
|
211
211
|
|
|
212
|
+
def method_missing(method_name, *args, &block)
|
|
213
|
+
if (role_name = parse_dynamic_role_name(method_name))
|
|
214
|
+
has_role?(role_name, *args)
|
|
215
|
+
else
|
|
216
|
+
super
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
221
|
+
parse_dynamic_role_name(method_name) || super
|
|
222
|
+
end
|
|
223
|
+
|
|
212
224
|
private
|
|
213
225
|
|
|
226
|
+
def parse_dynamic_role_name(method_name)
|
|
227
|
+
return nil unless (regex = RoleFu.configuration.dynamic_shortcuts_regex)
|
|
228
|
+
|
|
229
|
+
match = method_name.to_s.match(regex)
|
|
230
|
+
match ? match[:role] : nil
|
|
231
|
+
end
|
|
232
|
+
|
|
214
233
|
def filter_expired(relation)
|
|
215
234
|
return relation unless RoleFu.role_assignment_class.column_names.include?("expires_at")
|
|
216
235
|
|
data/lib/role_fu/version.rb
CHANGED