activeinteractor 2.0.0.alpha.2.1.0 → 2.0.0.alpha.2.3.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/lib/active_interactor/base.rb +41 -38
- data/lib/active_interactor/context/attribute.rb +72 -0
- data/lib/active_interactor/context/attribute_assignment.rb +58 -0
- data/lib/active_interactor/context/attribute_registration.rb +27 -0
- data/lib/active_interactor/context/attribute_set.rb +35 -0
- data/lib/active_interactor/context/base.rb +31 -0
- data/lib/active_interactor/context/input.rb +19 -0
- data/lib/active_interactor/context/output.rb +27 -0
- data/lib/active_interactor/context/runtime.rb +54 -0
- data/lib/active_interactor/context.rb +16 -0
- data/lib/active_interactor/has_active_model_errors.rb +28 -0
- data/lib/active_interactor/result.rb +13 -10
- data/lib/active_interactor/type/base.rb +9 -0
- data/lib/active_interactor/type/boolean.rb +14 -0
- data/lib/active_interactor/type/has_types.rb +34 -0
- data/lib/active_interactor/type/list.rb +29 -0
- data/lib/active_interactor/type/union.rb +27 -0
- data/lib/active_interactor/type.rb +13 -0
- data/lib/active_interactor.rb +3 -0
- metadata +20 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c201330d4b6c13c9b409888776d669e0b1ea73fcae310f4869de55fc0bbcc500
|
4
|
+
data.tar.gz: 62be913b6bee629928ba114f82a7c2a526610cab1d9978459fc8e9f4f70869b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef8f56b5ca0f79d60b9f45b3c8a1e9e394b15c15c89c98e7f747eaecf6621671020b624a0d5ba141d7948468eb83938ecd8bf62849038ad39e2f974a03bfce11
|
7
|
+
data.tar.gz: ed637132df8ef4ae80843e042d780b0423480e74f5d4cd9d12128118c10e3197467cb2e31f49d2d6a62cbadfd9c83ef4abad0beb123959ddb908d2f5f39fadc2
|
@@ -2,9 +2,23 @@
|
|
2
2
|
|
3
3
|
module ActiveInteractor
|
4
4
|
class Base
|
5
|
+
include Type::HasTypes
|
6
|
+
|
5
7
|
class << self
|
6
|
-
|
7
|
-
|
8
|
+
delegate :argument, :argument_names, :arguments, to: :input_context_class
|
9
|
+
delegate :returns, :field_names, :fields, to: :output_context_class
|
10
|
+
|
11
|
+
def input_context_class
|
12
|
+
@input_context_class ||= const_set(:InputContext, Class.new(Context::Input))
|
13
|
+
end
|
14
|
+
|
15
|
+
def accepts_arguments_matching(set_input_context_class)
|
16
|
+
@input_context_class = set_input_context_class
|
17
|
+
end
|
18
|
+
alias input_context accepts_arguments_matching
|
19
|
+
|
20
|
+
def output_context_class
|
21
|
+
@output_context_class ||= const_set(:OutputContext, Class.new(Context::Output))
|
8
22
|
end
|
9
23
|
|
10
24
|
def perform!(input_context = {})
|
@@ -19,26 +33,31 @@ module ActiveInteractor
|
|
19
33
|
Result.failure(errors: e.message)
|
20
34
|
end
|
21
35
|
|
22
|
-
def
|
23
|
-
|
36
|
+
def returns_data_matching(set_output_context_class)
|
37
|
+
@output_context_class = set_output_context_class
|
24
38
|
end
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
39
|
+
alias output_context returns_data_matching
|
40
|
+
|
41
|
+
def runtime_context_class
|
42
|
+
@runtime_context_class ||= begin
|
43
|
+
context_class = const_set(:RuntimeContext, Class.new(Context::Runtime))
|
44
|
+
context_class.send(:attribute_set).merge(input_context_class.send(:attribute_set).attributes)
|
45
|
+
context_class.send(:attribute_set).merge(output_context_class.send(:attribute_set).attributes)
|
46
|
+
context_class
|
47
|
+
end
|
30
48
|
end
|
31
49
|
end
|
32
50
|
|
33
51
|
def initialize(input = {})
|
34
|
-
@
|
35
|
-
|
52
|
+
@raw_input = input.dup
|
53
|
+
validate_input_and_generate_runtime_context!
|
36
54
|
end
|
37
55
|
|
38
56
|
def perform!
|
39
57
|
with_notification(:perform) do |payload|
|
40
58
|
interact
|
41
|
-
|
59
|
+
generate_and_validate_output_context!
|
60
|
+
payload[:result] = Result.success(data: @output)
|
42
61
|
end
|
43
62
|
end
|
44
63
|
|
@@ -68,36 +87,20 @@ module ActiveInteractor
|
|
68
87
|
raise Error, result
|
69
88
|
end
|
70
89
|
|
71
|
-
def
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
hash[name] = @input[name] || options[:default]
|
76
|
-
end
|
77
|
-
|
78
|
-
raise Error, Result.failure(errors: errors, status: Result::STATUS[:failed_on_input]) unless errors.empty?
|
79
|
-
|
80
|
-
context
|
81
|
-
end
|
82
|
-
|
83
|
-
def parse_output!
|
84
|
-
errors = {}
|
85
|
-
context = self.class.send(:attributes)[:fields].each_with_object({}) do |(name, options), hash|
|
86
|
-
errors = validate_attribute_value(self.context[name], name, options, errors)
|
87
|
-
hash[name] = self.context[name] || options[:default]
|
88
|
-
end
|
89
|
-
|
90
|
-
raise Error, Result.failure(errors: errors, status: Result::STATUS[:failed_on_output]) unless errors.empty?
|
90
|
+
def generate_and_validate_output_context!
|
91
|
+
@output = self.class.output_context_class.new(context.attributes)
|
92
|
+
@output.validate!
|
93
|
+
return @output if @output.errors.empty?
|
91
94
|
|
92
|
-
|
95
|
+
raise Error, Result.failure(errors: @output.errors, status: Result::STATUS[:failed_at_output])
|
93
96
|
end
|
94
97
|
|
95
|
-
def
|
96
|
-
|
98
|
+
def validate_input_and_generate_runtime_context!
|
99
|
+
@input = self.class.input_context_class.new(@raw_input)
|
100
|
+
@input.validate!
|
101
|
+
return (@context = self.class.runtime_context_class.new(@raw_input)) if @input.errors.empty?
|
97
102
|
|
98
|
-
errors
|
99
|
-
errors[attribute_name] << :blank
|
100
|
-
errors
|
103
|
+
raise Error, Result.failure(errors: @input.errors, status: Result::STATUS[:failed_at_input])
|
101
104
|
end
|
102
105
|
|
103
106
|
def with_notification(action)
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
module Context
|
5
|
+
class Attribute
|
6
|
+
NO_DEFAULT_VALUE = :__no_default_value__
|
7
|
+
attr_reader :description, :error_messages, :name
|
8
|
+
|
9
|
+
def initialize(owner, name, type, description = nil, **options)
|
10
|
+
@owner = owner
|
11
|
+
@name = name.to_sym
|
12
|
+
@type_expression = type
|
13
|
+
@description = description || options[:description]
|
14
|
+
@options = { required: false, default: NO_DEFAULT_VALUE }.merge(options)
|
15
|
+
@error_messages = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def assign_value(value)
|
19
|
+
@user_provided_value = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def default_value
|
23
|
+
return if @options[:default] == NO_DEFAULT_VALUE
|
24
|
+
|
25
|
+
@options[:default]
|
26
|
+
end
|
27
|
+
|
28
|
+
def required?
|
29
|
+
@options[:required]
|
30
|
+
end
|
31
|
+
|
32
|
+
def type
|
33
|
+
@type_expression
|
34
|
+
end
|
35
|
+
|
36
|
+
def validate!
|
37
|
+
validate_presence! && validate_type!
|
38
|
+
end
|
39
|
+
|
40
|
+
def value
|
41
|
+
@user_provided_value || default_value
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def type_is_a_active_interactor_type?
|
47
|
+
type.is_a?(ActiveInteractor::Type::Base) || type.superclass == ActiveInteractor::Type::Base
|
48
|
+
end
|
49
|
+
|
50
|
+
def validate_presence!
|
51
|
+
return true unless required? && value.nil?
|
52
|
+
|
53
|
+
error_messages << :blank
|
54
|
+
end
|
55
|
+
|
56
|
+
def validate_type!
|
57
|
+
return true if value.nil?
|
58
|
+
return true if %i[any untyped].include?(type)
|
59
|
+
|
60
|
+
if type_is_a_active_interactor_type?
|
61
|
+
return true if type.valid?(value)
|
62
|
+
|
63
|
+
error_messages << :invalid
|
64
|
+
elsif value.is_a?(type)
|
65
|
+
return true
|
66
|
+
end
|
67
|
+
|
68
|
+
error_messages << :invalid
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
module Context
|
5
|
+
module AttributeAssignment
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def [](attribute_name)
|
9
|
+
read_attribute_value(attribute_name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def []=(attribute_name, value)
|
13
|
+
assign_attribute_value(attribute_name, value)
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def assign_attribute_value(attribute_name, value)
|
19
|
+
attribute = attribute_set.find(attribute_name)
|
20
|
+
raise NoMethodError, "unknown attribute '#{attribute_name}' for #{self.class.name}" unless attribute
|
21
|
+
|
22
|
+
attribute.assign_value(value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def assignment_method_missing(method_name, *arguments)
|
26
|
+
if arguments.length != 1
|
27
|
+
raise ArgumentError,
|
28
|
+
"wrong number of arguments (given #{arguments.length}, expected 1)"
|
29
|
+
end
|
30
|
+
|
31
|
+
assign_attribute_value(method_name, arguments.first)
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_missing(method_id, *arguments)
|
35
|
+
return super unless respond_to_missing?(method_id)
|
36
|
+
|
37
|
+
method_name = method_id[/.*(?==\z)/m]
|
38
|
+
return assignment_method_missing(method_name, *arguments) if method_name
|
39
|
+
|
40
|
+
read_attribute_value(method_id)
|
41
|
+
end
|
42
|
+
|
43
|
+
def read_attribute_value(attribute_name)
|
44
|
+
attribute = attribute_set.find(attribute_name)
|
45
|
+
raise NoMethodError, "unknown attribute '#{attribute_name}' for #{self.class.name}" unless attribute
|
46
|
+
|
47
|
+
attribute.value
|
48
|
+
end
|
49
|
+
|
50
|
+
def respond_to_missing?(method_name, _include_private = false)
|
51
|
+
return true if attribute_set.attribute_names.include?(method_name.to_sym)
|
52
|
+
return true if attribute_set.attribute_names.include?(method_name[/.*(?==\z)/m]&.to_sym)
|
53
|
+
|
54
|
+
super
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
module Context
|
5
|
+
module AttributeRegistration
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
protected
|
10
|
+
|
11
|
+
def attribute_set
|
12
|
+
@attribute_set ||= AttributeSet.new(self)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
included do
|
17
|
+
extend ClassMethods
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def attribute_set
|
23
|
+
@attribute_set ||= AttributeSet.new(self, *self.class.send(:attribute_set).attributes.map(&:dup))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
module Context
|
5
|
+
class AttributeSet
|
6
|
+
def initialize(owner, *attributes)
|
7
|
+
@owner = owner
|
8
|
+
@set = {}
|
9
|
+
attributes.each { |attribute| @set[attribute.name] = attribute }
|
10
|
+
end
|
11
|
+
|
12
|
+
def add(*attribute_args)
|
13
|
+
attribute_options = attribute_args.extract_options!
|
14
|
+
attribute = Attribute.new(@owner, *attribute_args, **attribute_options)
|
15
|
+
@set[attribute.name] = attribute
|
16
|
+
end
|
17
|
+
|
18
|
+
def attribute_names
|
19
|
+
@set.keys
|
20
|
+
end
|
21
|
+
|
22
|
+
def attributes
|
23
|
+
@set.values
|
24
|
+
end
|
25
|
+
|
26
|
+
def find(attribute_name)
|
27
|
+
@set[attribute_name.to_sym]
|
28
|
+
end
|
29
|
+
|
30
|
+
def merge(attributes)
|
31
|
+
attributes.each { |attribute| @set[attribute.name] = attribute }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
module Context
|
5
|
+
class Base
|
6
|
+
include ActiveModel::Validations
|
7
|
+
include HasActiveModelErrors
|
8
|
+
include AttributeRegistration
|
9
|
+
include AttributeAssignment
|
10
|
+
include Type::HasTypes
|
11
|
+
|
12
|
+
attr_reader :errors
|
13
|
+
|
14
|
+
def initialize(attributes = {})
|
15
|
+
@errors = ActiveModel::Errors.new(self)
|
16
|
+
attribute_set.attributes.each do |attribute|
|
17
|
+
next unless attributes.with_indifferent_access.key?(attribute.name)
|
18
|
+
|
19
|
+
assign_attribute_value(attribute.name, attributes.with_indifferent_access[attribute.name])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate!
|
24
|
+
attribute_set.attributes.each do |attribute|
|
25
|
+
attribute.validate!
|
26
|
+
attribute.error_messages.each { |message| errors.add(attribute.name, message) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
module Context
|
5
|
+
class Input < Base
|
6
|
+
def self.argument(*attribute_args)
|
7
|
+
attribute_set.add(*attribute_args)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.argument_names
|
11
|
+
attribute_set.attribute_names
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.arguments
|
15
|
+
attribute_set.attributes
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
module Context
|
5
|
+
class Output < Base
|
6
|
+
delegate :to_h, :to_hash, :to_json, to: :fields
|
7
|
+
|
8
|
+
def self.returns(*attribute_args)
|
9
|
+
attribute_set.add(*attribute_args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.field_names
|
13
|
+
attribute_set.attribute_names
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.fields
|
17
|
+
attribute_set.attributes
|
18
|
+
end
|
19
|
+
|
20
|
+
def fields
|
21
|
+
attribute_set.attributes.each_with_object({}) do |attribute, result|
|
22
|
+
result[attribute.name] = attribute.value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
module Context
|
5
|
+
class Runtime < Base
|
6
|
+
def initialize(attributes = {})
|
7
|
+
super
|
8
|
+
@table = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def attributes
|
12
|
+
clean = attribute_set.attributes.each_with_object({}) do |attribute, result|
|
13
|
+
result[attribute.name] = attribute.value
|
14
|
+
end
|
15
|
+
@table.merge(clean)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def assign_attribute_value(attribute_name, value)
|
21
|
+
attribute = attribute_set.find(attribute_name)
|
22
|
+
return super if attribute
|
23
|
+
|
24
|
+
assign_dirty_attribute_value(attribute_name, value)
|
25
|
+
end
|
26
|
+
|
27
|
+
def assign_dirty_attribute_value(attribute_name, value)
|
28
|
+
@table[attribute_name.to_sym] = value
|
29
|
+
end
|
30
|
+
|
31
|
+
def method_missing(method_id, *arguments)
|
32
|
+
method_name = method_id[/.*(?==\z)/m]
|
33
|
+
return assignment_method_missing(method_name, *arguments) if method_name
|
34
|
+
|
35
|
+
read_attribute_value(method_id)
|
36
|
+
end
|
37
|
+
|
38
|
+
def read_attribute_value(attribute_name)
|
39
|
+
attribute = attribute_set.find(attribute_name)
|
40
|
+
return super if attribute
|
41
|
+
|
42
|
+
read_dirty_attribute_value(attribute_name)
|
43
|
+
end
|
44
|
+
|
45
|
+
def read_dirty_attribute_value(attribute_name)
|
46
|
+
@table[attribute_name.to_sym]
|
47
|
+
end
|
48
|
+
|
49
|
+
def respond_to_missing?(_method_name, _include_private = false)
|
50
|
+
true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
module Context
|
5
|
+
extend ActiveSupport::Autoload
|
6
|
+
|
7
|
+
autoload :Attribute
|
8
|
+
autoload :AttributeRegistration
|
9
|
+
autoload :AttributeSet
|
10
|
+
autoload :AttributeAssignment
|
11
|
+
autoload :Base
|
12
|
+
autoload :Input
|
13
|
+
autoload :Output
|
14
|
+
autoload :Runtime
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
module HasActiveModelErrors
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def self.human_attribute_name(attribute, _options = {})
|
9
|
+
attribute.respond_to?(:to_s) ? attribute.to_s.humanize : attribute
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.lookup_ancestors
|
13
|
+
[self]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
included do
|
18
|
+
extend ActiveModel::Naming
|
19
|
+
extend ClassMethods
|
20
|
+
|
21
|
+
attr_reader :errors
|
22
|
+
end
|
23
|
+
|
24
|
+
def read_attribute_for_validation(attribute_name)
|
25
|
+
send(attribute_name.to_sym)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -2,7 +2,9 @@
|
|
2
2
|
|
3
3
|
module ActiveInteractor
|
4
4
|
class Result
|
5
|
-
|
5
|
+
include HasActiveModelErrors
|
6
|
+
|
7
|
+
delegate :to_json, to: :to_hash
|
6
8
|
|
7
9
|
STATUS = {
|
8
10
|
success: 0,
|
@@ -26,14 +28,6 @@ module ActiveInteractor
|
|
26
28
|
result
|
27
29
|
end
|
28
30
|
|
29
|
-
def human_attribute_name(attribute, _options = {})
|
30
|
-
attribute.respond_to?(:to_s) ? attribute.to_s.humanize : attribute
|
31
|
-
end
|
32
|
-
|
33
|
-
def lookup_ancestors
|
34
|
-
[self]
|
35
|
-
end
|
36
|
-
|
37
31
|
private
|
38
32
|
|
39
33
|
def parse_errors(errors)
|
@@ -62,12 +56,21 @@ module ActiveInteractor
|
|
62
56
|
alias failed? failure?
|
63
57
|
|
64
58
|
def read_attribute_for_validation(attribute_name)
|
65
|
-
data
|
59
|
+
data.send(attribute_name.to_sym)
|
66
60
|
end
|
67
61
|
|
68
62
|
def success?
|
69
63
|
@status == STATUS[:success]
|
70
64
|
end
|
71
65
|
alias successful? success?
|
66
|
+
|
67
|
+
def to_hash
|
68
|
+
{
|
69
|
+
success: success?,
|
70
|
+
errors: errors.to_hash,
|
71
|
+
data: data.to_hash
|
72
|
+
}
|
73
|
+
end
|
74
|
+
alias to_h to_hash
|
72
75
|
end
|
73
76
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
module Type
|
5
|
+
class Boolean < Base
|
6
|
+
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON', 'yes', 'YES'].freeze
|
7
|
+
FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF', 'no', 'NO'].freeze
|
8
|
+
|
9
|
+
def self.valid?(value)
|
10
|
+
(TRUE_VALUES + FALSE_VALUES).include?(value)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
module Type
|
5
|
+
module HasTypes
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
Boolean = ActiveInteractor::Type::Boolean
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def any
|
12
|
+
:any
|
13
|
+
end
|
14
|
+
|
15
|
+
def list(type)
|
16
|
+
List.new(type)
|
17
|
+
end
|
18
|
+
alias array list
|
19
|
+
|
20
|
+
def union(*types)
|
21
|
+
Union.new(*types)
|
22
|
+
end
|
23
|
+
|
24
|
+
def untyped
|
25
|
+
:untyped
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
included do
|
30
|
+
extend ClassMethods
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
module Type
|
5
|
+
class List < Base
|
6
|
+
def initialize(type) # rubocop:disable Lint/MissingSuper
|
7
|
+
@type = type
|
8
|
+
end
|
9
|
+
|
10
|
+
def valid?(value)
|
11
|
+
return false unless value.is_a?(Array)
|
12
|
+
|
13
|
+
value.all? do |element|
|
14
|
+
element.nil? || element.is_a?(@type) || (@type.is_a?(ActiveInteractor::Type::Base) && @type.valid?(element))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def value_is_a_valid_active_interactor_type?(_type, value)
|
21
|
+
unless @type.is_a?(ActiveInteractor::Type::Base) || @type.superclass == ActiveInteractor::Type::Base
|
22
|
+
return false
|
23
|
+
end
|
24
|
+
|
25
|
+
@type.valid?(value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
module Type
|
5
|
+
class Union < Base
|
6
|
+
def initialize(*types) # rubocop:disable Lint/MissingSuper
|
7
|
+
@valid_types = types
|
8
|
+
end
|
9
|
+
|
10
|
+
def valid?(value)
|
11
|
+
return true if value.nil?
|
12
|
+
|
13
|
+
@valid_types.any? do |type|
|
14
|
+
value_is_a_valid_active_interactor_type?(type, value) || value.is_a?(type)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def value_is_a_valid_active_interactor_type?(type, value)
|
21
|
+
return false unless type.is_a?(ActiveInteractor::Type::Base) || type.superclass == ActiveInteractor::Type::Base
|
22
|
+
|
23
|
+
type.valid?(value)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/active_interactor.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activeinteractor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.alpha.2.
|
4
|
+
version: 2.0.0.alpha.2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aaron Allen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-10-
|
11
|
+
date: 2023-10-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -64,17 +64,33 @@ files:
|
|
64
64
|
- README.md
|
65
65
|
- lib/active_interactor.rb
|
66
66
|
- lib/active_interactor/base.rb
|
67
|
+
- lib/active_interactor/context.rb
|
68
|
+
- lib/active_interactor/context/attribute.rb
|
69
|
+
- lib/active_interactor/context/attribute_assignment.rb
|
70
|
+
- lib/active_interactor/context/attribute_registration.rb
|
71
|
+
- lib/active_interactor/context/attribute_set.rb
|
72
|
+
- lib/active_interactor/context/base.rb
|
73
|
+
- lib/active_interactor/context/input.rb
|
74
|
+
- lib/active_interactor/context/output.rb
|
75
|
+
- lib/active_interactor/context/runtime.rb
|
67
76
|
- lib/active_interactor/errors.rb
|
77
|
+
- lib/active_interactor/has_active_model_errors.rb
|
68
78
|
- lib/active_interactor/result.rb
|
79
|
+
- lib/active_interactor/type.rb
|
80
|
+
- lib/active_interactor/type/base.rb
|
81
|
+
- lib/active_interactor/type/boolean.rb
|
82
|
+
- lib/active_interactor/type/has_types.rb
|
83
|
+
- lib/active_interactor/type/list.rb
|
84
|
+
- lib/active_interactor/type/union.rb
|
69
85
|
- sig/active_interactor.rbs
|
70
86
|
homepage: https://github.com/activeinteractor/activeinteractor
|
71
87
|
licenses:
|
72
88
|
- MIT
|
73
89
|
metadata:
|
74
90
|
bug_tracker_uri: https://github.com/activeinteractor/activeinteractor/issues
|
75
|
-
changelog_uri: https://github.com/activeinteractor/activeinteractor/blob/v2.0.0-alpha.2.
|
91
|
+
changelog_uri: https://github.com/activeinteractor/activeinteractor/blob/v2.0.0-alpha.2.3.0/CHANGELOG.md
|
76
92
|
homepage_uri: https://github.com/activeinteractor/activeinteractor
|
77
|
-
source_code_uri: https://github.com/activeinteractor/activeinteractor/tree/v2.0.0-alpha.2.
|
93
|
+
source_code_uri: https://github.com/activeinteractor/activeinteractor/tree/v2.0.0-alpha.2.3.0
|
78
94
|
wiki_uri: https://github.com/activeinteractor/activeinteractor/wiki
|
79
95
|
rubygems_mfa_required: 'true'
|
80
96
|
post_install_message:
|