amazon-ecs 1.2.2 → 2.0.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.
Files changed (4) hide show
  1. data/README +25 -21
  2. data/lib/amazon/ecs.rb +67 -87
  3. data/test/amazon/ecs_test.rb +94 -80
  4. metadata +10 -9
data/README CHANGED
@@ -1,16 +1,18 @@
1
1
  == amazon-ecs
2
2
 
3
- Generic Product Advertising Ruby API using Hpricot. Uses Response and
3
+ Generic Product Advertising Ruby API using Nokogiri. Uses Response and
4
4
  Element wrapper classes for easy access to the REST API XML output.
5
5
 
6
6
  It is generic, so you can easily extend <tt>Amazon::Ecs</tt> to support
7
7
  other not implemented REST operations; and it is also generic because it just wraps around
8
- Hpricot element object, instead of providing one-to-one object/attributes to XML elements map.
8
+ Nokogiri element object, instead of providing one-to-one object/attributes to XML elements map.
9
9
 
10
10
  The idea is as the API evolves, there is a change in REST XML output structure,
11
11
  no updates will be required on <tt>amazon-ecs</tt> gem,
12
12
  instead you just need to update the element path.
13
13
 
14
+ For HPricot dependency implementation, please install 1.2.x version or checkout v1.2 branch.
15
+
14
16
  == INSTALLATION
15
17
 
16
18
  $ gem install amazon-ecs
@@ -48,45 +50,45 @@ instead you just need to update the element path.
48
50
  # traverse through each item (Amazon::Element)
49
51
  res.items.each do |item|
50
52
  # retrieve string value using XML path
51
- item.get('asin')
52
- item.get('itemattributes/title')
53
+ item.get('ASIN')
54
+ item.get('ItemAttributes/Title')
53
55
 
54
56
  # return Amazon::Element instance
55
- item_attributes = item.get_element('itemattributes')
56
- item_attributes.get('title')
57
+ item_attributes = item.get_element('ItemAttributes')
58
+ item_attributes.get('Title')
57
59
 
58
60
  # return first author or a string array of authors
59
- item_attributes.get('author') # 'Author 1'
60
- item_attributes.get_array('author') # ['Author 1', 'Author 2', ...]
61
+ item_attributes.get('Author') # 'Author 1'
62
+ item_attributes.get_array('Author') # ['Author 1', 'Author 2', ...]
61
63
 
62
64
  # return an hash of children text values with the element names as the keys
63
- item.get_hash('smallimage') # {:url => ..., :width => ..., :height => ...}
65
+ item.get_hash('SmallImage') # {:url => ..., :width => ..., :height => ...}
64
66
 
65
67
  # return the first matching path as Amazon::Element
66
- item_height = item.get_element('itemdimensions/height')
68
+ item_height = item.get_element('ItemDimensions/Height')
67
69
 
68
70
  # retrieve attributes from Amazon::Element
69
- item_height.attributes['units'] # 'hundredths-inches'
71
+ item_height.attributes['Units'] # 'hundredths-inches'
70
72
 
71
73
  # return an array of Amazon::Element
72
- authors = item.get_elements('author')
74
+ authors = item.get_elements('Author')
73
75
 
74
- # return Hpricot::Elements object or nil if not found
75
- reviews = item/'editorialreview'
76
+ # return Nokogiri::XML::NodeSet object or nil if not found
77
+ reviews = item/'EditorialReview'
76
78
 
77
- # traverse through Hpricot elements
79
+ # traverse through Nokogiri elements
78
80
  reviews.each do |review|
79
- # Getting hash value out of Hpricot element
81
+ # Getting hash value out of Nokogiri element
80
82
  Amazon::Element.get_hash(review) # [:source => ..., :content ==> ...]
81
83
 
82
84
  # Or to get unescaped HTML values
83
- Amazon::Element.get_unescaped(review, 'source')
84
- Amazon::Element.get_unescaped(review, 'content')
85
+ Amazon::Element.get_unescaped(review, 'Source')
86
+ Amazon::Element.get_unescaped(review, 'Content')
85
87
 
86
88
  # Or this way
87
89
  el = Amazon::Element.new(review)
88
- el.get_unescaped('source')
89
- el.get_unescaped('content')
90
+ el.get_unescaped('Source')
91
+ el.get_unescaped('Content')
90
92
  end
91
93
  end
92
94
 
@@ -121,8 +123,10 @@ http://www.awszone.com/scratchpads/aws/ecs.us/index.aws
121
123
 
122
124
  Thanks to Dan Milne (http://da.nmilne.com/) for the signed request patch.
123
125
 
126
+ Thanks to Bryan Housel (https://github.com/bhousel/amazon-ecs) for the initial Nokogiri patch
127
+
124
128
  == LICENSE
125
129
 
126
130
  (The MIT License)
127
131
 
128
- Copyright (c) 2010 Herryanto Siatono, http://www.siatono.com
132
+ Copyright (c) 2011 Herryanto Siatono, http://www.siatono.com
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2009 Herryanto Siatono, Pluit Solutions
2
+ # Copyright (c) 2010 Herryanto Siatono
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
5
5
  # a copy of this software and associated documentation files (the
@@ -22,7 +22,7 @@
22
22
  #++
23
23
 
24
24
  require 'net/http'
25
- require 'hpricot'
25
+ require 'nokogiri'
26
26
  require 'cgi'
27
27
  require 'hmac-sha2'
28
28
  require 'base64'
@@ -121,19 +121,23 @@ module Amazon
121
121
 
122
122
  # Response object returned after a REST call to Amazon service.
123
123
  class Response
124
+
124
125
  # XML input is in string format
125
126
  def initialize(xml)
126
- @doc = Hpricot(xml)
127
+ @doc = Nokogiri::XML(xml)
128
+ @doc.remove_namespaces!
129
+ # @doc.xpath("//*").each { |elem| elem.name = elem.name.downcase }
130
+ # @doc.xpath("//@*").each { |att| att.name = att.name.downcase }
127
131
  end
128
132
 
129
- # Return Hpricot object.
133
+ # Return Nokogiri::XML::Document object.
130
134
  def doc
131
135
  @doc
132
136
  end
133
137
 
134
138
  # Return true if request is valid.
135
139
  def is_valid_request?
136
- (@doc/"isvalid").inner_html == "True"
140
+ Element.get(@doc, "//IsValid") == "True"
137
141
  end
138
142
 
139
143
  # Return true if response has an error.
@@ -143,20 +147,17 @@ module Amazon
143
147
 
144
148
  # Return error message.
145
149
  def error
146
- Element.get(@doc, "error/message")
150
+ Element.get(@doc, "//Error/Message")
147
151
  end
148
152
 
149
153
  # Return error code
150
154
  def error_code
151
- Element.get(@doc, "error/code")
155
+ Element.get(@doc, "//Error/Code")
152
156
  end
153
157
 
154
158
  # Return an array of Amazon::Element item objects.
155
159
  def items
156
- unless @items
157
- @items = (@doc/"item").collect {|item| Element.new(item)}
158
- end
159
- @items
160
+ @items ||= (@doc/"Item").collect { |item| Element.new(item) }
160
161
  end
161
162
 
162
163
  # Return the first item (Amazon::Element)
@@ -166,26 +167,17 @@ module Amazon
166
167
 
167
168
  # Return current page no if :item_page option is when initiating the request.
168
169
  def item_page
169
- unless @item_page
170
- @item_page = (@doc/"itemsearchrequest/itempage").inner_html.to_i
171
- end
172
- @item_page
170
+ @item_page ||= Element.get(@doc, "//ItemPage").to_i
173
171
  end
174
172
 
175
173
  # Return total results.
176
174
  def total_results
177
- unless @total_results
178
- @total_results = (@doc/"totalresults").inner_html.to_i
179
- end
180
- @total_results
175
+ @total_results ||= Element.get(@doc, "//TotalResults").to_i
181
176
  end
182
177
 
183
178
  # Return total pages.
184
179
  def total_pages
185
- unless @total_pages
186
- @total_pages = (@doc/"totalpages").inner_html.to_i
187
- end
188
- @total_pages
180
+ @total_pages ||= Element.get(@doc, "//TotalPages").to_i
189
181
  end
190
182
 
191
183
  def marshal_dump
@@ -272,35 +264,68 @@ module Amazon
272
264
  end
273
265
  end
274
266
 
275
- # Internal wrapper class to provide convenient method to access Hpricot element value.
267
+ # Internal wrapper class to provide convenient method to access Nokogiri element value.
276
268
  class Element
277
- # Pass Hpricot::Elements object
269
+ class << self
270
+ # Return the text value of an element.
271
+ def get(element, path='.')
272
+ return unless element
273
+ result = element.at_xpath(path)
274
+ result = result.inner_html if result
275
+ result
276
+ end
277
+
278
+ # Return an unescaped text value of an element.
279
+ def get_unescaped(element, path='.')
280
+ result = self.get(element, path)
281
+ CGI::unescapeHTML(result) if result
282
+ end
283
+
284
+ # Return an array of values based on the given path.
285
+ def get_array(element, path='.')
286
+ return unless element
287
+
288
+ result = element/path
289
+ if (result.is_a? Nokogiri::XML::NodeSet) || (result.is_a? Array)
290
+ result.collect { |item| self.get(item) }
291
+ else
292
+ [self.get(result)]
293
+ end
294
+ end
295
+
296
+ # Return child element text values of the given path.
297
+ def get_hash(element, path='.')
298
+ return unless element
299
+
300
+ result = element.at_xpath(path)
301
+ if result
302
+ hash = {}
303
+ result = result.children
304
+ result.each do |item|
305
+ hash[item.name] = item.inner_html
306
+ end
307
+ hash
308
+ end
309
+ end
310
+ end
311
+
312
+ # Pass Nokogiri::XML::Element object
278
313
  def initialize(element)
279
314
  @element = element
280
315
  end
281
316
 
282
- # Returns Hpricot::Elments object
317
+ # Returns Nokogiri::XML::Element object
283
318
  def elem
284
319
  @element
285
320
  end
286
321
 
287
- # Find Hpricot::Elements matching the given path. Example: element/"author".
322
+ # Returns a Nokogiri::XML::NodeSet of elements matching the given path. Example: element/"author".
288
323
  def /(path)
289
324
  elements = @element/path
290
325
  return nil if elements.size == 0
291
326
  elements
292
327
  end
293
-
294
- # Return an array of Amazon::Element matching the given path, or Amazon::Element if there
295
- # is only one element found.
296
- #
297
- # <b>DEPRECATED:</b> Please use <tt>get_elements</tt> and <tt>get_element</tt> instead.
298
- def search_and_convert(path)
299
- elements = self.get_elements(path)
300
- return elements.first if elements and elements.size == 1
301
- elements
302
- end
303
-
328
+
304
329
  # Return an array of Amazon::Element matching the given path
305
330
  def get_elements(path)
306
331
  elements = self./(path)
@@ -315,22 +340,22 @@ module Amazon
315
340
  end
316
341
 
317
342
  # Get the text value of the given path, leave empty to retrieve current element value.
318
- def get(path='')
343
+ def get(path='.')
319
344
  Element.get(@element, path)
320
345
  end
321
346
 
322
347
  # Get the unescaped HTML text of the given path.
323
- def get_unescaped(path='')
348
+ def get_unescaped(path='.')
324
349
  Element.get_unescaped(@element, path)
325
350
  end
326
351
 
327
352
  # Get the array values of the given path.
328
- def get_array(path='')
353
+ def get_array(path='.')
329
354
  Element.get_array(@element, path)
330
355
  end
331
356
 
332
357
  # Get the children element text values in hash format with the element names as the hash keys.
333
- def get_hash(path='')
358
+ def get_hash(path='.')
334
359
  Element.get_hash(@element, path)
335
360
  end
336
361
 
@@ -339,51 +364,6 @@ module Amazon
339
364
  self.elem.attributes
340
365
  end
341
366
 
342
- # Similar to #get, except an element object must be passed-in.
343
- def self.get(element, path='')
344
- return unless element
345
- result = element.at(path)
346
- result = result.inner_html if result
347
- result
348
- end
349
-
350
- # Similar to #get_unescaped, except an element object must be passed-in.
351
- def self.get_unescaped(element, path='')
352
- result = get(element, path)
353
- CGI::unescapeHTML(result) if result
354
- end
355
-
356
- # Similar to #get_array, except an element object must be passed-in.
357
- def self.get_array(element, path='')
358
- return unless element
359
-
360
- result = element/path
361
- if (result.is_a? Hpricot::Elements) || (result.is_a? Array)
362
- parsed_result = []
363
- result.each {|item|
364
- parsed_result << Element.get(item)
365
- }
366
- parsed_result
367
- else
368
- [Element.get(result)]
369
- end
370
- end
371
-
372
- # Similar to #get_hash, except an element object must be passed-in.
373
- def self.get_hash(element, path='')
374
- return unless element
375
-
376
- result = element.at(path)
377
- if result
378
- hash = {}
379
- result = result.children
380
- result.each do |item|
381
- hash[item.name.to_sym] = item.inner_html
382
- end
383
- hash
384
- end
385
- end
386
-
387
367
  def to_s
388
368
  elem.to_s if elem
389
369
  end
@@ -19,54 +19,56 @@ class Amazon::EcsTest < Test::Unit::TestCase
19
19
  options[:aWS_secret_key] = AWS_SECRET_KEY
20
20
  end
21
21
 
22
- ## Test item_search
22
+ # Test item_search
23
23
  def test_item_search
24
- resp = Amazon::Ecs.item_search('ruby')
25
-
26
- assert(resp.is_valid_request?)
27
- assert(resp.total_results >= 3600)
28
- assert(resp.total_pages >= 360)
29
-
30
- signature_elements = (resp.doc/"arguments/argument").select do |ele|
31
- ele.attributes['name'] == 'Signature' || ele.attributes['Name'] == 'Signature'
32
- end.length
33
-
34
- assert(signature_elements == 1)
24
+ resp = Amazon::Ecs.item_search('ruby')
25
+ assert resp.is_valid_request?,
26
+ "Not a valid request"
27
+ assert (resp.total_results >= 3600),
28
+ "Results returned (#{resp.total_results}) were < 3600"
29
+ assert (resp.total_pages >= 360),
30
+ "Pages returned (#{resp.total_pages}) were < 360"
35
31
  end
36
-
32
+
37
33
  def test_item_search_with_special_characters
38
- Amazon::Ecs.debug = true
34
+ Amazon::Ecs.debug = true
39
35
  resp = Amazon::Ecs.item_search('()*&^%$')
40
- assert(resp.is_valid_request?)
36
+ assert resp.is_valid_request?,
37
+ "Not a valid request"
41
38
  end
42
-
39
+
43
40
  def test_item_search_with_paging
44
41
  resp = Amazon::Ecs.item_search('ruby', :item_page => 2)
45
- assert resp.is_valid_request?
46
- assert 2, resp.item_page
42
+ assert resp.is_valid_request?,
43
+ "Not a valid request"
44
+ assert_equal 2, resp.item_page,
45
+ "Page returned (#{resp.item_page}) different from expected (2)"
47
46
  end
48
-
47
+
49
48
  def test_item_search_with_invalid_request
50
49
  resp = Amazon::Ecs.item_search(nil)
51
- assert !resp.is_valid_request?
50
+ assert !resp.is_valid_request?,
51
+ "Expected invalid request error"
52
52
  end
53
-
53
+
54
54
  def test_item_search_with_no_result
55
- resp = Amazon::Ecs.item_search('afdsafds')
56
-
57
- assert resp.is_valid_request?
58
- assert_equal "We did not find any matches for your request.",
59
- resp.error
55
+ resp = Amazon::Ecs.item_search('afdsafds')
56
+ assert resp.is_valid_request?,
57
+ "Not a valid request"
58
+ assert_equal "We did not find any matches for your request.", resp.error,
59
+ "Error string different from expected"
60
60
  end
61
61
 
62
62
  def test_item_search_uk
63
63
  resp = Amazon::Ecs.item_search('ruby', :country => :uk)
64
- assert resp.is_valid_request?
64
+ assert resp.is_valid_request?,
65
+ "Not a valid request"
65
66
  end
66
67
 
67
68
  def test_item_search_by_author
68
69
  resp = Amazon::Ecs.item_search('dave', :type => :author)
69
- assert resp.is_valid_request?
70
+ assert resp.is_valid_request?,
71
+ "Not a valid request"
70
72
  end
71
73
 
72
74
  def test_item_get
@@ -75,96 +77,108 @@ class Amazon::EcsTest < Test::Unit::TestCase
75
77
 
76
78
  # test get
77
79
  assert_equal "Programming Ruby: The Pragmatic Programmers' Guide, Second Edition",
78
- item.get("itemattributes/title")
80
+ item.get("ItemAttributes/Title"),
81
+ "Title different from expected"
79
82
 
80
83
  # test get_array
81
84
  assert_equal ['Dave Thomas', 'Chad Fowler', 'Andy Hunt'],
82
- item.get_array("author")
83
-
85
+ item.get_array("Author"),
86
+ "Authors Array different from expected"
87
+
84
88
  # test get_hash
85
- small_image = item.get_hash("smallimage")
86
-
87
- assert_equal 3, small_image.keys.size
88
- assert_match ".jpg", small_image[:url]
89
- assert_equal "75", small_image[:height]
90
- assert_equal "59", small_image[:width]
89
+ small_image = item.get_hash("SmallImage")
91
90
 
91
+ assert_equal 3, small_image.keys.size,
92
+ "Image hash key count (#{small_image.keys.size}) different from expected (3)"
93
+ assert_match ".jpg", small_image['URL'],
94
+ "Image type different from expected (.jpg)"
95
+ assert_equal "75", small_image['Height'],
96
+ "Image height (#{small_image['Height']}) different from expected (75)"
97
+ assert_equal "59", small_image['Width'],
98
+ "Image width (#{small_image['Width']}) different from expected (59)"
99
+
92
100
  # test /
93
- reviews = item/"editorialreview"
101
+ reviews = item/"EditorialReview"
94
102
  reviews.each do |review|
95
- # returns unescaped HTML content, Hpricot escapes all text values
96
- assert Amazon::Element.get_unescaped(review, 'source')
97
- assert Amazon::Element.get_unescaped(review, 'content')
103
+ # returns unescaped HTML content, Nokogiri escapes all text values
104
+ assert Amazon::Element.get_unescaped(review, 'Source'),
105
+ "XPath editorialreview failed to get source"
106
+ assert Amazon::Element.get_unescaped(review, 'Content'),
107
+ "XPath editorialreview failed to get content"
98
108
  end
99
109
  end
100
-
110
+
101
111
  ## Test item_lookup
102
112
  def test_item_lookup
103
113
  resp = Amazon::Ecs.item_lookup('0974514055')
104
114
  assert_equal "Programming Ruby: The Pragmatic Programmers' Guide, Second Edition",
105
- resp.first_item.get("itemattributes/title")
115
+ resp.first_item.get("ItemAttributes/Title"),
116
+ "Title different from expected"
106
117
  end
107
118
 
108
119
  def test_item_lookup_with_invalid_request
109
120
  resp = Amazon::Ecs.item_lookup(nil)
110
- assert resp.has_error?
111
- assert resp.error
121
+ assert resp.has_error?,
122
+ "Response should have been invalid"
123
+ assert resp.error,
124
+ "Response should have contained an error"
112
125
  end
113
-
114
- def test_item_lookup_with_no_result
115
- resp = Amazon::Ecs.item_lookup('abc')
116
-
117
- assert resp.is_valid_request?
118
- assert_match(/ABC is not a valid value for ItemId/, resp.error)
119
- end
120
126
 
121
- def test_search_and_convert
122
- resp = Amazon::Ecs.item_lookup('0974514055')
123
- title = resp.first_item.get("itemattributes/title")
124
- authors = resp.first_item.search_and_convert("author")
125
-
126
- assert_equal "Programming Ruby: The Pragmatic Programmers' Guide, Second Edition", title
127
- assert authors.is_a?(Array)
128
- assert 3, authors.size
129
- assert_equal "Dave Thomas", authors.first.get
127
+ def test_item_lookup_with_no_result
128
+ resp = Amazon::Ecs.item_lookup('abc')
129
+ assert resp.is_valid_request?,
130
+ "Not a valid request"
131
+ assert_match /ABC is not a valid value for ItemId/, resp.error,
132
+ "Error Message for lookup of ASIN = ABC different from expected"
130
133
  end
131
134
 
132
135
  def test_get_elements
133
136
  resp = Amazon::Ecs.item_lookup('0974514055')
134
137
  item = resp.first_item
138
+
139
+ authors = item.get_elements("Author")
140
+ assert_instance_of Array, authors,
141
+ "Authors should be an Array"
142
+ assert_equal 3, authors.size,
143
+ "Author array size (#{authors.size}) different from expected (3)"
144
+ assert_instance_of Amazon::Element, authors.first,
145
+ "Authors array first element (#{authors.first.class}) should be an Amazon::Element"
146
+ assert_equal "Dave Thomas", authors.first.get,
147
+ "First Author (#{authors.first.get}) different from expected (Dave Thomas)"
135
148
 
136
- authors = item.get_elements("author")
137
- assert authors.is_a?(Array)
138
- assert 3, authors.size
139
- assert authors.first.is_a?(Amazon::Element)
140
- assert_equal "Dave Thomas", authors.first.get
141
-
142
- asin = item.get_elements("asin")
143
- assert asin.is_a?(Array)
144
- assert 1, authors.size
149
+ asin = item.get_elements("./ASIN")
150
+ assert_instance_of Array, asin,
151
+ "ASIN should be an Array"
152
+ assert_equal 1, asin.size,
153
+ "ASIN array size (#{asin.size}) different from expected (1)"
145
154
  end
146
155
 
147
156
  def test_get_element_and_attributes
148
157
  resp = Amazon::Ecs.item_lookup('0974514055')
149
158
  item = resp.first_item
150
-
151
- first_author = item.get_element("author")
152
- assert_equal "Dave Thomas", first_author.get
153
- assert_equal '', first_author.attributes['unknown']
159
+
160
+ first_author = item.get_element("Author")
161
+ assert_equal "Dave Thomas", first_author.get,
162
+ "First Author (#{first_author.get}) different from expected (Dave Thomas)"
163
+ assert_nil first_author.attributes['unknown'],
164
+ "First Author 'unknown' attributes should be nil"
154
165
 
155
- item_height = item.get_element("itemdimensions/height")
156
- assert_equal "hundredths-inches", item_height.attributes['units']
166
+ item_height = item.get_element("ItemDimensions/Height")
167
+ units = item_height.attributes['Units'].inner_html if item_height
168
+ assert_equal "hundredths-inches", units,
169
+ "Item Height 'units' attributes (#{units}) different from expected (hundredths-inches)"
157
170
  end
158
-
171
+
159
172
  def test_multibyte_search
160
173
  resp = Amazon::Ecs.item_search("パソコン")
161
- assert(resp.is_valid_request?)
174
+ assert resp.is_valid_request?,
175
+ "Not a valid request"
162
176
  end
163
-
177
+
164
178
  def test_marshal_dump_and_load
165
179
  resp = Amazon::Ecs::Response.new(File.read(File.expand_path('../../fixtures/item_search.xml', __FILE__)))
166
180
  dumped_resp = Marshal.load(Marshal.dump(resp))
167
-
181
+
168
182
  assert_equal resp.doc.to_s, dumped_resp.doc.to_s
169
183
  assert_equal resp.items.size, dumped_resp.items.size
170
184
  assert_equal resp.item_page, dumped_resp.item_page
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amazon-ecs
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
- - 1
8
7
  - 2
9
- - 2
10
- version: 1.2.2
8
+ - 0
9
+ - 0
10
+ version: 2.0.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Herryanto Siatono
@@ -15,22 +15,23 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-05-07 00:00:00 +08:00
18
+ date: 2011-05-09 00:00:00 +08:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- name: hpricot
22
+ name: nokogiri
23
23
  prerelease: false
24
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
25
  none: false
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- hash: 3
29
+ hash: 7
30
30
  segments:
31
- - 0
31
+ - 1
32
32
  - 4
33
- version: "0.4"
33
+ - 0
34
+ version: 1.4.0
34
35
  type: :runtime
35
36
  version_requirements: *id001
36
37
  - !ruby/object:Gem::Dependency