opera 0.2.6 → 0.2.9

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: 8d61667567e16b2d1e328826229d79e9c2551cafd013ab7f7f5b8f68f6ddb88d
4
- data.tar.gz: a29639d3fc66ac1cb0fbf72523e00124230230fc18cd5d86838ea5d4d7ff6690
3
+ metadata.gz: d380295acdf59d14c35c5396db1d047f5d4b0ae40e809b035a68af997049328f
4
+ data.tar.gz: 7557cd9228c4858ec28907d6e4351c6eed63e510a4f50a6cc63630fcd28c0bd5
5
5
  SHA512:
6
- metadata.gz: 863d1b065902feb3c1affd29b348f53d295a34a1717d960367df191dfce98a344c4de4a13909cfabae5ef9bf669c8372beae6bc2593ef868c7896be92af5952b
7
- data.tar.gz: f427a8932e93bd86b4d3fce9eb72b8f453ba3f3c57362d288c4de2621dd6c0d1d535005f713999f75aa70a5ace67476a46cb787f18d293d28575e25bf540090f
6
+ metadata.gz: 6bef0ee319eec7f626476d83e9624e4d68ef482fb44ff2ee587dce1d27a386903df3db97418c320fb8d67084d3f0144e788f98efc6a88e3b4147196b273b4b83
7
+ data.tar.gz: 5eea38666b8f4a8fddcc568fb140d41a3d3a32eeef1c2ac45b6cc08032a9a8280b1ec747c1f805dacfceb7eb4c7d5f5f4deb17ab37c9c4643be9baf8a963fc7f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Opera Changelog
2
2
 
3
+ ### 0.2.9 - May 16, 2022
4
+
5
+ - Improve executions for failing operations
6
+
7
+ ### 0.2.8 - December 7, 2021
8
+
9
+ - Fix issue with default value
10
+ - Update Readme
11
+
12
+ ### 0.2.7 - October 29, 2021
13
+
14
+ - Adds `finish_if` step to DSL
15
+
3
16
  ### 0.2.6 - October 29, 2021
4
17
 
5
18
  - New method Result#failures that returns combined errors and exceptions
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- opera (0.2.6)
4
+ opera (0.2.8)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -81,4 +81,4 @@ DEPENDENCIES
81
81
  rspec (~> 3.0)
82
82
 
83
83
  BUNDLED WITH
84
- 2.2.15
84
+ 2.2.32
data/README.md CHANGED
@@ -144,6 +144,8 @@ end
144
144
 
145
145
  [Success](#user-content-success)
146
146
 
147
+ [Finish if](#user-content-finish-if)
148
+
147
149
  [Inner Operation](#user-content-inner-operation)
148
150
 
149
151
  [Inner Operations](#user-content-inner-operations)
@@ -700,7 +702,7 @@ class Profile::Create < Opera::Operation::Base
700
702
  end
701
703
  ```
702
704
 
703
- #### Example with information (real and total) from benchmark
705
+ #### Example output for success block
704
706
 
705
707
  ```ruby
706
708
  Profile::Create.call(params: {
@@ -712,6 +714,67 @@ Profile::Create.call(params: {
712
714
  #<Opera::Operation::Result:0x007fd0248e5638 @errors={"mailer"=>["Missing dependency"]}, @exceptions={}, @information={}, @executions=[:profile_schema, :populate, :create, :update, :send_email, :output], @output={:model=>#<Profile id: 40, user_id: nil, linkedin_uid: nil, picture: nil, headline: nil, summary: nil, first_name: "foo", last_name: "bar", created_at: "2019-01-03 12:21:35", updated_at: "2019-01-02 12:21:35", agree_to_terms_and_conditions: nil, registration_status: "", account_id: 1, start_date: nil, supervisor_id: nil, picture_processing: false, statistics: {}, data: {}, notification_timestamps: {}, suggestions: {}, notification_settings: {}, contact_information: []>}>
713
715
  ```
714
716
 
717
+ ### Finish If
718
+
719
+ ```ruby
720
+ class Profile::Create < Opera::Operation::Base
721
+ context_accessor :profile
722
+ dependencies_reader :current_account, :mailer
723
+
724
+ validate :profile_schema
725
+
726
+ step :create
727
+ finish_if :profile_create_only
728
+ step :update
729
+
730
+ success do
731
+ step :send_email
732
+ step :output
733
+ end
734
+
735
+ def profile_schema
736
+ Dry::Validation.Schema do
737
+ required(:first_name).filled
738
+ end.call(params)
739
+ end
740
+
741
+ def create
742
+ self.profile = current_account.profiles.create(params)
743
+ end
744
+
745
+ def profile_create_only
746
+ dependencies[:create_only].present?
747
+ end
748
+
749
+ def update
750
+ profile.update(updated_at: 1.day.ago)
751
+ end
752
+
753
+ # NOTE: We can add an error in this step and it won't break the execution
754
+ def send_email
755
+ result.add_error('mailer', 'Missing dependency')
756
+ mailer&.send_mail(profile: profile)
757
+ end
758
+
759
+ def output
760
+ result.output = { model: context[:profile] }
761
+ end
762
+ end
763
+ ```
764
+
765
+ #### Example with information (real and total) from benchmark
766
+
767
+ ```ruby
768
+ Profile::Create.call(params: {
769
+ first_name: :foo,
770
+ last_name: :bar
771
+ }, dependencies: {
772
+ create_only: true,
773
+ current_account: Account.find(1)
774
+ })
775
+ #<Opera::Operation::Result:0x007fd0248e5638 @errors={}, @exceptions={}, @information={}, @executions=[:profile_schema, :create, :profile_create_only], @output={}>
776
+ ```
777
+
715
778
  ### Inner Operation
716
779
 
717
780
  ```ruby
@@ -60,7 +60,11 @@ module Opera
60
60
  check_method_availability!(attribute)
61
61
 
62
62
  define_method(attribute) do
63
- value = send(method).key?(attribute) ? send(method)[attribute] : self.instance_exec(&options[:default])
63
+ value = if send(method).key?(attribute)
64
+ send(method)[attribute]
65
+ elsif options[:default]
66
+ instance_exec(&options[:default])
67
+ end
64
68
 
65
69
  if send(method).frozen?
66
70
  send(method)[attribute] || value
@@ -3,7 +3,7 @@
3
3
  module Opera
4
4
  module Operation
5
5
  module Builder
6
- INSTRUCTIONS = %I[validate transaction benchmark step success operation operations].freeze
6
+ INSTRUCTIONS = %I[validate transaction benchmark step success finish_if operation operations].freeze
7
7
 
8
8
  def self.included(base)
9
9
  base.extend(ClassMethods)
@@ -46,6 +46,8 @@ module Opera
46
46
  Instructions::Executors::Transaction.new(operation).call(instruction)
47
47
  when :benchmark
48
48
  Instructions::Executors::Benchmark.new(operation).call(instruction)
49
+ when :finish_if
50
+ Instructions::Executors::FinishIf.new(operation).call(instruction)
49
51
  else
50
52
  raise(UnknownInstructionError, "Unknown instruction #{instruction[:kind]}")
51
53
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Opera
4
+ module Operation
5
+ module Instructions
6
+ module Executors
7
+ class FinishIf < Executor
8
+ def call(instruction)
9
+ instruction[:kind] = :step
10
+ operation.finish! if super
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -12,12 +12,13 @@ module Opera
12
12
 
13
13
  if operation_result.success?
14
14
  add_instruction_output(instruction, operation_result.output)
15
- execution = result.executions.pop
16
- result.executions << { execution => operation_result.executions }
17
15
  else
18
16
  result.add_errors(operation_result.errors)
19
17
  result.add_exceptions(operation_result.exceptions)
20
18
  end
19
+
20
+ execution = result.executions.pop
21
+ result.executions << { execution => operation_result.executions }
21
22
  end
22
23
 
23
24
  private
@@ -8,6 +8,7 @@ require 'opera/operation/config'
8
8
  require 'opera/operation/instructions/executors/success'
9
9
  require 'opera/operation/instructions/executors/transaction'
10
10
  require 'opera/operation/instructions/executors/benchmark'
11
+ require 'opera/operation/instructions/executors/finish_if'
11
12
  require 'opera/operation/instructions/executors/validate'
12
13
  require 'opera/operation/instructions/executors/operation'
13
14
  require 'opera/operation/instructions/executors/operations'
@@ -15,6 +16,5 @@ require 'opera/operation/instructions/executors/step'
15
16
 
16
17
  module Opera
17
18
  module Operation
18
-
19
19
  end
20
20
  end
data/lib/opera/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Opera
2
- VERSION = '0.2.6'
2
+ VERSION = '0.2.9'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opera
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.2.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - ProFinda Development Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-10-29 00:00:00.000000000 Z
11
+ date: 2022-05-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-validation
@@ -78,12 +78,12 @@ files:
78
78
  - lib/opera.rb
79
79
  - lib/opera/errors.rb
80
80
  - lib/opera/operation.rb
81
- - lib/opera/operation/README.md
82
81
  - lib/opera/operation/base.rb
83
82
  - lib/opera/operation/builder.rb
84
83
  - lib/opera/operation/config.rb
85
84
  - lib/opera/operation/executor.rb
86
85
  - lib/opera/operation/instructions/executors/benchmark.rb
86
+ - lib/opera/operation/instructions/executors/finish_if.rb
87
87
  - lib/opera/operation/instructions/executors/operation.rb
88
88
  - lib/opera/operation/instructions/executors/operations.rb
89
89
  - lib/opera/operation/instructions/executors/step.rb
@@ -1,786 +0,0 @@
1
- # Opera::Operation
2
-
3
- Simple DSL for services/interactions classes.
4
-
5
- # Installation
6
-
7
- ```
8
- gem install pro_finda-operation
9
- ```
10
-
11
- or in Gemfile:
12
-
13
- ```
14
- gem 'pro_finda-operation', path: 'vendor/pro_finda-operation'
15
- ```
16
-
17
- # Configuration
18
-
19
- ```ruby
20
- Opera::Operation::Config.configure do |config|
21
- config.transaction_class = ActiveRecord::Base
22
- config.transaction_method = :transaction
23
- config.reporter = if defined?(Rollbar) then Rollbar else Rails.logger
24
- end
25
-
26
- class A < Opera::Operation::Base
27
-
28
- configure do |config|
29
- config.transaction_class = Profile
30
- config.reporter = Rails.logger
31
- end
32
-
33
- success :populate
34
-
35
- operation :inner_operation
36
-
37
- validate :profile_schema
38
-
39
- transaction do
40
- step :create
41
- step :update
42
- step :destroy
43
- end
44
-
45
- validate do
46
- step :validate_object
47
- step :validate_relationships
48
- end
49
-
50
- benchmark do
51
- success :hal_sync
52
- end
53
-
54
- success do
55
- step :send_mail
56
- step :report_to_audit_log
57
- end
58
-
59
- step :output
60
- end
61
- ```
62
-
63
- # Specs
64
-
65
- When using Opera::Operation inside an engine add the following
66
- configuration to your spec_helper.rb or rails_helper.rb:
67
-
68
- ```
69
- Opera::Operation::Config.configure do |config|
70
- config.transaction_class = ActiveRecord::Base
71
- end
72
- ```
73
-
74
- Without this extra configuration you will receive:
75
- ```
76
- NoMethodError:
77
- undefined method `transaction' for nil:NilClass
78
- ```
79
-
80
- # Debugging
81
-
82
- When you want to easily debug exceptions you can add this
83
- to your dummy.rb:
84
-
85
- ```
86
- Rails.application.configure do
87
- config.x.reporter = Logger.new(STDERR)
88
- end
89
- ```
90
-
91
- This should display exceptions captured inside operations.
92
-
93
- You can also do it in Opera::Operation configuration block:
94
-
95
- ```
96
- Opera::Operation::Config.configure do |config|
97
- config.transaction_class = ActiveRecord::Base
98
- config.reporter = Logger.new(STDERR)
99
- end
100
- ```
101
-
102
- # Content
103
- [Basic operation](#user-content-basic-operation)
104
-
105
- [Example with sanitizing parameters](#user-content-example-with-sanitizing-parameters)
106
-
107
- [Example operation with old validations](#user-content-example-operation-with-old-validations)
108
-
109
- [Example with step that raises exception](#user-content-example-with-step-that-raises-exception)
110
-
111
- [Failing transaction](#user-content-failing-transaction)
112
-
113
- [Passing transaction](#user-content-passing-transaction)
114
-
115
- [Benchmark](#user-content-benchmark)
116
-
117
- [Success](#user-content-success)
118
-
119
- [Inner Operation](#user-content-inner-operation)
120
-
121
- [Inner Operations](#user-content-inner-operations)
122
-
123
- # Usage examples
124
-
125
- Some cases and example how to use new operations
126
-
127
- ## Basic operation
128
-
129
- ```ruby
130
- class Profile::Create < Opera::Operation::Base
131
- validate :profile_schema
132
-
133
- step :create
134
- step :send_email
135
- step :output
136
-
137
- def profile_schema
138
- Dry::Validation.Schema do
139
- required(:first_name).filled
140
- end.call(params)
141
- end
142
-
143
- def create
144
- context[:profile] = dependencies[:current_account].profiles.create(params)
145
- end
146
-
147
- def send_email
148
- dependencies[:mailer]&.send_mail(profile: context[:profile])
149
- end
150
-
151
- def output
152
- result.output = { model: context[:profile] }
153
- end
154
- end
155
- ```
156
-
157
- #### Call with valid parameters
158
-
159
- ```ruby
160
- Profile::Create.call(params: {
161
- first_name: :foo,
162
- last_name: :bar
163
- }, dependencies: {
164
- mailer: ProfindaMailer,
165
- current_account: Account.find(1)
166
- })
167
-
168
- #<Opera::Operation::Result:0x0000561636dced60 @errors={}, @exceptions={}, @information={}, @executions=[:profile_schema, :create, :send_email, :output], @output={:model=>#<Profile id: 30, user_id: nil, linkedin_uid: nil, picture: nil, headline: nil, summary: nil, first_name: "foo", last_name: "bar", created_at: "2018-12-14 16:04:08", updated_at: "2018-12-14 16:04:08", agree_to_terms_and_conditions: nil, registration_status: "", account_id: 1, start_date: nil, supervisor_id: nil, picture_processing: false, statistics: {}, data: {}, notification_timestamps: {}, suggestions: {}, notification_settings: {}, contact_information: []>}>
169
- ```
170
-
171
- #### Call with INVALID parameters - missing first_name
172
-
173
- ```ruby
174
- Profile::Create.call(params: {
175
- last_name: :bar
176
- }, dependencies: {
177
- mailer: ProfindaMailer,
178
- current_account: Account.find(1)
179
- })
180
-
181
- #<Opera::Operation::Result:0x0000562d3f635390 @errors={:first_name=>["is missing"]}, @exceptions={}, @information={}, @executions=[:profile_schema]>
182
- ```
183
-
184
- #### Call with MISSING dependencies
185
-
186
- ```ruby
187
- Profile::Create.call(params: {
188
- first_name: :foo,
189
- last_name: :bar
190
- }, dependencies: {
191
- current_account: Account.find(1)
192
- })
193
-
194
- #<Opera::Operation::Result:0x007f87ba2c8f00 @errors={}, @exceptions={}, @information={}, @executions=[:profile_schema, :create, :send_email, :output], @output={:model=>#<Profile id: 33, user_id: nil, linkedin_uid: nil, picture: nil, headline: nil, summary: nil, first_name: "foo", last_name: "bar", created_at: "2019-01-03 12:04:25", updated_at: "2019-01-03 12:04:25", agree_to_terms_and_conditions: nil, registration_status: "", account_id: 1, start_date: nil, supervisor_id: nil, picture_processing: false, statistics: {}, data: {}, notification_timestamps: {}, suggestions: {}, notification_settings: {}, contact_information: []>}>
195
- ```
196
-
197
- ## Example with sanitizing parameters
198
-
199
- ```ruby
200
- class Profile::Create < Opera::Operation::Base
201
- validate :profile_schema
202
-
203
- step :create
204
- step :send_email
205
- step :output
206
-
207
- def profile_schema
208
- Dry::Validation.Schema do
209
- configure { config.input_processor = :sanitizer }
210
-
211
- required(:first_name).filled
212
- end.call(params)
213
- end
214
-
215
- def create
216
- context[:profile] = dependencies[:current_account].profiles.create(context[:profile_schema_output])
217
- end
218
-
219
- def send_email
220
- return true unless dependencies[:mailer]
221
- dependencies[:mailer].send_mail(profile: context[:profile])
222
- end
223
-
224
- def output
225
- result.output = { model: context[:profile] }
226
- end
227
- end
228
- ```
229
-
230
- ```ruby
231
- Profile::Create.call(params: {
232
- first_name: :foo,
233
- last_name: :bar
234
- }, dependencies: {
235
- mailer: ProfindaMailer,
236
- current_account: Account.find(1)
237
- })
238
-
239
- # NOTE: Last name is missing in output model
240
- #<Opera::Operation::Result:0x000055e36a1fab78 @errors={}, @exceptions={}, @information={}, @executions=[:profile_schema, :create, :send_email, :output], @output={:model=>#<Profile id: 44, user_id: nil, linkedin_uid: nil, picture: nil, headline: nil, summary: nil, first_name: "foo", last_name: nil, created_at: "2018-12-17 11:07:08", updated_at: "2018-12-17 11:07:08", agree_to_terms_and_conditions: nil, registration_status: "", account_id: 1, start_date: nil, supervisor_id: nil, picture_processing: false, statistics: {}, data: {}, notification_timestamps: {}, suggestions: {}, notification_settings: {}, contact_information: []>}>
241
- ```
242
-
243
- ## Example operation with old validations
244
-
245
- ```ruby
246
- class Profile::Create < Opera::Operation::Base
247
- validate :profile_schema
248
-
249
- step :build_record
250
- step :old_validation
251
- step :create
252
- step :send_email
253
- step :output
254
-
255
- def profile_schema
256
- Dry::Validation.Schema do
257
- required(:first_name).filled
258
- end.call(params)
259
- end
260
-
261
- def build_record
262
- context[:profile] = dependencies[:current_account].profiles.build(params)
263
- context[:profile].force_name_validation = true
264
- end
265
-
266
- def old_validation
267
- return true if context[:profile].valid?
268
-
269
- result.add_information(missing_validations: "Please check dry validations")
270
- result.add_errors(context[:profile].errors.messages)
271
-
272
- false
273
- end
274
-
275
- def create
276
- context[:profile].save
277
- end
278
-
279
- def send_email
280
- dependencies[:mailer].send_mail(profile: context[:profile])
281
- end
282
-
283
- def output
284
- result.output = { model: context[:profile] }
285
- end
286
- end
287
- ```
288
-
289
- #### Call with valid parameters
290
-
291
- ```ruby
292
- Profile::Create.call(params: {
293
- first_name: :foo,
294
- last_name: :bar
295
- }, dependencies: {
296
- mailer: ProfindaMailer,
297
- current_account: Account.find(1)
298
- })
299
-
300
- #<Opera::Operation::Result:0x0000560ebc9e7a98 @errors={}, @exceptions={}, @information={}, @executions=[:profile_schema, :build_record, :old_validation, :create, :send_email, :output], @output={:model=>#<Profile id: 41, user_id: nil, linkedin_uid: nil, picture: nil, headline: nil, summary: nil, first_name: "foo", last_name: "bar", created_at: "2018-12-14 19:15:12", updated_at: "2018-12-14 19:15:12", agree_to_terms_and_conditions: nil, registration_status: "", account_id: 1, start_date: nil, supervisor_id: nil, picture_processing: false, statistics: {}, data: {}, notification_timestamps: {}, suggestions: {}, notification_settings: {}, contact_information: []>}>
301
- ```
302
-
303
- #### Call with INVALID parameters
304
-
305
- ```ruby
306
- Profile::Create.call(params: {
307
- first_name: :foo
308
- }, dependencies: {
309
- mailer: ProfindaMailer,
310
- current_account: Account.find(1)
311
- })
312
-
313
- #<Opera::Operation::Result:0x0000560ef76ba588 @errors={:last_name=>["can't be blank"]}, @exceptions={}, @information={:missing_validations=>"Please check dry validations"}, @executions=[:build_record, :old_validation]>
314
- ```
315
-
316
- ## Example with step that raises exception
317
-
318
- ```ruby
319
- class Profile::Create < Opera::Operation::Base
320
- validate :profile_schema
321
-
322
- step :build_record
323
- step :exception
324
- step :create
325
- step :send_email
326
- step :output
327
-
328
- def profile_schema
329
- Dry::Validation.Schema do
330
- required(:first_name).filled
331
- end.call(params)
332
- end
333
-
334
- def build_record
335
- context[:profile] = dependencies[:current_account].profiles.build(params)
336
- context[:profile].force_name_validation = true
337
- end
338
-
339
- def exception
340
- raise StandardError, 'Example'
341
- end
342
-
343
- def create
344
- context[:profile] = context[:profile].save
345
- end
346
-
347
- def send_email
348
- return true unless dependencies[:mailer]
349
-
350
- dependencies[:mailer].send_mail(profile: context[:profile])
351
- end
352
-
353
- def output
354
- result.output(model: context[:profile])
355
- end
356
- end
357
- ```
358
- ##### Call with step throwing exception
359
- ```ruby
360
- result = Profile::Create.call(params: {
361
- first_name: :foo,
362
- last_name: :bar
363
- }, dependencies: {
364
- current_account: Account.find(1)
365
- })
366
-
367
- #<Opera::Operation::Result:0x0000562ad0f897c8 @errors={}, @exceptions={"Profile::Create#exception"=>["Example"]}, @information={}, @executions=[:profile_schema, :build_record, :exception]>
368
- ```
369
-
370
- ## Example with step that finishes execution
371
-
372
- ```ruby
373
- class Profile::Create < Opera::Operation::Base
374
- validate :profile_schema
375
-
376
- step :build_record
377
- step :create
378
- step :send_email
379
- step :output
380
-
381
- def profile_schema
382
- Dry::Validation.Schema do
383
- required(:first_name).filled
384
- end.call(params)
385
- end
386
-
387
- def build_record
388
- context[:profile] = dependencies[:current_account].profiles.build(params)
389
- context[:profile].force_name_validation = true
390
- end
391
-
392
- def create
393
- context[:profile] = context[:profile].save
394
- finish
395
- end
396
-
397
- def send_email
398
- return true unless dependencies[:mailer]
399
-
400
- dependencies[:mailer].send_mail(profile: context[:profile])
401
- end
402
-
403
- def output
404
- result.output(model: context[:profile])
405
- end
406
- end
407
- ```
408
- ##### Call
409
- ```ruby
410
- result = Profile::Create.call(params: {
411
- first_name: :foo,
412
- last_name: :bar
413
- }, dependencies: {
414
- current_account: Account.find(1)
415
- })
416
-
417
- #<Opera::Operation::Result:0x007fc2c59a8460 @errors={}, @exceptions={}, @information={}, @executions=[:profile_schema, :build_record, :create]>
418
- ```
419
-
420
- ## Failing transaction
421
-
422
- ```ruby
423
- class Profile::Create < Opera::Operation::Base
424
- configure do |config|
425
- config.transaction_class = Profile
426
- end
427
-
428
- validate :profile_schema
429
-
430
- transaction do
431
- step :create
432
- step :update
433
- end
434
-
435
- step :send_email
436
- step :output
437
-
438
- def profile_schema
439
- Dry::Validation.Schema do
440
- required(:first_name).filled
441
- end.call(params)
442
- end
443
-
444
- def create
445
- context[:profile] = dependencies[:current_account].profiles.create(params)
446
- end
447
-
448
- def update
449
- context[:profile].update(example_attr: :Example)
450
- end
451
-
452
- def send_email
453
- return true unless dependencies[:mailer]
454
-
455
- dependencies[:mailer].send_mail(profile: context[:profile])
456
- end
457
-
458
- def output
459
- result.output = { model: context[:profile] }
460
- end
461
- end
462
- ```
463
-
464
- #### Example with non-existing attribute
465
-
466
- ```ruby
467
- Profile::Create.call(params: {
468
- first_name: :foo,
469
- last_name: :bar
470
- }, dependencies: {
471
- mailer: ProfindaMailer,
472
- current_account: Account.find(1)
473
- })
474
-
475
- D, [2018-12-14T16:13:30.946466 #2504] DEBUG -- : Account Load (0.5ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."deleted_at" IS NULL AND "accounts"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
476
- D, [2018-12-14T16:13:30.960254 #2504] DEBUG -- : (0.2ms) BEGIN
477
- D, [2018-12-14T16:13:30.983981 #2504] DEBUG -- : SQL (0.7ms) INSERT INTO "profiles" ("first_name", "last_name", "created_at", "updated_at", "account_id") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["first_name", "foo"], ["last_name", "bar"], ["created_at", "2018-12-14 16:13:30.982289"], ["updated_at", "2018-12-14 16:13:30.982289"], ["account_id", 1]]
478
- D, [2018-12-14T16:13:30.986233 #2504] DEBUG -- : (0.2ms) ROLLBACK
479
- #<Opera::Operation::Result:0x00005650e89b7708 @errors={}, @exceptions={"Profile::Create#update"=>["unknown attribute 'example_attr' for Profile."], "Profile::Create#transaction"=>["Opera::Operation::Base::RollbackTransactionError"]}, @information={}, @executions=[:profile_schema, :create, :update]>
480
- ```
481
-
482
- ## Passing transaction
483
-
484
- ```ruby
485
- class Profile::Create < Opera::Operation::Base
486
- configure do |config|
487
- config.transaction_class = Profile
488
- end
489
-
490
- validate :profile_schema
491
-
492
- transaction do
493
- step :create
494
- step :update
495
- end
496
-
497
- step :send_email
498
- step :output
499
-
500
- def profile_schema
501
- Dry::Validation.Schema do
502
- required(:first_name).filled
503
- end.call(params)
504
- end
505
-
506
- def create
507
- context[:profile] = dependencies[:current_account].profiles.create(params)
508
- end
509
-
510
- def update
511
- context[:profile].update(updated_at: 1.day.ago)
512
- end
513
-
514
- def send_email
515
- return true unless dependencies[:mailer]
516
-
517
- dependencies[:mailer].send_mail(profile: context[:profile])
518
- end
519
-
520
- def output
521
- result.output = { model: context[:profile] }
522
- end
523
- end
524
- ```
525
-
526
- #### Example with updating timestamp
527
-
528
- ```ruby
529
- Profile::Create.call(params: {
530
- first_name: :foo,
531
- last_name: :bar
532
- }, dependencies: {
533
- mailer: ProfindaMailer,
534
- current_account: Account.find(1)
535
- })
536
- D, [2018-12-17T12:10:44.842392 #2741] DEBUG -- : Account Load (0.7ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."deleted_at" IS NULL AND "accounts"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
537
- D, [2018-12-17T12:10:44.856964 #2741] DEBUG -- : (0.2ms) BEGIN
538
- D, [2018-12-17T12:10:44.881332 #2741] DEBUG -- : SQL (0.7ms) INSERT INTO "profiles" ("first_name", "last_name", "created_at", "updated_at", "account_id") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["first_name", "foo"], ["last_name", "bar"], ["created_at", "2018-12-17 12:10:44.879684"], ["updated_at", "2018-12-17 12:10:44.879684"], ["account_id", 1]]
539
- D, [2018-12-17T12:10:44.886168 #2741] DEBUG -- : SQL (0.6ms) UPDATE "profiles" SET "updated_at" = $1 WHERE "profiles"."id" = $2 [["updated_at", "2018-12-16 12:10:44.883164"], ["id", 47]]
540
- D, [2018-12-17T12:10:44.898132 #2741] DEBUG -- : (10.3ms) COMMIT
541
- #<Opera::Operation::Result:0x0000556528f29058 @errors={}, @exceptions={}, @information={}, @executions=[:profile_schema, :create, :update, :send_email, :output], @output={:model=>#<Profile id: 47, user_id: nil, linkedin_uid: nil, picture: nil, headline: nil, summary: nil, first_name: "foo", last_name: "bar", created_at: "2018-12-17 12:10:44", updated_at: "2018-12-16 12:10:44", agree_to_terms_and_conditions: nil, registration_status: "", account_id: 1, start_date: nil, supervisor_id: nil, picture_processing: false, statistics: {}, data: {}, notification_timestamps: {}, suggestions: {}, notification_settings: {}, contact_information: []>}>
542
- ```
543
-
544
- ## Benchmark
545
-
546
- ```ruby
547
- class Profile::Create < Opera::Operation::Base
548
- validate :profile_schema
549
-
550
- step :create
551
- step :update
552
-
553
- benchmark do
554
- step :send_email
555
- step :output
556
- end
557
-
558
- def profile_schema
559
- Dry::Validation.Schema do
560
- required(:first_name).filled
561
- end.call(params)
562
- end
563
-
564
- def create
565
- context[:profile] = dependencies[:current_account].profiles.create(params)
566
- end
567
-
568
- def update
569
- context[:profile].update(updated_at: 1.day.ago)
570
- end
571
-
572
- def send_email
573
- return true unless dependencies[:mailer]
574
-
575
- dependencies[:mailer].send_mail(profile: context[:profile])
576
- end
577
-
578
- def output
579
- result.output = { model: context[:profile] }
580
- end
581
- end
582
- ```
583
-
584
- #### Example with information (real and total) from benchmark
585
-
586
- ```ruby
587
- Profile::Create.call(params: {
588
- first_name: :foo,
589
- last_name: :bar
590
- }, dependencies: {
591
- current_account: Account.find(1)
592
- })
593
- #<Opera::Operation::Result:0x007ff414a01238 @errors={}, @exceptions={}, @information={:real=>1.800013706088066e-05, :total=>0.0}, @executions=[:profile_schema, :create, :update, :send_email, :output], @output={:model=>#<Profile id: 30, user_id: nil, linkedin_uid: nil, picture: nil, headline: nil, summary: nil, first_name: "foo", last_name: "bar", created_at: "2018-12-19 10:46:00", updated_at: "2018-12-18 10:46:00", agree_to_terms_and_conditions: nil, registration_status: "", account_id: 1, start_date: nil, supervisor_id: nil, picture_processing: false, statistics: {}, data: {}, notification_timestamps: {}, suggestions: {}, notification_settings: {}, contact_information: []>}>
594
- ```
595
-
596
- ## Success
597
-
598
- ```ruby
599
- class Profile::Create < Opera::Operation::Base
600
- validate :profile_schema
601
-
602
- success :populate
603
-
604
- step :create
605
- step :update
606
-
607
- success do
608
- step :send_email
609
- step :output
610
- end
611
-
612
- def profile_schema
613
- Dry::Validation.Schema do
614
- required(:first_name).filled
615
- end.call(params)
616
- end
617
-
618
- def populate
619
- context[:attributes] = {}
620
- context[:valid] = false
621
- end
622
-
623
- def create
624
- context[:profile] = dependencies[:current_account].profiles.create(params)
625
- end
626
-
627
- def update
628
- context[:profile].update(updated_at: 1.day.ago)
629
- end
630
-
631
- # NOTE: We can add an error in this step and it won't break the execution
632
- def send_email
633
- result.add_error('mailer', 'Missing dependency')
634
- dependencies[:mailer]&.send_mail(profile: context[:profile])
635
- end
636
-
637
- def output
638
- result.output = { model: context[:profile] }
639
- end
640
- end
641
- ```
642
-
643
- #### Example with information (real and total) from benchmark
644
-
645
- ```ruby
646
- Profile::Create.call(params: {
647
- first_name: :foo,
648
- last_name: :bar
649
- }, dependencies: {
650
- current_account: Account.find(1)
651
- })
652
- #<Opera::Operation::Result:0x007fd0248e5638 @errors={"mailer"=>["Missing dependency"]}, @exceptions={}, @information={}, @executions=[:profile_schema, :populate, :create, :update, :send_email, :output], @output={:model=>#<Profile id: 40, user_id: nil, linkedin_uid: nil, picture: nil, headline: nil, summary: nil, first_name: "foo", last_name: "bar", created_at: "2019-01-03 12:21:35", updated_at: "2019-01-02 12:21:35", agree_to_terms_and_conditions: nil, registration_status: "", account_id: 1, start_date: nil, supervisor_id: nil, picture_processing: false, statistics: {}, data: {}, notification_timestamps: {}, suggestions: {}, notification_settings: {}, contact_information: []>}>
653
- ```
654
-
655
- ## Inner Operation
656
-
657
- ```ruby
658
- class Profile::Find < Opera::Operation::Base
659
- step :find
660
-
661
- def find
662
- result.output = Profile.find(params[:id])
663
- end
664
- end
665
-
666
- class Profile::Create < Opera::Operation::Base
667
- validate :profile_schema
668
-
669
- operation :find
670
-
671
- step :create
672
-
673
- step :output
674
-
675
- def profile_schema
676
- Dry::Validation.Schema do
677
- optional(:id).filled
678
- end.call(params)
679
- end
680
-
681
- def find
682
- Profile::Find.call(params: params, dependencies: dependencies)
683
- end
684
-
685
- def create
686
- return if context[:find_output]
687
- puts 'not found'
688
- end
689
-
690
- def output
691
- result.output = { model: context[:find_output] }
692
- end
693
- end
694
- ```
695
-
696
- #### Example with inner operation doing the find
697
-
698
- ```ruby
699
- Profile::Create.call(params: {
700
- id: 1
701
- }, dependencies: {
702
- current_account: Account.find(1)
703
- })
704
- #<Opera::Operation::Result:0x007f99b25f0f20 @errors={}, @exceptions={}, @information={}, @executions=[:profile_schema, :find, :create, :output], @output={:model=>{:id=>1, :user_id=>1, :linkedin_uid=>nil, ...}}>
705
- ```
706
-
707
- ## Inner Operations
708
- Expects that method returns array of `Opera::Operation::Result`
709
-
710
- ```ruby
711
- class Profile::Create < Opera::Operation::Base
712
- step :validate
713
- step :create
714
-
715
- def validate; end
716
-
717
- def create
718
- result.output = { model: "Profile #{Kernel.rand(100)}" }
719
- end
720
- end
721
-
722
- class Profile::CreateMultiple < Opera::Operation::Base
723
- operations :create_multiple
724
-
725
- step :output
726
-
727
- def create_multiple
728
- (0..params[:number]).map do
729
- Profile::Create.call
730
- end
731
- end
732
-
733
- def output
734
- result.output = context[:create_multiple_output]
735
- end
736
- end
737
- ```
738
-
739
- ```ruby
740
- Profile::CreateMultiple.call(params: { number: 3 })
741
-
742
- #<Opera::Operation::Result:0x0000564189f38c90 @errors={}, @exceptions={}, @information={}, @executions=[{:create_multiple=>[[:validate, :create], [:validate, :create], [:validate, :create], [:validate, :create]]}, :output], @output=[{:model=>"Profile 1"}, {:model=>"Profile 7"}, {:model=>"Profile 69"}, {:model=>"Profile 92"}]>
743
- ```
744
-
745
- ## Opera::Operation::Result - Instance Methods
746
-
747
- Sometimes it may be useful to be able to create an instance of the `Result` with preset `output`.
748
- It can be handy especially in specs. Then just include it in the initializer:
749
-
750
- ```
751
- Opera::Operation::Result.new(output: 'success')
752
- ```
753
-
754
- >
755
- - success? - [true, false] - Return true if no errors and no exceptions
756
- - failure? - [true, false] - Return true if any error or exception
757
- - output - [Anything] - Return Anything
758
- - output=(Anything) - Sets content of operation output
759
- - add_error(key, value) - Adds new error message
760
- - add_errors(Hash) - Adds multiple error messages
761
- - add_exception(method, message, classname: nil) - Adds new exception
762
- - add_exceptions(Hash) - Adds multiple exceptions
763
- - add_information(Hash) - Adss new information - Useful informations for developers
764
-
765
- ## Opera::Operation::Base - Class Methods
766
- >
767
- - step(Symbol) - single instruction
768
- - return [Truthly] - continue operation execution
769
- - return [False] - stops operation execution
770
- - raise Exception - exception gets captured and stops operation execution
771
- - operation(Symbol) - single instruction - requires to return Opera::Operation::Result object
772
- - return [Opera::Operation::Result] - stops operation STEPS execution if any error, exception
773
- - validate(Symbol) - single dry-validations - requires to return Dry::Validation::Result object
774
- - return [Dry::Validation::Result] - stops operation STEPS execution if any error but continue with other validations
775
- - transaction(*Symbols) - list of instructions to be wrapped in transaction
776
- - return [Truthly] - continue operation execution
777
- - return [False|Exception] - stops operation execution and breaks transaction/do rollback
778
- - call(params: Hash, dependencies: Hash?)
779
- - return [Opera::Operation::Result] - never raises an exception
780
-
781
- ## Opera::Operation::Base - Instance Methods
782
- >
783
- - context [Hash] - used to pass information between steps - only for internal usage
784
- - params [Hash] - immutable and received in call method
785
- - dependencies [Hash] - immutable and received in call method
786
- - finish - this method interrupts the execution of steps after is invoked