lda_gov 0.1.0

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
+ SHA256:
3
+ metadata.gz: 6b11b53da03541ac152c2f82a616f5d148f25df35746066d8407abcaeed0ec2a
4
+ data.tar.gz: eed3157d2c8714c4c218652ecc727ecd4c399208a19054063a9ba75dbb4cf626
5
+ SHA512:
6
+ metadata.gz: c442d0857cbe0ce7da7e5abc3bc2971ff06c014f21bcde21c5a85a9de52d531187d81b3f0e4556cc98c73b046c961acf3cc3db771f7243ec10e9ab5c7817d671
7
+ data.tar.gz: 96c78265857b7b05a135806c09e8c60826575d94cf7b6e963a09e3b538ab9664d01936a76bd11ee94e264019c7d11d038b7b9722b9bf0136d8a43c564cfbc7d3
data/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ # Changelog
2
+
3
+ ## [0.1.0] - 2025-03-13
4
+
5
+ ### Added
6
+ - Initial release
7
+ - Filings resource (list, find, quarterly, by_registrant_and_client, by_issue)
8
+ - Contributions resource (list, find)
9
+ - Registrants resource (list, find)
10
+ - Clients resource (list, find)
11
+ - Lobbyists resource (list, find)
12
+ - Constants resource (filing_types, lobbying_issues, government_entities, states, countries, contribution_types)
13
+ - Django REST Framework pagination support
14
+ - Token-based authentication (optional)
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Jack Killilea
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,228 @@
1
+ # LDA Gov
2
+
3
+ A Ruby client for the [LDA.gov](https://lda.gov) Lobbying Disclosure Act API. Access lobbying filings, contributions, registrants, clients, lobbyists, and reference data.
4
+
5
+ No API key required for basic access (15 req/min anonymous). Optional API key available at [lda.gov/api](https://lda.gov/api/) for higher rate limits.
6
+
7
+ ## Installation
8
+
9
+ Add to your Gemfile:
10
+
11
+ ```ruby
12
+ gem 'lda_gov'
13
+ ```
14
+
15
+ Then `bundle install`, or install directly:
16
+
17
+ ```bash
18
+ gem install lda_gov
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ```ruby
24
+ require 'lda_gov'
25
+
26
+ # Search recent lobbying filings
27
+ filings = LDA.filings.list(filing_year: 2024)
28
+ filings.each { |f| puts f['filing_uuid'] }
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ### Filings
34
+
35
+ ```ruby
36
+ # List filings with filters
37
+ filings = LDA.filings.list(filing_year: 2024, filing_period: 'Q1')
38
+
39
+ # Find a specific filing
40
+ filing = LDA.filings.find('some-filing-uuid')
41
+
42
+ # Quarterly filings
43
+ LDA.filings.quarterly(year: 2024, quarter: 1)
44
+
45
+ # Filter by registrant and client
46
+ LDA.filings.by_registrant_and_client(
47
+ registrant_name: 'Acme Lobbying',
48
+ client_name: 'Big Corp'
49
+ )
50
+
51
+ # Filter by lobbying issue
52
+ LDA.filings.by_issue(issue_code: 'HCR')
53
+ ```
54
+
55
+ ### Contributions
56
+
57
+ ```ruby
58
+ contributions = LDA.contributions.list
59
+ contribution = LDA.contributions.find(123)
60
+ ```
61
+
62
+ ### Registrants
63
+
64
+ ```ruby
65
+ registrants = LDA.registrants.list
66
+ registrant = LDA.registrants.find(456)
67
+ ```
68
+
69
+ ### Clients
70
+
71
+ ```ruby
72
+ clients = LDA.clients.list
73
+ client = LDA.clients.find(789)
74
+ ```
75
+
76
+ ### Lobbyists
77
+
78
+ ```ruby
79
+ lobbyists = LDA.lobbyists.list
80
+ lobbyist = LDA.lobbyists.find(101)
81
+ ```
82
+
83
+ ### Constants (Reference Data)
84
+
85
+ ```ruby
86
+ LDA.constants.filing_types
87
+ LDA.constants.lobbying_issues
88
+ LDA.constants.government_entities
89
+ LDA.constants.states
90
+ LDA.constants.countries
91
+ LDA.constants.contribution_types
92
+ ```
93
+
94
+ ### Pagination
95
+
96
+ Iterate page by page with `each_page`:
97
+
98
+ ```ruby
99
+ LDA.filings.each_page(filing_year: 2024) do |page|
100
+ page.each { |filing| process(filing) }
101
+ end
102
+ ```
103
+
104
+ Fetch all results at once:
105
+
106
+ ```ruby
107
+ all_filings = LDA.filings.all_filings(filing_year: 2024)
108
+ all_filings.size # => total count
109
+
110
+ # Available on all resources
111
+ LDA.contributions.all_contributions
112
+ LDA.registrants.all_registrants
113
+ LDA.clients.all_clients
114
+ LDA.lobbyists.all_lobbyists
115
+ ```
116
+
117
+ ## Configuration
118
+
119
+ ```ruby
120
+ LDA.configure do |config|
121
+ config.api_key = ENV['LDA_API_KEY'] # Optional — enables higher rate limits
122
+ config.timeout = 60 # Request timeout in seconds (default: 30)
123
+ config.retries = 5 # Max retries on 429/5xx (default: 3)
124
+ config.logger = Logger.new($stdout) # Optional request logging
125
+ end
126
+ ```
127
+
128
+ ### Rails Initializer
129
+
130
+ ```ruby
131
+ # config/initializers/lda_gov.rb
132
+ LDA.configure do |config|
133
+ config.api_key = ENV['LDA_API_KEY']
134
+ config.timeout = 30
135
+ config.retries = 3
136
+ config.logger = Rails.logger if Rails.env.development?
137
+ end
138
+ ```
139
+
140
+ ### Per-Instance Client
141
+
142
+ For different configurations in the same process:
143
+
144
+ ```ruby
145
+ client = LDA::Client.new(custom_config)
146
+ client.filings.list(filing_year: 2024)
147
+ client.contributions.list
148
+ ```
149
+
150
+ ## Response Object
151
+
152
+ All API calls return an `LDA::Response`:
153
+
154
+ ```ruby
155
+ response = LDA.filings.list(filing_year: 2024)
156
+
157
+ response.results # Array of result hashes
158
+ response.each { |r| ... } # Enumerable — supports map, select, first, etc.
159
+ response.size # Results on this page
160
+ response.empty? # No results?
161
+ response.count # Total matching records (from API envelope)
162
+ response.total_count # Alias for count
163
+ response.next_page? # More pages available?
164
+ response.next_page_number # Page number from next URL
165
+ response.success? # HTTP 2xx?
166
+
167
+ response['key'] # Direct hash access
168
+ response.dig('nested', 'key')
169
+ response.raw # Raw parsed JSON body
170
+ response.to_h # Always returns a Hash
171
+ response.to_json # JSON string
172
+ ```
173
+
174
+ ## Error Handling
175
+
176
+ ```ruby
177
+ begin
178
+ LDA.filings.find('INVALID')
179
+ rescue LDA::BadRequestError => e
180
+ puts "Bad request: #{e.status} — #{e.body}"
181
+ rescue LDA::NotFoundError => e
182
+ puts "Not found: #{e.status}"
183
+ rescue LDA::UnauthorizedError => e
184
+ puts "Unauthorized: #{e.body}"
185
+ rescue LDA::RateLimitError
186
+ puts 'Rate limited — retries exhausted'
187
+ rescue LDA::ServerError => e
188
+ puts "Server error: #{e.status}"
189
+ rescue LDA::ConnectionError => e
190
+ puts "Network issue: #{e.message}"
191
+ end
192
+ ```
193
+
194
+ Error hierarchy:
195
+
196
+ ```
197
+ LDA::Error
198
+ ├── ClientError
199
+ │ ├── BadRequestError (400)
200
+ │ ├── NotFoundError (404)
201
+ │ ├── UnauthorizedError (401)
202
+ │ ├── UnprocessableEntityError (422)
203
+ │ └── RateLimitError (429)
204
+ ├── ServerError (500+)
205
+ └── ConnectionError (timeouts, DNS, refused)
206
+ ```
207
+
208
+ All `ClientError` and `ServerError` subclasses expose `.status` and `.body`.
209
+
210
+ ## Development
211
+
212
+ ```bash
213
+ git clone https://github.com/xjackk/lda_gov.git
214
+ cd lda_gov
215
+ bundle install
216
+
217
+ bundle exec rspec # Run tests
218
+ bundle exec rubocop # Lint
219
+ ```
220
+
221
+ ## Requirements
222
+
223
+ - Ruby >= 3.1
224
+ - Faraday ~> 2.0
225
+
226
+ ## License
227
+
228
+ MIT License. See [LICENSE.txt](LICENSE.txt).
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'faraday/retry'
5
+
6
+ module LDA
7
+ class Client
8
+ MAX_PAGES = 100
9
+
10
+ attr_reader :config
11
+
12
+ def initialize(config = LDA.configuration)
13
+ @config = config
14
+ end
15
+
16
+ def connection
17
+ @connection ||= build_connection
18
+ end
19
+
20
+ def get(path, params = {})
21
+ response = connection.get(path, compact_params(params))
22
+ handle_response(response)
23
+ rescue Faraday::TimeoutError => e
24
+ raise ConnectionError, "Request timed out: #{e.message}"
25
+ rescue Faraday::ConnectionFailed => e
26
+ raise ConnectionError, "Connection failed: #{e.message}"
27
+ end
28
+
29
+ def paginate(path, params = {})
30
+ page = 1
31
+ iterations = 0
32
+ loop do
33
+ iterations += 1
34
+ response = get(path, params.merge(page: page))
35
+ yield response
36
+ break if response.empty?
37
+ break unless response.next_page?
38
+
39
+ page = response.next_page_number
40
+ break if page.nil?
41
+ break if iterations >= MAX_PAGES
42
+ end
43
+ end
44
+
45
+ def all(path, params = {})
46
+ records = []
47
+ paginate(path, params) { |response| records.concat(response.results) }
48
+ records
49
+ end
50
+
51
+ # Resource accessors
52
+
53
+ def filings
54
+ @filings ||= Resources::Filings.new(self)
55
+ end
56
+
57
+ def contributions
58
+ @contributions ||= Resources::Contributions.new(self)
59
+ end
60
+
61
+ def registrants
62
+ @registrants ||= Resources::Registrants.new(self)
63
+ end
64
+
65
+ def clients
66
+ @clients ||= Resources::Clients.new(self)
67
+ end
68
+
69
+ def lobbyists
70
+ @lobbyists ||= Resources::Lobbyists.new(self)
71
+ end
72
+
73
+ def constants
74
+ @constants ||= Resources::Constants.new(self)
75
+ end
76
+
77
+ def inspect
78
+ "#<#{self.class} base_url=#{config.base_url.inspect} timeout=#{config.timeout}>"
79
+ end
80
+
81
+ private
82
+
83
+ def build_connection # rubocop:disable Metrics/MethodLength
84
+ Faraday.new(url: config.base_url) do |f|
85
+ f.options.timeout = config.timeout
86
+ f.options.open_timeout = 10
87
+
88
+ f.headers['User-Agent'] = "lda_gov-rb/#{LDA::VERSION}"
89
+ f.headers['Authorization'] = "Token #{config.api_key}" if config.api_key
90
+
91
+ f.request :json
92
+ f.response :json, content_type: /\bjson$/
93
+
94
+ f.request :retry,
95
+ max: config.retries,
96
+ interval: 0.5,
97
+ interval_randomness: 0.5,
98
+ backoff_factor: 2,
99
+ retry_statuses: [429, 500, 502, 503, 504],
100
+ exceptions: [
101
+ Faraday::TimeoutError,
102
+ Faraday::ConnectionFailed,
103
+ Faraday::RetriableResponse
104
+ ]
105
+
106
+ f.response :logger, config.logger if config.logger
107
+ f.adapter config.adapter
108
+ end
109
+ end
110
+
111
+ def compact_params(params)
112
+ params.compact.transform_keys(&:to_s)
113
+ end
114
+
115
+ def handle_response(response)
116
+ case response.status
117
+ when 200..299
118
+ Response.new(response.body, response.status)
119
+ when 400
120
+ raise BadRequestError.new(status: response.status, body: response.body)
121
+ when 401
122
+ raise UnauthorizedError.new(status: response.status, body: response.body)
123
+ when 404
124
+ raise NotFoundError.new(status: response.status, body: response.body)
125
+ when 422
126
+ raise UnprocessableEntityError.new(status: response.status, body: response.body)
127
+ when 429
128
+ raise RateLimitError.new(status: 429, body: response.body)
129
+ when 400..499
130
+ raise ClientError.new(status: response.status, body: response.body)
131
+ when 500..599
132
+ raise ServerError.new(status: response.status, body: response.body)
133
+ else
134
+ raise Error, "Unexpected HTTP status: #{response.status}"
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LDA
4
+ class Configuration
5
+ DEFAULT_BASE_URL = 'https://lda.gov/api/v1/'
6
+ DEFAULT_TIMEOUT = 30
7
+ DEFAULT_RETRIES = 3
8
+
9
+ attr_accessor :base_url, :timeout, :retries, :logger, :adapter, :api_key
10
+
11
+ def initialize
12
+ @base_url = DEFAULT_BASE_URL
13
+ @timeout = DEFAULT_TIMEOUT
14
+ @retries = DEFAULT_RETRIES
15
+ @logger = nil
16
+ @adapter = Faraday.default_adapter
17
+ @api_key = ENV.fetch('LDA_API_KEY', nil)
18
+ end
19
+
20
+ def to_h
21
+ {
22
+ base_url: base_url,
23
+ timeout: timeout,
24
+ retries: retries,
25
+ logger: logger,
26
+ adapter: adapter,
27
+ api_key: api_key
28
+ }
29
+ end
30
+
31
+ def inspect
32
+ "#<#{self.class} base_url=#{base_url.inspect} timeout=#{timeout} retries=#{retries}>"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LDA
4
+ class Error < StandardError; end
5
+
6
+ class ClientError < Error
7
+ attr_reader :status, :body
8
+
9
+ def initialize(message = nil, status: nil, body: nil)
10
+ @status = status
11
+ @body = body
12
+ super(message || "HTTP #{status}: #{body}")
13
+ end
14
+ end
15
+
16
+ class BadRequestError < ClientError; end
17
+ class NotFoundError < ClientError; end
18
+ class UnauthorizedError < ClientError; end
19
+ class RateLimitError < ClientError; end
20
+ class UnprocessableEntityError < ClientError; end
21
+
22
+ class ServerError < Error
23
+ attr_reader :status, :body
24
+
25
+ def initialize(message = nil, status: nil, body: nil)
26
+ @status = status
27
+ @body = body
28
+ super(message || "Server error HTTP #{status}")
29
+ end
30
+ end
31
+
32
+ class ConnectionError < Error; end
33
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LDA
4
+ module Resources
5
+ class Base
6
+ def initialize(client = LDA.client)
7
+ @client = client
8
+ end
9
+
10
+ private
11
+
12
+ attr_reader :client
13
+
14
+ def get(path, params = {})
15
+ client.get(path, params)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LDA
4
+ module Resources
5
+ class Clients < Base
6
+ def list(**params)
7
+ get('clients/', params)
8
+ end
9
+
10
+ def find(client_id)
11
+ get("clients/#{client_id}/")
12
+ end
13
+
14
+ def each_page(**params, &)
15
+ client.paginate('clients/', params, &)
16
+ end
17
+
18
+ def all_clients(**params)
19
+ client.all('clients/', params)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LDA
4
+ module Resources
5
+ class Constants < Base
6
+ def filing_types
7
+ get('constants/filing/filingtypes/')
8
+ end
9
+
10
+ def lobbying_issues
11
+ get('constants/filing/lobbyingissuecodes/')
12
+ end
13
+
14
+ def government_entities
15
+ get('constants/filing/governmententities/')
16
+ end
17
+
18
+ def states
19
+ get('constants/general/states/')
20
+ end
21
+
22
+ def countries
23
+ get('constants/general/countries/')
24
+ end
25
+
26
+ def contribution_types
27
+ get('constants/contribution/contributiontypes/')
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LDA
4
+ module Resources
5
+ class Contributions < Base
6
+ def list(**params)
7
+ get('contributions/', params)
8
+ end
9
+
10
+ def find(contribution_id)
11
+ get("contributions/#{contribution_id}/")
12
+ end
13
+
14
+ def each_page(**params, &)
15
+ client.paginate('contributions/', params, &)
16
+ end
17
+
18
+ def all_contributions(**params)
19
+ client.all('contributions/', params)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LDA
4
+ module Resources
5
+ class Filings < Base
6
+ def list(**params)
7
+ get('filings/', params)
8
+ end
9
+
10
+ def find(filing_uuid)
11
+ get("filings/#{filing_uuid}/")
12
+ end
13
+
14
+ def each_page(**params, &)
15
+ client.paginate('filings/', params, &)
16
+ end
17
+
18
+ def all_filings(**params)
19
+ client.all('filings/', params)
20
+ end
21
+
22
+ def quarterly(year:, quarter:, **params)
23
+ get('filings/', params.merge(filing_year: year, filing_period: "Q#{quarter}"))
24
+ end
25
+
26
+ def by_registrant_and_client(registrant_name:, client_name:, **params)
27
+ get('filings/', params.merge(registrant_name: registrant_name, client_name: client_name))
28
+ end
29
+
30
+ def by_issue(issue_code:, **params)
31
+ get('filings/', params.merge(lobbying_issue: issue_code))
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LDA
4
+ module Resources
5
+ class Lobbyists < Base
6
+ def list(**params)
7
+ get('lobbyists/', params)
8
+ end
9
+
10
+ def find(lobbyist_id)
11
+ get("lobbyists/#{lobbyist_id}/")
12
+ end
13
+
14
+ def each_page(**params, &)
15
+ client.paginate('lobbyists/', params, &)
16
+ end
17
+
18
+ def all_lobbyists(**params)
19
+ client.all('lobbyists/', params)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LDA
4
+ module Resources
5
+ class Registrants < Base
6
+ def list(**params)
7
+ get('registrants/', params)
8
+ end
9
+
10
+ def find(registrant_id)
11
+ get("registrants/#{registrant_id}/")
12
+ end
13
+
14
+ def each_page(**params, &)
15
+ client.paginate('registrants/', params, &)
16
+ end
17
+
18
+ def all_registrants(**params)
19
+ client.all('registrants/', params)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LDA
4
+ class Response
5
+ include Enumerable
6
+
7
+ attr_reader :raw, :status
8
+
9
+ def initialize(body, status)
10
+ @raw = body
11
+ @status = status
12
+ end
13
+
14
+ def each(&)
15
+ results.each(&)
16
+ end
17
+
18
+ def results
19
+ return raw if raw.is_a?(Array)
20
+
21
+ raw['results'] || []
22
+ end
23
+
24
+ def size
25
+ results.size
26
+ end
27
+ alias length size
28
+
29
+ def empty?
30
+ results.empty?
31
+ end
32
+
33
+ def count
34
+ return raw['count'] if raw.is_a?(Hash) && raw.key?('count')
35
+
36
+ size
37
+ end
38
+ alias total_count count
39
+
40
+ def next_url
41
+ return nil unless raw.is_a?(Hash)
42
+
43
+ raw['next']
44
+ end
45
+
46
+ def previous_url
47
+ return nil unless raw.is_a?(Hash)
48
+
49
+ raw['previous']
50
+ end
51
+
52
+ def next_page?
53
+ !next_url.nil?
54
+ end
55
+
56
+ def next_page_number
57
+ return nil unless next_url
58
+
59
+ uri = URI.parse(next_url)
60
+ params = URI.decode_www_form(uri.query || '')
61
+ page_param = params.find { |k, _| k == 'page' }
62
+ page_param ? page_param[1].to_i : nil
63
+ rescue URI::InvalidURIError
64
+ nil
65
+ end
66
+
67
+ def [](key)
68
+ return nil unless raw.is_a?(Hash)
69
+
70
+ raw[key]
71
+ end
72
+
73
+ def dig(*keys)
74
+ return nil unless raw.is_a?(Hash)
75
+
76
+ raw.dig(*keys)
77
+ end
78
+
79
+ def to_h
80
+ raw.is_a?(Hash) ? raw : { 'results' => raw }
81
+ end
82
+
83
+ def to_json(*)
84
+ to_h.to_json
85
+ end
86
+
87
+ def success?
88
+ status >= 200 && status < 300
89
+ end
90
+
91
+ def inspect
92
+ "#<#{self.class} status=#{status} size=#{size} count=#{count} next_page?=#{next_page?}>"
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LDA
4
+ VERSION = '0.1.0'
5
+ end
data/lib/lda_gov.rb ADDED
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lda_gov/version'
4
+ require 'lda_gov/configuration'
5
+ require 'lda_gov/error'
6
+ require 'lda_gov/response'
7
+ require 'lda_gov/client'
8
+ require 'lda_gov/resources/base'
9
+ require 'lda_gov/resources/filings'
10
+ require 'lda_gov/resources/contributions'
11
+ require 'lda_gov/resources/registrants'
12
+ require 'lda_gov/resources/clients'
13
+ require 'lda_gov/resources/lobbyists'
14
+ require 'lda_gov/resources/constants'
15
+
16
+ module LDA
17
+ @mutex = Mutex.new
18
+
19
+ class << self
20
+ def configuration
21
+ @mutex.synchronize { @configuration ||= Configuration.new }
22
+ end
23
+
24
+ def configure
25
+ @mutex.synchronize { yield(@configuration ||= Configuration.new) }
26
+ end
27
+
28
+ def reset!
29
+ @mutex.synchronize do
30
+ @configuration = Configuration.new
31
+ @client = nil
32
+ end
33
+ end
34
+
35
+ def client
36
+ @mutex.synchronize { @client ||= Client.new(@configuration || Configuration.new) }
37
+ end
38
+
39
+ def filings
40
+ Resources::Filings.new(client)
41
+ end
42
+
43
+ def contributions
44
+ Resources::Contributions.new(client)
45
+ end
46
+
47
+ def registrants
48
+ Resources::Registrants.new(client)
49
+ end
50
+
51
+ def clients
52
+ Resources::Clients.new(client)
53
+ end
54
+
55
+ def lobbyists
56
+ Resources::Lobbyists.new(client)
57
+ end
58
+
59
+ def constants
60
+ Resources::Constants.new(client)
61
+ end
62
+ end
63
+ end
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lda_gov
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jack Killilea
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-03-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday-retry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.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.13'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.13'
55
+ - !ruby/object:Gem::Dependency
56
+ name: vcr
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '6.2'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '6.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: webmock
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.23'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.23'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.65'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.65'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.22'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.22'
125
+ description: |
126
+ A Ruby client for the Lobbying Disclosure Act (LDA) API at lda.gov.
127
+ Access lobbying filings, registrants, clients, lobbyists, and contribution reports.
128
+ email:
129
+ - xkillilea@gmail.com
130
+ executables: []
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - CHANGELOG.md
135
+ - LICENSE.txt
136
+ - README.md
137
+ - lib/lda_gov.rb
138
+ - lib/lda_gov/client.rb
139
+ - lib/lda_gov/configuration.rb
140
+ - lib/lda_gov/error.rb
141
+ - lib/lda_gov/resources/base.rb
142
+ - lib/lda_gov/resources/clients.rb
143
+ - lib/lda_gov/resources/constants.rb
144
+ - lib/lda_gov/resources/contributions.rb
145
+ - lib/lda_gov/resources/filings.rb
146
+ - lib/lda_gov/resources/lobbyists.rb
147
+ - lib/lda_gov/resources/registrants.rb
148
+ - lib/lda_gov/response.rb
149
+ - lib/lda_gov/version.rb
150
+ homepage: https://github.com/xjackk/lda_gov
151
+ licenses:
152
+ - MIT
153
+ metadata:
154
+ homepage_uri: https://github.com/xjackk/lda_gov
155
+ source_code_uri: https://github.com/xjackk/lda_gov/tree/main/lib
156
+ changelog_uri: https://github.com/xjackk/lda_gov/blob/main/CHANGELOG.md
157
+ bug_tracker_uri: https://github.com/xjackk/lda_gov/issues
158
+ rubygems_mfa_required: 'true'
159
+ post_install_message:
160
+ rdoc_options: []
161
+ require_paths:
162
+ - lib
163
+ required_ruby_version: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: 3.1.0
168
+ required_rubygems_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ requirements: []
174
+ rubygems_version: 3.5.11
175
+ signing_key:
176
+ specification_version: 4
177
+ summary: Ruby client for the LDA.gov lobbying disclosure API
178
+ test_files: []