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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 841b259e40b7e446647ee02fcca64cd183c64978c194edb6917ba344d9f12e92
4
- data.tar.gz: 044c1aa8248f7b1b16f1c78648a0b4cafb0451f31d8fed5b72f6d3343a645c48
3
+ metadata.gz: bcf3063fc3e0bb700ae4e372cec9807b3796863f8e94e5b2f8e79d9594d50b10
4
+ data.tar.gz: 82abf396141f483ca0ec10982bb9ef35ea8a1d79f0a1e111f2c8b483a0e480e5
5
5
  SHA512:
6
- metadata.gz: a0880247a0c7f1af21dbb8099880d24ec200295e54f976855aac577fba55b692fee41a7ddfaf10f83848dfba20e2c0ac8289eb7bc94df21cabceb04da47925c1
7
- data.tar.gz: 024e3093a753d53f158532c829ca245b159448d19901d884b4c09518e70b81d39d710ee0867339ca064bc0519fd06ef53a7af507149d29e80a2a2216ad92a096
6
+ metadata.gz: 023fddca28a49a89534c1d5c0b716e7f518a3a975e2b6a5883f156f75acd73a28564b475080fd5abdf7ffce294b21c1af07be276141ac747d92b9087b8cbac96
7
+ data.tar.gz: 7ae10c74e73ee844d22011a6678a8b9af579d2657d3020fe28958912c3b4a1fb033ed0a11c7af126c7367e30fcd54a1058f529b39acf0cac31391f5bc0a76146
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## v5.1.2
2
+
3
+ ### Misc:
4
+
5
+ - Minor performance improvements and code cleanup
6
+ - Relaxed dependency versions
7
+
1
8
  ## v5.1.1
2
9
 
3
10
  ### Misc:
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
- # Default behavior: redirect back (HTML) or return 403 (other formats)
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 rather than globally.
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 Project model to Campaign)
345
- migrate_authorization_context!("Project", "Campaign")
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 a model entirely)
348
- delete_authorization_context!("DeletedModel")
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.process(
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.process(
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.process(
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.process(
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.process(
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.process(
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.process(
35
+ ).process,
36
+ Rabarber::Inputs::Contexts::Authorizational.new(
36
37
  context,
37
- as: :authorization_context,
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.process(
41
- binding.local_variable_get(:if),
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 #{binding.local_variable_get(:if).inspect}"
45
- ),
46
- Rabarber::Inputs.process(
47
- binding.local_variable_get(:unless),
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 #{binding.local_variable_get(:unless).inspect}"
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] += [rule] : controller_rules[controller] += [rule]
25
+ action ? action_rules[controller][action] << rule : controller_rules[controller] << rule
26
26
  end
27
27
 
28
28
  def controller_rules
@@ -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?(*roles, context: resolve_context(controller_instance))
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
- resolved_context = case context
39
- when Proc then controller_instance.instance_exec(&context)
40
- when Symbol then controller_instance.send(context)
41
- else context
42
- end
43
- Rabarber::Inputs.process(resolved_context, as: :role_context, message: "Expected an instance of ActiveRecord model, a Class, or nil, got #{resolved_context.inspect}")
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,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rabarber
4
+ module Inputs
5
+ class Boolean < Base
6
+ private
7
+
8
+ def type = self.class::Strict::Bool
9
+ end
10
+ end
11
+ 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,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rabarber
4
+ module Inputs
5
+ class DynamicRule < Base
6
+ private
7
+
8
+ def type = self.class::Coercible::Symbol.constrained(min_size: 1) | self.class::Instance(Proc)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rabarber
4
+ module Inputs
5
+ class Model < Base
6
+ private
7
+
8
+ def type = self.class::Strict::Class.constructor { _1.try(:safe_constantize) }.constrained(lt: ActiveRecord::Base)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rabarber
4
+ module Inputs
5
+ class NonEmptyString < Base
6
+ private
7
+
8
+ def type = self.class::Strict::String.constrained(min_size: 1)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rabarber
4
+ module Inputs
5
+ class Role < Base
6
+ private
7
+
8
+ def type = self.class::Coercible::Symbol.constrained(min_size: 1, format: /\A[a-z0-9_]+\z/)
9
+ end
10
+ end
11
+ 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
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rabarber
4
+ module Inputs
5
+ class Symbol < Base
6
+ private
7
+
8
+ def type = self.class::Coercible::Symbol.constrained(min_size: 1)
9
+ end
10
+ end
11
+ end
@@ -81,19 +81,18 @@ module Rabarber
81
81
  end
82
82
 
83
83
  def process_role_names(role_names)
84
- Rabarber::Inputs.process(
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.process(
91
+ Rabarber::Inputs::Context.new(
93
92
  context,
94
- as: :role_context,
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:)
@@ -69,25 +69,22 @@ module Rabarber
69
69
  private
70
70
 
71
71
  def delete_roleables_cache(role, context:)
72
- role.roleables.in_batches(of: 1000) do |batch|
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.process(
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.process(
83
+ Rabarber::Inputs::Context.new(
87
84
  context,
88
- as: :role_context,
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
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rabarber
4
- VERSION = "5.1.1"
4
+ VERSION = "5.1.2"
5
5
  end
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.3"
25
- spec.add_dependency "dry-types", "~> 1.8"
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.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.3'
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.3'
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.8'
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.8'
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
@@ -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