opera 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@
3
3
  module 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
@@ -17,7 +17,7 @@ module Opera
17
17
  @instrumentation_class = self.class.instrumentation_class
18
18
 
19
19
  @mode = self.class.mode || DEVELOPMENT_MODE
20
- @reporter = custom_reporter || self.class.reporter
20
+ @reporter = self.class.reporter
21
21
 
22
22
  validate!
23
23
  end
@@ -26,15 +26,11 @@ module Opera
26
26
  yield self
27
27
  end
28
28
 
29
- def custom_reporter
30
- Rails.application.config.x.reporter.presence if defined?(Rails)
31
- end
32
-
33
29
  private
34
30
 
35
31
  def validate!
36
32
  unless [DEVELOPMENT_MODE, PRODUCTION_MODE].include?(mode)
37
- raise ArgumentError, 'Mode is incorrect. Can be either: development or production'
33
+ raise ArgumentError, 'Mode is incorrect. Can be either: development or production'
38
34
  end
39
35
  end
40
36
 
@@ -47,7 +43,7 @@ module Opera
47
43
  end
48
44
 
49
45
  def development_mode?
50
- mode == DEFAULT_MODE
46
+ mode == DEVELOPMENT_MODE
51
47
  end
52
48
 
53
49
  def production_mode?
@@ -20,15 +20,24 @@ module Opera
20
20
  end
21
21
 
22
22
  def evaluate_instructions(instructions = [])
23
- instruction_copy = Marshal.load(Marshal.dump(instructions))
24
-
25
- while instruction_copy.any?
26
- instruction = instruction_copy.shift
23
+ instructions.each do |instruction|
27
24
  evaluate_instruction(instruction)
28
25
  break if break_condition
29
26
  end
30
27
  end
31
28
 
29
+ # Executes the operation method named in the instruction, instruments it,
30
+ # and records the execution. This is the shared primitive that all executors
31
+ # use to invoke a step method without mutating the instruction hash.
32
+ def execute_step(instruction)
33
+ method = instruction[:method]
34
+
35
+ Instrumentation.new(operation).instrument(name: "##{method}", level: :step) do
36
+ result.add_execution(method) unless production_mode?
37
+ operation.send(method)
38
+ end
39
+ end
40
+
32
41
  # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
33
42
  def evaluate_instruction(instruction)
34
43
  case instruction[:kind]
@@ -44,10 +53,10 @@ module Opera
44
53
  Instructions::Executors::Validate.new(operation).call(instruction)
45
54
  when :transaction
46
55
  Instructions::Executors::Transaction.new(operation).call(instruction)
47
- when :benchmark
48
- Instructions::Executors::Benchmark.new(operation).call(instruction)
49
56
  when :finish_if
50
57
  Instructions::Executors::FinishIf.new(operation).call(instruction)
58
+ when :within
59
+ Instructions::Executors::Within.new(operation).call(instruction)
51
60
  else
52
61
  raise(UnknownInstructionError, "Unknown instruction #{instruction[:kind]}")
53
62
  end
@@ -79,7 +88,7 @@ module Opera
79
88
  end
80
89
 
81
90
  def add_instruction_output(instruction, output = {})
82
- context["#{instruction[:method]}_output".to_sym] = output
91
+ context[:"#{instruction[:method]}_output"] = output
83
92
  end
84
93
  end
85
94
  end
@@ -6,8 +6,7 @@ module Opera
6
6
  module Executors
7
7
  class FinishIf < Executor
8
8
  def call(instruction)
9
- instruction[:kind] = :step
10
- operation.finish! if super
9
+ operation.finish! if execute_step(instruction)
11
10
  end
12
11
  end
13
12
  end
@@ -6,8 +6,7 @@ module Opera
6
6
  module Executors
7
7
  class Operation < Executor
8
8
  def call(instruction)
9
- instruction[:kind] = :step
10
- operation_result = super
9
+ operation_result = execute_step(instruction)
11
10
  save_information(operation_result)
12
11
 
13
12
  if operation_result.success?
@@ -9,8 +9,7 @@ module Opera
9
9
 
10
10
  # rubocop:disable Metrics/MethodLength
11
11
  def call(instruction)
12
- instruction[:kind] = :step
13
- operations_results = super
12
+ operations_results = execute_step(instruction)
14
13
 
15
14
  case operations_results
16
15
  when Array
@@ -42,7 +41,7 @@ module Opera
42
41
  def add_results(instruction, results)
43
42
  add_instruction_output(instruction, results.map(&:output))
44
43
  execution = result.executions.pop
45
- result.add_execution(execution => results.map(&:executions)) unless production_mode?
44
+ result.add_execution(execution => results.map(&:executions)) unless production_mode?
46
45
  end
47
46
 
48
47
  def raise_error
@@ -6,12 +6,7 @@ module Opera
6
6
  module Executors
7
7
  class Step < Executor
8
8
  def call(instruction)
9
- method = instruction[:method]
10
-
11
- Instrumentation.new(operation).instrument(name: "##{method}", level: :step) do
12
- operation.result.add_execution(method) unless production_mode?
13
- operation.send(method)
14
- end
9
+ execute_step(instruction)
15
10
  end
16
11
  end
17
12
  end
@@ -6,8 +6,11 @@ module Opera
6
6
  module Executors
7
7
  class Success < Executor
8
8
  def call(instruction)
9
- instruction[:kind] = :step
10
- super
9
+ if instruction[:instructions]
10
+ evaluate_instructions(instruction[:instructions])
11
+ else
12
+ execute_step(instruction)
13
+ end
11
14
  end
12
15
 
13
16
  def break_condition
@@ -12,8 +12,7 @@ module Opera
12
12
  private
13
13
 
14
14
  def evaluate_instruction(instruction)
15
- instruction[:kind] = :step
16
- validation_result = super
15
+ validation_result = execute_step(instruction)
17
16
 
18
17
  case validation_result
19
18
  when Opera::Operation::Result
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Opera
4
+ module Operation
5
+ module Instructions
6
+ module Executors
7
+ class Within < Executor
8
+ def call(instruction)
9
+ wrapper_method = instruction[:label]
10
+ nested_instructions = instruction[:instructions]
11
+
12
+ raise ArgumentError, 'within requires a method name' unless wrapper_method
13
+ raise ArgumentError, 'within requires a block with at least one instruction' if nested_instructions.nil?
14
+
15
+ operation.send(wrapper_method) do
16
+ evaluate_instructions(nested_instructions)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -9,12 +9,12 @@ require 'opera/operation/result'
9
9
  require 'opera/operation/config'
10
10
  require 'opera/operation/instructions/executors/success'
11
11
  require 'opera/operation/instructions/executors/transaction'
12
- require 'opera/operation/instructions/executors/benchmark'
13
12
  require 'opera/operation/instructions/executors/finish_if'
14
13
  require 'opera/operation/instructions/executors/validate'
15
14
  require 'opera/operation/instructions/executors/operation'
16
15
  require 'opera/operation/instructions/executors/operations'
17
16
  require 'opera/operation/instructions/executors/step'
17
+ require 'opera/operation/instructions/executors/within'
18
18
 
19
19
  module Opera
20
20
  module Operation
data/lib/opera/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Opera
4
- VERSION = '0.4.1'
4
+ VERSION = '0.5.1'
5
5
  end
data/opera.gemspec CHANGED
@@ -1,14 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'lib/opera/version'
2
4
 
3
5
  Gem::Specification.new do |spec|
4
- spec.name = 'opera'
5
- spec.version = Opera::VERSION
6
- spec.authors = ['ProFinda Development Team']
7
- spec.email = ['dev@profinda.com']
6
+ spec.name = 'opera'
7
+ spec.version = Opera::VERSION
8
+ spec.authors = ['ProFinda Development Team']
9
+ spec.email = ['dev@profinda.com']
8
10
 
9
- spec.summary = 'Use simple DSL language to keep your Operations clean and maintainable'
10
- spec.homepage = 'https://github.com/Profinda/opera'
11
- spec.license = 'MIT'
11
+ spec.summary = 'Use simple DSL language to keep your Operations clean and maintainable'
12
+ spec.homepage = 'https://github.com/Profinda/opera'
13
+ spec.license = 'MIT'
12
14
  spec.required_ruby_version = Gem::Requirement.new('>= 3.1.0')
13
15
 
14
16
  spec.metadata['homepage_uri'] = spec.homepage
@@ -17,11 +19,11 @@ Gem::Specification.new do |spec|
17
19
 
18
20
  # Specify which files should be added to the gem when it is released.
19
21
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
22
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
21
23
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
22
24
  end
23
- spec.bindir = 'exe'
24
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
+ spec.bindir = 'exe'
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
27
  spec.require_paths = ['lib']
26
28
 
27
29
  spec.add_development_dependency 'dry-validation', '>= 1.0'
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.4.1
4
+ version: 0.5.1
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: 2026-02-19 00:00:00.000000000 Z
11
+ date: 2026-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-validation
@@ -63,6 +63,7 @@ files:
63
63
  - ".github/workflows/specs.yml"
64
64
  - ".gitignore"
65
65
  - ".rspec"
66
+ - ".rubocop.yml"
66
67
  - ".travis.yml"
67
68
  - CHANGELOG.md
68
69
  - CODE_OF_CONDUCT.md
@@ -72,9 +73,18 @@ files:
72
73
  - LICENSE.txt
73
74
  - README.md
74
75
  - Rakefile
76
+ - benchmarks/operation_benchmark.rb
75
77
  - bin/console
76
78
  - bin/setup
77
79
  - docker-compose.yml
80
+ - docs/examples/basic-operation.md
81
+ - docs/examples/context-params-dependencies.md
82
+ - docs/examples/finish-if.md
83
+ - docs/examples/inner-operations.md
84
+ - docs/examples/success-blocks.md
85
+ - docs/examples/transactions.md
86
+ - docs/examples/validations.md
87
+ - docs/examples/within.md
78
88
  - lib/opera.rb
79
89
  - lib/opera/errors.rb
80
90
  - lib/opera/operation.rb
@@ -83,7 +93,6 @@ files:
83
93
  - lib/opera/operation/builder.rb
84
94
  - lib/opera/operation/config.rb
85
95
  - lib/opera/operation/executor.rb
86
- - lib/opera/operation/instructions/executors/benchmark.rb
87
96
  - lib/opera/operation/instructions/executors/finish_if.rb
88
97
  - lib/opera/operation/instructions/executors/operation.rb
89
98
  - lib/opera/operation/instructions/executors/operations.rb
@@ -91,6 +100,7 @@ files:
91
100
  - lib/opera/operation/instructions/executors/success.rb
92
101
  - lib/opera/operation/instructions/executors/transaction.rb
93
102
  - lib/opera/operation/instructions/executors/validate.rb
103
+ - lib/opera/operation/instructions/executors/within.rb
94
104
  - lib/opera/operation/instrumentation.rb
95
105
  - lib/opera/operation/result.rb
96
106
  - lib/opera/version.rb
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Opera
4
- module Operation
5
- module Instructions
6
- module Executors
7
- class Benchmark < Executor
8
- def call(instruction)
9
- benchmark = ::Benchmark.measure do
10
- instruction[:kind] = :step
11
- super
12
- end
13
-
14
- result.add_information(benchmark_key(instruction) => { real: benchmark.real, total: benchmark.total })
15
- end
16
-
17
- private
18
-
19
- def benchmark_key(instruction)
20
- instruction[:method] || instruction[:label] || instruction[:instructions].map { |e| e[:method] }.join('-').to_sym
21
- end
22
- end
23
- end
24
- end
25
- end
26
- end