servactory 3.1.0.rc1 → 3.1.0.rc3

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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +21 -0
  3. data/README.md +2 -2
  4. data/config/locales/ja.yml +134 -0
  5. data/lib/generators/README.md +1 -1
  6. data/lib/servactory/configuration/config.rb +16 -0
  7. data/lib/servactory/configuration/configurable.rb +6 -3
  8. data/lib/servactory/configuration/hash_mode/class_names_collection.rb +4 -1
  9. data/lib/servactory/configuration/option_helpers/option_helpers_collection.rb +14 -0
  10. data/lib/servactory/context/warehouse/inputs.rb +15 -28
  11. data/lib/servactory/context/workspace/inputs.rb +20 -17
  12. data/lib/servactory/context/workspace/internals.rb +36 -31
  13. data/lib/servactory/context/workspace/outputs.rb +28 -30
  14. data/lib/servactory/inputs/tools/validation.rb +10 -18
  15. data/lib/servactory/maintenance/options/collection.rb +26 -16
  16. data/lib/servactory/maintenance/options/registrar.rb +1 -1
  17. data/lib/servactory/maintenance/validations/performer.rb +12 -23
  18. data/lib/servactory/result.rb +25 -40
  19. data/lib/servactory/test_kit/rspec/helpers/mock_executor.rb +6 -2
  20. data/lib/servactory/test_kit/rspec/helpers/service_mock_builder.rb +78 -8
  21. data/lib/servactory/test_kit/rspec/helpers/service_mock_config.rb +19 -2
  22. data/lib/servactory/tool_kit/dynamic_options/consists_of.rb +5 -3
  23. data/lib/servactory/tool_kit/dynamic_options/format.rb +1 -1
  24. data/lib/servactory/tool_kit/dynamic_options/inclusion.rb +2 -2
  25. data/lib/servactory/tool_kit/dynamic_options/max.rb +2 -2
  26. data/lib/servactory/tool_kit/dynamic_options/min.rb +2 -2
  27. data/lib/servactory/tool_kit/dynamic_options/multiple_of.rb +2 -2
  28. data/lib/servactory/tool_kit/dynamic_options/schema.rb +6 -4
  29. data/lib/servactory/tool_kit/dynamic_options/target.rb +3 -3
  30. data/lib/servactory/version.rb +1 -1
  31. data/lib/servactory.rb +0 -4
  32. metadata +12 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1c4398d2ffb193d4acd86b96f02871198d13d4e8b31ec96dddaa05c3966f1525
4
- data.tar.gz: bf35a15c0d771ef1c6362d573e2fbd5a04a0021cb0a80d847f37b23d5dff3b76
3
+ metadata.gz: c5acfb374e0e85d7779b99658fcb899007ca346ca63c71344800490428ac61dd
4
+ data.tar.gz: 94ff484e768908909aa856d63310af3c896f198357b88c3089fe9393de53bc88
5
5
  SHA512:
6
- metadata.gz: baa8f5c12c6edbf0786d807611a759f8fd54b3f087967ea981942843e3a19b2f1026f607bc52ad9c5f4dd42830216582e4e54d38bef5e00fa48dea47bbe84210
7
- data.tar.gz: c8af2b260386c4c2711a6b034bb116ad843cfb57c5e2b3181d59e9b9d38692ec562afc69bd5b33b46820c20972c0904e9921dfd1516e1576ec8622439ae1b382
6
+ metadata.gz: 61facdd48b89752c3f2bfe0735c87c24f83c225c2474608c9aa871f150003a996b2b4775ef25941f0916f45524383cdf1e1814a237b522add763782c2ec03e9b
7
+ data.tar.gz: 13202fe9276b8323cd83fc2c0919613eeabfd9d99b4d1cfac541ef7ed88739a84c6d5719a44ca76fe960c33aaf6a66b1b9c9353e12a1e5662b8e19df05a68fd3
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Anton Sokolov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -52,7 +52,7 @@ gem "servactory"
52
52
  ### Define service
53
53
 
54
54
  ```ruby
55
- class UserService::Authenticate < Servactory::Base
55
+ class Users::Authenticate < Servactory::Base
56
56
  input :email, type: String
57
57
  input :password, type: String
58
58
 
@@ -77,7 +77,7 @@ end
77
77
  ```ruby
78
78
  class SessionsController < ApplicationController
79
79
  def create
80
- service = UserService::Authenticate.call(**session_params)
80
+ service = Users::Authenticate.call(**session_params)
81
81
 
82
82
  if service.success?
83
83
  session[:current_user_id] = service.user.id
@@ -0,0 +1,134 @@
1
+ ja:
2
+ servactory:
3
+ common:
4
+ undefined_method:
5
+ missing_name: "[%{service_class_name}] %{error_text}"
6
+ methods:
7
+ call:
8
+ not_used: "[%{service_class_name}] 実行する処理がありません。`make`を使用するか、`call`メソッドを作成してください。"
9
+ cannot_be_overwritten: "[%{service_class_name}] 次のメソッドは上書きできません: %{list_of_methods}"
10
+ inputs:
11
+ undefined:
12
+ for_fetch: "[%{service_class_name}] 未定義の入力`%{input_name}`"
13
+ for_assign: "[%{service_class_name}] 未定義の入力`%{input_name}`"
14
+ validations:
15
+ must:
16
+ default_error: "[%{service_class_name}] 入力`%{input_name}`は「%{code}」を満たす必要があります"
17
+ syntax_error: "[%{service_class_name}] 入力`%{input_name}`の`%{code}`内の構文エラー: %{exception_message}"
18
+ dynamic_options:
19
+ consists_of:
20
+ required: "[%{service_class_name}] 入力コレクション`%{input_name}`の必須要素が不足しています"
21
+ wrong_type: "[%{service_class_name}] 入力コレクション`%{input_name}`の型が不正です。`%{expected_type}`が期待されましたが、`%{given_type}`が渡されました"
22
+ wrong_element_type: "[%{service_class_name}] 入力コレクション`%{input_name}`の要素の型が不正です。`%{expected_type}`が期待されましたが、`%{given_type}`が渡されました"
23
+ format:
24
+ default: "[%{service_class_name}] 入力`%{input_name}`は`%{format_name}`フォーマットに一致しません"
25
+ wrong_pattern: "[%{service_class_name}] 入力`%{input_name}`は`%{format_name}`フォーマットに一致しません"
26
+ wrong_type: "[%{service_class_name}] 入力`%{input_name}`は`%{format_name}`フォーマット検証のためにStringである必要があります"
27
+ unknown: "[%{service_class_name}] 入力`%{input_name}`に指定された`%{format_name}`フォーマットは不明です"
28
+ inclusion:
29
+ default: "[%{service_class_name}] `%{input_name}`の値が不正です。`%{input_inclusion}`のいずれかである必要がありますが、`%{value}`が渡されました"
30
+ invalid_option: "[%{service_class_name}] 入力`%{input_name}`の`%{option_name}`オプションに値がありません"
31
+ min:
32
+ default: "[%{service_class_name}] 入力`%{input_name}`は値`%{value}`を受け取りましたが、`%{option_value}`より小さいです"
33
+ max:
34
+ default: "[%{service_class_name}] 入力`%{input_name}`は値`%{value}`を受け取りましたが、`%{option_value}`より大きいです"
35
+ multiple_of:
36
+ default: "[%{service_class_name}] 入力`%{input_name}`の値`%{value}`は`%{option_value}`の倍数ではありません"
37
+ blank: "[%{service_class_name}] 入力`%{input_name}`の`%{option_name}`オプションに無効な値`%{option_value}`があります"
38
+ divided_by_0: "[%{service_class_name}] 入力`%{input_name}`の`%{option_name}`オプションに無効な値`%{option_value}`があります"
39
+ schema:
40
+ wrong_type: "[%{service_class_name}] 入力`%{input_name}`の型が不正です。`%{expected_type}`が期待されましたが、`%{given_type}`が渡されました"
41
+ wrong_element_type: "[%{service_class_name}] 入力ハッシュ`%{input_name}`の型が不正です。`%{key_name}`には`%{expected_type}`が期待されましたが、`%{given_type}`が渡されました"
42
+ wrong_element_value: "[%{service_class_name}] 入力ハッシュ`%{input_name}`の値が不正です。`%{key_name}`には`%{expected_type}`型の値が期待されましたが、`%{given_type}`が渡されました"
43
+ target:
44
+ default: "[%{service_class_name}] 入力`%{input_name}`のターゲットが不正です。`%{expected_target}`である必要がありますが、`%{value}`が渡されました"
45
+ invalid_option: "[%{service_class_name}] 入力`%{input_name}`の`%{option_name}`オプションに値がありません"
46
+ required:
47
+ default_error:
48
+ default: "[%{service_class_name}] 必須の入力`%{input_name}`が不足しています"
49
+ type:
50
+ default_error:
51
+ default: "[%{service_class_name}] 入力`%{input_name}`の型が不正です。`%{expected_type}`が期待されましたが、`%{given_type}`が渡されました"
52
+ tools:
53
+ find_unnecessary:
54
+ error: "[%{service_class_name}] 予期しない属性: `%{unnecessary_attributes}`"
55
+ rules:
56
+ error: "[%{service_class_name}] 入力`%{input_name}`のオプションに競合があります: `%{conflict_code}`"
57
+ internals:
58
+ undefined:
59
+ for_fetch: "[%{service_class_name}] 未定義の内部属性`%{internal_name}`"
60
+ for_assign: "[%{service_class_name}] 未定義の内部属性`%{internal_name}`"
61
+ validations:
62
+ must:
63
+ default_error: "[%{service_class_name}] 内部属性`%{internal_name}`は「%{code}」を満たす必要があります"
64
+ syntax_error: "[%{service_class_name}] 内部属性`%{internal_name}`の`%{code}`内の構文エラー: %{exception_message}"
65
+ dynamic_options:
66
+ consists_of:
67
+ required: "[%{service_class_name}] 内部属性コレクション`%{internal_name}`の必須要素が不足しています"
68
+ wrong_type: "[%{service_class_name}] 内部属性コレクション`%{internal_name}`の型が不正です。`%{expected_type}`が期待されましたが、`%{given_type}`が渡されました"
69
+ wrong_element_type: "[%{service_class_name}] 内部属性コレクション`%{internal_name}`の要素の型が不正です。`%{expected_type}`が期待されましたが、`%{given_type}`が渡されました"
70
+ format:
71
+ default: "[%{service_class_name}] 内部属性`%{internal_name}`は`%{format_name}`フォーマットに一致しません"
72
+ wrong_pattern: "[%{service_class_name}] 内部属性`%{internal_name}`は`%{format_name}`フォーマットに一致しません"
73
+ wrong_type: "[%{service_class_name}] 内部属性`%{internal_name}`は`%{format_name}`フォーマット検証のためにStringである必要があります"
74
+ unknown: "[%{service_class_name}] 内部属性`%{internal_name}`に指定された`%{format_name}`フォーマットは不明です"
75
+ inclusion:
76
+ default: "[%{service_class_name}] `%{internal_name}`の値が不正です。`%{internal_inclusion}`のいずれかである必要がありますが、`%{value}`が渡されました"
77
+ invalid_option: "[%{service_class_name}] 内部属性`%{internal_name}`の`%{option_name}`オプションに値がありません"
78
+ min:
79
+ default: "[%{service_class_name}] 内部属性`%{internal_name}`は値`%{value}`を受け取りましたが、`%{option_value}`より小さいです"
80
+ max:
81
+ default: "[%{service_class_name}] 内部属性`%{internal_name}`は値`%{value}`を受け取りましたが、`%{option_value}`より大きいです"
82
+ multiple_of:
83
+ default: "[%{service_class_name}] 内部属性`%{internal_name}`の値`%{value}`は`%{option_value}`の倍数ではありません"
84
+ blank: "[%{service_class_name}] 内部属性`%{internal_name}`の`%{option_name}`オプションに無効な値`%{option_value}`があります"
85
+ divided_by_0: "[%{service_class_name}] 内部属性`%{internal_name}`の`%{option_name}`オプションに無効な値`%{option_value}`があります"
86
+ schema:
87
+ wrong_type: "[%{service_class_name}] 内部属性`%{internal_name}`の型が不正です。`%{expected_type}`が期待されましたが、`%{given_type}`が渡されました"
88
+ wrong_element_type: "[%{service_class_name}] 内部属性ハッシュ`%{internal_name}`の型が不正です。`%{key_name}`には`%{expected_type}`が期待されましたが、`%{given_type}`が渡されました"
89
+ wrong_element_value: "[%{service_class_name}] 内部属性ハッシュ`%{internal_name}`の値が不正です。`%{key_name}`には`%{expected_type}`型の値が期待されましたが、`%{given_type}`が渡されました"
90
+ target:
91
+ default: "[%{service_class_name}] 内部属性`%{internal_name}`のターゲットが不正です。`%{expected_target}`である必要がありますが、`%{value}`が渡されました"
92
+ invalid_option: "[%{service_class_name}] 内部属性`%{internal_name}`の`%{option_name}`オプションに値がありません"
93
+ type:
94
+ default_error:
95
+ default: "[%{service_class_name}] 内部属性`%{internal_name}`の型が不正です。`%{expected_type}`が期待されましたが、`%{given_type}`が渡されました"
96
+ outputs:
97
+ undefined:
98
+ for_fetch: "[%{service_class_name}] 未定義の出力属性`%{output_name}`"
99
+ for_assign: "[%{service_class_name}] 未定義の出力属性`%{output_name}`"
100
+ validations:
101
+ must:
102
+ default_error: "[%{service_class_name}] 出力属性`%{output_name}`は「%{code}」を満たす必要があります"
103
+ syntax_error: "[%{service_class_name}] 出力属性`%{output_name}`の`%{code}`内の構文エラー: %{exception_message}"
104
+ dynamic_options:
105
+ consists_of:
106
+ required: "[%{service_class_name}] 出力属性コレクション`%{output_name}`の必須要素が不足しています"
107
+ wrong_type: "[%{service_class_name}] 出力属性コレクション`%{output_name}`の型が不正です。`%{expected_type}`が期待されましたが、`%{given_type}`が渡されました"
108
+ wrong_element_type: "[%{service_class_name}] 出力属性コレクション`%{output_name}`の要素の型が不正です。`%{expected_type}`が期待されましたが、`%{given_type}`が渡されました"
109
+ format:
110
+ default: "[%{service_class_name}] 出力属性`%{output_name}`は`%{format_name}`フォーマットに一致しません"
111
+ wrong_pattern: "[%{service_class_name}] 出力属性`%{output_name}`は`%{format_name}`フォーマットに一致しません"
112
+ wrong_type: "[%{service_class_name}] 出力属性`%{output_name}`は`%{format_name}`フォーマット検証のためにStringである必要があります"
113
+ unknown: "[%{service_class_name}] 出力属性`%{output_name}`に指定された`%{format_name}`フォーマットは不明です"
114
+ inclusion:
115
+ default: "[%{service_class_name}] `%{output_name}`の値が不正です。`%{output_inclusion}`のいずれかである必要がありますが、`%{value}`が渡されました"
116
+ invalid_option: "[%{service_class_name}] 出力属性`%{output_name}`の`%{option_name}`オプションに値がありません"
117
+ min:
118
+ default: "[%{service_class_name}] 出力属性`%{output_name}`は値`%{value}`を受け取りましたが、`%{option_value}`より小さいです"
119
+ max:
120
+ default: "[%{service_class_name}] 出力属性`%{output_name}`は値`%{value}`を受け取りましたが、`%{option_value}`より大きいです"
121
+ multiple_of:
122
+ default: "[%{service_class_name}] 出力属性`%{output_name}`の値`%{value}`は`%{option_value}`の倍数ではありません"
123
+ blank: "[%{service_class_name}] 出力属性`%{output_name}`の`%{option_name}`オプションに無効な値`%{option_value}`があります"
124
+ divided_by_0: "[%{service_class_name}] 出力属性`%{output_name}`の`%{option_name}`オプションに無効な値`%{option_value}`があります"
125
+ schema:
126
+ wrong_type: "[%{service_class_name}] 出力属性`%{output_name}`の型が不正です。`%{expected_type}`が期待されましたが、`%{given_type}`が渡されました"
127
+ wrong_element_type: "[%{service_class_name}] 出力属性ハッシュ`%{output_name}`の型が不正です。`%{key_name}`には`%{expected_type}`が期待されましたが、`%{given_type}`が渡されました"
128
+ wrong_element_value: "[%{service_class_name}] 出力属性ハッシュ`%{output_name}`の値が不正です。`%{key_name}`には`%{expected_type}`型の値が期待されましたが、`%{given_type}`が渡されました"
129
+ target:
130
+ default: "[%{service_class_name}] 出力属性`%{output_name}`のターゲットが不正です。`%{expected_target}`である必要がありますが、`%{value}`が渡されました"
131
+ invalid_option: "[%{service_class_name}] 出力属性`%{output_name}`の`%{option_name}`オプションに値がありません"
132
+ type:
133
+ default_error:
134
+ default: "[%{service_class_name}] 出力属性`%{output_name}`の型が不正です。`%{expected_type}`が期待されましたが、`%{given_type}`が渡されました"
@@ -9,7 +9,7 @@ rails generate servactory:install [options]
9
9
  **Options:**
10
10
  - `--namespace` — Base namespace (default: `ApplicationService`)
11
11
  - `--path` — Path to install service files (default: `app/services`)
12
- - `--locales` — Locales to install (available: `en`, `ru`, `de`, `fr`, `es`, `it`)
12
+ - `--locales` — Locales to install (available: `en`, `ru`, `de`, `fr`, `es`, `it`, `ja`)
13
13
  - `--minimal` — Generate minimal setup without configuration examples
14
14
 
15
15
  ## Service
@@ -30,6 +30,22 @@ module Servactory
30
30
  @collection_mode_class_names = original.collection_mode_class_names.dup
31
31
  @hash_mode_class_names = original.hash_mode_class_names.dup
32
32
  @action_rescue_handlers = original.action_rescue_handlers.dup
33
+
34
+ rebind_dynamic_option_helpers!
35
+ end
36
+
37
+ private
38
+
39
+ def rebind_dynamic_option_helpers!
40
+ new_consists_of = Servactory::ToolKit::DynamicOptions::ConsistsOf
41
+ .use(collection_mode_class_names: @collection_mode_class_names)
42
+ new_schema = Servactory::ToolKit::DynamicOptions::Schema
43
+ .use(default_hash_mode_class_names: @hash_mode_class_names)
44
+
45
+ [@input_option_helpers, @internal_option_helpers, @output_option_helpers].each do |helpers|
46
+ helpers.replace(name: :consists_of, with: new_consists_of)
47
+ helpers.replace(name: :schema, with: new_schema)
48
+ end
33
49
  end
34
50
  end
35
51
  end
@@ -67,7 +67,8 @@ module Servactory
67
67
  .new(name: :optional, equivalent: { required: false }),
68
68
  Servactory::ToolKit::DynamicOptions::ConsistsOf
69
69
  .use(collection_mode_class_names: config.collection_mode_class_names),
70
- Servactory::ToolKit::DynamicOptions::Schema.use(default_hash_mode_class_names:),
70
+ Servactory::ToolKit::DynamicOptions::Schema
71
+ .use(default_hash_mode_class_names: config.hash_mode_class_names),
71
72
  Servactory::ToolKit::DynamicOptions::Inclusion.use
72
73
  ]
73
74
  end
@@ -76,7 +77,8 @@ module Servactory
76
77
  Set[
77
78
  Servactory::ToolKit::DynamicOptions::ConsistsOf
78
79
  .use(collection_mode_class_names: config.collection_mode_class_names),
79
- Servactory::ToolKit::DynamicOptions::Schema.use(default_hash_mode_class_names:),
80
+ Servactory::ToolKit::DynamicOptions::Schema
81
+ .use(default_hash_mode_class_names: config.hash_mode_class_names),
80
82
  Servactory::ToolKit::DynamicOptions::Inclusion.use
81
83
  ]
82
84
  end
@@ -85,7 +87,8 @@ module Servactory
85
87
  Set[
86
88
  Servactory::ToolKit::DynamicOptions::ConsistsOf
87
89
  .use(collection_mode_class_names: config.collection_mode_class_names),
88
- Servactory::ToolKit::DynamicOptions::Schema.use(default_hash_mode_class_names:),
90
+ Servactory::ToolKit::DynamicOptions::Schema
91
+ .use(default_hash_mode_class_names: config.hash_mode_class_names),
89
92
  Servactory::ToolKit::DynamicOptions::Inclusion.use
90
93
  ]
91
94
  end
@@ -6,7 +6,10 @@ module Servactory
6
6
  class ClassNamesCollection
7
7
  extend Forwardable
8
8
 
9
- def_delegators :@collection, :include?, :intersect?
9
+ def_delegators :@collection,
10
+ :merge,
11
+ :include?,
12
+ :intersect?
10
13
 
11
14
  def initialize(collection)
12
15
  @collection = collection
@@ -72,6 +72,20 @@ module Servactory
72
72
  helpers_index[name]
73
73
  end
74
74
 
75
+ # Replaces a helper by name with a new one, invalidating the index cache.
76
+ #
77
+ # @param name [Symbol] the helper name to replace
78
+ # @param with [Maintenance::Attributes::OptionHelper] the replacement helper
79
+ # @return [void]
80
+ def replace(name:, with:)
81
+ old = find_by(name:)
82
+ return unless old
83
+
84
+ @collection.delete(old)
85
+ @collection.add(with)
86
+ @helpers_index = nil
87
+ end
88
+
75
89
  private
76
90
 
77
91
  # Builds and caches a hash index for O(1) helper lookups.
@@ -29,55 +29,42 @@ module Servactory
29
29
  @arguments.keys
30
30
  end
31
31
 
32
- # Merges new arguments into storage and defines accessor methods.
32
+ # Merges new arguments into storage.
33
33
  #
34
34
  # @param arguments [Hash] Input name-value pairs to merge
35
- # @return [void]
35
+ # @return [Hash] Updated arguments hash
36
36
  def merge!(arguments)
37
37
  @arguments.merge!(arguments)
38
- define_methods_for!(arguments)
39
38
  end
40
39
 
41
- # Raises error for any method call not pre-defined as a singleton accessor.
40
+ # Delegates method calls to stored inputs.
42
41
  #
43
42
  # Supports predicate methods when enabled in config.
44
43
  #
45
44
  # @param name [Symbol] Method name (input or predicate)
46
45
  # @param _args [Array] Method arguments (unused)
47
- # @raise [Exception] Failure exception for undefined input
46
+ # @return [Object] Input value or predicate result
48
47
  def method_missing(name, *_args)
49
48
  predicate = @context.config.predicate_methods_enabled && name.end_with?("?")
49
+
50
50
  input_name = predicate ? name.to_s.chomp("?").to_sym : name
51
- raise_error_for(input_name)
51
+
52
+ input_value = @arguments.fetch(input_name) { raise_error_for(input_name) }
53
+
54
+ predicate ? Servactory::Utils.query_attribute(input_value) : input_value
52
55
  end
53
56
 
54
- # Returns false since all valid methods are defined as singleton methods.
57
+ # Checks if method corresponds to stored input.
55
58
  #
56
- # @return [Boolean] Always false
57
- def respond_to_missing?(*)
58
- false
59
+ # @param name [Symbol] Method name to check
60
+ # @return [Boolean] True if input exists for this method
61
+ def respond_to_missing?(name, *)
62
+ input_name = name.to_s.chomp("?").to_sym
63
+ @arguments.key?(input_name) || @arguments.key?(name) || super
59
64
  end
60
65
 
61
66
  private
62
67
 
63
- # Defines singleton accessor methods for given arguments.
64
- #
65
- # @param arguments [Hash] Input name-value pairs to define methods for
66
- # @return [void]
67
- def define_methods_for!(arguments)
68
- arguments.each_key do |name|
69
- define_singleton_method(name) do
70
- @arguments.fetch(name) { raise_error_for(name) }
71
- end
72
-
73
- next unless @context.config.predicate_methods_enabled
74
-
75
- define_singleton_method(:"#{name}?") do
76
- Servactory::Utils.query_attribute(@arguments.fetch(name) { raise_error_for(name) })
77
- end
78
- end
79
- end
80
-
81
68
  # Raises error for undefined input.
82
69
  #
83
70
  # @param input_name [Symbol] Name of undefined input
@@ -7,8 +7,6 @@ module Servactory
7
7
  def initialize(context:, collection_of_inputs:)
8
8
  @context = context
9
9
  @collection_of_inputs = collection_of_inputs
10
-
11
- define_attribute_methods!
12
10
  end
13
11
 
14
12
  def only(*names)
@@ -26,30 +24,31 @@ module Servactory
26
24
  def method_missing(name, *_args)
27
25
  if name.to_s.end_with?("=")
28
26
  prepared_name = name.to_s.delete("=").to_sym
27
+
29
28
  raise_error_for(:assign, prepared_name)
30
29
  else
31
- raise_error_for(:fetch, name)
30
+ fetch_with(name:) { raise_error_for(:fetch, name) }
32
31
  end
33
32
  end
34
33
 
35
- def respond_to_missing?(*)
36
- false
34
+ def respond_to_missing?(name, *)
35
+ resolve_input(name) || super
37
36
  end
38
37
 
39
38
  private
40
39
 
41
- def define_attribute_methods!
42
- @collection_of_inputs.each do |input|
43
- define_singleton_method(input.internal_name) do
44
- fetch_value(attribute: input)
45
- end
40
+ def fetch_with(name:, &_block)
41
+ predicate = @context.config.predicate_methods_enabled && name.end_with?("?")
46
42
 
47
- next unless @context.config.predicate_methods_enabled
43
+ input_name = predicate ? name.to_s.chomp("?").to_sym : name
48
44
 
49
- define_singleton_method(:"#{input.internal_name}?") do
50
- fetch_predicate(attribute: input)
51
- end
52
- end
45
+ input = @collection_of_inputs.find_by(name: input_name)
46
+
47
+ return yield if input.nil?
48
+
49
+ value = fetch_value(attribute: input)
50
+
51
+ predicate ? Servactory::Utils.query_attribute(value) : value
53
52
  end
54
53
 
55
54
  def fetch_value(attribute:)
@@ -65,8 +64,12 @@ module Servactory
65
64
  value
66
65
  end
67
66
 
68
- def fetch_predicate(attribute:)
69
- Servactory::Utils.query_attribute(fetch_value(attribute:))
67
+ def resolve_input(name)
68
+ return true if @collection_of_inputs.find_by(name:)
69
+
70
+ @context.config.predicate_methods_enabled &&
71
+ name.to_s.end_with?("?") &&
72
+ @collection_of_inputs.find_by(name: name.to_s.chomp("?").to_sym)
70
73
  end
71
74
 
72
75
  def raise_error_for(type, name)
@@ -7,9 +7,6 @@ module Servactory
7
7
  def initialize(context:, collection_of_internals:)
8
8
  @context = context
9
9
  @collection_of_internals = collection_of_internals
10
- @validated_attributes = Set.new
11
-
12
- define_attribute_methods!
13
10
  end
14
11
 
15
12
  def only(*names)
@@ -24,57 +21,61 @@ module Servactory
24
21
  .to_h { |internal| [internal.name, send(internal.name)] }
25
22
  end
26
23
 
27
- def method_missing(name, *_args)
24
+ def method_missing(name, *args)
28
25
  if name.to_s.end_with?("=")
29
26
  prepared_name = name.to_s.delete("=").to_sym
30
- raise_error_for(:assign, prepared_name)
27
+
28
+ assign_with(prepared_name:, value: args.pop) { raise_error_for(:assign, prepared_name) }
31
29
  else
32
- raise_error_for(:fetch, name)
30
+ fetch_with(name:) { raise_error_for(:fetch, name) }
33
31
  end
34
32
  end
35
33
 
36
- def respond_to_missing?(*)
37
- false
34
+ def respond_to_missing?(name, *)
35
+ resolve_internal(name) || super
38
36
  end
39
37
 
40
38
  private
41
39
 
42
- def define_attribute_methods! # rubocop:disable Metrics/MethodLength
43
- @collection_of_internals.each do |internal|
44
- define_singleton_method(internal.name) do
45
- fetch_value(attribute: internal)
46
- end
40
+ def validated_attributes
41
+ @validated_attributes ||= Set.new
42
+ end
47
43
 
48
- define_singleton_method(:"#{internal.name}=") do |value|
49
- assign_value(attribute: internal, value:)
50
- end
44
+ def assign_with(prepared_name:, value:, &_block)
45
+ internal = @collection_of_internals.find_by(name: prepared_name)
51
46
 
52
- next unless @context.config.predicate_methods_enabled
47
+ return yield if internal.nil?
53
48
 
54
- define_singleton_method(:"#{internal.name}?") do
55
- fetch_predicate(attribute: internal)
56
- end
57
- end
58
- end
59
-
60
- def assign_value(attribute:, value:)
61
- @validated_attributes.delete(attribute.name)
49
+ validated_attributes.delete(internal.name)
62
50
 
63
51
  Servactory::Maintenance::Validations::Performer.validate!(
64
52
  context: @context,
65
- attribute:,
53
+ attribute: internal,
66
54
  value:
67
55
  )
68
56
 
69
- @context.send(:servactory_service_warehouse).assign_internal(attribute.name, value)
57
+ @context.send(:servactory_service_warehouse).assign_internal(internal.name, value)
70
58
 
71
- @validated_attributes << attribute.name
59
+ validated_attributes << internal.name
60
+ end
61
+
62
+ def fetch_with(name:, &_block)
63
+ predicate = @context.config.predicate_methods_enabled && name.end_with?("?")
64
+
65
+ internal_name = predicate ? name.to_s.chomp("?").to_sym : name
66
+ internal = @collection_of_internals.find_by(name: internal_name)
67
+
68
+ return yield if internal.nil?
69
+
70
+ internal_value = fetch_value(attribute: internal)
71
+
72
+ predicate ? Servactory::Utils.query_attribute(internal_value) : internal_value
72
73
  end
73
74
 
74
75
  def fetch_value(attribute:)
75
76
  value = @context.send(:servactory_service_warehouse).fetch_internal(attribute.name)
76
77
 
77
- if @validated_attributes.exclude?(attribute.name)
78
+ if validated_attributes.exclude?(attribute.name)
78
79
  Servactory::Maintenance::Validations::Performer.validate!(
79
80
  context: @context,
80
81
  attribute:,
@@ -85,8 +86,12 @@ module Servactory
85
86
  value
86
87
  end
87
88
 
88
- def fetch_predicate(attribute:)
89
- Servactory::Utils.query_attribute(fetch_value(attribute:))
89
+ def resolve_internal(name)
90
+ return true if @collection_of_internals.find_by(name:)
91
+
92
+ @context.config.predicate_methods_enabled &&
93
+ name.to_s.end_with?("?") &&
94
+ @collection_of_internals.find_by(name: name.to_s.chomp("?").to_sym)
90
95
  end
91
96
 
92
97
  def raise_error_for(type, name)
@@ -7,8 +7,6 @@ module Servactory
7
7
  def initialize(context:, collection_of_outputs:)
8
8
  @context = context
9
9
  @collection_of_outputs = collection_of_outputs
10
-
11
- define_attribute_methods!
12
10
  end
13
11
 
14
12
  def only(*names)
@@ -23,55 +21,55 @@ module Servactory
23
21
  .to_h { |output| [output.name, send(output.name)] }
24
22
  end
25
23
 
26
- def method_missing(name, *_args)
24
+ def method_missing(name, *args)
27
25
  if name.to_s.end_with?("=")
28
26
  prepared_name = name.to_s.delete("=").to_sym
29
- raise_error_for(:assign, prepared_name)
27
+
28
+ assign_with(prepared_name:, value: args.pop) { raise_error_for(:assign, prepared_name) }
30
29
  else
31
- raise_error_for(:fetch, name)
30
+ fetch_with(name:) { raise_error_for(:fetch, name) }
32
31
  end
33
32
  end
34
33
 
35
- def respond_to_missing?(*)
36
- false
34
+ def respond_to_missing?(name, *)
35
+ resolve_output(name) || super
37
36
  end
38
37
 
39
38
  private
40
39
 
41
- def define_attribute_methods! # rubocop:disable Metrics/MethodLength
42
- @collection_of_outputs.each do |output|
43
- define_singleton_method(output.name) do
44
- fetch_value(attribute: output)
45
- end
46
-
47
- define_singleton_method(:"#{output.name}=") do |value|
48
- assign_value(attribute: output, value:)
49
- end
40
+ def assign_with(prepared_name:, value:, &_block)
41
+ output = @collection_of_outputs.find_by(name: prepared_name)
50
42
 
51
- next unless @context.config.predicate_methods_enabled
52
-
53
- define_singleton_method(:"#{output.name}?") do
54
- fetch_predicate(attribute: output)
55
- end
56
- end
57
- end
43
+ return yield if output.nil?
58
44
 
59
- def assign_value(attribute:, value:)
60
45
  Servactory::Maintenance::Validations::Performer.validate!(
61
46
  context: @context,
62
- attribute:,
47
+ attribute: output,
63
48
  value:
64
49
  )
65
50
 
66
- @context.send(:servactory_service_warehouse).assign_output(attribute.name, value)
51
+ @context.send(:servactory_service_warehouse).assign_output(output.name, value)
67
52
  end
68
53
 
69
- def fetch_value(attribute:)
70
- @context.send(:servactory_service_warehouse).fetch_output(attribute.name)
54
+ def fetch_with(name:, &_block)
55
+ predicate = @context.config.predicate_methods_enabled && name.end_with?("?")
56
+
57
+ output_name = predicate ? name.to_s.chomp("?").to_sym : name
58
+ output = @collection_of_outputs.find_by(name: output_name)
59
+
60
+ return yield if output.nil?
61
+
62
+ output_value = @context.send(:servactory_service_warehouse).fetch_output(output.name)
63
+
64
+ predicate ? Servactory::Utils.query_attribute(output_value) : output_value
71
65
  end
72
66
 
73
- def fetch_predicate(attribute:)
74
- Servactory::Utils.query_attribute(fetch_value(attribute:))
67
+ def resolve_output(name)
68
+ return true if @collection_of_outputs.find_by(name:)
69
+
70
+ @context.config.predicate_methods_enabled &&
71
+ name.to_s.end_with?("?") &&
72
+ @collection_of_outputs.find_by(name: name.to_s.chomp("?").to_sym)
75
73
  end
76
74
 
77
75
  def raise_error_for(type, name)