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.
Files changed (3) hide show
  1. data/lib/rubing.rb +179 -0
  2. data/test/test_search.rb +23 -0
  3. 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
@@ -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
+