govkit-h 0.7.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +3 -0
- data/Gemfile +13 -0
- data/LICENSE +20 -0
- data/README.md +70 -0
- data/Rakefile +82 -0
- data/USAGE +1 -0
- data/VERSION +1 -0
- data/generators/govkit/govkit_generator.rb +24 -0
- data/generators/govkit/templates/govkit.rb +24 -0
- data/govkit.gemspec +130 -0
- data/init.rb +4 -0
- data/lib/generators/govkit/govkit_generator.rb +21 -0
- data/lib/generators/govkit/templates/create_mentions.rb +21 -0
- data/lib/generators/govkit/templates/govkit.rb +24 -0
- data/lib/generators/govkit/templates/mention.rb +15 -0
- data/lib/gov_kit.rb +45 -0
- data/lib/gov_kit/acts_as_noteworthy.rb +63 -0
- data/lib/gov_kit/configuration.rb +58 -0
- data/lib/gov_kit/follow_the_money.rb +176 -0
- data/lib/gov_kit/open_congress.rb +125 -0
- data/lib/gov_kit/open_congress/bill.rb +171 -0
- data/lib/gov_kit/open_congress/blog_post.rb +15 -0
- data/lib/gov_kit/open_congress/news_post.rb +15 -0
- data/lib/gov_kit/open_congress/person.rb +141 -0
- data/lib/gov_kit/open_congress/person_stat.rb +13 -0
- data/lib/gov_kit/open_congress/roll_call.rb +14 -0
- data/lib/gov_kit/open_congress/roll_call_comparison.rb +28 -0
- data/lib/gov_kit/open_congress/voting_comparison.rb +44 -0
- data/lib/gov_kit/open_states.rb +132 -0
- data/lib/gov_kit/railtie.rb +24 -0
- data/lib/gov_kit/resource.rb +190 -0
- data/lib/gov_kit/search_engines.rb +7 -0
- data/lib/gov_kit/search_engines/bing.rb +38 -0
- data/lib/gov_kit/search_engines/google_blog.rb +32 -0
- data/lib/gov_kit/search_engines/google_news.rb +47 -0
- data/lib/gov_kit/search_engines/technorati.rb +35 -0
- data/lib/gov_kit/search_engines/wikipedia.rb +27 -0
- data/lib/gov_kit/transparency_data.rb +144 -0
- data/lib/gov_kit/vote_smart.rb +126 -0
- data/lib/govkit.rb +1 -0
- data/spec/fixtures/bing/news_search.response +1 -0
- data/spec/fixtures/bing/no_results.response +1 -0
- data/spec/fixtures/follow_the_money/business-page0.response +28 -0
- data/spec/fixtures/follow_the_money/business-page1.response +12 -0
- data/spec/fixtures/follow_the_money/contribution.response +12 -0
- data/spec/fixtures/follow_the_money/unauthorized.response +8 -0
- data/spec/fixtures/open_congress/person.response +8 -0
- data/spec/fixtures/open_states/401.response +10 -0
- data/spec/fixtures/open_states/404.response +9 -0
- data/spec/fixtures/open_states/410.response +6 -0
- data/spec/fixtures/open_states/bill.response +240 -0
- data/spec/fixtures/open_states/bill_query.response +1990 -0
- data/spec/fixtures/open_states/committee_find.response +53 -0
- data/spec/fixtures/open_states/committee_query.response +190 -0
- data/spec/fixtures/open_states/legislator.response +34 -0
- data/spec/fixtures/open_states/legislator_query.response +144 -0
- data/spec/fixtures/open_states/state.response +60 -0
- data/spec/fixtures/search_engines/google_news.response +8 -0
- data/spec/fixtures/transparency_data/contributions.response +18 -0
- data/spec/fixtures/transparency_data/entities_search.response +7 -0
- data/spec/fixtures/transparency_data/entities_search_limit_0.response +7 -0
- data/spec/fixtures/transparency_data/entities_search_limit_1.response +7 -0
- data/spec/fixtures/transparency_data/grants_find_all.response +7 -0
- data/spec/fixtures/transparency_data/lobbyists_find_all.response +7 -0
- data/spec/follow_the_money_spec.rb +61 -0
- data/spec/open_congress_spec.rb +108 -0
- data/spec/open_states_spec.rb +213 -0
- data/spec/search_engines_spec.rb +44 -0
- data/spec/spec_helper.rb +36 -0
- data/spec/transparency_data_spec.rb +106 -0
- metadata +258 -0
@@ -0,0 +1,132 @@
|
|
1
|
+
module GovKit
|
2
|
+
|
3
|
+
# Parent class for OpenStates resources
|
4
|
+
# See http://openstates.sunlightlabs.com/api/
|
5
|
+
class OpenStatesResource < Resource
|
6
|
+
|
7
|
+
# Uses default_params from the HTTParty gem.
|
8
|
+
# See HTTParty::ClassMethods:
|
9
|
+
# http://rubydoc.info/gems/httparty/0.7.4/HTTParty/ClassMethods#default_params-instance_method
|
10
|
+
default_params :output => 'json', :apikey => GovKit::configuration.sunlight_apikey
|
11
|
+
base_uri GovKit::configuration.openstates_base_url
|
12
|
+
|
13
|
+
# Do a GET query, with optional parameters.
|
14
|
+
#
|
15
|
+
# OpenStates returns a 404 error when a query
|
16
|
+
# returns nothing.
|
17
|
+
#
|
18
|
+
# So, if a query result is a resource not found error,
|
19
|
+
# we return an empty set.
|
20
|
+
def self.get_uri(uri, options={})
|
21
|
+
begin
|
22
|
+
response = get(uri, options)
|
23
|
+
result = parse(response)
|
24
|
+
rescue ResourceNotFound
|
25
|
+
return []
|
26
|
+
end
|
27
|
+
result
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
# Ruby module for interacting with the Open States Project API
|
33
|
+
# See http://openstates.sunlightlabs.com/api/
|
34
|
+
# Most +find+ and +search+ methods:
|
35
|
+
# * call HTTParty::ClassMethods#get
|
36
|
+
# * which returns an HTTParty::Response object
|
37
|
+
# * which is passed to GovKit::Resource#parse
|
38
|
+
# * which uses the response to populate a Resource
|
39
|
+
#
|
40
|
+
module OpenStates
|
41
|
+
ROLE_MEMBER = "member"
|
42
|
+
ROLE_COMMITTEE_MEMBER = "committee member"
|
43
|
+
CHAMBER_UPPER = "upper"
|
44
|
+
CHAMBER_LOWER = "lower"
|
45
|
+
|
46
|
+
# The State class represents the state data returned from Open States.
|
47
|
+
#
|
48
|
+
# For details about fields returned, see the Open States documentation, at
|
49
|
+
# http://openstates.sunlightlabs.com/api/metadata/,
|
50
|
+
#
|
51
|
+
class State < OpenStatesResource
|
52
|
+
def self.find_by_abbreviation(abbreviation)
|
53
|
+
get_uri("/metadata/#{abbreviation}/")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# The Bill class represents the bill data returned from Open States.
|
58
|
+
#
|
59
|
+
# For details about fields returned, see the Open States documentation, at
|
60
|
+
# http://openstates.sunlightlabs.com/api/bills/,
|
61
|
+
#
|
62
|
+
class Bill < OpenStatesResource
|
63
|
+
# http://openstates.sunlightlabs.com/api/v1/bills/ca/20092010/AB 667/
|
64
|
+
def self.find(state_abbrev, session, bill_id, chamber = '')
|
65
|
+
escaped_bill_id = bill_id.gsub(/ /, '%20')
|
66
|
+
escaped_session = session.gsub(/ /, '%20')
|
67
|
+
|
68
|
+
get_uri("/bills/#{state_abbrev.downcase}/#{escaped_session}/#{chamber.blank? ? '' : chamber + '/'}#{escaped_bill_id}/")
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.search(query, options = {})
|
72
|
+
result = get_uri('/bills/', :query => {:q => query}.merge(options))
|
73
|
+
return Array(result)
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.latest(updated_since, ops = {})
|
77
|
+
response = get('/bills/', :query => {:updated_since => updated_since.to_s}.merge(ops))
|
78
|
+
parse(response)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# The Legislator class represents the legislator data returned from Open States.
|
83
|
+
#
|
84
|
+
# For details about fields returned, see the Open States documentation, at
|
85
|
+
# http://openstates.sunlightlabs.com/api/legislators/,
|
86
|
+
#
|
87
|
+
class Legislator < OpenStatesResource
|
88
|
+
def self.find(legislator_id)
|
89
|
+
get_uri("/legislators/#{legislator_id}/")
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.search(options = {})
|
93
|
+
result = get_uri('/legislators/', :query => options)
|
94
|
+
return Array(result)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# The Committee class represents the committee data returned from Open States.
|
99
|
+
#
|
100
|
+
# For details about fields returned, see the Open States documentation, at
|
101
|
+
# http://openstates.sunlightlabs.com/api/committees/,
|
102
|
+
#
|
103
|
+
class Committee < OpenStatesResource
|
104
|
+
def self.find(committee_id)
|
105
|
+
get_uri("/committees/#{committee_id}/")
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.search(options = {})
|
109
|
+
get_uri('/committees/', :query => options)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class Role < OpenStatesResource; end
|
114
|
+
|
115
|
+
class Sponsor < OpenStatesResource; end
|
116
|
+
|
117
|
+
class Version < OpenStatesResource; end
|
118
|
+
|
119
|
+
class Source < OpenStatesResource; end
|
120
|
+
|
121
|
+
class Address < OpenStatesResource; end
|
122
|
+
|
123
|
+
class Action < OpenStatesResource; end
|
124
|
+
|
125
|
+
class Vote < OpenStatesResource
|
126
|
+
def self.find(vote_id)
|
127
|
+
response = get("/votes/#{vote_id}/")
|
128
|
+
parse(response)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'govkit'
|
2
|
+
|
3
|
+
module GovKit
|
4
|
+
if defined? Rails::Railtie
|
5
|
+
require 'rails'
|
6
|
+
class Railtie < Rails::Railtie
|
7
|
+
initializer 'govkit.insert_into_active_record' do
|
8
|
+
ActiveSupport.on_load :active_record do
|
9
|
+
GovKit::Railtie.insert
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# This class exists in order to run its insert method while
|
16
|
+
# Rails is loading.
|
17
|
+
# This then adds GovKit::ActsAsNoteworthy to ActiveRecord::Base.
|
18
|
+
# See http://api.rubyonrails.org/classes/Rails/Railtie.html
|
19
|
+
class Railtie
|
20
|
+
def self.insert
|
21
|
+
ActiveRecord::Base.send(:include, GovKit::ActsAsNoteworthy)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
module GovKit
|
2
|
+
|
3
|
+
|
4
|
+
# This is the parent class for the classes that wrap
|
5
|
+
# the data returned to govkit.
|
6
|
+
#
|
7
|
+
# Subclasses are responsible for fetching the data from
|
8
|
+
# different web services; Resource will then parse the returned data,
|
9
|
+
# converting returned fields to instance methods.
|
10
|
+
#
|
11
|
+
# Initialize a Resource with a hash of attributes, or an array of hashes.
|
12
|
+
# For each attribute, add a getter and setter to this instance.
|
13
|
+
# @example
|
14
|
+
# res = Resource.new { "aaa" => "111", "bbb" => "222", "ccc" => "333" }
|
15
|
+
# res.aaa == "111"
|
16
|
+
# res.bbb == "222"
|
17
|
+
# res.ccc == "333"
|
18
|
+
#
|
19
|
+
# Includes {http://rdoc.info/github/jnunemaker/httparty/master/HTTParty/ClassMethods HTTParty}, which provides convenience methods like get().
|
20
|
+
class Resource
|
21
|
+
include HTTParty
|
22
|
+
format :json
|
23
|
+
|
24
|
+
# The attributes data returned by the service.
|
25
|
+
attr_reader :attributes
|
26
|
+
|
27
|
+
# The response returned by the service.
|
28
|
+
attr_reader :raw_response
|
29
|
+
|
30
|
+
def initialize(attributes = {})
|
31
|
+
@attributes = {}
|
32
|
+
@raw_response = attributes
|
33
|
+
|
34
|
+
unload(attributes)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Hash] the response object, potentially useful for comparison on sync
|
38
|
+
#
|
39
|
+
def to_md5
|
40
|
+
Digest::MD5.hexdigest(@raw_response.body)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Handles the basic responses we might get back from a web service.
|
44
|
+
#
|
45
|
+
# On failure, throws an error.
|
46
|
+
#
|
47
|
+
# If a service returns something other than a 404 when an object is not found,
|
48
|
+
# you'll need to handle that in the subclass.
|
49
|
+
#
|
50
|
+
# @param [Object] response the object.
|
51
|
+
# @return [Resource] a new Resource created from the response.
|
52
|
+
#
|
53
|
+
def self.parse(response)
|
54
|
+
|
55
|
+
if response.class == HTTParty::Response
|
56
|
+
case response.response
|
57
|
+
when Net::HTTPNotFound
|
58
|
+
raise ResourceNotFound, "404 Not Found"
|
59
|
+
when Net::HTTPGone
|
60
|
+
raise ResourceNotFound, "404 Not Found"
|
61
|
+
when Net::HTTPUnauthorized
|
62
|
+
raise NotAuthorized, "401 Not Authorized; have you set up your API key?"
|
63
|
+
when Net::HTTPServerError
|
64
|
+
raise ServerError, '5xx server error'
|
65
|
+
when Net::HTTPClientError
|
66
|
+
raise ClientError, '4xx client error'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
return [] unless !response.blank?
|
71
|
+
|
72
|
+
instantiate(response)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Instantiate new GovKit::Resources.
|
76
|
+
#
|
77
|
+
# @param [Hash] record a hash of values returned by a service, or an array of hashes.
|
78
|
+
# @return [Resource]
|
79
|
+
#
|
80
|
+
# If +record+ is a hash, return a single GovKit::Resource.
|
81
|
+
# If it is an array, return an array of GovKit::Resources.
|
82
|
+
#
|
83
|
+
def self.instantiate(record)
|
84
|
+
if record.is_a?(Array)
|
85
|
+
instantiate_collection(record)
|
86
|
+
else
|
87
|
+
new(record)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Instantiate a set of records.
|
92
|
+
#
|
93
|
+
# @return [Array] Array of records
|
94
|
+
# @param [Array] collection An array of records
|
95
|
+
def self.instantiate_collection(collection)
|
96
|
+
collection.collect! { |record| new(record) }
|
97
|
+
end
|
98
|
+
|
99
|
+
# Given a hash of attributes, assign it to the @attributes member.
|
100
|
+
# Then for each attribute, create or set a pair of member accessors with the name
|
101
|
+
# of the attribute's key.
|
102
|
+
#
|
103
|
+
# If the value of the attribute is itself an array or a hash,
|
104
|
+
# then create a new class with the (singularized) key as a name, and with a parent class of Resource,
|
105
|
+
# and initialize it with the hash.
|
106
|
+
#
|
107
|
+
# @param [Hash] attributes the attributes returned by the web service.
|
108
|
+
#
|
109
|
+
def unload(attributes)
|
110
|
+
raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
|
111
|
+
|
112
|
+
attributes.each do |key, value|
|
113
|
+
@attributes[key.to_s] =
|
114
|
+
case value
|
115
|
+
when Array
|
116
|
+
resource = resource_for_collection(key)
|
117
|
+
value.map do |attrs|
|
118
|
+
if attrs.is_a?(String) || attrs.is_a?(Numeric)
|
119
|
+
attrs.duplicable? ? attrs.dup : attrs
|
120
|
+
else
|
121
|
+
resource.new(attrs)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
when Hash
|
125
|
+
resource = find_or_create_resource_for(key)
|
126
|
+
resource.new(value)
|
127
|
+
else
|
128
|
+
value.dup rescue value
|
129
|
+
end
|
130
|
+
end
|
131
|
+
self
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
# Finds a member of the GovKit module with the given name.
|
137
|
+
# If the resource doesn't exist, creates it.
|
138
|
+
#
|
139
|
+
def resource_for_collection(name)
|
140
|
+
find_or_create_resource_for(name.to_s.singularize)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Searches each module in +ancestors+ for members named +resource_name+
|
144
|
+
# Returns the named resource
|
145
|
+
# Throws a NameError if none of the resources in the list contains +resource_name+
|
146
|
+
#
|
147
|
+
def find_resource_in_modules(resource_name, ancestors)
|
148
|
+
if namespace = ancestors.detect { |a| a.constants.include?(resource_name.to_sym) }
|
149
|
+
return namespace.const_get(resource_name)
|
150
|
+
else
|
151
|
+
raise NameError, "Namespace for #{namespace} not found"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Searches the GovKit module for a resource with the name +name+, cleaned and camelized
|
156
|
+
# Returns that resource.
|
157
|
+
# If the resource isn't found, it's created.
|
158
|
+
#
|
159
|
+
def find_or_create_resource_for(name)
|
160
|
+
resource_name = name.to_s.gsub(/^[_\-+]/,'').gsub(/^(\-?\d)/, "n#{$1}").gsub(/(\s|-)/, '').camelize
|
161
|
+
if self.class.parents.size > 1
|
162
|
+
find_resource_in_modules(resource_name, self.class.parents)
|
163
|
+
else
|
164
|
+
self.class.const_get(resource_name)
|
165
|
+
end
|
166
|
+
rescue NameError
|
167
|
+
if self.class.const_defined?(resource_name)
|
168
|
+
resource = self.class.const_get(resource_name)
|
169
|
+
else
|
170
|
+
resource = self.class.const_set(resource_name, Class.new(GovKit::Resource))
|
171
|
+
end
|
172
|
+
resource
|
173
|
+
end
|
174
|
+
|
175
|
+
def method_missing(method_symbol, * arguments) #:nodoc:
|
176
|
+
method_name = method_symbol.to_s
|
177
|
+
|
178
|
+
case method_name.last
|
179
|
+
when "="
|
180
|
+
attributes[method_name.first(-1)] = arguments.first
|
181
|
+
when "?"
|
182
|
+
!attributes[method_name.first(-1)].blank?
|
183
|
+
when "]"
|
184
|
+
attributes[arguments.first.to_s]
|
185
|
+
else
|
186
|
+
attributes.has_key?(method_name) ? attributes[method_name] : super
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
module GovKit::SearchEngines
|
2
|
+
autoload :GoogleNews, 'gov_kit/search_engines/google_news'
|
3
|
+
autoload :GoogleBlog, 'gov_kit/search_engines/google_blog'
|
4
|
+
autoload :Technorati, 'gov_kit/search_engines/technorati'
|
5
|
+
autoload :Bing, 'gov_kit/search_engines/bing'
|
6
|
+
autoload :Wikipedia, 'gov_kit/search_engines/wikipedia'
|
7
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module GovKit
|
2
|
+
module SearchEngines
|
3
|
+
class Bing
|
4
|
+
def self.search(query=[], options={})
|
5
|
+
host = GovKit::configuration.bing_base_url
|
6
|
+
query = [query, options[:geo]].compact.join('+')
|
7
|
+
|
8
|
+
options['Sources'] ||= 'news'
|
9
|
+
|
10
|
+
path = "/json.aspx?Query=#{URI::encode(query)}&AppId=#{GovKit::configuration.bing_appid}&Sources=#{options['Sources']}"
|
11
|
+
|
12
|
+
doc = JSON.parse(make_request(host, path))
|
13
|
+
|
14
|
+
mentions = []
|
15
|
+
|
16
|
+
if news_items = doc['SearchResponse']['News']
|
17
|
+
puts "#{news_items['Results'].size} from Bing"
|
18
|
+
news_items['Results'].each do |i|
|
19
|
+
mention = GovKit::Mention.new
|
20
|
+
mention.title = i['Title']
|
21
|
+
mention.search_source = 'Bing'
|
22
|
+
mention.date = DateTime.parse(i['Date'])
|
23
|
+
mention.excerpt = i['Snippet']
|
24
|
+
mention.source = i['Source']
|
25
|
+
mention.url = i['Url']
|
26
|
+
|
27
|
+
mentions << mention
|
28
|
+
end
|
29
|
+
end
|
30
|
+
mentions
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.make_request(host, path)
|
34
|
+
Net::HTTP.get(host, path)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module GovKit
|
2
|
+
module SearchEngines
|
3
|
+
class GoogleBlog
|
4
|
+
def self.search(query=[], options = {})
|
5
|
+
query = [query, options[:geo]].compact.join('+')
|
6
|
+
host = GovKit::configuration.google_blog_base_url
|
7
|
+
path = "/blogsearch_feeds?q=#{URI::encode(query)}&hl=en&output=rss&num=50"
|
8
|
+
|
9
|
+
doc = Nokogiri::XML(make_request(host, path))
|
10
|
+
|
11
|
+
mentions = []
|
12
|
+
|
13
|
+
doc.xpath('//item').each do |i|
|
14
|
+
mention = GovKit::Mention.new
|
15
|
+
mention.title = i.xpath('title').inner_text
|
16
|
+
mention.search_source = 'Google Blogs'
|
17
|
+
mention.date = i.xpath('dc:date').inner_text
|
18
|
+
mention.excerpt = i.xpath('description').inner_text
|
19
|
+
mention.source = i.xpath('dc:publisher').inner_text
|
20
|
+
mention.url = i.xpath('link').inner_text
|
21
|
+
|
22
|
+
mentions << mention
|
23
|
+
end
|
24
|
+
mentions
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.make_request(host, path)
|
28
|
+
response = Net::HTTP.get(host, path)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module GovKit
|
2
|
+
module SearchEngines
|
3
|
+
|
4
|
+
# Class to wrap access to Google News.
|
5
|
+
class GoogleNews
|
6
|
+
|
7
|
+
# Fetches stories about a topic from google news.
|
8
|
+
# Returns an array of GovKit::Mention objects.
|
9
|
+
#
|
10
|
+
# query: The query wanted For example:
|
11
|
+
# mentions = GoogleNews.search("Nancy Pelosi")
|
12
|
+
#
|
13
|
+
# options: Any additional parameters to the search. eg.:
|
14
|
+
# :geo => 'Texas' will add &geo=Texas to the URL.
|
15
|
+
# :num => 100 will show 100 results per page.
|
16
|
+
#
|
17
|
+
def self.search(query=[], options={})
|
18
|
+
query = Array(query).join('+')
|
19
|
+
host = GovKit::configuration.google_news_base_url
|
20
|
+
options[:num] ||= 50
|
21
|
+
|
22
|
+
path = "/news?q=#{URI::encode(query)}&output=rss" + '&' + options.map { |k, v| URI::encode(k.to_s) + '=' + URI::encode(v.to_s) }.join('&')
|
23
|
+
|
24
|
+
doc = Nokogiri::XML(make_request(host, path))
|
25
|
+
|
26
|
+
mentions = []
|
27
|
+
|
28
|
+
doc.xpath('//item').each do |i|
|
29
|
+
mention = GovKit::Mention.new
|
30
|
+
mention.title = i.xpath('title').inner_text.split(" - ").first
|
31
|
+
mention.date = i.xpath('pubDate').inner_text
|
32
|
+
mention.excerpt = i.xpath('description').inner_text
|
33
|
+
mention.source = i.xpath('title').inner_text.split(" - ").last
|
34
|
+
mention.url = i.xpath('link').inner_text
|
35
|
+
|
36
|
+
mentions << mention
|
37
|
+
end
|
38
|
+
|
39
|
+
mentions
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.make_request(host, path)
|
43
|
+
response = Net::HTTP.get(host, path)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|