ecfr 1.1.6 → 1.1.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a590084cfafab933fe619638898f62eeacb37aa183af6174cb47406df26d4c5
4
- data.tar.gz: '095a4f5623deb58978f0c00941fa3c89d2f3bb2ef35bf9dff913e0648fb77af6'
3
+ metadata.gz: 13cc40dfeeb370c7a81be66f37bdf9f23cf4dfbb936465ce4c12b1ab88094deb
4
+ data.tar.gz: 6a80785bbd1e428f93f841401e678c1776f9b344a33e554738759937079eca06
5
5
  SHA512:
6
- metadata.gz: 5fda252dfcbf05c0922c9c06167d2f6a99b833d60a251544a2e3cecacf990378e629b793f96da9744caddcb2819f7f417736c6ebe432174c25530bad452f3097
7
- data.tar.gz: 44376ecbcfe743b0a105ae3d146a5c7abe01c304521bf140640dd0b7af67d723457df1a102f32d65f4596670d317ad1166d81130d1d456791d2e8b2d743bdd12
6
+ metadata.gz: 11198b0d371f4c2a49ed939af2db6f7b781809d107b1e17ef586b03676d21157f739b92e4ae681fe70112580650a7476b39cbdb0ea6e06c51067cc79943510dd
7
+ data.tar.gz: 3dddd1f095d6ea3015e8d4f9dfbb0d1fe9ab29cfd426274c6d646fa86bc02217c45545d2105682cf0e88ee33779b22f344c895feab0c55e2be0d036c9873332a
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## [Unreleased]
2
+
3
+ ## [1.1.7] - 2026-06-29
4
+ ### Additions
5
+ - Add authority endpoint
6
+ - Add topic endpoint
7
+ - Support for singular and plural change types (4552a8d5)
8
+
1
9
  ## [1.1.6] - 2025-09-23
2
10
  ### Fixes
3
11
  - Add missing Gemfile lock changes
data/Gemfile.lock CHANGED
@@ -11,94 +11,108 @@ PATH
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- activemodel (7.2.1)
15
- activesupport (= 7.2.1)
16
- activesupport (7.2.1)
14
+ activemodel (8.1.2)
15
+ activesupport (= 8.1.2)
16
+ activesupport (8.1.2)
17
17
  base64
18
18
  bigdecimal
19
19
  concurrent-ruby (~> 1.0, >= 1.3.1)
20
20
  connection_pool (>= 2.2.5)
21
21
  drb
22
22
  i18n (>= 1.6, < 2)
23
+ json
23
24
  logger (>= 1.4.2)
24
25
  minitest (>= 5.1)
25
26
  securerandom (>= 0.3)
26
27
  tzinfo (~> 2.0, >= 2.0.5)
28
+ uri (>= 0.13.1)
27
29
  ast (2.4.3)
28
- base64 (0.2.0)
29
- bigdecimal (3.1.9)
30
+ base64 (0.3.0)
31
+ bigdecimal (4.0.1)
30
32
  coderay (1.1.3)
31
- concurrent-ruby (1.3.5)
32
- connection_pool (2.5.3)
33
+ concurrent-ruby (1.3.6)
34
+ connection_pool (3.0.2)
33
35
  diff-lcs (1.6.2)
34
- drb (2.2.1)
35
- ethon (0.16.0)
36
+ drb (2.2.3)
37
+ ethon (0.18.0)
36
38
  ffi (>= 1.15.0)
37
- factory_bot (6.5.1)
39
+ logger
40
+ factory_bot (6.5.6)
38
41
  activesupport (>= 6.1.0)
39
- faraday (2.13.1)
42
+ faraday (2.14.0)
40
43
  faraday-net_http (>= 2.0, < 3.5)
41
44
  json
42
45
  logger
43
- faraday-net_http (3.4.0)
44
- net-http (>= 0.5.0)
45
- faraday-net_http_persistent (2.3.0)
46
+ faraday-net_http (3.4.2)
47
+ net-http (~> 0.5)
48
+ faraday-net_http_persistent (2.3.1)
46
49
  faraday (~> 2.5)
47
50
  net-http-persistent (>= 4.0.4, < 5)
48
51
  faraday-typhoeus (1.1.0)
49
52
  faraday (~> 2.0)
50
53
  typhoeus (~> 1.4)
51
- ffi (1.17.2-arm64-darwin)
52
- ffi (1.17.2-x86_64-linux-gnu)
53
- i18n (1.14.7)
54
+ ffi (1.17.3-arm64-darwin)
55
+ ffi (1.17.3-x86_64-darwin)
56
+ ffi (1.17.3-x86_64-linux-gnu)
57
+ ffi (1.17.3-x86_64-linux-musl)
58
+ i18n (1.14.8)
54
59
  concurrent-ruby (~> 1.0)
55
- json (2.12.0)
60
+ io-console (0.8.2)
61
+ json (2.18.0)
56
62
  language_server-protocol (3.17.0.5)
57
63
  lint_roller (1.1.0)
58
64
  logger (1.7.0)
59
65
  method_source (1.1.0)
60
- minitest (5.25.5)
61
- net-http (0.6.0)
62
- uri
63
- net-http-persistent (4.0.5)
64
- connection_pool (~> 2.2)
65
- nokogiri (1.18.8-arm64-darwin)
66
+ minitest (6.0.1)
67
+ prism (~> 1.5)
68
+ net-http (0.9.1)
69
+ uri (>= 0.11.1)
70
+ net-http-persistent (4.0.8)
71
+ connection_pool (>= 2.2.4, < 4)
72
+ nokogiri (1.19.0-arm64-darwin)
73
+ racc (~> 1.4)
74
+ nokogiri (1.19.0-x86_64-darwin)
75
+ racc (~> 1.4)
76
+ nokogiri (1.19.0-x86_64-linux-gnu)
66
77
  racc (~> 1.4)
67
- nokogiri (1.18.8-x86_64-linux-gnu)
78
+ nokogiri (1.19.0-x86_64-linux-musl)
68
79
  racc (~> 1.4)
69
- ostruct (0.6.1)
80
+ ostruct (0.6.3)
70
81
  parallel (1.27.0)
71
82
  parallel_tests (4.10.1)
72
83
  parallel
73
- parser (3.3.8.0)
84
+ parser (3.3.10.1)
74
85
  ast (~> 2.4.1)
75
86
  racc
76
- prism (1.4.0)
77
- pry (0.15.2)
87
+ prism (1.9.0)
88
+ pry (0.16.0)
78
89
  coderay (~> 1.1)
79
90
  method_source (~> 1.0)
91
+ reline (>= 0.6.0)
80
92
  racc (1.8.1)
81
- rack (3.1.14)
93
+ rack (3.2.4)
82
94
  rainbow (3.1.1)
83
- regexp_parser (2.10.0)
95
+ regexp_parser (2.11.3)
96
+ reline (0.6.3)
97
+ io-console (~> 0.5)
84
98
  request_store (1.7.0)
85
99
  rack (>= 1.4)
86
- rspec (3.13.0)
100
+ rspec (3.13.2)
87
101
  rspec-core (~> 3.13.0)
88
102
  rspec-expectations (~> 3.13.0)
89
103
  rspec-mocks (~> 3.13.0)
90
- rspec-core (3.13.3)
104
+ rspec-core (3.13.6)
91
105
  rspec-support (~> 3.13.0)
92
- rspec-expectations (3.13.4)
106
+ rspec-expectations (3.13.5)
93
107
  diff-lcs (>= 1.2.0, < 2.0)
94
108
  rspec-support (~> 3.13.0)
95
- rspec-mocks (3.13.4)
109
+ rspec-mocks (3.13.7)
96
110
  diff-lcs (>= 1.2.0, < 2.0)
97
111
  rspec-support (~> 3.13.0)
98
- rspec-support (3.13.3)
112
+ rspec-support (3.13.7)
99
113
  rspec_junit_formatter (0.6.0)
100
114
  rspec-core (>= 2, < 4, != 2.12.0)
101
- rubocop (1.75.6)
115
+ rubocop (1.82.1)
102
116
  json (~> 2.3)
103
117
  language_server-protocol (~> 3.17.0.2)
104
118
  lint_roller (~> 1.1.0)
@@ -106,30 +120,30 @@ GEM
106
120
  parser (>= 3.3.0.2)
107
121
  rainbow (>= 2.2.2, < 4.0)
108
122
  regexp_parser (>= 2.9.3, < 3.0)
109
- rubocop-ast (>= 1.44.0, < 2.0)
123
+ rubocop-ast (>= 1.48.0, < 2.0)
110
124
  ruby-progressbar (~> 1.7)
111
125
  unicode-display_width (>= 2.4.0, < 4.0)
112
- rubocop-ast (1.44.1)
126
+ rubocop-ast (1.49.0)
113
127
  parser (>= 3.3.7.2)
114
- prism (~> 1.4)
115
- rubocop-performance (1.25.0)
128
+ prism (~> 1.7)
129
+ rubocop-performance (1.26.1)
116
130
  lint_roller (~> 1.1)
117
131
  rubocop (>= 1.75.0, < 2.0)
118
- rubocop-ast (>= 1.38.0, < 2.0)
132
+ rubocop-ast (>= 1.47.1, < 2.0)
119
133
  ruby-progressbar (1.13.0)
120
134
  securerandom (0.4.1)
121
- standard (1.50.0)
135
+ standard (1.53.0)
122
136
  language_server-protocol (~> 3.17.0.2)
123
137
  lint_roller (~> 1.0)
124
- rubocop (~> 1.75.5)
138
+ rubocop (~> 1.82.0)
125
139
  standard-custom (~> 1.0.0)
126
140
  standard-performance (~> 1.8)
127
141
  standard-custom (1.0.2)
128
142
  lint_roller (~> 1.0)
129
143
  rubocop (~> 1.50)
130
- standard-performance (1.8.0)
144
+ standard-performance (1.9.0)
131
145
  lint_roller (~> 1.1)
132
- rubocop-performance (~> 1.25.0)
146
+ rubocop-performance (~> 1.26.0)
133
147
  turbo_tests (2.2.5)
134
148
  parallel_tests (>= 3.3.0, < 5)
135
149
  rspec (>= 3.10)
@@ -137,15 +151,17 @@ GEM
137
151
  ethon (>= 0.9.0)
138
152
  tzinfo (2.0.6)
139
153
  concurrent-ruby (~> 1.0)
140
- unicode-display_width (3.1.4)
141
- unicode-emoji (~> 4.0, >= 4.0.4)
142
- unicode-emoji (4.0.4)
143
- uri (1.0.3)
144
- yard (0.9.37)
154
+ unicode-display_width (3.2.0)
155
+ unicode-emoji (~> 4.1)
156
+ unicode-emoji (4.2.0)
157
+ uri (1.1.1)
158
+ yard (0.9.38)
145
159
 
146
160
  PLATFORMS
147
161
  arm64-darwin
162
+ x86_64-darwin
148
163
  x86_64-linux-gnu
164
+ x86_64-linux-musl
149
165
 
150
166
  DEPENDENCIES
151
167
  ecfr!
@@ -5,7 +5,6 @@ module Ecfr
5
5
  require_relative "status"
6
6
 
7
7
  require_relative "agency"
8
- require_relative "build"
9
8
  require_relative "ecfr_correction"
10
9
  require_relative "editorial_note"
11
10
  require_relative "ibr_cfr_range"
data/lib/ecfr/base.rb CHANGED
@@ -31,8 +31,8 @@ module Ecfr
31
31
 
32
32
  attr_reader :metadata, :results, :response_status, :request_data
33
33
 
34
- SUPPORTED_ARRAY_ACCESSORS = %i[empty? first last size]
35
- delegate(*SUPPORTED_ARRAY_ACCESSORS, to: :results)
34
+ SUPPORTED_ARRAY_ACCESSORS = %i[each empty? first last size]
35
+ delegate(*SUPPORTED_ARRAY_ACCESSORS, to: :results, allow_nil: true)
36
36
  alias_method :all, :results
37
37
 
38
38
  DEFAULT_OPTIONS = {base: true}
@@ -59,10 +59,6 @@ module Ecfr
59
59
  super(attributes)
60
60
  end
61
61
 
62
- def each
63
- @results.each { |result| yield result }
64
- end
65
-
66
62
  class Metadata
67
63
  include AttributeMethodDefinition
68
64
 
data/lib/ecfr/client.rb CHANGED
@@ -236,6 +236,7 @@ module Ecfr
236
236
 
237
237
  execute do
238
238
  c.get(path, params) do |req|
239
+ yield(req) if block_given?
239
240
  Ecfr.config.request_hook.call(req)
240
241
  end
241
242
  end
@@ -246,6 +247,7 @@ module Ecfr
246
247
 
247
248
  execute do
248
249
  c.post(path, params) do |req|
250
+ yield(req) if block_given?
249
251
  Ecfr.config.request_hook.call(req)
250
252
  end
251
253
  end
@@ -256,6 +258,7 @@ module Ecfr
256
258
 
257
259
  execute do
258
260
  c.delete(path, params) do |req|
261
+ yield(req) if block_given?
259
262
  Ecfr.config.request_hook.call(req)
260
263
  end
261
264
  end
@@ -268,6 +271,7 @@ module Ecfr
268
271
  execute do
269
272
  c.run_request(:purge, path, nil, nil) do |request|
270
273
  request.params.update(params)
274
+ yield(req) if block_given?
271
275
  Ecfr.config.request_hook.call(request)
272
276
  end
273
277
  end
@@ -0,0 +1,122 @@
1
+ module Ecfr
2
+ #
3
+ # Configures the Ecfr gem from a consuming application's settings and
4
+ # credentials. Lives in the gem (rather than each app) so the contract
5
+ # between app config and the gem is defined and validated in one place.
6
+ #
7
+ # Everything the gem can't assume exists is passed in explicitly:
8
+ # - settings: the app's Settings object (duck-typed; dotted/[] access)
9
+ # - env: the app environment, used in the user_agent string
10
+ # - credentials: optional; anything responding to #dig (e.g. Rails credentials)
11
+ #
12
+ # An optional block is yielded the config last, after every settings-derived
13
+ # assignment, so the app can override any value (request hooks, timeouts,
14
+ # urls, ...) without the gem depending on app-level constants.
15
+ #
16
+ class ClientConfiguration
17
+ class InvalidSettings < StandardError; end
18
+ class InvalidCredentials < StandardError; end
19
+
20
+ # Settings with no gem-side default must be present. Everything else has a
21
+ # default in Ecfr::Configuration::CONFIG_DEFAULTS and is therefore optional.
22
+ REQUIRED_SETTINGS = [
23
+ %i[container process],
24
+ %i[container role],
25
+ %i[container hostname]
26
+ ].freeze
27
+
28
+ def self.initialize_for(service, settings, env, credentials = nil)
29
+ validate_settings!(settings)
30
+ validate_credentials!(credentials)
31
+
32
+ Ecfr.configure do |config|
33
+ config.user_agent = [
34
+ "ecfr", service, env,
35
+ dig_setting(settings, :container, :process),
36
+ dig_setting(settings, :container, :role),
37
+ dig_setting(settings, :container, :hostname)
38
+ ].join("-")
39
+
40
+ log_http_requests = dig_setting(settings, :services, :ecfr, :log_http_requests)
41
+ config.log_http_requests = log_http_requests unless log_http_requests.nil?
42
+
43
+ cache_responses = dig_setting(settings, :services, :ecfr, :cache_responses)
44
+ config.cache_responses = cache_responses unless cache_responses.nil?
45
+
46
+ # prefer internal urls when setting up gem
47
+ base_url = dig_setting(settings, :services, :ecfr, :internal_base_url) ||
48
+ dig_setting(settings, :services, :ecfr, :base_url)
49
+ config.base_url = base_url if base_url.present?
50
+
51
+ profile_base_url = dig_setting(settings, :services, :ofr, :profile, :internal_base_url) ||
52
+ dig_setting(settings, :services, :ofr, :profile, :base_url)
53
+ config.ofr_profile_service_base_url = profile_base_url if profile_base_url.present?
54
+
55
+ profile_path = dig_setting(settings, :services, :ofr, :profile, :path)
56
+ config.ofr_profile_service_path = profile_path if profile_path.present?
57
+
58
+ # configure services according to settings
59
+ service_keys = Ecfr.services.filter_map do |service_module|
60
+ # ofr profile is configured separately above
61
+ next if service_module::Base.service_name == "OFR Profile"
62
+
63
+ service_module::Base.service_name.downcase.tr(" ", "_")
64
+ end
65
+
66
+ service_keys.each do |service_key|
67
+ url = dig_setting(settings, :services, :ecfr, service_key, :url)
68
+ config.send(:"#{service_key}_url=", url) if url.present?
69
+
70
+ path = dig_setting(settings, :services, :ecfr, service_key, :path)
71
+ config.send(:"#{service_key}_path=", path) if path.present?
72
+ end
73
+
74
+ open_timeout = dig_setting(settings, :services, :ecfr, :open_timeout)
75
+ config.open_timeout = open_timeout unless open_timeout.nil?
76
+
77
+ pdf_timeout = dig_setting(settings, :app, :timeouts, :pdf_timeout)
78
+ config.prince_xml_service_pdf_timeout = pdf_timeout unless pdf_timeout.nil?
79
+
80
+ # basic auth - some endpoints require auth (optional)
81
+ config.ecfr_basic_auth_username = credentials&.dig(:services, :ecfr, :http_basic, :username)
82
+ config.ecfr_basic_auth_password = credentials&.dig(:services, :ecfr, :http_basic, :password)
83
+
84
+ # let the app override any of the above (request hooks, urls, timeouts)
85
+ yield config if block_given?
86
+ end
87
+ end
88
+
89
+ def self.validate_settings!(settings)
90
+ raise InvalidSettings, "settings is required" if settings.nil?
91
+
92
+ missing = REQUIRED_SETTINGS.select { |path| dig_setting(settings, *path).blank? }
93
+ return if missing.empty?
94
+
95
+ raise InvalidSettings,
96
+ "missing required setting(s): #{missing.map { |path| path.join(".") }.join(", ")}"
97
+ end
98
+
99
+ def self.validate_credentials!(credentials)
100
+ return if credentials.nil?
101
+ return if credentials.respond_to?(:dig)
102
+
103
+ raise InvalidCredentials, "credentials must respond to #dig (got #{credentials.class})"
104
+ end
105
+
106
+ # Safely walk a settings tree via method or [] access, returning nil if any
107
+ # segment is missing rather than raising NoMethodError.
108
+ def self.dig_setting(node, *path)
109
+ path.reduce(node) do |obj, key|
110
+ break nil if obj.nil?
111
+
112
+ if obj.respond_to?(key)
113
+ obj.public_send(key)
114
+ elsif obj.respond_to?(:[])
115
+ obj[key]
116
+ end
117
+ end
118
+ end
119
+
120
+ private_class_method :validate_settings!, :validate_credentials!, :dig_setting
121
+ end
122
+ end
@@ -2,13 +2,15 @@ module Ecfr
2
2
  module Constants
3
3
  module ChangeTypes
4
4
  KNOWN_CHANGE_TYPES = {
5
+ cross_reference: "Regulations linked from the above-dated Federal Register:",
5
6
  cross_references: "Regulations linked from the above-dated Federal Register:",
6
7
  delayed: "Regulations delayed in the above-dated Federal Register:",
7
8
  delayed_withdrawn: "Regulations delayed or withdrawn in the above-dated Federal Register:",
8
9
  delayed_withdrawn_extended: "Regulations delayed, withdrawn, or extended in the above-dated Federal Register:",
9
10
  effective: "Effective regulations inserted from the above-dated Federal Register:",
11
+ effective_cross_reference: "Regulations inserted that were previously linked and became effective on the date listed above:",
10
12
  effective_cross_references: "Regulations inserted that were previously linked and became effective on the date listed above:",
11
- expired: "Effective dates that expire on this date:",
13
+ expired: "Effective regulations that expire on this date:",
12
14
  extended: "Regulations extended in the above-dated Federal Register",
13
15
  initial: "Initial import of this content - change type is indeterminate"
14
16
  }
@@ -1,24 +1,29 @@
1
1
  module Ecfr
2
2
  module ParallelClient
3
3
  module ClassMethods
4
- def parallel_client(base_url:, client_options: {})
4
+ def parallel_client(base_url: self.base_url, client_options: {})
5
5
  client(
6
6
  base_url: base_url,
7
7
  client_options: client_options.merge({adapter: :typhoeus})
8
8
  )
9
9
  end
10
10
 
11
- # currently only handles expected cases when calling -renderer
12
- def parallel_get(requests, client)
11
+ def parallel(method, requests, client = parallel_client)
13
12
  client.in_parallel do
14
13
  requests.each do |request|
15
- request.response = client.get(request.path, request.args) do |req|
14
+ request.response = client.send(method, request.path, request.args) do |req|
16
15
  Ecfr.config.request_hook.call(req)
17
16
  end
18
17
  end
19
18
  end
19
+ end
20
+
21
+ def parallel_get(requests, client = parallel_client)
22
+ parallel(:get, requests, client)
23
+ end
20
24
 
21
- requests
25
+ def parallel_post(requests, client = parallel_client)
26
+ parallel(:post, requests, client)
22
27
  end
23
28
  end
24
29
 
@@ -40,10 +40,7 @@ module Ecfr
40
40
  ORIGIN_PATH = "v1/origin"
41
41
 
42
42
  def self.find(date, title_number, params = {})
43
- supported_params = (
44
- Ecfr::Constants::Hierarchy::HIERARCHY_LEVELS[1, 8] +
45
- [:build_id]
46
- ).map(&:to_sym)
43
+ supported_params = Ecfr::Constants::Hierarchy::HIERARCHY_LEVELS[1, 8].map(&:to_sym)
47
44
 
48
45
  perform(
49
46
  :get,
@@ -0,0 +1,15 @@
1
+ class Ecfr::VersionerService::Reference
2
+ extend ResponseHelper
3
+
4
+ def self.response_for(references)
5
+ references = references.is_a?(Array) ? references : [references]
6
+
7
+ results = {
8
+ references: references
9
+ }.compact
10
+
11
+ build(
12
+ response: stubbed_response(results.to_json)
13
+ )
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ class Ecfr::VersionerService::Topic
2
+ extend ResponseHelper
3
+
4
+ def self.response_for(topics, meta: {})
5
+ topics = topics.is_a?(Array) ? topics : [topics]
6
+
7
+ results = {
8
+ topics: topics,
9
+ meta: meta
10
+ }.compact
11
+
12
+ build(
13
+ response: stubbed_response(results.to_json)
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ FactoryBot.define do
2
+ factory :versioner_service_reference, class: "Ecfr::VersionerService::Reference" do
3
+ skip_create
4
+
5
+ reference { "1 CFR 1" }
6
+ description { "Definitions" }
7
+
8
+ initialize_with do
9
+ new(attributes.deep_stringify_keys)
10
+ end
11
+
12
+ trait :minimal do
13
+ description { "" }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,34 @@
1
+ FactoryBot.define do
2
+ factory :topic, class: "Ecfr::VersionerService::Topic" do
3
+ skip_create
4
+
5
+ name { "Sample Topic" }
6
+ references { ["1 CFR 1", "1 CFR 2"] }
7
+ see_also { ["Related Topic"] }
8
+ see { [] }
9
+
10
+ initialize_with do
11
+ new(attributes.deep_stringify_keys)
12
+ end
13
+
14
+ trait :with_references do
15
+ references { ["1 CFR 1", "1 CFR 2", "1 CFR 3"] }
16
+ end
17
+
18
+ trait :with_see_also do
19
+ see_also { ["Related Topic 1", "Related Topic 2"] }
20
+ end
21
+
22
+ trait :with_see do
23
+ see { ["Redirect Topic"] }
24
+ references { [] }
25
+ see_also { [] }
26
+ end
27
+
28
+ trait :minimal do
29
+ references { [] }
30
+ see_also { [] }
31
+ see { [] }
32
+ end
33
+ end
34
+ end
data/lib/ecfr/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ecfr
4
- VERSION = "1.1.6"
4
+ VERSION = "1.1.7"
5
5
  end
@@ -0,0 +1,9 @@
1
+ module Ecfr
2
+ module VersionerService
3
+ class Agency < Ecfr::AdminService::Agency
4
+ attribute :references,
5
+ type: Array(:string),
6
+ desc: "array of CFR references for this topic"
7
+ end
8
+ end
9
+ end
@@ -65,8 +65,6 @@ module Ecfr
65
65
  # hierarchy of the requested content
66
66
  # @option options [<Integer>] :descendant_depth the number of
67
67
  # levels of descendents to include in the structure
68
- # @option options [<Integer>] :build_id internal use only - used to
69
- # retreive data about a specific build
70
68
  # @option options [<Boolean>] :metadata whether to include metadata in
71
69
  # the response
72
70
  # @option options [<Integer>] :max_items
@@ -80,8 +78,7 @@ module Ecfr
80
78
  hierarchy_args = hierarchy_args.symbolize_keys
81
79
  options = options.symbolize_keys
82
80
 
83
- options = hierarchy_args.merge(options).except(:title, :metadata)
84
- options[:metadata] = true
81
+ options = hierarchy_args.merge(options).except(:title)
85
82
 
86
83
  perform(
87
84
  :get,
@@ -0,0 +1,48 @@
1
+ module Ecfr
2
+ module VersionerService
3
+ #
4
+ # Authority entries represent individual references in the
5
+ # Parallel Table of Authorities.
6
+ #
7
+ class Authority < Base
8
+ result_key :authorities
9
+
10
+ attribute :kind,
11
+ desc: "authority reference kind"
12
+
13
+ attribute :components,
14
+ desc: "authority citation components"
15
+
16
+ attribute :date,
17
+ type: :date,
18
+ desc: "authority citation date"
19
+
20
+ attribute :references,
21
+ desc: "CFR titles, parts, and hierarchies that cite this authority"
22
+
23
+ AUTHORITIES_PATH = "v1/authorities"
24
+
25
+ metadata_key :meta
26
+
27
+ metadata :categories,
28
+ desc: "reference categories included in this response"
29
+
30
+ #
31
+ # Retrieves the Parallel Table of Authorities data.
32
+ #
33
+ # @param [<Hash>] options
34
+ # @option options [String] :year ("current") the year or 'current' to retrieve authorities for
35
+ #
36
+ # @return [Authority] grouped authority data and metadata
37
+ #
38
+ def self.all(options = {})
39
+ year = options.fetch(:year, "current")
40
+
41
+ perform(
42
+ :get,
43
+ "#{AUTHORITIES_PATH}/#{year}"
44
+ )
45
+ end
46
+ end
47
+ end
48
+ end
@@ -6,9 +6,13 @@ module Ecfr
6
6
 
7
7
  # note: structure must be required before ancestors
8
8
  require_relative "structure"
9
+ require_relative "agency"
9
10
  require_relative "ancestors"
11
+ require_relative "authority"
10
12
  require_relative "issue_package"
11
13
  require_relative "title"
14
+ require_relative "reference"
15
+ require_relative "topic"
12
16
  require_relative "xml_content"
13
17
 
14
18
  def self.base_url
@@ -0,0 +1,19 @@
1
+ module Ecfr
2
+ module VersionerService
3
+ #
4
+ # References in the topics endpoint provide detailed information
5
+ # about CFR references mentioned in topics and agencies.
6
+ #
7
+ # Each reference contains a CFR reference string and a description.
8
+ #
9
+ class Reference < Base
10
+ result_key :references
11
+
12
+ attribute :reference,
13
+ desc: "CFR reference ('1 CFR 1')"
14
+
15
+ attribute :description,
16
+ desc: "description of the reference"
17
+ end
18
+ end
19
+ end
@@ -61,8 +61,6 @@ module Ecfr
61
61
  # Retreive the list of all Titles
62
62
  #
63
63
  # @param [<Hash>] options
64
- # @option options [String] :build_id internal use only - used
65
- # to retreive data about a specific build
66
64
  #
67
65
  # @return [<Title>] array of Titles with data
68
66
  #
@@ -0,0 +1,74 @@
1
+ module Ecfr
2
+ module VersionerService
3
+ #
4
+ # Topics represent entries in the CFR Index and Finding Aids.
5
+ #
6
+ # Each topic contains a name and may have references to specific
7
+ # CFR sections, see_also references to other topics, or see
8
+ # references for redirects.
9
+ #
10
+ class Topic < Base
11
+ result_key :topics
12
+
13
+ attribute :adhoc,
14
+ type: :boolean,
15
+ desc: "distinguish informal topics from thesaurus entries"
16
+
17
+ attribute :name,
18
+ desc: "name of the topic"
19
+
20
+ attribute :references,
21
+ type: Array(:string),
22
+ desc: "array of CFR references for this topic"
23
+
24
+ attribute :see_also,
25
+ type: Array(:string),
26
+ desc: "array of related topic names to see also"
27
+
28
+ attribute :see,
29
+ type: Array(:string),
30
+ desc: "array of topic names to redirect to"
31
+
32
+ attribute :slug,
33
+ desc: "normalized topic identifier for URLs"
34
+
35
+ TOPICS_PATH = "v1/topics" # /current | /2024
36
+
37
+ metadata_key :meta
38
+
39
+ metadata :agencies,
40
+ type: Array(Ecfr::VersionerService::Agency),
41
+ desc: "array of agencies referenced in topics"
42
+
43
+ metadata :references,
44
+ type: Array(Ecfr::VersionerService::Reference),
45
+ desc: "array of CFR references with descriptions"
46
+
47
+ #
48
+ # Retrieves the list of all Topics
49
+ #
50
+ # @param [<Hash>] options
51
+ # @option options [String] :year ("current") the year or 'current' to retrieve topics for
52
+ # @option options [Boolean] :include_agencies (false) whether to include agencies in the response
53
+ #
54
+ # @return [<Topic>] array of Topics with their references and relationships
55
+ #
56
+ def self.all(options = {})
57
+ year = options.fetch(:year, "current")
58
+ include_agencies = options.fetch(:include_agencies, false)
59
+
60
+ params = {}
61
+ params[:include_agencies] = true if include_agencies
62
+
63
+ perform(
64
+ :get,
65
+ "#{TOPICS_PATH}/#{year}",
66
+ params: params,
67
+ perform_options: {
68
+ attributes_key: nil
69
+ }
70
+ )
71
+ end
72
+ end
73
+ end
74
+ end
@@ -19,7 +19,6 @@ module Ecfr
19
19
  # @param [<Date, String, 'current'>] date ISO string or 'current'
20
20
  # @param [<Integer, String>] title_number the title of interest
21
21
  # @param [<Hash>] options a hash of hierarchy levels for the content desired - see attributes defined in {Ecfr::Common::Hierarchy} for acceptable keys
22
- # @option options [<String>] build_id internal use only - a specific build id
23
22
  #
24
23
  # @return [<XML>] XML for the full title or pared down to the requested hierarchy
25
24
  #
@@ -40,7 +39,6 @@ module Ecfr
40
39
  # @param [<Date, String, 'current'>] date ISO string or 'current'
41
40
  # @param [<Integer, String>] title_number the title of interest
42
41
  # @param [<Hash>] options a hash of hierarchy levels for the content desired - see attributes defined in {Ecfr:Common::Hierarchy} for acceptable keys
43
- # @option options [<String>] build_id internal use only - a specific build id
44
42
  #
45
43
  # @return [<String>] URL to retreive XML content for the given parameters
46
44
  #
data/lib/ecfr.rb CHANGED
@@ -5,6 +5,7 @@ require "active_model/type"
5
5
 
6
6
  require "active_support"
7
7
  require "active_support/core_ext/class/attribute"
8
+ require "active_support/core_ext/enumerable"
8
9
  require "active_support/core_ext/hash"
9
10
  require "active_support/core_ext/module/delegation"
10
11
  require "active_support/core_ext/string"
@@ -92,3 +93,5 @@ require_relative "ecfr/search_service/base"
92
93
  require_relative "ecfr/subscriptions_service/base"
93
94
  require_relative "ecfr/varnish_cache_service/base"
94
95
  require_relative "ecfr/versioner_service/base"
96
+
97
+ require_relative "ecfr/client_configuration"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ecfr
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.6
4
+ version: 1.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peregrinator
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-09-23 00:00:00.000000000 Z
10
+ date: 2026-06-29 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activemodel
@@ -274,7 +274,6 @@ files:
274
274
  - lib/ecfr/admin_service/agency.rb
275
275
  - lib/ecfr/admin_service/api_documentation.rb
276
276
  - lib/ecfr/admin_service/base.rb
277
- - lib/ecfr/admin_service/build.rb
278
277
  - lib/ecfr/admin_service/ecfr_correction.rb
279
278
  - lib/ecfr/admin_service/ecfr_correction/cfr_reference.rb
280
279
  - lib/ecfr/admin_service/editorial_note.rb
@@ -290,6 +289,7 @@ files:
290
289
  - lib/ecfr/attribute_method_definition.rb
291
290
  - lib/ecfr/base.rb
292
291
  - lib/ecfr/client.rb
292
+ - lib/ecfr/client_configuration.rb
293
293
  - lib/ecfr/common/hierarchy.rb
294
294
  - lib/ecfr/configuration.rb
295
295
  - lib/ecfr/constants.rb
@@ -338,7 +338,9 @@ files:
338
338
  - lib/ecfr/testing/extensions/search_service/date_facet_extensions.rb
339
339
  - lib/ecfr/testing/extensions/search_service/timeline_extensions.rb
340
340
  - lib/ecfr/testing/extensions/versioner_service/ancestors_extensions.rb
341
+ - lib/ecfr/testing/extensions/versioner_service/reference_extensions.rb
341
342
  - lib/ecfr/testing/extensions/versioner_service/title_extenstions.rb
343
+ - lib/ecfr/testing/extensions/versioner_service/topic_extensions.rb
342
344
  - lib/ecfr/testing/factories/admin_service/agency_factory.rb
343
345
  - lib/ecfr/testing/factories/admin_service/cfr_reference_factory.rb
344
346
  - lib/ecfr/testing/factories/admin_service/ecfr_correction_factory.rb
@@ -353,26 +355,32 @@ files:
353
355
  - lib/ecfr/testing/factories/versioner_service/ancestors_factory.rb
354
356
  - lib/ecfr/testing/factories/versioner_service/metadata_node_info_factory.rb
355
357
  - lib/ecfr/testing/factories/versioner_service/node_summary_factory.rb
358
+ - lib/ecfr/testing/factories/versioner_service/reference_factory.rb
356
359
  - lib/ecfr/testing/factories/versioner_service/structure_factory.rb
357
360
  - lib/ecfr/testing/factories/versioner_service/title_factory.rb
361
+ - lib/ecfr/testing/factories/versioner_service/topic_factory.rb
358
362
  - lib/ecfr/testing/factory_bot_helpers/content_version.rb
359
363
  - lib/ecfr/testing/factory_bot_helpers/ecfr_gem_initialize_helpers.rb
360
364
  - lib/ecfr/testing/helpers/response_helper.rb
361
365
  - lib/ecfr/testing/strategies/ecfr_attribute_hash_strategy.rb
362
366
  - lib/ecfr/varnish_cache_service/base.rb
363
367
  - lib/ecfr/version.rb
368
+ - lib/ecfr/versioner_service/agency.rb
364
369
  - lib/ecfr/versioner_service/ancestors.rb
365
370
  - lib/ecfr/versioner_service/ancestors/metadata_node_info.rb
366
371
  - lib/ecfr/versioner_service/ancestors/node_summary.rb
367
372
  - lib/ecfr/versioner_service/api_documentation.rb
373
+ - lib/ecfr/versioner_service/authority.rb
368
374
  - lib/ecfr/versioner_service/base.rb
369
375
  - lib/ecfr/versioner_service/issue_package.rb
370
376
  - lib/ecfr/versioner_service/issue_package/issue_volume.rb
371
377
  - lib/ecfr/versioner_service/issue_package/sha_comparison.rb
372
378
  - lib/ecfr/versioner_service/issue_package/title_version.rb
379
+ - lib/ecfr/versioner_service/reference.rb
373
380
  - lib/ecfr/versioner_service/status.rb
374
381
  - lib/ecfr/versioner_service/structure.rb
375
382
  - lib/ecfr/versioner_service/title.rb
383
+ - lib/ecfr/versioner_service/topic.rb
376
384
  - lib/ecfr/versioner_service/xml_content.rb
377
385
  - lib/yard/attribute_handler.rb
378
386
  - lib/yard/metadata_handler.rb
@@ -1,38 +0,0 @@
1
- module Ecfr
2
- module AdminService
3
- class Build < Base
4
- result_key :builds
5
-
6
- attribute :id,
7
- desc: "build id"
8
- attribute :status,
9
- desc: "build status - either *Success* or *Failure*"
10
-
11
- attribute :expired,
12
- type: :boolean
13
- attribute :previewable,
14
- type: :boolean
15
-
16
- BUILDS_PATH = "v1/builds"
17
-
18
- #
19
- # Retrieve a Build by id
20
- #
21
- # @param [<String>] build_id - id of the desired build
22
- #
23
- # @return [<Build>] data for a single build
24
- #
25
- def self.find(build_id)
26
- perform(
27
- :get,
28
- build_path(build_id)
29
- )
30
- end
31
-
32
- def self.build_path(build_id)
33
- "#{BUILDS_PATH}/#{build_id}"
34
- end
35
- private_class_method :build_path
36
- end
37
- end
38
- end