ffmike-rshoeboxed 0.0.6

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,30 @@
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/parse_error.rb
10
+ lib/rshoeboxed/receipt.rb
11
+ script/console
12
+ script/destroy
13
+ script/generate
14
+ test/fixtures/category_request.xml
15
+ test/fixtures/category_response.xml
16
+ test/fixtures/receipt_info_request.xml
17
+ test/fixtures/receipt_info_response.xml
18
+ test/fixtures/receipt_request.xml
19
+ test/fixtures/receipt_response.xml
20
+ test/fixtures/receipt_response_bad_credentials.xml
21
+ test/fixtures/receipt_response_internal_error.xml
22
+ test/fixtures/receipt_response_restricted_ip.xml
23
+ test/fixtures/receipt_response_unknown_api_call.xml
24
+ test/fixtures/receipt_response_xml_validation.xml
25
+ test/fixtures/receipt_with_category_id_request.xml
26
+ test/test_category.rb
27
+ test/test_connection.rb
28
+ test/test_helper.rb
29
+ test/test_list_proxy.rb
30
+ test/test_receipt.rb
data/README.rdoc ADDED
@@ -0,0 +1,74 @@
1
+ = rshoeboxed
2
+
3
+ Master repo:
4
+
5
+ * http://github.com/bcurren/rshoeboxed
6
+
7
+ == DESCRIPTION:
8
+
9
+ Ruby wrapper for the Shoeboxed API.
10
+
11
+ == SYNOPSIS:
12
+
13
+ === Generate authentication url
14
+
15
+ Use the following code to generate a url to send a user to to authenticate. The first parameter is the
16
+ AppName provided by Shoeboxed. Shoeboxed will redirect back to the return url once a user successfully
17
+ authenticates. When the user is redirected back to the return_url, a token will be provided in the url
18
+ for you to store for future API calls.
19
+
20
+ RShoeboxed::Connection.authentication_url("Outright", "http://example.com")
21
+
22
+ === Get a list of all receipts
23
+
24
+ The api_token is provided by Shoeboxed when you setup your API account. The user_token is retrieved by
25
+ sending a user to authentication_url so they can log in to Shoeboxed. On success, they will be redirected
26
+ to the return_url with the user_token.
27
+
28
+ connection = RShoeboxed::Connection.new("api_token", "user_token")
29
+ receipts = connection.get_receipt_call(Date.new(2008, 1, 1), Date.new(2008, 12, 29))
30
+
31
+ === Get a particular receipt
32
+
33
+ connection = RShoeboxed::Connection.new("api_token", "user_token")
34
+ receipt = connection.get_receipt_info_call("2342442424")
35
+
36
+ === Get a list of all categories
37
+
38
+ connection = RShoeboxed::Connection.new("api_token", "user_token")
39
+ categories = connection.get_category_call
40
+
41
+ === Get a list of all business cards
42
+
43
+ connection = RShoeboxed::Connection.new("api_token", "user_token")
44
+ cards = connection.get_business_card_call(200,1)
45
+
46
+
47
+ == INSTALL:
48
+
49
+ * sudo gem install bcurren_rshoeboxed
50
+
51
+ == LICENSE:
52
+
53
+ (The MIT License)
54
+
55
+ Copyright (c) 2009 Ben Curren
56
+
57
+ Permission is hereby granted, free of charge, to any person obtaining
58
+ a copy of this software and associated documentation files (the
59
+ 'Software'), to deal in the Software without restriction, including
60
+ without limitation the rights to use, copy, modify, merge, publish,
61
+ distribute, sublicense, and/or sell copies of the Software, and to
62
+ permit persons to whom the Software is furnished to do so, subject to
63
+ the following conditions:
64
+
65
+ The above copyright notice and this permission notice shall be
66
+ included in all copies or substantial portions of the Software.
67
+
68
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
69
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
70
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
71
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
72
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
73
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
74
+ 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]
@@ -0,0 +1,26 @@
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
+ begin
12
+ category.id = category_element.attributes["id"]
13
+ category.name = category_element.attributes["name"]
14
+ rescue => e
15
+ raise RShoeboxed::ParseError.new(e, category_element.to_s, "Error parsing category.")
16
+ end
17
+ category
18
+ end
19
+ end
20
+
21
+ def ==(category)
22
+ self.id == category.id && self.name == category.name
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,234 @@
1
+ gem "builder"
2
+ require "builder"
3
+ require "cgi"
4
+ require "net/https"
5
+ require 'rexml/document'
6
+ require 'logger'
7
+ require 'iconv'
8
+
9
+ module RShoeboxed
10
+ class Error < StandardError; end;
11
+ class UnknownAPICallError < Error; end;
12
+ class InternalError < Error; end;
13
+ class AuthenticationError < Error; end;
14
+ class RestrictedIPError < Error; end;
15
+ class XMLValidationError < Error; end;
16
+
17
+ class Connection
18
+ attr_accessor :api_key, :user_token
19
+
20
+ API_SERVER = "app.shoeboxed.com"
21
+ API_PATH = "/ws/api.htm"
22
+ API_URL = "https://" + API_SERVER + API_PATH
23
+
24
+ @@logger = Logger.new(STDOUT)
25
+ def logger
26
+ @@logger
27
+ end
28
+
29
+ def self.log_level=(level)
30
+ @@logger.level = level
31
+ end
32
+ self.log_level = Logger::WARN
33
+
34
+ # Generates a url for you to obtain the user token. This url with take the user to the Shoeboxed authentication
35
+ # page. After the user successfully authenticates, they will be redirected to the app_url with the token and uname
36
+ # as parameters.
37
+ #
38
+ # app_name - string that contains the name of the app that is calling the API
39
+ # app_url - url that Shoeboxed will redirect to after a successful authentication
40
+ # app_params - option hash or array of strings containing params that will be passed back to app_url
41
+ def self.authentication_url(app_name, app_url, app_params={})
42
+ API_URL + "?" + encode_params(
43
+ [[:appname, app_name],
44
+ [:appurl, app_url],
45
+ [:apparams, encode_params(app_params)],
46
+ [:SignIn, 1]]
47
+ )
48
+ end
49
+
50
+
51
+ # The initialization method takes in two params:
52
+ # api_key -
53
+ # user_token (generated after validating from the authentication url)
54
+ def initialize(api_key, user_token)
55
+ @api_key = api_key
56
+ @user_token = user_token
57
+ end
58
+
59
+ def get_receipt_info_call(id)
60
+ request = build_receipt_info_request(id)
61
+ response = post_xml(request)
62
+
63
+ receipts = Receipt.parse(response)
64
+ receipts ? receipts.first : nil
65
+ end
66
+
67
+ # Note: the result_count can only be 50, 100, or 200
68
+ def get_receipt_call(start_date, end_date, options = {})
69
+ options = {
70
+ :use_sell_date => false,
71
+ :per_page => 50,
72
+ :current_page => 1
73
+ }.merge(options)
74
+
75
+ request = build_receipt_request(start_date, end_date, options)
76
+ response = post_xml(request)
77
+
78
+ receipts = Receipt.parse(response)
79
+ wrap_array_with_pagination(receipts, response, options[:current_page], options[:per_page])
80
+ end
81
+
82
+ def get_category_call
83
+ request = build_category_request
84
+ response = post_xml(request)
85
+
86
+ Category.parse(response)
87
+ end
88
+
89
+ def get_business_card_call(options = {})
90
+ request = build_business_card_request(options)
91
+ response = post_xml(request)
92
+
93
+ BusinessCard.parse(response)
94
+ end
95
+
96
+ private
97
+
98
+ def wrap_array_with_pagination(receipts, response, current_page, per_page)
99
+ document = REXML::Document.new(response)
100
+ counts = document.elements.collect("//Receipts") { |element| element.attributes["count"] || 0 }
101
+
102
+ ListProxy.new(receipts, counts.first, current_page, per_page)
103
+ end
104
+
105
+ def date_to_s(date)
106
+ my_date = date
107
+ if !date.kind_of?(String)
108
+ my_date = date.strftime("%Y-%m-%dT%H:%M:%S")
109
+ end
110
+ my_date
111
+ end
112
+
113
+ def self.encode_params(params)
114
+ # If hash, turn to array and give it alpha ordering
115
+ if params.kind_of? Hash
116
+ params = params.to_a
117
+ end
118
+
119
+ encoded_params = params.collect do |name, value|
120
+ "#{CGI.escape(name.to_s)}=#{CGI.escape(value.to_s)}"
121
+ end
122
+ encoded_params.join("&")
123
+ end
124
+
125
+
126
+ def post_xml(body)
127
+ connection = Net::HTTP.new(API_SERVER, 443)
128
+ connection.use_ssl = true
129
+ connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
130
+
131
+ request = Net::HTTP::Post.new(API_PATH)
132
+ request.set_form_data({'xml'=>body})
133
+
134
+ result = connection.start { |http| http.request(request) }
135
+
136
+ # Convert to UTF-8, shoeboxed encodes with ISO-8859-1
137
+ result_body = Iconv.conv('UTF-8', 'ISO-8859-1', result.body)
138
+
139
+ if logger.debug?
140
+ logger.debug "Request:"
141
+ logger.debug body
142
+ logger.debug "Response:"
143
+ logger.debug result_body
144
+ end
145
+
146
+ check_for_api_error(result_body)
147
+ end
148
+
149
+ def check_for_api_error(body)
150
+ document = REXML::Document.new(body)
151
+ root = document.root
152
+ puts root.inspect
153
+ if root && root.name == "Error"
154
+ description = root.attributes["description"]
155
+
156
+ case root.attributes["code"]
157
+ when "1"
158
+ raise AuthenticationError.new(description)
159
+ when "2"
160
+ raise UnknownAPICallError.new(description)
161
+ when "3"
162
+ raise RestrictedIPError.new(description)
163
+ when "4"
164
+ raise XMLValidationError.new(description)
165
+ when "5"
166
+ raise InternalError.new(description)
167
+ end
168
+ end
169
+
170
+ body
171
+ end
172
+
173
+ def build_receipt_request(start_date, end_date, options)
174
+ xml = Builder::XmlMarkup.new
175
+ xml.instruct!
176
+ xml.Request(:xmlns => "urn:sbx:apis:SbxBaseComponents") do |xml|
177
+ append_credentials(xml)
178
+ xml.GetReceiptCall do |xml|
179
+ xml.ReceiptFilter do |xml|
180
+ xml.Results(options[:per_page])
181
+ xml.PageNo(options[:current_page])
182
+ xml.DateStart(date_to_s(start_date))
183
+ xml.DateEnd(date_to_s(end_date))
184
+ xml.UseSellDate(options[:use_sell_date])
185
+ xml.Category(options[:category_id]) if options[:category_id]
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ def build_category_request
192
+ xml = Builder::XmlMarkup.new
193
+ xml.instruct!
194
+ xml.Request(:xmlns => "urn:sbx:apis:SbxBaseComponents") do |xml|
195
+ append_credentials(xml)
196
+ xml.GetCategoryCall
197
+ end
198
+ end
199
+
200
+ def build_business_card_request(options)
201
+ xml = Builder::XmlMarkup.new
202
+ xml.instruct!
203
+ xml.Request(:xmlns => "urn:sbx:apis:SbxBaseComponents") do |xml|
204
+ append_credentials(xml)
205
+ xml.GetBusinessCardCall do |xml|
206
+ xml.BusinessCardFilter do |xml|
207
+ xml.Results(options[:per_page])
208
+ xml.PageNo(options[:current_page])
209
+ end
210
+ end
211
+ end
212
+ end
213
+
214
+ def build_receipt_info_request(id)
215
+ xml = Builder::XmlMarkup.new
216
+ xml.instruct!
217
+ xml.Request(:xmlns => "urn:sbx:apis:SbxBaseComponents") do |xml|
218
+ append_credentials(xml)
219
+ xml.GetReceiptInfoCall do |xml|
220
+ xml.ReceiptFilter do |xml|
221
+ xml.ReceiptId(id)
222
+ end
223
+ end
224
+ end
225
+ end
226
+
227
+ def append_credentials(xml)
228
+ xml.RequesterCredentials do |xml|
229
+ xml.ApiUserToken(@api_key)
230
+ xml.SbxUserToken(@user_token)
231
+ end
232
+ end
233
+ end
234
+ 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,19 @@
1
+ module RShoeboxed
2
+ class ParseError < StandardError
3
+ attr_accessor :original_error, :xml
4
+
5
+ def initialize(original_error, xml, msg = nil)
6
+ @original_error = original_error
7
+ @xml = xml
8
+ super(msg)
9
+ end
10
+
11
+ def to_s
12
+ message = super
13
+
14
+ "Original Error: #{original_error.to_s}\n" +
15
+ "XML: #{xml.to_s}\n" +
16
+ "Message: #{message}\n"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,62 @@
1
+ require 'bigdecimal'
2
+ require 'date'
3
+ require 'rexml/document'
4
+
5
+ module RShoeboxed
6
+ class Receipt
7
+ attr_accessor :id, :store, :image_url, :categories
8
+ attr_reader :sell_date, :created_date, :modified_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
+ begin
15
+ receipt.id = receipt_element.attributes["id"]
16
+ receipt.store = receipt_element.attributes["store"]
17
+ receipt.sell_date = receipt_element.attributes["selldate"]
18
+ receipt.created_date = receipt_element.attributes["createdDate"]
19
+ receipt.modified_date = receipt_element.attributes["modifiedDate"]
20
+ receipt.total = receipt_element.attributes["total"]
21
+ receipt.image_url = receipt_element.attributes["imgurl"]
22
+
23
+ # Get the categories elements and have Category parse them
24
+ category_element = receipt_element.elements["Categories"]
25
+ receipt.categories = category_element ? Category.parse(category_element.to_s) : []
26
+ rescue => e
27
+ raise RShoeboxed::ParseError.new(e, receipt_element.to_s, "Error parsing receipt.")
28
+ end
29
+ receipt
30
+ end
31
+ end
32
+
33
+ def total=(total)
34
+ total.gsub!(/[^\d|.]/, "") if total.is_a?(String)
35
+ total = BigDecimal.new(total) unless total.is_a?(BigDecimal)
36
+
37
+ @total = total
38
+ end
39
+
40
+ def sell_date=(date)
41
+ date = Date.parse(date) if date.is_a?(String)
42
+ @sell_date = date
43
+ end
44
+
45
+ def created_date=(date)
46
+ date = Date.parse(date) if date.is_a?(String)
47
+ @created_date = date
48
+ end
49
+
50
+ def modified_date=(date)
51
+ date = Date.parse(date) if date.is_a?(String)
52
+ @modified_date = date
53
+ end
54
+
55
+ def ==(receipt)
56
+ self.id == receipt.id && self.store == receipt.store && self.image_url == receipt.image_url &&
57
+ self.categories == receipt.categories && self.sell_date == receipt.sell_date &&
58
+ self.modified_date == receipt.modified_date && self.created_date == receipt.created_date &&
59
+ self.total == receipt.total
60
+ end
61
+ end
62
+ end
data/lib/rshoeboxed.rb ADDED
@@ -0,0 +1,14 @@
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/business_card'
8
+ require 'rshoeboxed/connection'
9
+ require 'rshoeboxed/list_proxy'
10
+ require 'rshoeboxed/parse_error'
11
+
12
+ module RShoeboxed
13
+ VERSION = '0.0.6'
14
+ 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,5 @@
1
+ <Receipt store="Morgan Imports" id="1" total="$1,929.00" selldate="5/12/2008" createdDate="4/12/2008" modifiedDate="4/20/2008" imgurl="http://www.shoeboxed.com/receipt.jpeg">
2
+ <Categories>
3
+ <Category name="Category 1" id="1"/>
4
+ </Categories>
5
+ </Receipt>
@@ -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,14 @@
1
+ <GetReceiptCallResponse>
2
+ <Receipts count="2">
3
+ <Receipt store="Great Plains Trust Company" id="23984923842" total="$3,378.30" selldate="5/12/2008" createdDate="4/12/2008" modifiedDate="4/20/2008" imgurl="http://www.shoeboxed.com/receipt1.jpeg">
4
+ <Categories>
5
+ <Category name="Category 1" id="1"/>
6
+ </Categories>
7
+ </Receipt>
8
+ <Receipt store="RadioShack" id="39239293" total="$3.51" selldate="5/12/2008" createdDate="4/12/2008" modifiedDate="4/20/2008" imgurl="http://www.shoeboxed.com/receipt2.jpeg">
9
+ <Categories>
10
+ <Category name="Category 2" id="2"/>
11
+ </Categories>
12
+ </Receipt>
13
+ </Receipts>
14
+ </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,177 @@
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
+
9
+ @category1 = Category.new
10
+ @category1.id = "1"
11
+ @category1.name = "Category 1"
12
+
13
+ @category2 = Category.new
14
+ @category2.id = "2"
15
+ @category2.name = "Category 2"
16
+ end
17
+
18
+ def test_authenticate__connection_failures
19
+ assert true
20
+ end
21
+
22
+ def test_authentication_url__success_with_options
23
+ assert_equal "https://app.shoeboxed.com/ws/api.htm?appname=Bootstrap&appurl=http%3A%2F%2Fexample.com&apparams=test%3D1%26test2%3D1&SignIn=1",
24
+ Connection.authentication_url("Bootstrap", "http://example.com", [[:test, 1], [:test2, 1]])
25
+ end
26
+
27
+
28
+ def test_authentication_url__success_no_options
29
+ assert_equal "https://app.shoeboxed.com/ws/api.htm?appname=Bootstrap&appurl=http%3A%2F%2Fexample.com&apparams=&SignIn=1",
30
+ Connection.authentication_url("Bootstrap", "http://example.com")
31
+ end
32
+
33
+ def test_connection__instantiate_object
34
+ assert_equal "api_key", @conn.api_key
35
+ assert_equal "user_token", @conn.user_token
36
+ end
37
+
38
+
39
+ def test_get_receipt_info_call__success_getting_one_receipt
40
+ request = fixture_xml_content("receipt_info_request")
41
+ response = fixture_xml_content("receipt_info_response")
42
+
43
+ conn = Connection.new("api_key", "user_token")
44
+ conn.expects(:post_xml).with(request).returns(response)
45
+
46
+ receipt = conn.get_receipt_info_call('1')
47
+ assert_not_nil receipt
48
+ assert_equal "1", receipt.id
49
+ assert_equal "Morgan Imports", receipt.store
50
+ assert_equal Date.new(2008, 5, 12), receipt.sell_date
51
+ assert_equal Date.new(2008, 4, 12), receipt.created_date
52
+ assert_equal Date.new(2008, 4, 20), receipt.modified_date
53
+ assert_equal BigDecimal.new("1929.00"), receipt.total
54
+ assert_equal "http://www.shoeboxed.com/receipt.jpeg", receipt.image_url
55
+ assert_equal [@category1], receipt.categories
56
+ end
57
+
58
+ def test_build_receipt_info_call_request
59
+ assert_equal fixture_xml_content("receipt_info_request"), @conn.send(:build_receipt_info_request, "1")
60
+ end
61
+
62
+ def test_get_receipt_call__success_getting_two_receipt
63
+ request = fixture_xml_content("receipt_request")
64
+ response = fixture_xml_content("receipt_response")
65
+
66
+ conn = Connection.new("api_key", "user_token")
67
+ conn.expects(:post_xml).with(request).returns(response)
68
+
69
+ receipts = conn.get_receipt_call(Date.new(2008, 1, 1), Date.new(2009, 1, 1))
70
+ assert_equal 2, receipts.total
71
+ assert_equal 1, receipts.current_page
72
+ assert_equal 50, receipts.per_page
73
+ assert_equal 1, receipts.pages
74
+ assert_equal 2, receipts.size
75
+
76
+ receipt = receipts[0]
77
+ assert_equal "23984923842", receipt.id
78
+ assert_equal "Great Plains Trust Company", receipt.store
79
+ assert_equal Date.new(2008, 5, 12), receipt.sell_date
80
+ assert_equal Date.new(2008, 4, 12), receipt.created_date
81
+ assert_equal Date.new(2008, 4, 20), receipt.modified_date
82
+ assert_equal BigDecimal.new("3378.30"), receipt.total
83
+ assert_equal "http://www.shoeboxed.com/receipt1.jpeg", receipt.image_url
84
+ assert_equal [@category1], receipt.categories
85
+
86
+ receipt = receipts[1]
87
+ assert_equal "39239293", receipt.id
88
+ assert_equal "RadioShack", receipt.store
89
+ assert_equal Date.new(2008, 5, 12), receipt.sell_date
90
+ assert_equal Date.new(2008, 4, 12), receipt.created_date
91
+ assert_equal Date.new(2008, 4, 20), receipt.modified_date
92
+ assert_equal BigDecimal.new("3.51"), receipt.total
93
+ assert_equal "http://www.shoeboxed.com/receipt2.jpeg", receipt.image_url
94
+ assert_equal [@category2], receipt.categories
95
+ end
96
+
97
+ def test_build_receipt_request
98
+ options = {
99
+ :per_page => 50,
100
+ :current_page => 1,
101
+ :use_sell_date => false
102
+ }
103
+ assert_equal fixture_xml_content("receipt_request"),
104
+ @conn.send(:build_receipt_request, Date.new(2008, 1, 1), Date.new(2009, 1, 1), options)
105
+ end
106
+
107
+ def test_build_receipt_request__category_filter
108
+ options = {
109
+ :per_page => 50,
110
+ :current_page => 1,
111
+ :use_sell_date => true,
112
+ :category_id => 10
113
+ }
114
+ assert_equal fixture_xml_content("receipt_with_category_id_request"),
115
+ @conn.send(:build_receipt_request, Date.new(2008, 1, 1), Date.new(2009, 1, 1), options)
116
+ end
117
+
118
+ def test_check_for_api_error__all_error_codes
119
+ assert_api_error_raised("receipt_response_bad_credentials", AuthenticationError, "Bad credentials")
120
+ assert_api_error_raised("receipt_response_unknown_api_call", UnknownAPICallError, "Unknown API Call")
121
+ assert_api_error_raised("receipt_response_restricted_ip", RestrictedIPError, "Restricted IP")
122
+ assert_api_error_raised("receipt_response_xml_validation", XMLValidationError, "XML Validation")
123
+ assert_api_error_raised("receipt_response_unknown_api_call", UnknownAPICallError, "Unknown API Call")
124
+ end
125
+
126
+ def test_check_for_api_error__no_error
127
+ assert_nothing_raised do
128
+ assert_equal "", @conn.send(:check_for_api_error, "")
129
+ end
130
+
131
+ response = fixture_xml_content("receipt_info_response")
132
+ assert_nothing_raised do
133
+ assert_equal response, @conn.send(:check_for_api_error, response)
134
+ end
135
+ end
136
+
137
+ def test_date_to_s
138
+ assert_equal "2009-01-15T00:00:00", @conn.send(:date_to_s, Date.new(2009, 1, 15))
139
+ assert_equal "2010-02-13T05:10:23", @conn.send(:date_to_s, DateTime.new(2010, 2, 13, 5, 10, 23))
140
+ assert_equal "2008-03-14T12:13:14", @conn.send(:date_to_s, Time.utc(2008, 3, 14, 12, 13, 14))
141
+ assert_equal "2008-03-14T12:13:14", @conn.send(:date_to_s, "2008-03-14T12:13:14")
142
+ end
143
+
144
+ def test_build_category_request
145
+ assert_equal fixture_xml_content("category_request"), @conn.send(:build_category_request)
146
+ end
147
+
148
+
149
+ def test_get_category_call
150
+ request = fixture_xml_content("category_request")
151
+ response = fixture_xml_content("category_response")
152
+
153
+ conn = Connection.new("api_key", "user_token")
154
+ conn.expects(:post_xml).with(request).returns(response)
155
+
156
+ categories = conn.get_category_call
157
+ assert_equal 3, categories.size
158
+
159
+ categories.each_with_index do |category, i|
160
+ category_id = (i + 1).to_s
161
+ assert_equal category_id, category.id
162
+ assert_equal "Category #{category_id}", category.name
163
+ end
164
+ end
165
+
166
+ private
167
+
168
+ def assert_api_error_raised(response_fixture, error_type, error_message)
169
+ response = fixture_xml_content(response_fixture)
170
+ begin
171
+ @conn.send(:check_for_api_error, response)
172
+ fail "#{error_type.to_s} was not thrown"
173
+ rescue error_type => e
174
+ assert_equal error_message, e.message
175
+ end
176
+ end
177
+ 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,58 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestReceipt < Test::Unit::TestCase
4
+ include RShoeboxed
5
+
6
+ def setup
7
+ @category1 = Category.new
8
+ @category1.id = "1"
9
+ @category1.name = "Category 1"
10
+ end
11
+
12
+ def test_initialize_parse_xml
13
+ response = fixture_xml_content("receipt_info_response")
14
+ receipts = Receipt.parse(response)
15
+ assert_equal 1, receipts.size
16
+
17
+ receipt = receipts.first
18
+ assert_equal "1", receipt.id
19
+ assert_equal "Morgan Imports", receipt.store
20
+ assert_equal Date.new(2008, 5, 12), receipt.sell_date
21
+ assert_equal Date.new(2008, 4, 12), receipt.created_date
22
+ assert_equal Date.new(2008, 4, 20), receipt.modified_date
23
+ assert_equal BigDecimal.new("1929.00"), receipt.total
24
+ assert_equal "http://www.shoeboxed.com/receipt.jpeg", receipt.image_url
25
+ assert_equal [@category1], receipt.categories
26
+ end
27
+
28
+ def test_receipt__accessors
29
+ receipt = Receipt.new
30
+ assert receipt
31
+
32
+ receipt.store = "Macy's"
33
+ assert_equal "Macy's", receipt.store
34
+
35
+ receipt.id = "1"
36
+ assert_equal "1", receipt.id
37
+
38
+ receipt.total = '$1,000.19'
39
+ assert_equal BigDecimal.new('1000.19'), receipt.total
40
+
41
+ receipt.created_date = '1/2/2001'
42
+ assert_equal Date.parse('1/2/2001'), receipt.created_date
43
+
44
+ receipt.modified_date = '1/3/2001'
45
+ assert_equal Date.parse('1/3/2001'), receipt.modified_date
46
+
47
+ receipt.image_url = "http://www.example.com/one.image"
48
+ assert_equal "http://www.example.com/one.image", receipt.image_url
49
+
50
+ receipt.categories = [@category1]
51
+ assert_equal [@category1], receipt.categories
52
+ end
53
+
54
+ def test_equal
55
+ # lame test but at least execute the code
56
+ assert_equal Receipt.new, Receipt.new
57
+ end
58
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ffmike-rshoeboxed
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.6
5
+ platform: ruby
6
+ authors:
7
+ - Ben Curren
8
+ - William Chow
9
+ - Mike Gunderloy
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2009-02-13 00:00:00 -08:00
15
+ default_executable:
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: builder
19
+ type: :runtime
20
+ version_requirement:
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 2.1.2
26
+ version:
27
+ - !ruby/object:Gem::Dependency
28
+ name: newgem
29
+ type: :development
30
+ version_requirement:
31
+ version_requirements: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 1.1.0
36
+ version:
37
+ - !ruby/object:Gem::Dependency
38
+ name: mocha
39
+ type: :development
40
+ version_requirement:
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: 0.9.4
46
+ version:
47
+ - !ruby/object:Gem::Dependency
48
+ name: hoe
49
+ type: :development
50
+ version_requirement:
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: 1.8.0
56
+ version:
57
+ description: Ruby wrapper for the Shoeboxed API.
58
+ email:
59
+ - ben@outright.com
60
+ - will@outright.com
61
+ - MikeG1@larkfarm.com
62
+ executables: []
63
+
64
+ extensions: []
65
+
66
+ extra_rdoc_files:
67
+ - History.txt
68
+ - Manifest.txt
69
+ - README.rdoc
70
+ files:
71
+ - History.txt
72
+ - Manifest.txt
73
+ - README.rdoc
74
+ - Rakefile
75
+ - lib/rshoeboxed.rb
76
+ - lib/rshoeboxed/category.rb
77
+ - lib/rshoeboxed/connection.rb
78
+ - lib/rshoeboxed/list_proxy.rb
79
+ - lib/rshoeboxed/parse_error.rb
80
+ - lib/rshoeboxed/receipt.rb
81
+ - script/console
82
+ - script/destroy
83
+ - script/generate
84
+ - test/fixtures/category_request.xml
85
+ - test/fixtures/category_response.xml
86
+ - test/fixtures/receipt_info_request.xml
87
+ - test/fixtures/receipt_info_response.xml
88
+ - test/fixtures/receipt_request.xml
89
+ - test/fixtures/receipt_response.xml
90
+ - test/fixtures/receipt_response_bad_credentials.xml
91
+ - test/fixtures/receipt_response_internal_error.xml
92
+ - test/fixtures/receipt_response_restricted_ip.xml
93
+ - test/fixtures/receipt_response_unknown_api_call.xml
94
+ - test/fixtures/receipt_response_xml_validation.xml
95
+ - test/fixtures/receipt_with_category_id_request.xml
96
+ - test/test_category.rb
97
+ - test/test_connection.rb
98
+ - test/test_helper.rb
99
+ - test/test_list_proxy.rb
100
+ - test/test_receipt.rb
101
+ has_rdoc: true
102
+ homepage: http://github.com/bcurren/rshoeboxed
103
+ post_install_message:
104
+ rdoc_options:
105
+ - --main
106
+ - README.rdoc
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: "0"
114
+ version:
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: "0"
120
+ version:
121
+ requirements: []
122
+
123
+ rubyforge_project: rshoeboxed
124
+ rubygems_version: 1.2.0
125
+ signing_key:
126
+ specification_version: 2
127
+ summary: Ruby wrapper for the Shoeboxed API.
128
+ test_files:
129
+ - test/test_category.rb
130
+ - test/test_connection.rb
131
+ - test/test_helper.rb
132
+ - test/test_list_proxy.rb
133
+ - test/test_receipt.rb