weather-forecasts 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe WeatherForecasts::Client::SelectHourlyQuery do
4
+ let(:conditions) {
5
+ {
6
+ :latitude => 38.99,
7
+ :longitude => -77.01
8
+ }
9
+ }
10
+
11
+ subject { WeatherForecasts.client.select_hourly }
12
+
13
+ describe "#execute" do
14
+ before(:each) do
15
+ VCR.use_cassette('select_hourly_query') do
16
+ @response = subject.where(conditions).execute
17
+ end
18
+ end
19
+
20
+ it "returns a valid response" do
21
+ @response.should be_a(Hash)
22
+ @response[:parameters]["point1"][:temperature][:hourly][:values].first[:value].should be_a(Numeric)
23
+ end
24
+ end
25
+ end
@@ -23,10 +23,12 @@ Gem::Specification.new do |s|
23
23
 
24
24
  s.executables << 'wf-console'
25
25
 
26
+ s.add_runtime_dependency 'dwml', '~> 1.1.0', '>= 1.1.0'
26
27
  s.add_runtime_dependency 'savon', '~> 2.4.0', '>= 2.4.0'
28
+ s.add_runtime_dependency 'httpi', '~>2.1.0', '>= 2.1.0'
27
29
  s.add_runtime_dependency 'nokogiri', '~> 1.6.1', '>= 1.6.1'
28
30
  s.add_runtime_dependency 'multi_json', '~> 1.9.0', '>= 1.9.0'
29
- s.add_runtime_dependency 'activesupport', '~> 4.0.3', '>= 4.0.3'
31
+ s.add_runtime_dependency 'activesupport', '~> 4.1.0', '>= 4.1.0'
30
32
  s.add_runtime_dependency 'http_logger', '~> 0.4.2', '>= 0.4.2'
31
33
 
32
34
  s.post_install_message = "To start querying weather forecasts immediately, type `wf-console`."
metadata CHANGED
@@ -1,15 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: weather-forecasts
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Angelo Lakra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-02 00:00:00.000000000 Z
11
+ date: 2014-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dwml
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.1.0
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.1.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 1.1.0
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.1.0
13
33
  - !ruby/object:Gem::Dependency
14
34
  name: savon
15
35
  requirement: !ruby/object:Gem::Requirement
@@ -30,6 +50,26 @@ dependencies:
30
50
  - - ">="
31
51
  - !ruby/object:Gem::Version
32
52
  version: 2.4.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: httpi
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: 2.1.0
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 2.1.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 2.1.0
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 2.1.0
33
73
  - !ruby/object:Gem::Dependency
34
74
  name: nokogiri
35
75
  requirement: !ruby/object:Gem::Requirement
@@ -76,20 +116,20 @@ dependencies:
76
116
  requirements:
77
117
  - - "~>"
78
118
  - !ruby/object:Gem::Version
79
- version: 4.0.3
119
+ version: 4.1.0
80
120
  - - ">="
81
121
  - !ruby/object:Gem::Version
82
- version: 4.0.3
122
+ version: 4.1.0
83
123
  type: :runtime
84
124
  prerelease: false
85
125
  version_requirements: !ruby/object:Gem::Requirement
86
126
  requirements:
87
127
  - - "~>"
88
128
  - !ruby/object:Gem::Version
89
- version: 4.0.3
129
+ version: 4.1.0
90
130
  - - ">="
91
131
  - !ruby/object:Gem::Version
92
- version: 4.0.3
132
+ version: 4.1.0
93
133
  - !ruby/object:Gem::Dependency
94
134
  name: http_logger
95
135
  requirement: !ruby/object:Gem::Requirement
@@ -140,22 +180,18 @@ files:
140
180
  - lib/weather_forecasts/client/query/select_coordinates_by_zip_query.rb
141
181
  - lib/weather_forecasts/client/query/select_corner_coordinates_query.rb
142
182
  - lib/weather_forecasts/client/query/select_gridpoint_coordinates_query.rb
183
+ - lib/weather_forecasts/client/query/select_hourly_query.rb
143
184
  - lib/weather_forecasts/client/query/select_linepoint_coordinates_query.rb
144
185
  - lib/weather_forecasts/client/query/select_query.rb
145
186
  - lib/weather_forecasts/client/query/select_square_coordinates_query.rb
146
187
  - lib/weather_forecasts/client/query/selection_attributes.rb
147
188
  - lib/weather_forecasts/client/version.rb
148
- - lib/weather_forecasts/dwml.rb
149
- - lib/weather_forecasts/dwml/data_extractor.rb
150
- - lib/weather_forecasts/dwml/head_extractor.rb
151
- - lib/weather_forecasts/dwml/location.rb
152
- - lib/weather_forecasts/dwml/parameter_extractor.rb
153
- - lib/weather_forecasts/dwml/time_layout.rb
154
189
  - spec/fixtures/vcr_cassettes/select_by_days_query.yml
155
190
  - spec/fixtures/vcr_cassettes/select_coordinates_by_cities_query.yml
156
191
  - spec/fixtures/vcr_cassettes/select_coordinates_by_zip_query.yml
157
192
  - spec/fixtures/vcr_cassettes/select_corner_coordinates_query.yml
158
193
  - spec/fixtures/vcr_cassettes/select_gridpoint_coordinates_query.yml
194
+ - spec/fixtures/vcr_cassettes/select_hourly_query.yml
159
195
  - spec/fixtures/vcr_cassettes/select_linepoint_coordinates_query.yml
160
196
  - spec/fixtures/vcr_cassettes/select_query.yml
161
197
  - spec/fixtures/vcr_cassettes/select_square_coordinates_query.yml
@@ -165,6 +201,7 @@ files:
165
201
  - spec/lib/ndfd/client/query/select_coordinates_by_zip_query_spec.rb
166
202
  - spec/lib/ndfd/client/query/select_corner_coordinates_query_spec.rb
167
203
  - spec/lib/ndfd/client/query/select_gridpoint_coordinates_query_spec.rb
204
+ - spec/lib/ndfd/client/query/select_hourly_query_spec.rb
168
205
  - spec/lib/ndfd/client/query/select_linepoint_coordinates_query_spec.rb
169
206
  - spec/lib/ndfd/client/query/select_query_spec.rb
170
207
  - spec/lib/ndfd/client/query/select_square_coordinates_query_spec.rb
@@ -204,6 +241,7 @@ test_files:
204
241
  - spec/fixtures/vcr_cassettes/select_coordinates_by_zip_query.yml
205
242
  - spec/fixtures/vcr_cassettes/select_corner_coordinates_query.yml
206
243
  - spec/fixtures/vcr_cassettes/select_gridpoint_coordinates_query.yml
244
+ - spec/fixtures/vcr_cassettes/select_hourly_query.yml
207
245
  - spec/fixtures/vcr_cassettes/select_linepoint_coordinates_query.yml
208
246
  - spec/fixtures/vcr_cassettes/select_query.yml
209
247
  - spec/fixtures/vcr_cassettes/select_square_coordinates_query.yml
@@ -213,6 +251,7 @@ test_files:
213
251
  - spec/lib/ndfd/client/query/select_coordinates_by_zip_query_spec.rb
214
252
  - spec/lib/ndfd/client/query/select_corner_coordinates_query_spec.rb
215
253
  - spec/lib/ndfd/client/query/select_gridpoint_coordinates_query_spec.rb
254
+ - spec/lib/ndfd/client/query/select_hourly_query_spec.rb
216
255
  - spec/lib/ndfd/client/query/select_linepoint_coordinates_query_spec.rb
217
256
  - spec/lib/ndfd/client/query/select_query_spec.rb
218
257
  - spec/lib/ndfd/client/query/select_square_coordinates_query_spec.rb
@@ -1,38 +0,0 @@
1
- require 'multi_json'
2
-
3
- require 'weather_forecasts/dwml/head_extractor'
4
- require 'weather_forecasts/dwml/data_extractor'
5
-
6
- #
7
- # Note: See http://graphical.weather.gov/xml/mdl/XML/Design/MDL_XML_Design.pdf
8
- # for authoritative type definitions
9
- ################################################################################
10
-
11
- module WeatherForecasts
12
- class DWML
13
- attr_reader :output, :xmldoc
14
-
15
- def initialize(xmldoc)
16
- @xmldoc = xmldoc
17
- @output = {}
18
- end
19
-
20
- def process
21
- build_head
22
- build_data
23
- output
24
- end
25
-
26
- protected
27
-
28
- def build_head
29
- extractor = HeadExtractor.new(xmldoc.xpath("//dwml/head").first)
30
- @output.merge!(extractor.process)
31
- end
32
-
33
- def build_data
34
- extractor = DataExtractor.new(xmldoc.xpath("//dwml/data").first)
35
- @output.merge!(extractor.process)
36
- end
37
- end
38
- end
@@ -1,55 +0,0 @@
1
- require 'weather_forecasts/dwml/location'
2
- require 'weather_forecasts/dwml/time_layout'
3
- require 'weather_forecasts/dwml/parameter_extractor'
4
-
5
- module WeatherForecasts
6
- class DWML
7
- class DataExtractor
8
- attr_reader :output, :element
9
-
10
- def initialize(element)
11
- @element = element
12
- @locations = []
13
- @time_layouts = []
14
- @output = {}
15
- end
16
-
17
- def process
18
- extract_locations
19
- extract_time_layouts
20
- extract_parameters
21
-
22
- output
23
- end
24
-
25
- protected
26
-
27
- def extract_locations
28
- @locations = Location.extract(element.xpath("location"))
29
- end
30
-
31
- def extract_time_layouts
32
- @time_layouts = TimeLayout.extract(element.xpath("time-layout"))
33
- end
34
-
35
- def extract_parameters
36
- parameters = element.xpath("parameters")
37
-
38
- @output.merge!(
39
- :parameters => parameters.inject({}) do |memo, parameter|
40
- location = location_for_parameter(parameter)
41
- extractor = ParameterExtractor.new(parameter, location, @time_layouts)
42
- memo.merge!(location.location_key => extractor.process)
43
- memo
44
- end
45
- )
46
- end
47
-
48
- def location_for_parameter(parameter)
49
- @locations.detect do |location|
50
- parameter.attributes["applicable-location"].text == location.location_key
51
- end
52
- end
53
- end
54
- end
55
- end
@@ -1,49 +0,0 @@
1
- module WeatherForecasts
2
- class DWML
3
- class HeadExtractor
4
- attr_reader :output, :element
5
-
6
- def initialize(element)
7
- @element = element
8
- @output = {}
9
- end
10
-
11
- def process
12
- build_product
13
- build_source
14
- output
15
- end
16
-
17
- protected
18
-
19
- def build_product
20
- creation_date = Time.zone.parse(element.xpath('product/creation-date').text)
21
-
22
- @output.merge!(
23
- :product => {
24
- :title => element.xpath('product/title').text,
25
- :field => element.xpath('product/field').text,
26
- :category => element.xpath('product/category').text,
27
- :creation_date => creation_date
28
- }
29
- )
30
- end
31
-
32
- def build_source
33
- sub_center = element.xpath('source/production-center/sub-center').text
34
- production_center = element.xpath('source/production-center').text
35
-
36
- @output.merge!(
37
- :source => {
38
- :more_information => element.xpath('source/more-information').text,
39
- :product_center => production_center.gsub(sub_center, " - #{sub_center}"),
40
- :disclaimer => element.xpath('source/disclaimer').text,
41
- :credit => element.xpath('source/credit').text,
42
- :credit_logo => element.xpath('source/credit-logo').text,
43
- :feedback => element.xpath('source/feedback').text
44
- }
45
- )
46
- end
47
- end
48
- end
49
- end
@@ -1,33 +0,0 @@
1
- module WeatherForecasts
2
- class DWML
3
- class Location
4
- class << self
5
- def extract(elements)
6
- elements.map { |element| new(element) }
7
- end
8
- end
9
-
10
- attr_reader :element, :location_key, :latitude, :longitude
11
-
12
- def initialize(element)
13
- @element = element
14
-
15
- extract_key
16
- extract_coords
17
- end
18
-
19
- protected
20
-
21
- def extract_key
22
- @location_key = element.xpath("location-key").first.text
23
- end
24
-
25
- def extract_coords
26
- point = element.xpath('point').first
27
-
28
- @latitude = point.attributes["latitude"].text.to_f
29
- @longitude = point.attributes["longitude"].text.to_f
30
- end
31
- end
32
- end
33
- end
@@ -1,281 +0,0 @@
1
- module WeatherForecasts
2
- class DWML
3
- class ParameterExtractor
4
- attr_reader :output, :element, :location, :time_layouts
5
-
6
- def initialize(element, location, time_layouts)
7
- @element = element
8
- @location = location
9
- @time_layouts = time_layouts
10
-
11
- @output = {
12
- :latitude => location.latitude,
13
- :longitude => location.longitude
14
- }
15
- end
16
-
17
- def process
18
- extract_temperatures
19
- extract_precipitation
20
- extract_wind_speed
21
- extract_wind_direction
22
- extract_cloud_cover
23
- extract_probability_of_precipitation
24
- extract_fire_weather
25
- extract_convective_hazard
26
- extract_climate_anomaly
27
- extract_humidity
28
- extract_weather
29
- extract_conditions_icons
30
- extract_hazards
31
- extract_water_state
32
-
33
- output
34
- end
35
-
36
- protected
37
-
38
- def extract_temperatures
39
- extract_basic_time_series(:temperature)
40
- end
41
-
42
- def extract_precipitation
43
- extract_basic_time_series(:precipitation)
44
- end
45
-
46
- def extract_wind_speed
47
- extract_basic_time_series(:"wind-speed")
48
- end
49
-
50
- def extract_wind_direction
51
- extract_basic_time_series(:direction)
52
- end
53
-
54
- def extract_cloud_cover
55
- extract_basic_time_series(:"cloud-amount")
56
- end
57
-
58
- def extract_probability_of_precipitation
59
- extract_basic_time_series(:"probability-of-precipitation")
60
- end
61
-
62
- def extract_humidity
63
- extract_basic_time_series(:humidity)
64
- end
65
-
66
- def extract_fire_weather
67
- extract_basic_time_series(:"fire-weather")
68
- end
69
-
70
- def extract_water_state
71
- node = element.xpath("water-state").first
72
- return if node.blank?
73
-
74
- @output[:"water-state"] = {}
75
- layout = lookup_time_layout(node)
76
-
77
- waves_node = node.xpath("waves").first
78
- @output[:"water-state"][:type] = waves_node.attributes["type"].text
79
- @output[:"water-state"][:unit] = waves_node.attributes["units"].text
80
- @output[:"water-state"][:values] = []
81
-
82
- waves_node.xpath("value").each_with_index do |value, index|
83
- @output[:"water-state"][:values] << {
84
- :value => value.text,
85
- :start_time => layout.valid_times[index].start
86
- }
87
- end
88
- end
89
-
90
- def extract_hazards
91
- node = element.xpath("hazards").first
92
- return if node.blank?
93
-
94
- layout = lookup_time_layout(node)
95
-
96
- @output[:hazards] = {
97
- :name => node.xpath("name").first.text,
98
- :conditions => node.xpath("hazard-conditions").each_with_index.map do |condition_node, index|
99
- hazard_node = condition_node.xpath("hazard").first
100
- next if hazard_node.blank?
101
-
102
- {
103
- :code => hazard_node.attributes["hazardCode"].text,
104
- :phenomena => hazard_node.attributes["phenomena"].text,
105
- :significance => hazard_node.attributes["significance"].text,
106
- :type => hazard_node.attributes["hazardType"].text,
107
- :url => hazard_node.xpath("hazardTextURL").first.text,
108
- :start_time => layout.valid_times[index].start
109
- }
110
- end.compact
111
- }
112
- end
113
-
114
- def extract_conditions_icons
115
- node = element.xpath("conditions-icon").first
116
- return if node.blank?
117
-
118
- layout = lookup_time_layout(node)
119
-
120
- @output[:"conditions-icon"] = {
121
- :name => node.xpath("name").text,
122
- :type => node.attributes["type"].text,
123
- :links => node.xpath("icon-link").each_with_index.map do |icon_node, index|
124
- {
125
- :link => icon_node.text,
126
- :start_time => layout.valid_times[index].start
127
- }
128
- end
129
- }
130
- end
131
-
132
- def extract_convective_hazard
133
- return if element.xpath("convective-hazard").blank?
134
-
135
- @output[:"convective-hazard"] ||= {
136
- :outlook => { :name => nil, :values => []},
137
- :"severe-component" => []
138
- }
139
-
140
- extract_convective_hazard_outlook
141
- extract_convective_hazard_severity
142
- end
143
-
144
- def extract_convective_hazard_outlook
145
- outlook_node = element.xpath("convective-hazard/outlook").first
146
- layout = lookup_time_layout(outlook_node)
147
-
148
- @output[:"convective-hazard"][:outlook][:name] = outlook_node.xpath("node").text
149
-
150
- outlook_node.xpath("value").each_with_index do |value, index|
151
- @output[:"convective-hazard"][:outlook][:values] << {
152
- :start_time => layout.valid_times[index].start,
153
- :end_time => layout.valid_times[index].stop,
154
- :value => value.text
155
- }
156
- end
157
- end
158
-
159
- def extract_convective_hazard_severity
160
- element.xpath("convective-hazard/severe-component").each do |node|
161
- layout = lookup_time_layout(node)
162
-
163
- hsh = {
164
- :name => node.xpath("name").first.text,
165
- :type => node.attributes["type"].text,
166
- :unit => node.attributes["units"].text,
167
- :values => node.xpath("value").each_with_index.map do |value, index|
168
- {
169
- :value => value.text,
170
- :start_time => layout.valid_times[index].start,
171
- :end_time => layout.valid_times[index].stop
172
- }
173
- end
174
- }
175
-
176
- @output[:"convective-hazard"][:"severe-component"] << hsh
177
- end
178
- end
179
-
180
- def extract_climate_anomaly
181
- return if element.xpath("climate-anomaly").blank?
182
-
183
- [:weekly, :monthly, :seasonal].each do |period|
184
- element.xpath("climate-anomaly/#{period.to_s}").each_with_index do |node|
185
- layout = lookup_time_layout(node)
186
- valid_time = layout.valid_times.first
187
-
188
- @output[:"climate-anomaly"] ||= {}
189
- @output[:"climate-anomaly"][period] ||= []
190
- @output[:"climate-anomaly"][period] << {
191
- :name => node.xpath("name").first.text,
192
- :value => node.xpath("value").first.text,
193
- :type => node.attributes["type"].text,
194
- :unit => node.attributes["units"].text,
195
- :start_time => valid_time.start,
196
- :end_time => valid_time.stop
197
- }
198
- end
199
- end
200
- end
201
-
202
- def extract_weather
203
- node = element.xpath("weather")
204
- return if node.blank?
205
-
206
- node.map do |weather_node|
207
- @output[:weather] ||= {}
208
- @output[:weather][:name] = weather_node.xpath("name").text
209
- @output[:weather][:conditions] ||= []
210
-
211
- layout = lookup_time_layout(weather_node)
212
-
213
- weather_node.xpath("weather-conditions").each_with_index do |condition, index|
214
- value = condition.xpath("value").first
215
- next if value.blank?
216
-
217
- visibility_node = value.xpath("visibility").first
218
- visibility = if visibility_node.present? && visibility_node.text.present?
219
- {
220
- :unit => visibility_node.attributes["units"].text,
221
- :value => visibility_node.text.to_f
222
- }
223
- else
224
- ""
225
- end
226
-
227
- hsh = {
228
- :start_time => layout.valid_times[index].start,
229
- :coverage => value.attributes["coverage"].text,
230
- :intensity => value.attributes["intensity"].text,
231
- :"weather-type" => value.attributes["weather-type"].text,
232
- :qualifier => value.attributes["qualifier"].text,
233
- :visibility => visibility
234
- }
235
-
236
- additive = value.attributes["additive"]
237
- hsh.merge!(:additive => additive) if additive.present?
238
-
239
- @output[:weather][:conditions] << hsh
240
- end
241
- end
242
- end
243
-
244
- def extract_basic_time_series(metric)
245
- metric_node = element.xpath(metric.to_s)
246
- return if metric_node.blank?
247
-
248
- metric_node.map do |node|
249
- layout = lookup_time_layout(node)
250
- type = node.attributes["type"].text.to_sym
251
- unit = node.attributes["units"].try(:text)
252
-
253
- @output[metric] ||= {}
254
- @output[metric][type] ||= {}
255
- @output[metric][type][:name] = node.xpath("name").text
256
- @output[metric][type][:values] = []
257
-
258
- node.xpath("value").each_with_index do |value, index|
259
- hsh = {
260
- :value => value.text.to_f,
261
- :start_time => layout.valid_times[index].start
262
- }
263
-
264
- end_time = layout.valid_times[index].stop
265
-
266
- hsh.merge!(:unit => unit) if unit.present?
267
- hsh.merge!(:end_time => end_time) if end_time.present?
268
-
269
- @output[metric][type][:values] << hsh
270
- end
271
- end
272
- end
273
-
274
- def lookup_time_layout(node)
275
- @time_layouts.detect do |layout|
276
- node.attributes["time-layout"].text == layout.layout_key
277
- end
278
- end
279
- end
280
- end
281
- end