bls_api 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ - 2.2.3
5
+ - 1.9.3-p448
6
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in bls_api.gemspec
4
+ gemspec
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
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "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
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
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,11 @@
1
+ module BLS_API
2
+ module Constants
3
+ ENDPOINT_URL = "http://api.bls.gov/publicAPI/v2/timeseries/data/"
4
+
5
+ MAX_SERIES_PER_REQUEST = 50
6
+ MAX_YEARS_PER_REQUEST = 20
7
+
8
+ MAX_RETRIES = 3
9
+ TIME_BETWEEN_RETRIES = 5 # In seconds
10
+ end
11
+ 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,8 @@
1
+ module BLS_API
2
+ module Errors
3
+ class APIError < IOError; end
4
+ class ConfigurationError < ArgumentError; end
5
+ class NotRetrievedError < KeyError; end
6
+ class OptionsError < ArgumentError; end
7
+ end
8
+ 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
@@ -0,0 +1,3 @@
1
+ module BLS_API
2
+ VERSION = "1.0.1"
3
+ end
data/lib/bls_api.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "bls_api/client"
2
+ require "bls_api/constants"
3
+ require "bls_api/destringify"
4
+ require "bls_api/errors"
5
+ require "bls_api/month"
6
+ require "bls_api/raw_request"
7
+ require "bls_api/series"
8
+ require "bls_api/version"
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: []