light-service 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 481bcf8e63d44965c219dff04ab8889681ffb592
4
- data.tar.gz: b34c8d452cc394a8608b8d1387c83eb49b3fc807
3
+ metadata.gz: 713b37be5e541d42ebed24537f656289ed6d5ab6
4
+ data.tar.gz: dbf4a703cbfa397562d59a1f61ca8c08136a7111
5
5
  SHA512:
6
- metadata.gz: 40c7652d305c27fe6f07bcd21c8b4478e534f4f0efa5298a9cb076333fcab02b76ee6d6f841e76ed3eaf38103525fe598f697c9fb64c0b3cab60e1606568fa8c
7
- data.tar.gz: 824c92c2b6117867bb9e1a40d087249838622872162be5f28d6b7f1197b98787b6cf4bb6e4dc2bee9e10c53d88a288c08b95d15ca333b2e45e06901245cc7739
6
+ metadata.gz: 5040578df5d4f0bbce8f530a31f67fe1baacdd12658d3c20b4e936dc0f53511efe666858a491134d604ed246ff76d119c09cebfbbf802bb4ce95aeaad69dff0f
7
+ data.tar.gz: aa4adc93839de21f98b0e275da0d28e0dfbc5fe1f1114ac2bdb14b267bbff6208a19754357ed5c713861cba542229ad0ce0ac9033469b5f41c14ee887f49bdfe
@@ -1,5 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - "1.9.2"
3
+ - "1.9.3"
4
4
  # uncomment this line if your project needs to run something other than `rake`:
5
5
  script: bundle exec rspec spec
data/README.md CHANGED
@@ -49,11 +49,11 @@ The order of these tasks matters: you can't calculate the order tax without the
49
49
  Wouldn't it be nice to see this instead?
50
50
 
51
51
  ```ruby
52
- [
53
- LooksUpTaxPercentage,
54
- CalculatesOrderTax,
55
- ChecksFreeShipping
56
- ]
52
+ (
53
+ LooksUpTaxPercentageAction,
54
+ CalculatesOrderTaxAction,
55
+ ChecksFreeShippingAction
56
+ )
57
57
  ```
58
58
 
59
59
  This block of code should tell you the "story" of what's going on in this workflow.
@@ -69,30 +69,28 @@ class CalculatesTax
69
69
  include LightService::Organizer
70
70
 
71
71
  def self.for_order(order)
72
- with(order: order).reduce \
73
- [
72
+ with(:order => order).reduce(
74
73
  LooksUpTaxPercentageAction,
75
74
  CalculatesOrderTaxAction,
76
75
  ProvidesFreeShippingAction
77
- ]
76
+ )
78
77
  end
79
78
  end
80
79
 
81
80
  class LooksUpTaxPercentageAction
82
81
  include LightService::Action
82
+ expects :order
83
+ promises :tax_percentage
83
84
 
84
85
  executed do |context|
85
- order = context.fetch(:order)
86
- tax_ranges = TaxRange.for_region(order.region)
86
+ tax_ranges = TaxRange.for_region(self.order.region)
87
+ self.tax_percentage = 0
87
88
 
88
89
  next context if object_is_nil?(tax_ranges, context, 'The tax ranges were not found')
89
90
 
90
- order = context.fetch(:order)
91
- tax_percentage = tax_ranges.for_total(order.total)
92
-
93
- next context if object_is_nil?(tax_percentage, context, 'The tax percentage was not found')
91
+ self.tax_percentage = tax_ranges.for_total(self.order.total)
94
92
 
95
- context[:tax_percentage] = tax_percentage
93
+ next context if object_is_nil?(self.tax_percentage, context, 'The tax percentage was not found')
96
94
  end
97
95
 
98
96
  def self.object_is_nil?(object, context, message)
@@ -103,16 +101,13 @@ class LooksUpTaxPercentageAction
103
101
 
104
102
  false
105
103
  end
106
-
107
104
  end
108
105
 
109
106
  class CalculatesOrderTaxAction
110
107
  include ::LightService::Action
108
+ expects :order, :tax_percentage
111
109
 
112
110
  executed do |context|
113
- order = context.fetch(:order)
114
- tax_percentage = context.fetch(:tax_percentage)
115
-
116
111
  order.tax = (order.total * (tax_percentage/100)).round(2)
117
112
  end
118
113
 
@@ -120,10 +115,9 @@ end
120
115
 
121
116
  class ProvidesFreeShippingAction
122
117
  include LightService::Action
118
+ expects :order
123
119
 
124
120
  executed do |context|
125
- order = context.fetch(:order)
126
-
127
121
  if order.total_with_tax > 200
128
122
  order.provide_free_shipping!
129
123
  end
@@ -152,6 +146,60 @@ end
152
146
  I gave a [talk at RailsConf 2013](http://www.adomokos.com/2013/06/simple-and-elegant-rails-code-with.html) on
153
147
  simple and elegant Rails code where I told the story of how LightService was extracted from the projects I had worked on.
154
148
 
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
152
+ action is reduced. If either of them are violated, a custom exception is thrown.
153
+
154
+ This is how it's used:
155
+ ```ruby
156
+ class FooAction
157
+ include LightService::Action
158
+ expects :baz
159
+ promises :bar
160
+
161
+ executed do |context|
162
+ baz = context.fetch :baz
163
+
164
+ bar = baz + 2
165
+ context[:bar] = bar
166
+ end
167
+ end
168
+ ```
169
+
170
+ The `expects` macro does a bit more for you: it pulls the value with the expected key from the context, and
171
+ makes it available to you through a reader. You can refactor the action like this:
172
+
173
+ ```ruby
174
+ class FooAction
175
+ include LightService::Action
176
+ expects :baz
177
+ promises :bar
178
+
179
+ executed do |context|
180
+ bar = self.baz + 2
181
+ context[:bar] = bar
182
+ end
183
+ end
184
+ ```
185
+
186
+ The `promises` macro will not only check if the context has the promised keys, it also sets it for you in the context if
187
+ you use the accessor with the same name. The code above can be further simplified:
188
+
189
+ ```ruby
190
+ class FooAction
191
+ include LightService::Action
192
+ expects :baz
193
+ promises :bar
194
+
195
+ executed do |context|
196
+ self.bar = self.baz + 2
197
+ end
198
+ end
199
+ ```
200
+
201
+ Take a look at [this spec](spec/action_expects_and_promises_spec.rb) to see the refactoring in action.
202
+
155
203
  ## Requirements
156
204
 
157
205
  This gem requires ruby 1.9.x
@@ -188,6 +236,9 @@ For further examples, please visit the project's [Wiki](https://github.com/adomo
188
236
  Huge thanks to the [contributors](https://github.com/adomokos/light-service/graphs/contributors)!
189
237
 
190
238
  ## Release Notes
239
+ ### 0.3.0
240
+ * Adding the `expects` and `promises` macros - Read more about it in [this blog post](http://www.adomokos.com/2014/05/expects-and-promises-in-lightservice.html)
241
+
191
242
  ### 0.2.2
192
243
  * Adding the gem version icon to README
193
244
  * Actions can be invoked now [without arguments](https://github.com/adomokos/light-service/commit/244d5f03b9dbf61c97c1fdb865e6587f9aea177d), this makes it super easy to play with an action in the command line
@@ -1,5 +1,6 @@
1
1
  require "light-service/version"
2
2
 
3
3
  require 'light-service/context'
4
+ require 'light-service/context_key_verifier'
4
5
  require 'light-service/action'
5
6
  require 'light-service/organizer'
@@ -6,26 +6,67 @@ module LightService
6
6
  end
7
7
 
8
8
  module Macros
9
+ def expects(*args)
10
+ @_expected_keys = args
11
+ end
12
+
13
+ def promises(*args)
14
+ @_promised_keys = args
15
+ end
16
+
9
17
  def executed
10
18
  define_singleton_method "execute" do |context = {}|
11
19
  action_context = create_action_context(context)
12
- return action_context if action_context.failure? || action_context.skip_all?
20
+ return action_context if action_context.stop_processing?
21
+
22
+ ContextKeyVerifier.verify_expected_keys_are_in_context(action_context, @_expected_keys)
23
+
24
+ define_expectations_readers(context)
25
+ define_promises_accessors(context)
13
26
 
14
27
  yield(action_context)
15
28
 
16
- action_context
29
+ set_promises_in_context(action_context)
30
+ ContextKeyVerifier.verify_promised_keys_are_in_context(action_context, @_promised_keys)
17
31
  end
18
32
  end
19
33
 
20
34
  private
21
35
 
22
36
  def create_action_context(context)
23
- if context.respond_to? :failure?
37
+ if context.is_a? ::LightService::Context
24
38
  return context
25
39
  end
26
40
 
27
41
  LightService::Context.make(context)
28
42
  end
43
+
44
+ def define_expectations_readers(context)
45
+ context.keys.each do |key|
46
+ define_singleton_method key do
47
+ context.fetch(key)
48
+ end
49
+ end
50
+ end
51
+
52
+ def define_promises_accessors(context)
53
+ return unless @_promised_keys
54
+ @_promised_keys.each do |key|
55
+ instance_variable_set("@#{key}", VALUE_NOT_SET)
56
+ self.class.send(:attr_accessor, key)
57
+ end
58
+ end
59
+
60
+ def set_promises_in_context(context)
61
+ return unless @_promised_keys
62
+ @_promised_keys.each do |key|
63
+ value = instance_variable_get("@#{key}")
64
+ next if value == VALUE_NOT_SET
65
+ context[key] = value
66
+ end
67
+ end
68
+
69
+ VALUE_NOT_SET = "___value_was_not_set___"
29
70
  end
30
71
 
31
72
  end
@@ -70,5 +70,8 @@ module LightService
70
70
  @skip_all = true
71
71
  end
72
72
 
73
+ def stop_processing?
74
+ failure? || skip_all?
75
+ end
73
76
  end
74
77
  end
@@ -0,0 +1,37 @@
1
+ module LightService
2
+ class ExpectedKeysNotInContextError < StandardError; end
3
+ class PromisedKeysNotInContextError < StandardError; end
4
+
5
+ class ContextKeyVerifier
6
+ class << self
7
+ def verify_expected_keys_are_in_context(context, expected_keys)
8
+ verify_keys_are_in_context(context, expected_keys) do |not_found_keys|
9
+ fail ExpectedKeysNotInContextError, "expected #{format_keys(not_found_keys)} to be in the context"
10
+ end
11
+ end
12
+
13
+ def verify_promised_keys_are_in_context(context, promised_keys)
14
+ verify_keys_are_in_context(context, promised_keys) do |not_found_keys|
15
+ fail PromisedKeysNotInContextError, "promised #{format_keys(not_found_keys)} to be in the context"
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def verify_keys_are_in_context(context, keys)
22
+ keys ||= context.keys
23
+
24
+ not_found_keys = keys - context.keys
25
+ unless not_found_keys.empty?
26
+ yield not_found_keys
27
+ end
28
+
29
+ context
30
+ end
31
+
32
+ def format_keys(keys)
33
+ keys.map{|k| ":#{k}"}.join(', ')
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,3 +1,3 @@
1
1
  module LightService
2
- VERSION = "0.2.2"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -14,34 +14,40 @@ end
14
14
 
15
15
  class AddsOneAction
16
16
  include LightService::Action
17
+ expects :number
18
+ promises :number
17
19
 
18
20
  executed do |context|
19
- number = context.fetch :number
21
+ number = self.number
20
22
  number += 1
21
23
 
22
- context[:number] = number
24
+ self.number = number
23
25
  end
24
26
  end
25
27
 
26
28
  class AddsTwoAction
27
29
  include LightService::Action
30
+ expects :number
31
+ promises :number
28
32
 
29
33
  executed do |context|
30
- number = context.fetch :number
34
+ number = self.number
31
35
  number += 2
32
36
 
33
- context[:number] = number
37
+ self.number = number
34
38
  end
35
39
  end
36
40
 
37
41
  class AddsThreeAction
38
42
  include LightService::Action
43
+ expects :number
44
+ promises :number
39
45
 
40
46
  executed do |context|
41
- number = context.fetch :number
47
+ number = self.number
42
48
  number += 3
43
49
 
44
- context[:number] = number
50
+ self.number = number
45
51
  end
46
52
  end
47
53
 
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ module LightService
4
+ describe ":expects macro" do
5
+ class DummyActionForKeysToExpect
6
+ include LightService::Action
7
+ expects :tea, :milk
8
+ promises :milk_tea
9
+
10
+ executed do |context|
11
+ context[:milk_tea] = "#{self.tea} - #{milk}"
12
+ end
13
+ end
14
+
15
+ context "when expected keys are in the context" do
16
+ it "can access the keys as class methods" do
17
+ resulting_context = DummyActionForKeysToExpect.execute(
18
+ :tea => "black",
19
+ :milk => "full cream",
20
+ :something => "else"
21
+ )
22
+ expect(resulting_context[:milk_tea]).to eq("black - full cream")
23
+ end
24
+ end
25
+
26
+ context "when expected keys are not in the context" do
27
+ it "raises an error" do
28
+ exception_error_text = "expected :milk to be in the context"
29
+ expect {
30
+ DummyActionForKeysToExpect.execute(:tea => "black")
31
+ }.to raise_error(ExpectedKeysNotInContextError, exception_error_text)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ module LightService
4
+ describe ":expects and :promises macros" do
5
+ describe "actions are backward compatible" do
6
+ class FooAction
7
+ include LightService::Action
8
+
9
+ executed do |context|
10
+ baz = context.fetch :baz
11
+
12
+ bar = baz + 2
13
+ context[:bar] = bar
14
+ end
15
+ end
16
+ it "works without expects and promises" do
17
+ result = FooAction.execute(:baz => 3)
18
+ expect(result).to be_success
19
+ expect(result[:bar]).to eq(5)
20
+ end
21
+ end
22
+
23
+ context "when expected keys are not in context" do
24
+ class FooNoExpectedKeyAction
25
+ include LightService::Action
26
+ expects :baz
27
+
28
+ executed do |context|
29
+ baz = context.fetch :baz
30
+
31
+ bar = baz + 2
32
+ context[:bar] = bar
33
+ end
34
+ end
35
+ it "throws an ExpectedKeysNotInContextError" do
36
+ # FooAction invoked with nothing in the context
37
+ expect { FooNoExpectedKeyAction.execute }.to \
38
+ raise_error(ExpectedKeysNotInContextError)
39
+ end
40
+ end
41
+
42
+ describe "expected keys" do
43
+ class FooWithReaderAction
44
+ include LightService::Action
45
+ expects :baz
46
+
47
+ executed do |context|
48
+ # Notice how I use `self.baz` here
49
+ bar = self.baz + 2
50
+ context[:bar] = bar
51
+ end
52
+ end
53
+ it "can be accessed through a reader" do
54
+ result = FooWithReaderAction.execute(:baz => 3)
55
+ expect(result).to be_success
56
+ expect(result[:bar]).to eq(5)
57
+ end
58
+ end
59
+
60
+ context "when promised keys are not in context" do
61
+ class FooNoPromisedKeyAction
62
+ include LightService::Action
63
+ expects :baz
64
+ promises :bar
65
+
66
+ executed do |context|
67
+ # I am not adding anything to the context
68
+ end
69
+ end
70
+ it "throws a PromisedKeysNotInContextError" do
71
+ # FooAction invoked with nothing placed in the context
72
+ expect { FooNoPromisedKeyAction.execute(:baz => 3) }.to \
73
+ raise_error(PromisedKeysNotInContextError)
74
+ end
75
+ end
76
+
77
+ describe "promised keys" do
78
+ class FooWithExpectsAndPromisesAction
79
+ include LightService::Action
80
+ expects :baz
81
+ promises :bar
82
+
83
+ executed do |context|
84
+ # Notice how I use `self.bar` here
85
+ self.bar = self.baz + 2
86
+ end
87
+ end
88
+ it "puts the value through the accessor into the context" do
89
+ result = FooWithExpectsAndPromisesAction.execute(:baz => 3)
90
+ expect(result).to be_success
91
+ expect(result[:bar]).to eq(5)
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ module LightService
4
+ describe ":promises macro" do
5
+ class DummyActionForKeysToPromise
6
+ include LightService::Action
7
+ expects :tea, :milk
8
+ promises :milk_tea, :something_else
9
+
10
+ executed do |context|
11
+ context[:some_tea] = "#{self.tea} - #{self.milk}"
12
+ end
13
+ end
14
+
15
+ context "when the promised key is not in the context" do
16
+ it "raises an ArgumentError" do
17
+ exception_error_text = "promised :milk_tea, :something_else to be in the context"
18
+ expect {
19
+ DummyActionForKeysToPromise.execute(:tea => "black", :milk => "full cream")
20
+ }.to raise_error(PromisedKeysNotInContextError, exception_error_text)
21
+ end
22
+ end
23
+
24
+ context "when the promised key is in the context" do
25
+ class DummyActionSetsItemInContext
26
+ include LightService::Action
27
+ expects :tea, :milk
28
+ promises :milk_tea
29
+
30
+ executed do |context|
31
+ self.milk_tea = "#{self.tea} - #{self.milk}"
32
+ self.milk_tea += " hello"
33
+ end
34
+ end
35
+ it "sets in the context if it was set with not nil" do
36
+ result_context = DummyActionSetsItemInContext.execute(:tea => "black",
37
+ :milk => "full cream")
38
+ expect(result_context).to be_success
39
+ expect(result_context[:milk_tea]).to eq("black - full cream hello")
40
+ end
41
+
42
+ class DummyActionNilNotSetInContext
43
+ include LightService::Action
44
+ expects :tea, :milk
45
+ promises :milk_tea
46
+
47
+ executed do |context|
48
+ self.milk_tea = nil
49
+ end
50
+ end
51
+ it "sets in the context if it was set with nil" do
52
+ result_context = DummyActionNilNotSetInContext.execute(:tea => "black",
53
+ :milk => "full cream")
54
+ expect(result_context).to be_success
55
+ expect(result_context[:milk_tea]).to be_nil
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -18,6 +18,11 @@ module LightService
18
18
  end
19
19
  end
20
20
 
21
+ class PromisingAction
22
+ include LightService::Action
23
+ promises :milk_tea
24
+ end
25
+
21
26
  let(:context) { ::LightService::Context.make }
22
27
 
23
28
  context "when the action context has failure" do
@@ -79,5 +79,19 @@ module LightService
79
79
  expect(context).to be_skip_all
80
80
  end
81
81
 
82
+ context "stopping additional processing in an action" do
83
+ let(:context) { Context.make }
84
+
85
+ it "flags processing to stop on failure" do
86
+ context.fail!("on purpose")
87
+ expect(context.stop_processing?).to be_true
88
+ end
89
+
90
+ it "flags processing to stop when remaining actions should be skipped" do
91
+ context.skip_all!
92
+ expect(context.stop_processing?).to be_true
93
+ end
94
+ end
95
+
82
96
  end
83
97
  end
@@ -1,11 +1,8 @@
1
1
  class CalculatesOrderTaxAction
2
2
  include ::LightService::Action
3
+ expects :order, :tax_percentage
3
4
 
4
5
  executed do |context|
5
- order = context.fetch(:order)
6
- tax_percentage = context.fetch(:tax_percentage)
7
-
8
6
  order.tax = (order.total * (tax_percentage/100)).round(2)
9
7
  end
10
-
11
8
  end
@@ -2,11 +2,10 @@ class CalculatesTax
2
2
  include LightService::Organizer
3
3
 
4
4
  def self.for_order(order)
5
- with(order: order).reduce \
6
- [
5
+ with(:order => order).reduce(
7
6
  LooksUpTaxPercentageAction,
8
7
  CalculatesOrderTaxAction,
9
8
  ProvidesFreeShippingAction
10
- ]
9
+ )
11
10
  end
12
11
  end
@@ -1,18 +1,17 @@
1
1
  class LooksUpTaxPercentageAction
2
2
  include LightService::Action
3
+ expects :order
4
+ promises :tax_percentage
3
5
 
4
6
  executed do |context|
5
- order = context.fetch(:order)
6
- tax_ranges = TaxRange.for_region(order.region)
7
+ tax_ranges = TaxRange.for_region(self.order.region)
8
+ self.tax_percentage = 0
7
9
 
8
10
  next context if object_is_nil?(tax_ranges, context, 'The tax ranges were not found')
9
11
 
10
- order = context.fetch(:order)
11
- tax_percentage = tax_ranges.for_total(order.total)
12
+ self.tax_percentage = tax_ranges.for_total(self.order.total)
12
13
 
13
- next context if object_is_nil?(tax_percentage, context, 'The tax percentage was not found')
14
-
15
- context[:tax_percentage] = tax_percentage
14
+ next context if object_is_nil?(self.tax_percentage, context, 'The tax percentage was not found')
16
15
  end
17
16
 
18
17
  def self.object_is_nil?(object, context, message)
@@ -23,5 +22,4 @@ class LooksUpTaxPercentageAction
23
22
 
24
23
  false
25
24
  end
26
-
27
25
  end
@@ -1,9 +1,8 @@
1
1
  class ProvidesFreeShippingAction
2
2
  include LightService::Action
3
+ expects :order
3
4
 
4
5
  executed do |context|
5
- order = context.fetch(:order)
6
-
7
6
  if order.total_with_tax > 200
8
7
  order.provide_free_shipping!
9
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: light-service
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
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-03-12 00:00:00.000000000 Z
11
+ date: 2014-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -69,18 +69,22 @@ files:
69
69
  - lib/light-service.rb
70
70
  - lib/light-service/action.rb
71
71
  - lib/light-service/context.rb
72
+ - lib/light-service/context_key_verifier.rb
72
73
  - lib/light-service/organizer.rb
73
74
  - lib/light-service/version.rb
74
75
  - light-service.gemspec
75
76
  - resources/light-service.png
76
77
  - resources/organizer_and_actions.png
77
78
  - spec/acceptance/add_numbers_spec.rb
79
+ - spec/action_expected_keys_spec.rb
80
+ - spec/action_expects_and_promises_spec.rb
81
+ - spec/action_promised_keys_spec.rb
78
82
  - spec/action_spec.rb
79
83
  - spec/context_spec.rb
80
84
  - spec/organizer_spec.rb
81
85
  - spec/sample/calculates_order_tax_action_spec.rb
82
86
  - spec/sample/calculates_tax_spec.rb
83
- - spec/sample/looks_up_tax_percentage_action.rb
87
+ - spec/sample/looks_up_tax_percentage_action_spec.rb
84
88
  - spec/sample/provides_free_shipping_action_spec.rb
85
89
  - spec/sample/tax/calculates_order_tax_action.rb
86
90
  - spec/sample/tax/calculates_tax.rb
@@ -113,12 +117,15 @@ specification_version: 4
113
117
  summary: A service skeleton with an emphasis on simplicity
114
118
  test_files:
115
119
  - spec/acceptance/add_numbers_spec.rb
120
+ - spec/action_expected_keys_spec.rb
121
+ - spec/action_expects_and_promises_spec.rb
122
+ - spec/action_promised_keys_spec.rb
116
123
  - spec/action_spec.rb
117
124
  - spec/context_spec.rb
118
125
  - spec/organizer_spec.rb
119
126
  - spec/sample/calculates_order_tax_action_spec.rb
120
127
  - spec/sample/calculates_tax_spec.rb
121
- - spec/sample/looks_up_tax_percentage_action.rb
128
+ - spec/sample/looks_up_tax_percentage_action_spec.rb
122
129
  - spec/sample/provides_free_shipping_action_spec.rb
123
130
  - spec/sample/tax/calculates_order_tax_action.rb
124
131
  - spec/sample/tax/calculates_tax.rb