activeinteractor 2.0.0.alpha.2.1.0 → 2.0.0.alpha.2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|