campaign_cash 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :gemcutter
2
+
3
+ # Specify your gem's dependencies in campaign_cash.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake/testtask'
5
+ Rake::TestTask.new(:test) do |test|
6
+ test.libs << 'lib' << 'test'
7
+ test.pattern = 'test/**/test_*.rb'
8
+ test.verbose = true
9
+ end
10
+
11
+ task :default => :test
12
+
13
+ require 'rake/rdoctask'
14
+ Rake::RDocTask.new do |rdoc|
15
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
16
+
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = "campaign_cash #{version}"
19
+ rdoc.rdoc_files.include('README*')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/campaign_cash/version", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "campaign_cash"
6
+ s.version = CampaignCash::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ['Derek Willis']
9
+ s.email = ['dwillis@gmail.com']
10
+ s.homepage = "http://rubygems.org/gems/campaign_cash"
11
+ s.summary = "A thin client for The New York Times Campaign Finance API"
12
+ s.description = "Following the money."
13
+
14
+ s.required_rubygems_version = ">= 1.3.6"
15
+ s.rubyforge_project = "campaign_cash"
16
+
17
+ s.add_development_dependency "bundler", ">= 1.0.0"
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
21
+ s.require_path = 'lib'
22
+ end
@@ -0,0 +1,11 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module CampaignCash
5
+
6
+ end
7
+
8
+ require "#{File.dirname(__FILE__)}/campaign_cash/base"
9
+ require "#{File.dirname(__FILE__)}/campaign_cash/version"
10
+ require "#{File.dirname(__FILE__)}/campaign_cash/candidate"
11
+ require "#{File.dirname(__FILE__)}/campaign_cash/committee"
@@ -0,0 +1,81 @@
1
+ require 'rubygems'
2
+ require 'open-uri'
3
+ require 'json'
4
+
5
+ module CampaignCash
6
+ class Base
7
+ API_SERVER = 'api.nytimes.com'
8
+ API_VERSION = 'v3'
9
+ API_NAME = 'elections/us'
10
+ API_BASE = "/svc/#{API_NAME}/#{API_VERSION}/finances"
11
+
12
+ @@api_key = nil
13
+ @@copyright = nil
14
+
15
+ class << self
16
+
17
+ ##
18
+ # The copyright footer to be placed at the bottom of any data from the New York Times. Note this is only set after an API call.
19
+ def copyright
20
+ @@copyright
21
+ end
22
+
23
+ def cycle
24
+ @@cycle
25
+ end
26
+
27
+ def base_uri
28
+ @@base_uri
29
+ end
30
+
31
+ ##
32
+ # Set the API key used for operations. This needs to be called before any requests against the API. To obtain an API key, go to http://developer.nytimes.com/
33
+ def api_key=(key)
34
+ @@api_key = key
35
+ end
36
+
37
+ def api_key
38
+ @@api_key
39
+ end
40
+
41
+ ##
42
+ # Builds a request URI to call the API server
43
+ def build_request_url(path, params)
44
+ URI::HTTP.build :host => API_SERVER,
45
+ :path => "#{API_BASE}/#{path}.json",
46
+ :query => params.map {|k,v| "#{k}=#{v}"}.join('&')
47
+ end
48
+
49
+ def invoke(path, params={})
50
+ begin
51
+ if @@api_key.nil?
52
+ raise "You must initialize the API key before you run any API queries"
53
+ end
54
+
55
+ full_params = params.merge 'api-key' => @@api_key
56
+
57
+ uri = build_request_url(path, full_params)
58
+
59
+ reply = uri.read
60
+ parsed_reply = JSON.parse reply
61
+
62
+ if parsed_reply.nil?
63
+ raise "Empty reply returned from API"
64
+ end
65
+
66
+ @@copyright = parsed_reply['copyright']
67
+ @@cycle = parsed_reply['cycle']
68
+ @@base_uri = parsed_reply['base_uri']
69
+
70
+ parsed_reply
71
+ rescue OpenURI::HTTPError => e
72
+ if e.message =~ /^404/
73
+ return nil
74
+ end
75
+
76
+ raise "Error connecting to URL #{uri} #{e}"
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,80 @@
1
+ module CampaignCash
2
+ class Candidate < Base
3
+
4
+ attr_reader :name, :id, :state, :district, :party, :fec_uri, :committee,
5
+ :mailing_city, :mailing_address, :mailing_state, :mailing_zip,
6
+ :total_receipts, :total_contributions, :total_from_individuals,
7
+ :total_from_pacs, :candidate_loans, :total_disbursements,
8
+ :total_refunds, :debts_owed, :begin_cash, :end_cash, :status,
9
+ :date_coverage_to, :date_coverage_from, :relative_uri
10
+
11
+ def initialize(params={})
12
+ params.each_pair do |k,v|
13
+ instance_variable_set("@#{k}", v)
14
+ end
15
+ end
16
+
17
+ def self.create_from_api(params={})
18
+ self.new :name => params['name'],
19
+ :id => params['id'],
20
+ :state => params['state'],
21
+ :district => params['district'],
22
+ :party => params['party'],
23
+ :fec_uri => params['fec_uri'],
24
+ :committee => params['committee'],
25
+ :mailing_city => params['mailing_city'],
26
+ :mailing_address => params['mailing_address'],
27
+ :mailing_state => params['mailing_state'],
28
+ :mailing_zip => params['mailing_zip'],
29
+ :total_receipts => params['total_receipts'],
30
+ :total_contributions => params['total_contributions'],
31
+ :total_from_individuals => params['total_from_individuals'],
32
+ :total_from_pacs => params['total_from_pacs'],
33
+ :candidate_loans => params['candidate_loans'],
34
+ :total_disbursements => params['total_disbursements'],
35
+ :total_refunds => params['total_refunds'],
36
+ :debts_owed => params['debts_owed'],
37
+ :begin_cash => params['begin_cash'],
38
+ :end_cash => params['end_cash'],
39
+ :status => params['status'],
40
+ :date_coverage_from => params['date_coverage_from'],
41
+ :date_coverage_to => params['date_coverage_to']
42
+ end
43
+
44
+ def self.create_from_api_search_results(params={})
45
+ self.new :name => params['candidate']['name'],
46
+ :id => params['candidate']['id'],
47
+ :state => params['state'],
48
+ :district => params['district'],
49
+ :party => params['candidate']['party'],
50
+ :relative_uri => params['candidate']['relative_uri'],
51
+ :committee => params['committee']
52
+
53
+ end
54
+
55
+ def self.find_by_fecid(cycle, fecid)
56
+ reply = invoke("#{cycle}/candidates/#{fecid}")
57
+ result = reply['results']
58
+ self.create_from_api(result.first) if result.first
59
+ end
60
+
61
+ def self.leaders(cycle, category)
62
+ reply = invoke("#{cycle}/candidates/leaders/#{category}",{})
63
+ results = reply['results']
64
+ results.map{|c| self.create_from_api(c)}
65
+ end
66
+
67
+ def self.search(cycle, name)
68
+ reply = invoke("#{cycle}/candidates/search", {:query => name})
69
+ results = reply['results']
70
+ results.map{|c| self.create_from_api_search_results(c)}
71
+ end
72
+
73
+ def self.new_candidates(cycle)
74
+ reply = invoke("#{cycle}/candidates/new",{})
75
+ results = reply['results']
76
+ results.map{|c| self.create_from_api(c)}
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,74 @@
1
+ module CampaignCash
2
+ class Committee < Base
3
+
4
+ attr_reader :name, :id, :state, :district, :party, :fec_uri, :candidate,
5
+ :city, :address, :state, :zip, :relative_uri,
6
+ :total_receipts, :total_contributions, :total_from_individuals,
7
+ :total_from_pacs, :candidate_loans, :total_disbursements,
8
+ :total_refunds, :debts_owed, :begin_cash, :end_cash,
9
+ :date_coverage_to, :date_coverage_from
10
+
11
+ def initialize(params={})
12
+ params.each_pair do |k,v|
13
+ instance_variable_set("@#{k}", v)
14
+ end
15
+ end
16
+
17
+ def self.create_from_api(params={})
18
+ self.new :name => params['name'],
19
+ :id => params['id'],
20
+ :state => params['state'],
21
+ :party => params['party'],
22
+ :fec_uri => params['fec_uri'],
23
+ :city => params['city'],
24
+ :address => params['address'],
25
+ :zip => params['zip'],
26
+ :total_receipts => params['total_receipts'],
27
+ :total_contributions => params['total_contributions'],
28
+ :total_from_individuals => params['total_from_individuals'],
29
+ :total_from_pacs => params['total_from_pacs'],
30
+ :candidate_loans => params['candidate_loans'],
31
+ :total_disbursements => params['total_disbursements'],
32
+ :total_refunds => params['total_refunds'],
33
+ :debts_owed => params['debts_owed'],
34
+ :begin_cash => params['begin_cash'],
35
+ :end_cash => params['end_cash'],
36
+ :date_coverage_from => params['date_coverage_from'],
37
+ :date_coverage_to => params['date_coverage_to'],
38
+ :candidate => params['candidate']
39
+ end
40
+
41
+ def self.create_from_api_search_results(params={})
42
+ self.new :name => params['name'],
43
+ :id => params['id'],
44
+ :city => params['city'],
45
+ :state => params['state'],
46
+ :zip => params['zip'],
47
+ :district => params['district'],
48
+ :party => params['party'],
49
+ :relative_uri => params['relative_uri'],
50
+ :candidate => params['candidate'],
51
+ :treasurer => params['treasurer']
52
+
53
+ end
54
+
55
+ def self.find_by_fecid(cycle, fecid)
56
+ reply = invoke("#{cycle}/committees/#{fecid}")
57
+ result = reply['results']
58
+ self.create_from_api(result.first) if result.first
59
+ end
60
+
61
+ def self.search(cycle, name)
62
+ reply = invoke("#{cycle}/committees/search", {:query => name})
63
+ results = reply['results']
64
+ results.map{|c| self.create_from_api_search_results(c)}
65
+ end
66
+
67
+ def self.new_candidates(cycle)
68
+ reply = invoke("#{cycle}/committees/new",{})
69
+ results = reply['results']
70
+ results.map{|c| self.create_from_api(c)}
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,3 @@
1
+ module CampaignCash
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,74 @@
1
+ require File.dirname(__FILE__) + '/../test_helper.rb'
2
+
3
+ class TestCampaignCash::TestCandidate < Test::Unit::TestCase
4
+ include CampaignCash
5
+
6
+ context "Candidate.create_from_api" do
7
+ setup do
8
+ reply = Base.invoke('2010/candidates/H4NY07011', {})
9
+ @result = reply['results'].first
10
+ @candidate = Candidate.create_from_api(@result)
11
+ end
12
+
13
+ should "return an object of the Candidate type" do
14
+ assert_kind_of(Candidate, @candidate)
15
+ end
16
+
17
+ %w(name id state district party fec_uri committee).each do |attr|
18
+ should "assign the value of the @#{attr} attribute from the '#{attr}' key in the hash" do
19
+ assert_equal(@result[attr], @candidate.send(attr))
20
+ end
21
+ end
22
+ end
23
+
24
+ context "Candidate search" do
25
+ setup do
26
+ reply = Base.invoke('2010/candidates/search', {:query => "Udall"})
27
+ results = reply['results']
28
+ @candidates = results.map{|c| Candidate.create_from_api_search_results(c)}
29
+ end
30
+
31
+ should "return two candidate objects" do
32
+ assert_equal @candidates.size, 2
33
+ assert_kind_of(Candidate, @candidates.first)
34
+ assert_kind_of(Candidate, @candidates.last)
35
+ end
36
+ end
37
+
38
+ context "New Candidates" do
39
+ setup do
40
+ reply = Base.invoke('2010/candidates/new', {})
41
+ results = reply['results']
42
+ @candidates = results.map{|c| Candidate.create_from_api(c)}
43
+ end
44
+
45
+ should "return 20 new candidates" do
46
+ assert_equal @candidates.size, 20
47
+ assert_kind_of(Candidate, @candidates.first)
48
+ assert_kind_of(Candidate, @candidates.last)
49
+ end
50
+ end
51
+
52
+ context "candidate leaders" do
53
+ setup do
54
+ reply = Base.invoke('2010/candidates/leaders/end_cash', {})
55
+ results = reply['results']
56
+ @candidates = results.map{|c| Candidate.create_from_api(c)}
57
+ end
58
+
59
+ should "return 20 candidates each with a greater end_cash value than the next" do
60
+ assert (@candidates[0].end_cash >= @candidates[1].end_cash)
61
+ assert (@candidates[1].end_cash >= @candidates[2].end_cash)
62
+ assert (@candidates[2].end_cash >= @candidates[3].end_cash)
63
+ end
64
+ end
65
+
66
+ context "request with missing cycle" do
67
+ should "return an error" do
68
+ assert_raise RuntimeError do
69
+ Base.invoke('/candidates/new', {})
70
+ end
71
+ end
72
+ end
73
+
74
+ end
@@ -0,0 +1,59 @@
1
+ require File.dirname(__FILE__) + '/../test_helper.rb'
2
+
3
+ class TestCampaignCash::TestCommittee < Test::Unit::TestCase
4
+ include CampaignCash
5
+
6
+ context "Committee.create_from_api" do
7
+ setup do
8
+ reply = Base.invoke('2010/committees/C00312223', {})
9
+ @result = reply['results'].first
10
+ @committee = Committee.create_from_api(@result)
11
+ end
12
+
13
+ should "return an object of the Committee type" do
14
+ assert_kind_of(Committee, @committee)
15
+ end
16
+
17
+ %w(name id state party fec_uri candidate).each do |attr|
18
+ should "assign the value of the @#{attr} attribute from the '#{attr}' key in the hash" do
19
+ assert_equal(@result[attr], @committee.send(attr))
20
+ end
21
+ end
22
+ end
23
+
24
+ context "Committee search" do
25
+ setup do
26
+ reply = Base.invoke('2010/committees/search', {:query => "Boeing"})
27
+ results = reply['results']
28
+ @committees = results.map{|c| Committee.create_from_api_search_results(c)}
29
+ end
30
+
31
+ should "return two committee objects" do
32
+ assert_equal @committees.size, 1
33
+ assert_kind_of(Committee, @committees.first)
34
+ assert_kind_of(Committee, @committees.last)
35
+ end
36
+ end
37
+
38
+ context "New Committees" do
39
+ setup do
40
+ reply = Base.invoke('2010/committees/new', {})
41
+ results = reply['results']
42
+ @committees = results.map{|c| Committee.create_from_api_search_results(c)}
43
+ end
44
+
45
+ should "return 20 new committees" do
46
+ assert_equal @committees.size, 20
47
+ assert_kind_of(Committee, @committees.first)
48
+ assert_kind_of(Committee, @committees.last)
49
+ end
50
+ end
51
+
52
+ context "request with missing id" do
53
+ should "return an error" do
54
+ assert_raise RuntimeError do
55
+ Base.invoke('2010/committees/', {})
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,22 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+ require 'shoulda'
4
+ require 'mocha'
5
+ require 'json'
6
+
7
+ require File.dirname(__FILE__) + '/../lib/campaign_cash'
8
+ require File.dirname(__FILE__) + '/../lib/campaign_cash/base'
9
+ require File.dirname(__FILE__) + '/../lib/campaign_cash/candidate'
10
+ require File.dirname(__FILE__) + '/../lib/campaign_cash/committee'
11
+
12
+ # set your NYT Campaign Finance API key as an environment variable to run the tests
13
+ API_KEY = ENV['NYT_CAMPFIN_API_KEY']
14
+ CampaignCash::Base.api_key = API_KEY
15
+
16
+ def api_url_for(path, params = {})
17
+ full_params = params.merge 'api-key' => API_KEY
18
+ CampaignCash::Base.build_request_url(path, full_params).to_s
19
+ end
20
+
21
+ module TestCampaignCash
22
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: campaign_cash
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Derek Willis
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-12-03 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: bundler
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 23
30
+ segments:
31
+ - 1
32
+ - 0
33
+ - 0
34
+ version: 1.0.0
35
+ type: :development
36
+ version_requirements: *id001
37
+ description: Following the money.
38
+ email:
39
+ - dwillis@gmail.com
40
+ executables: []
41
+
42
+ extensions: []
43
+
44
+ extra_rdoc_files: []
45
+
46
+ files:
47
+ - .gitignore
48
+ - Gemfile
49
+ - Rakefile
50
+ - campaign_cash.gemspec
51
+ - lib/campaign_cash.rb
52
+ - lib/campaign_cash/base.rb
53
+ - lib/campaign_cash/candidate.rb
54
+ - lib/campaign_cash/committee.rb
55
+ - lib/campaign_cash/version.rb
56
+ - test/campaign_cash/test_candidate.rb
57
+ - test/campaign_cash/test_committee.rb
58
+ - test/test_helper.rb
59
+ has_rdoc: true
60
+ homepage: http://rubygems.org/gems/campaign_cash
61
+ licenses: []
62
+
63
+ post_install_message:
64
+ rdoc_options: []
65
+
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ hash: 23
83
+ segments:
84
+ - 1
85
+ - 3
86
+ - 6
87
+ version: 1.3.6
88
+ requirements: []
89
+
90
+ rubyforge_project: campaign_cash
91
+ rubygems_version: 1.3.7
92
+ signing_key:
93
+ specification_version: 3
94
+ summary: A thin client for The New York Times Campaign Finance API
95
+ test_files: []
96
+