light-service 0.2.2 → 0.3.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 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