bls_api 1.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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +108 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/bls_api.gemspec +27 -0
- data/lib/bls_api/client.rb +80 -0
- data/lib/bls_api/constants.rb +11 -0
- data/lib/bls_api/destringify.rb +124 -0
- data/lib/bls_api/errors.rb +8 -0
- data/lib/bls_api/month.rb +63 -0
- data/lib/bls_api/raw_request.rb +89 -0
- data/lib/bls_api/series.rb +56 -0
- data/lib/bls_api/version.rb +3 -0
- data/lib/bls_api.rb +8 -0
- metadata +133 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9f4938be36f9e555cb7ba3e8c68114143c5d06ae
|
4
|
+
data.tar.gz: cdf087da4ddc16b5c5d66d7fb380ea6739ab69a7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fd0c6a16df4d39cd383cd722a698dd8e25e1310cff4756c84f457a6e59b848cc726dfac422c1b7b28b7568b470ab2bcf7143acfc4770a0818cef078055264158
|
7
|
+
data.tar.gz: 07613e657478815be83623498283d6d5ac87a50161fb59e618e42f294509cbe3cee563d62241cbc40bb039d5d08bd65e481bd0e1aaf02acff4f8df7a7cae4f46
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 The Associated Press
|
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,108 @@
|
|
1
|
+
# Bureau of Labor Statistics API wrapper #
|
2
|
+
|
3
|
+
This is a Ruby wrapper for [v2][v2] of the [BLS Public Data API][bls-dev].
|
4
|
+
|
5
|
+
[bls-dev]: http://www.bls.gov/developers/home.htm
|
6
|
+
[v2]: http://www.bls.gov/developers/api_signature_v2.htm
|
7
|
+
|
8
|
+
## Installation ##
|
9
|
+
|
10
|
+
$ gem install bls_api
|
11
|
+
|
12
|
+
## Usage ##
|
13
|
+
|
14
|
+
>> require "bls_api"
|
15
|
+
>> client = BLS_API::Client.new
|
16
|
+
>> data = client.get(:series_ids => ["LNS14000000"], :start_year => 2015, :end_year => 2015)
|
17
|
+
|
18
|
+
You'll get back a Hash, with the series IDs you provided as keys. The values
|
19
|
+
are `BLS_API::Series` instances, which expose the data for each series along
|
20
|
+
with some BLS-provided metadata:
|
21
|
+
|
22
|
+
>> unemployment_rate = data["LNS14000000"]
|
23
|
+
|
24
|
+
>> unemployment_rate.catalog.series_title
|
25
|
+
=> "(Seas) Unemployment Rate"
|
26
|
+
>> unemployment_rate.catalog.seasonality
|
27
|
+
=> "Seasonally Adjusted"
|
28
|
+
|
29
|
+
>> june = unemployment_rate.get_month(2015, 6)
|
30
|
+
>> june.value.to_f
|
31
|
+
=> 5.3
|
32
|
+
|
33
|
+
### Series metadata ###
|
34
|
+
|
35
|
+
`Series#catalog` just returns an [OpenStruct][openstruct] with whatever
|
36
|
+
metadata the BLS API returned for that series:
|
37
|
+
|
38
|
+
>> unemployment_rate.catalog
|
39
|
+
=> #<OpenStruct series_title="(Seas) Unemployment Rate",
|
40
|
+
series_id="LNS14000000", seasonality="Seasonally Adjusted",
|
41
|
+
survey_name="Labor Force Statistics from the Current Population Survey",
|
42
|
+
measure_data_type="Percent or rate", commerce_industry="All Industries",
|
43
|
+
occupation="All Occupations", cps_labor_force_status="Unemployment rate",
|
44
|
+
demographic_age="16 years and over",
|
45
|
+
demographic_ethnic_origin="All Origins", demographic_race="All Races",
|
46
|
+
demographic_gender="Both Sexes",
|
47
|
+
demographic_marital_status="All marital statuses",
|
48
|
+
demographic_education="All educational levels">
|
49
|
+
|
50
|
+
This is great if you already know what fields to expect; otherwise the more
|
51
|
+
familiar way to explore it might be as a Hash:
|
52
|
+
|
53
|
+
>> unemployment_rate.catalog.to_h.keys
|
54
|
+
=> [:series_title, :series_id, :seasonality, :survey_name,
|
55
|
+
:measure_data_type, :commerce_industry, :occupation,
|
56
|
+
:cps_labor_force_status, :demographic_age, :demographic_ethnic_origin,
|
57
|
+
:demographic_race, :demographic_gender, :demographic_marital_status,
|
58
|
+
:demographic_education]
|
59
|
+
|
60
|
+
[openstruct]: http://ruby-doc.org/stdlib-2.3.0/libdoc/ostruct/rdoc/OpenStruct.html
|
61
|
+
|
62
|
+
### Data by month ###
|
63
|
+
|
64
|
+
You've already seen `Series#get_month`, which takes a year and month
|
65
|
+
(1 = January) and returns a `BLS_API::Month`, and you've seen `Month#value`:
|
66
|
+
|
67
|
+
>> june = unemployment_rate.get_month(2015, 6)
|
68
|
+
>> june.value.to_f
|
69
|
+
=> 5.3
|
70
|
+
|
71
|
+
You also can get one-, three-, six- or 12-month changes (net changes _or_
|
72
|
+
percent changes):
|
73
|
+
|
74
|
+
>> june.net_change(1).to_f
|
75
|
+
=> -0.2
|
76
|
+
>> june.percent_change(1).to_f
|
77
|
+
=> -3.6
|
78
|
+
|
79
|
+
These examples use `#to_f` for display purposes because this client uses
|
80
|
+
[BigDecimal][bigdecimal] instances by default to preserve precision:
|
81
|
+
|
82
|
+
>> june.value
|
83
|
+
=> #<BigDecimal:7ff5493f5390,'0.53E1',18(18)>
|
84
|
+
|
85
|
+
If you'd prefer Floats, you can set `client.use_floats = true` before making
|
86
|
+
your request:
|
87
|
+
|
88
|
+
>> client.use_floats = true
|
89
|
+
>> data = client.get(:series_ids => ["LNS14000000"], :start_year => 2015, :end_year => 2015)
|
90
|
+
>> unemployment_rate = data["LNS14000000"]
|
91
|
+
>> unemployment_rate.get_month(2015, 6).value
|
92
|
+
=> 5.3
|
93
|
+
|
94
|
+
Also, BLS sometimes provides footnotes for certain data points, which you can
|
95
|
+
access via `Month#footnotes`:
|
96
|
+
|
97
|
+
>> last_month.footnotes
|
98
|
+
=> {"P"=>"Preliminary"}
|
99
|
+
|
100
|
+
[bigdecimal]: http://ruby-doc.org/stdlib-2.3.0/libdoc/bigdecimal/rdoc/BigDecimal.html
|
101
|
+
|
102
|
+
## Configuration ##
|
103
|
+
|
104
|
+
You'll need an [API key][api-key], which you can provide as an argument to
|
105
|
+
`BLS_API::Client.new` or as a `BLS_API_KEY` environment variable. (If you
|
106
|
+
provide both, the argument to `Client.new` takes precedence.)
|
107
|
+
|
108
|
+
[api-key]: http://data.bls.gov/registrationEngine/
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "bls_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
data/bls_api.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "bls_api/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "bls_api"
|
8
|
+
spec.version = BLS_API::VERSION
|
9
|
+
spec.authors = ["Justin Myers"]
|
10
|
+
spec.email = ["jmyers@ap.org"]
|
11
|
+
spec.license = "MIT"
|
12
|
+
|
13
|
+
spec.summary = %q{API wrapper for data from the U.S. Bureau of Labor Statistics.}
|
14
|
+
spec.description = %q{API wrapper for data from the U.S. Bureau of Labor Statistics.}
|
15
|
+
spec.homepage = "https://github.com/associatedpress/bls_api"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
23
|
+
spec.add_development_dependency "climate_control", "~> 0.0.3"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "rspec"
|
26
|
+
spec.add_development_dependency "webmock", "~> 1.21"
|
27
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require "bls_api/constants"
|
2
|
+
require "bls_api/destringify"
|
3
|
+
require "bls_api/errors"
|
4
|
+
require "bls_api/raw_request"
|
5
|
+
|
6
|
+
module BLS_API
|
7
|
+
class Client
|
8
|
+
include BLS_API::Destringify
|
9
|
+
include BLS_API::RawRequest
|
10
|
+
|
11
|
+
attr_accessor :api_key
|
12
|
+
attr_accessor :request_annual_averages
|
13
|
+
attr_accessor :request_catalog
|
14
|
+
attr_accessor :request_calculations
|
15
|
+
attr_accessor :use_floats
|
16
|
+
|
17
|
+
def initialize(api_key = nil)
|
18
|
+
@api_key = ENV.fetch("BLS_API_KEY", nil)
|
19
|
+
@api_key = api_key unless api_key.nil?
|
20
|
+
@api_key = nil if @api_key.is_a?(String) && @api_key.empty?
|
21
|
+
if @api_key.nil?
|
22
|
+
missing_key_message = <<-EOF.gsub(/^ */, "").gsub(/\r?\n/, " ").strip
|
23
|
+
You must provide an API key as an argument to BLS_API::Client.new or
|
24
|
+
as the BLS_API_KEY environment variable. If you do not have an API
|
25
|
+
key, register for one at http://data.bls.gov/registrationEngine/.
|
26
|
+
EOF
|
27
|
+
raise BLS_API::Errors::ConfigurationError, missing_key_message
|
28
|
+
end
|
29
|
+
|
30
|
+
@request_annual_averages = true
|
31
|
+
@request_catalog = true
|
32
|
+
@request_calculations = true
|
33
|
+
@use_floats = false
|
34
|
+
end
|
35
|
+
|
36
|
+
# Public: Request a batch of data from the BLS API.
|
37
|
+
#
|
38
|
+
# By default, raises BLS_API::Errors::APIError if the request is
|
39
|
+
# unsuccessful. (You can catch this with an IOError, if that's more your
|
40
|
+
# thing.)
|
41
|
+
#
|
42
|
+
# options - A Hash with three required arguments and four optional
|
43
|
+
# arguments.
|
44
|
+
# Required arguments include:
|
45
|
+
# :series_ids - An Array of String series IDs for which to
|
46
|
+
# request data. If a String is provided instead, it
|
47
|
+
# is assumed to be a single series ID.
|
48
|
+
# :start_year - An Integer representing the earliest year for
|
49
|
+
# which to request data.
|
50
|
+
# :end_year - An Integer representing the latest year for which
|
51
|
+
# to request data. Note that the BLS API will
|
52
|
+
# return an error if you specify a year for which
|
53
|
+
# no data exists; for example, an :end_year of 2016
|
54
|
+
# will raise an error during January of 2016 when
|
55
|
+
# no 2016 data has yet been released.
|
56
|
+
# Optional arguments include:
|
57
|
+
# :catch_errors - A Boolean specifying whether to raise an
|
58
|
+
# APIError if the request is unsuccessful
|
59
|
+
# (default: true).
|
60
|
+
# :catalog - A Boolean specifying whether to include
|
61
|
+
# catalog data in the response
|
62
|
+
# (default: true).
|
63
|
+
# :calculations - A Boolean specifying whether to include
|
64
|
+
# net-change and percent-change calculations
|
65
|
+
# in the response (default: true).
|
66
|
+
# :annual_averages - A Boolean specifying whether to include
|
67
|
+
# annual averages in the response
|
68
|
+
# (default: true).
|
69
|
+
#
|
70
|
+
# Returns a Hash with the given String series IDs as keys and
|
71
|
+
# BLS_API::Series instances as values.
|
72
|
+
def get(options = {})
|
73
|
+
raw_response = self.make_api_request(options)
|
74
|
+
destringified = self.destringify(raw_response, @use_floats)
|
75
|
+
series = Hash[destringified["Results"]["series"].map do |raw_series|
|
76
|
+
[raw_series["seriesID"], BLS_API::Series.new(raw_series)]
|
77
|
+
end]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require "bigdecimal"
|
2
|
+
|
3
|
+
module BLS_API
|
4
|
+
module Destringify
|
5
|
+
# Public: Convert numeric data in a BLS API response from Strings to
|
6
|
+
# instances of more useful Numeric subclasses for the user's convenience.
|
7
|
+
#
|
8
|
+
# (My guess is BLS sends data as strings in order to maintain precision and
|
9
|
+
# simplify the JSON conversion on their end.)
|
10
|
+
#
|
11
|
+
# raw_response - A Hash of parsed JSON data from an API response, such as
|
12
|
+
# that returned by BLS_API::RawRequest#make_api_request.
|
13
|
+
# use_floats - An optional Boolean specifying whether to express values
|
14
|
+
# as Floats (for simplicity) instead of as BigDecimals
|
15
|
+
# (for precision) (default: false).
|
16
|
+
#
|
17
|
+
# Returns a Hash similar to raw_response but with some keys and values
|
18
|
+
# converted to Numerics.
|
19
|
+
def destringify(raw_response, use_floats = false)
|
20
|
+
output = {}
|
21
|
+
|
22
|
+
raw_response.keys.reject { |key| key == "Results" }.each do |key|
|
23
|
+
output[key] = raw_response[key]
|
24
|
+
end
|
25
|
+
|
26
|
+
output_results = {}
|
27
|
+
output["Results"] = output_results
|
28
|
+
raw_results = raw_response["Results"]
|
29
|
+
raw_results.keys.reject { |key| key == "series" }.each do |key|
|
30
|
+
output_results[key] = raw_results[key]
|
31
|
+
end
|
32
|
+
|
33
|
+
output_results["series"] = raw_results["series"].map do |raw_series|
|
34
|
+
self.destringify_series(raw_series, use_floats)
|
35
|
+
end
|
36
|
+
|
37
|
+
output
|
38
|
+
end
|
39
|
+
|
40
|
+
# Internal: Convert the keys and values in a calculations object (change in
|
41
|
+
# an indicator over the past {1,3,6,12} months) from Strings to Numerics.
|
42
|
+
#
|
43
|
+
# raw_calcs - A Hash with String keys (specifying the number of months
|
44
|
+
# over which a given change was calculated) and String values
|
45
|
+
# (specifying the change over that period of time).
|
46
|
+
# use_floats - An optional Boolean specifying whether to express values
|
47
|
+
# as Floats (for simplicity) instead of as BigDecimals
|
48
|
+
# (for precision) (default: false).
|
49
|
+
#
|
50
|
+
# Returns a Hash with Integer keys and BigDecimal values (to preserve
|
51
|
+
# precision).
|
52
|
+
def destringify_calculations(raw_calcs, use_floats = false)
|
53
|
+
Hash[raw_calcs.each_pair.map do |key, value|
|
54
|
+
[
|
55
|
+
key.to_i,
|
56
|
+
|
57
|
+
case use_floats
|
58
|
+
when true then value.to_f
|
59
|
+
else BigDecimal.new(value)
|
60
|
+
end
|
61
|
+
]
|
62
|
+
end]
|
63
|
+
end
|
64
|
+
|
65
|
+
# Internal: Convert all quantitative keys and values in a month object
|
66
|
+
# (statistics and optional changes corresponding to a particular
|
67
|
+
# series/month combination) from Strings to Numerics.
|
68
|
+
#
|
69
|
+
# raw_month - A Hash for a given month's data point. Should contain
|
70
|
+
# "year", "period" and "value" properties, at least.
|
71
|
+
# use_floats - An optional Boolean specifying whether to express values
|
72
|
+
# as Floats (for simplicity) instead of as BigDecimals
|
73
|
+
# (for precision) (default: false).
|
74
|
+
#
|
75
|
+
# Returns a Hash with all of the same keys as `raw_month`.
|
76
|
+
def destringify_month(raw_month, use_floats = false)
|
77
|
+
output = {}
|
78
|
+
|
79
|
+
output["year"] = raw_month["year"].to_i
|
80
|
+
output["value"] = case use_floats
|
81
|
+
when true then raw_month["value"].to_f
|
82
|
+
else BigDecimal.new(raw_month["value"])
|
83
|
+
end
|
84
|
+
|
85
|
+
if raw_month.include?("calculations")
|
86
|
+
output["calculations"] = Hash[
|
87
|
+
raw_month["calculations"].each_pair.map do |name, calcs|
|
88
|
+
[name, self.destringify_calculations(calcs, use_floats)]
|
89
|
+
end
|
90
|
+
]
|
91
|
+
end
|
92
|
+
|
93
|
+
keys_to_pass_through = raw_month.keys.reject do |key|
|
94
|
+
["year", "value", "calculations"].include?(key)
|
95
|
+
end
|
96
|
+
keys_to_pass_through.each { |key| output[key] = raw_month[key] }
|
97
|
+
|
98
|
+
output
|
99
|
+
end
|
100
|
+
|
101
|
+
# Internal: Convert numeric data in a BLS API series from Strings to
|
102
|
+
# Numerics.
|
103
|
+
#
|
104
|
+
# raw_series - An Array of month Hashes from a BLS API response.
|
105
|
+
# use_floats - An optional Boolean specifying whether to express values
|
106
|
+
# as Floats (for simplicity) instead of as BigDecimals
|
107
|
+
# (for precision) (default: false).
|
108
|
+
#
|
109
|
+
# Returns an Array of month Hashes after conversion by #destringify_month.
|
110
|
+
def destringify_series(raw_series, use_floats = false)
|
111
|
+
output = {}
|
112
|
+
|
113
|
+
raw_series.keys.reject { |key| key == "data" }.each do |key|
|
114
|
+
output[key] = raw_series[key]
|
115
|
+
end
|
116
|
+
|
117
|
+
output["data"] = raw_series["data"].map do |raw_month|
|
118
|
+
self.destringify_month(raw_month, use_floats)
|
119
|
+
end
|
120
|
+
|
121
|
+
output
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "bls_api/errors"
|
2
|
+
|
3
|
+
module BLS_API
|
4
|
+
class Month
|
5
|
+
def initialize(raw_month)
|
6
|
+
@raw_month = raw_month
|
7
|
+
end
|
8
|
+
|
9
|
+
def year
|
10
|
+
@raw_month["year"]
|
11
|
+
end
|
12
|
+
|
13
|
+
def month
|
14
|
+
@raw_month["period"].slice(/^M(\d+)$/, 1).to_i
|
15
|
+
end
|
16
|
+
|
17
|
+
def value
|
18
|
+
@raw_month["value"]
|
19
|
+
end
|
20
|
+
|
21
|
+
def footnotes
|
22
|
+
non_empty_footnotes = @raw_month["footnotes"].reject { |x| x.empty? }
|
23
|
+
Hash[non_empty_footnotes.map do |footnote|
|
24
|
+
[footnote["code"], footnote["text"]]
|
25
|
+
end]
|
26
|
+
end
|
27
|
+
|
28
|
+
def net_change(timeframe)
|
29
|
+
unless @raw_month.include?("calculations")
|
30
|
+
raise BLS_API::NotRetrievedError, "Calculations not retrieved"
|
31
|
+
end
|
32
|
+
net_changes = @raw_month["calculations"].fetch("net_changes") do
|
33
|
+
raise BLS_API::NotRetrievedError, (
|
34
|
+
"Net-change calculations not available from BLS")
|
35
|
+
end
|
36
|
+
net_changes.fetch(timeframe) do
|
37
|
+
raise BLS_API::NotRetrievedError, (
|
38
|
+
"#{timeframe}-month net changes not available from BLS")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def percent_change(timeframe)
|
43
|
+
unless @raw_month.include?("calculations")
|
44
|
+
raise BLS_API::NotRetrievedError, "Calculations not retrieved"
|
45
|
+
end
|
46
|
+
percent_changes = @raw_month["calculations"].fetch("pct_changes") do
|
47
|
+
raise BLS_API::NotRetrievedError, (
|
48
|
+
"Percent-change calculations not available from BLS")
|
49
|
+
end
|
50
|
+
percent_changes.fetch(timeframe) do
|
51
|
+
raise BLS_API::NotRetrievedError, (
|
52
|
+
"#{timeframe}-month percent changes not available from BLS")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def <=>(other)
|
57
|
+
year_comparison = self.year <=> other.year
|
58
|
+
return year_comparison unless year_comparison == 0
|
59
|
+
|
60
|
+
month_comparison = self.month <=> other.month
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
require "bls_api/constants"
|
4
|
+
require "bls_api/errors"
|
5
|
+
|
6
|
+
module BLS_API
|
7
|
+
module RawRequest
|
8
|
+
# Internal: Request a batch of data from the BLS API.
|
9
|
+
#
|
10
|
+
# By default, raises BLS_API::Errors::APIError if the request is
|
11
|
+
# unsuccessful. (You can catch this with an IOError, if that's more your
|
12
|
+
# thing.)
|
13
|
+
#
|
14
|
+
# options - A Hash with three required arguments and four optional
|
15
|
+
# arguments.
|
16
|
+
# Required arguments include:
|
17
|
+
# :series_ids - An Array of String series IDs for which to
|
18
|
+
# request data. If a String is provided instead, it
|
19
|
+
# is assumed to be a single series ID.
|
20
|
+
# :start_year - An Integer representing the earliest year for
|
21
|
+
# which to request data.
|
22
|
+
# :end_year - An Integer representing the latest year for which
|
23
|
+
# to request data. Note that the BLS API will
|
24
|
+
# return an error if you specify a year for which
|
25
|
+
# no data exists; for example, an :end_year of 2016
|
26
|
+
# will raise an error during January of 2016 when
|
27
|
+
# no 2016 data has yet been released.
|
28
|
+
# Optional arguments include:
|
29
|
+
# :catch_errors - A Boolean specifying whether to raise an
|
30
|
+
# APIError if the request is unsuccessful
|
31
|
+
# (default: true).
|
32
|
+
# :catalog - A Boolean specifying whether to include
|
33
|
+
# catalog data in the response
|
34
|
+
# (default: true).
|
35
|
+
# :calculations - A Boolean specifying whether to include
|
36
|
+
# net-change and percent-change calculations
|
37
|
+
# in the response (default: true).
|
38
|
+
# :annual_averages - A Boolean specifying whether to include
|
39
|
+
# annual averages in the response
|
40
|
+
# (default: true).
|
41
|
+
#
|
42
|
+
# Returns a Hash of the parsed JSON response from the API.
|
43
|
+
def make_api_request(options = {})
|
44
|
+
# Ensure required arguments are provided.
|
45
|
+
series_ids = options.fetch(:series_ids) do
|
46
|
+
raise BLS_API::Errors::OptionsError, "Missing series IDs"
|
47
|
+
end
|
48
|
+
series_ids = [series_ids] if series_ids.is_a?(String)
|
49
|
+
start_year = options.fetch(:start_year) do
|
50
|
+
raise BLS_API::Errors::OptionsError, "Missing start year"
|
51
|
+
end
|
52
|
+
end_year = options.fetch(:end_year) do
|
53
|
+
raise BLS_API::Errors::OptionsError, "Missing end year"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Build and make the API request.
|
57
|
+
endpoint_uri = URI.parse(BLS_API::Constants::ENDPOINT_URL)
|
58
|
+
req = Net::HTTP::Post.new(endpoint_uri.path)
|
59
|
+
req.body = {
|
60
|
+
"seriesid" => series_ids,
|
61
|
+
"startyear" => start_year.to_s,
|
62
|
+
"endyear" => end_year.to_s,
|
63
|
+
"annualaverage" => options.fetch(:annual_averages, true),
|
64
|
+
"calculations" => options.fetch(:calculations, true),
|
65
|
+
"catalog" => options.fetch(:catalog, true),
|
66
|
+
"registrationKey" => self.api_key
|
67
|
+
}.to_json
|
68
|
+
req.content_type = "application/json"
|
69
|
+
res = Net::HTTP.start(endpoint_uri.host, endpoint_uri.port) do |http|
|
70
|
+
http.request(req)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Let the user know if the API request failed.
|
74
|
+
parsed = JSON.parse(res.body)
|
75
|
+
catch_errors = options.fetch(:catch_errors, true)
|
76
|
+
if catch_errors && parsed["status"] != "REQUEST_SUCCEEDED"
|
77
|
+
error_messages = %Q{"#{parsed["message"].join(%Q{", "})}"}
|
78
|
+
error_message = <<-EOF.gsub(/^ */, "").gsub(/\r?\n/, " ").strip
|
79
|
+
BLS API returned an error: #{parsed["status"]}
|
80
|
+
Additional messages were: #{error_messages}
|
81
|
+
EOF
|
82
|
+
raise BLS_API::Errors::APIError, error_message
|
83
|
+
end
|
84
|
+
|
85
|
+
# Return the whole API response.
|
86
|
+
parsed
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "ostruct"
|
2
|
+
|
3
|
+
require "bls_api/month"
|
4
|
+
|
5
|
+
module BLS_API
|
6
|
+
class Series
|
7
|
+
include BLS_API::Destringify
|
8
|
+
|
9
|
+
attr_reader :id
|
10
|
+
|
11
|
+
def initialize(raw_series)
|
12
|
+
@id = raw_series["seriesID"]
|
13
|
+
begin
|
14
|
+
@raw_series = self.destringify_series(raw_series)
|
15
|
+
rescue ArgumentError
|
16
|
+
# Series was already destringified _and_ it was using Floats.
|
17
|
+
@raw_series = raw_series
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: Return catalog information for this series if available.
|
22
|
+
#
|
23
|
+
# Returns an OpenStruct if possible; returns nil if no catalog information
|
24
|
+
# was received.
|
25
|
+
def catalog
|
26
|
+
return nil unless @raw_series.include?("catalog")
|
27
|
+
return @catalog unless @catalog.nil?
|
28
|
+
@catalog = OpenStruct.new(@raw_series["catalog"])
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Return information for the given month.
|
32
|
+
#
|
33
|
+
# year - An Integer representing the year for which to retrieve data.
|
34
|
+
# month - An Integer representing the month (1 = January) for which to
|
35
|
+
# retrieve data.
|
36
|
+
#
|
37
|
+
# Returns a BLS_API::Month.
|
38
|
+
def get_month(year, month)
|
39
|
+
self.monthly # Ensure we've converted all months.
|
40
|
+
@months.detect do |parsed_month|
|
41
|
+
parsed_month.year == year && parsed_month.month == month
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public: Return information for all months.
|
46
|
+
#
|
47
|
+
# Returns an Array of BLS_API::Months.
|
48
|
+
def monthly
|
49
|
+
return @months unless @months.nil?
|
50
|
+
@months = @raw_series["data"].map do |month_data|
|
51
|
+
BLS_API::Month.new(month_data)
|
52
|
+
end
|
53
|
+
@months.sort!.reverse!
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/bls_api.rb
ADDED
metadata
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bls_api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Justin Myers
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-03-03 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.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: climate_control
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.0.3
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.0.3
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: webmock
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.21'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.21'
|
83
|
+
description: API wrapper for data from the U.S. Bureau of Labor Statistics.
|
84
|
+
email:
|
85
|
+
- jmyers@ap.org
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- ".rspec"
|
92
|
+
- ".travis.yml"
|
93
|
+
- Gemfile
|
94
|
+
- LICENSE
|
95
|
+
- README.md
|
96
|
+
- Rakefile
|
97
|
+
- bin/console
|
98
|
+
- bin/setup
|
99
|
+
- bls_api.gemspec
|
100
|
+
- lib/bls_api.rb
|
101
|
+
- lib/bls_api/client.rb
|
102
|
+
- lib/bls_api/constants.rb
|
103
|
+
- lib/bls_api/destringify.rb
|
104
|
+
- lib/bls_api/errors.rb
|
105
|
+
- lib/bls_api/month.rb
|
106
|
+
- lib/bls_api/raw_request.rb
|
107
|
+
- lib/bls_api/series.rb
|
108
|
+
- lib/bls_api/version.rb
|
109
|
+
homepage: https://github.com/associatedpress/bls_api
|
110
|
+
licenses:
|
111
|
+
- MIT
|
112
|
+
metadata: {}
|
113
|
+
post_install_message:
|
114
|
+
rdoc_options: []
|
115
|
+
require_paths:
|
116
|
+
- lib
|
117
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
requirements: []
|
128
|
+
rubyforge_project:
|
129
|
+
rubygems_version: 2.4.5.1
|
130
|
+
signing_key:
|
131
|
+
specification_version: 4
|
132
|
+
summary: API wrapper for data from the U.S. Bureau of Labor Statistics.
|
133
|
+
test_files: []
|