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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8353806528fdb9ad9625f469d16d400929d9ef3cae024490c85982354f4d9bad
4
- data.tar.gz: af9cfad32cffe177d1b30fa7fb482f3305a6757152090d82482aef39f448a628
3
+ metadata.gz: c201330d4b6c13c9b409888776d669e0b1ea73fcae310f4869de55fc0bbcc500
4
+ data.tar.gz: 62be913b6bee629928ba114f82a7c2a526610cab1d9978459fc8e9f4f70869b3
5
5
  SHA512:
6
- metadata.gz: 45a07cd05ee5ad9e27fb23f2ebe1a99c5bc01cdeeda5987e35a501b22230ecb56ea49ccc36c233348a838a6c25cd2fa8b6945326bef13839d1f4541a0d3ac043
7
- data.tar.gz: c11cbf40fa73910ce12a43a60db15f13983ec6174006627eae29eecd068002be2fcbd50b7062d4cd0d955736594a246611f66c570db59b152e889053c9b5128d
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
- def argument(name, type, description = nil, **options)
7
- attributes[:arguments][name.to_sym] = { name: name, type: type, description: description }.merge(options)
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 returns(name, type, description = nil, **options)
23
- attributes[:fields][name.to_sym] = { name: name, type: type, description: description }.merge(options)
36
+ def returns_data_matching(set_output_context_class)
37
+ @output_context_class = set_output_context_class
24
38
  end
25
-
26
- private
27
-
28
- def attributes
29
- @attributes ||= { arguments: {}, fields: {} }
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
- @input = input.freeze
35
- @context = parse_input!
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
- payload[:result] = Result.success(data: parse_output!)
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 parse_input!
72
- errors = {}
73
- context = self.class.send(:attributes)[:arguments].each_with_object({}) do |(name, options), hash|
74
- errors = validate_attribute_value(@input[name], name, options, errors)
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
- context
95
+ raise Error, Result.failure(errors: @output.errors, status: Result::STATUS[:failed_at_output])
93
96
  end
94
97
 
95
- def validate_attribute_value(value, attribute_name, attribute_options, errors)
96
- return errors unless value.blank? && attribute_options[:required] && !attribute_options[:default]
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[attribute_name] ||= []
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
- extend ActiveModel::Naming
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[attribute_name]
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,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveInteractor
4
+ module Type
5
+ class Base
6
+ def self.valid?(value); end
7
+ end
8
+ end
9
+ 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
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveInteractor
4
+ module Type
5
+ extend ActiveSupport::Autoload
6
+
7
+ autoload :Base
8
+ autoload :Boolean
9
+ autoload :HasTypes
10
+ autoload :List
11
+ autoload :Union
12
+ end
13
+ end
@@ -10,5 +10,8 @@ module ActiveInteractor
10
10
  extend ActiveSupport::Autoload
11
11
 
12
12
  autoload :Base
13
+ autoload :Context
14
+ autoload :HasActiveModelErrors
13
15
  autoload :Result
16
+ autoload :Type
14
17
  end
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.1.0
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 00:00:00.000000000 Z
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.1.0/CHANGELOG.md
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.1.0
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: