opera 0.4.1 → 0.5.0

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.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Opera Changelog
2
2
 
3
+ ### 0.5.0 - Apr 13, 2026
4
+
5
+ - Add `within` executor for wrapping one or more steps with a custom block method
6
+ - Remove `benchmark` executor
7
+
3
8
  ### 0.4.1 - Feb 18, 2026
4
9
  - Add parsed errors to default `output!` exception message
5
10
 
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in opera.gemspec
@@ -6,6 +8,13 @@ gemspec
6
8
  gem 'rake', '~> 13.2'
7
9
  gem 'rspec', '~> 3.13'
8
10
 
11
+ group :development do
12
+ gem 'rubocop'
13
+ gem 'rubocop-performance'
14
+ gem 'rubocop-rake'
15
+ gem 'rubocop-rspec'
16
+ end
17
+
9
18
  group :test, :development do
10
19
  gem 'dry-validation'
11
20
  gem 'pry'
data/Gemfile.lock CHANGED
@@ -1,11 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- opera (0.4.1)
4
+ opera (0.5.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
+ ast (2.4.3)
9
10
  bigdecimal (3.1.9)
10
11
  byebug (12.0.0)
11
12
  coderay (1.1.3)
@@ -46,15 +47,26 @@ GEM
46
47
  dry-initializer (~> 3.2)
47
48
  dry-schema (~> 1.14)
48
49
  zeitwerk (~> 2.6)
50
+ json (2.18.0)
51
+ language_server-protocol (3.17.0.5)
52
+ lint_roller (1.1.0)
49
53
  logger (1.7.0)
50
54
  method_source (1.1.0)
55
+ parallel (1.27.0)
56
+ parser (3.3.10.0)
57
+ ast (~> 2.4.1)
58
+ racc
59
+ prism (1.7.0)
51
60
  pry (0.15.2)
52
61
  coderay (~> 1.1)
53
62
  method_source (~> 1.0)
54
63
  pry-byebug (3.11.0)
55
64
  byebug (~> 12.0)
56
65
  pry (>= 0.13, < 0.16)
66
+ racc (1.8.1)
67
+ rainbow (3.1.1)
57
68
  rake (13.2.1)
69
+ regexp_parser (2.11.3)
58
70
  rspec (3.13.0)
59
71
  rspec-core (~> 3.13.0)
60
72
  rspec-expectations (~> 3.13.0)
@@ -68,6 +80,34 @@ GEM
68
80
  diff-lcs (>= 1.2.0, < 2.0)
69
81
  rspec-support (~> 3.13.0)
70
82
  rspec-support (3.13.3)
83
+ rubocop (1.82.1)
84
+ json (~> 2.3)
85
+ language_server-protocol (~> 3.17.0.2)
86
+ lint_roller (~> 1.1.0)
87
+ parallel (~> 1.10)
88
+ parser (>= 3.3.0.2)
89
+ rainbow (>= 2.2.2, < 4.0)
90
+ regexp_parser (>= 2.9.3, < 3.0)
91
+ rubocop-ast (>= 1.48.0, < 2.0)
92
+ ruby-progressbar (~> 1.7)
93
+ unicode-display_width (>= 2.4.0, < 4.0)
94
+ rubocop-ast (1.49.0)
95
+ parser (>= 3.3.7.2)
96
+ prism (~> 1.7)
97
+ rubocop-performance (1.26.1)
98
+ lint_roller (~> 1.1)
99
+ rubocop (>= 1.75.0, < 2.0)
100
+ rubocop-ast (>= 1.47.1, < 2.0)
101
+ rubocop-rake (0.7.1)
102
+ lint_roller (~> 1.1)
103
+ rubocop (>= 1.72.1)
104
+ rubocop-rspec (3.8.0)
105
+ lint_roller (~> 1.1)
106
+ rubocop (~> 1.81)
107
+ ruby-progressbar (1.13.0)
108
+ unicode-display_width (3.2.0)
109
+ unicode-emoji (~> 4.1)
110
+ unicode-emoji (4.1.0)
71
111
  zeitwerk (2.6.18)
72
112
 
73
113
  PLATFORMS
@@ -80,6 +120,10 @@ DEPENDENCIES
80
120
  pry-byebug
81
121
  rake (~> 13.2)
82
122
  rspec (~> 3.13)
123
+ rubocop
124
+ rubocop-performance
125
+ rubocop-rake
126
+ rubocop-rspec
83
127
 
84
128
  BUNDLED WITH
85
129
  2.3.3
data/README.md CHANGED
@@ -3,7 +3,6 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/opera.svg)](https://badge.fury.io/rb/opera)
4
4
  ![Master](https://github.com/Profinda/opera/actions/workflows/release.yml/badge.svg?branch=master)
5
5
 
6
-
7
6
  Simple DSL for services/interactions classes.
8
7
 
9
8
  Opera was born to mimic some of the philosophy of the dry gems but keeping the DSL simple.
@@ -48,7 +47,6 @@ end
48
47
 
49
48
  You can later override this configuration in each Operation to have more granularity
50
49
 
51
-
52
50
  ## Usage
53
51
 
54
52
  Once opera gem is in your project you can start to build Operations
@@ -77,10 +75,6 @@ class A < Opera::Operation::Base
77
75
  step :validate_relationships
78
76
  end
79
77
 
80
- benchmark do
81
- success :hal_sync
82
- end
83
-
84
78
  success do
85
79
  step :send_mail
86
80
  step :report_to_audit_log
@@ -92,7 +86,6 @@ end
92
86
 
93
87
  Start developing your business logic, services and interactions as Opera::Operations and benefit of code that is documented, self-explanatory, easy to maintain and debug.
94
88
 
95
-
96
89
  ### Specs
97
90
 
98
91
  When using Opera::Operation inside an engine add the following
@@ -105,6 +98,7 @@ end
105
98
  ```
106
99
 
107
100
  Without this extra configuration you will receive:
101
+
108
102
  ```ruby
109
103
  NoMethodError:
110
104
  undefined method `transaction' for nil:NilClass
@@ -137,6 +131,7 @@ end
137
131
  ```
138
132
 
139
133
  ### Content
134
+
140
135
  [Basic operation](#user-content-basic-operation)
141
136
 
142
137
  [Example with sanitizing parameters](#user-content-example-with-sanitizing-parameters)
@@ -147,8 +142,6 @@ end
147
142
 
148
143
  [Passing transaction](#user-content-passing-transaction)
149
144
 
150
- [Benchmark](#user-content-benchmark)
151
-
152
145
  [Success](#user-content-success)
153
146
 
154
147
  [Finish if](#user-content-finish-if)
@@ -157,6 +150,8 @@ end
157
150
 
158
151
  [Inner Operations](#user-content-inner-operations)
159
152
 
153
+ [Within](#user-content-within)
154
+
160
155
  ## Usage examples
161
156
 
162
157
  Some cases and example how to use new operations
@@ -594,71 +589,6 @@ D, [2020-08-17T12:10:44.898132 #2741] DEBUG -- : (10.3ms) COMMIT
594
589
  #<Opera::Operation::Result:0x0000556528f29058 @errors={}, @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: "2020-08-17 12:10:44", updated_at: "2020-08-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: []>}>
595
590
  ```
596
591
 
597
- ### Benchmark
598
-
599
- ```ruby
600
- class Profile::Create < Opera::Operation::Base
601
- # DEPRECATED
602
- # context_accessor :profile
603
- context do
604
- attr_accessor :profile
605
- end
606
- # DEPRECATED
607
- # dependencies_reader :current_account, :mailer
608
- dependencies do
609
- attr_reader :current_account, :mailer
610
- end
611
-
612
- validate :profile_schema
613
-
614
- benchmark :fast_section do
615
- step :create
616
- step :update
617
- end
618
-
619
- benchmark :slow_section do
620
- step :send_email
621
- step :output
622
- end
623
-
624
- def profile_schema
625
- Dry::Validation.Schema do
626
- required(:first_name).filled
627
- end.call(params)
628
- end
629
-
630
- def create
631
- self.profile = current_account.profiles.create(params)
632
- end
633
-
634
- def update
635
- profile.update(updated_at: 1.day.ago)
636
- end
637
-
638
- def send_email
639
- return true unless mailer
640
-
641
- mailer.send_mail(profile: profile)
642
- end
643
-
644
- def output
645
- result.output = { model: profile }
646
- end
647
- end
648
- ```
649
-
650
- #### Example with information (real and total) from benchmark
651
-
652
- ```ruby
653
- Profile::Create.call(params: {
654
- first_name: :foo,
655
- last_name: :bar
656
- }, dependencies: {
657
- current_account: Account.find(1)
658
- })
659
- #<Opera::Operation::Result:0x007ff414a01238 @errors={}, @information={fast_section: {:real=>0.300013706088066e-05, :total=>0.0}, slow_section: {: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: "2020-08-19 10:46:00", updated_at: "2020-08-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: []>}>
660
- ```
661
-
662
592
  ### Success
663
593
 
664
594
  ```ruby
@@ -785,8 +715,6 @@ class Profile::Create < Opera::Operation::Base
785
715
  end
786
716
  ```
787
717
 
788
- #### Example with information (real and total) from benchmark
789
-
790
718
  ```ruby
791
719
  Profile::Create.call(params: {
792
720
  first_name: :foo,
@@ -851,6 +779,7 @@ Profile::Create.call(params: {
851
779
  ```
852
780
 
853
781
  ### Inner Operations
782
+
854
783
  Expects that method returns array of `Opera::Operation::Result`
855
784
 
856
785
  ```ruby
@@ -888,6 +817,171 @@ Profile::CreateMultiple.call(params: { number: 3 })
888
817
  #<Opera::Operation::Result:0x0000564189f38c90 @errors={}, @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"}]>
889
818
  ```
890
819
 
820
+ ### Within
821
+
822
+ `within` wraps one or more steps with a method you define on the operation. The method must `yield` to execute the nested steps. If it does not yield, the nested steps are skipped. Normal break conditions (errors, `finish!`) still apply inside the block.
823
+
824
+ ```ruby
825
+ class Profile::Create < Opera::Operation::Base
826
+ context do
827
+ attr_accessor :profile
828
+ end
829
+
830
+ dependencies do
831
+ attr_reader :current_account
832
+ end
833
+
834
+ step :build
835
+
836
+ within :read_from_replica do
837
+ step :check_duplicate
838
+ step :validate_quota
839
+ end
840
+
841
+ step :create
842
+ step :output
843
+
844
+ def build
845
+ self.profile = current_account.profiles.build(params)
846
+ end
847
+
848
+ def check_duplicate
849
+ result.add_error(:base, 'already exists') if Profile.exists?(email: params[:email])
850
+ end
851
+
852
+ def validate_quota
853
+ result.add_error(:base, 'quota exceeded') if current_account.profiles.count >= 100
854
+ end
855
+
856
+ def create
857
+ profile.save!
858
+ end
859
+
860
+ def output
861
+ result.output = { model: profile }
862
+ end
863
+
864
+ private
865
+
866
+ def read_from_replica(&block)
867
+ ActiveRecord::Base.connected_to(role: :reading, &block)
868
+ end
869
+ end
870
+ ```
871
+
872
+ `within`-method can also be used inline inside any step method when you need the wrapper for only part of that method's logic:
873
+
874
+ ```ruby
875
+ def some_step
876
+ value = read_from_replica { Profile.count }
877
+ result.output = { count: value }
878
+ end
879
+
880
+ private
881
+
882
+ def read_from_replica(&block)
883
+ ActiveRecord::Base.connected_to(role: :reading, &block)
884
+ end
885
+ ```
886
+
887
+ #### Mixing step and operation inside within
888
+
889
+ `within` can wrap any combination of `step` and `operation` instructions. All of them execute inside the wrapper, and their outputs are available in context afterwards as usual.
890
+
891
+ ```ruby
892
+ class Profile::Create < Opera::Operation::Base
893
+ context do
894
+ attr_accessor :profile
895
+ end
896
+
897
+ dependencies do
898
+ attr_reader :current_account, :quota_checker
899
+ end
900
+
901
+ within :read_from_replica do
902
+ step :check_duplicate
903
+ operation :fetch_quota
904
+ end
905
+
906
+ step :create
907
+ step :output
908
+
909
+ def check_duplicate
910
+ result.add_error(:base, 'already exists') if Profile.exists?(email: params[:email])
911
+ end
912
+
913
+ def fetch_quota
914
+ quota_checker.call(params: params)
915
+ end
916
+
917
+ def create
918
+ self.profile = current_account.profiles.create(params)
919
+ end
920
+
921
+ def output
922
+ result.output = { model: profile, quota: context[:fetch_quota_output] }
923
+ end
924
+
925
+ private
926
+
927
+ def read_from_replica(&block)
928
+ ActiveRecord::Base.connected_to(role: :reading, &block)
929
+ end
930
+ end
931
+ ```
932
+
933
+ #### Nesting within inside a transaction
934
+
935
+ `within` can be placed inside a `transaction` block alongside other instructions. If any step or operation inside `within` fails, the error propagates up and the transaction is rolled back as normal.
936
+
937
+ ```ruby
938
+ class Profile::Create < Opera::Operation::Base
939
+ configure do |config|
940
+ config.transaction_class = ActiveRecord::Base
941
+ end
942
+
943
+ context do
944
+ attr_accessor :profile
945
+ end
946
+
947
+ dependencies do
948
+ attr_reader :current_account, :quota_checker, :audit_logger
949
+ end
950
+
951
+ transaction do
952
+ within :read_from_replica do
953
+ step :check_duplicate
954
+ operation :fetch_quota
955
+ end
956
+ operation :write_audit_log
957
+ end
958
+
959
+ step :output
960
+
961
+ def check_duplicate
962
+ result.add_error(:base, 'already exists') if Profile.exists?(email: params[:email])
963
+ end
964
+
965
+ def fetch_quota
966
+ quota_checker.call(params: params)
967
+ end
968
+
969
+ def write_audit_log
970
+ audit_logger.call(params: params)
971
+ end
972
+
973
+ def output
974
+ result.output = { quota: context[:fetch_quota_output] }
975
+ end
976
+
977
+ private
978
+
979
+ def read_from_replica(&block)
980
+ ActiveRecord::Base.connected_to(role: :reading, &block)
981
+ end
982
+ end
983
+ ```
984
+
891
985
  ## Opera::Operation::Result - Instance Methods
892
986
 
893
987
  Sometimes it may be useful to be able to create an instance of the `Result` with preset `output`.
@@ -898,6 +992,7 @@ Opera::Operation::Result.new(output: 'success')
898
992
  ```
899
993
 
900
994
  >
995
+
901
996
  - success? - [true, false] - Return true if no errors
902
997
  - failure? - [true, false] - Return true if any error
903
998
  - output - [Anything] - Return Anything
@@ -908,7 +1003,9 @@ Opera::Operation::Result.new(output: 'success')
908
1003
  - add_information(Hash) - Adss new information - Useful informations for developers
909
1004
 
910
1005
  ## Opera::Operation::Base - Instance Methods
1006
+
911
1007
  >
1008
+
912
1009
  - context [Hash] - used to pass information between steps - only for internal usage
913
1010
  - params [Hash] - immutable and received in call method
914
1011
  - dependencies [Hash] - immutable and received in call method
@@ -921,6 +1018,7 @@ Opera::Operation::Result.new(output: 'success')
921
1018
  The `context_reader` helper method is designed to facilitate easy access to specified keys within a `context` hash. It dynamically defines a method that acts as a getter for the value associated with a specified key, simplifying data retrieval.
922
1019
 
923
1020
  #### Parameters
1021
+
924
1022
  **key (Symbol):** The key(s) for which the getter and setter methods are to be created. These symbols should correspond to keys in the context hash.
925
1023
 
926
1024
  **default (Proc, optional):** A lambda or proc that returns a default value for the key if it is not present in the context hash. This proc is lazily evaluated only when the getter is invoked and the key is not present in the hash.
@@ -993,6 +1091,7 @@ def serializer
993
1091
  ProfileSerializer.new
994
1092
  end
995
1093
  ```
1094
+
996
1095
  **Conclusion**
997
1096
 
998
1097
  For creating instance methods that are meant to be read-only and not stored within a context hash, defining these methods as private is a more suitable and clear approach compared to using context_reader with a default. This method ensures that transient dependencies remain well-encapsulated and are not confused with persistent application state.
@@ -1008,6 +1107,7 @@ The `context|params|depenencies` helper method is designed to enable easy access
1008
1107
  **default (Proc, optional):** A lambda or proc that returns a default value for the key if it is not present in the context hash. This proc is lazily evaluated only when the getter is invoked and the key is not present in the hash.
1009
1108
 
1010
1109
  #### Usage
1110
+
1011
1111
  ```ruby
1012
1112
  context do
1013
1113
  attr_accessor :profile
@@ -1038,7 +1138,9 @@ end
1038
1138
  ```
1039
1139
 
1040
1140
  #### Other methods
1141
+
1041
1142
  >
1143
+
1042
1144
  - step(Symbol) - single instruction
1043
1145
  - return [Truthly] - continue operation execution
1044
1146
  - return [False] - stops operation execution
@@ -1049,6 +1151,8 @@ end
1049
1151
  - transaction(*Symbols) - list of instructions to be wrapped in transaction
1050
1152
  - return [Truthly] - continue operation execution
1051
1153
  - return [False] - stops operation execution and breaks transaction/do rollback
1154
+ - within(Symbol, &block) - wraps nested steps with a custom method that must yield
1155
+ - the named method receives a block and must yield to execute the nested steps
1052
1156
  - call(params: Hash, dependencies: Hash?)
1053
1157
  - return [Opera::Operation::Result]
1054
1158
 
@@ -1062,7 +1166,6 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
1062
1166
 
1063
1167
  Bug reports and pull requests are welcome on GitHub at https://github.com/profinda/opera. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/opera/blob/master/CODE_OF_CONDUCT.md).
1064
1168
 
1065
-
1066
1169
  ## License
1067
1170
 
1068
1171
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec)
5
7
 
6
- task :default => :spec
8
+ task default: :spec
data/bin/console CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require "bundler/setup"
4
- require "opera"
4
+ require 'bundler/setup'
5
+ require 'opera'
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +11,5 @@ require "opera"
10
11
  # require "pry"
11
12
  # Pry.start
12
13
 
13
- require "irb"
14
+ require 'irb'
14
15
  IRB.start(__FILE__)
data/lib/opera/errors.rb CHANGED
@@ -2,5 +2,6 @@
2
2
 
3
3
  module Opera
4
4
  class Error < StandardError; end
5
+
5
6
  class UnknownInstructionError < Error; end
6
7
  end
@@ -12,7 +12,10 @@ module Opera
12
12
  end
13
13
 
14
14
  def attr_reader(*attributes, **options)
15
- raise NoMethodError, "You cannot use attr_reader inside #{klass.name}##{block_name}" unless allowed.include?(:attr_reader)
15
+ unless allowed.include?(:attr_reader)
16
+ raise NoMethodError,
17
+ "You cannot use attr_reader inside #{klass.name}##{block_name}"
18
+ end
16
19
 
17
20
  attributes.each do |attribute|
18
21
  klass.check_method_availability!(attribute)
@@ -20,10 +23,10 @@ module Opera
20
23
  method = block_name
21
24
  klass.define_method(attribute) do
22
25
  value = if send(method).key?(attribute)
23
- send(method)[attribute]
24
- elsif options[:default]
25
- instance_exec(&options[:default])
26
- end
26
+ send(method)[attribute]
27
+ elsif options[:default]
28
+ instance_exec(&options[:default])
29
+ end
27
30
 
28
31
  if send(method).frozen?
29
32
  send(method)[attribute] || value
@@ -35,7 +38,10 @@ module Opera
35
38
  end
36
39
 
37
40
  def attr_writer(*attributes, **options)
38
- raise NoMethodError, "You cannot use attr_writer inside #{klass.name}##{block_name}" unless allowed.include?(:attr_accessor)
41
+ unless allowed.include?(:attr_accessor)
42
+ raise NoMethodError,
43
+ "You cannot use attr_writer inside #{klass.name}##{block_name}"
44
+ end
39
45
 
40
46
  attributes.each do |attribute|
41
47
  klass.check_method_availability!("#{attribute}=")
@@ -47,7 +53,10 @@ module Opera
47
53
  end
48
54
 
49
55
  def attr_accessor(*attributes, **options)
50
- raise NoMethodError, "You cannot use attr_accessor inside #{klass.name}##{block_name}" unless allowed.include?(:attr_accessor)
56
+ unless allowed.include?(:attr_accessor)
57
+ raise NoMethodError,
58
+ "You cannot use attr_accessor inside #{klass.name}##{block_name}"
59
+ end
51
60
 
52
61
  attr_reader(*attributes, **options)
53
62
  attr_writer(*attributes)
@@ -33,7 +33,7 @@ module Opera
33
33
  def call(args = {})
34
34
  operation = new(params: args.fetch(:params, {}), dependencies: args.fetch(:dependencies, {}))
35
35
  executor = Executor.new(operation)
36
- Instrumentation.new(operation).instrument(name: self.name, level: :operation) do
36
+ Instrumentation.new(operation).instrument(name: name, level: :operation) do
37
37
  executor.evaluate_instructions(instructions)
38
38
  end
39
39
  executor.result
@@ -58,7 +58,8 @@ module Opera
58
58
  end
59
59
 
60
60
  def context(&blk)
61
- AttributesDSL.new(klass: self, block_name: :context, allowed: [:attr_reader, :attr_accessor]).instance_exec(&blk)
61
+ AttributesDSL.new(klass: self, block_name: :context,
62
+ allowed: [:attr_reader, :attr_accessor]).instance_exec(&blk)
62
63
  end
63
64
 
64
65
  def params(&blk)
@@ -73,7 +74,7 @@ module Opera
73
74
  %i[context params dependencies].each do |method|
74
75
  define_method("#{method}_reader") do |*attributes, **options|
75
76
  send(method) do
76
- attr_reader *attributes, **options
77
+ attr_reader(*attributes, **options)
77
78
  end
78
79
  end
79
80
  end
@@ -81,14 +82,14 @@ module Opera
81
82
  %i[context].each do |method|
82
83
  define_method("#{method}_writer") do |*attributes|
83
84
  send(method) do
84
- attr_writer *attributes
85
+ attr_writer(*attributes)
85
86
  end
86
87
  end
87
88
 
88
89
  define_method("#{method}_accessor") do |*attributes, **options|
89
90
  send(method) do
90
- attr_reader *attributes, **options
91
- attr_writer *attributes, **options
91
+ attr_reader(*attributes, **options)
92
+ attr_writer(*attributes, **options)
92
93
  end
93
94
  end
94
95
  end
@@ -3,7 +3,7 @@
3
3
  module Opera
4
4
  module Operation
5
5
  module Builder
6
- INSTRUCTIONS = %I[validate transaction benchmark step success finish_if operation operations].freeze
6
+ INSTRUCTIONS = %I[validate transaction step success finish_if operation operations within].freeze
7
7
 
8
8
  def self.included(base)
9
9
  base.extend(ClassMethods)
@@ -16,7 +16,7 @@ module Opera
16
16
 
17
17
  INSTRUCTIONS.each do |instruction|
18
18
  define_method instruction do |method = nil, &blk|
19
- self.check_method_availability!(method) if method
19
+ check_method_availability!(method) if method
20
20
  instructions.concat(InnerBuilder.new.send(instruction, method, &blk))
21
21
  end
22
22
  end
@@ -34,7 +34,7 @@ module Opera
34
34
 
35
35
  def validate!
36
36
  unless [DEVELOPMENT_MODE, PRODUCTION_MODE].include?(mode)
37
- raise ArgumentError, 'Mode is incorrect. Can be either: development or production'
37
+ raise ArgumentError, 'Mode is incorrect. Can be either: development or production'
38
38
  end
39
39
  end
40
40
 
@@ -44,10 +44,10 @@ module Opera
44
44
  Instructions::Executors::Validate.new(operation).call(instruction)
45
45
  when :transaction
46
46
  Instructions::Executors::Transaction.new(operation).call(instruction)
47
- when :benchmark
48
- Instructions::Executors::Benchmark.new(operation).call(instruction)
49
47
  when :finish_if
50
48
  Instructions::Executors::FinishIf.new(operation).call(instruction)
49
+ when :within
50
+ Instructions::Executors::Within.new(operation).call(instruction)
51
51
  else
52
52
  raise(UnknownInstructionError, "Unknown instruction #{instruction[:kind]}")
53
53
  end
@@ -79,7 +79,7 @@ module Opera
79
79
  end
80
80
 
81
81
  def add_instruction_output(instruction, output = {})
82
- context["#{instruction[:method]}_output".to_sym] = output
82
+ context[:"#{instruction[:method]}_output"] = output
83
83
  end
84
84
  end
85
85
  end