rabarber 5.1.1 → 5.1.2
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 +7 -0
- data/README.md +9 -13
- data/lib/rabarber/configuration.rb +8 -12
- data/lib/rabarber/controllers/concerns/authorization.rb +18 -19
- data/lib/rabarber/core/permissions.rb +1 -1
- data/lib/rabarber/core/rule.rb +13 -10
- data/lib/rabarber/inputs/base.rb +35 -0
- data/lib/rabarber/inputs/boolean.rb +11 -0
- data/lib/rabarber/inputs/context.rb +33 -0
- data/lib/rabarber/inputs/contexts/authorizational.rb +13 -0
- data/lib/rabarber/inputs/dynamic_rule.rb +11 -0
- data/lib/rabarber/inputs/model.rb +11 -0
- data/lib/rabarber/inputs/non_empty_string.rb +11 -0
- data/lib/rabarber/inputs/role.rb +11 -0
- data/lib/rabarber/inputs/roles.rb +13 -0
- data/lib/rabarber/inputs/symbol.rb +11 -0
- data/lib/rabarber/models/concerns/has_roles.rb +5 -6
- data/lib/rabarber/models/role.rb +6 -9
- data/lib/rabarber/version.rb +1 -1
- data/lib/rabarber.rb +12 -2
- data/rabarber.gemspec +2 -2
- metadata +15 -6
- data/lib/rabarber/inputs.rb +0 -62
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bcf3063fc3e0bb700ae4e372cec9807b3796863f8e94e5b2f8e79d9594d50b10
|
4
|
+
data.tar.gz: 82abf396141f483ca0ec10982bb9ef35ea8a1d79f0a1e111f2c8b483a0e480e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 023fddca28a49a89534c1d5c0b716e7f518a3a975e2b6a5883f156f75acd73a28564b475080fd5abdf7ffce294b21c1af07be276141ac747d92b9087b8cbac96
|
7
|
+
data.tar.gz: 7ae10c74e73ee844d22011a6678a8b9af579d2657d3020fe28958912c3b4a1fb033ed0a11c7af126c7367e30fcd54a1058f529b39acf0cac31391f5bc0a76146
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -181,11 +181,6 @@ class TicketsController < ApplicationController
|
|
181
181
|
def index
|
182
182
|
# Accessible to admin, manager, and support roles
|
183
183
|
end
|
184
|
-
|
185
|
-
grant_access action: :destroy, roles: :owner, context: -> { Ticket.find(params[:id]) }
|
186
|
-
def destroy
|
187
|
-
# Accessible to admin and owner of the ticket
|
188
|
-
end
|
189
184
|
end
|
190
185
|
```
|
191
186
|
|
@@ -244,9 +239,7 @@ class ApplicationController < ActionController::Base
|
|
244
239
|
private
|
245
240
|
|
246
241
|
def when_unauthorized
|
247
|
-
#
|
248
|
-
# Custom behavior example:
|
249
|
-
head :not_found # Hide existence of protected resources
|
242
|
+
head :not_found # Custom behavior to hide existence of protected resources
|
250
243
|
end
|
251
244
|
end
|
252
245
|
```
|
@@ -288,7 +281,7 @@ end
|
|
288
281
|
|
289
282
|
## Multi-tenancy / Context
|
290
283
|
|
291
|
-
All Rabarber methods accept a `context` parameter, allowing you to work with roles within specific scopes
|
284
|
+
All Rabarber methods accept a `context` parameter, allowing you to work with roles within specific scopes. By default, context is `nil`, meaning roles are global.
|
292
285
|
|
293
286
|
### Contextual Role Assignment
|
294
287
|
|
@@ -304,6 +297,9 @@ user.assign_roles(:admin, context: Project)
|
|
304
297
|
user.has_role?(:owner, context: project)
|
305
298
|
user.has_role?(:admin, context: Project)
|
306
299
|
|
300
|
+
# Revoke roles within a specific context
|
301
|
+
user.revoke_roles(:owner, context: project)
|
302
|
+
|
307
303
|
# Get roles within context
|
308
304
|
user.roles(context: project)
|
309
305
|
Rabarber::Role.names(context: Project)
|
@@ -341,11 +337,11 @@ end
|
|
341
337
|
Handle context changes when models are renamed or removed. These are irreversible data migrations.
|
342
338
|
|
343
339
|
```rb
|
344
|
-
# Rename a context class (e.g., when you rename your
|
345
|
-
migrate_authorization_context!("
|
340
|
+
# Rename a context class (e.g., when you rename your Ticket model to Task)
|
341
|
+
migrate_authorization_context!("Ticket", "Task")
|
346
342
|
|
347
|
-
# Remove orphaned context data (e.g., when you delete
|
348
|
-
delete_authorization_context!("
|
343
|
+
# Remove orphaned context data (e.g., when you delete the Ticket model entirely)
|
344
|
+
delete_authorization_context!("Ticket")
|
349
345
|
```
|
350
346
|
|
351
347
|
## View Helpers
|
@@ -12,43 +12,39 @@ module Rabarber
|
|
12
12
|
default: true,
|
13
13
|
reader: true,
|
14
14
|
constructor: -> (value) do
|
15
|
-
Rabarber::Inputs.
|
15
|
+
Rabarber::Inputs::Boolean.new(
|
16
16
|
value,
|
17
|
-
as: :boolean,
|
18
17
|
error: Rabarber::ConfigurationError,
|
19
18
|
message: "Invalid configuration `cache_enabled`, expected a boolean, got #{value.inspect}"
|
20
|
-
)
|
19
|
+
).process
|
21
20
|
end
|
22
21
|
setting :current_user_method,
|
23
22
|
default: :current_user,
|
24
23
|
reader: true,
|
25
24
|
constructor: -> (value) do
|
26
|
-
Rabarber::Inputs.
|
25
|
+
Rabarber::Inputs::Symbol.new(
|
27
26
|
value,
|
28
|
-
as: :symbol,
|
29
27
|
error: Rabarber::ConfigurationError,
|
30
28
|
message: "Invalid configuration `current_user_method`, expected a symbol or a string, got #{value.inspect}"
|
31
|
-
)
|
29
|
+
).process
|
32
30
|
end
|
33
31
|
setting :user_model_name,
|
34
32
|
default: "User",
|
35
33
|
reader: true,
|
36
34
|
constructor: -> (value) do
|
37
|
-
Rabarber::Inputs.
|
35
|
+
Rabarber::Inputs::NonEmptyString.new(
|
38
36
|
value,
|
39
|
-
as: :string,
|
40
37
|
error: Rabarber::ConfigurationError,
|
41
38
|
message: "Invalid configuration `user_model_name`, expected an ActiveRecord model name, got #{value.inspect}"
|
42
|
-
)
|
39
|
+
).process
|
43
40
|
end
|
44
41
|
|
45
42
|
def user_model
|
46
|
-
Rabarber::Inputs.
|
43
|
+
Rabarber::Inputs::Model.new(
|
47
44
|
user_model_name,
|
48
|
-
as: :model,
|
49
45
|
error: Rabarber::ConfigurationError,
|
50
46
|
message: "Invalid configuration `user_model_name`, expected an ActiveRecord model name, got #{user_model_name.inspect}"
|
51
|
-
)
|
47
|
+
).process
|
52
48
|
end
|
53
49
|
end
|
54
50
|
end
|
@@ -19,36 +19,35 @@ module Rabarber
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def grant_access(action: nil, roles: nil, context: nil, if: nil, unless: nil)
|
22
|
+
if_rule = binding.local_variable_get(:if)
|
23
|
+
unless_rule = binding.local_variable_get(:unless)
|
24
|
+
|
22
25
|
Rabarber::Core::Permissions.add(
|
23
26
|
self,
|
24
|
-
Rabarber::Inputs.
|
27
|
+
Rabarber::Inputs::Symbol.new(
|
25
28
|
action,
|
26
|
-
as: :symbol,
|
27
29
|
optional: true,
|
28
30
|
message: "Expected a symbol or a string, got #{action.inspect}"
|
29
|
-
),
|
30
|
-
Rabarber::Inputs.
|
31
|
+
).process,
|
32
|
+
Rabarber::Inputs::Roles.new(
|
31
33
|
roles,
|
32
|
-
as: :roles,
|
33
34
|
message: "Expected an array of symbols or strings containing only lowercase letters, numbers, and underscores, got #{roles.inspect}"
|
34
|
-
),
|
35
|
-
Rabarber::Inputs.
|
35
|
+
).process,
|
36
|
+
Rabarber::Inputs::Contexts::Authorizational.new(
|
36
37
|
context,
|
37
|
-
|
38
|
+
error: Rabarber::InvalidContextError,
|
38
39
|
message: "Expected a Class, an instance of ActiveRecord model, a symbol, a string, or a proc, got #{context.inspect}"
|
39
|
-
),
|
40
|
-
Rabarber::Inputs.
|
41
|
-
|
42
|
-
as: :dynamic_rule,
|
40
|
+
).resolve,
|
41
|
+
Rabarber::Inputs::DynamicRule.new(
|
42
|
+
if_rule,
|
43
43
|
optional: true,
|
44
|
-
message: "Expected a symbol, a string, or a proc, got #{
|
45
|
-
),
|
46
|
-
Rabarber::Inputs.
|
47
|
-
|
48
|
-
as: :dynamic_rule,
|
44
|
+
message: "Expected a symbol, a string, or a proc, got #{if_rule.inspect}"
|
45
|
+
).process,
|
46
|
+
Rabarber::Inputs::DynamicRule.new(
|
47
|
+
unless_rule,
|
49
48
|
optional: true,
|
50
|
-
message: "Expected a symbol, a string, or a proc, got #{
|
51
|
-
)
|
49
|
+
message: "Expected a symbol, a string, or a proc, got #{unless_rule.inspect}"
|
50
|
+
).process
|
52
51
|
)
|
53
52
|
end
|
54
53
|
end
|
@@ -22,7 +22,7 @@ module Rabarber
|
|
22
22
|
class << self
|
23
23
|
def add(controller, action, roles, context, dynamic_rule, negated_dynamic_rule)
|
24
24
|
rule = Rabarber::Core::Rule.new(roles, context, dynamic_rule, negated_dynamic_rule)
|
25
|
-
action ? action_rules[controller][action]
|
25
|
+
action ? action_rules[controller][action] << rule : controller_rules[controller] << rule
|
26
26
|
end
|
27
27
|
|
28
28
|
def controller_rules
|
data/lib/rabarber/core/rule.rb
CHANGED
@@ -3,8 +3,6 @@
|
|
3
3
|
module Rabarber
|
4
4
|
module Core
|
5
5
|
class Rule
|
6
|
-
attr_reader :roles, :context, :dynamic_rule, :negated_dynamic_rule
|
7
|
-
|
8
6
|
DEFAULT_DYNAMIC_RULE = -> { true }.freeze
|
9
7
|
DEFAULT_NEGATED_DYNAMIC_RULE = -> { false }.freeze
|
10
8
|
private_constant :DEFAULT_DYNAMIC_RULE, :DEFAULT_NEGATED_DYNAMIC_RULE
|
@@ -21,11 +19,11 @@ module Rabarber
|
|
21
19
|
end
|
22
20
|
|
23
21
|
def roles_permitted?(roleable, controller_instance)
|
24
|
-
roles.empty? || roleable.has_role?(
|
22
|
+
@roles.empty? || roleable.has_role?(*@roles, context: resolve_context(controller_instance))
|
25
23
|
end
|
26
24
|
|
27
25
|
def dynamic_rules_followed?(controller_instance)
|
28
|
-
execute_rule(controller_instance, dynamic_rule) && !execute_rule(controller_instance, negated_dynamic_rule)
|
26
|
+
execute_rule(controller_instance, @dynamic_rule) && !execute_rule(controller_instance, @negated_dynamic_rule)
|
29
27
|
end
|
30
28
|
|
31
29
|
private
|
@@ -35,12 +33,17 @@ module Rabarber
|
|
35
33
|
end
|
36
34
|
|
37
35
|
def resolve_context(controller_instance)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
36
|
+
context = case @context
|
37
|
+
when Proc then controller_instance.instance_exec(&@context)
|
38
|
+
when Symbol then controller_instance.send(@context)
|
39
|
+
else @context
|
40
|
+
end
|
41
|
+
|
42
|
+
Rabarber::Inputs::Context.new(
|
43
|
+
context,
|
44
|
+
error: Rabarber::InvalidContextError,
|
45
|
+
message: "Expected an instance of ActiveRecord model, a Class, or nil, got #{context.inspect}"
|
46
|
+
).resolve
|
44
47
|
end
|
45
48
|
end
|
46
49
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry-types"
|
4
|
+
|
5
|
+
module Rabarber
|
6
|
+
module Inputs
|
7
|
+
class Base
|
8
|
+
include Dry.Types()
|
9
|
+
|
10
|
+
def initialize(value, optional: false, error: Rabarber::InvalidArgumentError, message: nil)
|
11
|
+
@value = value
|
12
|
+
@optional = optional
|
13
|
+
@error = error
|
14
|
+
@message = message
|
15
|
+
end
|
16
|
+
|
17
|
+
def process
|
18
|
+
type_checker = @optional ? type.optional : type
|
19
|
+
type_checker[@value]
|
20
|
+
rescue Dry::Types::CoercionError
|
21
|
+
raise_error
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def type
|
27
|
+
raise NotImplementedError
|
28
|
+
end
|
29
|
+
|
30
|
+
def raise_error
|
31
|
+
raise @error, @message
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rabarber
|
4
|
+
module Inputs
|
5
|
+
class Context < Base
|
6
|
+
def resolve
|
7
|
+
case context = process
|
8
|
+
when nil
|
9
|
+
{ context_type: nil, context_id: nil }
|
10
|
+
when Class
|
11
|
+
{ context_type: context.to_s, context_id: nil }
|
12
|
+
when ActiveRecord::Base
|
13
|
+
raise_error unless context.persisted?
|
14
|
+
{ context_type: context.class.to_s, context_id: context.public_send(context.class.primary_key) }
|
15
|
+
else
|
16
|
+
context
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def type
|
23
|
+
self.class::Strict::Class |
|
24
|
+
self.class::Instance(ActiveRecord::Base) |
|
25
|
+
self.class::Hash.schema(
|
26
|
+
context_type: self.class::Strict::String | self.class::Nil,
|
27
|
+
context_id: self.class::Strict::String | self.class::Strict::Integer | self.class::Nil
|
28
|
+
) |
|
29
|
+
self.class::Nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rabarber
|
4
|
+
module Inputs
|
5
|
+
module Contexts
|
6
|
+
class Authorizational < Context
|
7
|
+
private
|
8
|
+
|
9
|
+
def type = self.class::Coercible::Symbol.constrained(min_size: 1) | self.class::Instance(Proc) | super
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rabarber
|
4
|
+
module Inputs
|
5
|
+
class Roles < Base
|
6
|
+
private
|
7
|
+
|
8
|
+
def type
|
9
|
+
self.class::Array.of(self.class::Coercible::Symbol.constrained(min_size: 1, format: /\A[a-z0-9_]+\z/)).constructor { Kernel::Array(_1) }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -81,19 +81,18 @@ module Rabarber
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def process_role_names(role_names)
|
84
|
-
Rabarber::Inputs.
|
84
|
+
Rabarber::Inputs::Roles.new(
|
85
85
|
role_names,
|
86
|
-
as: :roles,
|
87
86
|
message: "Expected an array of symbols or strings containing only lowercase letters, numbers, and underscores, got #{role_names.inspect}"
|
88
|
-
)
|
87
|
+
).process
|
89
88
|
end
|
90
89
|
|
91
90
|
def process_context(context)
|
92
|
-
Rabarber::Inputs.
|
91
|
+
Rabarber::Inputs::Context.new(
|
93
92
|
context,
|
94
|
-
|
93
|
+
error: Rabarber::InvalidContextError,
|
95
94
|
message: "Expected an instance of ActiveRecord model, a Class, or nil, got #{context.inspect}"
|
96
|
-
)
|
95
|
+
).resolve
|
97
96
|
end
|
98
97
|
|
99
98
|
def delete_roleable_cache(contexts:)
|
data/lib/rabarber/models/role.rb
CHANGED
@@ -69,25 +69,22 @@ module Rabarber
|
|
69
69
|
private
|
70
70
|
|
71
71
|
def delete_roleables_cache(role, context:)
|
72
|
-
role.roleables.
|
73
|
-
Rabarber::Core::Cache.delete(*batch.pluck(:id).flat_map { [[_1, context], [_1, :all]] })
|
74
|
-
end
|
72
|
+
Rabarber::Core::Cache.delete(*role.roleables.pluck(:id).flat_map { [[_1, context], [_1, :all]] })
|
75
73
|
end
|
76
74
|
|
77
75
|
def process_role_name(name)
|
78
|
-
Rabarber::Inputs.
|
76
|
+
Rabarber::Inputs::Role.new(
|
79
77
|
name,
|
80
|
-
as: :role,
|
81
78
|
message: "Expected a symbol or a string containing only lowercase letters, numbers, and underscores, got #{name.inspect}"
|
82
|
-
)
|
79
|
+
).process
|
83
80
|
end
|
84
81
|
|
85
82
|
def process_context(context)
|
86
|
-
Rabarber::Inputs.
|
83
|
+
Rabarber::Inputs::Context.new(
|
87
84
|
context,
|
88
|
-
|
85
|
+
error: Rabarber::InvalidContextError,
|
89
86
|
message: "Expected an instance of ActiveRecord model, a Class, or nil, got #{context.inspect}"
|
90
|
-
)
|
87
|
+
).resolve
|
91
88
|
end
|
92
89
|
end
|
93
90
|
|
data/lib/rabarber/version.rb
CHANGED
data/lib/rabarber.rb
CHANGED
@@ -5,14 +5,24 @@ require_relative "rabarber/version"
|
|
5
5
|
require "active_record"
|
6
6
|
require "active_support"
|
7
7
|
|
8
|
-
require_relative "rabarber/inputs"
|
8
|
+
require_relative "rabarber/inputs/base"
|
9
|
+
require_relative "rabarber/inputs/boolean"
|
10
|
+
require_relative "rabarber/inputs/context"
|
11
|
+
require_relative "rabarber/inputs/contexts/authorizational"
|
12
|
+
require_relative "rabarber/inputs/dynamic_rule"
|
13
|
+
require_relative "rabarber/inputs/model"
|
14
|
+
require_relative "rabarber/inputs/non_empty_string"
|
15
|
+
require_relative "rabarber/inputs/role"
|
16
|
+
require_relative "rabarber/inputs/roles"
|
17
|
+
require_relative "rabarber/inputs/symbol"
|
9
18
|
|
10
19
|
require_relative "rabarber/configuration"
|
11
20
|
|
12
21
|
module Rabarber
|
13
22
|
class Error < StandardError; end
|
14
|
-
class ConfigurationError < Rabarber::Error; end
|
15
23
|
class InvalidArgumentError < Rabarber::Error; end
|
24
|
+
class ConfigurationError < Rabarber::InvalidArgumentError; end
|
25
|
+
class InvalidContextError < Rabarber::InvalidArgumentError; end
|
16
26
|
class NotFoundError < Rabarber::Error; end
|
17
27
|
|
18
28
|
delegate :configure, to: Rabarber::Configuration
|
data/rabarber.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
|
-
spec.add_dependency "dry-configurable", "~> 1.
|
25
|
-
spec.add_dependency "dry-types", "~> 1.
|
24
|
+
spec.add_dependency "dry-configurable", "~> 1.1"
|
25
|
+
spec.add_dependency "dry-types", "~> 1.7"
|
26
26
|
spec.add_dependency "rails", ">= 7.1", "< 8.1"
|
27
27
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rabarber
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.1.
|
4
|
+
version: 5.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- enjaku4
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.1'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: dry-types
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1.
|
33
|
+
version: '1.7'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '1.
|
40
|
+
version: '1.7'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rails
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -78,7 +78,16 @@ files:
|
|
78
78
|
- lib/rabarber/core/rule.rb
|
79
79
|
- lib/rabarber/helpers/helpers.rb
|
80
80
|
- lib/rabarber/helpers/migration_helpers.rb
|
81
|
-
- lib/rabarber/inputs.rb
|
81
|
+
- lib/rabarber/inputs/base.rb
|
82
|
+
- lib/rabarber/inputs/boolean.rb
|
83
|
+
- lib/rabarber/inputs/context.rb
|
84
|
+
- lib/rabarber/inputs/contexts/authorizational.rb
|
85
|
+
- lib/rabarber/inputs/dynamic_rule.rb
|
86
|
+
- lib/rabarber/inputs/model.rb
|
87
|
+
- lib/rabarber/inputs/non_empty_string.rb
|
88
|
+
- lib/rabarber/inputs/role.rb
|
89
|
+
- lib/rabarber/inputs/roles.rb
|
90
|
+
- lib/rabarber/inputs/symbol.rb
|
82
91
|
- lib/rabarber/models/concerns/has_roles.rb
|
83
92
|
- lib/rabarber/models/role.rb
|
84
93
|
- lib/rabarber/railtie.rb
|
data/lib/rabarber/inputs.rb
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "dry-types"
|
4
|
-
|
5
|
-
module Rabarber
|
6
|
-
module Inputs
|
7
|
-
extend self
|
8
|
-
|
9
|
-
include Dry.Types()
|
10
|
-
|
11
|
-
PROC_TYPE = self::Instance(Proc)
|
12
|
-
SYMBOL_TYPE = self::Coercible::Symbol.constrained(min_size: 1)
|
13
|
-
ROLE_TYPE = self::SYMBOL_TYPE.constrained(format: /\A[a-z0-9_]+\z/)
|
14
|
-
|
15
|
-
CONTEXT_TYPE = self::Strict::Class | self::Instance(ActiveRecord::Base) | self::Hash.schema(
|
16
|
-
context_type: self::Strict::String | self::Nil,
|
17
|
-
context_id: self::Strict::String | self::Strict::Integer | self::Nil
|
18
|
-
) | self::Nil
|
19
|
-
|
20
|
-
TYPES = {
|
21
|
-
boolean: self::Strict::Bool,
|
22
|
-
string: self::Strict::String.constrained(min_size: 1).constructor { _1.is_a?(::String) ? _1.strip : _1 },
|
23
|
-
symbol: self::SYMBOL_TYPE,
|
24
|
-
role: self::ROLE_TYPE,
|
25
|
-
roles: self::Array.of(self::ROLE_TYPE).constructor { Kernel::Array(_1) },
|
26
|
-
model: self::Strict::Class.constructor { _1.try(:safe_constantize) }.constrained(lt: ActiveRecord::Base),
|
27
|
-
dynamic_rule: self::SYMBOL_TYPE | self::PROC_TYPE,
|
28
|
-
role_context: self::CONTEXT_TYPE,
|
29
|
-
authorization_context: self::SYMBOL_TYPE | self::PROC_TYPE | self::CONTEXT_TYPE
|
30
|
-
}.freeze
|
31
|
-
|
32
|
-
def process(value, as:, optional: false, error: Rabarber::InvalidArgumentError, message: nil)
|
33
|
-
checker = type_for(as)
|
34
|
-
checker = checker.optional if optional
|
35
|
-
|
36
|
-
result = checker[value]
|
37
|
-
|
38
|
-
[:role_context, :authorization_context].include?(as) ? resolve_context(result) : result
|
39
|
-
rescue Dry::Types::CoercionError => e
|
40
|
-
raise error, message || e.message
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def type_for(name) = self::TYPES.fetch(name)
|
46
|
-
|
47
|
-
def resolve_context(value)
|
48
|
-
case value
|
49
|
-
when nil
|
50
|
-
{ context_type: nil, context_id: nil }
|
51
|
-
when Class
|
52
|
-
{ context_type: value.to_s, context_id: nil }
|
53
|
-
when ActiveRecord::Base
|
54
|
-
raise Dry::Types::CoercionError, "instance context not persisted" unless value.persisted?
|
55
|
-
|
56
|
-
{ context_type: value.class.to_s, context_id: value.public_send(value.class.primary_key) }
|
57
|
-
else
|
58
|
-
value
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|