activeinteractor 2.0.0.alpha.2.1.0 → 2.0.0.alpha.2.2.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 +39 -38
- data/lib/active_interactor/context/attribute.rb +47 -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 +30 -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.rb +2 -0
- metadata +14 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e288a5c77ab45d9b0009db2a3d1a2997bd577207bb20f16386c8aa29be50f454
|
4
|
+
data.tar.gz: 6e045fd1af8f93d2f22aeed4d96ddb44ac93f83cce65ea9237da3e2de3d167db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e22e0efcf6e53c4c70cf1b30e7871b17de7ebf13933779b989b40119ce3628eab3ade0228f1f3bbf96e7976440661cb0d4b8ec8d09f7af64d56074bf15e47ed1
|
7
|
+
data.tar.gz: 7cef976acbe9bfd3ef6a9e7d24237587d4a51220b5c08b8595cdfa4522ae6e84aba8903c6ab5fadb97d1529529fb216a00eab92adc171cc3c997eb0f05c7cdda
|
@@ -3,8 +3,20 @@
|
|
3
3
|
module ActiveInteractor
|
4
4
|
class Base
|
5
5
|
class << self
|
6
|
-
|
7
|
-
|
6
|
+
delegate :argument, :argument_names, :arguments, to: :input_context_class
|
7
|
+
delegate :returns, :field_names, :fields, to: :output_context_class
|
8
|
+
|
9
|
+
def input_context_class
|
10
|
+
@input_context_class ||= const_set(:InputContext, Class.new(Context::Input))
|
11
|
+
end
|
12
|
+
|
13
|
+
def accepts_arguments_matching(set_input_context_class)
|
14
|
+
@input_context_class = set_input_context_class
|
15
|
+
end
|
16
|
+
alias input_context accepts_arguments_matching
|
17
|
+
|
18
|
+
def output_context_class
|
19
|
+
@output_context_class ||= const_set(:OutputContext, Class.new(Context::Output))
|
8
20
|
end
|
9
21
|
|
10
22
|
def perform!(input_context = {})
|
@@ -19,26 +31,31 @@ module ActiveInteractor
|
|
19
31
|
Result.failure(errors: e.message)
|
20
32
|
end
|
21
33
|
|
22
|
-
def
|
23
|
-
|
34
|
+
def returns_data_matching(set_output_context_class)
|
35
|
+
@output_context_class = set_output_context_class
|
24
36
|
end
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
37
|
+
alias output_context returns_data_matching
|
38
|
+
|
39
|
+
def runtime_context_class
|
40
|
+
@runtime_context_class ||= begin
|
41
|
+
context_class = const_set(:RuntimeContext, Class.new(Context::Runtime))
|
42
|
+
context_class.send(:attribute_set).merge(input_context_class.send(:attribute_set).attributes)
|
43
|
+
context_class.send(:attribute_set).merge(output_context_class.send(:attribute_set).attributes)
|
44
|
+
context_class
|
45
|
+
end
|
30
46
|
end
|
31
47
|
end
|
32
48
|
|
33
49
|
def initialize(input = {})
|
34
|
-
@
|
35
|
-
|
50
|
+
@raw_input = input.dup
|
51
|
+
validate_input_and_generate_runtime_context!
|
36
52
|
end
|
37
53
|
|
38
54
|
def perform!
|
39
55
|
with_notification(:perform) do |payload|
|
40
56
|
interact
|
41
|
-
|
57
|
+
generate_and_validate_output_context!
|
58
|
+
payload[:result] = Result.success(data: @output)
|
42
59
|
end
|
43
60
|
end
|
44
61
|
|
@@ -68,36 +85,20 @@ module ActiveInteractor
|
|
68
85
|
raise Error, result
|
69
86
|
end
|
70
87
|
|
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?
|
88
|
+
def generate_and_validate_output_context!
|
89
|
+
@output = self.class.output_context_class.new(context.attributes)
|
90
|
+
@output.validate!
|
91
|
+
return @output if @output.errors.empty?
|
91
92
|
|
92
|
-
|
93
|
+
raise Error, Result.failure(errors: @output.errors, status: Result::STATUS[:failed_at_output])
|
93
94
|
end
|
94
95
|
|
95
|
-
def
|
96
|
-
|
96
|
+
def validate_input_and_generate_runtime_context!
|
97
|
+
@input = self.class.input_context_class.new(@raw_input)
|
98
|
+
@input.validate!
|
99
|
+
return (@context = self.class.runtime_context_class.new(@raw_input)) if @input.errors.empty?
|
97
100
|
|
98
|
-
errors
|
99
|
-
errors[attribute_name] << :blank
|
100
|
-
errors
|
101
|
+
raise Error, Result.failure(errors: @input.errors, status: Result::STATUS[:failed_at_input])
|
101
102
|
end
|
102
103
|
|
103
104
|
def with_notification(action)
|
@@ -0,0 +1,47 @@
|
|
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
|
+
return true unless required? && value.nil?
|
38
|
+
|
39
|
+
error_messages << :blank
|
40
|
+
end
|
41
|
+
|
42
|
+
def value
|
43
|
+
@user_provided_value || default_value
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
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,30 @@
|
|
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
|
+
|
11
|
+
attr_reader :errors
|
12
|
+
|
13
|
+
def initialize(attributes = {})
|
14
|
+
@errors = ActiveModel::Errors.new(self)
|
15
|
+
attribute_set.attributes.each do |attribute|
|
16
|
+
next unless attributes.with_indifferent_access.key?(attribute.name)
|
17
|
+
|
18
|
+
assign_attribute_value(attribute.name, attributes.with_indifferent_access[attribute.name])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def validate!
|
23
|
+
attribute_set.attributes.each do |attribute|
|
24
|
+
attribute.validate!
|
25
|
+
attribute.error_messages.each { |message| errors.add(attribute.name, message) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
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
|
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.2.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,7 +64,17 @@ 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
|
69
79
|
- sig/active_interactor.rbs
|
70
80
|
homepage: https://github.com/activeinteractor/activeinteractor
|
@@ -72,9 +82,9 @@ licenses:
|
|
72
82
|
- MIT
|
73
83
|
metadata:
|
74
84
|
bug_tracker_uri: https://github.com/activeinteractor/activeinteractor/issues
|
75
|
-
changelog_uri: https://github.com/activeinteractor/activeinteractor/blob/v2.0.0-alpha.2.
|
85
|
+
changelog_uri: https://github.com/activeinteractor/activeinteractor/blob/v2.0.0-alpha.2.2.0/CHANGELOG.md
|
76
86
|
homepage_uri: https://github.com/activeinteractor/activeinteractor
|
77
|
-
source_code_uri: https://github.com/activeinteractor/activeinteractor/tree/v2.0.0-alpha.2.
|
87
|
+
source_code_uri: https://github.com/activeinteractor/activeinteractor/tree/v2.0.0-alpha.2.2.0
|
78
88
|
wiki_uri: https://github.com/activeinteractor/activeinteractor/wiki
|
79
89
|
rubygems_mfa_required: 'true'
|
80
90
|
post_install_message:
|