metar-parser 0.9.11 → 0.9.12
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.
- data/README.md +15 -9
- data/Rakefile +23 -14
- data/bin/download_raw.rb +0 -2
- data/lib/metar/data.rb +84 -73
- data/lib/metar/parser.rb +2 -8
- data/lib/metar/raw.rb +1 -5
- data/lib/metar/report.rb +22 -48
- data/lib/metar/station.rb +16 -63
- data/lib/metar/version.rb +1 -1
- data/locales/en.yml +8 -2
- data/locales/it.yml +6 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/unit/distance_spec.rb +85 -0
- data/spec/unit/parser_spec.rb +234 -0
- data/spec/unit/pressure_spec.rb +32 -0
- data/spec/unit/raw_spec.rb +196 -0
- data/spec/unit/report_spec.rb +187 -0
- data/spec/unit/runway_visible_range_spec.rb +88 -0
- data/spec/unit/sky_condition_spec.rb +73 -0
- data/spec/unit/speed_spec.rb +50 -0
- data/spec/unit/station_spec.rb +254 -0
- data/spec/unit/temperature_spec.rb +47 -0
- data/spec/unit/variable_wind_spec.rb +34 -0
- data/spec/unit/vertical_visibility_spec.rb +37 -0
- data/spec/unit/visibility_spec.rb +84 -0
- data/spec/unit/weather_phenomenon_spec.rb +68 -0
- data/spec/unit/wind_spec.rb +99 -0
- metadata +85 -18
- data/test/all_tests.rb +0 -6
- data/test/metar_test_helper.rb +0 -33
- data/test/unit/data_test.rb +0 -376
- data/test/unit/parser_test.rb +0 -144
- data/test/unit/raw_test.rb +0 -37
- data/test/unit/report_test.rb +0 -105
- data/test/unit/station_test.rb +0 -88
@@ -0,0 +1,73 @@
|
|
1
|
+
load File.expand_path( '../spec_helper.rb', File.dirname(__FILE__) )
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
RSpec::Matchers.define :be_sky_condition do | quantity, height, type |
|
5
|
+
match do | sk |
|
6
|
+
if sk.nil? && quantity == :expect_nil
|
7
|
+
true
|
8
|
+
elsif sk.nil? && quantity != :expect_nil
|
9
|
+
false
|
10
|
+
elsif sk.quantity != quantity
|
11
|
+
false
|
12
|
+
elsif sk.height.is_a?(Metar::Distance) && sk.height.value != height
|
13
|
+
false
|
14
|
+
elsif sk.type != type
|
15
|
+
false
|
16
|
+
else
|
17
|
+
true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe Metar::SkyCondition do
|
23
|
+
|
24
|
+
context '.parse' do
|
25
|
+
|
26
|
+
[
|
27
|
+
[ 'understands clear skies codes', 'NSC', [ nil, nil, nil ] ],
|
28
|
+
[ 'quantity + height', 'BKN12', [ 'broken', 360, nil ] ],
|
29
|
+
[ 'quantity + height + type', 'BKN12CB', [ 'broken', 360, 'cumulonimbus' ] ],
|
30
|
+
[ 'quantity + height + ///', 'BKN12///', [ 'broken', 360, nil ] ],
|
31
|
+
[ 'returns nil for unmatched', 'FUBAR', [ :expect_nil, nil, nil ] ],
|
32
|
+
].each do | docstring, raw, expected |
|
33
|
+
example docstring do
|
34
|
+
Metar::SkyCondition.parse( raw ).should be_sky_condition( *expected )
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
context '.to_summary' do
|
41
|
+
|
42
|
+
[
|
43
|
+
[ 'all values nil', [ nil, nil, nil ], 'clear skies' ],
|
44
|
+
[ 'quantity', [ 'broken', nil, nil ], 'broken cloud' ],
|
45
|
+
[ 'quantity + type', [ 'broken', nil, 'cumulonimbus'], 'broken cumulonimbus' ],
|
46
|
+
].each do | docstring, ( quantity, height, type ), expected |
|
47
|
+
example docstring do
|
48
|
+
sk = Metar::SkyCondition.new( quantity, height, type )
|
49
|
+
|
50
|
+
sk.to_summary. should == expected
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
context '.to_s' do
|
57
|
+
|
58
|
+
[
|
59
|
+
[ 'all values nil', [ nil, nil, nil ], 'clear skies' ],
|
60
|
+
[ 'quantity', [ 'broken', 360, nil ], 'broken cloud at 360' ],
|
61
|
+
[ 'quantity + type', [ 'broken', 360, 'cumulonimbus'], 'broken cumulonimbus at 360' ],
|
62
|
+
].each do | docstring, ( quantity, height, type ), expected |
|
63
|
+
example docstring do
|
64
|
+
sk = Metar::SkyCondition.new( quantity, height, type )
|
65
|
+
|
66
|
+
sk.to_s. should == expected
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
load File.expand_path( '../spec_helper.rb', File.dirname(__FILE__) )
|
2
|
+
|
3
|
+
describe Metar::Speed do
|
4
|
+
|
5
|
+
context '.parse' do
|
6
|
+
|
7
|
+
it 'returns nil for nil' do
|
8
|
+
speed = Metar::Speed.parse( nil )
|
9
|
+
|
10
|
+
speed. should be_nil
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'parses knots' do
|
14
|
+
speed = Metar::Speed.parse( '5KT' )
|
15
|
+
|
16
|
+
speed. should be_a( Metar::Speed )
|
17
|
+
speed.value. should be_within( 0.01 ).of( 2.57 )
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'parses meters per second' do
|
21
|
+
speed = Metar::Speed.parse( '7MPS' )
|
22
|
+
|
23
|
+
speed. should be_a( Metar::Speed )
|
24
|
+
speed.value. should be_within( 0.01 ).of( 7.00 )
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'parses kilometers per hour' do
|
28
|
+
speed = Metar::Speed.parse( '14KMH' )
|
29
|
+
|
30
|
+
speed. should be_a( Metar::Speed )
|
31
|
+
speed.value. should be_within( 0.01 ).of( 3.89 )
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'trates straight numbers as kilomters per hour' do
|
35
|
+
speed = Metar::Speed.parse( '14' )
|
36
|
+
|
37
|
+
speed. should be_a( Metar::Speed )
|
38
|
+
speed.value. should be_within( 0.01 ).of( 3.89 )
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'returns nil for other strings' do
|
42
|
+
speed = Metar::Speed.parse( '' )
|
43
|
+
|
44
|
+
speed. should be_nil
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,254 @@
|
|
1
|
+
load File.expand_path( '../spec_helper.rb', File.dirname(__FILE__) )
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
RSpec::Matchers.define :have_attribute do | attribute |
|
7
|
+
match do | object |
|
8
|
+
if ! object.respond_to?( attribute )
|
9
|
+
false
|
10
|
+
elsif object.method( attribute ).arity != 0
|
11
|
+
false
|
12
|
+
else
|
13
|
+
true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe Metar::Station do
|
19
|
+
|
20
|
+
before :each do
|
21
|
+
@file = stub( 'file' )
|
22
|
+
end
|
23
|
+
|
24
|
+
context '.download_local' do
|
25
|
+
|
26
|
+
it 'downloads the station list' do
|
27
|
+
File.stub!( :open => @file )
|
28
|
+
|
29
|
+
Metar::Station. should_receive( :open ).
|
30
|
+
with( /^http/ )
|
31
|
+
|
32
|
+
Metar::Station.download_local
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'saves the list to disk' do
|
36
|
+
Metar::Station.stub!( :open ).and_return( 'aaa' )
|
37
|
+
|
38
|
+
File. should_receive( :open ) do | path, mode, &block |
|
39
|
+
path. should =~ %r(data/nsd_cccc.txt$)
|
40
|
+
mode. should == 'w'
|
41
|
+
block.call @file
|
42
|
+
end
|
43
|
+
@file. should_receive( :write ).
|
44
|
+
with( 'aaa' )
|
45
|
+
|
46
|
+
Metar::Station.download_local
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
context '.load_local' do
|
52
|
+
|
53
|
+
it 'downloads the station list, if missing, then loads it' do
|
54
|
+
File. should_receive( :exist? ).
|
55
|
+
and_return( false )
|
56
|
+
# open-uri call
|
57
|
+
Metar::Station. should_receive( :open ).
|
58
|
+
and_return( 'aaa' )
|
59
|
+
|
60
|
+
outfile = stub( 'outfile' )
|
61
|
+
File. should_receive( :open ).once.with( %r(nsd_cccc.txt), 'w' ) do | *args, &block |
|
62
|
+
block.call outfile
|
63
|
+
end
|
64
|
+
outfile. should_receive( :write ).
|
65
|
+
with( 'aaa' )
|
66
|
+
|
67
|
+
infile = stub( 'infile' )
|
68
|
+
File. should_receive( :open ).once.with( %r(nsd_cccc.txt) ) do | *args, &block |
|
69
|
+
block.call infile
|
70
|
+
end
|
71
|
+
infile. should_receive( :read ).
|
72
|
+
and_return( 'bbb' )
|
73
|
+
|
74
|
+
Metar::Station.load_local. should == 'bbb'
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'loads the file, if already present' do
|
78
|
+
File. should_receive( :exist? ).
|
79
|
+
and_return( true )
|
80
|
+
|
81
|
+
File. should_receive( :open ).once.with( %r(nsd_cccc.txt) )
|
82
|
+
|
83
|
+
Metar::Station.load_local
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'using structures' do
|
89
|
+
|
90
|
+
before :each do
|
91
|
+
preload_data
|
92
|
+
end
|
93
|
+
|
94
|
+
context '.countries' do
|
95
|
+
it 'lists unique countries in alphabetical order' do
|
96
|
+
Metar::Station.countries. should == [ 'Aaaaa', 'Bbbbb', 'Ppppp' ]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context '.all' do
|
101
|
+
it 'lists all stations' do
|
102
|
+
all = Metar::Station.all
|
103
|
+
|
104
|
+
all.map(&:cccc). should == ['PPPP', 'AAAA', 'AAAB', 'BBBA']
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context '.find_by_cccc' do
|
109
|
+
it 'returns the matching station, if is exists' do
|
110
|
+
Metar::Station.find_by_cccc( 'AAAA' ).name.
|
111
|
+
should == 'Airport A1'
|
112
|
+
Metar::Station.find_by_cccc( 'ZZZZ' ).
|
113
|
+
should be_nil
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context '.exist?' do
|
118
|
+
it 'check if the cccc code exists' do
|
119
|
+
Metar::Station.exist?( 'AAAA' ).
|
120
|
+
should be_true
|
121
|
+
Metar::Station.exist?( 'ZZZZ' ).
|
122
|
+
should be_false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context '.find_all_by_country' do
|
127
|
+
it 'lists all stations in a country' do
|
128
|
+
aaaaa = Metar::Station.find_all_by_country( 'Aaaaa' )
|
129
|
+
|
130
|
+
aaaaa.map(&:cccc). should == [ 'AAAA', 'AAAB' ]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def nsd_file
|
135
|
+
#0 1 2 3 4 5 6 7 8 9 10 11 12 13
|
136
|
+
#CCCC;??;???;name;state;country;?;latitude;longitude;latitude;longitude;???;???;?
|
137
|
+
nsd_text =<<EOT
|
138
|
+
PPPP;00;000;Airport P1;;Ppppp;1;11-03S;055-24E;11-03S;055-24E;000;000;P
|
139
|
+
AAAA;00;000;Airport A1;;Aaaaa;1;11-03S;055-24E;11-03S;055-24E;000;000;P
|
140
|
+
AAAB;00;000;Airport A2;;Aaaaa;1;11-03S;055-24E;11-03S;055-24E;000;000;P
|
141
|
+
BBBA;00;000;Airport B1;;Bbbbb;1;11-03S;055-24E;11-03S;055-24E;000;000;P
|
142
|
+
EOT
|
143
|
+
StringIO.new( nsd_text )
|
144
|
+
end
|
145
|
+
|
146
|
+
def preload_data
|
147
|
+
File.stub!( :exist? ).and_return( true )
|
148
|
+
File.stub!( :open ).with( %r(nsd_cccc.txt) ).and_return( nsd_file )
|
149
|
+
Metar::Station.load_local
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
context '.to_longitude' do
|
155
|
+
it 'converts strings to longitude' do
|
156
|
+
Metar::Station.to_longitude('055-24E').
|
157
|
+
should == 55.4
|
158
|
+
end
|
159
|
+
it 'returns nil for badly formed strings' do
|
160
|
+
Metar::Station.to_longitude('aaa').
|
161
|
+
should be_nil
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
context '.to_latitude' do
|
166
|
+
it 'converts strings to latitude' do
|
167
|
+
Metar::Station.to_latitude('11-03S').
|
168
|
+
should == -11.05
|
169
|
+
end
|
170
|
+
it 'returns nil for badly formed strings' do
|
171
|
+
Metar::Station.to_latitude('aaa').
|
172
|
+
should be_nil
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def noaa_data
|
177
|
+
{
|
178
|
+
:cccc => 'DDDD',
|
179
|
+
:name => 'Station name',
|
180
|
+
:state => 'State',
|
181
|
+
:country => 'Country',
|
182
|
+
:longitude => '055-24E',
|
183
|
+
:latitude => '11-03S',
|
184
|
+
:raw => 'DDDD;00;000;Station name;State;Country;1;11-03S;055-24E;11-03S;055-24E;000;000;P',
|
185
|
+
}
|
186
|
+
end
|
187
|
+
|
188
|
+
context 'attributes' do
|
189
|
+
|
190
|
+
subject { Metar::Station.new( 'DDDD', noaa_data ) }
|
191
|
+
it { should have_attribute( :cccc ) }
|
192
|
+
it { should have_attribute( :code ) }
|
193
|
+
it { should have_attribute( :name ) }
|
194
|
+
it { should have_attribute( :state ) }
|
195
|
+
it { should have_attribute( :country ) }
|
196
|
+
it { should have_attribute( :longitude) }
|
197
|
+
it { should have_attribute( :latitude ) }
|
198
|
+
it { should have_attribute( :raw ) }
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
context 'initialization' do
|
203
|
+
|
204
|
+
it 'should fail if cccc is missing' do
|
205
|
+
expect do
|
206
|
+
Metar::Station.new( nil, {} )
|
207
|
+
end. to raise_error( RuntimeError, /must not be nil/ )
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'should fail if cccc is empty' do
|
211
|
+
expect do
|
212
|
+
Metar::Station.new( '', {} )
|
213
|
+
end. to raise_error( RuntimeError, /must not be empty/ )
|
214
|
+
end
|
215
|
+
|
216
|
+
context 'with noaa data' do
|
217
|
+
|
218
|
+
subject { Metar::Station.new( 'DDDD', noaa_data ) }
|
219
|
+
specify { subject.cccc. should == 'DDDD' }
|
220
|
+
specify { subject.name. should == 'Station name' }
|
221
|
+
specify { subject.state. should == 'State' }
|
222
|
+
specify { subject.country. should == 'Country' }
|
223
|
+
specify { subject.longitude. should == 55.4 }
|
224
|
+
specify { subject.latitude. should == -11.05 }
|
225
|
+
specify { subject.raw. should == 'DDDD;00;000;Station name;State;Country;1;11-03S;055-24E;11-03S;055-24E;000;000;P' }
|
226
|
+
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
context 'object navigation' do
|
232
|
+
before :each do
|
233
|
+
@raw = stub('raw', :metar => 'PAIL 061610Z 24006KT 1 3/4SM -SN BKN016 OVC030 M17/M20 A2910 RMK AO2 P0000', :time => '2010/02/06 16:10' )
|
234
|
+
# TODO: hack - once parser returns station this can be removed
|
235
|
+
Metar::Raw. should_receive( :new ).
|
236
|
+
and_return( @raw )
|
237
|
+
end
|
238
|
+
|
239
|
+
subject { Metar::Station.new( 'DDDD', noaa_data ) }
|
240
|
+
|
241
|
+
it '.station should return the Parser' do
|
242
|
+
subject.parser. should be_a Metar::Parser
|
243
|
+
end
|
244
|
+
|
245
|
+
it '.report should return the Report' do
|
246
|
+
Metar::Station. should_receive( :find_by_cccc ).
|
247
|
+
and_return( subject )
|
248
|
+
|
249
|
+
subject.report. should be_a Metar::Report
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
254
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
load File.expand_path( '../spec_helper.rb', File.dirname(__FILE__) )
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
describe Metar::Temperature do
|
5
|
+
|
6
|
+
context '.parse' do
|
7
|
+
|
8
|
+
it 'understands numbers' do
|
9
|
+
t = Metar::Temperature.parse( '5' )
|
10
|
+
|
11
|
+
t.value. should be_within( 0.01 ).of( 5.0 )
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'treats an M-prefix as a negative indicator' do
|
15
|
+
t = Metar::Temperature.parse( 'M5' )
|
16
|
+
|
17
|
+
t.value. should be_within( 0.01 ).of( -5.0 )
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'returns nil for other values' do
|
21
|
+
Metar::Temperature.parse('').
|
22
|
+
should be_nil
|
23
|
+
Metar::Temperature.parse('aaa').
|
24
|
+
should be_nil
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
context '#to_s' do
|
30
|
+
|
31
|
+
it 'abbreviates the units' do
|
32
|
+
t = Metar::Temperature.new( 5 )
|
33
|
+
|
34
|
+
t.to_s. should == '5°C'
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'rounds to the nearest degree' do
|
38
|
+
Metar::Temperature.new( 5.1 ).to_s.
|
39
|
+
should == '5°C'
|
40
|
+
Metar::Temperature.new( 5.5 ).to_s.
|
41
|
+
should == '6°C'
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
load File.expand_path( '../spec_helper.rb', File.dirname(__FILE__) )
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
describe Metar::VariableWind do
|
5
|
+
|
6
|
+
context '.parse' do
|
7
|
+
|
8
|
+
it 'understands nnn + V + nnn' do
|
9
|
+
vw = Metar::VariableWind.parse( '090V180' )
|
10
|
+
|
11
|
+
vw.direction1.value. should == 90.0
|
12
|
+
vw.direction2.value. should == 180.0
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'returns nil for other' do
|
16
|
+
vw = Metar::VariableWind.parse( 'XXX' )
|
17
|
+
|
18
|
+
vw. should be_nil
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
context '#to_s' do
|
24
|
+
|
25
|
+
it 'renders the ' do
|
26
|
+
vw = Metar::VariableWind.parse( '090V180' )
|
27
|
+
|
28
|
+
vw.to_s. should == 'E - S'
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|