sports_south 1.3.1 → 2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a6481c86cca1535a34412d14a8741f5d60a921d0
4
- data.tar.gz: ed83dfd81a0648e0480ec19fdda1f3b2899aae31
3
+ metadata.gz: e83de8dbfabf6f57ae9602d11a61b4678f286011
4
+ data.tar.gz: d0dd3565443b6f251a5a93f1b87d35c20ccc48a2
5
5
  SHA512:
6
- metadata.gz: 7c2da2c40f4f5074571225f424846805dae56614f6dea2165a2d5cbe9e4202e494e11cccd4dc00a843da534aa205cd06e8412a079119a43e146b6db4a704222a
7
- data.tar.gz: 14ece0078b29da5ea1f64004645844958e873df6b3a47389967e2604f3546db8598322383846d989c1b898aa04dfbc35efd46a87131d3798bb1629ec33888849
6
+ metadata.gz: 305f47410723bed1e4a3c07a381a0fb9da61391860385fd81bcfff673e59e2a0b2c73ca88c1d1009ade12aaf9f88068679426ef411637d2ddde013bb455cfcec
7
+ data.tar.gz: 76c87dcba4dc4f82110abc575db18f1b5b1f88009a131cba597b353d805b78d90869e029bda640ab2a83df5ffd39019cda5dcf70ee73afc2e739339491b02655
@@ -26,6 +26,11 @@ module SportsSouth
26
26
  end
27
27
  end
28
28
 
29
+ def content_for(xml_doc, field)
30
+ node = xml_doc.css(field).first
31
+ node.nil? ? nil : node.content.strip
32
+ end
33
+
29
34
  # Returns a hash of common form params.
30
35
  def self.form_params(options = {})
31
36
  {
@@ -52,12 +57,6 @@ module SportsSouth
52
57
  end
53
58
  def get_http_and_request(*args); self.class.get_http_and_request(*args); end
54
59
 
55
- def self.content_for(xml_doc, field)
56
- node = xml_doc.css(field).first
57
- node.nil? ? nil : node.content.strip
58
- end
59
- def content_for(*args); self.class.content_for(*args); end
60
-
61
60
  def self.not_authenticated?(xml_doc)
62
61
  msg = content_for(xml_doc, 'ERROR')
63
62
  (msg =~ /Authentication Failed/i) || (msg =~ /NOT AUTHENTICATED/i)
@@ -0,0 +1,92 @@
1
+ module SportsSouth
2
+ class Catalog < Base
3
+
4
+ API_URL = 'http://webservices.theshootingwarehouse.com/smart/inventory.asmx'
5
+
6
+ CATALOG_CODES = {
7
+ 'S' => :special,
8
+ 'C' => :closeout,
9
+ 'F' => :flyer,
10
+ 'B' => :buyers_special,
11
+ 'N' => :net_price,
12
+ }
13
+
14
+ ITEM_TYPES = {
15
+ '1' => :handgun,
16
+ '2' => :long_gun,
17
+ '3' => :accessory,
18
+ '4' => :ammunition,
19
+ '5' => :optics,
20
+ '6' => :archery,
21
+ '7' => :reloading,
22
+ '8' => :suppressor,
23
+ }
24
+
25
+ def initialize(options = {})
26
+ requires!(options, :username, :password)
27
+
28
+ @options = options
29
+ @options[:customer_number] ||= options[:username]
30
+ @options[:source] ||= 'ammor'
31
+ end
32
+
33
+ def self.all(chunk_size = 15, options = {}, &block)
34
+ requires!(options, :username, :password)
35
+
36
+ options[:last_updated] ||= '1/1/1990'
37
+ options[:last_item] ||= '-1'
38
+
39
+ new(options).all(chunk_size, &block)
40
+ end
41
+
42
+ def all(chunk_size, &block)
43
+ chunker = SportsSouth::Chunker.new(chunk_size)
44
+ http, request = get_http_and_request(API_URL, '/DailyItemUpdate')
45
+
46
+ request.set_form_data(form_params(@options).merge({
47
+ LastUpdate: @options[:last_updated],
48
+ LastItem: @options[:last_item].to_s
49
+ }))
50
+
51
+ response = http.request(request)
52
+ xml_doc = Nokogiri::XML(sanitize_response(response))
53
+
54
+ xml_doc.css('Table').map do |item|
55
+ if chunker.is_full?
56
+ yield(chunker.chunk)
57
+
58
+ chunker.reset!
59
+ else
60
+ chunker.add(self.map_hash(item))
61
+ end
62
+
63
+ if chunker.chunk.count > 0
64
+ yield(chunker.chunk)
65
+ end
66
+ end
67
+ end
68
+
69
+ protected
70
+
71
+ def map_hash(node)
72
+ {
73
+ upc: content_for(node, 'ITUPC'),
74
+ item_identifier: content_for(node, 'ITEMNO'),
75
+ quantity: content_for(node, 'QTYOH').to_i,
76
+ price: content_for(node, 'CPRC'),
77
+ short_description: content_for(node, 'SHDESC'),
78
+ long_description: content_for(node, 'IDESC'),
79
+ category: content_for(node, 'CATID'),
80
+ weight: content_for(node, 'WTPBX'),
81
+ map_price: content_for(node, 'MFPRC'),
82
+ brand: content_for(node, 'ITBRDNO'),
83
+ features: {
84
+ length: content_for(node, 'LENGTH'),
85
+ height: content_for(node, 'HEIGHT'),
86
+ width: content_for(node, 'WIDTH')
87
+ }
88
+ }
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,34 @@
1
+ module SportsSouth
2
+ class Chunker
3
+
4
+ attr_accessor :chunk, :total_count, :current_count, :size
5
+
6
+ def initialize(size, total_count = nil)
7
+ @size = size
8
+ @chunk = Array.new
9
+ @current_count = 0
10
+ @total_count = total_count
11
+ end
12
+
13
+ def add(row)
14
+ self.reset! if is_full?
15
+
16
+ @chunk.push(row)
17
+
18
+ @current_count += 1
19
+ end
20
+
21
+ def reset!
22
+ @chunk.clear
23
+ end
24
+
25
+ def is_full?
26
+ @chunk.count == @size
27
+ end
28
+
29
+ def is_complete?
30
+ @total_count == @current_count
31
+ end
32
+
33
+ end
34
+ end
@@ -3,236 +3,77 @@ module SportsSouth
3
3
 
4
4
  API_URL = 'http://webservices.theshootingwarehouse.com/smart/inventory.asmx'
5
5
 
6
- CATALOG_CODES = {
7
- 'S' => :special,
8
- 'C' => :closeout,
9
- 'F' => :flyer,
10
- 'B' => :buyers_special,
11
- 'N' => :net_price,
12
- }
13
-
14
- ITEM_TYPES = {
15
- '1' => :handgun,
16
- '2' => :long_gun,
17
- '3' => :accessory,
18
- '4' => :ammunition,
19
- '5' => :optics,
20
- '6' => :archery,
21
- '7' => :reloading,
22
- '8' => :suppressor,
23
- }
24
-
25
- def self.all(options = {})
26
- requires!(options, :username, :password, :source, :customer_number)
27
-
28
- options[:last_update] ||= '1/1/1990' # Return full catalog.
29
- options[:last_item] ||= '-1' # Return all items.
30
-
31
- http, request = get_http_and_request(API_URL, '/DailyItemUpdate')
32
-
33
- request.set_form_data(form_params(options).merge({
34
- LastUpdate: options[:last_update],
35
- LastItem: options[:last_item].to_s,
36
- }))
37
-
38
- xml_doc = Nokogiri::XML(sanitize_response(http.request(request)))
39
-
40
- raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
41
-
42
- xml_doc.css('Table').map { |item| map_hash(item, mode: options[:mode]) }
43
- end
44
-
45
- def self.get_text(item_number, options = {})
46
- requires!(options, :username, :password, :source, :customer_number)
47
-
48
- http, request = get_http_and_request(API_URL, '/GetText')
49
-
50
- request.set_form_data(form_params(options).merge({ ItemNumber: item_number }))
51
-
52
- response = http.request(request)
53
- xml_doc = Nokogiri::XML(sanitize_response(response))
54
-
55
- raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
56
-
57
- {
58
- item_number: item_number,
59
- catalog_text: content_for(xml_doc, 'CATALOGTEXT')
60
- }
61
- end
6
+ def initialize(options = {})
7
+ requires!(options, :username, :password)
62
8
 
63
- def self.inquiry(item_number, options = {})
64
- requires!(options, :username, :password, :source, :customer_number)
65
-
66
- http, request = get_http_and_request(API_URL, '/OnhandInquiry')
67
-
68
- request.set_form_data(form_params(options).merge({ ItemNumber: item_number }))
69
-
70
- response = http.request(request)
71
- xml_doc = Nokogiri::XML(sanitize_response(response))
72
-
73
- raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
74
-
75
- {
76
- item_number: content_for(xml_doc, 'I'),
77
- quantity_on_hand: content_for(xml_doc, 'Q').to_i,
78
- catalog_price: content_for(xml_doc, 'P'),
79
- customer_price: content_for(xml_doc, 'C'),
80
- }
9
+ @options = options
10
+ @options[:customer_number] ||= options[:username]
11
+ @options[:source] ||= 'ammor'
81
12
  end
82
13
 
83
- def self.list_new_text(options = {})
84
- requires!(options, :username, :password, :source, :customer_number)
85
- options[:since] ||= (Time.now - 86400).strftime('%m/%d/%Y')
86
-
87
- http, request = get_http_and_request(API_URL, '/ListNewText')
88
- request.set_form_data(form_params(options).merge({ DateFrom: options[:since] }))
14
+ def self.all(chunk_size = 15, options = {}, &block)
15
+ requires!(options, :username, :password)
89
16
 
90
- xml_doc = Nokogiri::XML(sanitize_response(http.request(request)))
91
-
92
- xml_doc.css('Table').map do |item|
93
- {
94
- item_number: content_for(item, 'ITEMNO'),
95
- text: content_for(item, 'TEXT')
96
- }
17
+ if options[:last_updated].present?
18
+ options[:last_updated].to_s("yyyy-MM-ddTHH:mm:sszzz")
19
+ else
20
+ options[:last_updated] = '2017-09-25T14:15:47-04:00'
97
21
  end
98
- end
99
-
100
- # This method accepts an Array of +item_numbers+.
101
- def self.onhand_update_by_csv(item_numbers, options = {})
102
- requires!(options, :username, :password, :source, :customer_number)
103
22
 
104
- http, request = get_http_and_request(API_URL, '/OnhandUpdatebyCSV')
23
+ options[:last_item] ||= '-1'
105
24
 
106
- request.set_form_data(form_params(options).merge({ CSVItems: item_numbers.join(',') }))
107
-
108
- response = http.request(request)
109
- xml_doc = Nokogiri::XML(sanitize_response(response))
110
-
111
- raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
112
-
113
- xml_doc.css('Table').map do |item|
114
- {
115
- item_number: content_for(item, 'I'),
116
- quantity: content_for(item, 'Q'),
117
- price: content_for(item, 'P'),
118
- }
119
- end
25
+ new(options).all(chunk_size, &block)
120
26
  end
121
27
 
122
- # Pass an optional `:since` option (YYYY-MM-DDTHH:mm:ss.mss-HH:00) to get items updated since that timestamp.
123
- def self.incremental_onhand_update(options = {})
124
- requires!(options, :username, :password, :source, :customer_number)
125
-
126
- options[:since] ||= '-1'
28
+ def self.get(item_identifier, options = {})
29
+ requires!(options, :username, :password)
30
+ new(options).get(item_identifier)
31
+ end
127
32
 
33
+ def all(chunk_size, &block)
34
+ chunker = SportsSouth::Chunker.new(chunk_size)
128
35
  http, request = get_http_and_request(API_URL, '/IncrementalOnhandUpdate')
129
36
 
130
- request.set_form_data(form_params(options).merge({ SinceDateTime: options[:since] }))
131
-
132
- xml_doc = Nokogiri::XML(sanitize_response(http.request(request)))
37
+ request.set_form_data(form_params = form_params(@options).merge({
38
+ SinceDateTime: @options[:last_updated],
39
+ LastItem: @options[:last_item].to_s
40
+ }))
133
41
 
134
- raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
42
+ response = http.request(request)
43
+ xml_doc = Nokogiri::XML(sanitize_response(response))
135
44
 
136
45
  xml_doc.css('Onhand').map do |item|
137
- {
138
- item_number: content_for(item, 'I'),
139
- quantity: content_for(item, 'Q'),
140
- quantity_changed: content_for(item, 'D'),
141
- catalog_price: content_for(item, 'P'),
142
- customer_price: content_for(item, 'C'),
143
- }
46
+ if chunker.is_full?
47
+ yield(chunker.chunk)
48
+
49
+ chunker.reset!
50
+ else
51
+ chunker.add(self.map_hash(item))
52
+ end
53
+
54
+ if chunker.chunk.count > 0
55
+ yield(chunker.chunk)
56
+ end
144
57
  end
145
58
  end
146
59
 
147
- def self.onhand_update(options = {})
148
- requires!(options, :username, :password, :source, :customer_number)
149
-
150
- http, request = get_http_and_request(API_URL, '/OnhandUpdate')
60
+ def get(item_identifier)
61
+ http, request = get_http_and_request(API_URL, '/OnhandInquiry')
151
62
 
152
- request.set_form_data(form_params(options))
63
+ request.set_form_data(form_params(@options).merge({ ItemNumber: item_identifier }))
153
64
 
154
65
  response = http.request(request)
155
66
  xml_doc = Nokogiri::XML(sanitize_response(response))
156
-
157
- raise SportsSouth::NotAuthenticated if not_authenticated?(xml_doc)
158
-
159
- xml_doc.css('Table').map do |item|
160
- {
161
- item_number: content_for(item, 'I'),
162
- quantity: content_for(item, 'Q').to_i,
163
- catalog_price: content_for(item, 'P'),
164
- customer_price: content_for(item, 'C'),
165
- }
166
- end
167
67
  end
168
68
 
169
69
  protected
170
70
 
171
- def self.map_hash(node, mode: nil)
172
- if mode == :minimal
173
- {
174
- item_number: content_for(node, 'ITEMNO'),
175
- catalog_price: content_for(node, 'PRC1'),
176
- customer_price: content_for(node, 'CPRC'),
177
- quantity_on_hand: content_for(node, 'QTYOH'),
178
- }
179
- else
180
- {
181
- item_number: content_for(node, 'ITEMNO'),
182
- description: content_for(node, 'IDESC'),
183
- manufacturer_sequence: content_for(node, 'IMFSEQ'),
184
- manufacturer_number: content_for(node, 'IMFGNO'),
185
- catalog_sequence: content_for(node, 'CSEQ'),
186
- item_type: ITEM_TYPES[content_for(node, 'ITYPE')],
187
- short_description: content_for(node, 'SHDESC'),
188
- unit_of_measure: content_for(node, 'UOM'),
189
- catalog_price: content_for(node, 'PRC1'),
190
- customer_price: content_for(node, 'CPRC'),
191
- quantity_on_hand: content_for(node, 'QTYOH'),
192
- weight_per_box: content_for(node, 'WTPBX'),
193
- upc: content_for(node, 'ITUPC'),
194
- manufacturer_item_number: content_for(node, 'MFGINO'),
195
- scan_name_1: content_for(node, 'SCNAM1'),
196
- scan_name_2: content_for(node, 'SCNAM2'),
197
- catalog_code: CATALOG_CODES[content_for(node, 'CATCD')],
198
- mapp_price_code: content_for(node, 'MFPRTYP'),
199
- mapp_price: content_for(node, 'MFPRC'),
200
- category_id: content_for(node, 'CATID'),
201
- text_reference_number: content_for(node, 'TXTREF'),
202
- picture_reference_number: content_for(node, 'PICREF'),
203
- brand_id: content_for(node, 'ITBRDNO'),
204
- item_model_number: content_for(node, 'IMODEL'),
205
- item_purpose: content_for(node, 'IPURPOSE'),
206
- series_description: content_for(node, 'SERIES'),
207
- item_length: content_for(node, 'LENGTH'),
208
- item_height: content_for(node, 'HEIGHT'),
209
- item_width: content_for(node, 'WIDTH'),
210
- item_ships_hazmat_air: content_for(node, 'HAZAIR'),
211
- item_ships_hazmat_ground: content_for(node, 'HAZGRND'),
212
- date_of_last_change: content_for(node, 'CHGDTE'),
213
- date_added: content_for(node, 'CHGDTE'),
214
- attribute_1: content_for(node, 'ITATR1'),
215
- attribute_2: content_for(node, 'ITATR2'),
216
- attribute_3: content_for(node, 'ITATR3'),
217
- attribute_4: content_for(node, 'ITATR4'),
218
- attribute_5: content_for(node, 'ITATR5'),
219
- attribute_6: content_for(node, 'ITATR6'),
220
- attribute_7: content_for(node, 'ITATR7'),
221
- attribute_8: content_for(node, 'ITATR8'),
222
- attribute_9: content_for(node, 'ITATR9'),
223
- attribute_10: content_for(node, 'ITATR0'),
224
- attribute_11: content_for(node, 'ITATR11'),
225
- attribute_12: content_for(node, 'ITATR12'),
226
- attribute_13: content_for(node, 'ITATR13'),
227
- attribute_14: content_for(node, 'ITATR14'),
228
- attribute_15: content_for(node, 'ITATR15'),
229
- attribute_16: content_for(node, 'ITATR16'),
230
- attribute_17: content_for(node, 'ITATR17'),
231
- attribute_18: content_for(node, 'ITATR18'),
232
- attribute_19: content_for(node, 'ITATR19'),
233
- attribute_20: content_for(node, 'ITATR20')
234
- }
235
- end
71
+ def map_hash(node)
72
+ {
73
+ item_identifier: content_for(node, 'I'),
74
+ quantity: content_for(node, 'Q').to_i,
75
+ price: content_for(node, 'C')
76
+ }
236
77
  end
237
78
 
238
79
  end
@@ -4,12 +4,11 @@ module SportsSouth
4
4
  API_URL = 'http://webservices.theshootingwarehouse.com/smart/orders.asmx'
5
5
 
6
6
  SHIP_VIA = {
7
- ground: '',
8
- next_day: 'N',
9
- two_day: '2',
7
+ ground: '',
8
+ next_day: 'N',
9
+ two_day: '2',
10
10
  three_day: '3',
11
- saturday: 'S',
12
- premium_ground: 'G',
11
+ saturday: 'S',
13
12
  }
14
13
 
15
14
  # D=Placed, E=Error placing Order, R=Placed-Verifying, W=Open
@@ -43,8 +42,8 @@ module SportsSouth
43
42
 
44
43
  requires!(header[:shipping], :name, :address_one, :city, :state, :zip, :phone)
45
44
  header[:shipping][:attn] = '' unless header[:shipping].has_key?(:attn)
45
+ header[:shipping][:via] = SHIP_VIA[:ground] unless header[:shipping].has_key?(:ship_via)
46
46
  header[:shipping][:address_two] = '' unless header[:shipping].has_key?(:address_two)
47
- header[:shipping][:via] = (header[:shipping].has_key?(:ship_via) ? SHIP_VIA[header[:shipping][:ship_via]] : SHIP_VIA[:ground])
48
47
 
49
48
  http, request = get_http_and_request(API_URL, '/AddHeader')
50
49
 
@@ -1,3 +1,3 @@
1
1
  module SportsSouth
2
- VERSION = '1.3.1'.freeze
2
+ VERSION = '2.0'
3
3
  end
data/lib/sports_south.rb CHANGED
@@ -5,7 +5,9 @@ require 'net/http'
5
5
  require 'nokogiri'
6
6
 
7
7
  require 'sports_south/base'
8
+ require 'sports_south/chunker'
8
9
  require 'sports_south/brand'
10
+ require 'sports_south/catalog'
9
11
  require 'sports_south/category'
10
12
  require 'sports_south/ffl'
11
13
  require 'sports_south/image'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sports_south
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: '2.0'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dale Campbell
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-10-18 00:00:00.000000000 Z
11
+ date: 2017-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -115,7 +115,9 @@ files:
115
115
  - lib/sports_south.rb
116
116
  - lib/sports_south/base.rb
117
117
  - lib/sports_south/brand.rb
118
+ - lib/sports_south/catalog.rb
118
119
  - lib/sports_south/category.rb
120
+ - lib/sports_south/chunker.rb
119
121
  - lib/sports_south/ffl.rb
120
122
  - lib/sports_south/image.rb
121
123
  - lib/sports_south/inventory.rb
@@ -145,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
145
147
  version: '0'
146
148
  requirements: []
147
149
  rubyforge_project:
148
- rubygems_version: 2.6.12
150
+ rubygems_version: 2.5.1
149
151
  signing_key:
150
152
  specification_version: 4
151
153
  summary: Sports South API Ruby library.