classifieds_api 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +6 -1
- data/README.rdoc +9 -0
- data/Rakefile +8 -0
- data/classifieds_api.gemspec +3 -2
- data/lib/classifieds_api.rb +4 -2
- data/lib/classifieds_api/ad.rb +115 -0
- data/lib/classifieds_api/kijiji.rb +322 -0
- data/lib/classifieds_api/search_filters.rb +134 -0
- data/lib/classifieds_api/version.rb +1 -1
- metadata +23 -5
- data/README.md +0 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 714c4645bfb468ff19cb1512775b8933d91c3fb6
|
4
|
+
data.tar.gz: b3d558c70659613ce0cfad26b8dcb63296c16bd6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b4c19a643adf817a6dd7fb9a8333772cb466d7f88c8cbe31ee7e2e2ed39411565c6c4a38ee8d80b5078976377ef691a9879e1d25ff41430e84f550e5bb069bf
|
7
|
+
data.tar.gz: 8e5623695ce780cf04cd8f47ffb34645a3d0d130784f7fc778a96a177e50066d6ec1563ec82a1e84b4f0cce5eec2f6473d408ce7baf758605881331249aff0ab
|
data/.gitignore
CHANGED
data/README.rdoc
ADDED
data/Rakefile
CHANGED
data/classifieds_api.gemspec
CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Jean-Francois Bisson"]
|
10
10
|
spec.email = ["jbisson@uwaterloo.ca"]
|
11
11
|
|
12
|
-
spec.summary = %q{
|
13
|
-
spec.description = %q{Simple
|
12
|
+
spec.summary = %q{Simple API for popular classifieds website}
|
13
|
+
spec.description = %q{Simple API for popular classifieds website. Currently, the gem allows you to do search items on Kijiji in Ontario.}
|
14
14
|
spec.homepage = "https://github.com/jfbisson4/classifieds_api"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
|
25
25
|
spec.add_development_dependency "bundler", "~> 1.8"
|
26
26
|
spec.add_development_dependency "rake", "~> 10.0"
|
27
|
+
spec.add_development_dependency "test-unit", "~> 3.0"
|
27
28
|
|
28
29
|
spec.add_runtime_dependency "nokogiri", "~> 1.6"
|
29
30
|
end
|
data/lib/classifieds_api.rb
CHANGED
@@ -0,0 +1,115 @@
|
|
1
|
+
module ClassifiedsApi
|
2
|
+
class Ad
|
3
|
+
attr_reader :title, :date_listed, :price, :address, :description,
|
4
|
+
:image_urls, :offering, :featured, :url
|
5
|
+
|
6
|
+
def initialize(title, date_listed, price, address, description, image_urls,
|
7
|
+
offering, featured, url)
|
8
|
+
self.title = title
|
9
|
+
self.date_listed = date_listed
|
10
|
+
self.price = price
|
11
|
+
self.address = address
|
12
|
+
self.description = description
|
13
|
+
self.image_urls = image_urls
|
14
|
+
self.offering = offering
|
15
|
+
self.featured = featured
|
16
|
+
self.url = url
|
17
|
+
end
|
18
|
+
|
19
|
+
def title=(title)
|
20
|
+
unless title.is_a? String
|
21
|
+
raise TypeError, "Argument #{title} is not a string."
|
22
|
+
end
|
23
|
+
@title = title
|
24
|
+
end
|
25
|
+
|
26
|
+
def date_listed=(date_listed)
|
27
|
+
if date_listed.nil? == false
|
28
|
+
unless date_listed.is_a? DateTime
|
29
|
+
raise TypeError, "Argument #{date_listed} is not a DateTime."
|
30
|
+
end
|
31
|
+
end
|
32
|
+
@date_listed = date_listed
|
33
|
+
end
|
34
|
+
|
35
|
+
def price=(price)
|
36
|
+
if price.nil? == false
|
37
|
+
unless price.is_a? Numeric
|
38
|
+
raise TypeError, "Argument #{price} is not a numeric."
|
39
|
+
end
|
40
|
+
end
|
41
|
+
@price = price
|
42
|
+
end
|
43
|
+
|
44
|
+
def address=(address)
|
45
|
+
if address.nil? == false
|
46
|
+
unless address.is_a? String
|
47
|
+
raise TypeError, "Argument #{address} is not a string."
|
48
|
+
end
|
49
|
+
end
|
50
|
+
@address = address
|
51
|
+
end
|
52
|
+
|
53
|
+
def description=(description)
|
54
|
+
unless description.is_a? String
|
55
|
+
raise TypeError, "Argument #{description} is not a string."
|
56
|
+
end
|
57
|
+
@description = description
|
58
|
+
end
|
59
|
+
|
60
|
+
def image_urls=(image_urls)
|
61
|
+
if image_urls.nil? == false
|
62
|
+
unless image_urls.is_a? Array
|
63
|
+
raise TypeError, "Argument #{image_urls} is not a string."
|
64
|
+
end
|
65
|
+
end
|
66
|
+
@image_urls = image_urls
|
67
|
+
end
|
68
|
+
|
69
|
+
def offering=(offering)
|
70
|
+
unless !!offering == offering
|
71
|
+
raise TypeError "Argument #{offering} is not a string."
|
72
|
+
end
|
73
|
+
@offering = offering
|
74
|
+
end
|
75
|
+
|
76
|
+
def featured=(featured)
|
77
|
+
unless !!featured == featured
|
78
|
+
raise TypeError "Argument #{featured} is not a boolean."
|
79
|
+
end
|
80
|
+
@featured = featured
|
81
|
+
end
|
82
|
+
|
83
|
+
def url=(url)
|
84
|
+
unless url.is_a? String
|
85
|
+
raise TypeError, "Argument #{address} is no a string."
|
86
|
+
end
|
87
|
+
@url = url
|
88
|
+
end
|
89
|
+
|
90
|
+
def print_ad
|
91
|
+
puts '-------------------------------------------------------------------'
|
92
|
+
if @offering == true
|
93
|
+
print "Offering - "
|
94
|
+
else
|
95
|
+
print "Wanted - "
|
96
|
+
end
|
97
|
+
if @featured_ad == true
|
98
|
+
puts "Featured Ad"
|
99
|
+
else
|
100
|
+
puts "Regular Ad"
|
101
|
+
end
|
102
|
+
if (@date_listed.nil? == false)
|
103
|
+
puts "Date Listed: #{@date_listed.strftime("%d/%m/%Y %H:%M")}"
|
104
|
+
else
|
105
|
+
puts "Date Listed:"
|
106
|
+
end
|
107
|
+
puts "Title: #{@title}"
|
108
|
+
puts "Price: #{@price}"
|
109
|
+
puts "Description: #{@description}"
|
110
|
+
puts "Address: #{@address}"
|
111
|
+
puts "URL: #{@url}"
|
112
|
+
puts '-------------------------------------------------------------------'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,322 @@
|
|
1
|
+
module ClassifiedsApi
|
2
|
+
class Kijiji
|
3
|
+
|
4
|
+
def search(keyword, search_limit, search_filters)
|
5
|
+
|
6
|
+
unless keyword.is_a? String
|
7
|
+
raise TypeError, "Argument #{keyword} is not a string."
|
8
|
+
end
|
9
|
+
unless search_limit.is_a? Numeric
|
10
|
+
raise TypeError, "Argument #{search_limit} is not a numeric."
|
11
|
+
end
|
12
|
+
if search_limit <= 0
|
13
|
+
raise RangeError, "Argument #{search_limit} should be bigger than 0."
|
14
|
+
end
|
15
|
+
if search_limit > SEARCH_LIMIT
|
16
|
+
raise RangeError,
|
17
|
+
"Argument #{search_limit} should be smaller than the search limit " +
|
18
|
+
"of #{SEARCH_LIMIT}."
|
19
|
+
end
|
20
|
+
unless search_filters.instance_of? SearchFilters
|
21
|
+
raise TypeError,
|
22
|
+
"The search filters argument is not of the SearchFilters class."
|
23
|
+
end
|
24
|
+
|
25
|
+
ads = []
|
26
|
+
page_number = 1
|
27
|
+
until ads.count >= search_limit
|
28
|
+
|
29
|
+
search_page_url = get_search_page_url(keyword,
|
30
|
+
page_number,
|
31
|
+
search_filters)
|
32
|
+
search_page_html = get_search_page_html(search_page_url)
|
33
|
+
noko_search_html = get_nokogiri_html(search_page_html)
|
34
|
+
noko_search_results = get_noko_results_on_search_page(
|
35
|
+
noko_search_html,
|
36
|
+
search_filters.include_sponsored_ads)
|
37
|
+
|
38
|
+
if noko_search_results.count == 0
|
39
|
+
return ads
|
40
|
+
end
|
41
|
+
|
42
|
+
noko_search_results.each do |noko_result|
|
43
|
+
if ads.count >= search_limit
|
44
|
+
return ads
|
45
|
+
end
|
46
|
+
|
47
|
+
ad_title_wanted = get_title (noko_result)
|
48
|
+
ad_title = remove_wanted (ad_title_wanted)
|
49
|
+
ad_price = get_price (noko_result)
|
50
|
+
ad_short_description = get_short_description (noko_result)
|
51
|
+
ad_offering = get_offering (ad_title_wanted)
|
52
|
+
ad_featured = get_ad_featured (noko_result)
|
53
|
+
ad_url = get_ad_url (noko_result)
|
54
|
+
|
55
|
+
|
56
|
+
new_ad = Ad.new(ad_title,
|
57
|
+
nil,
|
58
|
+
ad_price,
|
59
|
+
nil,
|
60
|
+
ad_short_description,
|
61
|
+
nil,
|
62
|
+
ad_offering,
|
63
|
+
ad_featured,
|
64
|
+
ad_url)
|
65
|
+
|
66
|
+
ads.push(new_ad)
|
67
|
+
end
|
68
|
+
page_number += 1
|
69
|
+
end
|
70
|
+
return ads
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def get_search_page_url(keyword, page_number, search_filters)
|
76
|
+
category_key = get_category_key (search_filters.category)
|
77
|
+
category_id = get_category_id (search_filters.category)
|
78
|
+
page_key = get_page_key (page_number)
|
79
|
+
location_string_key = get_location_string_key (search_filters.location)
|
80
|
+
location_id = get_location_id (search_filters.location)
|
81
|
+
distance_key = get_distance_key (search_filters.distance)
|
82
|
+
offer_type_key = get_offer_type_key (search_filters.offer_type)
|
83
|
+
price_key = get_price_key(search_filters.price_minimum,
|
84
|
+
search_filters.price_maximum,
|
85
|
+
offer_type_key)
|
86
|
+
|
87
|
+
|
88
|
+
url = ""
|
89
|
+
url += "http://www.kijiji.ca/b-"
|
90
|
+
url += "#{category_key}"
|
91
|
+
url += "#{location_string_key}/"
|
92
|
+
url += "#{keyword}/"
|
93
|
+
url += "#{page_key}"
|
94
|
+
url += "#{category_id}"
|
95
|
+
url += "#{location_id}"
|
96
|
+
url += "#{distance_key}"
|
97
|
+
url += "#{offer_type_key}"
|
98
|
+
url += "#{price_key}"
|
99
|
+
return url
|
100
|
+
end
|
101
|
+
|
102
|
+
def get_search_page_html(page_url)
|
103
|
+
html = ""
|
104
|
+
open(page_url) {|io| html = io.read}
|
105
|
+
end
|
106
|
+
|
107
|
+
def get_nokogiri_html(html)
|
108
|
+
noko_html = Nokogiri::HTML(html)
|
109
|
+
end
|
110
|
+
|
111
|
+
def get_noko_results_on_search_page(noko_html, include_sponsored_ads)
|
112
|
+
noko_regular_ads = noko_html.css('table.regular-ad')
|
113
|
+
if include_sponsored_ads == true
|
114
|
+
noko_featured_ads = noko_html.css('table.top-feature')
|
115
|
+
noko_ads = noko_featured_ads + noko_regular_ads
|
116
|
+
else
|
117
|
+
noko_ads = noko_regular_ads
|
118
|
+
end
|
119
|
+
return noko_ads
|
120
|
+
end
|
121
|
+
|
122
|
+
def get_title(noko_result)
|
123
|
+
title = noko_result.css('td.description').css('a.title').text.strip
|
124
|
+
end
|
125
|
+
|
126
|
+
def remove_wanted(title_wanted)
|
127
|
+
title = title_wanted.gsub('Wanted:', '').strip
|
128
|
+
end
|
129
|
+
|
130
|
+
def get_price(noko_result)
|
131
|
+
string_price =
|
132
|
+
noko_result.css('td.price').text.strip.gsub('$', '').gsub(',', '')
|
133
|
+
price = string_price.to_f
|
134
|
+
if string_price == "Free"
|
135
|
+
return 0
|
136
|
+
elsif string_price == "Please Contact" || string_price == ""
|
137
|
+
return nil
|
138
|
+
else
|
139
|
+
return price
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def get_short_description(noko_result)
|
144
|
+
description = noko_result.css('td.description').css('p').text.strip
|
145
|
+
end
|
146
|
+
|
147
|
+
def get_offering(title_wanted)
|
148
|
+
if title_wanted.include? 'Wanted:'
|
149
|
+
return false
|
150
|
+
else
|
151
|
+
return true
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def get_ad_featured(noko_result)
|
156
|
+
css_class = get_node_class(noko_result)
|
157
|
+
if (css_class.include? 'regular-ad')
|
158
|
+
return false
|
159
|
+
else
|
160
|
+
return true
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def get_node_class(noko_result)
|
165
|
+
css_class = noko_result['class'].strip
|
166
|
+
end
|
167
|
+
|
168
|
+
def get_ad_url(noko_result)
|
169
|
+
extension = noko_result.css('td.description').css('a.title')[0]["href"]
|
170
|
+
url = "http://www.kijiji.ca" + extension
|
171
|
+
end
|
172
|
+
|
173
|
+
def get_category_key(category)
|
174
|
+
if category == 'all'
|
175
|
+
return ""
|
176
|
+
else
|
177
|
+
return "#{CATEGORY_KEYS[category]}/"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def get_category_id(category)
|
182
|
+
return CATEGORY_IDS[category]
|
183
|
+
end
|
184
|
+
|
185
|
+
def get_page_key(page_number)
|
186
|
+
if (page_number > 1)
|
187
|
+
return "page-#{page_number}/"
|
188
|
+
else
|
189
|
+
return ""
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def get_location_string_key(location)
|
194
|
+
if location == 'Toronto (GTA)'
|
195
|
+
return 'gta-greater-toronto-area'
|
196
|
+
end
|
197
|
+
location_string = location.dup
|
198
|
+
location_string.gsub! '/', ''
|
199
|
+
location_string.gsub! '.', ''
|
200
|
+
location_string.downcase!
|
201
|
+
location_string.split(' ').join('-')
|
202
|
+
end
|
203
|
+
|
204
|
+
def get_location_id(location)
|
205
|
+
location_id = LOCATION_IDS[location]
|
206
|
+
end
|
207
|
+
|
208
|
+
def get_distance_key(distance)
|
209
|
+
if distance != 50
|
210
|
+
return "r#{distance}"
|
211
|
+
else
|
212
|
+
return ""
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def get_offer_type_key(offer_type)
|
217
|
+
offer_key = ""
|
218
|
+
if offer_type == 'Offering'
|
219
|
+
offer_key = "?ad=offering"
|
220
|
+
elsif offer_type == 'Wanted'
|
221
|
+
offer_key = '?ad=wanted'
|
222
|
+
end
|
223
|
+
return offer_key
|
224
|
+
end
|
225
|
+
|
226
|
+
def get_price_key(min_price, max_price, previous_param)
|
227
|
+
price_key = ""
|
228
|
+
if min_price != 0 || max_price != 10000000
|
229
|
+
if previous_param != ""
|
230
|
+
price_key += "&price="
|
231
|
+
else
|
232
|
+
price_key += "?price="
|
233
|
+
end
|
234
|
+
if min_price != 0
|
235
|
+
price_key += "#{min_price}"
|
236
|
+
end
|
237
|
+
price_key += "__"
|
238
|
+
if max_price != 10000000
|
239
|
+
price_key += "#{max_price}"
|
240
|
+
end
|
241
|
+
end
|
242
|
+
return price_key
|
243
|
+
end
|
244
|
+
|
245
|
+
SEARCH_LIMIT = 10
|
246
|
+
|
247
|
+
CATEGORY_KEYS = Hash[ 'buy and sell', 'buy-sell',
|
248
|
+
'services', 'services',
|
249
|
+
'cars & vehicles', 'cars-vehicles',
|
250
|
+
'pets', 'pets',
|
251
|
+
'vacation rentals', 'vacation-rentals',
|
252
|
+
'community', 'community',
|
253
|
+
'real estate', 'real-estate',
|
254
|
+
'jobs', 'jobs',
|
255
|
+
'resumes', 'resumes']
|
256
|
+
|
257
|
+
|
258
|
+
CATEGORY_IDS = Hash['all', 'k0l',
|
259
|
+
'buy and sell', 'k0c10l',
|
260
|
+
'services', 'k0c72l',
|
261
|
+
'cars & vehicles', 'k0c27l',
|
262
|
+
'pets', 'k0c112l',
|
263
|
+
'vacation rentals', 'k0c800l',
|
264
|
+
'community', 'k0c1l',
|
265
|
+
'real estate', 'k0c34l',
|
266
|
+
'jobs', 'k0c45l',
|
267
|
+
'resumes', 'k0c218l']
|
268
|
+
|
269
|
+
LOCATION_IDS = Hash['Berrie', '1700006',
|
270
|
+
'Belleville Area', '1700129',
|
271
|
+
'Belleville', '1700130',
|
272
|
+
'Trenton', '1700132',
|
273
|
+
'Brantford', '1700206',
|
274
|
+
'Brockville', '1700247',
|
275
|
+
'Chatham-Kent', '1700239',
|
276
|
+
'Cornwall', '1700133',
|
277
|
+
'Guelph', '1700242',
|
278
|
+
'Hamilton', '80014',
|
279
|
+
'Kapuskasing', '1700237',
|
280
|
+
'Kenora', '1700249',
|
281
|
+
'Kingston Area', '1700181',
|
282
|
+
'Kingston', '1700183',
|
283
|
+
'Napanee', '1700182',
|
284
|
+
'Kitchener Area', '1700209',
|
285
|
+
'Cambridge', '1700210',
|
286
|
+
'Kitchener / Waterloo', '1700212',
|
287
|
+
'Stratford', '1700213',
|
288
|
+
'Leamington', '1700240',
|
289
|
+
'London', '1700214',
|
290
|
+
'Muskoka', '1700078',
|
291
|
+
'Norfolk County', '1700248',
|
292
|
+
'North Bay', '1700243',
|
293
|
+
'Ottawa / Gatineau Area', '1700184',
|
294
|
+
'Gatineau', '1700186',
|
295
|
+
'Ottawa', '1700185',
|
296
|
+
'Owen Sound', '1700187',
|
297
|
+
'Peterborough Area', '1700217',
|
298
|
+
'Kawartha Lakes', '1700219',
|
299
|
+
'Peterborough', '1700218',
|
300
|
+
'Renfrew County Area', '1700074',
|
301
|
+
'Pembroke', '1700075',
|
302
|
+
'Petawawa', '1700076',
|
303
|
+
'Renfrew', '1700077',
|
304
|
+
'Sarnia Area', '1700189',
|
305
|
+
'Grand Bend', '1700190',
|
306
|
+
'Sarnia', '1700191',
|
307
|
+
'Sault Ste. Marie', '1700244',
|
308
|
+
'St. Catharines', '80016',
|
309
|
+
'Sudbury', '1700245',
|
310
|
+
'Thunder Bay', '1700126',
|
311
|
+
'Timmins', '1700238',
|
312
|
+
'Toronto (GTA)', '1700272',
|
313
|
+
'City of Toronto', '1700273',
|
314
|
+
'Markham / York Region', '1700274',
|
315
|
+
'Mississauga / Peel Region', '1700276',
|
316
|
+
'Oakville / Halton Region', '1700277',
|
317
|
+
'Oshawa / Durham Region', '1700275',
|
318
|
+
'Windsor Region', '1700220',
|
319
|
+
'Woodstock', '1700241']
|
320
|
+
|
321
|
+
end
|
322
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module ClassifiedsApi
|
2
|
+
class SearchFilters
|
3
|
+
|
4
|
+
attr_reader :category, :location, :distance, :offer_type, :price_minimum,
|
5
|
+
:price_maximum, :additional_info, :include_sponsored_ads
|
6
|
+
|
7
|
+
def initialize(params={})
|
8
|
+
self.category = params[:category] || 'all'
|
9
|
+
self.location = params[:location]
|
10
|
+
self.distance = params[:distance] || 50
|
11
|
+
self.offer_type = params[:offer_type] || 'all'
|
12
|
+
self.price_minimum = params[:price_minimum] || 0
|
13
|
+
self.price_maximum = params[:price_maximum] || 10000000
|
14
|
+
self.include_sponsored_ads = params[:include_sponsored_ads] || false
|
15
|
+
end
|
16
|
+
|
17
|
+
def category=(category)
|
18
|
+
unless CATEGORIES.include? category
|
19
|
+
#print_list('CATEGORIES', CATEGORIES)
|
20
|
+
raise ArgumentError,
|
21
|
+
"Argument #{category} is not a valid category."
|
22
|
+
end
|
23
|
+
@category = category
|
24
|
+
end
|
25
|
+
|
26
|
+
def location=(location)
|
27
|
+
unless LOCATIONS.include? location
|
28
|
+
#print_list('LOCATIONS', LOCATIONS)
|
29
|
+
raise ArgumentError,
|
30
|
+
"Argument #{location} is not a valid location."
|
31
|
+
end
|
32
|
+
@location = location
|
33
|
+
end
|
34
|
+
|
35
|
+
def distance=(distance)
|
36
|
+
unless distance.is_a? Numeric
|
37
|
+
raise TypeError, "Argument #{distance} is not numeric."
|
38
|
+
end
|
39
|
+
if 0 > distance || distance > 500
|
40
|
+
raise RangeError, "Argument #{distance} should be between 0 and 500."
|
41
|
+
end
|
42
|
+
@distance = distance
|
43
|
+
end
|
44
|
+
|
45
|
+
def offer_type=(offer_type)
|
46
|
+
unless OFFER_TYPES.include? offer_type
|
47
|
+
#print_list('OFFER TYPES', OFFER_TYPES)
|
48
|
+
raise ArgumentError,
|
49
|
+
"Argument #{offer_type} is not a valid offer type."
|
50
|
+
end
|
51
|
+
@offer_type = offer_type
|
52
|
+
end
|
53
|
+
|
54
|
+
def price_minimum=(price_minimum)
|
55
|
+
unless price_minimum.is_a? Numeric
|
56
|
+
raise TypeError, "Argument #{price_minimum} is not numeric."
|
57
|
+
end
|
58
|
+
if 0 > price_minimum
|
59
|
+
raise RangeError,
|
60
|
+
"Argument #{price_minimum} should be between bigger than 0."
|
61
|
+
end
|
62
|
+
if price_minimum > (@price_maximum || 10000000)
|
63
|
+
raise RangeError,
|
64
|
+
"Argument #{price_minimum} should be smaller than maximum price " +
|
65
|
+
"#{@price_maximum}."
|
66
|
+
end
|
67
|
+
@price_minimum = price_minimum
|
68
|
+
end
|
69
|
+
|
70
|
+
def price_maximum=(price_maximum)
|
71
|
+
unless price_maximum.is_a? Numeric
|
72
|
+
raise TypeError, "Argument #{price_maximum} is not numeric."
|
73
|
+
end
|
74
|
+
if price_maximum < (@price_minimum || 0)
|
75
|
+
raise RangeError,
|
76
|
+
"Argument #{price_maximum} should be bigger than minimum price " +
|
77
|
+
"#{@price_minimum}."
|
78
|
+
end
|
79
|
+
@price_maximum = price_maximum
|
80
|
+
end
|
81
|
+
|
82
|
+
def include_sponsored_ads=(include_sponsored_ads)
|
83
|
+
unless !!include_sponsored_ads == include_sponsored_ads
|
84
|
+
raise TypeError, "Argument #{include_sponsored_ads} is not a boolean."
|
85
|
+
end
|
86
|
+
@include_sponsored_ads = include_sponsored_ads
|
87
|
+
end
|
88
|
+
|
89
|
+
def print_filters
|
90
|
+
puts '-------------------------------------------------------------------'
|
91
|
+
puts 'Search Filters:'
|
92
|
+
puts "Category: #{@category}"
|
93
|
+
puts "Location: #{@location}"
|
94
|
+
puts "Distance: #{@distance}"
|
95
|
+
puts "Offer Type: #{@offer_type}"
|
96
|
+
puts "Minimum Price: #{@price_minimum}"
|
97
|
+
puts "Maximum Price: #{@price_maximum}"
|
98
|
+
puts "Include Sponsored Ads: #{@include_sponsored_ads}"
|
99
|
+
puts '-------------------------------------------------------------------'
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def print_list(list_name, list)
|
105
|
+
puts '-------------------------------------------------------------------'
|
106
|
+
puts "#{list_name}:"
|
107
|
+
list.each do |element|
|
108
|
+
puts element
|
109
|
+
end
|
110
|
+
puts '-------------------------------------------------------------------'
|
111
|
+
end
|
112
|
+
|
113
|
+
CATEGORIES = ['all', 'buy and sell', 'services', 'cars & vehicles', 'pets',
|
114
|
+
'vacation rentals', 'community', 'real estate', 'jobs',
|
115
|
+
'resumes']
|
116
|
+
|
117
|
+
LOCATIONS = ['Berrie', 'Belleville Area', 'Belleville', 'Trenton',
|
118
|
+
'Brantford', 'Brockville', 'Chatham-Kent', 'Cornwall',
|
119
|
+
'Guelph', 'Hamilton', 'Kapuskasing', 'Kenora', 'Kingston Area',
|
120
|
+
'Kingston', 'Napanee', 'Kitchener Area', 'Cambridge',
|
121
|
+
'Kitchener / Waterloo', 'Stratford', 'Leamington', 'London',
|
122
|
+
'Muskoka', 'Norfolk County', 'North Bay',
|
123
|
+
'Ottawa / Gatineau Area', 'Gatineau', 'Ottawa', 'Owen Sound',
|
124
|
+
'Peterborough Area', 'Kawartha Lakes', 'Peterborough',
|
125
|
+
'Renfrew County Area', 'Pembroke', 'Petawawa', 'Renfrew',
|
126
|
+
'Sarnia Area', 'Grand Bend', 'Sarnia', 'Sault Ste. Marie',
|
127
|
+
'St. Catharines', 'Sudbury', 'Thunder Bay', 'Timmins',
|
128
|
+
'Toronto (GTA)', 'City of Toronto', 'Markham / York Region',
|
129
|
+
'Mississauga / Peel Region', 'Oakville / Halton Region',
|
130
|
+
'Oshawa / Durham Region', 'Windsor Region', 'Woodstock']
|
131
|
+
|
132
|
+
OFFER_TYPES = ['all', 'Offering', 'Wanted']
|
133
|
+
end
|
134
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: classifieds_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jean-Francois Bisson
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: test-unit
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: nokogiri
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,7 +66,8 @@ dependencies:
|
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '1.6'
|
55
|
-
description: Simple
|
69
|
+
description: Simple API for popular classifieds website. Currently, the gem allows
|
70
|
+
you to do search items on Kijiji in Ontario.
|
56
71
|
email:
|
57
72
|
- jbisson@uwaterloo.ca
|
58
73
|
executables: []
|
@@ -63,12 +78,15 @@ files:
|
|
63
78
|
- ".travis.yml"
|
64
79
|
- Gemfile
|
65
80
|
- LICENSE
|
66
|
-
- README.
|
81
|
+
- README.rdoc
|
67
82
|
- Rakefile
|
68
83
|
- bin/console
|
69
84
|
- bin/setup
|
70
85
|
- classifieds_api.gemspec
|
71
86
|
- lib/classifieds_api.rb
|
87
|
+
- lib/classifieds_api/ad.rb
|
88
|
+
- lib/classifieds_api/kijiji.rb
|
89
|
+
- lib/classifieds_api/search_filters.rb
|
72
90
|
- lib/classifieds_api/version.rb
|
73
91
|
homepage: https://github.com/jfbisson4/classifieds_api
|
74
92
|
licenses: []
|
@@ -93,5 +111,5 @@ rubyforge_project:
|
|
93
111
|
rubygems_version: 2.4.5
|
94
112
|
signing_key:
|
95
113
|
specification_version: 4
|
96
|
-
summary:
|
114
|
+
summary: Simple API for popular classifieds website
|
97
115
|
test_files: []
|
data/README.md
DELETED