campaign_cash 0.0.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/.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
+