govkit 0.0.1 → 0.0.2

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/README.markdown CHANGED
@@ -1,26 +1,29 @@
1
- Govkit
2
- ======
1
+ # Govkit
3
2
 
4
3
  Govkit is a Ruby gem that provides simple access to open government APIs around the web.
5
4
 
6
- Setup
7
- =====
5
+ # Installation
6
+
7
+ From gemcutter:
8
+
9
+ gem install govkit
10
+
11
+ # Setup
8
12
 
9
13
  Add govkit to your environment.rb or Gemfile
10
14
 
11
15
  Run <code>./script/generate govkit</code> to copy a config file into <code>config/initializers/govkit.rb</code>. You will need to add your API keys to this config file.
12
16
 
13
- Example
14
- =======
17
+ # Example
15
18
 
16
19
  [http://fiftystates-dev.sunlightlabs.com/](The Fifty States project) has a RESTful API for accessing data about state legislators, bills, votes, etc.
17
20
 
18
- >> Govkit::FiftyStates::State.find_by_abbrev('CA')
21
+ >> Govkit::FiftyStates::State.find_by_abbreviation('CA')
22
+ >> Govkit::VoteSmart::Address.find(legislator_id)
19
23
 
20
24
  (TODO: add usage examples...)
21
25
 
22
- Bugs? Questions?
23
- ================
26
+ # Bugs? Questions?
24
27
 
25
28
  Please join the [Govkit Google Group](http://groups.google.com/group/govkit), especially if you'd like to talk about a new feature and get announcements.
26
29
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.2
@@ -1,9 +1,9 @@
1
- class GovkitGenerator < Rails::Generator::Base
2
-
1
+ class GovKitGenerator < Rails::Generator::Base
2
+
3
3
  def initialize(*runtime_args)
4
4
  super
5
5
  end
6
-
6
+
7
7
  def manifest
8
8
  record do |m|
9
9
  m.directory File.join('config', 'initializers')
@@ -12,9 +12,9 @@ class GovkitGenerator < Rails::Generator::Base
12
12
  end
13
13
 
14
14
  protected
15
-
15
+
16
16
  def banner
17
17
  %{Usage: #{$0} #{spec.name}\nCopies a config initializer to config/initializers/govkit.rb}
18
18
  end
19
-
20
- end
19
+
20
+ end
@@ -1,6 +1,6 @@
1
- if defined? Govkit
2
-
3
- Govkit.configure do |config|
1
+ if defined? GovKit
2
+
3
+ GovKit.configure do |config|
4
4
  # Get an API key for Sunlight's Fifty States project here:
5
5
  # http://services.sunlightlabs.com/accounts/register/
6
6
  config.fiftystates_apikey = 'YOUR_FIFTYSTATES_API_KEY'
data/govkit.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{govkit}
8
- s.version = "0.0.1"
8
+ s.version = "0.0.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Participatory Politics Foundation", "Srinivas Aki", "Carl Tashian"]
12
- s.date = %q{2010-05-10}
12
+ s.date = %q{2010-05-14}
13
13
  s.description = %q{Govkit lets you quickly get encapsulated Ruby objects for common open government APIs. We're starting with Sunlight's Fifty States API and the Project Vote Smart API.}
14
14
  s.email = %q{carl@ppolitics.org}
15
15
  s.extra_rdoc_files = [
@@ -27,9 +27,15 @@ Gem::Specification.new do |s|
27
27
  "generators/govkit/govkit_generator.rb",
28
28
  "generators/govkit/templates/govkit.rb",
29
29
  "govkit.gemspec",
30
+ "lib/gov_kit.rb",
31
+ "lib/gov_kit/configuration.rb",
32
+ "lib/gov_kit/fifty_states.rb",
33
+ "lib/gov_kit/resource.rb",
34
+ "lib/gov_kit/vote_smart.rb",
30
35
  "lib/govkit.rb",
31
36
  "lib/govkit/configuration.rb",
32
37
  "lib/govkit/fifty_states.rb",
38
+ "lib/govkit/vote_smart.rb",
33
39
  "rails/init.rb",
34
40
  "spec/fifty_states_spec.rb",
35
41
  "spec/spec.opts",
@@ -0,0 +1,29 @@
1
+ module GovKit
2
+ class Configuration
3
+ attr_accessor :fiftystates_apikey, :fiftystates_base_url
4
+ attr_accessor :votesmart_apikey, :votesmart_base_url
5
+
6
+ def initialize
7
+ @fiftystates_apikey = ''
8
+ @fiftystates_base_url = 'fiftystates-dev.sunlightlabs.com/api'
9
+
10
+ @votesmart_apikey = ''
11
+ @votesmart_base_url = 'api.votesmart.org/'
12
+ end
13
+ end
14
+
15
+ class << self
16
+ attr_accessor :configuration
17
+ end
18
+
19
+ # Configure GovKit in config/initializers/govkit.rb
20
+ #
21
+ # @example
22
+ # GovKit.configure do |config|
23
+ # config.fiftystates_apikey = ''
24
+ # end
25
+ def self.configure
26
+ self.configuration ||= Configuration.new
27
+ yield(configuration)
28
+ end
29
+ end
@@ -0,0 +1,57 @@
1
+ module GovKit
2
+ class FiftyStatesResource < Resource
3
+ default_params :output => 'json', :apikey => GovKit::configuration.fiftystates_apikey
4
+ base_uri GovKit::configuration.fiftystates_base_url
5
+ end
6
+
7
+ module FiftyStates
8
+ ROLE_MEMBER = "member"
9
+ ROLE_COMMITTEE_MEMBER = "committee member"
10
+ CHAMBER_UPPER = "upper"
11
+ CHAMBER_LOWER = "lower"
12
+
13
+ class State < FiftyStatesResource
14
+ def self.find_by_abbreviation(abbreviation)
15
+ response = get("/#{abbreviation}")
16
+ instantiate_record(response)
17
+ end
18
+ end
19
+
20
+ class Bill < FiftyStatesResource
21
+ # http://fiftystates-dev.sunlightlabs.com/api/ca/20092010/lower/bills/AB667/
22
+ def self.find(state_abbrev, session, chamber, bill_id)
23
+ response = get("/#{state_abbrev}/#{session}/#{chamber}/bills/#{bill_id}/")
24
+ instantiate_record(response)
25
+ end
26
+
27
+ def self.search(query, options = {})
28
+ response = get('/bills/search', :query => {:q => query}.merge(options))
29
+ instantiate_collection(response)
30
+ end
31
+
32
+ def self.latest(updated_since, state_abbrev)
33
+ response = get('/bills/latest/', :query => {:updated_since => updated_since, :state => state_abbrev})
34
+ instantiate_collection(response)
35
+ end
36
+ end
37
+
38
+ class Legislator < FiftyStatesResource
39
+ def self.find(legislator_id)
40
+ response = get("/legislators/#{legislator_id}")
41
+ instantiate_record(response)
42
+ end
43
+
44
+ def self.search(options = {})
45
+ response = get('/legislators/search', :query => options)
46
+ instantiate_collection(response)
47
+ end
48
+ end
49
+
50
+ class Vote < FiftyStatesResource
51
+ def self.find(vote_id)
52
+ response = get("/votes/#{vote_id}")
53
+ instantiate_record(response)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,125 @@
1
+ module GovKit
2
+ class GovKitError < StandardError
3
+ attr_reader :response
4
+
5
+ def initialize(response, message = nil)
6
+ @response = response
7
+ @message = message
8
+ end
9
+
10
+ def to_s
11
+ "Failed with #{response.code} #{response.message if response.respond_to?(:message)}"
12
+ end
13
+ end
14
+
15
+ class NotauthorizedError < GovKitError;
16
+ end
17
+
18
+ class InvalidRequestError < GovKitError;
19
+ end
20
+
21
+ class NotFoundError < GovKitError;
22
+ end
23
+
24
+ class NameError < GovKitError;
25
+ end
26
+
27
+ class Resource
28
+ include HTTParty
29
+ format :json
30
+
31
+ attr_accessor :attributes
32
+
33
+ def initialize(attributes = {})
34
+ @attributes = {}
35
+ unload(attributes)
36
+ end
37
+
38
+ class << self
39
+ def instantiate_record(record)
40
+ new(record)
41
+ end
42
+
43
+ def instantiate_collection(collection)
44
+ collection.collect! { |record| instantiate_record(record) }
45
+ end
46
+
47
+ def parse(json)
48
+ instantiate_record(json)
49
+ end
50
+ end
51
+
52
+ def unload(attributes)
53
+ raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
54
+ attributes.each do |key, value|
55
+ @attributes[key.to_s] =
56
+ case value
57
+ when Array
58
+ resource = resource_for_collection(key)
59
+ value.map do |attrs|
60
+ if attrs.is_a?(String) || attrs.is_a?(Numeric)
61
+ attrs.duplicable? ? attrs.dup : attrs
62
+ else
63
+ resource.new(attrs)
64
+ end
65
+ end
66
+ when Hash
67
+ resource = find_or_create_resource_for(key)
68
+ resource.new(value)
69
+ else
70
+ value.dup rescue value
71
+ end
72
+ end
73
+ self
74
+ end
75
+
76
+ private
77
+ def resource_for_collection(name)
78
+ find_or_create_resource_for(name.to_s.singularize)
79
+ end
80
+
81
+ def find_resource_in_modules(resource_name, module_names)
82
+ receiver = Object
83
+ namespaces = module_names[0, module_names.size-1].map do |module_name|
84
+ receiver = receiver.const_get(module_name)
85
+ end
86
+ if namespace = namespaces.reverse.detect { |ns| ns.const_defined?(resource_name) }
87
+ return namespace.const_get(resource_name)
88
+ else
89
+ raise NameError, "Namespace for #{namespace} not found"
90
+ end
91
+ end
92
+
93
+ def find_or_create_resource_for(name)
94
+ resource_name = name.to_s.camelize
95
+ ancestors = self.class.name.split("::")
96
+ if ancestors.size > 1
97
+ find_resource_in_modules(resource_name, ancestors)
98
+ else
99
+ self.class.const_get(resource_name)
100
+ end
101
+ rescue NameError
102
+ if self.class.const_defined?(resource_name)
103
+ resource = self.class.const_get(resource_name)
104
+ else
105
+ resource = self.class.const_set(resource_name, Class.new(GovKit::Resource))
106
+ end
107
+ resource
108
+ end
109
+
110
+ def method_missing(method_symbol, * arguments) #:nodoc:
111
+ method_name = method_symbol.to_s
112
+
113
+ case method_name.last
114
+ when "="
115
+ attributes[method_name.first(-1)] = arguments.first
116
+ when "?"
117
+ attributes[method_name.first(-1)]
118
+ when "]"
119
+ attributes[arguments.first.to_s]
120
+ else
121
+ attributes.has_key?(method_name) ? attributes[method_name] : super
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,22 @@
1
+ module GovKit
2
+ class VoteSmartResource < Resource
3
+ default_params :o => 'JSON', :key => GovKit::configuration.votesmart_apikey
4
+ base_uri GovKit::configuration.votesmart_base_url
5
+ end
6
+
7
+ module VoteSmart
8
+ class Address < VoteSmartResource
9
+ def self.find(candidate_id)
10
+ response = get("/Address.getOffice", :query => {"candidateId" => candidate_id})
11
+ instantiate_record(response['address'])
12
+ end
13
+ end
14
+
15
+ class WebAddress < VoteSmartResource
16
+ def self.find(candidate_id)
17
+ response = get("/Address.getOfficeWebAddress", :query => {"candidateId" => candidate_id})
18
+ instantiate_record(response['webaddress'])
19
+ end
20
+ end
21
+ end
22
+ end
data/lib/gov_kit.rb ADDED
@@ -0,0 +1,20 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
2
+
3
+ require 'active_support'
4
+ require 'gov_kit/configuration'
5
+
6
+ module GovKit
7
+ autoload :FiftyStates, 'gov_kit/fifty_states'
8
+ autoload :VoteSmart, 'gov_kit/vote_smart'
9
+
10
+ class Bill < Resource; end
11
+ class Vote < Resource; end
12
+ class Session < Resource; end
13
+ class Role < Resource; end
14
+ class Legislator < Resource; end
15
+ class Vote < Resource; end
16
+ class Sponsor < Resource; end
17
+ class Version < Resource; end
18
+ class Source < Resource; end
19
+ class Address < Resource; end
20
+ end
@@ -1,10 +1,14 @@
1
1
  module Govkit
2
2
  class Configuration
3
3
  attr_accessor :fiftystates_apikey, :fiftystates_base_url
4
+ attr_accessor :votesmart_apikey, :votesmart_base_url
4
5
 
5
6
  def initialize
6
7
  @fiftystates_apikey = ''
7
8
  @fiftystates_base_url = 'fiftystates-dev.sunlightlabs.com/api'
9
+
10
+ @votesmart_apikey = ''
11
+ @votesmart_base_url = 'api.votesmart.org/'
8
12
  end
9
13
  end
10
14
 
@@ -0,0 +1,146 @@
1
+ require 'httparty'
2
+
3
+ module Govkit::VoteSmart
4
+ class VoteSmartError < StandardError
5
+ attr_reader :response
6
+
7
+ def initialize(response, message = nil)
8
+ @response = response
9
+ @message = message
10
+ end
11
+
12
+ def to_s
13
+ "Failed with #{response.code} #{response.message if response.respond_to?(:message)}"
14
+ end
15
+ end
16
+
17
+ class NotauthorizedError < VoteSmartError;
18
+ end
19
+
20
+ class InvalidRequestError < StandardError;
21
+ end
22
+
23
+ class NotFoundError < VoteSmartError;
24
+ end
25
+
26
+ class NameError < VoteSmartError;
27
+ end
28
+
29
+ class Base
30
+ include HTTParty
31
+ default_params :o => 'JSON', :key => Govkit::configuration.votesmart_apikey
32
+ base_uri Govkit::configuration.votesmart_base_url
33
+
34
+ attr_accessor :attributes
35
+
36
+ def initialize(attributes = {})
37
+ @attributes = {}
38
+ unload(attributes)
39
+ end
40
+
41
+ class << self
42
+ def instantiate_record(record)
43
+ new(record)
44
+ end
45
+
46
+ def instantiate_collection(collection)
47
+ collection.collect! { |record| instantiate_record(record) }
48
+ end
49
+
50
+ def parse(json)
51
+ instantiate_record(json)
52
+ end
53
+ end
54
+
55
+ def unload(attributes)
56
+ raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
57
+ attributes.each do |key, value|
58
+ @attributes[key.to_s] =
59
+ case value
60
+ when Array
61
+ resource = resource_for_collection(key)
62
+ value.map do |attrs|
63
+ if attrs.is_a?(String) || attrs.is_a?(Numeric)
64
+ attrs.duplicable? ? attrs.dup : attrs
65
+ else
66
+ resource.new(attrs)
67
+ end
68
+ end
69
+ when Hash
70
+ resource = find_or_create_resource_for(key)
71
+ resource.new(value)
72
+ else
73
+ value.dup rescue value
74
+ end
75
+ end
76
+ self
77
+ end
78
+
79
+ private
80
+ def resource_for_collection(name)
81
+ find_or_create_resource_for(name.to_s.singularize)
82
+ end
83
+
84
+ def find_resource_in_modules(resource_name, module_names)
85
+ receiver = Object
86
+ namespaces = module_names[0, module_names.size-1].map do |module_name|
87
+ receiver = receiver.const_get(module_name)
88
+ end
89
+ if namespace = namespaces.reverse.detect { |ns| ns.const_defined?(resource_name) }
90
+ return namespace.const_get(resource_name)
91
+ else
92
+ raise NameError, "Namespace for #{namespace} not found"
93
+ end
94
+ end
95
+
96
+ def find_or_create_resource_for(name)
97
+ resource_name = name.to_s.camelize
98
+ ancestors = self.class.name.split("::")
99
+ if ancestors.size > 1
100
+ find_resource_in_modules(resource_name, ancestors)
101
+ else
102
+ self.class.const_get(resource_name)
103
+ end
104
+ rescue NameError
105
+ if self.class.const_defined?(resource_name)
106
+ resource = self.class.const_get(resource_name)
107
+ else
108
+ resource = self.class.const_set(resource_name, Class.new(Base))
109
+ end
110
+ resource
111
+ end
112
+
113
+ def method_missing(method_symbol, * arguments) #:nodoc:
114
+ method_name = method_symbol.to_s
115
+
116
+ case method_name.last
117
+ when "="
118
+ attributes[method_name.first(-1)] = arguments.first
119
+ when "?"
120
+ attributes[method_name.first(-1)]
121
+ when "]"
122
+ attributes[arguments.first.to_s]
123
+ else
124
+ attributes.has_key?(method_name) ? attributes[method_name] : super
125
+ end
126
+ end
127
+ end
128
+
129
+ class Address < Base
130
+ class << self
131
+ def find(candidate_id)
132
+ response = get("/Address.getOffice", :query => {"candidateId" => candidate_id})
133
+ instantiate_record(response['address'])
134
+ end
135
+ end
136
+ end
137
+
138
+ class WebAddress < Base
139
+ class << self
140
+ def find(candidate_id)
141
+ response = get("/Address.getOfficeWebAddress", :query => {"candidateId" => candidate_id})
142
+ instantiate_record(response['webaddress'])
143
+ end
144
+ end
145
+ end
146
+ end
data/lib/govkit.rb CHANGED
@@ -1,8 +1 @@
1
- $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
2
-
3
- require 'active_support'
4
- require 'govkit/configuration'
5
-
6
- module Govkit
7
- autoload :FiftyStates, 'govkit/fifty_states'
8
- end
1
+ require 'gov_kit'
@@ -2,8 +2,8 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  #TODO: Mock http requests, rather than making an actual call to the api
4
4
 
5
- module Govkit::FiftyStates
6
- describe Govkit::FiftyStates do
5
+ module GovKit::FiftyStates
6
+ describe GovKit::FiftyStates do
7
7
  it "should have the base uri set properly" do
8
8
  [State, Bill, Legislator].each do |klass|
9
9
  klass.base_uri.should == "http://fiftystates-dev.sunlightlabs.com/api"
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 1
9
- version: 0.0.1
8
+ - 2
9
+ version: 0.0.2
10
10
  platform: ruby
11
11
  authors:
12
12
  - Participatory Politics Foundation
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-05-10 00:00:00 -07:00
19
+ date: 2010-05-14 00:00:00 -07:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -67,9 +67,15 @@ files:
67
67
  - generators/govkit/govkit_generator.rb
68
68
  - generators/govkit/templates/govkit.rb
69
69
  - govkit.gemspec
70
+ - lib/gov_kit.rb
71
+ - lib/gov_kit/configuration.rb
72
+ - lib/gov_kit/fifty_states.rb
73
+ - lib/gov_kit/resource.rb
74
+ - lib/gov_kit/vote_smart.rb
70
75
  - lib/govkit.rb
71
76
  - lib/govkit/configuration.rb
72
77
  - lib/govkit/fifty_states.rb
78
+ - lib/govkit/vote_smart.rb
73
79
  - rails/init.rb
74
80
  - spec/fifty_states_spec.rb
75
81
  - spec/spec.opts