quixoten-craigler 1.0.0 → 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/LICENSE CHANGED
@@ -1,3 +1,5 @@
1
+ (MIT License)
2
+
1
3
  Copyright (c) 2009 Devin Christensen
2
4
 
3
5
  Permission is hereby granted, free of charge, to any person obtaining
@@ -1,21 +1,40 @@
1
- = craigler
1
+ = Craigler
2
2
 
3
3
  Search API for craigslist
4
4
 
5
5
  == SYNOPSIS:
6
+ I couldn't decide which API I liked best, so you have two choices.
6
7
 
7
- Craigler.search(:motorcycles, :in => [:utah, :nevada, :arizona], :for => 'Boulevard M50) do |item|
8
+ Craigler.search(:motorcycles, :in => [:utah, :nevada, :arizona], :for => 'Boulevard M50') do |item|
8
9
  puts item.title
9
10
  puts item.url
10
11
  puts item.time
11
12
  end
13
+
14
+ or
12
15
 
13
- Craigler.find('Boulevard M50', :in => [:utah, :nevada, :arizona], :only => :motorcycles) do |item|
16
+ Craigler.find('Boulevard M50', :in => :california, :only => :motorcycles) do |item|
14
17
  puts item.title
15
18
  puts item.url
16
19
  puts item.time
17
20
  end
18
21
 
22
+ You can also create a search object to fetch the results later. When no location or category is given Craigler searches <tt>:all_for_sale_or_wanted</tt> in <tt>:anywhere</tt>
23
+
24
+ search = Craigler::Search.new('Yamaha')
25
+ search.results(:page_limit => 1)
26
+
27
+ Note that additional calls to <tt>search.results()</tt> will always return the same result set unless refresh is forced
28
+ search.results(:refresh => true)
29
+
30
+ === Supported Categories
31
+ <tt>:all_for_sale_or_wanted</tt>, <tt>:art_and_crafts</tt>, <tt>:auto_parts</tt>, <tt>:baby_and_kid_stuff</tt>, <tt>:barter</tt>, <tt>:bicycles</tt>, <tt>:boats</tt>, <tt>:books</tt>, <tt>:business</tt>, <tt>:cars_and_trucks</tt>, <tt>:clothing</tt>, <tt>:collectibles</tt>, <tt>:community</tt>, <tt>:computers_and_tech</tt>, <tt>:electronics</tt>, <tt>:event</tt>, <tt>:farm_and_garden</tt>, <tt>:free_stuff</tt>, <tt>:furniture</tt>, <tt>:games_and_toys</tt>, <tt>:garage_sales</tt>, <tt>:general</tt>, <tt>:gigs</tt>, <tt>:household</tt>, <tt>:housing</tt>, <tt>:items_wanted</tt>, <tt>:jewelry</tt>, <tt>:jobs</tt>, <tt>:materials</tt>, <tt>:media</tt>, <tt>:motorcycles</tt>, <tt>:musical_instruments</tt>, <tt>:personals</tt>, <tt>:photo_and_video</tt>, <tt>:recreational_vehicles</tt>, <tt>:resume</tt>, <tt>:services_offered</tt>, <tt>:sporting_goods</tt>, <tt>:tickets</tt>, <tt>:tools</tt>
32
+
33
+ === Supported Locations
34
+ <tt>:alaska</tt>, <tt>:arizona</tt>, <tt>:arkansas</tt>, <tt>:california</tt>, <tt>:colorado</tt>, <tt>:connecticut</tt>, <tt>:delaware</tt>, <tt>:dc</tt>, <tt>:florida</tt>, <tt>:georgia</tt>, <tt>:hawaii</tt>, <tt>:idaho</tt>, <tt>:illinois</tt>, <tt>:indiana</tt>, <tt>:iowa</tt>, <tt>:kansas</tt>, <tt>:kentucky</tt>, <tt>:louisiana</tt>, <tt>:maine</tt>, <tt>:maryland</tt>, <tt>:mass</tt>, <tt>:michigan</tt>, <tt>:minnesota</tt>, <tt>:mississippi</tt>, <tt>:missouri</tt>, <tt>:montana</tt>, <tt>:nebraska</tt>, <tt>:nevada</tt>, <tt>:n_hampshire</tt>, <tt>:new_jersey</tt>, <tt>:new_mexico</tt>, <tt>:new_york</tt>, <tt>:n_carolina</tt>, <tt>:north_dakota</tt>, <tt>:ohio</tt>, <tt>:oklahoma</tt>, <tt>:oregon</tt>, <tt>:pennsylvania</tt>, <tt>:rhode_island</tt>, <tt>:s_carolina</tt>, <tt>:south_dakota</tt>, <tt>:tennessee</tt>, <tt>:texas</tt>, <tt>:utah</tt>, <tt>:vermont</tt>, <tt>:virginia</tt>, <tt>:washington</tt>, <tt>:west_virginia</tt> <tt>:wisconsin</tt>, <tt>:wyoming</tt>
35
+
36
+ Or use <tt>:anywhere</tt> to search all supported locations.
37
+
19
38
  == REQUIREMENTS:
20
39
 
21
40
  * Hpricot
data/Rakefile CHANGED
@@ -49,8 +49,8 @@ Rake::RDocTask.new do |rdoc|
49
49
  end
50
50
 
51
51
  rdoc.rdoc_dir = 'rdoc'
52
- rdoc.title = "craigler #{version}"
53
- rdoc.rdoc_files.include('README*')
52
+ rdoc.title = "Craigler #{version}"
53
+ rdoc.rdoc_files.include('README.rdoc', 'LICENSE')
54
54
  rdoc.rdoc_files.include('lib/**/*.rb')
55
55
  end
56
56
 
@@ -1,4 +1,4 @@
1
- ---
1
+ ---
2
2
  :major: 1
3
- :minor: 0
3
+ :minor: 1
4
4
  :patch: 0
@@ -1,12 +1,15 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
1
4
  # -*- encoding: utf-8 -*-
2
5
 
3
6
  Gem::Specification.new do |s|
4
7
  s.name = %q{craigler}
5
- s.version = "1.0.0"
8
+ s.version = "1.1.0"
6
9
 
7
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
11
  s.authors = ["Devin Christensen"]
9
- s.date = %q{2009-07-06}
12
+ s.date = %q{2009-08-11}
10
13
  s.email = %q{quixoten@gmail.com}
11
14
  s.extra_rdoc_files = [
12
15
  "LICENSE",
@@ -30,7 +33,7 @@ Gem::Specification.new do |s|
30
33
  s.homepage = %q{http://github.com/quixoten/craigler}
31
34
  s.rdoc_options = ["--charset=UTF-8"]
32
35
  s.require_paths = ["lib"]
33
- s.rubygems_version = %q{1.3.4}
36
+ s.rubygems_version = %q{1.3.5}
34
37
  s.summary = %q{Search API for craigslist}
35
38
  s.test_files = [
36
39
  "test/craigler_search_test.rb",
@@ -5,20 +5,26 @@ require 'craigler/constants'
5
5
  require 'craigler/search'
6
6
 
7
7
  module Craigler
8
- VERSION = '0.1.0'
9
-
10
8
  class CraiglerError < StandardError; end
11
9
  class InvalidCategory < CraiglerError; end
12
10
  class InvalidSearchTerm < CraiglerError; end
13
11
  class InvalidLocation < CraiglerError; end
14
12
 
15
13
  class << self
14
+ # Interface to Search that may or may not be more readable
16
15
  def search(category, options = {})
17
- search = Search.new(options[:for], :in => (options[:in] || :anywhere), :only => category)
16
+ results = Search.new(options[:for], :in => (options[:in] || :anywhere), :only => category).results()
17
+ results.each {|result| yield(result) } if block_given?
18
+ results
18
19
  end
19
20
 
21
+ # Interface to Search that somewhat mimics ActiveRecord#find
22
+ #
23
+ # Supports all the options of Search#new
20
24
  def find(search_term, options = {})
21
- search = Search.new(search_term, options)
25
+ results = Search.new(search_term, options).results()
26
+ results.each {|result| yield(result) } if block_given?
27
+ results
22
28
  end
23
29
  end
24
30
  end
@@ -1,4 +1,6 @@
1
1
  module Craigler
2
+ RESULTS_PER_PAGE = 25 # :nodoc:
3
+
2
4
  LOCATIONS = {
3
5
  :alabama => ['http://auburn.craigslist.org/','http://bham.craigslist.org/','http://columbusga.craigslist.org/','http://dothan.craigslist.org/','http://shoals.craigslist.org/','http://gadsden.craigslist.org/','http://huntsville.craigslist.org/','http://mobile.craigslist.org/','http://montgomery.craigslist.org/','http://tuscaloosa.craigslist.org/'],
4
6
  :alaska => ['http://anchorage.craigslist.org/'],
@@ -51,7 +53,7 @@ module Craigler
51
53
  :west_virginia => ['http://charlestonwv.craigslist.org/','http://huntington.craigslist.org/','http://martinsburg.craigslist.org/','http://morgantown.craigslist.org/','http://parkersburg.craigslist.org/','http://wv.craigslist.org/','http://wheeling.craigslist.org/'],
52
54
  :wisconsin => ['http://appleton.craigslist.org/','http://duluth.craigslist.org/','http://eauclaire.craigslist.org/','http://greenbay.craigslist.org/','http://janesville.craigslist.org/','http://racine.craigslist.org/','http://lacrosse.craigslist.org/','http://madison.craigslist.org/','http://milwaukee.craigslist.org/','http://sheboygan.craigslist.org/','http://wausau.craigslist.org/'],
53
55
  :wyoming => ['http://wyoming.craigslist.org/']
54
- }
56
+ } # :nodoc:
55
57
 
56
58
  CATEGORIES = {
57
59
  :community => 'ccc',
@@ -94,5 +96,5 @@ module Craigler
94
96
  :personals => 'ppp',
95
97
  :resume => 'res',
96
98
  :services_offered => 'bbb'
97
- }
99
+ } # :nodoc:
98
100
  end
@@ -6,6 +6,13 @@ module Craigler
6
6
 
7
7
  attr_reader :search_term, :categories, :locations
8
8
 
9
+ # Creates a wrapper object for a craigslist search
10
+ #
11
+ # === Options
12
+ # [:in]
13
+ # Specifies the location(s) to search in. Defaults to <tt>:anywhere</tt>.
14
+ # [:only]
15
+ # Specifies the category or categories to search in. Defaults to <tt>:all_for_sale_or_wanted</tt>
9
16
  def initialize(search_term, options = {})
10
17
  raise InvalidSearchTerm if search_term.nil? || search_term == ''
11
18
 
@@ -14,15 +21,30 @@ module Craigler
14
21
  _parse_options(options)
15
22
  end
16
23
 
17
- def results(refresh = false)
18
- return @results unless @results.nil? || refresh
24
+ # Returns the results of the search. If this is the first time
25
+ # calling #results then they will be fetched over the internet and cached in the search object.
26
+ #
27
+ # === Options
28
+ # [:page_limit]
29
+ # Maximum number of pages to fetch results from. Defaults to <tt>4</tt>.
30
+ # <b>Note:</b> A location may, and often does, have more than one searchable
31
+ # url assciated with it, e.g., {California}[http://geo.craigslist.org/iso/us/ca]. Because
32
+ # <tt>:page_limit</tt> is applied seperately to each url within the location, searching <tt>:in => :california</tt>
33
+ # with a <tt>:page_limit => 4</tt> could potentially make up to 100 page requests.</em>
34
+ # [:refresh]
35
+ # Set to <tt>true</tt> to force an update across the internet.
36
+ def results(options = {})
37
+ options = { :page_limit => 4, :refresh => false }.merge(options)
38
+ return @results unless @results.nil? || options[:refresh]
39
+
40
+ @results = []
41
+ last_page = options[:page_limit] - 1 # pages start at 0
19
42
 
20
- @results = []
21
43
  _for_each_locations_search_url() do |location, url|
22
- (0..19).each do |page|
23
- items = _extract_items_from_url(location, "#{url}&s=#{page*25}")
24
- @results.push(*items)
25
- break unless items.size == 25
44
+ (0..last_page).each do |page|
45
+ results = _extract_items_from_url(location, "#{url}&s=#{page*25}")
46
+ @results.push(*results)
47
+ break if results.size < RESULTS_PER_PAGE
26
48
  end
27
49
  end
28
50
 
@@ -30,6 +30,12 @@ class CraiglerSearchTest < Test::Unit::TestCase
30
30
  end
31
31
  end
32
32
 
33
+ should "use a default category of :all_for_sale_or_wanted" do
34
+ search = Craigler::Search.new('Buell', :in => :utah)
35
+ assert(search.categories == [:all_for_sale_or_wanted],
36
+ "category was [:#{search.categories.join(", :")}] but should have been [:all_for_sale_or_wanted]")
37
+ end
38
+
33
39
  should "require that the location is valid" do
34
40
  assert_raises(Craigler::InvalidLocation) do
35
41
  Craigler::Search.new('Honda Nighthawk', :in => :invalid)
@@ -45,7 +51,7 @@ class CraiglerSearchTest < Test::Unit::TestCase
45
51
 
46
52
  context "fetching search results" do
47
53
  setup do
48
- @search = Craigler::Search.new('Honda Shadow', :in => :utah, :only => :motorcycles)
54
+ @search = Craigler::Search.new('Honda', :in => :utah, :only => :motorcycles, :page_limit => 1)
49
55
  end
50
56
 
51
57
  should "return an array of hashes" do
@@ -54,5 +60,11 @@ class CraiglerSearchTest < Test::Unit::TestCase
54
60
  assert(results.size > 0, "No results were returned")
55
61
  assert(results.inject(true) {|t,r| t && r.is_a?(Hash)})
56
62
  end
63
+
64
+ should "allow us to limit the number of pages searched" do
65
+ one_page_count = @search.results(:page_limit => 1).size
66
+ two_page_count = @search.results(:page_limit => 2, :refresh => true).size
67
+ assert(one_page_count < two_page_count, "#{one_page_count} is not less than #{two_page_count}")
68
+ end
57
69
  end
58
70
  end
@@ -1,11 +1,33 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class CraiglerTest < Test::Unit::TestCase
4
- should "respond to search" do
5
- assert_respond_to(Craigler, :search)
4
+ context "Search interface" do
5
+ should "respond to search" do
6
+ assert_respond_to(Craigler, :search)
7
+ end
8
+
9
+ should "yield results to a block if given" do
10
+ yielded = false
11
+ Craigler::search(:motorcycles, :in => :utah, :for => 'Yamaha', :page_limit => 1) do
12
+ yielded = true; break;
13
+ end
14
+
15
+ assert(yielded, "results were never yielded to the block")
16
+ end
6
17
  end
7
18
 
8
- should "respond to find" do
9
- assert_respond_to(Craigler, :find)
19
+ context "Find interface" do
20
+ should "respond to find" do
21
+ assert_respond_to(Craigler, :find)
22
+ end
23
+
24
+ should "yield results to a block if given" do
25
+ yielded = false
26
+ Craigler::find('Yamaha', :in => :utah, :only => :motorcycles, :page_limit => 1) do
27
+ yielded = true; break;
28
+ end
29
+
30
+ assert(yielded, "results were never yielded to the block")
31
+ end
10
32
  end
11
33
  end
@@ -1,10 +1,17 @@
1
1
  require 'rubygems'
2
2
  require 'test/unit'
3
3
  require 'shoulda'
4
+ require 'ruby-debug'
4
5
 
5
6
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
7
  $LOAD_PATH.unshift(File.dirname(__FILE__))
8
+
7
9
  require 'craigler'
8
10
 
9
- class Test::Unit::TestCase
11
+ module Craigler
12
+ class Search
13
+ def open(*args)
14
+ @@results_page ||= Kernel::open("http://saltlakecity.craigslist.org/search/sss?query=Honda&format=rss&s=0").read()
15
+ end
16
+ end
10
17
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quixoten-craigler
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Devin Christensen
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-07-06 00:00:00 -07:00
12
+ date: 2009-08-11 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -38,6 +38,7 @@ files:
38
38
  - test/test_helper.rb
39
39
  has_rdoc: false
40
40
  homepage: http://github.com/quixoten/craigler
41
+ licenses:
41
42
  post_install_message:
42
43
  rdoc_options:
43
44
  - --charset=UTF-8
@@ -58,7 +59,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
58
59
  requirements: []
59
60
 
60
61
  rubyforge_project:
61
- rubygems_version: 1.2.0
62
+ rubygems_version: 1.3.5
62
63
  signing_key:
63
64
  specification_version: 3
64
65
  summary: Search API for craigslist