rapa 0.2.1 → 0.3.0

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.
@@ -0,0 +1,120 @@
1
+ require "cgi"
2
+ require "time"
3
+
4
+ module Rapa
5
+ module Queries
6
+ class BaseQuery
7
+ SERVICE = "AWSECommerceService"
8
+ VERSION = "2013-08-01"
9
+
10
+ class << self
11
+ attr_writer :property_names
12
+
13
+ # @note Override
14
+ def inherited(child_class)
15
+ child_class.property_names = property_names.clone
16
+ end
17
+
18
+ # @return [Array<Symbol>]
19
+ def property_names
20
+ @property_names ||= []
21
+ end
22
+
23
+ # @param property_name [Symbol]
24
+ def property(property_name)
25
+ property_names << property_name
26
+ end
27
+ end
28
+
29
+ property :AssociateTag
30
+ property :AWSAccessKeyId
31
+ property :Operation
32
+ property :RelatedItemPage
33
+ property :RelationshipType
34
+ property :ResponseGroup
35
+ property :Service
36
+ property :Timestamp
37
+ property :Version
38
+
39
+ # @param options [Hash]
40
+ def initialize(options = {})
41
+ @options = options
42
+ end
43
+
44
+ # @return [String]
45
+ def AssociateTag
46
+ options[:associate_tag]
47
+ end
48
+
49
+ # @return [String]
50
+ def AWSAccessKeyId
51
+ options[:access_key_id]
52
+ end
53
+
54
+ # @return [String]
55
+ def Operation
56
+ raise ::NotImplementedError
57
+ end
58
+
59
+ # @return [Integer, nil]
60
+ def RelatedItemPage
61
+ options[:related_item_page]
62
+ end
63
+
64
+ # @return [String, nil]
65
+ def RelationshipType
66
+ options[:relationship_type]
67
+ end
68
+
69
+ # @return [String, nil]
70
+ def ResponseGroup
71
+ if options[:response_groups]
72
+ options[:response_groups].join(",")
73
+ end
74
+ end
75
+
76
+ # @return [String]
77
+ def Service
78
+ SERVICE
79
+ end
80
+
81
+ # @return [String]
82
+ def Timestamp
83
+ ::Time.now.utc.iso8601
84
+ end
85
+
86
+ # @return [Hash]
87
+ def to_hash
88
+ self.class.property_names.each_with_object({}) do |property_name, result|
89
+ value = send(property_name)
90
+ unless value.nil?
91
+ result[property_name.to_s] = value
92
+ end
93
+ end.sort.to_h
94
+ end
95
+
96
+ # @return [String]
97
+ def to_s
98
+ to_hash.map do |key, value|
99
+ [
100
+ key,
101
+ ::CGI.escape(value),
102
+ ].join("=")
103
+ end.join("&")
104
+ end
105
+
106
+ # @return [String]
107
+ def Version
108
+ VERSION
109
+ end
110
+
111
+ private
112
+
113
+ # @private
114
+ # @return [Hash]
115
+ def options
116
+ @options
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,19 @@
1
+ module Rapa
2
+ module Queries
3
+ class ListItemsQuery < BaseQuery
4
+ OPERATION = "ItemLookup"
5
+
6
+ property :ItemId
7
+
8
+ # @return [String]
9
+ def ItemId
10
+ options[:asins].join(",")
11
+ end
12
+
13
+ # @note Override
14
+ def Operation
15
+ OPERATION
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ module Rapa
2
+ module Queries
3
+ class SearchItemsQuery < BaseQuery
4
+ SEARCH_INDEX_DEFAULT = "All"
5
+ OPERATION = "ItemSearch"
6
+
7
+ property :Keywords
8
+ property :SearchIndex
9
+
10
+ # @return [Array<String>]
11
+ def Keywords
12
+ options[:keywords].join(",")
13
+ end
14
+
15
+ # @note Override
16
+ def Operation
17
+ OPERATION
18
+ end
19
+
20
+ # @return [String]
21
+ def SearchIndex
22
+ options[:search_index] || SEARCH_INDEX_DEFAULT
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ module Rapa
2
+ module Resources
3
+ class BaseResource
4
+ # @param source [Hash]
5
+ def initialize(source)
6
+ @source = source
7
+ end
8
+
9
+ # @return [Hash]
10
+ def source
11
+ @source
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,357 @@
1
+ require "date"
2
+
3
+ module Rapa
4
+ module Resources
5
+ class ItemResource < BaseResource
6
+ # @return [Boolean, nil]
7
+ def adult_product?
8
+ if flag_source = source.dig("ItemAttributes", "IsAdultProduct")
9
+ flag_source != "0"
10
+ end
11
+ end
12
+
13
+ # @return [Array<Rapa::AlternateVersion>, nil]
14
+ def alternate_versions
15
+ if alternate_version_sources = source.dig("AlternateVersions", "AlternateVersion")
16
+ ::Array.wrap(alternate_version_sources).map do |alternate_version_source|
17
+ ::Rapa::AlternateVersion.new(alternate_version_source)
18
+ end
19
+ end
20
+ end
21
+
22
+ # @return [String]
23
+ def asin
24
+ source["ASIN"]
25
+ end
26
+
27
+ # @return [Array<String>, nil]
28
+ def authors
29
+ if author_sources = source.dig("ItemAttributes", "Author")
30
+ Array(author_sources)
31
+ end
32
+ end
33
+
34
+ # @return [String, nil]
35
+ def binding
36
+ source.dig("ItemAttributes", "Binding")
37
+ end
38
+
39
+ # @return [Array, nil]
40
+ def browse_nodes
41
+ source.dig("BrowseNodes", "BrowseNode")
42
+ end
43
+
44
+ # @return [String, nil]
45
+ def creator_name
46
+ source.dig("ItemAttributes", "Creator", "__content__")
47
+ end
48
+
49
+ # @return [String, nil]
50
+ def creator_role
51
+ source.dig("ItemAttributes", "Creator", "Role")
52
+ end
53
+
54
+ # @return [String, nil]
55
+ def ean
56
+ source.dig("ItemAttributes", "EAN")
57
+ end
58
+
59
+ # @return [Integer, nil]
60
+ def episode_sequence
61
+ if value = source.dig("ItemAttributes", "EpisodeSequence")
62
+ value.to_i
63
+ end
64
+ end
65
+
66
+ # @return [String, nil]
67
+ def genre
68
+ source.dig("ItemAttributes", "Genre")
69
+ end
70
+
71
+ # @return [Boolean]
72
+ def has_customer_reviews?
73
+ source.dig("CustomerReviews", "HasReviews") == "true"
74
+ end
75
+
76
+ # @return [Rapa::Image, nil]
77
+ def image_hi_res
78
+ find_image("HiResImage")
79
+ end
80
+
81
+ # @return [Rapa::Image, nil]
82
+ def image_large
83
+ find_image("LargeImage")
84
+ end
85
+
86
+ # @return [Rapa::Image, nil]
87
+ def image_medium
88
+ find_image("MediumImage")
89
+ end
90
+
91
+ # @return [Rapa::Image, nil]
92
+ def image_small
93
+ find_image("SmallImage")
94
+ end
95
+
96
+ # @return [Rapa::Image, nil]
97
+ def image_swatch
98
+ find_image("SwatchImage")
99
+ end
100
+
101
+ # @return [Rapa::Image, nil]
102
+ def image_thumbnail
103
+ find_image("ThumbnailImage")
104
+ end
105
+
106
+ # @return [Rapa::Image, nil]
107
+ def image_tiny
108
+ find_image("TinyImage")
109
+ end
110
+
111
+ # @return [String, nil]
112
+ def isbn
113
+ source.dig("ItemAttributes", "ISBN")
114
+ end
115
+
116
+ # @return [Rapa::Quantity]
117
+ def item_height
118
+ if quantity_source = source.dig("ItemAttributes", "ItemDimensions", "Height")
119
+ ::Rapa::Quantity.new(quantity_source)
120
+ end
121
+ end
122
+
123
+ # @return [Rapa::Quantity]
124
+ def item_length
125
+ if quantity_source = source.dig("ItemAttributes", "ItemDimensions", "Length")
126
+ ::Rapa::Quantity.new(quantity_source)
127
+ end
128
+ end
129
+
130
+ # @return [Rapa::Quantity]
131
+ def item_width
132
+ if quantity_source = source.dig("ItemAttributes", "ItemDimensions", "Width")
133
+ ::Rapa::Quantity.new(quantity_source)
134
+ end
135
+ end
136
+
137
+ # @return [String, nil]
138
+ def label
139
+ source.dig("ItemAttributes", "Label")
140
+ end
141
+
142
+ # @return [Array<Rapa::Language>, nil]
143
+ def languages
144
+ if language_sources = source.dig("ItemAttributes", "Languages", "Language")
145
+ language_sources.map do |language_source|
146
+ ::Rapa::Language.new(language_source)
147
+ end
148
+ end
149
+ end
150
+
151
+ # @return [String, nil]
152
+ def manufacturer
153
+ source.dig("ItemAttributes", "Manufacturer")
154
+ end
155
+
156
+ # @return [Integer, nil]
157
+ def number_of_pages
158
+ if value = source.dig("ItemAttributes", "NumberOfPages")
159
+ value.to_i
160
+ end
161
+ end
162
+
163
+ # @return [Rapa::Quantity]
164
+ def package_height
165
+ if quantity_source = source.dig("ItemAttributes", "PackageDimensions", "Height")
166
+ ::Rapa::Quantity.new(quantity_source)
167
+ end
168
+ end
169
+
170
+ # @return [Rapa::Quantity]
171
+ def package_length
172
+ if quantity_source = source.dig("ItemAttributes", "PackageDimensions", "Length")
173
+ ::Rapa::Quantity.new(quantity_source)
174
+ end
175
+ end
176
+
177
+ # @return [Rapa::Quantity]
178
+ def package_weight
179
+ if quantity_source = source.dig("ItemAttributes", "PackageDimensions", "Weight")
180
+ ::Rapa::Quantity.new(quantity_source)
181
+ end
182
+ end
183
+
184
+ # @return [Rapa::Quantity]
185
+ def package_width
186
+ if quantity_source = source.dig("ItemAttributes", "PackageDimensions", "Width")
187
+ ::Rapa::Quantity.new(quantity_source)
188
+ end
189
+ end
190
+
191
+ # @return [String, nil]
192
+ def product_group
193
+ source.dig("ItemAttributes", "ProductGroup")
194
+ end
195
+
196
+ # @return [String, nil]
197
+ def product_type_name
198
+ source.dig("ItemAttributes", "ProductTypeName")
199
+ end
200
+
201
+ # @return [Date, nil]
202
+ def publication_date
203
+ if value = source.dig("ItemAttributes", "PublicationDate")
204
+ ::Date.parse(value)
205
+ end
206
+ end
207
+
208
+ # @return [String, nil]
209
+ def publisher
210
+ source.dig("ItemAttributes", "Publisher")
211
+ end
212
+
213
+ # @return [Integer, nil]
214
+ def related_item_count
215
+ if value = source.dig("RelatedItems", "RelatedItemCount")
216
+ value.to_i
217
+ end
218
+ end
219
+
220
+ # @return [Integer, nil]
221
+ def related_item_page
222
+ if value = source.dig("RelatedItems", "RelatedItemPage")
223
+ value.to_i
224
+ end
225
+ end
226
+
227
+ # @return [Integer, nil]
228
+ def related_item_page_count
229
+ if value = source.dig("RelatedItems", "RelatedItemPageCount")
230
+ value.to_i
231
+ end
232
+ end
233
+
234
+ # @return [Array<Rapa::Resources::ItemResource>, nil]
235
+ def related_items
236
+ if source_or_sources = source.dig("RelatedItems", "RelatedItem")
237
+ if source_or_sources.is_a?(::Array)
238
+ source_or_sources
239
+ else
240
+ [source_or_sources]
241
+ end.map do |related_item_source|
242
+ ::Rapa::Resources::ItemResource.new(related_item_source["Item"])
243
+ end
244
+ end
245
+ end
246
+
247
+ # @return [String, nil]
248
+ def relationship
249
+ source.dig("RelatedItems", "Relationship")
250
+ end
251
+
252
+ # @return [String, nil]
253
+ def relationship_type
254
+ source.dig("RelatedItems", "RelationshipType")
255
+ end
256
+
257
+ # @return [Date, nil]
258
+ def release_date
259
+ if value = source.dig("ItemAttributes", "ReleaseDate")
260
+ ::Date.parse(value)
261
+ end
262
+ end
263
+
264
+ # @return [Rapa::Quantity, nil]
265
+ def running_time
266
+ if value = source.dig("ItemAttributes", "RunningTime")
267
+ ::Rapa::Quantity.new(value)
268
+ end
269
+ end
270
+
271
+ # @return [Integer, nil]
272
+ def sales_rank
273
+ source["SalesRank"]
274
+ end
275
+
276
+ # @return [Integer, nil]
277
+ def season_sequence
278
+ if value = source.dig("ItemAttributes", "SeasonSequence")
279
+ value.to_i
280
+ end
281
+ end
282
+
283
+ # @return [Array<Rapa::SimilarProduct>, nil]
284
+ def similar_products
285
+ if similar_product_sources = source["SimilarProducts"]
286
+ similar_product_sources.map do |similar_product_source|
287
+ ::Rapa::SimilarProduct.new(similar_product_source)
288
+ end
289
+ end
290
+ end
291
+
292
+ # @return [String, nil]
293
+ def studio
294
+ source.dig("ItemAttributes", "Studio")
295
+ end
296
+
297
+ # @return [String, nil]
298
+ def title
299
+ source.dig("ItemAttributes", "Title")
300
+ end
301
+
302
+ # @return [String, nil]
303
+ def url_add_to_wishlist
304
+ item_links["Add To Wishlist"]
305
+ end
306
+
307
+ # @return [String, nil]
308
+ def url_all_customer_reviews
309
+ item_links["All Customer Reviews"]
310
+ end
311
+
312
+ # @return [String, nil]
313
+ def url_all_offers
314
+ item_links["All Offers"]
315
+ end
316
+
317
+ # @return [String, nil]
318
+ def url_customer_reviews_iframe
319
+ source.dig("CustomerReviews", "IFrameURL")
320
+ end
321
+
322
+ # @return [String]
323
+ def url_details_page
324
+ source["DetailPageURL"]
325
+ end
326
+
327
+ # @return [String, nil]
328
+ def url_tell_a_friend
329
+ item_links["Tell A Friend"]
330
+ end
331
+
332
+ private
333
+
334
+ # @param key [String]
335
+ # @return [Rapa::Image, nil]
336
+ def find_image(key)
337
+ if image_source = (source[key] || source.dig("ImageSets", "ImageSet", key))
338
+ ::Rapa::Image.new(image_source)
339
+ end
340
+ end
341
+
342
+ # @private
343
+ # @return [Hash, nil]
344
+ def item_attributes
345
+ source["ItemAttributes"]
346
+ end
347
+
348
+ # @private
349
+ # @return [Hash]
350
+ def item_links
351
+ @item_links ||= source["ItemLinks"]["ItemLink"].each_with_object({}) do |element, result|
352
+ result[element["Description"]] = element["URL"]
353
+ end
354
+ end
355
+ end
356
+ end
357
+ end
@@ -0,0 +1,70 @@
1
+ module Rapa
2
+ module Responses
3
+ class BaseResponse
4
+ include ::Enumerable
5
+
6
+ # @param faraday_response [Faraday::Response]
7
+ def initialize(faraday_response)
8
+ @faraday_response = faraday_response
9
+ end
10
+
11
+ # @return [String]
12
+ def body
13
+ faraday_response.body
14
+ end
15
+
16
+ # @note Implementation for Enumerable
17
+ def each(&block)
18
+ resources.each(&block)
19
+ end
20
+
21
+ # @return [Faraday::Utils::Headers]
22
+ def headers
23
+ faraday_response.headers
24
+ end
25
+
26
+ # @return [Array<Rapa::Resources::ItemResource>]
27
+ def resources
28
+ sources.map do |source|
29
+ resource_class.new(source)
30
+ end
31
+ end
32
+
33
+ # @return [Integer]
34
+ def status
35
+ faraday_response.status
36
+ end
37
+
38
+ private
39
+
40
+ # @private
41
+ # @return [Faraday::Response]
42
+ def faraday_response
43
+ @faraday_response
44
+ end
45
+
46
+ # @return [Class]
47
+ def resource_class
48
+ raise ::NotImplementedError
49
+ end
50
+
51
+ # @private
52
+ def source_or_sources
53
+ raise ::NotImplementedError
54
+ end
55
+
56
+ # @private
57
+ # @return [Array<Hash>]
58
+ def sources
59
+ case source_or_sources
60
+ when ::Array
61
+ source_or_sources
62
+ when nil
63
+ []
64
+ else
65
+ [source_or_sources]
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,17 @@
1
+ module Rapa
2
+ module Responses
3
+ class ListItemsResponse < BaseResponse
4
+ private
5
+
6
+ # @note Override
7
+ def resource_class
8
+ ::Rapa::Resources::ItemResource
9
+ end
10
+
11
+ # @note Override
12
+ def source_or_sources
13
+ body.dig("ItemLookupResponse", "Items", "Item")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,48 @@
1
+ module Rapa
2
+ module Responses
3
+ class SearchItemsResponse < BaseResponse
4
+ # @return [Rapa::Error]
5
+ def error
6
+ if value = body.dig("ItemSearchResponse", "Items", "Request", "Errors", "Error")
7
+ ::Rapa::Error.new(value)
8
+ end
9
+ end
10
+
11
+ # @return [Boolean]
12
+ def has_error?
13
+ !error.nil?
14
+ end
15
+
16
+ # @return [Boolean]
17
+ def has_valid_request?
18
+ body.dig("ItemSearchResponse", "Items", "Request", "IsValid") == "True"
19
+ end
20
+
21
+ # @return [Integer, nil]
22
+ def total_pages
23
+ if value = body.dig("ItemSearchResponse", "Items", "TotalPages")
24
+ value.to_i
25
+ end
26
+ end
27
+
28
+ # @return [Integer, nil]
29
+ def total_results
30
+ if value = body.dig("ItemSearchResponse", "Items", "TotalResults")
31
+ value.to_i
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ # @note Override
38
+ def resource_class
39
+ ::Rapa::Resources::ItemResource
40
+ end
41
+
42
+ # @note Override
43
+ def source_or_sources
44
+ body.dig("ItemSearchResponse", "Items", "Item")
45
+ end
46
+ end
47
+ end
48
+ end