bcurren-rshoeboxed 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2009-01-12
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,29 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.rdoc
4
+ Rakefile
5
+ lib/rshoeboxed.rb
6
+ lib/rshoeboxed/category.rb
7
+ lib/rshoeboxed/connection.rb
8
+ lib/rshoeboxed/list_proxy.rb
9
+ lib/rshoeboxed/receipt.rb
10
+ script/console
11
+ script/destroy
12
+ script/generate
13
+ test/fixtures/category_request.xml
14
+ test/fixtures/category_response.xml
15
+ test/fixtures/receipt_info_request.xml
16
+ test/fixtures/receipt_info_response.xml
17
+ test/fixtures/receipt_request.xml
18
+ test/fixtures/receipt_response.xml
19
+ test/fixtures/receipt_response_bad_credentials.xml
20
+ test/fixtures/receipt_response_internal_error.xml
21
+ test/fixtures/receipt_response_restricted_ip.xml
22
+ test/fixtures/receipt_response_unknown_api_call.xml
23
+ test/fixtures/receipt_response_xml_validation.xml
24
+ test/fixtures/receipt_with_category_id_request.xml
25
+ test/test_category.rb
26
+ test/test_connection.rb
27
+ test/test_helper.rb
28
+ test/test_list_proxy.rb
29
+ test/test_receipt.rb
data/README.rdoc ADDED
@@ -0,0 +1,66 @@
1
+ = rshoeboxed
2
+
3
+ * http://github.com/bcurren/rshoeboxed
4
+
5
+ == DESCRIPTION:
6
+
7
+ Ruby wrapper for the Shoeboxed API.
8
+
9
+ == SYNOPSIS:
10
+
11
+ === Generate authentication url
12
+
13
+ Use the following code to generate a url to send a user to to authenticate. The first parameter is the
14
+ AppName provided by shoeboxed. Shoeboxed will redirect back to the return url once a user successfully
15
+ authenticates. When the user is redirected back to the return_url, a token will be provided in the url
16
+ for you to store for future API calls.
17
+
18
+ RShoeboxed::Connection.authentication_url("Outright", "http://example.com")
19
+
20
+ === Get a list of all receipts
21
+
22
+ The api_token is provided by shoeboxed when you setup your API account. The user_token is retrieved by
23
+ sending a user to authentication_url so they can log in to shoeboxed. On success, they will be redirected
24
+ to the return_url with the user_token.
25
+
26
+ connection = RShoeboxed::Connection.new("api_token", "user_token")
27
+ receipts = connection.get_receipt_call(Date.new(2008, 1, 1), Date.new(2008, 12, 29))
28
+
29
+ === Get a particular receipt
30
+
31
+ connection = RShoeboxed::Connection.new("api_token", "user_token")
32
+ receipt = connection.get_category_info_call("2342442424")
33
+
34
+ === Get a list of all categories
35
+
36
+ connection = RShoeboxed::Connection.new("api_token", "user_token")
37
+ categories = connection.get_category_call
38
+
39
+ == INSTALL:
40
+
41
+ * sudo gem install bcurren_rshoeboxed
42
+
43
+ == LICENSE:
44
+
45
+ (The MIT License)
46
+
47
+ Copyright (c) 2009 Ben Curren
48
+
49
+ Permission is hereby granted, free of charge, to any person obtaining
50
+ a copy of this software and associated documentation files (the
51
+ 'Software'), to deal in the Software without restriction, including
52
+ without limitation the rights to use, copy, modify, merge, publish,
53
+ distribute, sublicense, and/or sell copies of the Software, and to
54
+ permit persons to whom the Software is furnished to do so, subject to
55
+ the following conditions:
56
+
57
+ The above copyright notice and this permission notice shall be
58
+ included in all copies or substantial portions of the Software.
59
+
60
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
61
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
62
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
63
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
64
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
65
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
66
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ %w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
2
+ require File.dirname(__FILE__) + '/lib/rshoeboxed'
3
+
4
+ # Generate all the Rake tasks
5
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
6
+ $hoe = Hoe.new('rshoeboxed', RShoeboxed::VERSION) do |p|
7
+ p.developer('Ben Curren', 'ben@gobootstrap.com')
8
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
9
+ p.rubyforge_name = p.name # TODO this is default value
10
+ p.extra_deps = [
11
+ ['builder','>= 2.1.2'],
12
+ ]
13
+ p.extra_dev_deps = [
14
+ ['newgem', ">= #{::Newgem::VERSION}"],
15
+ ['mocha', ">= 0.9.4"]
16
+ ]
17
+
18
+ p.clean_globs |= %w[**/.DS_Store tmp *.log]
19
+ path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
20
+ p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
21
+ p.rsync_args = '-av --delete --ignore-errors'
22
+ end
23
+
24
+ require 'newgem/tasks' # load /tasks/*.rake
25
+ Dir['tasks/**/*.rake'].each { |t| load t }
26
+
27
+ # TODO - want other tests/tasks run by default? Add them to the list
28
+ # task :default => [:spec, :features]
data/lib/rshoeboxed.rb ADDED
@@ -0,0 +1,12 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'rubygems'
5
+ require 'rshoeboxed/receipt'
6
+ require 'rshoeboxed/category'
7
+ require 'rshoeboxed/connection'
8
+ require 'rshoeboxed/list_proxy'
9
+
10
+ module RShoeboxed
11
+ VERSION = '0.0.1'
12
+ end
@@ -0,0 +1,18 @@
1
+ require 'rexml/document'
2
+
3
+ module RShoeboxed
4
+ class Category
5
+ attr_accessor :id, :name
6
+
7
+ def self.parse(xml)
8
+ document = REXML::Document.new(xml)
9
+ document.elements.collect("//Category") do |category_element|
10
+ category = Category.new
11
+ category.id = category_element.attributes["id"]
12
+ category.name = category_element.attributes["name"]
13
+ category
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,208 @@
1
+ gem "builder"
2
+ require "builder"
3
+ require "cgi"
4
+ require "net/https"
5
+ require 'rexml/document'
6
+ require 'logger'
7
+
8
+ module RShoeboxed
9
+ class Error < StandardError; end;
10
+ class UnknownAPICallError < Error; end;
11
+ class InternalError < Error; end;
12
+ class AuthenticationError < Error; end;
13
+ class RestrictedIPError < Error; end;
14
+ class XMLValidationError < Error; end;
15
+
16
+ class Connection
17
+ attr_accessor :api_key, :user_token
18
+
19
+ API_SERVER = "www.shoeboxed.com"
20
+ API_PATH = "/ws/api.htm"
21
+ API_URL = "https://" + API_SERVER + API_PATH
22
+
23
+ @@logger = Logger.new(STDOUT)
24
+ def logger
25
+ @@logger
26
+ end
27
+
28
+ def self.log_level=(level)
29
+ @@logger.level = level
30
+ end
31
+ self.log_level = Logger::WARN
32
+
33
+ # Generates a url for you to obtain the user token. This url with take the user to the Shoeboxed authentication
34
+ # page. After the user successfully authenticates, they will be redirected to the app_url with the token and uname
35
+ # as parameters.
36
+ #
37
+ # app_name - string that contains the name of the app that is calling the API
38
+ # app_url - url that Shoeboxed will redirect to after a successful authentication
39
+ # app_params - option hash or array of strings containing params that will be passed back to app_url
40
+ def self.authentication_url(app_name, app_url, app_params={})
41
+ API_URL + "?" + encode_params(
42
+ [[:appname, app_name],
43
+ [:appurl, app_url],
44
+ [:apparams, encode_params(app_params)],
45
+ [:SignIn, 1]]
46
+ )
47
+ end
48
+
49
+
50
+ # The initialization method takes in two params:
51
+ # api_key -
52
+ # user_token (generated after validating from the authentication url)
53
+ def initialize(api_key, user_token)
54
+ @api_key = api_key
55
+ @user_token = user_token
56
+ end
57
+
58
+ def get_receipt_info_call(id)
59
+ request = build_receipt_info_request(id)
60
+ response = post_xml(request)
61
+
62
+ receipts = Receipt.parse(response)
63
+ receipts ? receipts.first : nil
64
+ end
65
+
66
+ # Note: the result_count can only be 50, 100, or 200
67
+ def get_receipt_call(start_date, end_date, options = {})
68
+ options = {
69
+ :use_sell_date => false,
70
+ :per_page => 50,
71
+ :current_page => 1
72
+ }.merge(options)
73
+
74
+ request = build_receipt_request(start_date, end_date, options)
75
+ response = post_xml(request)
76
+
77
+ receipts = Receipt.parse(response)
78
+ wrap_array_with_pagination(receipts, response, options[:current_page], options[:per_page])
79
+ end
80
+
81
+ def get_category_call
82
+ request = build_category_request
83
+ response = post_xml(request)
84
+
85
+ Category.parse(response)
86
+ end
87
+
88
+ private
89
+
90
+ def wrap_array_with_pagination(receipts, response, current_page, per_page)
91
+ document = REXML::Document.new(response)
92
+ counts = document.elements.collect("//Receipts") { |element| element.attributes["count"] || 0 }
93
+
94
+ ListProxy.new(receipts, counts.first, current_page, per_page)
95
+ end
96
+
97
+ def date_to_s(date)
98
+ my_date = date
99
+ if !date.kind_of?(String)
100
+ my_date = date.strftime("%Y-%m-%dT%H:%M:%S")
101
+ end
102
+ my_date
103
+ end
104
+
105
+ def self.encode_params(params)
106
+ # If hash, turn to array and give it alpha ordering
107
+ if params.kind_of? Hash
108
+ params = params.to_a
109
+ end
110
+
111
+ encoded_params = params.collect do |name, value|
112
+ "#{CGI.escape(name.to_s)}=#{CGI.escape(value.to_s)}"
113
+ end
114
+ encoded_params.join("&")
115
+ end
116
+
117
+ def post_xml(body)
118
+ connection = Net::HTTP.new(API_SERVER, 443)
119
+ connection.use_ssl = true
120
+ connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
121
+
122
+ request = Net::HTTP::Post.new(API_PATH)
123
+ request.set_form_data({'xml'=>body})
124
+
125
+ result = connection.start { |http| http.request(request) }
126
+
127
+ if logger.debug?
128
+ logger.debug "Request:"
129
+ logger.debug body
130
+ logger.debug "Response:"
131
+ logger.debug result.body
132
+ end
133
+
134
+ check_for_api_error(result.body)
135
+ end
136
+
137
+ def check_for_api_error(body)
138
+ document = REXML::Document.new(body)
139
+ root = document.root
140
+
141
+ if root && root.name == "Error"
142
+ description = root.attributes["description"]
143
+
144
+ case root.attributes["code"]
145
+ when "1"
146
+ raise AuthenticationError.new(description)
147
+ when "2"
148
+ raise UnknownAPICallError.new(description)
149
+ when "3"
150
+ raise RestrictedIPError.new(description)
151
+ when "4"
152
+ raise XMLValidationError.new(description)
153
+ when "5"
154
+ raise InternalError.new(description)
155
+ end
156
+ end
157
+
158
+ body
159
+ end
160
+
161
+ def build_receipt_request(start_date, end_date, options)
162
+ xml = Builder::XmlMarkup.new
163
+ xml.instruct!
164
+ xml.Request(:xmlns => "urn:sbx:apis:SbxBaseComponents") do |xml|
165
+ append_credentials(xml)
166
+ xml.GetReceiptCall do |xml|
167
+ xml.ReceiptFilter do |xml|
168
+ xml.Results(options[:per_page])
169
+ xml.PageNo(options[:current_page])
170
+ xml.DateStart(date_to_s(start_date))
171
+ xml.DateEnd(date_to_s(end_date))
172
+ xml.UseSellDate(options[:use_sell_date])
173
+ xml.Category(options[:category_id]) if options[:category_id]
174
+ end
175
+ end
176
+ end
177
+ end
178
+
179
+ def build_category_request
180
+ xml = Builder::XmlMarkup.new
181
+ xml.instruct!
182
+ xml.Request(:xmlns => "urn:sbx:apis:SbxBaseComponents") do |xml|
183
+ append_credentials(xml)
184
+ xml.GetCategoryCall
185
+ end
186
+ end
187
+
188
+ def build_receipt_info_request(id)
189
+ xml = Builder::XmlMarkup.new
190
+ xml.instruct!
191
+ xml.Request(:xmlns => "urn:sbx:apis:SbxBaseComponents") do |xml|
192
+ append_credentials(xml)
193
+ xml.GetReceiptInfoCall do |xml|
194
+ xml.ReceiptFilter do |xml|
195
+ xml.ReceiptId(id)
196
+ end
197
+ end
198
+ end
199
+ end
200
+
201
+ def append_credentials(xml)
202
+ xml.RequesterCredentials do |xml|
203
+ xml.ApiUserToken(@api_key)
204
+ xml.SbxUserToken(@user_token)
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,25 @@
1
+ module RShoeboxed
2
+ class ListProxy
3
+ attr_reader :current_page, :per_page, :total
4
+
5
+ def initialize(array, total, current_page, per_page)
6
+ @array = array
7
+
8
+ @total = total.to_i
9
+ @current_page = current_page.to_i
10
+ @per_page = per_page.to_i
11
+ end
12
+
13
+ def pages
14
+ pages = total / per_page
15
+ if total % per_page != 0
16
+ pages += 1
17
+ end
18
+ pages
19
+ end
20
+
21
+ def method_missing(method, *args, &block)
22
+ @array.send(method, *args, &block)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,41 @@
1
+ require 'bigdecimal'
2
+ require 'date'
3
+ require 'rexml/document'
4
+
5
+ module RShoeboxed
6
+ class Receipt
7
+ attr_accessor :id, :store, :image_url, :category_id
8
+ attr_reader :date, :total
9
+
10
+ def self.parse(xml)
11
+ document = REXML::Document.new(xml)
12
+ document.elements.collect("//Receipt") do |receipt_element|
13
+ receipt = Receipt.new
14
+
15
+ receipt.id = receipt_element.attributes["id"]
16
+ receipt.store = receipt_element.attributes["store"]
17
+ receipt.date = receipt_element.attributes["date"]
18
+ receipt.total = receipt_element.attributes["total"]
19
+ receipt.image_url = receipt_element.attributes["imgurl"]
20
+ receipt.category_id = receipt_element.attributes["category"]
21
+
22
+ receipt
23
+ end
24
+ end
25
+
26
+ def initialize
27
+ end
28
+
29
+ def total=(total)
30
+ total.gsub!(/[^\d|.]/, "") if total.is_a?(String)
31
+ total = BigDecimal.new(total) unless total.is_a?(BigDecimal)
32
+
33
+ @total = total
34
+ end
35
+
36
+ def date=(date)
37
+ date = Date.parse(date) if date.is_a?(String)
38
+ @date = date
39
+ end
40
+ end
41
+ end
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/rshoeboxed.rb'}"
9
+ puts "Loading rshoeboxed gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Request xmlns="urn:sbx:apis:SbxBaseComponents">
3
+ <RequesterCredentials>
4
+ <ApiUserToken>api_key</ApiUserToken>
5
+ <SbxUserToken>user_token</SbxUserToken>
6
+ </RequesterCredentials>
7
+ <GetCategoryCall/>
8
+ </Request>
@@ -0,0 +1,7 @@
1
+ <GetCategoryCallResponse>
2
+ <Categories>
3
+ <Category id="1" name="Category 1"/>
4
+ <Category id="2" name="Category 2"/>
5
+ <Category id="3" name="Category 3"/>
6
+ </Categories>
7
+ </GetCategoryCallResponse>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Request xmlns="urn:sbx:apis:SbxBaseComponents">
3
+ <RequesterCredentials>
4
+ <ApiUserToken>api_key</ApiUserToken>
5
+ <SbxUserToken>user_token</SbxUserToken>
6
+ </RequesterCredentials>
7
+ <GetReceiptInfoCall>
8
+ <ReceiptFilter>
9
+ <ReceiptId>1</ReceiptId>
10
+ </ReceiptFilter>
11
+ </GetReceiptInfoCall>
12
+ </Request>
@@ -0,0 +1 @@
1
+ <Receipt store="Morgan Imports" id="1" total="$1,929.00" date="5/12/2008" imgurl="http://www.shoeboxed.com/receipt.jpeg" category="1" />
@@ -0,0 +1,16 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Request xmlns="urn:sbx:apis:SbxBaseComponents">
3
+ <RequesterCredentials>
4
+ <ApiUserToken>api_key</ApiUserToken>
5
+ <SbxUserToken>user_token</SbxUserToken>
6
+ </RequesterCredentials>
7
+ <GetReceiptCall>
8
+ <ReceiptFilter>
9
+ <Results>50</Results>
10
+ <PageNo>1</PageNo>
11
+ <DateStart>2008-01-01T00:00:00</DateStart>
12
+ <DateEnd>2009-01-01T00:00:00</DateEnd>
13
+ <UseSellDate>false</UseSellDate>
14
+ </ReceiptFilter>
15
+ </GetReceiptCall>
16
+ </Request>
@@ -0,0 +1,6 @@
1
+ <GetReceiptCallResponse>
2
+ <Receipts count="2">
3
+ <Receipt store="Great Plains Trust Company" id="23984923842" total="$3,378.30" date="5/12/2008" imgurl="http://www.shoeboxed.com/receipt1.jpeg" category="1"/>
4
+ <Receipt store="RadioShack" id="39239293" total="$3.51" date="5/12/2008" imgurl="http://www.shoeboxed.com/receipt2.jpeg" category="2"/>
5
+ </Receipts>
6
+ </GetReceiptCallResponse>
@@ -0,0 +1 @@
1
+ <Error code="1" description="Bad credentials" />
@@ -0,0 +1 @@
1
+ <Error code="5" description="Internal Error" />
@@ -0,0 +1 @@
1
+ <Error code="3" description="Restricted IP" />
@@ -0,0 +1 @@
1
+ <Error code="2" description="Unknown API Call" />
@@ -0,0 +1 @@
1
+ <Error code="4" description="XML Validation" />
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Request xmlns="urn:sbx:apis:SbxBaseComponents">
3
+ <RequesterCredentials>
4
+ <ApiUserToken>api_key</ApiUserToken>
5
+ <SbxUserToken>user_token</SbxUserToken>
6
+ </RequesterCredentials>
7
+ <GetReceiptCall>
8
+ <ReceiptFilter>
9
+ <Results>50</Results>
10
+ <PageNo>1</PageNo>
11
+ <DateStart>2008-01-01T00:00:00</DateStart>
12
+ <DateEnd>2009-01-01T00:00:00</DateEnd>
13
+ <UseSellDate>true</UseSellDate>
14
+ <Category>10</Category>
15
+ </ReceiptFilter>
16
+ </GetReceiptCall>
17
+ </Request>
@@ -0,0 +1,31 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestCategory < Test::Unit::TestCase
4
+ include RShoeboxed
5
+
6
+ def setup
7
+ end
8
+
9
+ def test_initialize_parse_xml
10
+ response = fixture_xml_content("category_response")
11
+ categories = Category.parse(response)
12
+ assert_equal 3, categories.size
13
+
14
+ categories.each_with_index do |category, i|
15
+ category_id = (i+1).to_s
16
+ assert_equal category_id, category.id
17
+ assert_equal "Category #{category_id}", category.name
18
+ end
19
+ end
20
+
21
+ def test_category__accessors
22
+ category = Category.new
23
+ assert category
24
+
25
+ category.name = "Category 1"
26
+ assert_equal "Category 1", category.name
27
+
28
+ category.id = "1"
29
+ assert_equal "1", category.id
30
+ end
31
+ end
@@ -0,0 +1,163 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestConnection < Test::Unit::TestCase
4
+ include RShoeboxed
5
+
6
+ def setup
7
+ @conn = Connection.new("api_key", "user_token")
8
+ end
9
+
10
+ def test_authenticate__connection_failures
11
+ assert true
12
+ end
13
+
14
+ def test_authentication_url__success_with_options
15
+ assert_equal "https://www.shoeboxed.com/ws/api.htm?appname=Bootstrap&appurl=http%3A%2F%2Fexample.com&apparams=test%3D1%26test2%3D1&SignIn=1",
16
+ Connection.authentication_url("Bootstrap", "http://example.com", [[:test, 1], [:test2, 1]])
17
+ end
18
+
19
+
20
+ def test_authentication_url__success_no_options
21
+ assert_equal "https://www.shoeboxed.com/ws/api.htm?appname=Bootstrap&appurl=http%3A%2F%2Fexample.com&apparams=&SignIn=1",
22
+ Connection.authentication_url("Bootstrap", "http://example.com")
23
+ end
24
+
25
+ def test_connection__instantiate_object
26
+ assert_equal "api_key", @conn.api_key
27
+ assert_equal "user_token", @conn.user_token
28
+ end
29
+
30
+
31
+ def test_get_receipt_info_call__success_getting_one_receipt
32
+ request = fixture_xml_content("receipt_info_request")
33
+ response = fixture_xml_content("receipt_info_response")
34
+
35
+ conn = Connection.new("api_key", "user_token")
36
+ conn.expects(:post_xml).with(request).returns(response)
37
+
38
+ receipt = conn.get_receipt_info_call('1')
39
+ assert_not_nil receipt
40
+ assert_equal "1", receipt.id
41
+ assert_equal "Morgan Imports", receipt.store
42
+ assert_equal Date.new(2008, 5, 12), receipt.date
43
+ assert_equal BigDecimal.new("1929.00"), receipt.total
44
+ assert_equal "http://www.shoeboxed.com/receipt.jpeg", receipt.image_url
45
+ assert_equal "1", receipt.category_id
46
+ end
47
+
48
+ def test_build_receipt_info_call_request
49
+ assert_equal fixture_xml_content("receipt_info_request"), @conn.send(:build_receipt_info_request, "1")
50
+ end
51
+
52
+ def test_get_receipt_call__success_getting_two_receipt
53
+ request = fixture_xml_content("receipt_request")
54
+ response = fixture_xml_content("receipt_response")
55
+
56
+ conn = Connection.new("api_key", "user_token")
57
+ conn.expects(:post_xml).with(request).returns(response)
58
+
59
+ receipts = conn.get_receipt_call(Date.new(2008, 1, 1), Date.new(2009, 1, 1))
60
+ assert_equal 2, receipts.total
61
+ assert_equal 1, receipts.current_page
62
+ assert_equal 50, receipts.per_page
63
+ assert_equal 1, receipts.pages
64
+ assert_equal 2, receipts.size
65
+
66
+ receipt = receipts[0]
67
+ assert_equal "23984923842", receipt.id
68
+ assert_equal "Great Plains Trust Company", receipt.store
69
+ assert_equal Date.new(2008, 5, 12), receipt.date
70
+ assert_equal BigDecimal.new("3378.30"), receipt.total
71
+ assert_equal "http://www.shoeboxed.com/receipt1.jpeg", receipt.image_url
72
+ assert_equal "1", receipt.category_id
73
+
74
+ receipt = receipts[1]
75
+ assert_equal "39239293", receipt.id
76
+ assert_equal "RadioShack", receipt.store
77
+ assert_equal Date.new(2008, 5, 12), receipt.date
78
+ assert_equal BigDecimal.new("3.51"), receipt.total
79
+ assert_equal "http://www.shoeboxed.com/receipt2.jpeg", receipt.image_url
80
+ assert_equal "2", receipt.category_id
81
+ end
82
+
83
+ def test_build_receipt_request
84
+ options = {
85
+ :per_page => 50,
86
+ :current_page => 1,
87
+ :use_sell_date => false
88
+ }
89
+ assert_equal fixture_xml_content("receipt_request"),
90
+ @conn.send(:build_receipt_request, Date.new(2008, 1, 1), Date.new(2009, 1, 1), options)
91
+ end
92
+
93
+ def test_build_receipt_request__category_filter
94
+ options = {
95
+ :per_page => 50,
96
+ :current_page => 1,
97
+ :use_sell_date => true,
98
+ :category_id => 10
99
+ }
100
+ assert_equal fixture_xml_content("receipt_with_category_id_request"),
101
+ @conn.send(:build_receipt_request, Date.new(2008, 1, 1), Date.new(2009, 1, 1), options)
102
+ end
103
+
104
+ def test_check_for_api_error__all_error_codes
105
+ assert_api_error_raised("receipt_response_bad_credentials", AuthenticationError, "Bad credentials")
106
+ assert_api_error_raised("receipt_response_unknown_api_call", UnknownAPICallError, "Unknown API Call")
107
+ assert_api_error_raised("receipt_response_restricted_ip", RestrictedIPError, "Restricted IP")
108
+ assert_api_error_raised("receipt_response_xml_validation", XMLValidationError, "XML Validation")
109
+ assert_api_error_raised("receipt_response_unknown_api_call", UnknownAPICallError, "Unknown API Call")
110
+ end
111
+
112
+ def test_check_for_api_error__no_error
113
+ assert_nothing_raised do
114
+ assert_equal "", @conn.send(:check_for_api_error, "")
115
+ end
116
+
117
+ response = fixture_xml_content("receipt_info_response")
118
+ assert_nothing_raised do
119
+ assert_equal response, @conn.send(:check_for_api_error, response)
120
+ end
121
+ end
122
+
123
+ def test_date_to_s
124
+ assert_equal "2009-01-15T00:00:00", @conn.send(:date_to_s, Date.new(2009, 1, 15))
125
+ assert_equal "2010-02-13T05:10:23", @conn.send(:date_to_s, DateTime.new(2010, 2, 13, 5, 10, 23))
126
+ assert_equal "2008-03-14T12:13:14", @conn.send(:date_to_s, Time.utc(2008, 3, 14, 12, 13, 14))
127
+ assert_equal "2008-03-14T12:13:14", @conn.send(:date_to_s, "2008-03-14T12:13:14")
128
+ end
129
+
130
+ def test_build_category_request
131
+ assert_equal fixture_xml_content("category_request"), @conn.send(:build_category_request)
132
+ end
133
+
134
+
135
+ def test_get_category_call
136
+ request = fixture_xml_content("category_request")
137
+ response = fixture_xml_content("category_response")
138
+
139
+ conn = Connection.new("api_key", "user_token")
140
+ conn.expects(:post_xml).with(request).returns(response)
141
+
142
+ categories = conn.get_category_call
143
+ assert_equal 3, categories.size
144
+
145
+ categories.each_with_index do |category, i|
146
+ category_id = (i + 1).to_s
147
+ assert_equal category_id, category.id
148
+ assert_equal "Category #{category_id}", category.name
149
+ end
150
+ end
151
+
152
+ private
153
+
154
+ def assert_api_error_raised(response_fixture, error_type, error_message)
155
+ response = fixture_xml_content(response_fixture)
156
+ begin
157
+ @conn.send(:check_for_api_error, response)
158
+ fail "#{error_type.to_s} was not thrown"
159
+ rescue error_type => e
160
+ assert_equal error_message, e.message
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,20 @@
1
+ require File.dirname(__FILE__) + '/../lib/rshoeboxed'
2
+
3
+ require 'stringio'
4
+ require 'test/unit'
5
+
6
+ gem 'mocha'
7
+ require 'mocha'
8
+
9
+ class Test::Unit::TestCase
10
+ def fixture_xml_content(file_name)
11
+ # Quick way to remove white space and newlines from xml. Makes it easier to compare in tests
12
+ open(File.join(fixture_dir, "#{file_name}.xml"), "r").readlines.inject("") do |contents, line|
13
+ contents + line.strip
14
+ end
15
+ end
16
+
17
+ def fixture_dir
18
+ File.join(File.dirname(__FILE__), "fixtures")
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestConnection < Test::Unit::TestCase
4
+ include RShoeboxed
5
+
6
+ def test_pages
7
+ proxy = ListProxy.new([], 39, 2, 20)
8
+ assert_equal 39, proxy.total
9
+ assert_equal 2, proxy.current_page
10
+ assert_equal 20, proxy.per_page
11
+ assert_equal 2, proxy.pages
12
+
13
+ proxy = ListProxy.new([], 40, 2, 20)
14
+ assert_equal 2, proxy.pages
15
+
16
+ proxy = ListProxy.new([], 0, 1, 20)
17
+ assert_equal 0, proxy.pages
18
+ end
19
+ end
@@ -0,0 +1,45 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestReceipt < Test::Unit::TestCase
4
+ include RShoeboxed
5
+
6
+ def setup
7
+ end
8
+
9
+ def test_initialize_parse_xml
10
+ response = fixture_xml_content("receipt_info_response")
11
+ receipts = Receipt.parse(response)
12
+ assert_equal 1, receipts.size
13
+
14
+ receipt = receipts.first
15
+ assert_equal "1", receipt.id
16
+ assert_equal "Morgan Imports", receipt.store
17
+ assert_equal Date.new(2008, 5, 12), receipt.date
18
+ assert_equal BigDecimal.new("1929.00"), receipt.total
19
+ assert_equal "http://www.shoeboxed.com/receipt.jpeg", receipt.image_url
20
+ assert_equal "1", receipt.category_id
21
+ end
22
+
23
+ def test_receipt__accessors
24
+ receipt = Receipt.new
25
+ assert receipt
26
+
27
+ receipt.store = "Macy's"
28
+ assert_equal "Macy's", receipt.store
29
+
30
+ receipt.id = "1"
31
+ assert_equal "1", receipt.id
32
+
33
+ receipt.total = '$1,000.19'
34
+ assert_equal BigDecimal.new('1000.19'), receipt.total
35
+
36
+ receipt.date = '1/1/2001'
37
+ assert_equal Date.parse('1/1/2001'), receipt.date
38
+
39
+ receipt.image_url = "http://www.example.com/one.image"
40
+ assert_equal "http://www.example.com/one.image", receipt.image_url
41
+
42
+ receipt.category_id = "2"
43
+ assert_equal "2", receipt.category_id
44
+ end
45
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bcurren-rshoeboxed
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ben Curren
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-01-21 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: builder
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 2.1.2
23
+ version:
24
+ - !ruby/object:Gem::Dependency
25
+ name: newgem
26
+ version_requirement:
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 1.1.0
32
+ version:
33
+ - !ruby/object:Gem::Dependency
34
+ name: mocha
35
+ version_requirement:
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.9.4
41
+ version:
42
+ - !ruby/object:Gem::Dependency
43
+ name: hoe
44
+ version_requirement:
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 1.8.0
50
+ version:
51
+ description: Ruby wrapper for the Shoeboxed API.
52
+ email:
53
+ - ben@gobootstrap.com
54
+ executables: []
55
+
56
+ extensions: []
57
+
58
+ extra_rdoc_files:
59
+ - History.txt
60
+ - Manifest.txt
61
+ - README.rdoc
62
+ files:
63
+ - History.txt
64
+ - Manifest.txt
65
+ - README.rdoc
66
+ - Rakefile
67
+ - lib/rshoeboxed.rb
68
+ - lib/rshoeboxed/category.rb
69
+ - lib/rshoeboxed/connection.rb
70
+ - lib/rshoeboxed/list_proxy.rb
71
+ - lib/rshoeboxed/receipt.rb
72
+ - script/console
73
+ - script/destroy
74
+ - script/generate
75
+ - test/fixtures/category_request.xml
76
+ - test/fixtures/category_response.xml
77
+ - test/fixtures/receipt_info_request.xml
78
+ - test/fixtures/receipt_info_response.xml
79
+ - test/fixtures/receipt_request.xml
80
+ - test/fixtures/receipt_response.xml
81
+ - test/fixtures/receipt_response_bad_credentials.xml
82
+ - test/fixtures/receipt_response_internal_error.xml
83
+ - test/fixtures/receipt_response_restricted_ip.xml
84
+ - test/fixtures/receipt_response_unknown_api_call.xml
85
+ - test/fixtures/receipt_response_xml_validation.xml
86
+ - test/fixtures/receipt_with_category_id_request.xml
87
+ - test/test_category.rb
88
+ - test/test_connection.rb
89
+ - test/test_helper.rb
90
+ - test/test_list_proxy.rb
91
+ - test/test_receipt.rb
92
+ has_rdoc: true
93
+ homepage: http://github.com/bcurren/rshoeboxed
94
+ post_install_message:
95
+ rdoc_options:
96
+ - --main
97
+ - README.rdoc
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: "0"
105
+ version:
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: "0"
111
+ version:
112
+ requirements: []
113
+
114
+ rubyforge_project: rshoeboxed
115
+ rubygems_version: 1.2.0
116
+ signing_key:
117
+ specification_version: 2
118
+ summary: Ruby wrapper for the Shoeboxed API.
119
+ test_files:
120
+ - test/test_category.rb
121
+ - test/test_connection.rb
122
+ - test/test_helper.rb
123
+ - test/test_list_proxy.rb
124
+ - test/test_receipt.rb