usps-ruby-client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +2 -0
  3. data/.gitignore +11 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +41 -0
  6. data/.rubocop_todo.yml +0 -0
  7. data/.travis.yml +6 -0
  8. data/CODE_OF_CONDUCT.md +74 -0
  9. data/Gemfile +8 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +77 -0
  12. data/Rakefile +9 -0
  13. data/bin/console +14 -0
  14. data/bin/setup +8 -0
  15. data/lib/data/api/address-information-api.htm +8229 -0
  16. data/lib/data/api/domestic-mail-service-standards-api.htm +11016 -0
  17. data/lib/data/api/evs-international-label-api.htm +33291 -0
  18. data/lib/data/api/evs-label-api.htm +18895 -0
  19. data/lib/data/api/hold-for-pickup-facilities-lookup-api.htm +5530 -0
  20. data/lib/data/api/package-pickup-api.htm +19157 -0
  21. data/lib/data/api/rate-calculator-api.htm +16355 -0
  22. data/lib/data/api/scan-api.htm +5934 -0
  23. data/lib/data/api/service-delivery-calculator-get-locations-api.htm +12548 -0
  24. data/lib/data/api/track-and-confirm-api.htm +21288 -0
  25. data/lib/data/api/uspsreturnslabel-api.htm +9474 -0
  26. data/lib/helpers/erubis_helper.rb +10 -0
  27. data/lib/tasks/api.rake +246 -0
  28. data/lib/usps-ruby-client.rb +30 -0
  29. data/lib/usps/api/endpoints.rb +70 -0
  30. data/lib/usps/api/endpoints/carrier_pickup_schedule.rb +117 -0
  31. data/lib/usps/api/endpoints/city_state_lookup.rb +50 -0
  32. data/lib/usps/api/endpoints/e_vs_express_mail_intl.rb +359 -0
  33. data/lib/usps/api/endpoints/e_vs_first_class_mail_intl.rb +323 -0
  34. data/lib/usps/api/endpoints/e_vs_priority_mail_intl.rb +366 -0
  35. data/lib/usps/api/endpoints/e_vsgxg_get_label.rb +366 -0
  36. data/lib/usps/api/endpoints/e_vsi_cancel.rb +55 -0
  37. data/lib/usps/api/endpoints/hfp_facility_info.rb +71 -0
  38. data/lib/usps/api/endpoints/intl_rate_v2.rb +135 -0
  39. data/lib/usps/api/endpoints/pts_email.rb +73 -0
  40. data/lib/usps/api/endpoints/pts_pod.rb +86 -0
  41. data/lib/usps/api/endpoints/pts_rre.rb +72 -0
  42. data/lib/usps/api/endpoints/ptst_pod.rb +75 -0
  43. data/lib/usps/api/endpoints/rate_v4.rb +140 -0
  44. data/lib/usps/api/endpoints/scan.rb +121 -0
  45. data/lib/usps/api/endpoints/track_v2.rb +62 -0
  46. data/lib/usps/api/endpoints/usps_returns_label.rb +149 -0
  47. data/lib/usps/api/endpoints/verify.rb +75 -0
  48. data/lib/usps/api/endpoints/zip_code_lookup.rb +69 -0
  49. data/lib/usps/api/error.rb +6 -0
  50. data/lib/usps/api/errors/too_many_requests_error.rb +22 -0
  51. data/lib/usps/api/errors/usps_error.rb +30 -0
  52. data/lib/usps/api/templates/_build_xml.erb +7 -0
  53. data/lib/usps/api/templates/_options.erb +6 -0
  54. data/lib/usps/api/templates/_throw_argument_error.erb +3 -0
  55. data/lib/usps/api/templates/endpoints.erb +22 -0
  56. data/lib/usps/api/templates/method.erb +49 -0
  57. data/lib/usps/api/templates/method_spec.erb +22 -0
  58. data/lib/usps/api/xml.rb +8 -0
  59. data/lib/usps/client.rb +37 -0
  60. data/lib/usps/config.rb +50 -0
  61. data/lib/usps/faraday/connection.rb +34 -0
  62. data/lib/usps/faraday/request.rb +39 -0
  63. data/lib/usps/faraday/response/raise_error.rb +16 -0
  64. data/lib/usps/logger.rb +14 -0
  65. data/lib/usps/version.rb +4 -0
  66. data/usps-ruby-client.gemspec +46 -0
  67. metadata +307 -0
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+ module ErubisHelper
3
+ def self.tabs(amount = 1)
4
+ (0..amount).map { "\t" }.join
5
+ end
6
+
7
+ def self.spaces(amount = 1)
8
+ (0..amount).map { "\s" }.join
9
+ end
10
+ end
@@ -0,0 +1,246 @@
1
+ # frozen_string_literal: true
2
+ require 'amazing_print'
3
+ require 'nokogiri'
4
+ require 'erubis'
5
+ require 'active_support'
6
+ require 'active_support/core_ext'
7
+ require 'pry'
8
+ require 'helpers/erubis_helper'
9
+
10
+ namespace :usps do
11
+ namespace :api do
12
+ task :update do
13
+ def dig_set(obj, keys, value)
14
+ key = keys.first
15
+ if keys.length == 1
16
+ obj[key] = value
17
+ else
18
+ obj[key] = {} unless obj[key]
19
+ dig_set(obj[key], keys.slice(1..-1), value)
20
+ end
21
+ end
22
+
23
+ def table_array_to_hash_array(columns, rows)
24
+ rows.map do |row|
25
+ row_data = {}
26
+ columns.each_with_index do |k, i|
27
+ row_data[k.to_s.gsub(/[[:space:]]+/, ' ').strip] =
28
+ row[i].is_a? String ? row[i].to_s.gsub(/[[:space:]]+/, ' ').strip : row[i]
29
+ end
30
+ row_data
31
+ end
32
+ end
33
+
34
+ def standardize_table_hash_array(arr)
35
+ arr.map do |option|
36
+ next if option['Tag Name'].split('/').pop.strip.casecmp('userid').zero?
37
+
38
+ {
39
+ type: option['Type'],
40
+ name: option['Tag Name'].split('/').pop.strip,
41
+ required: option['Occurs'] == 'Required',
42
+ description: option['Description'],
43
+ }
44
+ end.compact
45
+ end
46
+
47
+ def table_hash_array_to_shape(arr)
48
+ shape = {}
49
+
50
+ nesting = []
51
+ arr[0][:type] = '(Alias)' unless arr[0][:type].casecmp('(alias)').zero?
52
+ arr.each do |tag|
53
+ if tag[:type].casecmp('(alias)').zero?
54
+ if nesting.last == tag[:name]
55
+ nesting.pop
56
+ else
57
+ nesting.push(tag[:name])
58
+ dig_set(shape, nesting, tag.merge(children: {}))
59
+ end
60
+ elsif tag[:type].casecmp('(group)').zero?
61
+ if nesting.last == tag[:name]
62
+ nesting.pop(2)
63
+ else
64
+ nesting.push(:children)
65
+ nesting.push(tag[:name])
66
+ dig_set(shape, nesting, tag.merge(children: {}))
67
+ end
68
+ elsif nesting.length.positive?
69
+ nesting_with_children = nesting + [:children]
70
+ dig_set(shape, nesting_with_children,
71
+ shape.dig(*nesting_with_children).merge({ tag[:name] => tag.merge(children: {}) }))
72
+ else
73
+ shape[tag[:name]] = tag
74
+ end
75
+ end
76
+ shape
77
+ end
78
+
79
+ def pp_xml(xml = '')
80
+ doc = Nokogiri.XML(xml) do |config|
81
+ config.default_xml.noblanks
82
+ end
83
+ doc.to_xml(indent: 2)
84
+ end
85
+
86
+ @failed_methods = []
87
+
88
+ @endpoint_files = []
89
+
90
+ def parse_doc(path)
91
+ doc = Nokogiri::HTML(File.open(path))
92
+
93
+ table_of_contents = {}
94
+ raw_table_of_contents = doc.search('sdt[docparttype="Table of Contents"] p a').map do |link|
95
+ full_title = link.content.tr("\n", ' ')
96
+ next if full_title.blank?
97
+
98
+ full_version = full_title.match(/^[\d.]+/)[0]
99
+ major, minor, tiny = full_version.split('.')
100
+
101
+ title = full_title.match(/\s+[A-Za-z\s\t.:]*\s+/)[0].gsub(/[[:space:]]+/,
102
+ ' ').strip.chomp('.')
103
+
104
+ data = {
105
+ # full_title: full_title,
106
+ # link: link['href'],
107
+ anchor: link['href'].split('#')[1],
108
+ version: full_version,
109
+ major: major,
110
+ minor: minor,
111
+ tiny: tiny,
112
+ title: title,
113
+ }
114
+
115
+ table_of_contents[major] ||= []
116
+ table_of_contents[major] << data
117
+
118
+ data
119
+ end
120
+
121
+ data = table_of_contents.map do |section_number, section|
122
+ section_data = {}
123
+ section.each do |subsection|
124
+ header_node = doc.search("a[name=#{subsection[:anchor]}]")[0].parent
125
+ header_node = header_node.next_element if header_node.content.blank? && header_node.next_element
126
+
127
+ content_node = if header_node.next_element && header_node.next_element.content.present?
128
+ header_node.next_element
129
+ elsif header_node.parent.next_element && header_node.parent.next_element.content.present?
130
+ header_node.parent.next_element
131
+ else
132
+ header_node.parent.parent.next_element
133
+ end
134
+ next unless content_node # For section 1
135
+
136
+ section_data[:link] = subsection[:link]
137
+
138
+ if subsection[:version] == "#{section_number}.0"
139
+ section_data[:title] = subsection[:title]
140
+ elsif subsection[:title] == 'Overview'
141
+ section_data[:description] = content_node.content.tr("\n", ' ')
142
+ elsif subsection[:title] == 'API Signature'
143
+ columns, *rows = content_node.search('tr').map do |tr|
144
+ tr.search('td').map do |td|
145
+ td.content.gsub(/\n\s/, '').strip
146
+ end
147
+ end
148
+ section_data[:signature] = table_array_to_hash_array(columns, rows)
149
+ section_data[:url] =
150
+ "#{section_data[:signature][0]['Scheme']}#{section_data[:signature][0]['Host']}#{section_data[:signature][0]['Path'].chomp('?')}"
151
+ section_data[:group] =
152
+ section_data[:signature][0]['API'].sub('API=', '').gsub(/[[:space:]]+/,
153
+ ' ').strip
154
+ # ap section_data[:signature]
155
+ # ap section_data[:signature].map{|s| s[]}
156
+ # elsif subsection[:title] == "Request Descriptions"
157
+ # columns, *rows = content_node.search('tr').map{|tr| tr.search('td').map{|td| td.content.gsub(/\n\s/, '').strip}}
158
+ # section_data[:request_descriptions] = table_hash_array_to_shape(standardize_table_hash_array(table_array_to_hash_array(columns, rows)))
159
+ elsif subsection[:title] == 'Sample Request'
160
+ title, xml = content_node.content.gsub(/[[:space:]]+/, ' ').strip.split('<', 2)
161
+ xml = pp_xml("<#{xml}")
162
+ section_data[:sample_request] = <<~XML
163
+ #{title}
164
+ #{xml}
165
+ XML
166
+ elsif subsection[:title] == 'Response Descriptions' || subsection[:title] == 'Request Descriptions'
167
+ columns, *rows = content_node.search('tr').map do |tr|
168
+ next if tr.ancestors('table').length > 1
169
+
170
+ tr.search('td').map do |td|
171
+ has_table = td.search('table').length.positive?
172
+ if !has_table
173
+ td.content.gsub(/\n\s/, '').strip
174
+ else
175
+ table = td.search('table')[0]
176
+ c, *r = table.search('tr').map do |tr|
177
+ tr.search('td').map do |td|
178
+ td.content.gsub(/\n\s/, '').strip
179
+ end
180
+ end
181
+ table_array_to_hash_array(c, r)
182
+ end
183
+ end
184
+ end.compact
185
+ binding.pry if rows.blank?
186
+ section_data[subsection[:title].downcase.gsub(/\s+/, '_').to_sym] =
187
+ table_hash_array_to_shape(standardize_table_hash_array(table_array_to_hash_array(
188
+ columns, rows
189
+ )))
190
+ elsif subsection[:title] == 'Sample Response'
191
+ title, xml = content_node.content.gsub(/[[:space:]]+/, ' ').strip.split('<', 2)
192
+ xml = pp_xml("<#{xml}")
193
+ section_data[:sample_response] = <<~XML
194
+ #{title}
195
+ #{xml}
196
+ XML
197
+ end
198
+ end
199
+
200
+ section_data
201
+ end
202
+
203
+ method_template = Erubis::Eruby.new(File.read('lib/usps/api/templates/method.erb'))
204
+ method_spec_template = Erubis::Eruby.new(File.read('lib/usps/api/templates/method_spec.erb'))
205
+ data.each do |api|
206
+ next if api[:group].nil? || api[:group].empty?
207
+
208
+ # begin
209
+ rendered_method = method_template.result(group: api[:group], data: api)
210
+ File.write("lib/usps/api/endpoints/#{api[:group].underscore}.rb", rendered_method)
211
+
212
+ rendered_method_spec = method_spec_template.result(group: api[:group], data: api)
213
+ File.write("spec/usps/api/endpoints/#{api[:group].underscore}_spec.rb",
214
+ rendered_method_spec)
215
+
216
+ @endpoint_files << {
217
+ file: api[:group].underscore,
218
+ module: api[:group].camelize,
219
+ }
220
+ # rescue => e
221
+ # @failed_methods << {
222
+ # file: path,
223
+ # api: api[:group],
224
+ # }
225
+ # end
226
+ end
227
+ end
228
+
229
+ failed_docs = []
230
+ Dir.glob('lib/data/api/*.htm').each do |f|
231
+ ap f
232
+ # begin
233
+ parse_doc(f)
234
+ # rescue => e
235
+ # failed_docs << f
236
+ # end
237
+ end
238
+
239
+ endpoints_template = Erubis::Eruby.new(File.read('lib/usps/api/templates/endpoints.erb'))
240
+ File.write(
241
+ 'lib/usps/api/endpoints.rb',
242
+ endpoints_template.result(files: @endpoint_files.sort_by { |f| f[:file] })
243
+ )
244
+ end
245
+ end
246
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ require 'usps/version'
3
+ require 'usps/logger'
4
+ require 'usps/config'
5
+
6
+ require 'active_support'
7
+ require 'active_support/core_ext'
8
+ require 'builder'
9
+ require 'faraday'
10
+ require 'faraday_middleware'
11
+ require 'json'
12
+ require 'logger'
13
+
14
+ require_relative 'usps/api/errors/usps_error'
15
+ require_relative 'usps/api/errors/too_many_requests_error'
16
+ # require_relative 'usps/api/error'
17
+ # require_relative 'usps/api/errors'
18
+ require_relative 'usps/faraday/response/raise_error'
19
+ require_relative 'usps/faraday/connection'
20
+ require_relative 'usps/faraday/request'
21
+ # require_relative 'usps/api/mixins'
22
+ require_relative 'usps/api/xml'
23
+ require_relative 'usps/api/endpoints'
24
+ # require_relative 'usps/pagination/cursor'
25
+ require_relative 'usps/client'
26
+
27
+ # module Usps
28
+ # class Error < StandardError; end
29
+ # # Your code goes here...
30
+ # end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+ # This file was auto-generated by lib/tasks/web.rake
3
+
4
+ require_relative 'endpoints/carrier_pickup_schedule'
5
+ require_relative 'endpoints/city_state_lookup'
6
+ require_relative 'endpoints/e_vs_express_mail_intl'
7
+ require_relative 'endpoints/e_vs_first_class_mail_intl'
8
+ require_relative 'endpoints/e_vs_priority_mail_intl'
9
+ require_relative 'endpoints/e_vsgxg_get_label'
10
+ require_relative 'endpoints/e_vsi_cancel'
11
+ require_relative 'endpoints/hfp_facility_info'
12
+ require_relative 'endpoints/intl_rate_v2'
13
+ require_relative 'endpoints/pts_email'
14
+ require_relative 'endpoints/pts_pod'
15
+ require_relative 'endpoints/pts_rre'
16
+ require_relative 'endpoints/ptst_pod'
17
+ require_relative 'endpoints/rate_v4'
18
+ require_relative 'endpoints/scan'
19
+ require_relative 'endpoints/track_v2'
20
+ require_relative 'endpoints/usps_returns_label'
21
+ require_relative 'endpoints/verify'
22
+ require_relative 'endpoints/zip_code_lookup'
23
+
24
+ module Usps
25
+ module Api
26
+ module Endpoints
27
+ include CarrierPickupSchedule
28
+ include CityStateLookup
29
+ include EVSExpressMailIntl
30
+ include EVSFirstClassMailIntl
31
+ include EVSPriorityMailIntl
32
+ include EVSGXGGetLabel
33
+ include EVSICancel
34
+ include HFPFacilityInfo
35
+ include IntlRateV2
36
+ include PTSEmail
37
+ include PTSPod
38
+ include PTSRre
39
+ include PTSTPod
40
+ include RateV4
41
+ include SCAN
42
+ include TrackV2
43
+ include USPSReturnsLabel
44
+ include Verify
45
+ include ZipCodeLookup
46
+
47
+ ACTIONS = {
48
+ carrier_pickup_schedule: 'CarrierPickupSchedule',
49
+ city_state_lookup: 'CityStateLookup',
50
+ e_vs_express_mail_intl: 'EVSExpressMailIntl',
51
+ e_vs_first_class_mail_intl: 'EVSFirstClassMailIntl',
52
+ e_vs_priority_mail_intl: 'EVSPriorityMailIntl',
53
+ e_vsgxg_get_label: 'EVSGXGGetLabel',
54
+ e_vsi_cancel: 'EVSICancel',
55
+ hfp_facility_info: 'HFPFacilityInfo',
56
+ intl_rate_v2: 'IntlRateV2',
57
+ pts_email: 'PTSEmail',
58
+ pts_pod: 'PTSPod',
59
+ pts_rre: 'PTSRre',
60
+ ptst_pod: 'PTSTPod',
61
+ rate_v4: 'RateV4',
62
+ scan: 'SCAN',
63
+ track_v2: 'TrackV2',
64
+ usps_returns_label: 'USPSReturnsLabel',
65
+ verify: 'Verify',
66
+ zip_code_lookup: 'ZipCodeLookup',
67
+ }.freeze
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was auto-generated by lib/tasks/api.rake
4
+
5
+ module Usps
6
+ module Api
7
+ module Endpoints
8
+ module CarrierPickupSchedule
9
+ #
10
+ # Package Pickup Schedule API
11
+ #
12
+ # The Package Pickup Schedule API schedules a
13
+ # Package Pickup and provides the user a confirmation number for the scheduled
14
+ # pickup. Prior to making a Pickup Schedule call, it is recommended to use
15
+ # the Pickup Availability API to confirm that service is available.
16
+ #
17
+ # @option option [(Alias)] :CarrierPickupScheduleRequest (Required)
18
+ # @option option [String] :FirstName (Required)
19
+ # - Only alpha characters, apostrophes, spaces, periods, and hyphens "-" may be used. For example: <FirstName>John</FirstName>
20
+ # @option option [String] :LastName (Required)
21
+ # - Only alpha characters, apostrophes, spaces, periods and hyphens "-" may be used. For example: <LastName>Doe</LastName>
22
+ # @option option [String] :FirmName (Optional)
23
+ # - Only alpha and numeric characters, apostrophes, spaces, hyphens "-" and ampersands "&" may be used. Use this tag for a firm or company name. Some firms/companies that have their own ZIP codes require the use of firm name to properly identify their address. Note: FirmName is Optional except if the First Name and Last Name tags are null. For example: <FirmName>ABC Company</FirmName>
24
+ # @option option [String] :SuiteOrApt (Required)
25
+ # - Apartment or suite number. Optional except if needed to uniquely identify an address at a multiple dwelling address, for example, an apartment building. For example: <SuiteOrApt>Suite 777</SuiteOrApt>
26
+ # @option option [String] :Address2 (Required)
27
+ # - Street address. For example: <Address2>1390 Market Street</Address2>
28
+ # @option option [String] :Urbanization (Required)
29
+ # - Use this tag for Urbanization (for Puerto Rico only). ZIP Code prefixes 006 to 009, if area is so designated.
30
+ # @option option [String] :City (Required)
31
+ # - City name. Either ZIP5 or City and State are required. For example: <City>Houston</City>
32
+ # @option option [String] :State (Required)
33
+ # - State abbreviation. Either ZIP5 or City and State are required. For example: <State>TX</State>
34
+ # @option option [String] :ZIP5 (Required)
35
+ # - 5-digit ZIP Code. Either ZIP5 or City and State are required. For example: <ZIP5>77058</ZIP5>
36
+ # @option option [String] :ZIP4 (Required)
37
+ # - Use this tag for a 4 digit ZIP Code. For example: <ZIP4>1234</ZIP4>
38
+ # @option option [String] :Phone (Required)
39
+ # - Two formats are allowed: (###) 123-4567 or ###-123-4567. For example: <Phone>5555551234</Phone> or <Phone>555-555-1234</Phone>
40
+ # @option option [String] :Extension (Optional)
41
+ # - Optional value for Phone Extension. For example: <Extension>201</Extension>
42
+ # @option option [(Group)] :Package (Required)
43
+ # - No values entered with this tag. <ServiceType> and <Count> tags are embedded under this. Refer to the XML request example section, below, to see how these embedded tags are formatted. If the <Count> for a service type is zero, you do not need to encode a <Package> but you must have at least one <Package> with embedded <ServiceType> and <Count> tags.
44
+ # @option option [String] :ServiceType (Required)
45
+ # - This tag is embedded under the <Package> tag. If your pickup contains more than one Service Type, use additional <Package> tags for each service type with the accompanying <ServiceType> and <Count> tags. Refer to the XML Request Example below to see how these embedded tags are formatted. For example: <ServiceType>PriorityMailExpress</ServiceType>
46
+ # @option option [String] :Count (Required)
47
+ # - This tag is embedded under the <Package> tag. Enter the number of packages for the accompanying <ServiceType> tag. Maximum characters allowed: 3 or 999 packages. If your pickup contains more than one Service Type, use additional <Package> tags for each service type with the accompanying <ServiceType> and <Count> tags. Refer to the XML request example section, below, to see how these embedded tags are formatted. For example: <Count>2</Count>
48
+ # @option option [String] :EstimatedWeight (Required)
49
+ # - Enter the estimated aggregate weight (in pounds) of all packages being picked up. For example: <EstimatedWeight>14</EstimatedWeight>
50
+ # @option option [String] :PackageLocation (Required)
51
+ # - Enter one of the following values: Note: "Other" requires information in the value for the <SpecialInstructions> tag. For example: <PackageLocation>Front Door</PackageLocation>
52
+ # @option option [String] :SpecialInstructions (Optional)
53
+ # - Value Required when PackageLocation is “Other”. Only alpha, numeric, commas, periods, apostrophes, _, &, -, ( ), ?, #, / +, @ and space characters may be used. For example: <SpecialInstructions>Packages are behind the screen door.</SpecialInstructions>
54
+ # @option option [String] :EmailAddress (Optional)
55
+ # - If provided, email notifications will be sent confirming package pickup, or request changes and cancellations. Maximum characters allowed: 50. For example: <EmailAddress>cpapple@email.com</EmailAddress>
56
+
57
+ #
58
+ # @see
59
+ def carrier_pickup_schedule(options = {})
60
+ throw ArgumentError.new('Required arguments :carrier_pickup_schedule_request missing') if options[:carrier_pickup_schedule_request].nil?
61
+ throw ArgumentError.new('Required arguments :carrier_pickup_schedule_request, :first_name missing') if options[:carrier_pickup_schedule_request][:first_name].nil?
62
+ throw ArgumentError.new('Required arguments :carrier_pickup_schedule_request, :last_name missing') if options[:carrier_pickup_schedule_request][:last_name].nil?
63
+ throw ArgumentError.new('Required arguments :carrier_pickup_schedule_request, :suite_or_apt missing') if options[:carrier_pickup_schedule_request][:suite_or_apt].nil?
64
+ throw ArgumentError.new('Required arguments :carrier_pickup_schedule_request, :address2 missing') if options[:carrier_pickup_schedule_request][:address2].nil?
65
+ throw ArgumentError.new('Required arguments :carrier_pickup_schedule_request, :urbanization missing') if options[:carrier_pickup_schedule_request][:urbanization].nil?
66
+ throw ArgumentError.new('Required arguments :carrier_pickup_schedule_request, :city missing') if options[:carrier_pickup_schedule_request][:city].nil?
67
+ throw ArgumentError.new('Required arguments :carrier_pickup_schedule_request, :state missing') if options[:carrier_pickup_schedule_request][:state].nil?
68
+ throw ArgumentError.new('Required arguments :carrier_pickup_schedule_request, :zip5 missing') if options[:carrier_pickup_schedule_request][:zip5].nil?
69
+ throw ArgumentError.new('Required arguments :carrier_pickup_schedule_request, :zip4 missing') if options[:carrier_pickup_schedule_request][:zip4].nil?
70
+ throw ArgumentError.new('Required arguments :carrier_pickup_schedule_request, :phone missing') if options[:carrier_pickup_schedule_request][:phone].nil?
71
+ throw ArgumentError.new('Required arguments :carrier_pickup_schedule_request, :package missing') if options[:carrier_pickup_schedule_request][:package].nil?
72
+ throw ArgumentError.new('Required arguments :carrier_pickup_schedule_request, :package, :service_type missing') if options[:carrier_pickup_schedule_request][:package][:service_type].nil?
73
+ throw ArgumentError.new('Required arguments :carrier_pickup_schedule_request, :package, :count missing') if options[:carrier_pickup_schedule_request][:package][:count].nil?
74
+ throw ArgumentError.new('Required arguments :carrier_pickup_schedule_request, :package, :estimated_weight missing') if options[:carrier_pickup_schedule_request][:package][:estimated_weight].nil?
75
+ throw ArgumentError.new('Required arguments :carrier_pickup_schedule_request, :package, :package_location missing') if options[:carrier_pickup_schedule_request][:package][:package_location].nil?
76
+
77
+ request = build_request(:carrier_pickup_schedule, options)
78
+ get('https://secure.shippingapis.com/ShippingAPI.dll', {
79
+ API: 'CarrierPickupSchedule',
80
+ XML: request,
81
+ })
82
+ end
83
+
84
+ private
85
+
86
+ def tag_unless_blank(xml, tag_name, data)
87
+ xml.tag!(tag_name, data) unless data.blank? || data.nil?
88
+ end
89
+
90
+ def build_carrier_pickup_schedule_request(xml, options = {})
91
+ xml.tag!('FirstName', options[:carrier_pickup_schedule_request][:first_name])
92
+ xml.tag!('LastName', options[:carrier_pickup_schedule_request][:last_name])
93
+ tag_unless_blank(xml, 'FirmName', options[:carrier_pickup_schedule_request][:firm_name])
94
+ xml.tag!('SuiteOrApt', options[:carrier_pickup_schedule_request][:suite_or_apt])
95
+ xml.tag!('Address2', options[:carrier_pickup_schedule_request][:address2])
96
+ xml.tag!('Urbanization', options[:carrier_pickup_schedule_request][:urbanization])
97
+ xml.tag!('City', options[:carrier_pickup_schedule_request][:city])
98
+ xml.tag!('State', options[:carrier_pickup_schedule_request][:state])
99
+ xml.tag!('ZIP5', options[:carrier_pickup_schedule_request][:zip5])
100
+ xml.tag!('ZIP4', options[:carrier_pickup_schedule_request][:zip4])
101
+ xml.tag!('Phone', options[:carrier_pickup_schedule_request][:phone])
102
+ tag_unless_blank(xml, 'Extension', options[:carrier_pickup_schedule_request][:extension])
103
+ xml.tag!('Package') do
104
+ xml.tag!('ServiceType', options[:carrier_pickup_schedule_request][:package][:service_type])
105
+ xml.tag!('Count', options[:carrier_pickup_schedule_request][:package][:count])
106
+ xml.tag!('EstimatedWeight', options[:carrier_pickup_schedule_request][:package][:estimated_weight])
107
+ xml.tag!('PackageLocation', options[:carrier_pickup_schedule_request][:package][:package_location])
108
+ tag_unless_blank(xml, 'SpecialInstructions', options[:carrier_pickup_schedule_request][:package][:special_instructions])
109
+ tag_unless_blank(xml, 'EmailAddress', options[:carrier_pickup_schedule_request][:package][:email_address])
110
+ end
111
+ xml.target!
112
+ end
113
+
114
+ end
115
+ end
116
+ end
117
+ end