light-service-ext 0.1.0 → 0.1.2

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/.rubocop.yml +6 -0
  3. data/CHANGELOG.md +9 -1
  4. data/README.md +193 -5
  5. data/dev/setup.rb +5 -3
  6. data/lib/light-service-ext/application_action.rb +0 -21
  7. data/lib/light-service-ext/application_context.rb +29 -1
  8. data/lib/light-service-ext/application_validator_action.rb +2 -2
  9. data/lib/light-service-ext/configuration.rb +28 -0
  10. data/lib/light-service-ext/constants.rb +1 -1
  11. data/lib/light-service-ext/error_info.rb +11 -11
  12. data/lib/light-service-ext/version.rb +1 -1
  13. data/lib/light-service-ext/with_error_handler.rb +26 -0
  14. data/lib/light-service-ext.rb +35 -13
  15. data/light-service-ext.gemspec +2 -1
  16. data/spec/light-service-ext/application_action_spec.rb +39 -0
  17. data/spec/light-service-ext/application_context_spec.rb +189 -0
  18. data/spec/{light_service_ext → light-service-ext}/application_organizer_spec.rb +9 -2
  19. data/spec/light-service-ext/configuration_spec.rb +66 -0
  20. data/spec/{light_service_ext → light-service-ext}/error_info_spec.rb +18 -7
  21. data/spec/light-service-ext/with_error_handler_spec.rb +62 -0
  22. data/spec/light_service_ext_spec.rb +67 -1
  23. data/spec/spec_helper.rb +7 -3
  24. metadata +42 -22
  25. data/spec/light_service_ext/application_action_spec.rb +0 -77
  26. data/spec/light_service_ext/application_context_spec.rb +0 -68
  27. /data/spec/{light_service_ext → light-service-ext}/all_actions_complete_action_spec.rb +0 -0
  28. /data/spec/{light_service_ext → light-service-ext}/application_contract_spec.rb +0 -0
  29. /data/spec/{light_service_ext → light-service-ext}/application_validator_action_spec.rb +0 -0
  30. /data/spec/{light_service_ext → light-service-ext}/around_action_execute_extension_spec.rb +0 -0
  31. /data/spec/{light_service_ext → light-service-ext}/context_error_spec.rb +0 -0
  32. /data/spec/{light_service_ext → light-service-ext}/regex_spec.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 337b734063a899dc19d0a5d6591b127015473b682cd98a3d4c0454e3425ccdef
4
- data.tar.gz: 17bd81121eb9be17e5f81bb050e86d43edf82eb358bfe2034cfd1f27ddde1475
3
+ metadata.gz: a72952c78aab0b82bbce081bd1c2ab955ef264a9fbb5186a9ead3a429eaa504e
4
+ data.tar.gz: 43cb1863bac420a7150573a0e24cc141bd2384e36cfc4440dbd12f2fce276e93
5
5
  SHA512:
6
- metadata.gz: 6315693fe915515d1c1def2c80af6bc6b68b7f6268fb4cb9e3303da344d8284b95719ab219fc3c601c646f2169edfd894f222770f269c7e422b748ff1945e398
7
- data.tar.gz: 3249c9ef1fecde4b3312685fc2b68a46a1f41466bf89f81ce4db00f6bf9327cbe70c8b2c475c23c48dd24f8df486f0d618a0604fd367c663a5cdbf39f3f4776a
6
+ metadata.gz: cabc217b43f33aa0771de27613eeecd83f425786e93946ed14d716523814a4af7045d464ae16841b6d3e2db20a8b0f4d780190363ad130bf934c100b962ba1f1
7
+ data.tar.gz: 5ab3b2151ebb98e80b65a24717a628d7e9f60f206b25c258a05d32e3d57c85e832d99b92905f4883e2d770e9240da85e611771b2497d17f7c87687e670b82838
data/.rubocop.yml CHANGED
@@ -236,3 +236,9 @@ RSpec/MultipleMemoizedHelpers:
236
236
 
237
237
  RSpec/PredicateMatcher:
238
238
  Enabled: false
239
+
240
+ RSpec/FilePath:
241
+ Enabled: false
242
+
243
+ RSpec/SubjectStub:
244
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  ## [Unreleased]
2
2
 
3
- ## [0.1.0] - 2023-02-07
3
+ ## [0.1.0] - 2023-02-15
4
4
 
5
5
  - Initial release
6
+
7
+ ## [0.1.1] - 2023-02-15
8
+
9
+ - Fixing issue with with use of `relative_path` inside of `light-service-ext.gemspec`
10
+
11
+ ## [0.1.2] - YYYY-MM-DD
12
+
13
+ - Updates `README.md` with detailed information on features provided
data/README.md CHANGED
@@ -1,8 +1,11 @@
1
- # LightServiceExt
1
+ ![LightService](https://raw.githubusercontent.com/adomokos/light-service/master/resources/light-service.png)
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/light-service-ext`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ # Light Service Extensions
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ Aims to enhance [light-service](https://github.com/adomokos/light-service) to enhance this powerful and flexible service skeleton framework with an emphasis on simplicity
6
+
7
+ ## Console
8
+ run `bin/console` for an interactive prompt.
6
9
 
7
10
  ## Installation
8
11
 
@@ -20,9 +23,194 @@ Or install it yourself as:
20
23
 
21
24
  $ gem install light-service-ext
22
25
 
23
- ## Usage
26
+ ## ApplicationContext
27
+
28
+ > Adds useful defaults to the organizer/orchestrator context
29
+ - `:input` ~> values originally provided to organizer get moved here for better isolation
30
+ - `:params`
31
+ - stores values `filtered` and `mapped` from original `input`
32
+ - outcomes/return values provided by any action that implements `LightServiceExt::ApplicationAction`
33
+ - `:errors`
34
+ - validation errors processed by `LightServiceExt::ApplicationValidatorAction` [dry-validation](https://github.com/dry-rb/dry-validation) contract
35
+ - manually added by an action e.g. `{ errors: { email: 'not found' } }`
36
+ - `:successful_actions` ~> provides a list of actions processed mostly useful for debugging purposes
37
+ - `:api_responses` ~> contains a list of external API interactions mostly for recording/debugging purposes
38
+ - `:allow_raise_on_failure` ~> determines whether or not to throw a `RaiseOnContextError` error up the stack in the case of validation errors and/or captured exceptions
39
+ - `:outcome` denotes the current status of the organizer with one of the following flags:
40
+ - `LightServiceExt::Outcome::COMPLETE`
41
+
42
+ Example
43
+
44
+ ````ruby
45
+ input = { order: order }
46
+ overrides = {} # optionally override `params`, `errors` and `allow_raise_on_failure`
47
+ LightServiceExt::ApplicationContext.make_with_defaults(input, overrides)
48
+
49
+ # => { input: { order: order },
50
+ # params: {},
51
+ # errors: {},
52
+ # successful_actions: [],
53
+ # api_responses: [],
54
+ # allow_raise_on_failure: true
55
+ # }
56
+ ````
57
+
58
+ #### Useful methods
59
+
60
+ - `.add_params(**params)`
61
+ - Adds given args to context's `params` field
62
+ - e.g. `add_params(user_id: 1) # => { params: { user_id: 1 } }`
63
+ - `.add_errors(**errors)`
64
+ - Adds given args to to context's `errors` field
65
+ - Fails and returns from current action/organizer's context
66
+ - e.g. `add_to_errors(email: 'not found') # => { errors: { email: 'not found' } }`
67
+
68
+
69
+ ## ApplicationOrganizer
70
+
71
+ > Adds the following support
72
+
73
+ ### Useful methods
74
+
75
+ - `.reduce_if_success(<list of actions>)` prevents execution of action/step in the case of context failure or `:errors` present
76
+ - `.with_context(&block)` calls given block with `:ctx` argument
77
+ - `.execute_if` ~> Useful if you want the current `Organizer` to act as a `Orchestrator` and call another organizer
78
+ - *ONLY* modifies the current organizer/orchestrator's as a result of executing `organizer_or_action_class_or_proc` if manually applied by a given `result_callback` Proc
79
+ - Executed `steps` do modify the current organizer/orchestrator's context without the need for manual intervention
80
+ - Arguments:
81
+ - `condition_block` (required) ~> given block is called with current `context` argument
82
+ - `organizer_or_action_class_or_proc` (required) ~> only executed if `condition_block` evaluates to `true`
83
+ - must be one of `ApplicationOrganizer`, `ApplicationAction`, `Proc`
84
+ - `apply_ctx_transform` (optional)
85
+ - given block is called prior to `organizer_or_action_class_or_proc` being executed
86
+ - e.g. `apply_ctx_transform: -> (context) { context[:params][:user_id] = record(context)&.id }`
87
+ - returned value gets passed to `organizer_or_action_class_or_proc` call
88
+ - `result_callback` (optional)
89
+ - given block is called after `organizer_or_action_class_or_proc` has been executed
90
+ - Useful in the case where you want to augment the current organizer's context based on the context returned from the `organizer_or_action_class_or_proc` call
91
+ - e.g. `result_callback: -> (ctx:, result:) { ctx[:params] = result[:params] }`
92
+ - `ctx:` represents the main `organizer/orchestrator's` context
93
+ - `result:` represents the context returned from the executed `organizer_or_action_class_or_proc`
94
+ - `steps` (optional) ~> calls current `organizer/orchestrator's` actions/steps and called once `organizer_or_action_class_or_proc` has been processed
95
+ - *PLEASE NOTE* called regardless of the result from the `organizer_or_action_class_or_proc` call unless you *manually* fail the current context or add `:errors`
96
+
97
+ #### Error Handling
98
+ > Provided by `.with_error_handler`
99
+
100
+ - Records errors via `issue_error_report!` into context as exemplified below:
101
+ ```ruby
102
+ {
103
+ errors: {
104
+ base: "some-exception-message",
105
+ internal_only: {
106
+ type: 'ArgumentError',
107
+ message: "`user_id` must be a number",
108
+ exception: "ArgumentError : `user_id` must be a number",
109
+ backtrace: [], # filtered backtrace via `[ActiveSupport::BacktraceCleaner](https://api.rubyonrails.org/classes/ActiveSupport/BacktraceCleaner.html)`
110
+ error: original_captured_exception
111
+ }
112
+ }
113
+ }
114
+ ```
115
+
116
+ - Captures `model validation` exceptions and record the messages to the organizer's `:errors` context field
117
+ - Supports the following exceptions by default
118
+ - `ActiveRecord::Errors`
119
+ - `ActiveModel::Errors`
120
+ - Raises any non validation errors up the stack
121
+
122
+ #### API Responses
123
+ - records api responses set by an action's `:api_response` context field
124
+ - Stored inside of the organizer's `:api_responses` field
125
+
126
+ #### Retrieve Record
127
+ > Allows for a block to be defined on an organizer in order to retrieve the model record
128
+
129
+ Example
130
+
131
+ ```ruby
132
+ class TaxCalculator < LightServiceExt::ApplicationOrganizer
133
+ self.retrieve_record = -> (ctx:) { User.find_by(email: ctx.params[:email]) }
134
+
135
+ def self.call(input:)
136
+ user = record(ctx: input) # `.record` method executes proc provided to `retrieve_record`
137
+ input = { user: user }.merge(user: user)
138
+ reduce_with({ input: input }, steps)
139
+ end
140
+ end
141
+ ```
142
+
143
+ #### Failing The Context
144
+ - Prevents further action's been executed in the following scenarios:
145
+ - All actions complete determined by organizer's `:outcome` context field set to `LightServiceExt::Outcome::COMPLETE`
146
+
147
+ ### ApplicationAction
148
+
149
+ #### Useful methods
150
+ - TODO
151
+
152
+ #### Invoked Action
153
+ - *NOTE* Action's `executed` block gets called by the underlying `LightService::Action`
154
+ - this means in order to call your action's methods you need to invoke it from `invoked_action:` instead of `self`
155
+ - `invoked_action:` added to current action's context before it gets executed
156
+ - Consist of an instance of the current action that implements `LightServiceExt::ApplicationAction`
157
+
158
+ ## ApplicationContract
159
+
160
+ - Enhances `Dry::Validation::Contract` with the following methods:
161
+ - `#keys` ~> returns names of params defined
162
+ - `#t` ~> returns translation messages in context with the current organizer
163
+ - Arguments:
164
+ - `key` e.g. :not_found
165
+ - `base_path:` e.g. :user
166
+ - `**opts` options passed into underlying Rails i18n translate call
167
+ - E.g. `t(:not_found, base_path: 'business_create', scope: 'user')` would execute
168
+ - => `I18n.t('business_create.user.not_found', opts.except(:scope))`
169
+
170
+ ## ApplicationValidatorAction
171
+
172
+ > Responsible for mapping, filtering and validating the context `input:` field
173
+
174
+ - `executed` block does the following:
175
+ - Appends `params:` field to the current context with the mapped and filtered values
176
+ - Appends errors returned from a `ApplicationContract` [dry-validation](https://github.com/dry-rb/dry-validation) contract to the current context's `errors:` field
177
+ - *NOTE* fails current context if `errors:` present
178
+
179
+ ##### Useful Accessors
180
+
181
+ - `.contract_class` ~> sets the [dry-validation](https://github.com/dry-rb/dry-validation) contract to be applied by the current validator action
182
+ - `.params_mapper_class` ~> sets the mapper class that must implement `.map_from(context)` and return mapped `:input` values
183
+
184
+ ## ContextError
185
+
186
+ > Provides all the information related to an exception/validation errors captured by the current organizer
187
+
188
+ #### Useful methods
189
+ - `#error_info` ~> `ErrorInfo` instance
190
+ - `#context` ~> state of context provided
191
+ - `#error` ~> original exception
192
+ - `#message` ~> summarizes which action failed etc.
193
+
194
+ ## ErrorInfo
195
+ - Summarize captured exception
196
+
197
+ #### Useful accessors
198
+ - `non_fatal_errors` ~> takes a list of error class names considered to be non fatal exceptions
199
+
200
+ #### Useful methods
201
+ - `#error` ~> captured exception
202
+ - `#type` ~> exception class name e.g. `ArgumentError`
203
+ - `#message` ~> error message
204
+ - `title` ~> combined error class name and error message e.g. `ArgumentError : email must be present`
205
+ - `#fatal_error?`
206
+ - `#error_summary` ~> summarizes exception with message and cleaned backtrace via `ActiveSupport::BacktraceCleaner`
207
+
208
+ ## Regex
24
209
 
25
- TODO: Write usage instructions here
210
+ #### Useful methods
211
+ - `.match?(type, value)` e.g. `LightServiceExt::Regex.match?(email:, 'email@domain.com')`
212
+ - supported `type`:
213
+ - :email
26
214
 
27
215
  ## Development
28
216
 
data/dev/setup.rb CHANGED
@@ -5,8 +5,10 @@
5
5
  require 'json'
6
6
  require 'light-service'
7
7
  require 'dry-validation'
8
+ require 'rspec/core'
8
9
 
9
- Dir.glob("lib/**/*.rb").each do |f|
10
- require File.join(__dir__, '..', f)
11
- end
10
+ require 'active_support/core_ext/array'
11
+ require 'active_support/configurable'
12
12
 
13
+ require File.expand_path("../../lib/light-service-ext", Pathname.new(__FILE__).realpath)
14
+ require File.expand_path("../../spec/spec_helper", Pathname.new(__FILE__).realpath)
@@ -4,27 +4,6 @@ module LightServiceExt
4
4
  class ApplicationAction
5
5
  extend LightService::Action
6
6
 
7
- class << self
8
- def add_params(ctx, **params)
9
- add_to_context(ctx, :params, **params)
10
- end
11
-
12
- def add_errors(ctx, **errors)
13
- add_to_context(ctx, :errors, **errors)
14
-
15
- ctx.fail_and_return! if ctx[:errors].present?
16
- end
17
-
18
- private
19
-
20
- def add_to_context(ctx, key, **args)
21
- return if ctx.nil?
22
-
23
- ctx[key].merge!(args.dup)
24
- nil
25
- end
26
- end
27
-
28
7
  def self.inherited(base)
29
8
  base.singleton_class.prepend AroundActionExecuteExtension
30
9
  super
@@ -13,10 +13,38 @@ module LightServiceExt
13
13
  private
14
14
 
15
15
  def default_attrs
16
- { errors: {}, params: {}, successful_actions: [], api_responses: [], allow_raise_on_failure: true }.freeze
16
+ { errors: {}, params: {}, successful_actions: [], api_responses: [],
17
+ allow_raise_on_failure: LightServiceExt.config.allow_raise_on_failure?,
18
+ internal_only: { error_info: nil } }.freeze
17
19
  end
18
20
  end
19
21
 
22
+ def add_params(**params)
23
+ return if params.blank?
24
+
25
+ self[:params].merge!(params.dup)
26
+ end
27
+
28
+ def add_internal_only(**attrs)
29
+ return if attrs.blank?
30
+
31
+ self[:internal_only].merge!(attrs.dup)
32
+ end
33
+
34
+ def add_errors(**errors)
35
+ return if errors.blank?
36
+
37
+ self[:errors].merge!(errors.dup)
38
+ nil
39
+ end
40
+
41
+ def add_errors!(**errors)
42
+ return if errors.blank?
43
+
44
+ add_errors(**errors)
45
+ fail_and_return!
46
+ end
47
+
20
48
  def invoked_action
21
49
  self[:invoked_action]
22
50
  end
@@ -7,8 +7,8 @@ module LightServiceExt
7
7
 
8
8
  executed do |context|
9
9
  validator = map_and_validate_inputs(context)
10
- add_params(context, **validator.to_h)
11
- add_errors(context, **validator.errors.to_h.transform_values(&:first))
10
+ context.add_params(**validator.to_h)
11
+ context.add_errors!(**validator.errors.to_h.transform_values(&:first))
12
12
  end
13
13
 
14
14
  class << self
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LightServiceExt
4
+ class Configuration
5
+ include ActiveSupport::Configurable
6
+
7
+ config_accessor(:allow_raise_on_failure, default: true)
8
+ config_accessor(:non_fatal_error_classes, default: [])
9
+ config_accessor(:default_non_fatal_error_classes) { ['Rails::ActiveRecordError'.safe_constantize] }
10
+ config_accessor(:logger) { (defined? Rails.logger).nil? ? Logger.new($stdout) : Rails.logger }
11
+
12
+ def allow_raise_on_failure?
13
+ !!allow_raise_on_failure
14
+ end
15
+
16
+ def non_fatal_errors
17
+ (default_non_fatal_error_classes + non_fatal_error_classes).compact.uniq.map(&:to_s).freeze
18
+ end
19
+
20
+ def fatal_error?(exception)
21
+ !non_fatal_errors.exclude?(exception.class.name)
22
+ end
23
+
24
+ def non_fatal_error?(exception)
25
+ non_fatal_errors.include?(exception.class.name)
26
+ end
27
+ end
28
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module LightServiceExt
4
4
  module Outcome
5
- COMPLETE = "all_steps_complete"
5
+ COMPLETE = :all_steps_complete
6
6
  end
7
7
  end
@@ -29,6 +29,13 @@ module LightServiceExt
29
29
  TEXT
30
30
  end
31
31
 
32
+ def errors
33
+ model = error && (error.try(:model) || error.try(:record))
34
+ return model.errors.messages.transform_values(&:first) if model.present?
35
+
36
+ { base: message }
37
+ end
38
+
32
39
  def to_h
33
40
  {
34
41
  type: type,
@@ -36,7 +43,8 @@ module LightServiceExt
36
43
  exception: title,
37
44
  backtrace: clean_backtrace[0, 3]&.join("\n"),
38
45
  error: error,
39
- fatal_error?: fatal_error?
46
+ fatal_error?: fatal_error?,
47
+ errors: errors
40
48
  }
41
49
  end
42
50
 
@@ -45,7 +53,7 @@ module LightServiceExt
45
53
  end
46
54
 
47
55
  def clean_backtrace
48
- @clean_backtrace ||= if defined? Rails
56
+ @clean_backtrace ||= if defined? Rails.backtrace_cleaner
49
57
  Rails.backtrace_cleaner.clean(backtrace || [])
50
58
  else
51
59
  backtrace || []
@@ -53,15 +61,7 @@ module LightServiceExt
53
61
  end
54
62
 
55
63
  def non_fatal_error?
56
- error.nil? || self.class.non_fatal_errors.map(&:to_s).include?(type)
57
- end
58
-
59
- class << self
60
- attr_writer :non_fatal_errors
61
-
62
- def non_fatal_errors
63
- @non_fatal_errors ||= []
64
- end
64
+ error.nil? || LightServiceExt.config.non_fatal_error?(error)
65
65
  end
66
66
  end
67
67
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LightServiceExt
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.2"
5
5
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Metrics/AbcSize
4
+
5
+ module LightServiceExt
6
+ module WithErrorHandler
7
+ def with_error_handler(ctx:)
8
+ @result = yield || ApplicationContext.make_with_defaults
9
+ rescue Rails::ActiveRecordError => e
10
+ error_info = ErrorInfo.new(e, fatal: false)
11
+ ctx.add_internal_only(error_info: error_info)
12
+ ctx.add_errors(**error_info.errors)
13
+
14
+ LightServiceExt.config.logger.error(error_info.error_summary)
15
+
16
+ ctx.fail!
17
+ ctx
18
+ rescue StandardError => e
19
+ error_info = ErrorInfo.new(e, fatal: false)
20
+ LightServiceExt.config.logger.error(error_info.error_summary)
21
+
22
+ raise
23
+ end
24
+ end
25
+ end
26
+ # rubocop:enable Metrics/AbcSize
@@ -1,16 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "light-service-ext/version"
4
- require "light-service-ext/constants"
5
- require "light-service-ext/regex"
6
- require "light-service-ext/error_info"
7
- require "light-service-ext/context_error"
8
- require "light-service-ext/application_context"
9
- require "light-service-ext/application_contract"
10
- require "light-service-ext/around_action_execute_extension"
11
- require "light-service-ext/application_action"
12
- require "light-service-ext/all_actions_complete_action"
13
- require "light-service-ext/application_validator_action"
14
- require "light-service-ext/application_organizer"
3
+ %w[
4
+ version
5
+ constants
6
+ regex
7
+ error_info
8
+ context_error
9
+ configuration
10
+ with_error_handler
11
+ application_context
12
+ application_contract
13
+ around_action_execute_extension
14
+ application_action
15
+ all_actions_complete_action
16
+ application_validator_action
17
+ application_organizer
18
+ ].each do |filename|
19
+ require File.expand_path("../light-service-ext/#{filename}", Pathname.new(__FILE__).realpath)
20
+ end
15
21
 
16
- module LightServiceExt; end
22
+
23
+
24
+ module LightServiceExt
25
+ class << self
26
+ def config
27
+ self.configuration
28
+ end
29
+
30
+ def configuration
31
+ @configuration ||= Configuration.new
32
+ end
33
+
34
+ def configure
35
+ yield configuration
36
+ end
37
+ end
38
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "lib/light-service-ext/version"
3
+ require File.expand_path('../lib/light-service-ext/version', __FILE__)
4
4
 
5
5
  Gem::Specification.new do |gem|
6
6
  gem.authors = ["Desmond O'Leary"]
@@ -26,6 +26,7 @@ Gem::Specification.new do |gem|
26
26
  gem.add_runtime_dependency 'dry-struct', '~> 1.6'
27
27
  gem.add_runtime_dependency 'dry-validation', '~> 1.10'
28
28
  gem.add_runtime_dependency 'json', '~> 2.6', '>= 2.6.3'
29
+ gem.add_runtime_dependency 'activesupport', '>= 5'
29
30
 
30
31
  gem.add_development_dependency("rake", "~> 13.0.6")
31
32
  gem.add_development_dependency("rspec", "~> 3.12.0")
@@ -0,0 +1,39 @@
1
+ module LightServiceExt
2
+ RSpec.describe ApplicationAction do
3
+ FakeApplicationAction = Class.new(described_class) do
4
+ executed do |context|
5
+ value = context.dig(:input, :callback).call
6
+ context.add_params(value: value)
7
+ context.add_errors!(value: value)
8
+ end
9
+ end
10
+
11
+ let(:organizer_class) do
12
+ Class.new(ApplicationOrganizer) do
13
+ def self.steps
14
+ [FakeApplicationAction]
15
+ end
16
+ end
17
+ end
18
+
19
+ let(:value) { 'some-value' }
20
+ let(:callback) { -> { value } }
21
+ let(:input) { { callback: callback } }
22
+ let(:ctx) do
23
+ LightService::Testing::ContextFactory
24
+ .make_from(organizer_class)
25
+ .for(FakeApplicationAction)
26
+ .with(callback: callback)
27
+ end
28
+
29
+ subject(:context) do
30
+ FakeApplicationAction.execute(ctx)
31
+ end
32
+
33
+ it 'adds value returned by callback to params' do
34
+ expect(context.keys).to include(:input, :errors, :params)
35
+
36
+ expect(context[:params]).to eql({ value: value })
37
+ end
38
+ end
39
+ end