light-service 0.10.3 → 0.15.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.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.rubocop.yml +6 -0
- data/.travis.yml +12 -11
- data/Appraisals +4 -4
- data/Gemfile +0 -2
- data/README.md +240 -34
- data/RELEASES.md +20 -1
- data/gemfiles/activesupport_4.gemfile +0 -1
- data/gemfiles/activesupport_5.gemfile +0 -1
- data/gemfiles/{activesupport_3.gemfile → activesupport_6.gemfile} +1 -2
- data/lib/generators/light_service/action_generator.rb +90 -0
- data/lib/generators/light_service/generator_utils.rb +45 -0
- data/lib/generators/light_service/organizer_generator.rb +66 -0
- data/lib/generators/light_service/templates/action_spec_template.erb +31 -0
- data/lib/generators/light_service/templates/action_template.erb +30 -0
- data/lib/generators/light_service/templates/organizer_spec_template.erb +20 -0
- data/lib/generators/light_service/templates/organizer_template.erb +22 -0
- data/lib/light-service.rb +1 -0
- data/lib/light-service/context.rb +6 -2
- data/lib/light-service/localization_adapter.rb +1 -1
- data/lib/light-service/organizer.rb +18 -0
- data/lib/light-service/organizer/with_reducer.rb +11 -6
- data/lib/light-service/organizer/with_reducer_factory.rb +11 -7
- data/lib/light-service/organizer/with_reducer_log_decorator.rb +5 -2
- data/lib/light-service/version.rb +1 -1
- data/light-service.gemspec +9 -4
- data/spec/acceptance/custom_log_from_organizer_spec.rb +60 -0
- data/spec/acceptance/fail_spec.rb +42 -16
- data/spec/acceptance/organizer/add_aliases_spec.rb +28 -0
- data/spec/acceptance/organizer/add_to_context_spec.rb +30 -0
- data/spec/acceptance/organizer/execute_spec.rb +1 -1
- data/spec/acceptance/organizer/iterate_spec.rb +7 -0
- data/spec/acceptance/organizer/reduce_if_spec.rb +38 -0
- data/spec/acceptance/organizer/reduce_until_spec.rb +6 -0
- data/spec/action_spec.rb +8 -0
- data/spec/lib/generators/action_generator_advanced_spec.rb +43 -0
- data/spec/lib/generators/action_generator_simple_spec.rb +37 -0
- data/spec/lib/generators/full_generator_test_blobs.rb +193 -0
- data/spec/lib/generators/organizer_generator_advanced_spec.rb +37 -0
- data/spec/lib/generators/organizer_generator_simple_spec.rb +37 -0
- data/spec/organizer_spec.rb +42 -14
- data/spec/sample/provides_free_shipping_action_spec.rb +1 -1
- data/spec/spec_helper.rb +7 -2
- data/spec/test_doubles.rb +77 -0
- metadata +104 -15
- data/gemfiles/activesupport_3.gemfile.lock +0 -76
- data/gemfiles/activesupport_4.gemfile.lock +0 -82
- data/gemfiles/activesupport_5.gemfile.lock +0 -82
@@ -1,10 +1,16 @@
|
|
1
1
|
module LightService
|
2
2
|
module Organizer
|
3
3
|
class WithReducer
|
4
|
-
attr_reader
|
4
|
+
attr_reader :context
|
5
|
+
attr_accessor :organizer
|
6
|
+
|
7
|
+
def initialize(monitored_organizer = nil)
|
8
|
+
@organizer = monitored_organizer
|
9
|
+
end
|
5
10
|
|
6
11
|
def with(data = {})
|
7
12
|
@context = LightService::Context.make(data)
|
13
|
+
@context.organized_by = organizer
|
8
14
|
self
|
9
15
|
end
|
10
16
|
|
@@ -23,19 +29,18 @@ module LightService
|
|
23
29
|
|
24
30
|
def reduce(*actions)
|
25
31
|
raise "No action(s) were provided" if actions.empty?
|
32
|
+
|
26
33
|
actions.flatten!
|
27
34
|
|
28
|
-
actions.
|
35
|
+
actions.each_with_object(context) do |action, current_context|
|
29
36
|
begin
|
30
|
-
|
37
|
+
invoke_action(current_context, action)
|
31
38
|
rescue FailWithRollbackError
|
32
|
-
|
39
|
+
reduce_rollback(actions)
|
33
40
|
ensure
|
34
41
|
# For logging
|
35
42
|
yield(current_context, action) if block_given?
|
36
43
|
end
|
37
|
-
|
38
|
-
result
|
39
44
|
end
|
40
45
|
end
|
41
46
|
|
@@ -2,13 +2,17 @@ module LightService
|
|
2
2
|
module Organizer
|
3
3
|
class WithReducerFactory
|
4
4
|
def self.make(monitored_organizer)
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
logger = monitored_organizer.logger ||
|
6
|
+
LightService::Configuration.logger
|
7
|
+
decorated = WithReducer.new(monitored_organizer)
|
8
|
+
|
9
|
+
return decorated if logger.nil?
|
10
|
+
|
11
|
+
WithReducerLogDecorator.new(
|
12
|
+
monitored_organizer,
|
13
|
+
:decorated => decorated,
|
14
|
+
:logger => logger
|
15
|
+
)
|
12
16
|
end
|
13
17
|
end
|
14
18
|
end
|
@@ -5,10 +5,13 @@ module LightService
|
|
5
5
|
|
6
6
|
alias logged? logged
|
7
7
|
|
8
|
-
def initialize(organizer, decorated
|
8
|
+
def initialize(organizer, decorated: WithReducer.new, logger:)
|
9
9
|
@decorated = decorated
|
10
10
|
@organizer = organizer
|
11
|
-
|
11
|
+
|
12
|
+
decorated.organizer = organizer
|
13
|
+
|
14
|
+
@logger = logger
|
12
15
|
@logged = false
|
13
16
|
end
|
14
17
|
|
data/light-service.gemspec
CHANGED
@@ -16,10 +16,15 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.require_paths = ["lib"]
|
17
17
|
gem.version = LightService::VERSION
|
18
18
|
|
19
|
-
gem.
|
19
|
+
gem.add_runtime_dependency("activesupport", ">= 3.0.0")
|
20
20
|
|
21
|
+
gem.add_development_dependency("generator_spec", "~> 0.9.4")
|
22
|
+
gem.add_development_dependency("test-unit", "~> 3.0") # Needed for generator specs.
|
23
|
+
gem.add_development_dependency("appraisal", "~> 2.3")
|
21
24
|
gem.add_development_dependency("rspec", "~> 3.0")
|
22
|
-
gem.add_development_dependency("simplecov", "~> 0.
|
23
|
-
gem.add_development_dependency("
|
24
|
-
gem.add_development_dependency("
|
25
|
+
gem.add_development_dependency("simplecov", "~> 0.17")
|
26
|
+
gem.add_development_dependency("codecov", "~> 0.1")
|
27
|
+
gem.add_development_dependency("rubocop", "~> 0.68.0")
|
28
|
+
gem.add_development_dependency("rubocop-performance", "~> 1.2.0")
|
29
|
+
gem.add_development_dependency("pry", "~> 0.12.2")
|
25
30
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Log from an organizer with a custom logger" do
|
4
|
+
context "when overriding the global LightService organizer" do
|
5
|
+
let(:global_logger_organizer) do
|
6
|
+
Class.new do
|
7
|
+
extend LightService::Organizer
|
8
|
+
|
9
|
+
def self.call(number)
|
10
|
+
with(:number => number).reduce(actions)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.actions
|
14
|
+
[
|
15
|
+
TestDoubles::AddsOneAction,
|
16
|
+
TestDoubles::AddsTwoAction,
|
17
|
+
TestDoubles::AddsThreeAction
|
18
|
+
]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:global_logger_string) { StringIO.new }
|
24
|
+
|
25
|
+
let(:custom_logger_string) { StringIO.new }
|
26
|
+
let(:custom_logger_organizer) do
|
27
|
+
custom_logger = Logger.new(custom_logger_string)
|
28
|
+
|
29
|
+
Class.new do
|
30
|
+
extend LightService::Organizer
|
31
|
+
log_with custom_logger
|
32
|
+
|
33
|
+
def self.call(coffee, this_hot = :very_hot)
|
34
|
+
with(:milk => this_hot, :coffee => coffee)
|
35
|
+
.reduce(TestDoubles::MakesLatteAction,
|
36
|
+
TestDoubles::AddsTwoActionWithFetch)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
before do
|
42
|
+
@original_global_logger = LightService::Configuration.logger
|
43
|
+
LightService::Configuration.logger = Logger.new(global_logger_string)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "logs in own logger" do
|
47
|
+
global_logger_organizer.call(1)
|
48
|
+
custom_logger_organizer.call(:coffee => "Cappucino")
|
49
|
+
|
50
|
+
expect(custom_logger_string.string).to include("MakesLatteAction")
|
51
|
+
expect(custom_logger_string.string).to_not include("AddsOneAction")
|
52
|
+
expect(global_logger_string.string).to include("AddsOneAction")
|
53
|
+
expect(global_logger_string.string).to_not include("MakesLatteAction")
|
54
|
+
end
|
55
|
+
|
56
|
+
after do
|
57
|
+
LightService::Configuration.logger = @original_global_logger
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -1,24 +1,50 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
RSpec.describe "
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
3
|
+
RSpec.describe "fail_and_return!" do
|
4
|
+
describe "returns immediately from executed block" do
|
5
|
+
class FailAndReturnAction
|
6
|
+
extend LightService::Action
|
7
|
+
promises :one, :two
|
8
|
+
|
9
|
+
executed do |ctx|
|
10
|
+
ctx.one = 1
|
11
|
+
# Have to set it in Context
|
12
|
+
ctx.two = nil
|
13
|
+
|
14
|
+
ctx.fail_and_return!('Something went wrong')
|
15
|
+
ctx.two = 2
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it "returns immediately from executed block" do
|
20
|
+
result = FailAndReturnAction.execute
|
21
|
+
|
22
|
+
expect(result).to be_failure
|
23
|
+
expect(result.two).to be_nil
|
15
24
|
end
|
16
25
|
end
|
17
26
|
|
18
|
-
|
19
|
-
|
27
|
+
describe "accepts error_code option" do
|
28
|
+
class FailAndReturnWithErrorCodeAction
|
29
|
+
extend LightService::Action
|
30
|
+
promises :one, :two
|
20
31
|
|
21
|
-
|
22
|
-
|
32
|
+
executed do |ctx|
|
33
|
+
ctx.one = 1
|
34
|
+
# Have to set it in Context
|
35
|
+
ctx.two = nil
|
36
|
+
|
37
|
+
ctx.fail_and_return!('Something went wrong', :error_code => 401)
|
38
|
+
ctx.two = 2
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it "returned context contains the error_code" do
|
43
|
+
result = FailAndReturnWithErrorCodeAction.execute
|
44
|
+
|
45
|
+
expect(result).to be_failure
|
46
|
+
expect(result.error_code).to eq 401
|
47
|
+
expect(result.two).to be_nil
|
48
|
+
end
|
23
49
|
end
|
24
50
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe LightService::Organizer do
|
4
|
+
class TestAddAliases
|
5
|
+
extend LightService::Organizer
|
6
|
+
|
7
|
+
def self.call(context = LightService::Context.make)
|
8
|
+
with(context).reduce(steps)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.steps
|
12
|
+
[
|
13
|
+
add_to_context(:my_message => 'Hello There'),
|
14
|
+
# This will add the alias `:a_message` which points
|
15
|
+
# to the :my_message key's value
|
16
|
+
add_aliases(:my_message => :a_message),
|
17
|
+
TestDoubles::CapitalizeMessage
|
18
|
+
]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'adds aliases to the context embedded in the series of actions' do
|
23
|
+
result = TestAddAliases.call
|
24
|
+
|
25
|
+
expect(result).to be_success
|
26
|
+
expect(result.final_message).to eq('HELLO THERE')
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe LightService::Organizer do
|
4
|
+
class TestAddToContext
|
5
|
+
extend LightService::Organizer
|
6
|
+
|
7
|
+
def self.call(context = LightService::Context.make)
|
8
|
+
with(context).reduce(steps)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.steps
|
12
|
+
[
|
13
|
+
# This will add the `:number` key to the context
|
14
|
+
# with the value of 0, so it's available for
|
15
|
+
# AddsOneAction
|
16
|
+
add_to_context(:number => 0),
|
17
|
+
TestDoubles::AddsOneAction,
|
18
|
+
add_to_context(:something => 'hello')
|
19
|
+
]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'adds items to the context on the fly' do
|
24
|
+
result = TestAddToContext.call
|
25
|
+
|
26
|
+
expect(result).to be_success
|
27
|
+
expect(result.number).to eq(1)
|
28
|
+
expect(result[:something]).to eq('hello')
|
29
|
+
end
|
30
|
+
end
|
@@ -20,6 +20,13 @@ RSpec.describe LightService::Organizer do
|
|
20
20
|
expect(result.number).to eq(5)
|
21
21
|
end
|
22
22
|
|
23
|
+
it "knows that it's being iterated from within an organizer" do
|
24
|
+
result = TestDoubles::TestIterate.call(:number => 1,
|
25
|
+
:counters => [1, 2, 3, 4])
|
26
|
+
|
27
|
+
expect(result.organized_by).to eq TestDoubles::TestIterate
|
28
|
+
end
|
29
|
+
|
23
30
|
it 'will not iterate over a failed context' do
|
24
31
|
empty_context.fail!('Something bad happened')
|
25
32
|
|
@@ -48,4 +48,42 @@ RSpec.describe LightService::Organizer do
|
|
48
48
|
result = TestReduceIf.call(empty_context)
|
49
49
|
expect(result).to be_success
|
50
50
|
end
|
51
|
+
|
52
|
+
it "knows that it's being conditionally reduced from within an organizer" do
|
53
|
+
result = TestReduceIf.call(:number => 2)
|
54
|
+
|
55
|
+
expect(result.organized_by).to eq TestReduceIf
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'skips actions within in its own scope' do
|
59
|
+
org = Class.new do
|
60
|
+
extend LightService::Organizer
|
61
|
+
|
62
|
+
def self.call
|
63
|
+
reduce(actions)
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.actions
|
67
|
+
[
|
68
|
+
reduce_if(
|
69
|
+
->(c) { !c.nil? },
|
70
|
+
[
|
71
|
+
execute(->(c) { c[:first_reduce_if] = true }),
|
72
|
+
execute(->(c) { c.skip_remaining! }),
|
73
|
+
execute(->(c) { c[:second_reduce_if] = true })
|
74
|
+
]
|
75
|
+
),
|
76
|
+
execute(->(c) { c[:last_outside] = true })
|
77
|
+
]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
result = org.call
|
82
|
+
|
83
|
+
aggregate_failures do
|
84
|
+
expect(result[:first_reduce_if]).to be true
|
85
|
+
expect(result[:second_reduce_if]).to be_nil
|
86
|
+
expect(result[:last_outside]).to be true
|
87
|
+
end
|
88
|
+
end
|
51
89
|
end
|
@@ -40,4 +40,10 @@ RSpec.describe LightService::Organizer do
|
|
40
40
|
result = TestReduceUntil.call(empty_context)
|
41
41
|
expect(result).to be_success
|
42
42
|
end
|
43
|
+
|
44
|
+
it "is expected to know its organizer when reducing until a condition" do
|
45
|
+
result = TestReduceUntil.call(:number => 1)
|
46
|
+
|
47
|
+
expect(result.organized_by).to eq TestReduceUntil
|
48
|
+
end
|
43
49
|
end
|
data/spec/action_spec.rb
CHANGED
@@ -68,6 +68,14 @@ describe LightService::Action do
|
|
68
68
|
expect(result.to_hash).to eq(:number => 2)
|
69
69
|
end
|
70
70
|
|
71
|
+
context "when called directly" do
|
72
|
+
it "is expected to not be organized" do
|
73
|
+
result = TestDoubles::AddsTwoActionWithFetch.execute(context)
|
74
|
+
|
75
|
+
expect(result.organized_by).to be_nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
71
79
|
context "when invoked with hash" do
|
72
80
|
it "creates LightService::Context implicitly" do
|
73
81
|
ctx = { :some_key => "some value" }
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require_relative '../../../lib/generators/light_service/action_generator.rb'
|
4
|
+
require_relative './full_generator_test_blobs'
|
5
|
+
|
6
|
+
describe LightService::Generators::ActionGenerator, :type => :generator do
|
7
|
+
destination File.expand_path('tmp', __dir__)
|
8
|
+
|
9
|
+
context "when generating an advanced action" do
|
10
|
+
before(:all) do
|
11
|
+
prepare_destination
|
12
|
+
run_generator
|
13
|
+
end
|
14
|
+
|
15
|
+
after(:all) do
|
16
|
+
FileUtils.rm_rf destination_root
|
17
|
+
end
|
18
|
+
|
19
|
+
arguments %w[
|
20
|
+
my/fancy/action
|
21
|
+
expects:foo,bar
|
22
|
+
promises:baz,qux
|
23
|
+
--no-roll-back
|
24
|
+
--dir=services
|
25
|
+
]
|
26
|
+
|
27
|
+
specify do
|
28
|
+
expect(destination_root).to(have_structure do
|
29
|
+
directory "app/services/my/fancy" do
|
30
|
+
file "action.rb" do
|
31
|
+
contains FullGeneratorTestBlobs.advanced_action_blob
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
directory "spec/services/my/fancy" do
|
36
|
+
file "action_spec.rb" do
|
37
|
+
contains FullGeneratorTestBlobs.advanced_action_spec_blob
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require_relative '../../../lib/generators/light_service/action_generator.rb'
|
4
|
+
require_relative './full_generator_test_blobs'
|
5
|
+
|
6
|
+
describe LightService::Generators::ActionGenerator, :type => :generator do
|
7
|
+
destination File.expand_path('tmp', __dir__)
|
8
|
+
|
9
|
+
context "when generating a simple action" do
|
10
|
+
before(:all) do
|
11
|
+
prepare_destination
|
12
|
+
run_generator
|
13
|
+
end
|
14
|
+
|
15
|
+
after(:all) do
|
16
|
+
FileUtils.rm_rf destination_root
|
17
|
+
end
|
18
|
+
|
19
|
+
arguments %w[my_action]
|
20
|
+
|
21
|
+
specify do
|
22
|
+
expect(destination_root).to(have_structure do
|
23
|
+
directory "app/actions" do
|
24
|
+
file "my_action.rb" do
|
25
|
+
contains FullGeneratorTestBlobs.simple_action_blob
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
directory "spec/actions" do
|
30
|
+
file "my_action_spec.rb" do
|
31
|
+
contains FullGeneratorTestBlobs.simple_action_spec_blob
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|