light-service 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ vendor/bundle
19
+ bin
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.2"
4
+ # uncomment this line if your project needs to run something other than `rake`:
5
+ script: bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in light_service.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Attila Domokos
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,173 @@
1
+ ![Light Service](https://raw.github.com/adomokos/light-service/master/light-service.png)
2
+
3
+ [![Build Status](https://secure.travis-ci.org/adomokos/light-service.png)](http://travis-ci.org/adomokos/light-service)
4
+
5
+ What do you think of this code?
6
+
7
+ ```ruby
8
+ class TaxController < ApplicationContoller
9
+ def update
10
+ @order = Order.find(params[:id])
11
+ tax_ranges = TaxRange.for_region(order.region)
12
+
13
+ if tax_ranges.nil?
14
+ render :action => :edit, :error => "The tax ranges were not found"
15
+ return # Avoiding the double render error
16
+ end
17
+
18
+ tax_percentage = tax_ranges.for_total(@order.total)
19
+
20
+ if tax_percentage.nil?
21
+ render :action => :edit, :error => "The tax percentage was not found"
22
+ return # Avoiding the double render error
23
+ end
24
+
25
+ @order.tax = (@order.total * (tax_percentage/100)).round(2)
26
+
27
+ if @order.total_with_tax > 200
28
+ @order.provide_free_shipping!
29
+ end
30
+
31
+ redirect_to checkout_shipping_path(@order), :notice => "Tax was calculated successfully"
32
+ end
33
+ end
34
+ ```
35
+
36
+ This controller violates [SRP](http://en.wikipedia.org/wiki/Single_responsibility_principle) all over.
37
+ Also, imagine what it takes to test this beast.
38
+ You could move the tax_percentage finders and calculations into the tax model,
39
+ but then you'll make your model logic heavy.
40
+
41
+ This controller does 3 things in order:
42
+ * Looks up the tax percentage based on order total
43
+ * Calculates the order tax
44
+ * Provides free shipping if the total with tax is greater than $200
45
+
46
+ The order of these tasks matters: you can't calculate the order tax without the percentage.
47
+ Wouldn't it be nice to see this instead?
48
+
49
+ ```ruby
50
+ [
51
+ LooksUpTaxPercentage,
52
+ CalculatesOrderTax,
53
+ ChecksFreeShipping
54
+ ]
55
+ ```
56
+
57
+ This block of code should tell you the "story" of what's going on in this workflow.
58
+ With the help of LightService you can write code this way. First you need an organizer object that sets up the actions in order
59
+ and executes them one-by-one. Then you need to create the actions which will only have one method and will do only one thing.
60
+
61
+ ```ruby
62
+ class CalculatesTax
63
+ def self.for_order(order)
64
+ context = LightService::Context.make(:order => order)
65
+
66
+ [
67
+ LooksUpTaxPercentageAction,
68
+ CalculatesOrderTaxAction,
69
+ ProvidesFreeShippingAction
70
+ ].each{ |action| action.execute(context) }
71
+
72
+ context
73
+ end
74
+ end
75
+
76
+ class LooksUpTaxPercentageAction
77
+ include LightService::Action
78
+
79
+ executed do |context|
80
+ order = context.fetch(:order)
81
+ tax_ranges = TaxRange.for_region(order.region)
82
+
83
+ next context if object_is_nil?(tax_ranges, context, 'The tax ranges were not found')
84
+
85
+ order = context.fetch(:order)
86
+ tax_percentage = tax_ranges.for_total(order.total)
87
+
88
+ next context if object_is_nil?(tax_percentage, context, 'The tax percentage was not found')
89
+
90
+ context[:tax_percentage] = tax_percentage
91
+ end
92
+
93
+ def self.object_is_nil?(object, context, message)
94
+ if object.nil?
95
+ context.set_failure!(message)
96
+ return true
97
+ end
98
+
99
+ false
100
+ end
101
+
102
+ end
103
+
104
+ class CalculatesOrderTaxAction
105
+ include ::LightService::Action
106
+
107
+ executed do |context|
108
+ order = context.fetch(:order)
109
+ tax_percentage = context.fetch(:tax_percentage)
110
+
111
+ order.tax = (order.total * (tax_percentage/100)).round(2)
112
+ end
113
+
114
+ end
115
+
116
+ class ProvidesFreeShippingAction
117
+ include LightService::Action
118
+
119
+ executed do |context|
120
+ order = context.fetch(:order)
121
+
122
+ if order.total_with_tax > 200
123
+ order.provide_free_shipping!
124
+ end
125
+ end
126
+ end
127
+ ```
128
+
129
+ And with all that, your controller should be super simple:
130
+
131
+ ```ruby
132
+ class TaxController < ApplicationContoller
133
+ def update
134
+ @order = Order.find(params[:id])
135
+
136
+ service_result = CalculatesTax.for_order(@order)
137
+
138
+ if service_result.failure?
139
+ render :action => :edit, :error => service_result.message
140
+ else
141
+ redirect_to checkout_shipping_path(@order), :notice => "Tax was calculated successfully"
142
+ end
143
+
144
+ end
145
+ end
146
+ ```
147
+
148
+ ## Installation
149
+
150
+ Add this line to your application's Gemfile:
151
+
152
+ gem 'light_service'
153
+
154
+ And then execute:
155
+
156
+ $ bundle
157
+
158
+ Or install it yourself as:
159
+
160
+ $ gem install light_service
161
+
162
+ ## Usage
163
+
164
+ Based on the refactoring example above, just create an organizer object that calls the
165
+ actions in order and write code for the actions. That's it.
166
+
167
+ ## Contributing
168
+
169
+ 1. Fork it
170
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
171
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
172
+ 4. Push to the branch (`git push origin my-new-feature`)
173
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,21 @@
1
+ module LightService
2
+ module Action
3
+
4
+ def self.included(base_class)
5
+ base_class.extend Macros
6
+ end
7
+
8
+ module Macros
9
+ def executed
10
+ define_singleton_method "execute" do |context|
11
+ return context if context.failure?
12
+
13
+ yield(context)
14
+
15
+ context
16
+ end
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,58 @@
1
+ module LightService
2
+ module Outcomes
3
+ SUCCESS = 0
4
+ FAILURE = 1
5
+ end
6
+
7
+ class Context
8
+ attr_accessor :outcome, :message
9
+
10
+ def initialize(outcome=::LightService::Outcomes::SUCCESS, message='', context={})
11
+ @outcome, @message, @context = outcome, message, context
12
+ end
13
+
14
+ def self.make(context={})
15
+ Context.new(::LightService::Outcomes::SUCCESS, '', context)
16
+ end
17
+
18
+ def add_to_context(values)
19
+ @context.merge! values
20
+ end
21
+
22
+ def [](index)
23
+ @context[index]
24
+ end
25
+
26
+ def []=(index, value)
27
+ @context[index] = value
28
+ end
29
+
30
+ def fetch(index)
31
+ @context.fetch(index)
32
+ end
33
+
34
+ # It's really there for testing and debugging
35
+ def context_hash
36
+ @context.dup
37
+ end
38
+
39
+ def success?
40
+ @outcome == ::LightService::Outcomes::SUCCESS
41
+ end
42
+
43
+ def failure?
44
+ success? == false
45
+ end
46
+
47
+ def set_success!(message)
48
+ @message = message
49
+ @outcome = ::LightService::Outcomes::SUCCESS
50
+ end
51
+
52
+ def set_failure!(message)
53
+ @message = message
54
+ @outcome = ::LightService::Outcomes::FAILURE
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,3 @@
1
+ module LightService
2
+ VERSION = "0.0.7"
3
+ end
@@ -0,0 +1,4 @@
1
+ require "light-service/version"
2
+
3
+ require 'light-service/context'
4
+ require 'light-service/action'
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/light-service/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Attila Domokos"]
6
+ gem.email = ["adomokos@gmail.com"]
7
+ gem.description = %q{A service skeleton with an emphasis on simplicity}
8
+ gem.summary = %q{A service skeleton with an emphasis on simplicity}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "light-service"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = LightService::VERSION
17
+
18
+ gem.add_development_dependency("rspec", "~> 2.0")
19
+ gem.add_development_dependency("simplecov", "~> 0.7.1")
20
+ end
data/light-service.png ADDED
Binary file
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ require_relative 'sample/calculates_order_tax_action'
3
+
4
+ describe CalculatesOrderTaxAction do
5
+ let(:order) { double('order') }
6
+ let(:context) do
7
+ data = { :order => order, :tax_percentage => 7.2 }
8
+ ::LightService::Context.make(data)
9
+ end
10
+
11
+ it "calculates the tax based on the tax percentage" do
12
+ order.stub(:total => 100)
13
+ order.should_receive(:tax=).with 7.2
14
+ CalculatesOrderTaxAction.execute(context)
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+ require_relative 'sample/calculates_tax'
3
+ require_relative 'sample/looks_up_tax_percentage_action'
4
+ require_relative 'sample/calculates_order_tax_action'
5
+ require_relative 'sample/provides_free_shipping_action'
6
+
7
+ describe CalculatesTax do
8
+ let(:order) { double('order') }
9
+ let(:context) { double('context') }
10
+
11
+ it "calls the actions in order" do
12
+ ::LightService::Context.stub(:make) \
13
+ .with(:order => order) \
14
+ .and_return context
15
+
16
+ LooksUpTaxPercentageAction.stub(:execute).with(context)
17
+ CalculatesOrderTaxAction.stub(:execute).with(context)
18
+ ProvidesFreeShippingAction.stub(:execute).with(context)
19
+
20
+ result = CalculatesTax.for_order(order)
21
+ result.should eq context
22
+ end
23
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+ require_relative 'sample/looks_up_tax_percentage_action'
3
+
4
+ class TaxRange; end
5
+
6
+ describe LooksUpTaxPercentageAction do
7
+ let(:region) { double('region') }
8
+ let(:order) do
9
+ order = double('order')
10
+ order.stub(:region => region)
11
+ order.stub(:total => 200)
12
+ order
13
+ end
14
+ let(:context) do
15
+ ::LightService::Context.make(:order => order)
16
+ end
17
+ let(:tax_percentage) { double('tax_percentage') }
18
+ let(:tax_ranges) { double('tax_ranges') }
19
+
20
+ context "when the tax_ranges were not found" do
21
+ it "sets the context to failure" do
22
+ TaxRange.stub(:for_region).with(region).and_return nil
23
+ LooksUpTaxPercentageAction.execute(context)
24
+
25
+ context.should be_failure
26
+ context.message.should eq "The tax ranges were not found"
27
+ end
28
+ end
29
+
30
+ context "when the tax_percentage is not found" do
31
+ it "sets the context to failure" do
32
+ TaxRange.stub(:for_region).with(region).and_return tax_ranges
33
+ tax_ranges.stub(:for_total => nil)
34
+
35
+ LooksUpTaxPercentageAction.execute(context)
36
+
37
+ context.should be_failure
38
+ context.message.should eq "The tax percentage was not found"
39
+ end
40
+ end
41
+
42
+ context "when the tax_percentage is found" do
43
+ it "sets the tax_percentage in context" do
44
+ TaxRange.stub(:for_region).with(region).and_return tax_ranges
45
+ tax_ranges.stub(:for_total => 25)
46
+
47
+ LooksUpTaxPercentageAction.execute(context)
48
+
49
+ context.should be_success
50
+ context.fetch(:tax_percentage).should eq 25
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require_relative 'sample/provides_free_shipping_action'
3
+
4
+ describe ProvidesFreeShippingAction do
5
+ let(:order) { double('order') }
6
+ let(:context) do
7
+ data = { :order => order }
8
+ ::LightService::Context.make(data)
9
+ end
10
+
11
+ context "when the order total with tax is > 200" do
12
+ specify "order gets free shipping" do
13
+ order.stub(:total_with_tax => 201)
14
+ order.should_receive(:provide_free_shipping!)
15
+
16
+ ProvidesFreeShippingAction.execute(context)
17
+ end
18
+ end
19
+
20
+ context "when the order total with tax is <= 200" do
21
+ specify "order gets free shipping" do
22
+ order.stub(:total_with_tax => 200)
23
+ order.should_not_receive(:provide_free_shipping!)
24
+
25
+ ProvidesFreeShippingAction.execute(context)
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,11 @@
1
+ class CalculatesOrderTaxAction
2
+ include ::LightService::Action
3
+
4
+ executed do |context|
5
+ order = context.fetch(:order)
6
+ tax_percentage = context.fetch(:tax_percentage)
7
+
8
+ order.tax = (order.total * (tax_percentage/100)).round(2)
9
+ end
10
+
11
+ end
@@ -0,0 +1,13 @@
1
+ class CalculatesTax
2
+ def self.for_order(order)
3
+ context = ::LightService::Context.make(:order => order)
4
+
5
+ [
6
+ LooksUpTaxPercentageAction,
7
+ CalculatesOrderTaxAction,
8
+ ProvidesFreeShippingAction
9
+ ].each{ |action| action.execute(context) }
10
+
11
+ context
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ class LooksUpTaxPercentageAction
2
+ include LightService::Action
3
+
4
+ executed do |context|
5
+ order = context.fetch(:order)
6
+ tax_ranges = TaxRange.for_region(order.region)
7
+
8
+ next context if object_is_nil?(tax_ranges, context, 'The tax ranges were not found')
9
+
10
+ order = context.fetch(:order)
11
+ tax_percentage = tax_ranges.for_total(order.total)
12
+
13
+ next context if object_is_nil?(tax_percentage, context, 'The tax percentage was not found')
14
+
15
+ context[:tax_percentage] = tax_percentage
16
+ end
17
+
18
+ def self.object_is_nil?(object, context, message)
19
+ if object.nil?
20
+ context.set_failure!(message)
21
+ return true
22
+ end
23
+
24
+ false
25
+ end
26
+
27
+ end
@@ -0,0 +1,12 @@
1
+ class ProvidesFreeShippingAction
2
+ include LightService::Action
3
+
4
+ executed do |context|
5
+ order = context.fetch(:order)
6
+
7
+ if order.total_with_tax > 200
8
+ order.provide_free_shipping!
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ module LightService
4
+ describe Action do
5
+ class DummyAction
6
+ include LightService::Action
7
+
8
+ executed do |context|
9
+ context[:test_key] = "test_value"
10
+ end
11
+ end
12
+
13
+ let(:context) { ::LightService::Context.new }
14
+
15
+ context "when the action context has failure" do
16
+ it "returns immediately" do
17
+ context.set_failure!("an error")
18
+
19
+ DummyAction.execute(context)
20
+
21
+ context.context_hash.keys.should be_empty
22
+ end
23
+ end
24
+
25
+ context "when the action context does not have failure" do
26
+ it "executes the block" do
27
+ DummyAction.execute(context)
28
+
29
+ context.context_hash.keys.should eq [:test_key]
30
+ end
31
+ end
32
+
33
+ it "returns the context" do
34
+ result = DummyAction.execute(context)
35
+
36
+ result.context_hash.should eq ({:test_key => "test_value"})
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,59 @@
1
+ require "spec_helper"
2
+
3
+ module LightService
4
+ describe Context do
5
+ subject { Context.new(Outcomes::SUCCESS, "some_message", {:test => 1}) }
6
+
7
+ it "initializes the object with default arguments" do
8
+ service_result = Context.new
9
+ service_result.should be_success
10
+ end
11
+
12
+ it "initializes the object with the context" do
13
+ service_result = Context.new.tap { |o| o.add_to_context({:test => 1})}
14
+ service_result.should be_success
15
+ service_result.message.should eq ""
16
+ service_result[:test].should eq 1
17
+ end
18
+
19
+ it "initializes the object with make" do
20
+ service_result = Context.make({:test => 1})
21
+ service_result.should be_success
22
+ service_result.message.should eq ""
23
+ service_result[:test].should eq 1
24
+ end
25
+
26
+ context "when created" do
27
+ it { should be_success }
28
+ end
29
+
30
+ it "allows to set success" do
31
+ subject.set_success!("the success")
32
+ subject.should be_success
33
+ subject.message.should == "the success"
34
+ end
35
+
36
+ specify "evaluates failure?" do
37
+ subject.set_success!("the success")
38
+ subject.should_not be_failure
39
+ end
40
+
41
+ it "allows to set failure" do
42
+ subject.set_failure!("the failure")
43
+ subject.should_not be_success
44
+ subject.message.should == "the failure"
45
+ end
46
+
47
+ it "lets setting a group of context values" do
48
+ subject.context_hash.should include(:test => 1)
49
+ subject.context_hash.keys.length.should == 1
50
+
51
+ subject.add_to_context(:test => 1, :two => 2)
52
+
53
+ subject.context_hash.keys.length.should == 2
54
+ subject.context_hash.should include(:test => 1)
55
+ subject.context_hash.should include(:two => 2)
56
+ end
57
+ end
58
+
59
+ end
@@ -0,0 +1,5 @@
1
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
2
+ $LOAD_PATH << File.join(File.dirname(__FILE__))
3
+
4
+ require 'light-service'
5
+ require 'ostruct'
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: light-service
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.7
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Attila Domokos
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: simplecov
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.7.1
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.7.1
46
+ description: A service skeleton with an emphasis on simplicity
47
+ email:
48
+ - adomokos@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - .rspec
55
+ - .travis.yml
56
+ - Gemfile
57
+ - LICENSE
58
+ - README.md
59
+ - Rakefile
60
+ - lib/light-service.rb
61
+ - lib/light-service/action.rb
62
+ - lib/light-service/context.rb
63
+ - lib/light-service/version.rb
64
+ - light-service.gemspec
65
+ - light-service.png
66
+ - spec/acceptance/calculates_order_tax_action_spec.rb
67
+ - spec/acceptance/calculates_tax_spec.rb
68
+ - spec/acceptance/looks_up_tax_percentage_action.rb
69
+ - spec/acceptance/provides_free_shipping_action_spec.rb
70
+ - spec/acceptance/sample/calculates_order_tax_action.rb
71
+ - spec/acceptance/sample/calculates_tax.rb
72
+ - spec/acceptance/sample/looks_up_tax_percentage_action.rb
73
+ - spec/acceptance/sample/provides_free_shipping_action.rb
74
+ - spec/action_base_spec.rb
75
+ - spec/context_spec.rb
76
+ - spec/spec_helper.rb
77
+ homepage: ''
78
+ licenses: []
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 1.8.24
98
+ signing_key:
99
+ specification_version: 3
100
+ summary: A service skeleton with an emphasis on simplicity
101
+ test_files:
102
+ - spec/acceptance/calculates_order_tax_action_spec.rb
103
+ - spec/acceptance/calculates_tax_spec.rb
104
+ - spec/acceptance/looks_up_tax_percentage_action.rb
105
+ - spec/acceptance/provides_free_shipping_action_spec.rb
106
+ - spec/acceptance/sample/calculates_order_tax_action.rb
107
+ - spec/acceptance/sample/calculates_tax.rb
108
+ - spec/acceptance/sample/looks_up_tax_percentage_action.rb
109
+ - spec/acceptance/sample/provides_free_shipping_action.rb
110
+ - spec/action_base_spec.rb
111
+ - spec/context_spec.rb
112
+ - spec/spec_helper.rb