open_fec_api 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 818b705a49bd80cf98215fef54f4b5eda20769da
4
+ data.tar.gz: 4ab0d6b6a6075df42cdc644ebb577244531d98a8
5
+ SHA512:
6
+ metadata.gz: 1e2b362f084c2dcef094944a678e411b337bf2815c1b4ba833b437561f444278163c98c99430ec7e36d82a4c203e32276b5613203dee2618747f9ae3ea26e373
7
+ data.tar.gz: 8aad09cc00374547cbd3621f462eaaf77ee26efda67b4c82e34f8d7c7cd8fc9864f64c19b3460e3ab7d570f960adafc5b4304593595c8efc3d1af077d9b1f1df
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/CREDITS.md ADDED
@@ -0,0 +1,14 @@
1
+ # Credits, Notes, and Reference
2
+ + http://sunlightfoundation.com/blog/2015/07/08/openfec-makes-campaign-finance-data-more-accessible-with-new-api-heres-how-to-get-started/
3
+ + http://brandonhilkert.com/blog/ruby-gem-configuration-patterns/
4
+ + https://robots.thoughtbot.com/mygem-configure-block
5
+ + http://stackoverflow.com/questions/10584638/setting-up-configuration-settings-when-writing-a-gem
6
+ + http://sunlightfoundation.com/blog/2015/07/08/openfec-makes-campaign-finance-data-more-accessible-with-new-api-heres-how-to-get-started/
7
+ + http://betterspecs.org/
8
+ + https://github.com/jnunemaker/httparty
9
+ + https://github.com/jnunemaker/httparty/tree/master/examples
10
+ + http://s2t2.info/process-documentation/2015/07/03/how-to-make-a-ruby-gem.html
11
+ + https://gorails.com/episodes/consuming-an-api-using-httparty-and-creating-a-gem
12
+ + https://api.open.fec.gov/
13
+ + http://plumbing.pipelinedeals.com/wrapping-rest-apis-with-ruby/
14
+ + http://stackoverflow.com/questions/2680523/dry-ruby-initialization-with-hash-argument
data/ENDPOINTS.md ADDED
@@ -0,0 +1,36 @@
1
+ ## Endpoints
2
+
3
+ Endpoint | Operation | Wrapper Method
4
+ --- | --- | ---
5
+ Candidates | `GET /candidate/{candidate_id}` | `Candidate#find`
6
+ Candidates | `GET /candidate/{candidate_id}/history` | N/A
7
+ Candidates | `GET /candidate/{candidate_id}/history/{cycle}` | N/A
8
+ Candidates | `GET /candidates` | `Candidate#all`
9
+ Candidates | `GET /candidates/search` | N/A
10
+ Candidates | `GET /candidate/{candidate_id}/committees` | N/A
11
+ Candidates | `GET /candidate/{candidate_id}/committees/history` | N/A
12
+ Candidates | `GET /candidate/{candidate_id}/committees/history/{cycle}` | N/A
13
+ Committees | `GET /committee/{committee_id}/candidates` | N/A
14
+ Committees | `GET /committee/{committee_id}/candidates/history` | N/A
15
+ Committees | `GET /committee/{committee_id}/candidates/history/{cycle}` | N/A
16
+ Committees | `GET /committee/{committee_id}` | N/A
17
+ Committees | `GET /committee/{committee_id}/history` | N/A
18
+ Committees | `GET /committee/{committee_id}/history/{cycle}` | N/A
19
+ Committees | `GET /committees` | N/A
20
+ Filings | `GET /committee/{committee_id}/filings` | N/A
21
+ Filings | `GET /filings` | N/A
22
+ Financial | `GET /committee/{committee_id}/reports` | N/A
23
+ Financial | `GET /committee/{committee_id}/totals` | N/A
24
+ Financial | `GET /reports/{committee_type}` | N/A
25
+ Schedules | `GET /committee/{committee_id}/schedules/schedule_a/by_contributor` | N/A
26
+ Schedules | `GET /committee/{committee_id}/schedules/schedule_a/by_size` | N/A
27
+ Schedules | `GET /committee/{committee_id}/schedules/schedule_a/by_state` | N/A
28
+ Schedules | `GET /committee/{committee_id}/schedules/schedule_a/by_zip` | N/A
29
+ Schedules | `GET /schedules/schedule_a` | N/A
30
+ Schedules | `GET /schedules/schedule_a/by_contributor` | N/A
31
+ Schedules | `GET /schedules/schedule_a/by_size` | N/A
32
+ Schedules | `GET /schedules/schedule_a/by_state` | N/A
33
+ Schedules | `GET /schedules/schedule_a/by_zip` | N/A
34
+ Schedules | `GET /schedules/schedule_b` | N/A
35
+ Search | `GET /names/candidates` | `Candidate#where`
36
+ Search | `GET /names/committees` | N/A
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in open_fec_api.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 MJ Rossetti (@s2t2)
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # OpenFecApi
2
+
3
+ A ruby wrapper for 18F's [Open FEC (Federal Elections Commission) API](https://api.open.fec.gov/).
4
+
5
+ ## Installation
6
+
7
+ Add `gem 'open_fec_api'` to your application's *Gemfile* and run `bundle install`, or install manually with `gem install open_fec_api`.
8
+
9
+ ## Configuration
10
+
11
+ Configure a client with your [API key](https://api.data.gov/signup/) before making requests.
12
+
13
+ ```` rb
14
+ client = OpenFecApi::Client.new("api_key_123")
15
+ ````
16
+
17
+ ## Usage
18
+
19
+ Make a request.
20
+
21
+ ```` rb
22
+ response = client.candidates
23
+ ````
24
+
25
+ Request different pages by setting the `:page` request parameter. Avoid rate-limits by increasing the `:per_page` request parameter to 100.
26
+
27
+ ```` rb
28
+ options = {:page => 1, :per_page => 100}
29
+ response = client.candidates(options)
30
+ while response.page < response.pages do
31
+ options.merge!({:page => response.page + 1})
32
+ response = client.candidates(options)
33
+ end
34
+ ````
35
+
36
+ Make requests using endpoint-specific parameters.
37
+
38
+ ```` rb
39
+ options = {:party => "DEM"}
40
+ response = client.candidates(options)
41
+ ````
42
+
43
+ ## Contributing
44
+
45
+ Help wrap all the [endpoints](ENDPOINTS.md)!
46
+
47
+ Browse existing issues or create a new issue to communicate bugs, desired features, etc.
48
+
49
+ After forking the repo and pushing your changes, create a pull request referencing the applicable issue(s).
50
+
51
+ ### Installation
52
+
53
+ Check out the repo with `git clone git@github.com:debate-watch/open-fec-api-ruby.git`, and `cd open-fec-api-ruby`.
54
+
55
+ After checking out the repo, run `bin/setup` to install dependencies.
56
+
57
+ ### Testing
58
+
59
+ Run `bundle exec rake` or `bundle exec rspec spec/` to run the tests.
60
+
61
+ You can also run `bin/console` for an interactive prompt that will allow you to experiment.
62
+
63
+ To install this gem onto your local machine, run `bundle exec rake install`.
64
+
65
+ ### Releasing
66
+
67
+ Update the version number in `version.rb`, then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "open_fec_api"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require "pry"
11
+ Pry.start
12
+
13
+ # require "irb"
14
+ # IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,81 @@
1
+ require "open_fec_api/version"
2
+ require "open_fec_api/client"
3
+ require "open_fec_api/response"
4
+
5
+ module OpenFecApi
6
+ =begin
7
+
8
+ #
9
+ # CANDIDATE
10
+ #
11
+
12
+ GET /candidate/{candidate_id}
13
+ GET /candidate/{candidate_id}/history
14
+ GET /candidate/{candidate_id}/history/{cycle}
15
+
16
+ #
17
+ # CANDIDATES
18
+ #
19
+
20
+ GET /candidates
21
+ GET /candidates/search
22
+
23
+ GET /committee/{committee_id}/candidates
24
+ GET /committee/{committee_id}/candidates/history
25
+ GET /committee/{committee_id}/candidates/history/{cycle}
26
+
27
+ #
28
+ # COMMITTEE
29
+ #
30
+
31
+ GET /committee/{committee_id}
32
+ GET /committee/{committee_id}/filings
33
+ GET /committee/{committee_id}/reports
34
+ GET /committee/{committee_id}/totals
35
+ GET /committee/{committee_id}/history
36
+ GET /committee/{committee_id}/history/{cycle}
37
+
38
+ #
39
+ # COMMITTEES
40
+ #
41
+
42
+ GET /committees
43
+
44
+ GET /candidate/{candidate_id}/committees
45
+ GET /candidate/{candidate_id}/committees/history
46
+ GET /candidate/{candidate_id}/committees/history/{cycle}
47
+
48
+ #
49
+ # SCHEDULES
50
+ #
51
+
52
+ GET /schedules/schedule_a
53
+ GET /schedules/schedule_a/by_contributor
54
+ GET /schedules/schedule_a/by_size
55
+ GET /schedules/schedule_a/by_state
56
+ GET /schedules/schedule_a/by_zip
57
+ GET /schedules/schedule_b
58
+
59
+ GET /committee/{committee_id}/schedules/schedule_a/by_contributor
60
+ GET /committee/{committee_id}/schedules/schedule_a/by_size
61
+ GET /committee/{committee_id}/schedules/schedule_a/by_state
62
+ GET /committee/{committee_id}/schedules/schedule_a/by_zip
63
+
64
+ # NAMES
65
+
66
+ GET /names/candidates
67
+ GET /names/committees
68
+
69
+ HTTParty.get("http://api.open.fec.gov/v1/names/candidates?api_key=#{@api_key}")
70
+ HTTParty.get("http://api.open.fec.gov/v1/names/committees?api_key=#{@api_key}")
71
+
72
+ # FILINGS
73
+
74
+ GET /filings
75
+
76
+ # REPORTS
77
+
78
+ GET /reports/{committee_type}
79
+
80
+ =end
81
+ end
@@ -0,0 +1,51 @@
1
+ require 'httparty'
2
+
3
+ module OpenFecApi
4
+ class Client
5
+ attr_reader :api_key
6
+ include HTTParty
7
+ base_uri 'https://api.open.fec.gov/v1'
8
+
9
+ def initialize(api_key)
10
+ @api_key = api_key
11
+ end
12
+
13
+ def configured?
14
+ !self.api_key.nil?
15
+ end
16
+
17
+ # Candidates Endpoint
18
+ #
19
+ # https://api.open.fec.gov/developers#!/candidate/get_candidates
20
+ #
21
+ # @param [Hash] options
22
+ # option options Array[string] :sort Provide a field to sort by. Use - for descending order.
23
+ # option options Boolean :sort_hide_null Hide null values on sorted column(s).
24
+ # option options String :year See records pertaining to a particular year.
25
+ # option options Array[string] :office Governmental office candidate runs for: House (H), Senate (S) or President (P).
26
+ # option options Array[string] :candidate_status One letter code explaining if the candidate is: present (C), future (F), not yet (N), or prior (P).
27
+ # option options Array[string] :party Three letter code for the party under which a candidate ran for office.
28
+ # option options Array[string] :state U.S. State candidate or territory where a candidate runs for office.
29
+ # option options Array[integer] :cycle Two-year election cycle in which a candidate runs for office. Calculated from FEC form 2.
30
+ # option options Array[string] :district Two digit district number.
31
+ # option options Array[string] :incumbent_challenge One letter code explaining if the candidate is an incumbent (I), a challenger (C), or if the seat is open (O).
32
+ # option options String :q Text to search all fields for.
33
+ # option options String :name Candidate's name (full or partial).
34
+ # option options Array[string] :candidate_id A unique identifier assigned to each candidate registered with the FEC. If a person runs for several offices, that person will have separate candidate IDs for each office.
35
+ # option options Integer :page For paginating through results, starting at page 1.
36
+ # option options Integer :per_page The number of results returned per page. Defaults to 20.
37
+ #
38
+ # @example
39
+ # OpenFecApi::Client.new(:api_key => API_KEY).candidates(:page => 1, :per_page => 100)
40
+ def candidates(options = {})
41
+ query = {'api_key' => @api_key}
42
+ request_params = ["sort", "sort_hide_null", "year", "office", "candidate_status", "party", "state", "cycle", "district", "incumbent_challenge", "name", "candidate_id", "page", "per_page"]
43
+ request_options = options.select{|k,v| request_params.include?(k.to_s)}
44
+ request_options.each do |k,v|
45
+ query.merge!({k.to_s => v})
46
+ end
47
+ response = self.class.get("/candidates", query: query)
48
+ return Response.new(response)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,49 @@
1
+ module OpenFecApi
2
+ class Response
3
+ attr_reader :request, :headers, :api_version, :pagination, :results
4
+
5
+ def initialize(response)
6
+ @request = response.request
7
+ @headers = response.headers
8
+ @api_version = response["api_version"]
9
+ @pagination = response["pagination"]
10
+ @results = response["results"]
11
+ end
12
+
13
+ def request_query
14
+ request.options[:query]
15
+ end
16
+
17
+ def limit
18
+ headers["x-ratelimit-limit"].to_i
19
+ end
20
+
21
+ def remaining
22
+ headers["x-ratelimit-remaining"].to_i
23
+ end
24
+
25
+ def page
26
+ pagination["page"].to_i
27
+ end
28
+
29
+ def pages
30
+ pagination["pages"].to_i
31
+ end
32
+
33
+ def results_count
34
+ results.count
35
+ end
36
+
37
+ def first_result_name
38
+ results.first["name"]
39
+ end
40
+
41
+ def last_result_name
42
+ results.last["name"]
43
+ end
44
+
45
+ def summary
46
+ "PAGE #{page}/#{pages} -- RATE #{remaining}/#{limit} -- #{first_result_name} ... #{last_result_name}"
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ module OpenFecApi
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'open_fec_api/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "open_fec_api"
8
+ spec.version = OpenFecApi::VERSION
9
+ spec.authors = ["MJ Rossetti (@s2t2)"]
10
+ spec.email = ["s2t2mail@gmail.com"]
11
+ spec.summary = %q{A ruby wrapper for 18F's [Open FEC (Federal Elections Commission) API](https://api.open.fec.gov/).}
12
+ spec.description = %q{A ruby wrapper for 18F's [Open FEC (Federal Elections Commission) API](https://api.open.fec.gov/).}
13
+ spec.homepage = "https://github.com/debate-watch/open_fec_api/"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "~> 3.1"
24
+ spec.add_development_dependency "pry", "~> 0.10"
25
+ spec.add_dependency "httparty", "~> 0.13"
26
+ end
@@ -0,0 +1,303 @@
1
+ {"pagination"=>{"pages"=>1020, "count"=>20393, "per_page"=>20, "page"=>1},
2
+ "api_version"=>"1.0",
3
+ "results"=>
4
+ [{"candidate_status"=>"N",
5
+ "active_through"=>2004,
6
+ "incumbent_challenge_full"=>"Unknown",
7
+ "district"=>nil,
8
+ "election_years"=>[2004],
9
+ "party"=>"IND",
10
+ "office"=>"P",
11
+ "candidate_status_full"=>"Not yet a statutory candidate",
12
+ "state"=>"US",
13
+ "incumbent_challenge"=>nil,
14
+ "party_full"=>"Independent",
15
+ "office_full"=>"President",
16
+ "candidate_id"=>"P40002172",
17
+ "name"=>"AABBATTE, MICHAEL THOMAS WITORT",
18
+ "cycles"=>[2004]},
19
+ {"candidate_status"=>"C",
20
+ "active_through"=>2012,
21
+ "incumbent_challenge_full"=>"Challenger",
22
+ "district"=>nil,
23
+ "election_years"=>[2012],
24
+ "party"=>"REP",
25
+ "office"=>"S",
26
+ "candidate_status_full"=>"Statutory candidate",
27
+ "state"=>"UT",
28
+ "incumbent_challenge"=>"C",
29
+ "party_full"=>"Republican Party",
30
+ "office_full"=>"Senate",
31
+ "candidate_id"=>"S2UT00229",
32
+ "name"=>"AALDERS, TIMOTHY NOEL",
33
+ "cycles"=>[2012]},
34
+ {"candidate_status"=>"C",
35
+ "active_through"=>2012,
36
+ "incumbent_challenge_full"=>"Challenger",
37
+ "district"=>"01",
38
+ "election_years"=>[2012],
39
+ "party"=>"REP",
40
+ "office"=>"H",
41
+ "candidate_status_full"=>"Statutory candidate",
42
+ "state"=>"CA",
43
+ "incumbent_challenge"=>"C",
44
+ "party_full"=>"Republican Party",
45
+ "office_full"=>"House",
46
+ "candidate_id"=>"H2CA01110",
47
+ "name"=>"AANESTAD, SAMUEL",
48
+ "cycles"=>[2012]},
49
+ {"candidate_status"=>"N",
50
+ "active_through"=>2008,
51
+ "incumbent_challenge_full"=>"Open seat",
52
+ "district"=>nil,
53
+ "election_years"=>[2008],
54
+ "party"=>"DEM",
55
+ "office"=>"P",
56
+ "candidate_status_full"=>"Not yet a statutory candidate",
57
+ "state"=>"US",
58
+ "incumbent_challenge"=>"O",
59
+ "party_full"=>"Democratic Party",
60
+ "office_full"=>"President",
61
+ "candidate_id"=>"P80002926",
62
+ "name"=>"AARON, LAURA DAVIS",
63
+ "cycles"=>[2008]},
64
+ {"candidate_status"=>"P",
65
+ "active_through"=>2000,
66
+ "incumbent_challenge_full"=>"Unknown",
67
+ "district"=>"01",
68
+ "election_years"=>[2000],
69
+ "party"=>"REP",
70
+ "office"=>"H",
71
+ "candidate_status_full"=>"Statutory candidate in a prior cycle",
72
+ "state"=>"MA",
73
+ "incumbent_challenge"=>nil,
74
+ "party_full"=>"Republican Party",
75
+ "office_full"=>"House",
76
+ "candidate_id"=>"H0MA01024",
77
+ "name"=>"ABAIR, PETER JON",
78
+ "cycles"=>[2000]},
79
+ {"candidate_status"=>"N",
80
+ "active_through"=>2012,
81
+ "incumbent_challenge_full"=>"Open seat",
82
+ "district"=>"00",
83
+ "election_years"=>[2012],
84
+ "party"=>"DEM",
85
+ "office"=>"H",
86
+ "candidate_status_full"=>"Not yet a statutory candidate",
87
+ "state"=>"MT",
88
+ "incumbent_challenge"=>"O",
89
+ "party_full"=>"Democratic Party",
90
+ "office_full"=>"House",
91
+ "candidate_id"=>"H2MT00039",
92
+ "name"=>"ABARR, JOHN ALLEN",
93
+ "cycles"=>[2012]},
94
+ {"candidate_status"=>"C",
95
+ "active_through"=>2008,
96
+ "incumbent_challenge_full"=>"Unknown",
97
+ "district"=>"05",
98
+ "election_years"=>[2006, 2008],
99
+ "party"=>"DEM",
100
+ "office"=>"H",
101
+ "candidate_status_full"=>"Statutory candidate",
102
+ "state"=>"NJ",
103
+ "incumbent_challenge"=>nil,
104
+ "party_full"=>"Democratic Party",
105
+ "office_full"=>"House",
106
+ "candidate_id"=>"H6NJ05155",
107
+ "name"=>"ABATE, CAMILLE M",
108
+ "cycles"=>[2006, 2008]},
109
+ {"candidate_status"=>"P",
110
+ "active_through"=>1992,
111
+ "incumbent_challenge_full"=>"Unknown",
112
+ "district"=>"12",
113
+ "election_years"=>[1992],
114
+ "party"=>"DEM",
115
+ "office"=>"H",
116
+ "candidate_status_full"=>"Statutory candidate in a prior cycle",
117
+ "state"=>"NJ",
118
+ "incumbent_challenge"=>nil,
119
+ "party_full"=>"Democratic Party",
120
+ "office_full"=>"House",
121
+ "candidate_id"=>"H2NJ12036",
122
+ "name"=>"ABATE, FRANK G",
123
+ "cycles"=>[1992]},
124
+ {"candidate_status"=>"C",
125
+ "active_through"=>1990,
126
+ "incumbent_challenge_full"=>"Unknown",
127
+ "district"=>"03",
128
+ "election_years"=>[1990],
129
+ "party"=>"REP",
130
+ "office"=>"H",
131
+ "candidate_status_full"=>"Statutory candidate",
132
+ "state"=>"IA",
133
+ "incumbent_challenge"=>nil,
134
+ "party_full"=>"Republican Party",
135
+ "office_full"=>"House",
136
+ "candidate_id"=>"H0IA03071",
137
+ "name"=>"ABBAS, JEFFREY LYN",
138
+ "cycles"=>[1990]},
139
+ {"candidate_status"=>"C",
140
+ "active_through"=>1994,
141
+ "incumbent_challenge_full"=>"Unknown",
142
+ "district"=>"15",
143
+ "election_years"=>[1994],
144
+ "party"=>"REP",
145
+ "office"=>"H",
146
+ "candidate_status_full"=>"Statutory candidate",
147
+ "state"=>"TX",
148
+ "incumbent_challenge"=>nil,
149
+ "party_full"=>"Republican Party",
150
+ "office_full"=>"House",
151
+ "candidate_id"=>"H4TX15043",
152
+ "name"=>"ABBOTT, BONNIE",
153
+ "cycles"=>[1994]},
154
+ {"candidate_status"=>"N",
155
+ "active_through"=>2016,
156
+ "incumbent_challenge_full"=>"Open seat",
157
+ "district"=>nil,
158
+ "election_years"=>[2016],
159
+ "party"=>"IND",
160
+ "office"=>"P",
161
+ "candidate_status_full"=>"Not yet a statutory candidate",
162
+ "state"=>"US",
163
+ "incumbent_challenge"=>"O",
164
+ "party_full"=>"Independent",
165
+ "office_full"=>"President",
166
+ "candidate_id"=>"P60005840",
167
+ "name"=>"ABBOTT, GIFFORD WHEELER JR",
168
+ "cycles"=>[2016]},
169
+ {"candidate_status"=>"P",
170
+ "active_through"=>1996,
171
+ "incumbent_challenge_full"=>"Challenger",
172
+ "district"=>"00",
173
+ "election_years"=>[1996],
174
+ "party"=>"DEM",
175
+ "office"=>"H",
176
+ "candidate_status_full"=>"Statutory candidate in a prior cycle",
177
+ "state"=>"SD",
178
+ "incumbent_challenge"=>"C",
179
+ "party_full"=>"Democratic Party",
180
+ "office_full"=>"House",
181
+ "candidate_id"=>"H6SD00077",
182
+ "name"=>"ABBOTT, JAMES W",
183
+ "cycles"=>[1996]},
184
+ {"candidate_status"=>"P",
185
+ "active_through"=>1992,
186
+ "incumbent_challenge_full"=>"Unknown",
187
+ "district"=>nil,
188
+ "election_years"=>[1988, 1992],
189
+ "party"=>"DEM",
190
+ "office"=>"P",
191
+ "candidate_status_full"=>"Statutory candidate in a prior cycle",
192
+ "state"=>"US",
193
+ "incumbent_challenge"=>nil,
194
+ "party_full"=>"Democratic Party",
195
+ "office_full"=>"President",
196
+ "candidate_id"=>"P80002579",
197
+ "name"=>"ABBOTT, JOHN HANCOCK",
198
+ "cycles"=>[1992]},
199
+ {"candidate_status"=>"N",
200
+ "active_through"=>1988,
201
+ "incumbent_challenge_full"=>"Unknown",
202
+ "district"=>nil,
203
+ "election_years"=>[1986, 1988],
204
+ "party"=>"DEM",
205
+ "office"=>"S",
206
+ "candidate_status_full"=>"Not yet a statutory candidate",
207
+ "state"=>"CA",
208
+ "incumbent_challenge"=>nil,
209
+ "party_full"=>"Democratic Party",
210
+ "office_full"=>"Senate",
211
+ "candidate_id"=>"S6CA00345",
212
+ "name"=>"ABBOTT, JOHN HANCOCK",
213
+ "cycles"=>[1986]},
214
+ {"candidate_status"=>"T",
215
+ "active_through"=>2004,
216
+ "incumbent_challenge_full"=>"Unknown",
217
+ "district"=>"18",
218
+ "election_years"=>[2002, 2004],
219
+ "party"=>"REP",
220
+ "office"=>"H",
221
+ "candidate_status_full"=>"Obsolete",
222
+ "state"=>"TX",
223
+ "incumbent_challenge"=>nil,
224
+ "party_full"=>"Republican Party",
225
+ "office_full"=>"House",
226
+ "candidate_id"=>"H2TX18082",
227
+ "name"=>"ABBOTT, PHILLIP J.",
228
+ "cycles"=>[2002, 2004]},
229
+ {"candidate_status"=>"P",
230
+ "active_through"=>2000,
231
+ "incumbent_challenge_full"=>"Unknown",
232
+ "district"=>"03",
233
+ "election_years"=>[1994, 1996, 2000],
234
+ "party"=>"DEM",
235
+ "office"=>"H",
236
+ "candidate_status_full"=>"Statutory candidate in a prior cycle",
237
+ "state"=>"KY",
238
+ "incumbent_challenge"=>nil,
239
+ "party_full"=>"Democratic Party",
240
+ "office_full"=>"House",
241
+ "candidate_id"=>"H4KY03095",
242
+ "name"=>"ABBOTT, RAYMOND H",
243
+ "cycles"=>[1994, 1996, 1998, 2000]},
244
+ {"candidate_status"=>"N",
245
+ "active_through"=>2014,
246
+ "incumbent_challenge_full"=>"Challenger",
247
+ "district"=>"03",
248
+ "election_years"=>[2014],
249
+ "party"=>"DEM",
250
+ "office"=>"H",
251
+ "candidate_status_full"=>"Not yet a statutory candidate",
252
+ "state"=>"MI",
253
+ "incumbent_challenge"=>"C",
254
+ "party_full"=>"Democratic Party",
255
+ "office_full"=>"House",
256
+ "candidate_id"=>"H4MI03201",
257
+ "name"=>"ABBOTT, RICHARD ALLEN IV",
258
+ "cycles"=>[2014]},
259
+ {"candidate_status"=>"P",
260
+ "active_through"=>2004,
261
+ "incumbent_challenge_full"=>"Unknown",
262
+ "district"=>"04",
263
+ "election_years"=>[2002, 2004],
264
+ "party"=>"DEM",
265
+ "office"=>"H",
266
+ "candidate_status_full"=>"Statutory candidate in a prior cycle",
267
+ "state"=>"IN",
268
+ "incumbent_challenge"=>nil,
269
+ "party_full"=>"Democratic Party",
270
+ "office_full"=>"House",
271
+ "candidate_id"=>"H2IN04101",
272
+ "name"=>"ABBOTT, WILLIAM ANTHONY",
273
+ "cycles"=>[2002, 2004]},
274
+ {"candidate_status"=>"P",
275
+ "active_through"=>1988,
276
+ "incumbent_challenge_full"=>"Challenger",
277
+ "district"=>"02",
278
+ "election_years"=>[1988],
279
+ "party"=>"REP",
280
+ "office"=>"H",
281
+ "candidate_status_full"=>"Statutory candidate in a prior cycle",
282
+ "state"=>"NE",
283
+ "incumbent_challenge"=>"C",
284
+ "party_full"=>"Republican Party",
285
+ "office_full"=>"House",
286
+ "candidate_id"=>"H8NE02089",
287
+ "name"=>"ABBOUD, CHRIS",
288
+ "cycles"=>[1988]},
289
+ {"candidate_status"=>"C",
290
+ "active_through"=>2008,
291
+ "incumbent_challenge_full"=>"Challenger",
292
+ "district"=>"16",
293
+ "election_years"=>[2008],
294
+ "party"=>"DEM",
295
+ "office"=>"H",
296
+ "candidate_status_full"=>"Statutory candidate",
297
+ "state"=>"IL",
298
+ "incumbent_challenge"=>"C",
299
+ "party_full"=>"Democratic Party",
300
+ "office_full"=>"House",
301
+ "candidate_id"=>"H8IL16062",
302
+ "name"=>"ABBOUD, ROBERT",
303
+ "cycles"=>[2008]}]}
@@ -0,0 +1,3 @@
1
+ {"error"=>
2
+ {"code"=>"OVER_RATE_LIMIT",
3
+ "message"=>"You have exceeded your rate limit. Try again later or contact us at https://api.open.fec.gov/contact for assistance"}}
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ module OpenFecApi
4
+ RSpec.describe Client do
5
+ describe '#candidates' do
6
+ context 'when configured' do
7
+ before do
8
+ @client = OpenFecApi::Client.new(ENV["OPEN_FEC_API_KEY"])
9
+ end
10
+
11
+ def request_and_print(options)
12
+ response = @client.candidates(options)
13
+ pp response.summary
14
+ return response
15
+ end
16
+
17
+ it "is configured" do
18
+ expect(@client.configured?)
19
+ end
20
+
21
+ it "returns responses" do
22
+ response = @client.candidates
23
+ expect(response).to be_kind_of(OpenFecApi::Response)
24
+ end
25
+
26
+ it "accepts pagination options and avoids rate limits" do
27
+ options = {:page => 1, :per_page => 100}
28
+ response = request_and_print(options)
29
+ expect(response.results_count).to eql(100)
30
+ while response.page < response.pages && response.page < 5 do
31
+ options.merge!({:page => response.page + 1})
32
+ response = request_and_print(options)
33
+ end
34
+ success = true
35
+ expect(success)
36
+ end
37
+
38
+ it "accepts endpoint-specific options" do
39
+ options = {:party => "DEM"}
40
+ response = request_and_print(options)
41
+ expect(response.results.map{|c| c["party"]}.uniq).to eql(["DEM"])
42
+ end
43
+
44
+ it "prevents unrecognized params from being requested" do
45
+ unrecognized_params = {:hair_color => "brown"}
46
+ response = request_and_print(unrecognized_params)
47
+ recognized_params = response.request_query
48
+ expect(!recognized_params.keys.include?("hair_color"))
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,100 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'open_fec_api'
3
+ require 'pry'
4
+
5
+ # This file was generated by the `rspec --init` command. Conventionally, all
6
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
7
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
8
+ # this file to always be loaded, without a need to explicitly require it in any
9
+ # files.
10
+ #
11
+ # Given that it is always loaded, you are encouraged to keep this file as
12
+ # light-weight as possible. Requiring heavyweight dependencies from this file
13
+ # will add to the boot time of your test suite on EVERY test run, even for an
14
+ # individual file that may not need all of that loaded. Instead, consider making
15
+ # a separate helper file that requires the additional dependencies and performs
16
+ # the additional setup, and require it from the spec files that actually need
17
+ # it.
18
+ #
19
+ # The `.rspec` file also contains a few flags that are not defaults but that
20
+ # users commonly want.
21
+ #
22
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
23
+ RSpec.configure do |config|
24
+ # rspec-expectations config goes here. You can use an alternate
25
+ # assertion/expectation library such as wrong or the stdlib/minitest
26
+ # assertions if you prefer.
27
+ config.expect_with :rspec do |expectations|
28
+ # This option will default to `true` in RSpec 4. It makes the `description`
29
+ # and `failure_message` of custom matchers include text for helper methods
30
+ # defined using `chain`, e.g.:
31
+ # be_bigger_than(2).and_smaller_than(4).description
32
+ # # => "be bigger than 2 and smaller than 4"
33
+ # ...rather than:
34
+ # # => "be bigger than 2"
35
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
36
+ end
37
+
38
+ # rspec-mocks config goes here. You can use an alternate test double
39
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
40
+ config.mock_with :rspec do |mocks|
41
+ # Prevents you from mocking or stubbing a method that does not exist on
42
+ # a real object. This is generally recommended, and will default to
43
+ # `true` in RSpec 4.
44
+ mocks.verify_partial_doubles = true
45
+ end
46
+
47
+ # The settings below are suggested to provide a good initial experience
48
+ # with RSpec, but feel free to customize to your heart's content.
49
+ =begin
50
+ # These two settings work together to allow you to limit a spec run
51
+ # to individual examples or groups you care about by tagging them with
52
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
53
+ # get run.
54
+ config.filter_run :focus
55
+ config.run_all_when_everything_filtered = true
56
+
57
+ # Allows RSpec to persist some state between runs in order to support
58
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
59
+ # you configure your source control system to ignore this file.
60
+ config.example_status_persistence_file_path = "spec/examples.txt"
61
+
62
+ # Limits the available syntax to the non-monkey patched syntax that is
63
+ # recommended. For more details, see:
64
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
65
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
66
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
67
+ config.disable_monkey_patching!
68
+
69
+ # This setting enables warnings. It's recommended, but in some cases may
70
+ # be too noisy due to issues in dependencies.
71
+ config.warnings = true
72
+
73
+ # Many RSpec users commonly either run the entire suite or an individual
74
+ # file, and it's useful to allow more verbose output when running an
75
+ # individual spec file.
76
+ if config.files_to_run.one?
77
+ # Use the documentation formatter for detailed output,
78
+ # unless a formatter has already been configured
79
+ # (e.g. via a command-line flag).
80
+ config.default_formatter = 'doc'
81
+ end
82
+
83
+ # Print the 10 slowest examples and example groups at the
84
+ # end of the spec run, to help surface which specs are running
85
+ # particularly slow.
86
+ config.profile_examples = 10
87
+
88
+ # Run specs in random order to surface order dependencies. If you find an
89
+ # order dependency and want to debug it, you can fix the order by providing
90
+ # the seed, which is printed after each run.
91
+ # --seed 1234
92
+ config.order = :random
93
+
94
+ # Seed global randomization in this process using the `--seed` CLI option.
95
+ # Setting this allows you to use `--seed` to deterministically reproduce
96
+ # test failures related to randomization by passing the same `--seed` value
97
+ # as the one that triggered the failure.
98
+ Kernel.srand config.seed
99
+ =end
100
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: open_fec_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - MJ Rossetti (@s2t2)
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.10'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.10'
69
+ - !ruby/object:Gem::Dependency
70
+ name: httparty
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.13'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.13'
83
+ description: A ruby wrapper for 18F's [Open FEC (Federal Elections Commission) API](https://api.open.fec.gov/).
84
+ email:
85
+ - s2t2mail@gmail.com
86
+ executables:
87
+ - console
88
+ - setup
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - ".gitignore"
93
+ - ".rspec"
94
+ - CREDITS.md
95
+ - ENDPOINTS.md
96
+ - Gemfile
97
+ - LICENSE.txt
98
+ - README.md
99
+ - Rakefile
100
+ - bin/console
101
+ - bin/setup
102
+ - lib/open_fec_api.rb
103
+ - lib/open_fec_api/client.rb
104
+ - lib/open_fec_api/response.rb
105
+ - lib/open_fec_api/version.rb
106
+ - open_fec_api.gemspec
107
+ - reponse_examples/candidates.rb
108
+ - reponse_examples/over_rate_limit.rb
109
+ - spec/client_spec.rb
110
+ - spec/spec_helper.rb
111
+ homepage: https://github.com/debate-watch/open_fec_api/
112
+ licenses:
113
+ - MIT
114
+ metadata: {}
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubyforge_project:
131
+ rubygems_version: 2.4.5
132
+ signing_key:
133
+ specification_version: 4
134
+ summary: A ruby wrapper for 18F's [Open FEC (Federal Elections Commission) API](https://api.open.fec.gov/).
135
+ test_files:
136
+ - spec/client_spec.rb
137
+ - spec/spec_helper.rb