libcraigscrape 0.9.1 → 1.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.
data/CHANGELOG CHANGED
@@ -1,5 +1,8 @@
1
1
  == Change Log
2
2
 
3
+ === Release 1.0
4
+ - Replaced hpricot dependency with Nokogiri. Nokogiri should be faster and more reliable. Whoo-hoo!
5
+
3
6
  === Release 0.9.1
4
7
  - Added support for posting_has_expired? and expired post recognition
5
8
  - Fixed a weird bug in craigwatch that would cause a scrape to abort if a flagged_for_removal? was encountered when using certain (minimal) filtering
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ include FileUtils
11
11
  RbConfig = Config unless defined? RbConfig
12
12
 
13
13
  NAME = "libcraigscrape"
14
- VERS = ENV['VERSION'] || "0.9.1"
14
+ VERS = ENV['VERSION'] || "1.0"
15
15
  PKG = "#{NAME}-#{VERS}"
16
16
 
17
17
  RDOC_OPTS = ['--quiet', '--title', 'The libcraigscrape Reference', '--main', 'README', '--inline-source']
@@ -37,7 +37,7 @@ SPEC =
37
37
  s.files = PKG_FILES
38
38
  s.require_paths = ["lib"]
39
39
  s.test_files = FileList['test/test_*.rb']
40
- s.add_dependency 'hpricot', '> 0.8'
40
+ s.add_dependency 'nokogiri', '>= 1.4.4'
41
41
  s.add_dependency 'htmlentities', '>= 4.0.0'
42
42
  s.add_dependency 'activesupport','>= 2.3.0', '< 3'
43
43
  s.add_dependency 'activerecord', '>= 2.3.0', '< 3'
data/lib/geo_listings.rb CHANGED
@@ -36,7 +36,7 @@ class CraigScrape
36
36
  def location
37
37
  unless @location
38
38
  cursor = html % 'h3 > b > a:first-of-type'
39
- cursor = cursor.next_node if cursor
39
+ cursor = cursor.next if cursor
40
40
  @location = $1 if cursor and LOCATION_NAME.match he_decode(cursor.to_s)
41
41
  end
42
42
 
@@ -5,12 +5,13 @@
5
5
  require 'rubygems'
6
6
 
7
7
  gem 'activesupport', '~> 2.3'
8
- gem 'hpricot', '~> 0.8'
8
+ gem 'nokogiri', '~> 1.4.4'
9
9
  gem 'htmlentities', '~> 4.0.0'
10
10
 
11
+
11
12
  require 'net/http'
12
13
  require 'zlib'
13
- require 'hpricot'
14
+ require 'nokogiri'
14
15
  require 'htmlentities'
15
16
  require 'active_support'
16
17
 
data/lib/listings.rb CHANGED
@@ -21,7 +21,8 @@ class CraigScrape::Listings < CraigScrape::Scraper
21
21
  current_date = nil
22
22
  @posts = []
23
23
 
24
- post_tags = html.get_elements_by_tag_name('p','h4')
24
+ # All we care about are p and h4 tags. This seemed to be the only way I could do this on Nokogiri:
25
+ post_tags = html.search('*').reject{|n| !/^(?:p|h4)$/i.match n.name }
25
26
 
26
27
  # The last p in the list is sometimes a 'next XXX pages' link. We don't want to include this in our PostSummary output:
27
28
  post_tags.pop if (
@@ -82,7 +83,7 @@ class CraigScrape::Listings < CraigScrape::Scraper
82
83
 
83
84
  # If there's no 'a' in the next sibling, we'll have just performed a nil assignment, otherwise
84
85
  # We're looking good.
85
- next_link = cursor.next_sibling if cursor and /^[\d]+$/.match cursor.inner_html
86
+ next_link = cursor.next_element if cursor and /^[\d]+$/.match cursor.inner_html
86
87
  end
87
88
 
88
89
  # We have an anchor tag - so - let's assign the href:
data/lib/posting.rb CHANGED
@@ -79,7 +79,7 @@ class CraigScrape::Posting < CraigScrape::Scraper
79
79
  def reply_to
80
80
  unless @reply_to
81
81
  cursor = html_head.at 'hr' if html_head
82
- cursor = cursor.next_sibling until cursor.nil? or cursor.name == 'a'
82
+ cursor = cursor.next until cursor.nil? or cursor.name == 'a'
83
83
  @reply_to = $1 if cursor and REPLY_TO.match he_decode(cursor.inner_html)
84
84
  end
85
85
 
@@ -90,7 +90,7 @@ class CraigScrape::Posting < CraigScrape::Scraper
90
90
  def post_time
91
91
  unless @post_time
92
92
  cursor = html_head.at 'hr' if html_head
93
- cursor = cursor.next_node until cursor.nil? or POST_DATE.match cursor.to_s
93
+ cursor = cursor.next until cursor.nil? or POST_DATE.match cursor.to_s
94
94
  @post_time = Time.parse $1 if $1
95
95
  end
96
96
 
@@ -100,8 +100,8 @@ class CraigScrape::Posting < CraigScrape::Scraper
100
100
  # Integer, Craigslist's unique posting id
101
101
  def posting_id
102
102
  unless @posting_id
103
- cursor = Hpricot.parse html_footer if html_footer
104
- cursor = cursor.next_node until cursor.nil? or POSTING_ID.match cursor.to_s
103
+ cursor = Nokogiri::HTML html_footer, nil, HTML_ENCODING if html_footer
104
+ cursor = cursor.next until cursor.nil? or POSTING_ID.match cursor.to_s
105
105
  @posting_id = $1.to_i if $1
106
106
  end
107
107
 
@@ -135,7 +135,7 @@ class CraigScrape::Posting < CraigScrape::Scraper
135
135
  # Real estate listings can work a little different for location:
136
136
  unless @location
137
137
  cursor = craigslist_body.at 'small'
138
- cursor = cursor.previous_node until cursor.nil? or cursor.text?
138
+ cursor = cursor.previous until cursor.nil? or cursor.text?
139
139
 
140
140
  @location = he_decode(cursor.to_s.strip) if cursor
141
141
  end
@@ -295,7 +295,7 @@ class CraigScrape::Posting < CraigScrape::Scraper
295
295
  # I set apart from html to work around the SystemStackError bugs in test_bugs_found061710. Essentially we
296
296
  # return everything above the user_body
297
297
  def html_head
298
- @html_head = Hpricot.parse $1 if @html_head.nil? and HTML_HEADER.match html_source
298
+ @html_head = Nokogiri::HTML $1, nil, HTML_ENCODING if @html_head.nil? and HTML_HEADER.match html_source
299
299
  # We return html itself if HTML_HEADER doesn't match, which would be case for a 404 page or something
300
300
  @html_head ||= html
301
301
 
@@ -316,9 +316,9 @@ class CraigScrape::Posting < CraigScrape::Scraper
316
316
  end
317
317
 
318
318
  # Read the notes on user_body. However, unlike the user_body, the craigslist portion of this div can be relied upon to be valid html.
319
- # So - we'll return it as an Hpricot object.
319
+ # So - we'll return it as a Nokogiri object.
320
320
  def craigslist_body
321
- Hpricot.parse $3 if USERBODY_PARTS.match html_source
321
+ Nokogiri::HTML $3, nil, HTML_ENCODING if USERBODY_PARTS.match html_source
322
322
  end
323
323
 
324
324
  end
data/lib/scraper.rb CHANGED
@@ -33,6 +33,8 @@ class CraigScrape::Scraper
33
33
 
34
34
  URL_PARTS = /^(?:([^\:]+)\:\/\/([^\/]*))?(.*)$/
35
35
  HTML_TAG = /<\/?[^>]*>/
36
+ # We have to specify this to nokogiri. Sometimes it tries to figure out encoding on its own, and craigslist users post crazy bytes sometimes
37
+ HTML_ENCODING = "UTF-8"
36
38
 
37
39
  # Returns the full url that corresponds to this resource
38
40
  attr_reader :url
@@ -202,9 +204,9 @@ class CraigScrape::Scraper
202
204
  @html_source
203
205
  end
204
206
 
205
- # Returns an hpricot parse, of the current URI
207
+ # Returns an Nokogiri parse, of the current URI
206
208
  def html
207
- @html ||= Hpricot.parse html_source if html_source
209
+ @html ||= Nokogiri::HTML html_source, nil, HTML_ENCODING if html_source
208
210
  @html
209
211
  end
210
212
  end
@@ -210,47 +210,47 @@ class CraigslistListingTest < Test::Unit::TestCase
210
210
 
211
211
  mia_search_kitten031510 = CraigScrape::Listings.new relative_uri_for('listing_samples/mia_search_kitten.3.15.10.html')
212
212
  assert_equal "Adopt a 7 month on kitten- $75", mia_search_kitten031510.posts[0].label
213
- assert_equal [0, 0, 0, 15, 3, 2010, 1, 74, true, "EDT"], mia_search_kitten031510.posts[0].post_date.to_a
213
+ assert_equal [15, 3], mia_search_kitten031510.posts[0].post_date.to_a[3..4]
214
214
  assert_equal "Adorable Kitten! Free!!!", mia_search_kitten031510.posts[1].label
215
- assert_equal [0, 0, 0, 15, 3, 2010, 1, 74, true, "EDT"], mia_search_kitten031510.posts[1].post_date.to_a
215
+ assert_equal [15, 3], mia_search_kitten031510.posts[1].post_date.to_a[3..4]
216
216
  assert_equal "KITTENS,5 months, 1 Russian blue, 1 grey & white,vac spy/neu,$35fee ea", mia_search_kitten031510.posts[2].label
217
- assert_equal [0, 0, 0, 13, 3, 2010, 6, 72, false, "EST"], mia_search_kitten031510.posts[2].post_date.to_a
217
+ assert_equal [13, 3], mia_search_kitten031510.posts[2].post_date.to_a[3..4]
218
218
  assert_equal "Kitties need a good home", mia_search_kitten031510.posts[3].label
219
- assert_equal [0, 0, 0, 13, 3, 2010, 6, 72, false, "EST"], mia_search_kitten031510.posts[3].post_date.to_a
219
+ assert_equal [13, 3], mia_search_kitten031510.posts[3].post_date.to_a[3..4]
220
220
  assert_equal "7 week old kittens for adoption", mia_search_kitten031510.posts[4].label
221
- assert_equal [0, 0, 0, 13, 3, 2010, 6, 72, false, "EST"], mia_search_kitten031510.posts[4].post_date.to_a
221
+ assert_equal [13, 3], mia_search_kitten031510.posts[4].post_date.to_a[3..4]
222
222
  assert_equal "Adorable Orange Kitten Free to Good Home", mia_search_kitten031510.posts[5].label
223
- assert_equal [0, 0, 0, 12, 3, 2010, 5, 71, false, "EST"], mia_search_kitten031510.posts[5].post_date.to_a
223
+ assert_equal [12, 3], mia_search_kitten031510.posts[5].post_date.to_a[3..4]
224
224
  assert_equal "7 month old kitten free to good home", mia_search_kitten031510.posts[6].label
225
- assert_equal [0, 0, 0, 12, 3, 2010, 5, 71, false, "EST"], mia_search_kitten031510.posts[6].post_date.to_a
225
+ assert_equal [12, 3], mia_search_kitten031510.posts[6].post_date.to_a[3..4]
226
226
  assert_equal "FEMALE KITTEN FOR GOOD HOME", mia_search_kitten031510.posts[7].label
227
- assert_equal [0, 0, 0, 9, 3, 2010, 2, 68, false, "EST"], mia_search_kitten031510.posts[7].post_date.to_a
227
+ assert_equal [9, 3], mia_search_kitten031510.posts[7].post_date.to_a[3..4]
228
228
  assert_equal "Kitten", mia_search_kitten031510.posts[8].label
229
- assert_equal [0, 0, 0, 4, 3, 2010, 4, 63, false, "EST"], mia_search_kitten031510.posts[8].post_date.to_a
229
+ assert_equal [4, 3], mia_search_kitten031510.posts[8].post_date.to_a[3..4]
230
230
  assert_equal "Kitties need a good home", mia_search_kitten031510.posts[9].label
231
- assert_equal [0, 0, 0, 4, 3, 2010, 4, 63, false, "EST"], mia_search_kitten031510.posts[9].post_date.to_a
231
+ assert_equal [4, 3], mia_search_kitten031510.posts[9].post_date.to_a[3..4]
232
232
  assert_equal "Persain Cat And Tabby Cat", mia_search_kitten031510.posts[10].label
233
- assert_equal [0, 0, 0, 1, 3, 2010, 1, 60, false, "EST"], mia_search_kitten031510.posts[10].post_date.to_a
233
+ assert_equal [1, 3], mia_search_kitten031510.posts[10].post_date.to_a[3..4]
234
234
  assert_equal "Tabby female kitten in a parking lot needs your help", mia_search_kitten031510.posts[11].label
235
- assert_equal [0, 0, 0, 23, 2, 2010, 2, 54, false, "EST"], mia_search_kitten031510.posts[11].post_date.to_a
235
+ assert_equal [23, 2], mia_search_kitten031510.posts[11].post_date.to_a[3..4]
236
236
  assert_equal "Spring is almost officially here, grow your family, adopt a kitty!", mia_search_kitten031510.posts[12].label
237
- assert_equal [0, 0, 0, 22, 2, 2010, 1, 53, false, "EST"], mia_search_kitten031510.posts[12].post_date.to_a
237
+ assert_equal [22, 2], mia_search_kitten031510.posts[12].post_date.to_a[3..4]
238
238
  assert_equal "Many adorable kittens for adoption!", mia_search_kitten031510.posts[13].label
239
- assert_equal [0, 0, 0, 22, 2, 2010, 1, 53, false, "EST"], mia_search_kitten031510.posts[13].post_date.to_a
239
+ assert_equal [22, 2], mia_search_kitten031510.posts[13].post_date.to_a[3..4]
240
240
  assert_equal "2 free cats/kitten to good home", mia_search_kitten031510.posts[14].label
241
- assert_equal [0, 0, 0, 19, 2, 2010, 5, 50, false, "EST"], mia_search_kitten031510.posts[14].post_date.to_a
241
+ assert_equal [19, 2], mia_search_kitten031510.posts[14].post_date.to_a[3..4]
242
242
  assert_equal "BEAUTIFUL KITTENS", mia_search_kitten031510.posts[15].label
243
- assert_equal [0, 0, 0, 19, 2, 2010, 5, 50, false, "EST"], mia_search_kitten031510.posts[15].post_date.to_a
243
+ assert_equal [19, 2], mia_search_kitten031510.posts[15].post_date.to_a[3..4]
244
244
  assert_equal "MANY new adorable kittens for good homes!!!", mia_search_kitten031510.posts[16].label
245
- assert_equal [0, 0, 0, 18, 2, 2010, 4, 49, false, "EST"], mia_search_kitten031510.posts[16].post_date.to_a
245
+ assert_equal [18, 2], mia_search_kitten031510.posts[16].post_date.to_a[3..4]
246
246
  assert_equal "Kitten living in a parking lot needs your help", mia_search_kitten031510.posts[17].label
247
- assert_equal [0, 0, 0, 16, 2, 2010, 2, 47, false, "EST"], mia_search_kitten031510.posts[17].post_date.to_a
247
+ assert_equal [16, 2], mia_search_kitten031510.posts[17].post_date.to_a[3..4]
248
248
  assert_equal "BEAUTIFUL 8 WEEK KITTENS", mia_search_kitten031510.posts[18].label
249
- assert_equal [0, 0, 0, 16, 2, 2010, 2, 47, false, "EST"], mia_search_kitten031510.posts[18].post_date.to_a
249
+ assert_equal [16, 2], mia_search_kitten031510.posts[18].post_date.to_a[3..4]
250
250
  assert_equal "ORANGE TABBY KITTEN", mia_search_kitten031510.posts[19].label
251
- assert_equal [0, 0, 0, 13, 2, 2010, 6, 44, false, "EST"], mia_search_kitten031510.posts[19].post_date.to_a
251
+ assert_equal [13, 2], mia_search_kitten031510.posts[19].post_date.to_a[3..4]
252
252
  assert_equal "Lots of kittens to choose from! Pics!!", mia_search_kitten031510.posts[20].label
253
- assert_equal [0, 0, 0, 13, 2, 2010, 6, 44, false, "EST"], mia_search_kitten031510.posts[20].post_date.to_a
253
+ assert_equal [13, 2], mia_search_kitten031510.posts[20].post_date.to_a[3..4]
254
254
 
255
255
  end
256
256
 
@@ -37,7 +37,9 @@ EOD
37
37
  <p><a href="/mdc/jwl/1128691192.html">925 Sterling Silver Dragonfly Charm Bracelet - $25 -</a> <span class="p"> img</span></p>
38
38
  EOD
39
39
 
40
- one = CraigScrape::Posting.new CraigScrape::Listings.parse_summary(Hpricot.parse(search_html_one).at('p'))
40
+ one = CraigScrape::Posting.new CraigScrape::Listings.parse_summary(
41
+ Nokogiri::HTML(search_html_one, nil, CraigScrape::Scraper::HTML_ENCODING).at('p')
42
+ )
41
43
  assert_equal true, one.has_img?
42
44
  assert_equal false, one.has_pic?
43
45
  assert_equal true, one.has_pic_or_img?
@@ -49,7 +51,9 @@ EOD
49
51
  assert_equal 18, one.post_date.day
50
52
  assert_equal nil, one.price
51
53
 
52
- two = CraigScrape::Posting.new CraigScrape::Listings.parse_summary(Hpricot.parse(search_html_two).at('p'))
54
+ two = CraigScrape::Posting.new CraigScrape::Listings.parse_summary(
55
+ Nokogiri::HTML(search_html_two, nil, CraigScrape::Scraper::HTML_ENCODING).at('p')
56
+ )
53
57
  assert_equal true, two.has_img?
54
58
  assert_equal true, two.has_pic?
55
59
  assert_equal true, two.has_pic_or_img?
@@ -61,7 +65,9 @@ EOD
61
65
  assert_equal 4, two.post_date.day
62
66
  assert_equal 348000.0, two.price
63
67
 
64
- three = CraigScrape::Posting.new CraigScrape::Listings.parse_summary(Hpricot.parse(search_html_three).at('p'))
68
+ three = CraigScrape::Posting.new CraigScrape::Listings.parse_summary(
69
+ Nokogiri::HTML(search_html_three, nil, CraigScrape::Scraper::HTML_ENCODING).at('p')
70
+ )
65
71
  assert_equal false, three.has_img?
66
72
  assert_equal true, three.has_pic?
67
73
  assert_equal true, three.has_pic_or_img?
@@ -73,7 +79,9 @@ EOD
73
79
  assert_equal 31, three.post_date.day
74
80
  assert_equal 22.0, three.price
75
81
 
76
- four = CraigScrape::Posting.new CraigScrape::Listings.parse_summary(Hpricot.parse(search_html_four).at('p'))
82
+ four = CraigScrape::Posting.new CraigScrape::Listings.parse_summary(
83
+ Nokogiri::HTML(search_html_four, nil, CraigScrape::Scraper::HTML_ENCODING).at('p')
84
+ )
77
85
  assert_equal false, four.has_img?
78
86
  assert_equal false, four.has_pic?
79
87
  assert_equal false, four.has_pic_or_img?
@@ -85,7 +93,9 @@ EOD
85
93
  assert_equal 22, four.post_date.day
86
94
  assert_equal 325000.0, four.price
87
95
 
88
- five = CraigScrape::Posting.new CraigScrape::Listings.parse_summary(Hpricot.parse(search_html_five).at('p'))
96
+ five = CraigScrape::Posting.new CraigScrape::Listings.parse_summary(
97
+ Nokogiri::HTML(search_html_five, nil, CraigScrape::Scraper::HTML_ENCODING).at('p')
98
+ )
89
99
  assert_equal false, five.has_img?
90
100
  assert_equal true, five.has_pic?
91
101
  assert_equal true, five.has_pic_or_img?
@@ -97,27 +107,31 @@ EOD
97
107
  assert_equal 9, five.post_date.day
98
108
  assert_equal 105000.0, five.price
99
109
 
100
- five = CraigScrape::Posting.new CraigScrape::Listings.parse_summary(Hpricot.parse(category_listing_one).at('p'))
101
- assert_equal false, five.has_img?
102
- assert_equal true, five.has_pic?
103
- assert_equal true, five.has_pic_or_img?
104
- assert_equal '/pbc/reb/1128661387.html', five.href
105
- assert_equal "$2995000 / 5br - Downtown Boca New Home To Be Built", five.label
106
- assert_equal "real estate - by broker", five.section
107
- assert_equal "Boca Raton", five.location
108
- assert_equal nil, five.post_date
109
- assert_equal 2995000.0, five.price
110
-
111
- six = CraigScrape::Posting.new CraigScrape::Listings.parse_summary(Hpricot.parse(category_listing_two).at('p'))
112
- assert_equal true, six.has_img?
113
- assert_equal false, six.has_pic?
110
+ six = CraigScrape::Posting.new CraigScrape::Listings.parse_summary(
111
+ Nokogiri::HTML(category_listing_one, nil, CraigScrape::Scraper::HTML_ENCODING).at('p')
112
+ )
113
+ assert_equal false, six.has_img?
114
+ assert_equal true, six.has_pic?
114
115
  assert_equal true, six.has_pic_or_img?
115
- assert_equal '/mdc/jwl/1128691192.html', six.href
116
- assert_equal "925 Sterling Silver Dragonfly Charm Bracelet - $25", six.label
117
- assert_equal nil, six.section
118
- assert_equal nil, six.location
116
+ assert_equal '/pbc/reb/1128661387.html', six.href
117
+ assert_equal "$2995000 / 5br - Downtown Boca New Home To Be Built", six.label
118
+ assert_equal "real estate - by broker", six.section
119
+ assert_equal "Boca Raton", six.location
119
120
  assert_equal nil, six.post_date
120
- assert_equal 25.0, six.price
121
+ assert_equal 2995000.0, six.price
122
+
123
+ seven = CraigScrape::Posting.new CraigScrape::Listings.parse_summary(
124
+ Nokogiri::HTML(category_listing_two, nil, CraigScrape::Scraper::HTML_ENCODING).at('p')
125
+ )
126
+ assert_equal true, seven.has_img?
127
+ assert_equal false, seven.has_pic?
128
+ assert_equal true, seven.has_pic_or_img?
129
+ assert_equal '/mdc/jwl/1128691192.html', seven.href
130
+ assert_equal "925 Sterling Silver Dragonfly Charm Bracelet - $25", seven.label
131
+ assert_equal nil, seven.section
132
+ assert_equal nil, seven.location
133
+ assert_equal nil, seven.post_date
134
+ assert_equal 25.0, seven.price
121
135
  end
122
136
 
123
137
 
@@ -302,7 +316,7 @@ EOD
302
316
  assert_equal [:pic], sfbay_art_1223614914.img_types
303
317
  end
304
318
 
305
- # This is actually a 'bug' with hpricot itself when the ulimit is set too low.
319
+ # This was actually a 'bug' with hpricot itself when the ulimit is set too low.
306
320
  # the Easy fix is running "ulimit -s 16384" before the tests. But the better fix was
307
321
  # to remove the userbody sending these pages to be parsed by Hpricot
308
322
  def test_bugs_found061710
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: libcraigscrape
3
3
  version: !ruby/object:Gem::Version
4
- hash: 57
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
- - 0
8
- - 9
9
7
  - 1
10
- version: 0.9.1
8
+ - 0
9
+ version: "1.0"
11
10
  platform: ruby
12
11
  authors:
13
12
  - Chris DeRose, DeRose Technologies, Inc.
@@ -15,22 +14,23 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2011-01-05 00:00:00 -05:00
17
+ date: 2011-02-13 00:00:00 -05:00
19
18
  default_executable:
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
22
- name: hpricot
21
+ name: nokogiri
23
22
  prerelease: false
24
23
  requirement: &id001 !ruby/object:Gem::Requirement
25
24
  none: false
26
25
  requirements:
27
- - - ">"
26
+ - - ">="
28
27
  - !ruby/object:Gem::Version
29
- hash: 27
28
+ hash: 15
30
29
  segments:
31
- - 0
32
- - 8
33
- version: "0.8"
30
+ - 1
31
+ - 4
32
+ - 4
33
+ version: 1.4.4
34
34
  type: :runtime
35
35
  version_requirements: *id001
36
36
  - !ruby/object:Gem::Dependency