federal_register 0.5.1 → 0.6.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.
Files changed (50) hide show
  1. data/.rubocop.yml +286 -0
  2. data/Gemfile +3 -4
  3. data/Gemfile.lock +82 -22
  4. data/README.md +6 -6
  5. data/Rakefile +5 -5
  6. data/VERSION +1 -1
  7. data/federal_register.gemspec +49 -19
  8. data/lib/federal_register.rb +41 -0
  9. data/lib/federal_register/agency.rb +45 -9
  10. data/lib/federal_register/article.rb +2 -106
  11. data/lib/federal_register/base.rb +52 -3
  12. data/lib/federal_register/client.rb +3 -3
  13. data/lib/federal_register/document.rb +113 -0
  14. data/lib/federal_register/document_image.rb +17 -0
  15. data/lib/federal_register/facet.rb +21 -0
  16. data/lib/federal_register/facet/agency.rb +5 -0
  17. data/lib/federal_register/facet/document.rb +2 -0
  18. data/lib/federal_register/facet/document/agency.rb +5 -0
  19. data/lib/federal_register/facet/document/daily.rb +5 -0
  20. data/lib/federal_register/facet/document/frequency.rb +12 -0
  21. data/lib/federal_register/facet/document/monthly.rb +5 -0
  22. data/lib/federal_register/facet/document/quarterly.rb +5 -0
  23. data/lib/federal_register/facet/document/section.rb +5 -0
  24. data/lib/federal_register/facet/document/topic.rb +5 -0
  25. data/lib/federal_register/facet/document/type.rb +5 -0
  26. data/lib/federal_register/facet/document/weekly.rb +5 -0
  27. data/lib/federal_register/facet/document/yearly.rb +5 -0
  28. data/lib/federal_register/facet/presidential_document_type.rb +6 -0
  29. data/lib/federal_register/facet/public_inspection_document.rb +2 -0
  30. data/lib/federal_register/facet/public_inspection_document/agencies.rb +5 -0
  31. data/lib/federal_register/facet/public_inspection_document/agency.rb +5 -0
  32. data/lib/federal_register/facet/public_inspection_document/type.rb +5 -0
  33. data/lib/federal_register/facet/public_inspection_issue.rb +36 -0
  34. data/lib/federal_register/facet/public_inspection_issue/daily.rb +11 -0
  35. data/lib/federal_register/facet/public_inspection_issue/daily_filing.rb +6 -0
  36. data/lib/federal_register/facet/public_inspection_issue/type.rb +11 -0
  37. data/lib/federal_register/facet/public_inspection_issue/type_filing.rb +22 -0
  38. data/lib/federal_register/facet/topic.rb +5 -0
  39. data/lib/federal_register/facet_result_set.rb +25 -0
  40. data/lib/federal_register/highlighted_document.rb +42 -0
  41. data/lib/federal_register/public_inspection_document.rb +21 -7
  42. data/lib/federal_register/public_inspection_issue_result_set.rb +10 -0
  43. data/lib/federal_register/result_set.rb +5 -5
  44. data/lib/federal_register/section.rb +26 -0
  45. data/lib/federal_register/suggested_search.rb +26 -0
  46. data/lib/federal_register/topic.rb +13 -0
  47. data/spec/{article_spec.rb → document_spec.rb} +20 -28
  48. data/spec/public_inspection_document_spec.rb +5 -6
  49. data/spec/result_set_spec.rb +4 -4
  50. metadata +99 -39
@@ -1,6 +1,6 @@
1
1
  class FederalRegister::Client
2
2
  include HTTParty
3
-
3
+
4
4
  class ResponseError < HTTParty::ResponseError
5
5
  def message
6
6
  response.body
@@ -16,10 +16,10 @@ class FederalRegister::Client
16
16
  class ServerError < ResponseError; end
17
17
 
18
18
  base_uri 'https://www.federalregister.gov/api/v1'
19
-
19
+
20
20
  def self.get(url, *options)
21
21
  response = super
22
-
22
+
23
23
  case response.code
24
24
  when 200
25
25
  response
@@ -0,0 +1,113 @@
1
+ class FederalRegister::Document < FederalRegister::Base
2
+ extend FederalRegister::Utilities
3
+
4
+ add_attribute :abstract,
5
+ :abstract_html_url,
6
+ :action,
7
+ :agencies,
8
+ :agency_names,
9
+ :body_html_url,
10
+ :cfr_references,
11
+ :citation,
12
+ :comment_url,
13
+ :corrections,
14
+ :correction_of,
15
+ :dates,
16
+ :docket_id,
17
+ :docket_ids,
18
+ :document_number,
19
+ :end_page,
20
+ :excerpts,
21
+ :executive_order_notes,
22
+ :executive_order_number,
23
+ :full_text_xml_url,
24
+ :html_url,
25
+ :images,
26
+ :json_url,
27
+ :mods_url,
28
+ :pdf_url,
29
+ :president,
30
+ :public_inspection_pdf_url,
31
+ :regulation_id_number_info,
32
+ :regulation_id_numbers,
33
+ :regulations_dot_gov_info,
34
+ :regulations_dot_gov_url,
35
+ :significant,
36
+ :start_page,
37
+ :subtype,
38
+ :raw_text_url,
39
+ :title,
40
+ :toc_subject,
41
+ :toc_doc,
42
+ :type,
43
+ :volume
44
+
45
+ add_attribute :comments_close_on,
46
+ :effective_on,
47
+ :publication_date,
48
+ :signing_date,
49
+ :type => :date
50
+
51
+ def self.search(args)
52
+ FederalRegister::ResultSet.fetch("/documents.json", :query => args, :result_class => self)
53
+ end
54
+
55
+ def self.search_metadata(args)
56
+ FederalRegister::ResultSet.fetch("/documents.json", :query => args.merge(:metadata_only => '1'), :result_class => self)
57
+ end
58
+
59
+ def self.find(document_number, options={})
60
+ if options[:fields].present?
61
+ attributes = get("/documents/#{document_number}.json", :query => {:fields => options[:fields]})
62
+ new(attributes)
63
+ else
64
+ attributes = get("/documents/#{document_number}.json")
65
+ new(attributes, :full => true)
66
+ end
67
+ end
68
+
69
+ def self.find_all(*args)
70
+ options, document_numbers = extract_options(args)
71
+
72
+ fetch_options = {:result_class => self}
73
+ fetch_options.merge!(:query => {:fields => options[:fields]}) if options[:fields]
74
+
75
+ document_numbers = document_numbers.flatten
76
+
77
+ #TODO: fix this gross hack to ensure that find_all with a single document number
78
+ # is returned in the same way multiple document numbers are
79
+ if document_numbers.size == 1
80
+ document_numbers << " "
81
+ end
82
+
83
+ result_set = FederalRegister::ResultSet.fetch("/documents/#{document_numbers.join(',').strip}.json", fetch_options)
84
+ end
85
+
86
+ def agencies
87
+ attributes["agencies"].map do |attr|
88
+ FederalRegister::Agency.new(attr)
89
+ end
90
+ end
91
+
92
+ %w(full_text_xml abstract_html body_html raw_text mods).each do |file_type|
93
+ define_method file_type do
94
+ begin
95
+ self.class.get(send("#{file_type}_url")).body
96
+ rescue FederalRegister::Client::RecordNotFound
97
+ nil
98
+ rescue
99
+ raise send("#{file_type}_url").inspect
100
+ end
101
+ end
102
+ end
103
+
104
+ def images
105
+ if attributes["images"]
106
+ attributes["images"].map do |attributes|
107
+ FederalRegister::DocumentImage.new(attributes)
108
+ end
109
+ else
110
+ []
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,17 @@
1
+ class FederalRegister::DocumentImage < FederalRegister::Document
2
+ def identifier
3
+ attributes[0]
4
+ end
5
+
6
+ def sizes
7
+ attributes[1].keys
8
+ end
9
+
10
+ def url_for(size)
11
+ attributes[1][size]
12
+ end
13
+
14
+ def default_url
15
+ sizes.include?('original_png') ? url_for('original_png') : url_for('original')
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ class FederalRegister::Facet < FederalRegister::Base
2
+ extend FederalRegister::Utilities
3
+
4
+ attr_reader :result_set
5
+
6
+ add_attribute :count,
7
+ :name,
8
+ :slug
9
+
10
+ def self.search(args={})
11
+ FederalRegister::FacetResultSet.fetch(
12
+ url, :query => args, :result_class => self
13
+ )
14
+ end
15
+
16
+ def initialize(attributes={}, options={})
17
+ @result_set = options.delete(:result_set)
18
+
19
+ super(attributes, options)
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ class FederalRegister::Facet::Agency < FederalRegister::Facet
2
+ def self.url
3
+ '/documents/facets/agency'
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ class FederalRegister::Facet::Document < FederalRegister::Facet
2
+ end
@@ -0,0 +1,5 @@
1
+ class FederalRegister::Facet::Document::Agency < FederalRegister::Facet::Document
2
+ def self.url
3
+ '/documents/facets/agency'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class FederalRegister::Facet::Document::Daily < FederalRegister::Facet::Document::Frequency
2
+ def self.url
3
+ '/documents/facets/daily'
4
+ end
5
+ end
@@ -0,0 +1,12 @@
1
+ class FederalRegister::Facet::Document::Frequency < FederalRegister::Facet::Document
2
+ def self.chart_url(args={})
3
+ uri = [base_uri, url, '.png']
4
+
5
+ if args.present?
6
+ uri << '?'
7
+ uri << HTTParty::HashConversions.to_params(args)
8
+ end
9
+
10
+ uri.join('')
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ class FederalRegister::Facet::Document::Monthly < FederalRegister::Facet::Document::Frequency
2
+ def self.url
3
+ '/documents/facets/monthly'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class FederalRegister::Facet::Document::Quarterly < FederalRegister::Facet::Document::Frequency
2
+ def self.url
3
+ '/documents/facets/quarterly'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class FederalRegister::Facet::Document::Section < FederalRegister::Facet::Document
2
+ def self.url
3
+ '/documents/facets/section'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class FederalRegister::Facet::Document::Topic < FederalRegister::Facet::Document
2
+ def self.url
3
+ '/documents/facets/topic'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class FederalRegister::Facet::Document::Type < FederalRegister::Facet::Document
2
+ def self.url
3
+ '/documents/facets/type'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class FederalRegister::Facet::Document::Weekly < FederalRegister::Facet::Document::Frequency
2
+ def self.url
3
+ '/documents/facets/weekly'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class FederalRegister::Facet::Document::Yearly < FederalRegister::Facet::Document::Frequency
2
+ def self.url
3
+ '/documents/facets/yearly'
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class FederalRegister::Facet::PresidentialDocumentType < FederalRegister::Facet
2
+ def self.url
3
+ '/documents/facets/subtype'
4
+ end
5
+ end
6
+
@@ -0,0 +1,2 @@
1
+ class FederalRegister::Facet::PublicInspectionDocument < FederalRegister::Facet
2
+ end
@@ -0,0 +1,5 @@
1
+ class FederalRegister::Facet::PublicInspectionDocument::Agencies < FederalRegister::Facet::PublicInspectionDocument
2
+ def self.url
3
+ '/public-inspection-documents/facets/agencies'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class FederalRegister::Facet::PublicInspectionDocument::Agency < FederalRegister::Facet::PublicInspectionDocument
2
+ def self.url
3
+ '/public-inspection-documents/facets/agency'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class FederalRegister::Facet::PublicInspectionDocument::Type < FederalRegister::Facet::PublicInspectionDocument
2
+ def self.url
3
+ '/public-inspection-documents/facets/type'
4
+ end
5
+ end
@@ -0,0 +1,36 @@
1
+ class FederalRegister::Facet::PublicInspectionIssue < FederalRegister::Base
2
+ add_attribute :slug
3
+ attr_reader :conditions
4
+
5
+ def initialize(attributes, options = {})
6
+ @conditions = options[:query] || {}
7
+ super
8
+ end
9
+
10
+ def special_filings
11
+ @special_filings ||= filing_class.new(
12
+ attributes['special_filings'],
13
+ conditions.deep_merge({
14
+ conditions: {special_filing: 1}
15
+ })
16
+ )
17
+ end
18
+
19
+ def regular_filings
20
+ @regular_filings ||= filing_class.new(
21
+ attributes['regular_filings'],
22
+ conditions.deep_merge({
23
+ conditions: {special_filing: 0}
24
+ })
25
+ )
26
+ end
27
+
28
+ def self.search(args={})
29
+ response = get(url, query: args)
30
+
31
+ response.map do |slug, attributes|
32
+ attributes['slug'] = slug
33
+ new(attributes, query: args)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,11 @@
1
+ class FederalRegister::Facet::PublicInspectionIssue::Daily < FederalRegister::Facet::PublicInspectionIssue
2
+ def self.url
3
+ '/public-inspection-issues/facets/daily'
4
+ end
5
+
6
+ private
7
+
8
+ def filing_class
9
+ FederalRegister::Facet::PublicInspectionIssue::DailyFiling
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ class FederalRegister::Facet::PublicInspectionIssue::DailyFiling < FederalRegister::Facet::PublicInspectionIssue
2
+ add_attribute :agencies, :documents
3
+
4
+ add_attribute :last_updated_at,
5
+ :type => :datetime
6
+ end
@@ -0,0 +1,11 @@
1
+ class FederalRegister::Facet::PublicInspectionIssue::Type < FederalRegister::Facet::PublicInspectionIssue
2
+ def self.url
3
+ '/public-inspection-issues/facets/type'
4
+ end
5
+
6
+ private
7
+
8
+ def filing_class
9
+ FederalRegister::Facet::PublicInspectionIssue::TypeFiling
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ class FederalRegister::Facet::PublicInspectionIssue::TypeFiling < FederalRegister::Facet::PublicInspectionIssue
2
+ attr_reader :document_types, :search_conditions
3
+
4
+ def initialize(attributes, conditions, options={})
5
+ @search_conditions = conditions
6
+ @document_types = attributes.map{|k,v| DocumentTypeFacet.new(k, v, @search_conditions)}
7
+ end
8
+
9
+ private
10
+
11
+ class DocumentTypeFacet
12
+ attr_reader :count, :name, :search_conditions
13
+
14
+ def initialize(type, attributes, search_conditions)
15
+ @count = attributes['count']
16
+ @name = attributes['name']
17
+ @search_conditions = search_conditions.deep_merge({
18
+ conditions: {type: type}
19
+ })
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ class FederalRegister::Facet::Topic < FederalRegister::Facet
2
+ def self.url
3
+ '/documents/facets/topic'
4
+ end
5
+ end
@@ -0,0 +1,25 @@
1
+ class FederalRegister::FacetResultSet < FederalRegister::Client
2
+ include Enumerable
3
+
4
+ attr_reader :conditions, :results
5
+
6
+ def initialize(attributes, result_class, options={})
7
+ @result_class = result_class
8
+ @conditions = options[:query] || {}
9
+
10
+ @results = (attributes || {}).map do |slug, attributes|
11
+ attributes["slug"] = slug
12
+ @result_class.new(attributes, options.merge(:result_set => self) )
13
+ end
14
+ end
15
+
16
+ def self.fetch(url, options = {})
17
+ result_class = options.delete(:result_class)
18
+ response = get(url, options)
19
+ new(response, result_class, options)
20
+ end
21
+
22
+ def each
23
+ @results.each {|result| yield result }
24
+ end
25
+ end
@@ -0,0 +1,42 @@
1
+ class FederalRegister::HighlightedDocument < FederalRegister::Base
2
+ class InvalidPhotoSize < StandardError; end
3
+
4
+ add_attribute :curated_abstract,
5
+ :curated_title,
6
+ :document_number,
7
+ :html_url,
8
+ :photo
9
+
10
+ VALID_PHOTO_SIZES = [
11
+ 'full_size',
12
+ 'homepage',
13
+ 'large',
14
+ 'medium',
15
+ 'navigation',
16
+ 'small'
17
+ ]
18
+
19
+ def photo_url(size)
20
+ unless VALID_PHOTO_SIZES.include?(size)
21
+ raise InvalidPhotoSize,
22
+ "valid photo sizes are #{VALID_PHOTO_SIZES.join(', ')}"
23
+ end
24
+
25
+ if attributes['photo']
26
+ attributes['photo']['urls'][size]
27
+ end
28
+ end
29
+
30
+ def photo_credit
31
+ @credit ||= PhotoCredit.new(attributes['photo']['credit']) if attributes['photo']
32
+ end
33
+
34
+ class PhotoCredit
35
+ attr_reader :name, :url
36
+
37
+ def initialize(attributes)
38
+ @name = attributes['name']
39
+ @url = attributes['url']
40
+ end
41
+ end
42
+ end