RuBing 0.1.1
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/lib/rubing.rb +179 -0
- data/test/test_search.rb +23 -0
- metadata +81 -0
data/lib/rubing.rb
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
# Usage:
|
4
|
+
# RuBing::Search::app_id = 'xxxxxxxxxx'
|
5
|
+
# RuBing::Search::base_site = 'learnhub.com'
|
6
|
+
#
|
7
|
+
# response = RuBing::Search.get('Ruby')
|
8
|
+
# puts response.total_results
|
9
|
+
# response.results.each do |result|
|
10
|
+
# puts "#{result.title} #{result.url}"
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# Author:: Wesley Moxam (wesley.moxam@savvica.com)
|
14
|
+
# Copyright:: Copyright (c) 2009 Savvica Inc
|
15
|
+
# Licence:: Distributes under the same terms as Ruby
|
16
|
+
#
|
17
|
+
module RuBing
|
18
|
+
class Search
|
19
|
+
require 'net/http'
|
20
|
+
require 'uri'
|
21
|
+
|
22
|
+
DEFAULT_COUNT = 20
|
23
|
+
|
24
|
+
@@base_url = 'http://api.search.live.net/json.aspx?'
|
25
|
+
@@base_site = nil
|
26
|
+
@@app_id = nil
|
27
|
+
|
28
|
+
def self.base_site=(bs)
|
29
|
+
@@base_site = bs
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.base_site
|
33
|
+
@@base_site
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.app_id=(ai)
|
37
|
+
@@app_id = ai
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.app_id
|
41
|
+
@@app_id
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.get(query, options = {})
|
45
|
+
Response.new(get_json(query, options), options[:web_count] || DEFAULT_COUNT)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def self.get_json(query, options)
|
50
|
+
Net::HTTP.get(URI.parse(@@base_url + get_options(query, options)))
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.get_options(query, options)
|
54
|
+
normalized_query = base_site.nil? ? query : "site:#{base_site} #{query}"
|
55
|
+
# TODO: will need seperate defaults when I fully support more than just web sources
|
56
|
+
config = { :app_id => app_id,
|
57
|
+
:disable_host_collapsing => true,
|
58
|
+
:sources => 'web',
|
59
|
+
:web_count => DEFAULT_COUNT,
|
60
|
+
:web_offset => 0,
|
61
|
+
:page => 1,
|
62
|
+
:query => normalized_query
|
63
|
+
}.update(options)
|
64
|
+
config[:page] = 1 if config[:page].to_i < 1
|
65
|
+
config[:web_offset] = config[:web_count] * (config[:page].to_i - 1)
|
66
|
+
|
67
|
+
raise 'You must set app_id in order to query live.com' if config[:app_id].nil?
|
68
|
+
dotted_params = %w{ ad image news mobile_web phonebook web video }
|
69
|
+
query_items = []
|
70
|
+
config.each_pair do |key, value|
|
71
|
+
if(/^(#{dotted_params.join('|')})/.match(key.to_s))
|
72
|
+
# most params are camelcase, except if they are Source specific. ex: Web.Count
|
73
|
+
first_key = $~[1]
|
74
|
+
second_key = key.to_s.sub("#{first_key}_", '')
|
75
|
+
query_items << "#{camelize(first_key)}.#{camelize(second_key)}=#{URI.encode(value.to_s)}"
|
76
|
+
else
|
77
|
+
query_items << "#{camelize(key.to_s)}=#{URI.encode(value.to_s)}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
query_items.join("&")
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.camelize(str)
|
84
|
+
words = str.split(/_/)
|
85
|
+
words.inject("") {|camelized, word| camelized + word.capitalize }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class Response
|
90
|
+
require 'json'
|
91
|
+
|
92
|
+
def initialize(json, count)
|
93
|
+
@json = json
|
94
|
+
@count = count
|
95
|
+
end
|
96
|
+
|
97
|
+
def results
|
98
|
+
@results ||= get_results
|
99
|
+
end
|
100
|
+
|
101
|
+
def total_results
|
102
|
+
@total_results ||= get_total_results
|
103
|
+
end
|
104
|
+
|
105
|
+
def total_pages
|
106
|
+
total_results / @count
|
107
|
+
end
|
108
|
+
|
109
|
+
def current_page
|
110
|
+
(offset / @count) + 1
|
111
|
+
end
|
112
|
+
|
113
|
+
def previous_page
|
114
|
+
current_page > 1 ? (current_page - 1) : nil
|
115
|
+
end
|
116
|
+
|
117
|
+
def next_page
|
118
|
+
current_page < total_pages ? (current_page + 1) : nil
|
119
|
+
end
|
120
|
+
|
121
|
+
def offset
|
122
|
+
@offset ||= get_offset
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
def get_results
|
127
|
+
parsed_json["SearchResponse"]["Web"]["Results"].collect {|r| Result.new(r) } rescue []
|
128
|
+
end
|
129
|
+
|
130
|
+
def get_total_results
|
131
|
+
parsed_json["SearchResponse"]["Web"]["Total"] rescue 0
|
132
|
+
end
|
133
|
+
|
134
|
+
def get_offset
|
135
|
+
parsed_json["SearchResponse"]["Web"]["Offset"] rescue 0
|
136
|
+
end
|
137
|
+
|
138
|
+
def parsed_json
|
139
|
+
@parsed_json ||= JSON.parse(@json)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class Result
|
144
|
+
REQUIRED_ATTRIBUTES = %w{title description url}
|
145
|
+
|
146
|
+
def initialize(result_hash)
|
147
|
+
@data = result_hash
|
148
|
+
result_hash.each_pair do |key, value|
|
149
|
+
next if key == "SearchTags" || key == "DeepLinks" # not sure why this is returned. I don't want it :p
|
150
|
+
begin
|
151
|
+
instance_eval(<<-EOS, __FILE__, __LINE__)
|
152
|
+
def #{Result.rubyize(key)}
|
153
|
+
#{value.dump}
|
154
|
+
end
|
155
|
+
EOS
|
156
|
+
rescue Exception => e
|
157
|
+
puts "Bad Key: '#{key}' #{e.message}"
|
158
|
+
puts result_hash.inspect
|
159
|
+
end
|
160
|
+
|
161
|
+
# So in some unknown situations Bing does not return some attributes!
|
162
|
+
def method_missing(method_name, *args, &block)
|
163
|
+
return "" if REQUIRED_ATTRIBUTES.include?(method_name)
|
164
|
+
raise NoMethodError
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def data
|
170
|
+
@data
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
def self.rubyize(str)
|
175
|
+
letters = str.split(/([A-Z])/).reject {|l| l == ""}
|
176
|
+
letters.inject("") {|rubyized, letter| (letter == letter.downcase) ? rubyized + letter + '_' : rubyized + letter.downcase }.chop
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
data/test/test_search.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require File.join(File.dirname(__FILE__), "..", "lib", "rubing")
|
3
|
+
|
4
|
+
class SearchTest < Test::Unit::TestCase
|
5
|
+
RuBing::Search::app_id = 'YOURAPPID'
|
6
|
+
RuBing::Search::base_site = 'learnhub.com'
|
7
|
+
|
8
|
+
def test_response
|
9
|
+
response = RuBing::Search.get('Ruby')
|
10
|
+
assert_not_nil response, "Should return a result"
|
11
|
+
assert response.total_results > 0, "Total results should be greater than zero"
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_get_results
|
15
|
+
response = RuBing::Search.get('Ruby', {:web_count => 20})
|
16
|
+
assert_equal 20, response.results.length
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_result_structure
|
20
|
+
response = RuBing::Search.get('Ruby')
|
21
|
+
assert response.results.first.methods.include?('url')
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: RuBing
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 1
|
9
|
+
version: 0.1.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Wesley Moxam
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-12-09 00:00:00 -05:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: json
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 1
|
30
|
+
- 1
|
31
|
+
- 0
|
32
|
+
version: 1.1.0
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description: A Ruby wrapper for Bing search API
|
36
|
+
email: wesley.moxam@savvica.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- lib/rubing.rb
|
45
|
+
- test/test_search.rb
|
46
|
+
has_rdoc: true
|
47
|
+
homepage: http://github.com/wmoxam/RuBing
|
48
|
+
licenses: []
|
49
|
+
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
segments:
|
61
|
+
- 1
|
62
|
+
- 8
|
63
|
+
- 6
|
64
|
+
version: 1.8.6
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
version: "0"
|
73
|
+
requirements: []
|
74
|
+
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 1.3.7
|
77
|
+
signing_key:
|
78
|
+
specification_version: 3
|
79
|
+
summary: A Ruby wrapper for Bing search API
|
80
|
+
test_files: []
|
81
|
+
|