light-service 0.5.1 → 0.5.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4d68f9d14bf6972f0063f0e4765b582857ecdf8c
4
- data.tar.gz: ac5f09a4248a65df0781d5e93a5e7df6e95c1cfb
3
+ metadata.gz: cce43f62b668f6fa04385c772ca0d006b9e92ca0
4
+ data.tar.gz: 3d246a1c22bc6dfa9afef1c64d77c1b15b16618b
5
5
  SHA512:
6
- metadata.gz: 1ca0cb1b7e3f80e13a43560069e854c8ba410d905a2ed874a700203908465f408bc379d650cf1c820ab74ec6983ed0b30fea978e3b1c6797acb9694603b8934f
7
- data.tar.gz: 6b66eb9c58f7a7fa6414bff2df6223a2e66f8d4f17de15e107514f45aa77131b778bec94f9715c026f4216720836c37069c9c93f5684587ab6d6f1293646e4de
6
+ metadata.gz: 2c00210b56fb1be0cf27ac8c415f421421902088443ed8919d48ee600f22446e8d2b007586b5a4fae553fe39e11092589face9df3950e6e638f5e1a7fa524a81
7
+ data.tar.gz: 4171398c9f082dae55888485c98e7fe0d335cd0bd527c488b095921055b05659ab1495b33be899b1460839ac41783d902d43121c074276e94c8670481646dae9
data/README.md CHANGED
@@ -147,8 +147,8 @@ I gave a [talk at RailsConf 2013](http://www.adomokos.com/2013/06/simple-and-ele
147
147
  simple and elegant Rails code where I told the story of how LightService was extracted from the projects I had worked on.
148
148
 
149
149
  ## Expects and Promises
150
- Let me introduce to you the `expects` and `promises` macros. Think of these as a rule set of inputs/outputs for the action.
151
- `expects` describes what keys it needs to execute and `promises` makes sure the keys are in the context after the
150
+ The `expects` and `promises` macros are rules for the inputs/outputs of an action.
151
+ `expects` describes what keys it needs to execute, and `promises` makes sure the keys are in the context after the
152
152
  action is reduced. If either of them are violated, a custom exception is thrown.
153
153
 
154
154
  This is how it's used:
data/RELEASES.md CHANGED
@@ -1,5 +1,8 @@
1
1
  A brief list of new features and changes introduced with the specified version.
2
2
 
3
+ ### 0.5.2
4
+ * Guarding context keys against the reserved keys the context needs to operate.
5
+
3
6
  ### 0.5.1
4
7
  * Removing the thrown exception for invoking the "executed" macro twice
5
8
 
data/lib/light-service.rb CHANGED
@@ -6,7 +6,7 @@ require 'light-service/errors'
6
6
  require 'light-service/configuration'
7
7
  require 'light-service/localization_adapter'
8
8
  require 'light-service/context'
9
- require 'light-service/context_key_verifier'
9
+ require 'light-service/context/key_verifier'
10
10
  require 'light-service/organizer/with_reducer'
11
11
  require 'light-service/organizer/with_reducer_log_decorator'
12
12
  require 'light-service/organizer/with_reducer_factory'
@@ -25,28 +25,25 @@ module LightService
25
25
  end
26
26
 
27
27
  def executed
28
- define_singleton_method "execute" do |context = {}|
28
+ define_singleton_method :execute do |context = {}|
29
29
  action_context = create_action_context(context)
30
30
  return action_context if action_context.stop_processing?
31
31
 
32
32
  # Store the action within the context
33
33
  action_context.current_action = self
34
34
 
35
- Context::KeyVerifier.verify_expected_keys_are_in_context(action_context)
35
+ Context::KeyVerifier.verify_keys(action_context) do
36
+ action_context.define_accessor_methods_for_keys(all_keys)
36
37
 
37
- action_context.define_accessor_methods_for_keys(expected_keys)
38
- action_context.define_accessor_methods_for_keys(promised_keys)
39
-
40
- yield(action_context)
41
-
42
- Context::KeyVerifier.verify_promised_keys_are_in_context(action_context)
38
+ yield(action_context)
39
+ end
43
40
  end
44
41
  end
45
42
 
46
43
  def rolled_back
47
44
  raise "`rolled_back` macro can not be invoked again" if self.respond_to?(:rollback)
48
45
 
49
- define_singleton_method "rollback" do |context = {}|
46
+ define_singleton_method :rollback do |context = {}|
50
47
  yield(context)
51
48
 
52
49
  context
@@ -64,6 +61,10 @@ module LightService
64
61
  LightService::Context.make(context)
65
62
  end
66
63
 
64
+ def all_keys
65
+ expected_keys + promised_keys
66
+ end
67
+
67
68
  end
68
69
  end
69
70
  end
@@ -0,0 +1,114 @@
1
+ module LightService; class Context
2
+ class KeyVerifier
3
+ attr_reader :context, :action
4
+
5
+ def initialize(context)
6
+ @context = context
7
+ @action = context.current_action
8
+ end
9
+
10
+ def are_all_keys_in_context?(keys)
11
+ not_found_keys = keys_not_found(keys)
12
+ !not_found_keys.any?
13
+ end
14
+
15
+ def keys_not_found(keys)
16
+ keys ||= context.keys
17
+ keys - context.keys
18
+ end
19
+
20
+ def format_keys(keys)
21
+ keys.map { |k| ":#{k}"}.join(', ')
22
+ end
23
+
24
+ def error_message
25
+ "#{type_name} #{format_keys(keys_not_found(keys))} to be in the context during #{action}"
26
+ end
27
+
28
+ def throw_error_predicate(keys)
29
+ raise NotImplementedError, 'Sorry, you have to override length'
30
+ end
31
+
32
+ def verify
33
+ return context if context.failure?
34
+
35
+ if throw_error_predicate(keys)
36
+ Configuration.logger.error error_message
37
+ fail error_to_throw, error_message
38
+ end
39
+
40
+ context
41
+ end
42
+
43
+ def self.verify_keys(context, &block)
44
+ ReservedKeysVerifier.new(context).verify
45
+ ExpectedKeyVerifier.new(context).verify
46
+
47
+ block.call
48
+
49
+ PromisedKeyVerifier.new(context).verify
50
+ end
51
+ end
52
+
53
+ class ExpectedKeyVerifier < KeyVerifier
54
+ def type_name
55
+ "expected"
56
+ end
57
+
58
+ def keys
59
+ action.expected_keys
60
+ end
61
+
62
+ def error_to_throw
63
+ ExpectedKeysNotInContextError
64
+ end
65
+
66
+ def throw_error_predicate(keys)
67
+ !are_all_keys_in_context?(keys)
68
+ end
69
+ end
70
+
71
+ class PromisedKeyVerifier < KeyVerifier
72
+ def type_name
73
+ "promised"
74
+ end
75
+
76
+ def keys
77
+ action.promised_keys
78
+ end
79
+
80
+ def error_to_throw
81
+ PromisedKeysNotInContextError
82
+ end
83
+
84
+ def throw_error_predicate(keys)
85
+ !are_all_keys_in_context?(keys)
86
+ end
87
+ end
88
+
89
+ class ReservedKeysVerifier < KeyVerifier
90
+ def violated_keys
91
+ (action.promised_keys + action.expected_keys) & reserved_keys
92
+ end
93
+
94
+ def error_message
95
+ "promised or expected keys cannot be a reserved key: [#{format_keys(violated_keys)}]"
96
+ end
97
+
98
+ def keys
99
+ violated_keys
100
+ end
101
+
102
+ def error_to_throw
103
+ ReservedKeysInContextError
104
+ end
105
+
106
+ def throw_error_predicate(keys)
107
+ keys.any?
108
+ end
109
+
110
+ def reserved_keys
111
+ [:message, :error_code, :current_action].freeze
112
+ end
113
+ end
114
+ end; end
@@ -2,4 +2,5 @@ module LightService
2
2
  class FailWithRollbackError < StandardError; end
3
3
  class ExpectedKeysNotInContextError < StandardError; end
4
4
  class PromisedKeysNotInContextError < StandardError; end
5
+ class ReservedKeysInContextError < StandardError; end
5
6
  end
@@ -1,3 +1,3 @@
1
1
  module LightService
2
- VERSION = "0.5.1"
2
+ VERSION = "0.5.2"
3
3
  end
@@ -32,4 +32,19 @@ describe ":expects macro" do
32
32
  expect(resulting_context[:milk_tea]).to eq("black - full cream - with dark chocolate")
33
33
  end
34
34
 
35
+ context "when a reserved key is listed as an expected key" do
36
+ it "raises an error indicating a reserved key is expected" do
37
+ exception_error_text = "promised or expected keys cannot be a reserved key: [:message]"
38
+ expect {
39
+ TestDoubles::MakesTeaExpectingReservedKey.execute(:tea => "black", :message => "no no")
40
+ }.to raise_error(LightService::ReservedKeysInContextError, exception_error_text)
41
+ end
42
+
43
+ it "raises an error indicating that multiple reserved keys are expected" do
44
+ exception_error_text = "promised or expected keys cannot be a reserved key: [:message, :error_code, :current_action]"
45
+ expect {
46
+ TestDoubles::MakesTeaExpectingMultipleReservedKeys.execute(:tea => "black", :message => "no no", :error_code => 1, :current_action => "update")
47
+ }.to raise_error(LightService::ReservedKeysInContextError, exception_error_text)
48
+ end
49
+ end
35
50
  end
@@ -77,6 +77,22 @@ describe ":promises macro" do
77
77
  end
78
78
  end
79
79
 
80
+ context "when a reserved key is listed as a promised key" do
81
+ it "raises an error indicating a reserved key has been promised" do
82
+ exception_error_text = "promised or expected keys cannot be a reserved key: [:message]"
83
+ expect {
84
+ TestDoubles::MakesTeaPromisingReservedKey.execute(:tea => "black")
85
+ }.to raise_error(LightService::ReservedKeysInContextError, exception_error_text)
86
+ end
87
+
88
+ it "raises an error indicating that multiple reserved keys have been promised" do
89
+ exception_error_text = "promised or expected keys cannot be a reserved key: [:message, :error_code, :current_action]"
90
+ expect {
91
+ TestDoubles::MakesTeaPromisingMultipleReservedKeys.execute(:tea => "black")
92
+ }.to raise_error(LightService::ReservedKeysInContextError, exception_error_text)
93
+ end
94
+ end
95
+
80
96
  it "can collect promised keys when the `promised` macro is called multiple times" do
81
97
  resulting_context = TestDoubles::MultiplePromisesAction.execute(
82
98
  :coffee => "espresso",
@@ -85,5 +101,4 @@ describe ":promises macro" do
85
101
  expect(resulting_context.cappuccino).to eq("Cappucino needs espresso and a little milk")
86
102
  expect(resulting_context.latte).to eq("Latte needs espresso and a lot of milk")
87
103
  end
88
-
89
104
  end
data/spec/test_doubles.rb CHANGED
@@ -176,4 +176,41 @@ module TestDoubles
176
176
  end
177
177
  end
178
178
 
179
+ class MakesTeaExpectingReservedKey
180
+ include LightService::Action
181
+ expects :tea, :message
182
+
183
+ executed do |context|
184
+ context.product = context.number + 3
185
+ end
186
+ end
187
+
188
+ class MakesTeaExpectingMultipleReservedKeys
189
+ include LightService::Action
190
+ expects :tea, :message, :error_code, :current_action
191
+
192
+ executed do |context|
193
+ context.product = context.number + 3
194
+ end
195
+ end
196
+
197
+ class MakesTeaPromisingReservedKey
198
+ include LightService::Action
199
+ expects :tea
200
+ promises :product, :message
201
+
202
+ executed do |context|
203
+ context.product = context.number + 3
204
+ end
205
+ end
206
+
207
+ class MakesTeaPromisingMultipleReservedKeys
208
+ include LightService::Action
209
+ expects :tea
210
+ promises :product, :message, :error_code, :current_action
211
+
212
+ executed do |context|
213
+ context.product = context.number + 3
214
+ end
215
+ end
179
216
  end
metadata CHANGED
@@ -1,69 +1,69 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: light-service
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Attila Domokos
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-12 00:00:00.000000000 Z
11
+ date: 2015-04-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - '>='
18
18
  - !ruby/object:Gem::Version
19
19
  version: '4.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '4.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ~>
32
32
  - !ruby/object:Gem::Version
33
33
  version: '3.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ~>
39
39
  - !ruby/object:Gem::Version
40
40
  version: '3.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec-its
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ~>
46
46
  - !ruby/object:Gem::Version
47
47
  version: '1.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ~>
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: simplecov
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ~>
60
60
  - !ruby/object:Gem::Version
61
61
  version: 0.7.1
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ~>
67
67
  - !ruby/object:Gem::Version
68
68
  version: 0.7.1
69
69
  - !ruby/object:Gem::Dependency
@@ -87,9 +87,9 @@ executables: []
87
87
  extensions: []
88
88
  extra_rdoc_files: []
89
89
  files:
90
- - ".gitignore"
91
- - ".rspec"
92
- - ".travis.yml"
90
+ - .gitignore
91
+ - .rspec
92
+ - .travis.yml
93
93
  - Gemfile
94
94
  - LICENSE
95
95
  - README.md
@@ -99,7 +99,7 @@ files:
99
99
  - lib/light-service/action.rb
100
100
  - lib/light-service/configuration.rb
101
101
  - lib/light-service/context.rb
102
- - lib/light-service/context_key_verifier.rb
102
+ - lib/light-service/context/key_verifier.rb
103
103
  - lib/light-service/errors.rb
104
104
  - lib/light-service/localization_adapter.rb
105
105
  - lib/light-service/organizer.rb
@@ -142,17 +142,17 @@ require_paths:
142
142
  - lib
143
143
  required_ruby_version: !ruby/object:Gem::Requirement
144
144
  requirements:
145
- - - ">="
145
+ - - '>='
146
146
  - !ruby/object:Gem::Version
147
147
  version: '0'
148
148
  required_rubygems_version: !ruby/object:Gem::Requirement
149
149
  requirements:
150
- - - ">="
150
+ - - '>='
151
151
  - !ruby/object:Gem::Version
152
152
  version: '0'
153
153
  requirements: []
154
154
  rubyforge_project:
155
- rubygems_version: 2.2.2
155
+ rubygems_version: 2.0.14
156
156
  signing_key:
157
157
  specification_version: 4
158
158
  summary: A service skeleton with an emphasis on simplicity
@@ -1,48 +0,0 @@
1
- module LightService
2
- class Context
3
- class KeyVerifier
4
- class << self
5
- def verify_expected_keys_are_in_context(context)
6
- action = context.current_action
7
-
8
- verify_keys_are_in_context(context, action.expected_keys) do |not_found_keys|
9
- error_message = "expected #{format_keys(not_found_keys)} to be in the context during #{action}"
10
-
11
- Configuration.logger.error error_message
12
- fail ExpectedKeysNotInContextError, error_message
13
- end
14
- end
15
-
16
- def verify_promised_keys_are_in_context(context)
17
- return context if context.failure?
18
-
19
- action = context.current_action
20
-
21
- verify_keys_are_in_context(context, action.promised_keys) do |not_found_keys|
22
- error_message = "promised #{format_keys(not_found_keys)} to be in the context during #{action}"
23
-
24
- Configuration.logger.error error_message
25
- fail PromisedKeysNotInContextError, error_message
26
- end
27
- end
28
-
29
- private
30
-
31
- def verify_keys_are_in_context(context, keys)
32
- keys ||= context.keys
33
-
34
- not_found_keys = keys - context.keys
35
- unless not_found_keys.empty?
36
- yield not_found_keys
37
- end
38
-
39
- context
40
- end
41
-
42
- def format_keys(keys)
43
- keys.map { |k| ":#{k}"}.join(', ')
44
- end
45
- end
46
- end
47
- end
48
- end