light-service 0.15.0 → 0.18.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/project-build.yml +28 -0
  3. data/.rubocop.yml +117 -3
  4. data/.travis.yml +3 -8
  5. data/README.md +89 -37
  6. data/RELEASES.md +18 -1
  7. data/lib/generators/light_service/generator_utils.rb +0 -2
  8. data/lib/light-service/action.rb +61 -4
  9. data/lib/light-service/context/key_verifier.rb +22 -3
  10. data/lib/light-service/context.rb +10 -12
  11. data/lib/light-service/errors.rb +5 -0
  12. data/lib/light-service/orchestrator.rb +1 -1
  13. data/lib/light-service/organizer/reduce_case.rb +48 -0
  14. data/lib/light-service/organizer/reduce_if_else.rb +21 -0
  15. data/lib/light-service/organizer/with_reducer.rb +11 -14
  16. data/lib/light-service/organizer/with_reducer_log_decorator.rb +2 -2
  17. data/lib/light-service/organizer.rb +20 -3
  18. data/lib/light-service/testing/context_factory.rb +2 -0
  19. data/lib/light-service/version.rb +1 -1
  20. data/lib/light-service.rb +2 -0
  21. data/light-service.gemspec +3 -2
  22. data/spec/acceptance/after_actions_spec.rb +17 -6
  23. data/spec/acceptance/around_each_spec.rb +15 -0
  24. data/spec/acceptance/before_actions_spec.rb +3 -9
  25. data/spec/acceptance/log_from_organizer_spec.rb +1 -1
  26. data/spec/acceptance/organizer/add_to_context_spec.rb +27 -0
  27. data/spec/acceptance/organizer/execute_with_add_to_context_spec.rb +28 -0
  28. data/spec/acceptance/organizer/reduce_case_spec.rb +53 -0
  29. data/spec/acceptance/organizer/reduce_if_else_spec.rb +60 -0
  30. data/spec/acceptance/organizer/reduce_if_spec.rb +2 -0
  31. data/spec/action_optional_expected_keys_spec.rb +82 -0
  32. data/spec/context/inspect_spec.rb +5 -21
  33. data/spec/context_spec.rb +1 -1
  34. data/spec/lib/generators/action_generator_advanced_spec.rb +1 -1
  35. data/spec/lib/generators/action_generator_simple_spec.rb +1 -1
  36. data/spec/lib/generators/organizer_generator_advanced_spec.rb +1 -1
  37. data/spec/lib/generators/organizer_generator_simple_spec.rb +1 -1
  38. data/spec/sample/calculates_tax_spec.rb +0 -1
  39. data/spec/sample/looks_up_tax_percentage_action_spec.rb +3 -1
  40. data/spec/test_doubles.rb +48 -5
  41. metadata +22 -13
  42. data/gemfiles/activesupport_4.gemfile +0 -7
  43. data/resources/orchestrators_deprecated.svg +0 -10
@@ -6,10 +6,12 @@ module LightService
6
6
  FAILURE = 1
7
7
  end
8
8
 
9
- # rubocop:disable ClassLength
9
+ # rubocop:disable Metrics/ClassLength
10
10
  class Context < Hash
11
- attr_accessor :message, :error_code, :current_action, :organized_by
11
+ attr_accessor :message, :error_code, :current_action, :around_actions,
12
+ :organized_by
12
13
 
14
+ # rubocop:disable Metrics/ParameterLists, Lint/MissingSuper
13
15
  def initialize(context = {},
14
16
  outcome = Outcomes::SUCCESS,
15
17
  message = '',
@@ -21,6 +23,7 @@ module LightService
21
23
 
22
24
  context.to_hash.each { |k, v| self[k] = v }
23
25
  end
26
+ # rubocop:enable Metrics/ParameterLists, Lint/MissingSuper
24
27
 
25
28
  def self.make(context = {})
26
29
  unless context.is_a?(Hash) || context.is_a?(LightService::Context)
@@ -115,9 +118,9 @@ module LightService
115
118
  end
116
119
 
117
120
  def define_accessor_methods_for_keys(keys)
118
- return if keys.nil?
121
+ return if keys.blank?
119
122
 
120
- keys.each do |key|
123
+ Array(keys).each do |key|
121
124
  next if respond_to?(key.to_sym)
122
125
 
123
126
  define_singleton_method(key.to_s) { fetch(key) }
@@ -151,13 +154,8 @@ module LightService
151
154
  end
152
155
 
153
156
  def inspect
154
- "#{self.class}(#{self}, " \
155
- + "success: #{success?}, " \
156
- + "message: #{check_nil(message)}, " \
157
- + "error_code: #{check_nil(error_code)}, " \
158
- + "skip_remaining: #{@skip_remaining}, " \
159
- + "aliases: #{@aliases}" \
160
- + ")"
157
+ "#{self.class}(#{self}, success: #{success?}, message: #{check_nil(message)}, error_code: " \
158
+ "#{check_nil(error_code)}, skip_remaining: #{@skip_remaining}, aliases: #{@aliases})"
161
159
  end
162
160
 
163
161
  private
@@ -168,5 +166,5 @@ module LightService
168
166
  "'#{value}'"
169
167
  end
170
168
  end
171
- # rubocop:enable ClassLength
169
+ # rubocop:enable Metrics/ClassLength
172
170
  end
@@ -1,6 +1,11 @@
1
1
  module LightService
2
2
  class FailWithRollbackError < StandardError; end
3
+
3
4
  class ExpectedKeysNotInContextError < StandardError; end
5
+
4
6
  class PromisedKeysNotInContextError < StandardError; end
7
+
5
8
  class ReservedKeysInContextError < StandardError; end
9
+
10
+ class UnusableExpectKeyDefaultError < StandardError; end
6
11
  end
@@ -115,7 +115,7 @@ module LightService
115
115
 
116
116
  def issue_deprecation_warning_for(method_name)
117
117
  msg = "`Orchestrator##{method_name}` is DEPRECATED and will be " \
118
- "removed, please switch to `Organizer##{method_name} instead. "
118
+ "removed, please switch to `Organizer##{method_name} instead. "
119
119
  ActiveSupport::Deprecation.warn(msg)
120
120
  end
121
121
  end
@@ -0,0 +1,48 @@
1
+ module LightService
2
+ module Organizer
3
+ class ReduceCase
4
+ extend ScopedReducable
5
+
6
+ class Arguments
7
+ attr_reader :value, :when, :else
8
+
9
+ def initialize(**args)
10
+ validate_arguments(**args)
11
+ @value = args[:value]
12
+ @when = args[:when]
13
+ @else = args[:else]
14
+ end
15
+
16
+ private
17
+
18
+ # rubocop:disable Style/MultilineIfModifier
19
+ def validate_arguments(**args)
20
+ raise(
21
+ ArgumentError,
22
+ "Expected keyword arguments: [:value, :when, :else]. Given: #{args.keys}"
23
+ ) unless args.keys.intersection(mandatory_arguments).count == mandatory_arguments.count
24
+ end
25
+ # rubocop:enable Style/MultilineIfModifier
26
+
27
+ def mandatory_arguments
28
+ %i[value when else]
29
+ end
30
+ end
31
+
32
+ def self.run(organizer, **args)
33
+ arguments = Arguments.new(**args)
34
+
35
+ lambda do |ctx|
36
+ return ctx if ctx.stop_processing?
37
+
38
+ matched_case = arguments.when.keys.find { |k| k.eql?(ctx[arguments.value]) }
39
+ steps = arguments.when[matched_case] || arguments.else
40
+
41
+ ctx = scoped_reduce(organizer, ctx, steps)
42
+
43
+ ctx
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,21 @@
1
+ module LightService
2
+ module Organizer
3
+ class ReduceIfElse
4
+ extend ScopedReducable
5
+
6
+ def self.run(organizer, condition_block, if_steps, else_steps)
7
+ lambda do |ctx|
8
+ return ctx if ctx.stop_processing?
9
+
10
+ ctx = if condition_block.call(ctx)
11
+ scoped_reduce(organizer, ctx, if_steps)
12
+ else
13
+ scoped_reduce(organizer, ctx, else_steps)
14
+ end
15
+
16
+ ctx
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -30,17 +30,16 @@ module LightService
30
30
  def reduce(*actions)
31
31
  raise "No action(s) were provided" if actions.empty?
32
32
 
33
+ @context.around_actions ||= around_each_handler
33
34
  actions.flatten!
34
35
 
35
36
  actions.each_with_object(context) do |action, current_context|
36
- begin
37
- invoke_action(current_context, action)
38
- rescue FailWithRollbackError
39
- reduce_rollback(actions)
40
- ensure
41
- # For logging
42
- yield(current_context, action) if block_given?
43
- end
37
+ invoke_action(current_context, action)
38
+ rescue FailWithRollbackError
39
+ reduce_rollback(actions)
40
+ ensure
41
+ # For logging
42
+ yield(current_context, action) if block_given?
44
43
  end
45
44
  end
46
45
 
@@ -59,12 +58,10 @@ module LightService
59
58
  private
60
59
 
61
60
  def invoke_action(current_context, action)
62
- around_each_handler.call(current_context) do
63
- if action.respond_to?(:call)
64
- action.call(current_context)
65
- else
66
- action.execute(current_context)
67
- end
61
+ if action.respond_to?(:call)
62
+ action.call(current_context)
63
+ else
64
+ action.execute(current_context)
68
65
  end
69
66
  end
70
67
 
@@ -5,7 +5,7 @@ module LightService
5
5
 
6
6
  alias logged? logged
7
7
 
8
- def initialize(organizer, decorated: WithReducer.new, logger:)
8
+ def initialize(organizer, logger:, decorated: WithReducer.new)
9
9
  @decorated = decorated
10
10
  @organizer = organizer
11
11
 
@@ -22,7 +22,7 @@ module LightService
22
22
 
23
23
  logger.info do
24
24
  "[LightService] - keys in context: " \
25
- "#{extract_keys(decorated.context.keys)}"
25
+ "#{extract_keys(decorated.context.keys)}"
26
26
  end
27
27
  self
28
28
  end
@@ -41,10 +41,18 @@ module LightService
41
41
  ReduceIf.run(self, condition_block, steps)
42
42
  end
43
43
 
44
+ def reduce_if_else(condition_block, if_steps, else_steps)
45
+ ReduceIfElse.run(self, condition_block, if_steps, else_steps)
46
+ end
47
+
44
48
  def reduce_until(condition_block, steps)
45
49
  ReduceUntil.run(self, condition_block, steps)
46
50
  end
47
51
 
52
+ def reduce_case(**args)
53
+ ReduceCase.run(self, **args)
54
+ end
55
+
48
56
  def iterate(collection_key, steps)
49
57
  Iterate.run(self, collection_key, steps)
50
58
  end
@@ -65,9 +73,18 @@ module LightService
65
73
  @logger
66
74
  end
67
75
 
68
- def add_to_context(**args)
69
- args.map do |key, value|
70
- execute(->(ctx) { ctx[key.to_sym] = value })
76
+ # Set the value as a key on the context hash
77
+ # and also create convenience accessors for the keys
78
+ def add_to_context(args)
79
+ Context::ReservedKeysViaOrganizerVerifier.new(args).verify
80
+
81
+ Hash(args).map do |key, value|
82
+ context_key = lambda do |ctx|
83
+ ctx[key.to_sym] = value
84
+ ctx.define_accessor_methods_for_keys(key)
85
+ end
86
+
87
+ execute(context_key)
71
88
  end
72
89
  end
73
90
 
@@ -26,11 +26,13 @@ module LightService
26
26
 
27
27
  # More than one arguments can be passed to the
28
28
  # Organizer's #call method
29
+ # rubocop:disable Style/ArgumentsForwarding
29
30
  def with(*args, &block)
30
31
  catch(:return_ctx_from_execution) do
31
32
  @organizer.call(*args, &block)
32
33
  end
33
34
  end
35
+ # rubocop:enable Style/ArgumentsForwarding
34
36
 
35
37
  def initialize(organizer)
36
38
  @organizer = organizer
@@ -1,3 +1,3 @@
1
1
  module LightService
2
- VERSION = "0.15.0".freeze
2
+ VERSION = "0.18.0".freeze
3
3
  end
data/lib/light-service.rb CHANGED
@@ -13,7 +13,9 @@ require 'light-service/organizer/with_reducer'
13
13
  require 'light-service/organizer/with_reducer_log_decorator'
14
14
  require 'light-service/organizer/with_reducer_factory'
15
15
  require 'light-service/organizer/reduce_if'
16
+ require 'light-service/organizer/reduce_if_else'
16
17
  require 'light-service/organizer/reduce_until'
18
+ require 'light-service/organizer/reduce_case'
17
19
  require 'light-service/organizer/iterate'
18
20
  require 'light-service/organizer/execute'
19
21
  require 'light-service/organizer/with_callback'
@@ -15,8 +15,9 @@ Gem::Specification.new do |gem|
15
15
  gem.name = "light-service"
16
16
  gem.require_paths = ["lib"]
17
17
  gem.version = LightService::VERSION
18
+ gem.required_ruby_version = '>= 2.6.0'
18
19
 
19
- gem.add_runtime_dependency("activesupport", ">= 3.0.0")
20
+ gem.add_runtime_dependency("activesupport", ">= 4.0.0")
20
21
 
21
22
  gem.add_development_dependency("generator_spec", "~> 0.9.4")
22
23
  gem.add_development_dependency("test-unit", "~> 3.0") # Needed for generator specs.
@@ -24,7 +25,7 @@ Gem::Specification.new do |gem|
24
25
  gem.add_development_dependency("rspec", "~> 3.0")
25
26
  gem.add_development_dependency("simplecov", "~> 0.17")
26
27
  gem.add_development_dependency("codecov", "~> 0.1")
27
- gem.add_development_dependency("rubocop", "~> 0.68.0")
28
+ gem.add_development_dependency("rubocop", "~> 1.26.0")
28
29
  gem.add_development_dependency("rubocop-performance", "~> 1.2.0")
29
30
  gem.add_development_dependency("pry", "~> 0.12.2")
30
31
  end
@@ -34,14 +34,10 @@ RSpec.describe 'Action after_actions' do
34
34
  class AdditionOrganizer
35
35
  extend LightService::Organizer
36
36
  after_actions (lambda do |ctx|
37
- if ctx.current_action == TestDoubles::AddsOneAction
38
- ctx.number -= 2
39
- end
37
+ ctx.number -= 2 if ctx.current_action == TestDoubles::AddsOneAction
40
38
  end),
41
39
  (lambda do |ctx|
42
- if ctx.current_action == TestDoubles::AddsThreeAction
43
- ctx.number -= 3
44
- end
40
+ ctx.number -= 3 if ctx.current_action == TestDoubles::AddsThreeAction
45
41
  end)
46
42
 
47
43
  def self.call(number)
@@ -65,6 +61,21 @@ RSpec.describe 'Action after_actions' do
65
61
  end
66
62
  end
67
63
 
64
+ context 'with callbacks' do
65
+ it 'ensures the correct :current_action is set' do
66
+ TestDoubles::TestWithCallback.after_actions = [
67
+ lambda do |ctx|
68
+ ctx.total -= 1000 if ctx.current_action == TestDoubles::IterateCollectionAction
69
+ end
70
+ ]
71
+
72
+ result = TestDoubles::TestWithCallback.call
73
+
74
+ expect(result.counter).to eq(3)
75
+ expect(result.total).to eq(-994)
76
+ end
77
+ end
78
+
68
79
  describe 'after_actions can be appended' do
69
80
  it 'adds to the :_after_actions collection' do
70
81
  TestDoubles::AdditionOrganizer.append_after_actions(
@@ -16,4 +16,19 @@ describe 'Executing arbitrary code around each action' do
16
16
  }]
17
17
  )
18
18
  end
19
+
20
+ it 'logs data with nested actions' do
21
+ context = { :number => 1, :logger => TestDoubles::TestLogger.new }
22
+
23
+ result = TestDoubles::AroundEachWithReduceIfOrganizer.call(context)
24
+
25
+ expect(result.fetch(:number)).to eq(7)
26
+ expect(result[:logger].logs).to eq(
27
+ [
28
+ { :action => TestDoubles::AddsOneAction, :before => 1, :after => 2 },
29
+ { :action => TestDoubles::AddsTwoAction, :before => 2, :after => 4 },
30
+ { :action => TestDoubles::AddsThreeAction, :before => 4, :after => 7 }
31
+ ]
32
+ )
33
+ end
19
34
  end
@@ -34,14 +34,10 @@ RSpec.describe 'Action before_actions' do
34
34
  class AdditionOrganizer
35
35
  extend LightService::Organizer
36
36
  before_actions (lambda do |ctx|
37
- if ctx.current_action == TestDoubles::AddsOneAction
38
- ctx.number -= 2
39
- end
37
+ ctx.number -= 2 if ctx.current_action == TestDoubles::AddsOneAction
40
38
  end),
41
39
  (lambda do |ctx|
42
- if ctx.current_action == TestDoubles::AddsThreeAction
43
- ctx.number -= 3
44
- end
40
+ ctx.number -= 3 if ctx.current_action == TestDoubles::AddsThreeAction
45
41
  end)
46
42
 
47
43
  def self.call(number)
@@ -69,9 +65,7 @@ RSpec.describe 'Action before_actions' do
69
65
  it 'can interact with actions from the outside' do
70
66
  TestDoubles::TestWithCallback.before_actions = [
71
67
  lambda do |ctx|
72
- if ctx.current_action == TestDoubles::AddToTotalAction
73
- ctx.total -= 1000
74
- end
68
+ ctx.total -= 1000 if ctx.current_action == TestDoubles::AddToTotalAction
75
69
  end
76
70
  ]
77
71
  result = TestDoubles::TestWithCallback.call
@@ -59,7 +59,7 @@ describe "Logs from organizer" do
59
59
  expect(log_message).to include(organizer_log_message)
60
60
  end
61
61
 
62
- it "lists the keys in contect after the actions are executed" do
62
+ it "lists the keys in context after the actions are executed" do
63
63
  organizer_log_message = "[LightService] - keys in context: " \
64
64
  ":tea, :milk, :coffee, :milk_tea, :latte"
65
65
  expect(log_message).to include(organizer_log_message)
@@ -20,6 +20,20 @@ RSpec.describe LightService::Organizer do
20
20
  end
21
21
  end
22
22
 
23
+ class TestAddToContextReservedWords
24
+ extend LightService::Organizer
25
+
26
+ def self.call(context = LightService::Context.make)
27
+ with(context).reduce(steps)
28
+ end
29
+
30
+ def self.steps
31
+ [
32
+ add_to_context(:message => "yo", "error_code" => "00P5")
33
+ ]
34
+ end
35
+ end
36
+
23
37
  it 'adds items to the context on the fly' do
24
38
  result = TestAddToContext.call
25
39
 
@@ -27,4 +41,17 @@ RSpec.describe LightService::Organizer do
27
41
  expect(result.number).to eq(1)
28
42
  expect(result[:something]).to eq('hello')
29
43
  end
44
+
45
+ it 'adds items to the context as accessors' do
46
+ result = TestAddToContext.call
47
+
48
+ expect(result).to be_success
49
+ expect(result.something).to eq('hello')
50
+ end
51
+
52
+ it "will not add items as accessors when they are reserved" do
53
+ expect { TestAddToContextReservedWords.call }.to \
54
+ raise_error(LightService::ReservedKeysInContextError)
55
+ .with_message(/:message, :error_code/)
56
+ end
30
57
  end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ RSpec.describe LightService::Organizer do
5
+ class TestExecuteWithAddToContext
6
+ extend LightService::Organizer
7
+
8
+ def self.call
9
+ with.reduce(steps)
10
+ end
11
+
12
+ def self.steps
13
+ [
14
+ add_to_context(:greeting => "hello"),
15
+ execute(->(ctx) { ctx.greeting.upcase! })
16
+ ]
17
+ end
18
+ end
19
+
20
+ context "when using context values created by add_to_context" do
21
+ it "is expected to reference them as accessors" do
22
+ result = TestExecuteWithAddToContext.call
23
+
24
+ expect(result).to be_a_success
25
+ expect(result.greeting).to eq "HELLO"
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ RSpec.describe LightService::Organizer do
5
+ class TestReduceCase
6
+ extend LightService::Organizer
7
+
8
+ def self.call(context)
9
+ with(context).reduce(actions)
10
+ end
11
+
12
+ def self.actions
13
+ [
14
+ reduce_case(
15
+ :value => :incr_num,
16
+ :when => {
17
+ :one => [TestDoubles::AddsOneAction],
18
+ :two => [TestDoubles::AddsTwoAction],
19
+ :three => [TestDoubles::AddsThreeAction]
20
+ },
21
+ :else => [TestDoubles::FailureAction]
22
+ )
23
+ ]
24
+ end
25
+ end
26
+
27
+ it 'adds one if the incr_num is one' do
28
+ result = TestReduceCase.call(:number => 0, :incr_num => :one)
29
+
30
+ expect(result).to be_success
31
+ expect(result[:number]).to eq(1)
32
+ end
33
+
34
+ it 'adds two if the incr_num is two' do
35
+ result = TestReduceCase.call(:number => 0, :incr_num => :two)
36
+
37
+ expect(result).to be_success
38
+ expect(result[:number]).to eq(2)
39
+ end
40
+
41
+ it 'adds three if the incr_num is three' do
42
+ result = TestReduceCase.call(:number => 0, :incr_num => :three)
43
+
44
+ expect(result).to be_success
45
+ expect(result[:number]).to eq(3)
46
+ end
47
+
48
+ it 'will fail if the incr_num is neither one, two, or three' do
49
+ result = TestReduceCase.call(:number => 0, :incr_num => :four)
50
+
51
+ expect(result).to be_failure
52
+ end
53
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ RSpec.describe LightService::Organizer do
5
+ class TestReduceIfElse
6
+ extend LightService::Organizer
7
+
8
+ def self.call(context)
9
+ with(context).reduce(actions)
10
+ end
11
+
12
+ def self.actions
13
+ [
14
+ TestDoubles::AddsOneAction,
15
+ reduce_if_else(
16
+ ->(ctx) { ctx.number == 1 },
17
+ [TestDoubles::AddsOneAction],
18
+ [TestDoubles::AddsTwoAction]
19
+ )
20
+ ]
21
+ end
22
+ end
23
+
24
+ let(:empty_context) { LightService::Context.make }
25
+
26
+ it 'reduces the if_steps if the condition is true' do
27
+ result = TestReduceIfElse.call(:number => 0)
28
+
29
+ expect(result).to be_success
30
+ expect(result[:number]).to eq(2)
31
+ end
32
+
33
+ it 'reduces the else_steps if the condition is false' do
34
+ result = TestReduceIfElse.call(:number => 2)
35
+
36
+ expect(result).to be_success
37
+ expect(result[:number]).to eq(5)
38
+ end
39
+
40
+ it 'will not reduce over a failed context' do
41
+ empty_context.fail!('Something bad happened')
42
+
43
+ result = TestReduceIfElse.call(empty_context)
44
+
45
+ expect(result).to be_failure
46
+ end
47
+
48
+ it 'does not reduce over a skipped context' do
49
+ empty_context.skip_remaining!('No more needed')
50
+
51
+ result = TestReduceIfElse.call(empty_context)
52
+ expect(result).to be_success
53
+ end
54
+
55
+ it "knows that it's being conditionally reduced from within an organizer" do
56
+ result = TestReduceIfElse.call(:number => 2)
57
+
58
+ expect(result.organized_by).to eq TestReduceIfElse
59
+ end
60
+ end
@@ -63,6 +63,7 @@ RSpec.describe LightService::Organizer do
63
63
  reduce(actions)
64
64
  end
65
65
 
66
+ # rubocop:disable Metrics/AbcSize
66
67
  def self.actions
67
68
  [
68
69
  reduce_if(
@@ -76,6 +77,7 @@ RSpec.describe LightService::Organizer do
76
77
  execute(->(c) { c[:last_outside] = true })
77
78
  ]
78
79
  end
80
+ # rubocop:enable Metrics/AbcSize
79
81
  end
80
82
 
81
83
  result = org.call