peddler 0.1.3

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.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .DS_Store
data/History.txt ADDED
@@ -0,0 +1,8 @@
1
+ == 0.1.0 / 2009-08-14
2
+ * First public release.
3
+ == 0.1.1 / 2009-08-15
4
+ * Bumping gem to tag correctly on Github.
5
+ == 0.1.2 / 2009-08-17
6
+ * Minor stuff.
7
+ == 0.1.3 / 2009-11-06
8
+ * Nothing major, Tom.
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2009 Hakan Şenol Ensari
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 NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,118 @@
1
+ = Peddler
2
+
3
+ Peddler is a Ruby wrapper to the Amazon Inventory management API.
4
+
5
+ == Example usage
6
+
7
+ Fire off a client:
8
+
9
+ client = Peddler::Client.new(
10
+ :username => "foo@bar.com",
11
+ :password => "secret",
12
+ :region => "us")
13
+
14
+ Create an inventory file:
15
+
16
+ batch = client.new_inventory_batch
17
+ item = client.new_inventory_item(
18
+ :product_id => "1234567890",
19
+ :price => 100.00,
20
+ :sku => "SKU-123",
21
+ :quantity => 10)
22
+ batch << item
23
+ ...
24
+
25
+ Repeat ad infinitum and upload:
26
+
27
+ batch.upload
28
+
29
+ The batch now should have an upload ID. Go ahead and check the error log:
30
+
31
+ upload_log = client.new_report(
32
+ :upload,
33
+ :id => batch.id)
34
+ upload_log.body
35
+ => "Feed Processing Summary:\n\tNumber of records processed\t\t1\n\tNumber of records successful\t\t1\n\n"
36
+
37
+ You're done listing and are wondering if you have any new orders:
38
+
39
+ orders_report = client.new_report :order
40
+ orders = client.detab(orders_report.body)
41
+ p orders.size
42
+ => 1500
43
+ p orders[0].item_name
44
+ => "A Thousand Plateaus: Capitalism and Schizophrenia (Paperback) by Gilles Deleuze"
45
+
46
+ Now that you have diligently processed the orders, post back the results to Amazon:
47
+
48
+ feed = client.new_order_fulfillment_feed
49
+ fulfilled_order = client.new_fulfilled_order(
50
+ :order_id => "123-1234567-1234567",
51
+ :order_date => "2009-08-01",
52
+ :carrier_code => "USPS",
53
+ :tracking_number => "0308 0330 0000 0000 0000")
54
+ feed << fulfilled_order
55
+
56
+ Again, repeat until done and upload:
57
+
58
+ feed.upload
59
+
60
+ Curious to see the processing report?
61
+
62
+ p feed.status
63
+ => "_SUBMITTED_"
64
+
65
+ Refresh until you get:
66
+
67
+ p feed.status!
68
+ => "_DONE_"
69
+
70
+ Finally, check the report:
71
+
72
+ p feed.download.to_s
73
+ => ...
74
+
75
+ Sadly, you also have an order you can't fulfill. No problem. The workflow is similar:
76
+
77
+ feed = client.new_order_cancellation_feed
78
+ cancelled_order = client.new_cancelled_order(
79
+ :order_id => "123-1234567-1234567",
80
+ :cancellation_reason_code => "NoInventory",
81
+ :amazon_order_item_code => "12341234567890")
82
+ feed << cancelled_order
83
+ feed.upload
84
+ sleep(60)
85
+ feed.status!
86
+ => "_DONE_"
87
+ p feed.download.to_s
88
+ => ...
89
+
90
+ Need to post a partial refund? You'll have to revert to the older batch refund API method:
91
+
92
+ refunds = client.new_refund_batch
93
+ refund = client.new_refund(
94
+ :order_id => "123-1234567-1234567",
95
+ :payments_transaction_id => "12341234567890",
96
+ :refund_amount => 1.00,
97
+ :reason => "GeneralAdjustment",
98
+ :message => "With our apologies.")
99
+ refunds << refund
100
+ refunds.upload
101
+
102
+ Great sales. For a change, let's download something different from Amazon. Here's a preorder report:
103
+
104
+ preorder_report = client.new_report(
105
+ :preorder,
106
+ :product_line => "Books",
107
+ :frequency => 2)
108
+ preorders = client.detab(preorder_report.body)
109
+ p preorders.size
110
+ => 2000
111
+ p preorders[0].asin
112
+ => "1234567890"
113
+ p preorders[0].average_asking_price
114
+ => "100"
115
+
116
+ Run rdoc and check the source for more detailed info.
117
+
118
+ Copyright © 2009 Hakan Senol Ensari, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require "rubygems"
2
+ require "rake"
3
+ begin
4
+ require "jeweler"
5
+ Jeweler::Tasks.new do |s|
6
+ s.name = "peddler"
7
+ s.summary = "A Ruby wrapper to the Amazon Inventory Management API"
8
+ s.email = "hakan.ensari@papercavalier.com"
9
+ s.homepage = "http://snl.github.com/peddler"
10
+ s.description = "Peddler is a Ruby wrapper to the Amazon Inventory Management API."
11
+ s.authors = ["Hakan Senol Ensari"]
12
+ s.add_dependency "xml-simple"
13
+ s.add_development_dependency "rspec"
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
18
+ end
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 1
4
+ :patch: 3
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'peddler'
@@ -0,0 +1,215 @@
1
+ # = Peddler
2
+ # Peddler is a Ruby wrapper to the Amazon Inventory management API.
3
+ #
4
+ # Peddler::Client has some detailed explanation and examples of usage.
5
+ module Peddler
6
+ # This is the public interface of the Peddler library.
7
+ class Client
8
+ # Creates a client instance.
9
+ #
10
+ # client = Peddler::Client.new :username => "foo@bar.com",
11
+ # :password => "secret",
12
+ # :region => "us"
13
+ #
14
+ def initialize(params={})
15
+ params.each_pair { |key, value| self.send("#{key}=", value) }
16
+ end
17
+
18
+ def username=(username)
19
+ self.transport.username = username
20
+ end
21
+
22
+ def password=(password)
23
+ self.transport.password = password
24
+ end
25
+
26
+ # Sets Amazon region.
27
+ #
28
+ # Possible regions: ["us", "uk", "de", "ca", "fr", "jp"]
29
+ def region=(region)
30
+ self.transport.region = region
31
+ end
32
+
33
+ # Creates an inventory batch.
34
+ #
35
+ # batch = client.new_inventory_batch
36
+ # book = new_inventory_item(
37
+ #  :product_id => "1234567890",
38
+ # :sku => "SKU-001",
39
+ # :price => 10.00,
40
+ # :quantity => 1)
41
+ # batch << book
42
+ # batch.upload
43
+ # report = client.new_report :upload, :id => batch.id
44
+ # p report.body
45
+ # => "Feed Processing Summary:\n\tNumber of records processed\t\t1\n\tNumber of records successful\t\t1\n\n"
46
+ #
47
+ def new_inventory_batch
48
+ Peddler::Inventory::Batch.new(self.transport.dup)
49
+ end
50
+
51
+ # Creates an inventory item. Parameter keys are lowercased and underscored but otherwise the same as
52
+ # Amazon's colum titles in their tab-delimited templates.
53
+ def new_inventory_item(params={})
54
+ Peddler::Inventory::Item.new(params)
55
+ end
56
+
57
+ # Returns count of pending inventory uploads queued at Amazon.
58
+ def inventory_queue
59
+ Peddler::Inventory::Queue.count(self.transport)
60
+ end
61
+
62
+ # Creates an order fulfillment batch.
63
+ #
64
+ # feed = client.new_order_fulfillment_feed
65
+ # fulfilled_order = client.new_fulfilled_order(
66
+ # :order_id => "123-1234567-1234567",
67
+ # :order_date => "2009-08-01")
68
+ # feed << fulfilled_order
69
+ # feed.upload
70
+ # feed.status
71
+ # => "_SUBMITTED_"
72
+ # sleep(60)
73
+ # feed.status!
74
+ # => "_DONE_"
75
+ # p feed.download.to_s
76
+ #
77
+ def new_order_fulfillment_feed
78
+ Peddler::Feeds::OrderFulfillment::Batch.new(self.transport.dup)
79
+ end
80
+
81
+ # Creates an item that can then be added to an order fulfillment feed. Keys are lowercased and underscored but
82
+ # otherwise the same as Amazon's headers. See section 7.1 in the API docs.
83
+ def new_fulfilled_order(params={})
84
+ Peddler::Feeds::OrderFulfillment::Item.new(params)
85
+ end
86
+
87
+ # Creates an order cancellation batch.
88
+ #
89
+ # feed = client.new_order_cancellation_feed
90
+ # cancelled_order = client.new_cancelled_order(
91
+ # :order_id => "123-1234567-1234567",
92
+ # :cancellation_reason_code => "NoInventory",
93
+ # :amazon_order_item_code => "12341234567890")
94
+ # feed << cancelled_order
95
+ # feed.upload
96
+ # feed.status
97
+ # => "_SUBMITTED_"
98
+ # sleep(60)
99
+ # feed.status!
100
+ # => "_DONE_"
101
+ # p feed.download.to_s
102
+ #
103
+ def new_order_cancellation_feed
104
+ Peddler::Feeds::OrderCancellation::Batch.new(self.transport.dup)
105
+ end
106
+
107
+ # Creates an item that can then be added to an order cancellation feed. Keys are lowercased and underscored but
108
+ # otherwise the same as Amazon's headers. See section 7.4 in the API docs.
109
+ def new_cancelled_order(params={})
110
+ Peddler::Feeds::OrderCancellation::Item.new(params)
111
+ end
112
+
113
+ # Creates a refund batch.
114
+ #
115
+ # batch = client.new_refund_batch
116
+ # refund = client.new_refund(
117
+ # :order_id => "123-1234567-1234567",
118
+ # :payments_transaction_id => "12341234567890",
119
+ # :refund_amount => 10.00,
120
+ # :reason => "CouldNotShip",
121
+ # :message => "With our apologies.")
122
+ # batch << refund
123
+ # batch.upload
124
+ # sleep(60)
125
+ # status = client.latest_reports :batch_refund, :count => 1
126
+ # report = client.new_report(
127
+ # :batch_refund,
128
+ # :id => status[0].id)
129
+ # p report.body
130
+ # => "123-1234567-1234567order-item-id: 12341234567890\tSUCCESS 10.00 is Refunded.\r\n"
131
+ #
132
+ def new_refund_batch
133
+ Peddler::Refunds::Batch.new(self.transport.dup)
134
+ end
135
+
136
+ # Creates a refund item that can then be added to a refund batch.
137
+ #
138
+ # Possible reasons: ["GeneralAdjustment", "CouldNotShip", "DifferentItem", "MerchandiseNotReceived", "MerchandiseNotAsDescribed"]
139
+ def new_refund(params={})
140
+ Peddler::Refunds::Item.new(params)
141
+ end
142
+
143
+ # Creates an instance for an already-generated report. Works only with what I call legacy reports, that is,
144
+ # anything that comes before section 7 in the API docs.
145
+ #
146
+ # Possible report names: [:upload, :order, :preorder, :batch_refund, :open_listings, :open_listings_lite, :open_listings_liter]
147
+ #
148
+ # You can download a specific report by using its ID. Otherwise, the instance will fetch the latest available report. One
149
+ # oddball exception: upload reports do require an ID and will return nil if you don't provide one.
150
+ #
151
+ # orders_report = client.new_report :order
152
+ # orders = client.detab(orders_report.body)
153
+ # orders[0].buyer_name
154
+ # => "John Doe"
155
+ #
156
+ # preorders_report = client.new_report(
157
+ #  :preorder,
158
+ # :product_line => "Books",
159
+ # :frequency => 2)
160
+ # preorders = client.detab(preorders_report.body)
161
+ # preorders[0].average_asking_price
162
+ # => "100"
163
+ #
164
+ def new_report(name,params={})
165
+ Peddler::LegacyReports::Report.new(self.transport.dup, name, params)
166
+ end
167
+
168
+ # Requests a report. Returns true when successful.
169
+ #
170
+ # Possible report names: [:order, :open_listings, :open_listings_lite, :open_listings_liter]
171
+ #
172
+ # client.generate_report :order, :number_of_days => 15
173
+ #
174
+ # A word of caution. Open listings may crap up with larger inventories. I will have to migrate to a cURL-based
175
+ # HTTP client to get that working again.
176
+ def generate_report(name,params={})
177
+ Peddler::LegacyReports.generate(self.transport, name, params)
178
+ end
179
+
180
+ # Creates an unshipped order report. Takes on some optional parameters, such as :id, :starts_at, :ends_at. By default,
181
+ # it will request a new unshipped order report for the past seven days.
182
+ #
183
+ # report = client.new_unshipped_orders_report
184
+ # report.status
185
+ # => "_SUBMITTED_"
186
+ # sleep(60)
187
+ # report.status!
188
+ # => "_DONE_"
189
+ # p report.unshipped_orders
190
+ #
191
+ def new_unshipped_orders_report(params={})
192
+ Peddler::Reports::UnshippedOrdersReport.new(self.transport.dup, params)
193
+ end
194
+
195
+ # Returns status of most recent reports. Optional "count" defaults to 10. Name can be [ :upload, :order, :batch_refund, :open_listings, :open_listings_lite, :open_listings_liter ].
196
+ #
197
+ # reports = client.latest_reports :order, :count => 1
198
+ # reports[0]
199
+ # => #<Peddler::LegacyReports::ReportStatus starts_at="07-29-2009:10-00-06" ...
200
+ #
201
+ def latest_reports(name,params={})
202
+ Peddler::LegacyReports.latest(self.transport, name, params)
203
+ end
204
+
205
+ # Decodes tab-delimited content into an array of OpenStruct objects.
206
+ def detab(msg)
207
+ Peddler::Handlers::TabDelimitedHandler.decode_response(msg)
208
+ end
209
+
210
+ protected
211
+ def transport #:nodoc:all
212
+ @transport ||= Peddler::Transport.new
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,184 @@
1
+ module Peddler
2
+ # This module includes functionality to handle the charge-when-ship-related feeds Amazon added to the API
3
+ # in its latest incarnation in 2009.
4
+ module Feeds
5
+ # Downloadable file. The processing report in case of feeds. Outputs Amazon's response verbatim.
6
+ # Will add functionality to parse the response some time down the road.
7
+ class Download
8
+ attr_accessor :id, :type, :related_reference_id, :available_at, :acknowledged
9
+
10
+ def initialize(transport, params={})
11
+ @mapped_params = {
12
+ "DownloadId" => "id",
13
+ "DownloadType" => "type",
14
+ "RelatedReferenceId" => "related_reference_id",
15
+ "AvailableDate" => "available_at",
16
+ "Acknowledged" => "acknowledged"}
17
+ @transport = transport
18
+ params.each_pair{ |key, value| self.send "#{@mapped_params[key]}=", value }
19
+ end
20
+
21
+ # Retrieves and returns report
22
+ def to_s
23
+ @body ||= download_report
24
+ end
25
+ private
26
+ def download_report
27
+ return nil if @id.nil?
28
+ @transport.modernize_request
29
+ @transport.query_params.merge!({
30
+ "Action" => "download",
31
+ "downloadId" => @id})
32
+ @transport.execute_request
33
+ end
34
+ end
35
+
36
+ # This is the base class.
37
+ class Feed
38
+ attr_writer :file_content
39
+ attr_accessor :batch, :download, :status, :type, :id, :submitted_at, :started_processing_at, :completed_processing_at, :messages_processed, :messages_successful, :messages_with_errors, :messages_with_warnings
40
+
41
+ def initialize(transport)
42
+ @transport = transport
43
+ @batch = []
44
+ @mapped_params = {
45
+ "UploadStatus" => "status",
46
+ "UploadType" => "type",
47
+ "UploadId" => "id",
48
+ "SubmittedDate" => "submitted_at",
49
+ "StartedProcessingDate" => "started_processing_at",
50
+ "CompletedProcessingDate" => "completed_processing_at",
51
+ "CompletedProcesssingDate" => "completed_processing_at",
52
+ "MessagesProcessed" => "messages_processed",
53
+ "MessagesSuccessful" => "messages_successful",
54
+ "MessagesWithErrors" => "messages_with_errors",
55
+ "MessagesWithWarnings" => "messages_with_warnings"}
56
+ end
57
+
58
+ # Returns content of the upload file.
59
+ def file_content
60
+ return @file_content if @file_content
61
+ out = @file_header
62
+ @batch.each{ |item| out << item.to_s }
63
+ @file_content = out
64
+ end
65
+
66
+ # Returns status and will also refresh if not already "done."
67
+ def status!
68
+ return @status if @status.nil? || @status =~ /_DONE_/
69
+ refresh_status
70
+ @status
71
+ end
72
+
73
+ # Uploads batch.
74
+ def upload
75
+ raise PeddlerError.new("Batch already uploaded") unless @id.nil?
76
+ @transport.modernize_request
77
+ @transport.query_params.merge!({
78
+ "Action" => "upload",
79
+ "uploadType" => @type})
80
+ @transport.body = file_content
81
+ res = @transport.execute_request
82
+ process_response(res)
83
+ @status
84
+ end
85
+
86
+ # Adds an item to the batch.
87
+ def <<(item)
88
+ @batch << item
89
+ end
90
+ private
91
+ def refresh_status
92
+ @transport.modernize_request
93
+ @transport.query_params.merge!({
94
+ "Action" => "uploadStatus",
95
+ "uploadId" => @id})
96
+ res = @transport.execute_request
97
+ process_response(res)
98
+ end
99
+
100
+ def process_response(res)
101
+ xml = Peddler::Handlers::XMLHandler.decode_response(res)
102
+ params = Peddler::Handlers::XMLHandler.parse(:upload, xml)
103
+ if params[0]
104
+ params[0].each_pair do |key, value|
105
+ if key == "RelatedDownloadsList"
106
+ params = Peddler::Handlers::XMLHandler.parse(:download, value)
107
+ @download = Peddler::Feeds::Download.new(@transport, params[0])
108
+ else
109
+ self.send "#{@mapped_params[key]}=", value
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ module OrderFulfillment
117
+ # This class contains methods to upload order fulfillment info to Amazon.
118
+ # See sections 7.1 through 7.3 in the API documentation for more detail.
119
+ class Batch < Peddler::Feeds::Feed
120
+ def initialize(transport)
121
+ @file_header = "order-id\torder-item-id\tquantity\tship-date\tcarrier-code\tcarrier-name\ttracking-number\tship-method\r\n"
122
+ @type = "_POST_FLAT_FILE_FULFILLMENT_DATA_"
123
+ super(transport)
124
+ end
125
+ end
126
+
127
+ # This is an order fulfillment item.
128
+ class Item
129
+ attr_accessor :order_id, :order_item_id, :quantity, :ship_date, :carrier_name, :tracking_number, :ship_method
130
+ attr_reader :carrier_code
131
+
132
+ def initialize(params={})
133
+ params.each_pair{ |key, value| send("#{key}=", value) }
134
+ end
135
+
136
+ # Validates when setting carrier code.
137
+ def carrier_code=(carrier_code)
138
+ @carrier_code = carrier_code if %w{USPS UPS FedEx other}.include?(carrier_code)
139
+ end
140
+
141
+ # Outputs a formatted line for the tab-delimited upload file.
142
+ def to_s
143
+ "#{@order_id}\t#{@order_item_id}\t#{@quantity}\t#{@ship_date}\t#{@carrier_code}\t#{@carrier_name}\t#{@tracking_number}\t#{@ship_method}\r\n"
144
+ end
145
+ end
146
+ end
147
+
148
+ # This module contains methods to upload cancelled orders to Amazon.
149
+ # See section 7.4 in the API documentation for more detail.
150
+ module OrderCancellation
151
+ class Batch < Peddler::Feeds::Feed
152
+ def initialize(transport)
153
+ @file_header = "TemplateType=OrderCancellation Version=1.0/1.0.3 This row for Amazon.com use only. Do not modify or delete.\r\n" +
154
+ "order-id\tcancellation-reason-code\tamazon-order-item-code\r\n"
155
+ @type = "_POST_FLAT_FILE_ORDER_ACKNOWLEDGEMENT_DATA_"
156
+ super(transport)
157
+ end
158
+ end
159
+
160
+ # This is a cancelled order item.
161
+ class Item
162
+ attr_accessor :order_id, :amazon_order_item_code
163
+ attr_reader :cancellation_reason_code
164
+
165
+ def initialize(params={})
166
+ params.each_pair{ |key, value| send("#{key}=", value) }
167
+ end
168
+
169
+ # Validates when setting cancellation reason code.
170
+ def cancellation_reason_code=(cancellation_reason_code)
171
+ @cancellation_reason_code = cancellation_reason_code if %w{ BuyerCanceled CustomerExchange CustomerReturn GeneralAdjustment MerchandiseNotReceived NoInventory ShippingAddressUndeliverable }.include?(cancellation_reason_code)
172
+ end
173
+
174
+ # Outputs a formatted line for the tab-delimited upload file.
175
+ def to_s
176
+ if @cancellation_reason_code.nil? != @amazon_order_item_code.nil?
177
+ raise PeddlerError.new("Provide codes for both cancellation reason and Amazon order item (or omit both).")
178
+ end
179
+ "#{@order_id}\t#{@cancellation_reason_code}\t#{@amazon_order_item_code}\r\n"
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,58 @@
1
+ module Peddler
2
+ module Handlers
3
+ class XMLHandler
4
+ # Decodes an XML response.
5
+ def self.decode_response(res)
6
+ XmlSimple.xml_in(res)
7
+ end
8
+ # Parses responses to uploads and status queries for feeds in Section 7 of the docs. Walks
9
+ # through lists and returns an array of hashes.
10
+ def self.parse(name, xml)
11
+ name = name.to_s.capitalize
12
+ list = xml["#{name}sStatusList"] || xml["#{name}sList"]
13
+ if list
14
+ list.collect { |s| parse_status(name, s) }
15
+ else
16
+ [ parse_status(name, xml) ]
17
+ end
18
+ end
19
+
20
+ # Parses legacy responses to queries on statuses of generated reports and inventory uploads.
21
+ def self.parse_legacy(xml)
22
+ if xml["Batch"]
23
+ xml["Batch"].collect { |input| Peddler::LegacyReports::UploadStatus.new(input) }
24
+ elsif xml["Report"]
25
+ xml["Report"].collect { |input| Peddler::LegacyReports::ReportStatus.new(input) }
26
+ end
27
+ end
28
+ protected
29
+ def self.parse_status(name, xml)
30
+ if xml[name]
31
+ xml[name][0].inject({}) do |memo, pair|
32
+ key, value = pair
33
+ value[0] = Time.parse(value[0]) if key =~ /Date$/
34
+ memo.merge!({ key => value[0] })
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ class TabDelimitedHandler
41
+ # Decodes tab-delimited content into an array of OpenStruct objects.
42
+ def self.decode_response(res)
43
+ lines = res.split("\n")
44
+ if lines.size > 1
45
+ params = lines[0].split("\t").collect{ |value| value.gsub(/-/, "_") }
46
+ params_size = params.size
47
+ (1..(lines.size - 1)).collect do |line_key|
48
+ values = lines[line_key].split("\t")
49
+ data = (0..(params_size - 1)).inject({}) { |memo, key| memo.merge( { params[key] => values[key] } ) }
50
+ OpenStruct.new(data)
51
+ end
52
+ else
53
+ res
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end