servactory 2.14.1 → 2.15.0.rc1

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.
@@ -3,7 +3,7 @@
3
3
  module Servactory
4
4
  module Maintenance
5
5
  module Attributes
6
- class Option
6
+ class Option # rubocop:disable Metrics/ClassLength
7
7
  DEFAULT_BODY = ->(key:, body:, message: nil) { { key => body, message: } }
8
8
 
9
9
  private_constant :DEFAULT_BODY
@@ -29,14 +29,14 @@ module Servactory
29
29
  define_conflicts: nil,
30
30
  with_advanced_mode: true,
31
31
  **options
32
- ) # do
32
+ )
33
33
  @name = name.to_sym
34
34
  @validation_class = validation_class
35
35
  @define_methods = define_methods
36
36
  @define_conflicts = define_conflicts
37
37
  @need_for_checks = need_for_checks
38
38
 
39
- @body = prepare_value_for(
39
+ @body = construct_body(
40
40
  original_value:,
41
41
  options:,
42
42
  body_key:,
@@ -45,7 +45,7 @@ module Servactory
45
45
  with_advanced_mode:
46
46
  )
47
47
 
48
- prepare_methods_for(attribute)
48
+ apply_dynamic_methods_to(attribute:)
49
49
  end
50
50
  # rubocop:enable Metrics/MethodLength
51
51
 
@@ -55,7 +55,7 @@ module Servactory
55
55
 
56
56
  private
57
57
 
58
- def prepare_value_for(
58
+ def construct_body(
59
59
  original_value:,
60
60
  options:,
61
61
  body_key:,
@@ -64,44 +64,78 @@ module Servactory
64
64
  with_advanced_mode:
65
65
  )
66
66
  return original_value if original_value.present?
67
-
68
67
  return options.fetch(@name, body_fallback) unless with_advanced_mode
69
68
 
70
- prepare_advanced_for(
71
- body: options.fetch(@name, DEFAULT_BODY.call(key: body_key, body: body_fallback)),
69
+ use_advanced_mode(
70
+ options:,
72
71
  body_key:,
73
72
  body_value:,
74
73
  body_fallback:
75
74
  )
76
75
  end
77
76
 
78
- def prepare_advanced_for(
79
- body:,
80
- body_key:,
81
- body_value:,
82
- body_fallback:
83
- )
77
+ def use_advanced_mode(options:, body_key:, body_value:, body_fallback:)
78
+ default_body = create_default_body(body_key:, body_fallback:)
79
+ body = extract_body_from_options(options:, default_body:)
80
+
81
+ construct_advanced_body(
82
+ body:,
83
+ body_key:,
84
+ body_value:,
85
+ body_fallback:
86
+ )
87
+ end
88
+
89
+ def create_default_body(body_key:, body_fallback:)
90
+ DEFAULT_BODY.call(key: body_key, body: body_fallback)
91
+ end
92
+
93
+ def extract_body_from_options(options:, default_body:)
94
+ options.fetch(@name, default_body)
95
+ end
96
+
97
+ def construct_advanced_body(body:, body_key:, body_value:, body_fallback:)
84
98
  if body.is_a?(Hash)
85
- message = body.fetch(:message, nil)
99
+ build_hash_body(body:, body_key:, body_value:, body_fallback:)
100
+ else
101
+ build_simple_body(body_key:, body:)
102
+ end
103
+ end
86
104
 
87
- DEFAULT_BODY.call(
88
- key: body_key,
89
- body: body.fetch(body_key, message.present? ? body_value : body_fallback),
90
- message:
91
- )
105
+ def build_hash_body(body:, body_key:, body_value:, body_fallback:)
106
+ message = body[:message]
107
+ body_content = extract_body_content(
108
+ body:,
109
+ body_key:,
110
+ body_value:,
111
+ body_fallback:,
112
+ message:
113
+ )
114
+ DEFAULT_BODY.call(key: body_key, body: body_content, message:)
115
+ end
116
+
117
+ def extract_body_content(body:, body_key:, body_value:, body_fallback:, message:)
118
+ if message.present?
119
+ body.fetch(body_key, body_value)
92
120
  else
93
- DEFAULT_BODY.call(key: body_key, body:)
121
+ body.fetch(body_key, body_fallback)
94
122
  end
95
123
  end
96
124
 
97
- def prepare_methods_for(attribute)
98
- attribute.instance_eval(define_methods_template) if define_methods_template.present?
125
+ def build_simple_body(body_key:, body:)
126
+ DEFAULT_BODY.call(key: body_key, body:)
127
+ end
128
+
129
+ def apply_dynamic_methods_to(attribute:)
130
+ return if @define_methods.blank?
131
+
132
+ attribute.instance_eval(generate_methods_code)
99
133
  end
100
134
 
101
- def define_methods_template
135
+ def generate_methods_code
102
136
  return if @define_methods.blank?
103
137
 
104
- @define_methods_template ||= @define_methods.map do |define_method|
138
+ @define_methods.map do |define_method|
105
139
  <<-RUBY.squish
106
140
  def #{define_method.name};
107
141
  #{define_method.content.call(option: @body)};
@@ -37,56 +37,46 @@ module Servactory
37
37
  ########################################################################
38
38
 
39
39
  def register
40
- # Validation Class: Servactory::Inputs::Validations::Required
41
- register_required_option if @features.fetch(:required)
40
+ register_feature(:required, Servactory::Inputs::Validations::Required)
41
+ register_feature(:types, Servactory::Maintenance::Attributes::Validations::Type)
42
+ register_feature(:default, Servactory::Maintenance::Attributes::Validations::Type)
43
+ register_feature(:must, Servactory::Maintenance::Attributes::Validations::Must)
44
+ register_feature(:prepare, nil)
42
45
 
43
- # Validation Class: Servactory::Maintenance::Attributes::Validations::Type
44
- register_types_option if @features.fetch(:types)
45
- register_default_option if @features.fetch(:default)
46
+ self
47
+ end
46
48
 
47
- # Validation Class: Servactory::Maintenance::Attributes::Validations::Must
48
- register_must_option if @features.fetch(:must)
49
+ def collection
50
+ @collection ||= Servactory::Maintenance::Attributes::OptionsCollection.new
51
+ end
49
52
 
50
- # Validation Class: nil
51
- register_prepare_option if @features.fetch(:prepare)
53
+ private
52
54
 
53
- self
55
+ def register_feature(feature_name, validation_class)
56
+ return unless @features.fetch(feature_name)
57
+
58
+ method_name = "register_#{feature_name}_option"
59
+ send(method_name, validation_class)
54
60
  end
55
61
 
56
62
  ########################################################################
57
63
 
58
- def register_required_option # rubocop:disable Metrics/MethodLength
59
- collection << Servactory::Maintenance::Attributes::Option.new(
64
+ def register_required_option(validation_class)
65
+ create_option(
60
66
  name: :required,
61
- attribute: @attribute,
62
- validation_class: Servactory::Inputs::Validations::Required,
63
- define_methods: [
64
- Servactory::Maintenance::Attributes::DefineMethod.new(
65
- name: :required?,
66
- content: ->(option:) { Servactory::Utils.true?(option[:is]) }
67
- ),
68
- Servactory::Maintenance::Attributes::DefineMethod.new(
69
- name: :optional?,
70
- content: ->(option:) { !Servactory::Utils.true?(option[:is]) }
71
- )
72
- ],
73
- define_conflicts: [
74
- Servactory::Maintenance::Attributes::DefineConflict.new(
75
- content: -> { :required_vs_default if @attribute.required? && @attribute.default_value_present? }
76
- )
77
- ],
67
+ validation_class:,
68
+ define_methods: required_define_methods,
69
+ define_conflicts: required_define_conflicts,
78
70
  need_for_checks: true,
79
71
  body_key: :is,
80
- body_fallback: true,
81
- **@options
72
+ body_fallback: true
82
73
  )
83
74
  end
84
75
 
85
- def register_types_option
86
- collection << Servactory::Maintenance::Attributes::Option.new(
76
+ def register_types_option(validation_class)
77
+ create_option(
87
78
  name: :types,
88
- attribute: @attribute,
89
- validation_class: Servactory::Maintenance::Attributes::Validations::Type,
79
+ validation_class:,
90
80
  original_value: Array(@options.fetch(:type)).uniq,
91
81
  need_for_checks: true,
92
82
  body_fallback: nil,
@@ -94,31 +84,28 @@ module Servactory
94
84
  )
95
85
  end
96
86
 
97
- def register_default_option # rubocop:disable Metrics/MethodLength
98
- collection << Servactory::Maintenance::Attributes::Option.new(
87
+ def register_default_option(validation_class) # rubocop:disable Metrics/MethodLength
88
+ create_option(
99
89
  name: :default,
100
- attribute: @attribute,
101
- validation_class: Servactory::Maintenance::Attributes::Validations::Type,
90
+ validation_class:,
102
91
  define_methods: [
103
- Servactory::Maintenance::Attributes::DefineMethod.new(
92
+ create_define_method(
104
93
  name: :default_value_present?,
105
94
  content: ->(option:) { !option.nil? }
106
95
  )
107
96
  ],
108
97
  need_for_checks: true,
109
98
  body_fallback: nil,
110
- with_advanced_mode: false,
111
- **@options
99
+ with_advanced_mode: false
112
100
  )
113
101
  end
114
102
 
115
- def register_must_option # rubocop:disable Metrics/MethodLength
116
- collection << Servactory::Maintenance::Attributes::Option.new(
103
+ def register_must_option(validation_class) # rubocop:disable Metrics/MethodLength
104
+ create_option(
117
105
  name: :must,
118
- attribute: @attribute,
119
- validation_class: Servactory::Maintenance::Attributes::Validations::Must,
106
+ validation_class:,
120
107
  define_methods: [
121
- Servactory::Maintenance::Attributes::DefineMethod.new(
108
+ create_define_method(
122
109
  name: :must_present?,
123
110
  content: ->(option:) { option.present? }
124
111
  )
@@ -126,33 +113,66 @@ module Servactory
126
113
  need_for_checks: true,
127
114
  body_key: :is,
128
115
  body_fallback: nil,
129
- with_advanced_mode: false,
130
- **@options
116
+ with_advanced_mode: false
131
117
  )
132
118
  end
133
119
 
134
- def register_prepare_option # rubocop:disable Metrics/MethodLength
135
- collection << Servactory::Maintenance::Attributes::Option.new(
120
+ def register_prepare_option(_validation_class) # rubocop:disable Metrics/MethodLength
121
+ create_option(
136
122
  name: :prepare,
137
- attribute: @attribute,
138
123
  validation_class: nil,
139
124
  define_methods: [
140
- Servactory::Maintenance::Attributes::DefineMethod.new(
125
+ create_define_method(
141
126
  name: :prepare_present?,
142
127
  content: ->(option:) { option[:in].present? }
143
128
  )
144
129
  ],
145
130
  need_for_checks: false,
146
131
  body_key: :in,
147
- body_fallback: false,
148
- **@options
132
+ body_fallback: false
149
133
  )
150
134
  end
151
135
 
152
136
  ########################################################################
153
137
 
154
- def collection
155
- @collection ||= Servactory::Maintenance::Attributes::OptionsCollection.new
138
+ def required_define_methods
139
+ [
140
+ create_define_method(
141
+ name: :required?,
142
+ content: ->(option:) { Servactory::Utils.true?(option[:is]) }
143
+ ),
144
+ create_define_method(
145
+ name: :optional?,
146
+ content: ->(option:) { !Servactory::Utils.true?(option[:is]) }
147
+ )
148
+ ]
149
+ end
150
+
151
+ def required_define_conflicts
152
+ [
153
+ Servactory::Maintenance::Attributes::DefineConflict.new(
154
+ content: -> { :required_vs_default if @attribute.required? && @attribute.default_value_present? }
155
+ )
156
+ ]
157
+ end
158
+
159
+ ########################################################################
160
+
161
+ def create_option(name:, validation_class:, **options)
162
+ collection << Servactory::Maintenance::Attributes::Option.new(
163
+ name:,
164
+ attribute: @attribute,
165
+ validation_class:,
166
+ **options,
167
+ **@options
168
+ )
169
+ end
170
+
171
+ def create_define_method(name:, content:)
172
+ Servactory::Maintenance::Attributes::DefineMethod.new(
173
+ name:,
174
+ content:
175
+ )
156
176
  end
157
177
  end
158
178
  end
@@ -5,9 +5,10 @@ module Servactory
5
5
  module Attributes
6
6
  class OptionsCollection
7
7
  extend Forwardable
8
- def_delegators :@collection, :<<, :filter, :each, :map, :flat_map, :find
9
8
 
10
- def initialize(*)
9
+ def_delegators :@collection, :<<, :filter, :each, :map, :flat_map, :find, :empty?, :size
10
+
11
+ def initialize
11
12
  @collection = Set.new
12
13
  end
13
14
 
@@ -16,32 +17,41 @@ module Servactory
16
17
  end
17
18
 
18
19
  def validation_classes
19
- filter { |option| option.validation_class.present? }.map(&:validation_class).uniq
20
+ filter { |option| option.validation_class.present? }
21
+ .map(&:validation_class)
22
+ .uniq
20
23
  end
21
24
 
22
25
  def options_for_checks
23
26
  filter(&:need_for_checks?).to_h do |option|
24
- option_body = if option.body.is_a?(Hash)
25
- option.body.key?(:is) ? option.body.fetch(:is) : option.body
26
- else
27
- option.body
28
- end
29
-
30
- [option.name, option_body]
27
+ [option.name, extract_normalized_body_from(option:)]
31
28
  end
32
29
  end
33
30
 
34
31
  def defined_conflict_code
35
- flat_map do |option|
36
- option.define_conflicts&.map do |define_input_conflict|
37
- define_input_conflict.content.call
38
- end
39
- end.reject(&:blank?).first
32
+ flat_map { |option| resolve_conflicts_from(option:) }
33
+ .reject(&:blank?)
34
+ .first
40
35
  end
41
36
 
42
37
  def find_by(name:)
43
38
  find { |option| option.name == name }
44
39
  end
40
+
41
+ private
42
+
43
+ def extract_normalized_body_from(option:)
44
+ body = option.body
45
+ return body unless body.is_a?(Hash)
46
+
47
+ body.key?(:is) ? body.fetch(:is) : body
48
+ end
49
+
50
+ def resolve_conflicts_from(option:)
51
+ return [] unless option.define_conflicts
52
+
53
+ option.define_conflicts.map { |conflict| conflict.content.call }
54
+ end
45
55
  end
46
56
  end
47
57
  end
@@ -58,7 +58,7 @@ module Servactory
58
58
  ########################################################################
59
59
 
60
60
  def validation_classes
61
- @attribute.collection_of_options.validation_classes
61
+ @validation_classes ||= @attribute.collection_of_options.validation_classes
62
62
  end
63
63
 
64
64
  ########################################################################
@@ -13,12 +13,23 @@ module Servactory
13
13
  @types = output.types
14
14
  @options = output.options
15
15
 
16
- define_singleton_method(:system_name) { output.system_name }
17
- define_singleton_method(:i18n_name) { output.i18n_name }
18
- # The methods below are required to support the internal work.
19
- define_singleton_method(:input?) { false }
20
- define_singleton_method(:internal?) { false }
21
- define_singleton_method(:output?) { true }
16
+ define_identity_methods(output)
17
+ end
18
+
19
+ private
20
+
21
+ def define_identity_methods(output)
22
+ methods_map = {
23
+ system_name: -> { output.system_name },
24
+ i18n_name: -> { output.i18n_name },
25
+ input?: -> { false },
26
+ internal?: -> { false },
27
+ output?: -> { true }
28
+ }
29
+
30
+ methods_map.each do |method_name, implementation|
31
+ define_singleton_method(method_name, &implementation)
32
+ end
22
33
  end
23
34
  end
24
35
 
@@ -40,7 +51,6 @@ module Servactory
40
51
 
41
52
  def method_missing(name, *args, &block)
42
53
  option = @collection_of_options.find_by(name:)
43
-
44
54
  return super if option.nil?
45
55
 
46
56
  option.body
@@ -50,46 +60,14 @@ module Servactory
50
60
  @collection_of_options.names.include?(name) || super
51
61
  end
52
62
 
53
- def register_options(helpers:, options:) # rubocop:disable Metrics/MethodLength
54
- advanced_helpers = options.except(*Servactory::Maintenance::Attributes::Options::Registrar::RESERVED_OPTIONS)
55
-
56
- options = apply_helpers_for_options(helpers:, options:) if helpers.present?
57
- options = apply_helpers_for_options(helpers: advanced_helpers, options:) if advanced_helpers.present?
58
-
59
- options_registrar = Servactory::Maintenance::Attributes::Options::Registrar.register(
60
- attribute: self,
61
- options:,
62
- features: {
63
- types: true,
64
- must: true
65
- }
66
- )
63
+ def register_options(helpers:, options:)
64
+ merged_options = augment_options_with_helpers(helpers:, options:)
65
+ options_registrar = create_options_registrar(options: merged_options)
67
66
 
68
- @options = options
67
+ @options = merged_options
69
68
  @collection_of_options = options_registrar.collection
70
69
  end
71
70
 
72
- def apply_helpers_for_options(helpers:, options:) # rubocop:disable Metrics/MethodLength
73
- prepared_options = {}
74
-
75
- helpers.each do |(helper, values)|
76
- found_helper = @option_helpers.find_by(name: helper)
77
-
78
- next if found_helper.blank?
79
-
80
- prepared_option =
81
- if found_helper.equivalent.is_a?(Proc)
82
- values.is_a?(Hash) ? found_helper.equivalent.call(**values) : found_helper.equivalent.call(values)
83
- else
84
- found_helper.equivalent
85
- end
86
-
87
- prepared_options.deep_merge!(prepared_option)
88
- end
89
-
90
- options.deep_merge(prepared_options)
91
- end
92
-
93
71
  def options_for_checks
94
72
  @collection_of_options.options_for_checks
95
73
  end
@@ -113,6 +91,68 @@ module Servactory
113
91
  def output?
114
92
  true
115
93
  end
94
+
95
+ private
96
+
97
+ def create_options_registrar(options:)
98
+ Servactory::Maintenance::Attributes::Options::Registrar.register(
99
+ attribute: self,
100
+ options:,
101
+ features: available_feature_options
102
+ )
103
+ end
104
+
105
+ def available_feature_options
106
+ {
107
+ types: true,
108
+ must: true
109
+ }
110
+ end
111
+
112
+ def augment_options_with_helpers(helpers:, options:)
113
+ result_options = options.dup
114
+ merge_standard_helpers_into(target_options: result_options, helpers:) if helpers.present?
115
+ merge_advanced_helpers_into(target_options: result_options, source_options: options)
116
+ result_options
117
+ end
118
+
119
+ def merge_standard_helpers_into(target_options:, helpers:)
120
+ standard_helpers_result = transform_helpers_to_options(helpers:)
121
+ target_options.deep_merge!(standard_helpers_result)
122
+ end
123
+
124
+ def merge_advanced_helpers_into(target_options:, source_options:)
125
+ advanced_helpers = filter_advanced_helpers(options: source_options)
126
+ return if advanced_helpers.blank?
127
+
128
+ advanced_helpers_result = transform_helpers_to_options(helpers: advanced_helpers)
129
+ target_options.deep_merge!(advanced_helpers_result)
130
+ end
131
+
132
+ def filter_advanced_helpers(options:)
133
+ reserved_options = Servactory::Maintenance::Attributes::Options::Registrar::RESERVED_OPTIONS
134
+ options.except(*reserved_options)
135
+ end
136
+
137
+ def transform_helpers_to_options(helpers:)
138
+ helpers.each_with_object({}) do |(helper_name, values), result|
139
+ helper = @option_helpers.find_by(name: helper_name)
140
+ next if helper.blank?
141
+
142
+ transformed_option = transform_helper_to_option(helper:, values:)
143
+ result.deep_merge!(transformed_option) if transformed_option.present?
144
+ end
145
+ end
146
+
147
+ def transform_helper_to_option(helper:, values:)
148
+ return helper.equivalent unless helper.equivalent.is_a?(Proc)
149
+
150
+ if values.is_a?(Hash)
151
+ helper.equivalent.call(**values)
152
+ else
153
+ helper.equivalent.call(values)
154
+ end
155
+ end
116
156
  end
117
157
  end
118
158
  end
@@ -44,11 +44,10 @@ module Servactory
44
44
  end
45
45
 
46
46
  def to_h
47
- filtered = methods(false).filter do |key|
48
- !key.in?(STATE_PREDICATE_NAMES) && !key.to_s.end_with?("?")
49
- end
50
-
51
- filtered.to_h { |key| [key, public_send(key)] }.compact
47
+ methods(false)
48
+ .reject { |key| key.in?(STATE_PREDICATE_NAMES) || key.to_s.end_with?("?") }
49
+ .to_h { |key| [key, public_send(key)] }
50
+ .compact
52
51
  end
53
52
 
54
53
  def inspect
@@ -67,7 +66,7 @@ module Servactory
67
66
  self
68
67
  end
69
68
 
70
- def method_missing(name, *_args)
69
+ def method_missing(name, *args, &block)
71
70
  super
72
71
  rescue NoMethodError => e
73
72
  rescue_no_method_error_with(exception: e)
@@ -46,6 +46,8 @@ module Servactory
46
46
  def submatcher_passes?(_subject)
47
47
  attribute_default_value = attribute_data.fetch(:default)
48
48
 
49
+ return default_value.nil? if attribute_default_value.is_a?(NilClass)
50
+
49
51
  attribute_default_value.to_s.casecmp(default_value.to_s).zero?
50
52
  end
51
53
 
@@ -7,13 +7,13 @@ module Servactory
7
7
  module TestKit
8
8
  module Rspec
9
9
  module Matchers # rubocop:disable Metrics/ModuleLength
10
- def have_service_input(input_name) # rubocop:disable Naming/PredicateName
10
+ def have_service_input(input_name) # rubocop:disable Naming/PredicatePrefix
11
11
  HaveServiceInputMatcher.new(described_class, input_name)
12
12
  end
13
13
 
14
14
  RSpec::Matchers.alias_matcher :have_input, :have_service_input
15
15
 
16
- def have_service_internal(internal_name) # rubocop:disable Naming/PredicateName
16
+ def have_service_internal(internal_name) # rubocop:disable Naming/PredicatePrefix
17
17
  HaveServiceInternalMatcher.new(described_class, internal_name)
18
18
  end
19
19
 
@@ -57,11 +57,11 @@ module Servactory
57
57
  of == :integer ? { fake: 1 } : { fake: :yes }
58
58
  end
59
59
 
60
- def fake_true_class
60
+ def fake_true_class # rubocop:disable Naming/PredicateMethod
61
61
  true
62
62
  end
63
63
 
64
- def fake_false_class
64
+ def fake_false_class # rubocop:disable Naming/PredicateMethod
65
65
  false
66
66
  end
67
67
 
@@ -20,7 +20,7 @@ module Servactory
20
20
  common_condition_with(...)
21
21
  end
22
22
 
23
- def common_condition_with(value:, option:, **)
23
+ def common_condition_with(value:, option:, **) # rubocop:disable Naming/PredicateMethod
24
24
  case value
25
25
  when Integer
26
26
  value <= option.value
@@ -20,7 +20,7 @@ module Servactory
20
20
  common_condition_with(...)
21
21
  end
22
22
 
23
- def common_condition_with(value:, option:, **)
23
+ def common_condition_with(value:, option:, **) # rubocop:disable Naming/PredicateMethod
24
24
  case value
25
25
  when Integer
26
26
  value >= option.value