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,32 @@
|
|
1
|
+
load File.expand_path( '../spec_helper.rb', File.dirname(__FILE__) )
|
2
|
+
|
3
|
+
describe Metar::Pressure do
|
4
|
+
|
5
|
+
context '.parse' do
|
6
|
+
|
7
|
+
it 'interprets the Q prefix as hectopascals' do
|
8
|
+
Metar::Pressure.parse( 'Q1300' ).value.
|
9
|
+
should be_within( 0.01 ).of( 1.3 )
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'interprets the A prefix as inches of mercury' do
|
13
|
+
Metar::Pressure.parse( 'A1234' ).value.
|
14
|
+
should be_within( 0.01 ).of( 0.42 )
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'require 4 digits' do
|
18
|
+
Metar::Pressure.parse( 'Q12345' ).
|
19
|
+
should be_nil
|
20
|
+
Metar::Pressure.parse( 'A123' ).
|
21
|
+
should be_nil
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'returns nil for nil' do
|
25
|
+
Metar::Pressure.parse( nil ).
|
26
|
+
should be_nil
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,196 @@
|
|
1
|
+
load File.expand_path( '../spec_helper.rb', File.dirname(__FILE__) )
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'net/ftp'
|
5
|
+
|
6
|
+
describe Metar::Raw do
|
7
|
+
|
8
|
+
after :each do
|
9
|
+
Metar::Raw.send( :class_variable_set, '@@connection', nil )
|
10
|
+
end
|
11
|
+
|
12
|
+
context '.connection' do
|
13
|
+
|
14
|
+
context 'uncached' do
|
15
|
+
|
16
|
+
it 'sets up the connection' do
|
17
|
+
ftp = stub( 'ftp', :login => nil, :chdir => nil, :passive= => nil )
|
18
|
+
|
19
|
+
Net::FTP. should_receive( :new ).
|
20
|
+
and_return( ftp )
|
21
|
+
|
22
|
+
Metar::Raw.connect
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'cached' do
|
28
|
+
|
29
|
+
before :each do
|
30
|
+
@ftp = stub( 'ftp' )
|
31
|
+
Metar::Raw.send( :class_variable_set, '@@connection', @ftp )
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'does not connect to FTP' do
|
35
|
+
Net::FTP. should_not_receive( :new )
|
36
|
+
|
37
|
+
Metar::Raw.connection
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'returns the cached connection' do
|
41
|
+
connection = Metar::Raw.connection
|
42
|
+
|
43
|
+
connection. should == @ftp
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
context '.connect' do
|
51
|
+
|
52
|
+
it 'sets up the connection' do
|
53
|
+
ftp = stub( 'ftp' )
|
54
|
+
|
55
|
+
Net::FTP. should_receive( :new ).
|
56
|
+
and_return( ftp )
|
57
|
+
ftp. should_receive( :login )
|
58
|
+
ftp. should_receive( :chdir )
|
59
|
+
ftp. should_receive( :passive= ).
|
60
|
+
with( true )
|
61
|
+
|
62
|
+
Metar::Raw.connect
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
context '.fetch' do
|
68
|
+
|
69
|
+
it 'uses the connection' do
|
70
|
+
ftp = stub( 'ftp', :login => nil, :chdir => nil, :passive= => nil, :retrbinary => nil )
|
71
|
+
|
72
|
+
Net::FTP. should_receive( :new ).
|
73
|
+
and_return( ftp )
|
74
|
+
|
75
|
+
Metar::Raw.fetch( 'the_cccc' )
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'downloads the raw report' do
|
79
|
+
ftp = stub( 'ftp', :login => nil, :chdir => nil, :passive= => nil )
|
80
|
+
Net::FTP.stub!( :new ).and_return( ftp )
|
81
|
+
|
82
|
+
ftp. should_receive( :retrbinary ) do | *args, &block |
|
83
|
+
args[ 0 ]. should == 'RETR the_cccc.TXT'
|
84
|
+
args[ 1 ]. should be_a Fixnum
|
85
|
+
end
|
86
|
+
|
87
|
+
Metar::Raw.fetch( 'the_cccc' )
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'returns the data' do
|
91
|
+
ftp = stub( 'ftp', :login => nil, :chdir => nil, :passive= => nil )
|
92
|
+
def ftp.retrbinary( *args, &block )
|
93
|
+
block.call "chunk 1\n"
|
94
|
+
block.call "chunk 2\n"
|
95
|
+
end
|
96
|
+
Net::FTP.stub!( :new ).and_return( ftp )
|
97
|
+
|
98
|
+
raw = Metar::Raw.fetch( 'the_cccc' )
|
99
|
+
|
100
|
+
raw. should == "chunk 1\nchunk 2\n"
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'retries retrieval once' do
|
104
|
+
ftp = stub( 'ftp', :login => nil, :chdir => nil, :passive= => nil )
|
105
|
+
def ftp.attempt
|
106
|
+
@attempt
|
107
|
+
end
|
108
|
+
def ftp.attempt=(a)
|
109
|
+
@attempt = a
|
110
|
+
end
|
111
|
+
ftp.attempt = 0
|
112
|
+
def ftp.retrbinary( *args, &block )
|
113
|
+
self.attempt = self.attempt + 1
|
114
|
+
raise Net::FTPTempError if self.attempt == 1
|
115
|
+
block.call "chunk 1\n"
|
116
|
+
block.call "chunk 2\n"
|
117
|
+
end
|
118
|
+
Net::FTP.stub!( :new ).and_return( ftp )
|
119
|
+
|
120
|
+
raw = Metar::Raw.fetch( 'the_cccc' )
|
121
|
+
|
122
|
+
raw. should == "chunk 1\nchunk 2\n"
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'fails with an error, if retrieval fails twice' do
|
126
|
+
ftp = stub( 'ftp', :login => nil, :chdir => nil, :passive= => nil )
|
127
|
+
def ftp.attempt
|
128
|
+
@attempt
|
129
|
+
end
|
130
|
+
def ftp.attempt=(a)
|
131
|
+
@attempt = a
|
132
|
+
end
|
133
|
+
ftp.attempt = 0
|
134
|
+
def ftp.retrbinary( *args, &block )
|
135
|
+
self.attempt = self.attempt + 1
|
136
|
+
raise Net::FTPTempError
|
137
|
+
end
|
138
|
+
Net::FTP.stub!( :new ).and_return( ftp )
|
139
|
+
|
140
|
+
expect do
|
141
|
+
Metar::Raw.fetch( 'the_cccc' )
|
142
|
+
end. to raise_error( RuntimeError, /failed 2 times/)
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
context 'initialization' do
|
148
|
+
|
149
|
+
it 'should accept CCCC codes' do
|
150
|
+
raw = Metar::Raw.new( 'XXXX' )
|
151
|
+
|
152
|
+
raw.cccc. should == 'XXXX'
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'should accept Stations' do
|
156
|
+
station = stub( 'Metar::Station', :cccc => 'YYYY' )
|
157
|
+
raw = Metar::Raw.new( station )
|
158
|
+
|
159
|
+
raw.cccc. should == 'YYYY'
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'should parse data, if supplied' do
|
163
|
+
raw = Metar::Raw.new( 'XXXX', raw_metar )
|
164
|
+
|
165
|
+
raw.data. should == raw_metar
|
166
|
+
raw.raw_time. should == @raw_time
|
167
|
+
raw.metar. should == @metar
|
168
|
+
raw.time. should == Time.parse( @raw_time )
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
context 'lazy loading' do
|
174
|
+
|
175
|
+
it 'should fetch data on demand' do
|
176
|
+
raw = Metar::Raw.new( 'ESSB' )
|
177
|
+
|
178
|
+
Metar::Raw. should_receive( :fetch ).
|
179
|
+
with( 'ESSB' ).
|
180
|
+
and_return( raw_metar )
|
181
|
+
|
182
|
+
raw.metar
|
183
|
+
|
184
|
+
raw.data. should == raw_metar
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
def raw_metar
|
190
|
+
@raw_time = "2010/02/15 10:20"
|
191
|
+
@metar = "ESSB 151020Z 26003KT 2000 R12/1000N R30/1500N VV002 M07/M07 Q1013 1271//55"
|
192
|
+
"#{ @raw_time }\n#{ @metar }"
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
|
@@ -0,0 +1,187 @@
|
|
1
|
+
load File.expand_path( '../spec_helper.rb', File.dirname(__FILE__) )
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
describe Metar::Report do
|
5
|
+
|
6
|
+
context 'initialization' do
|
7
|
+
|
8
|
+
it 'loads the Station' do
|
9
|
+
station = stub( 'station' )
|
10
|
+
parser = stub( 'parser', :station_code => 'SSSS' )
|
11
|
+
|
12
|
+
Metar::Station. should_receive( :find_by_cccc ).
|
13
|
+
with( 'SSSS' ).
|
14
|
+
and_return( station )
|
15
|
+
|
16
|
+
Metar::Report.new( parser )
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'attributes' do
|
22
|
+
|
23
|
+
before :each do
|
24
|
+
@locale = I18n.locale
|
25
|
+
@station_code = 'SSSS'
|
26
|
+
@metar_date = '2008/05/06'
|
27
|
+
@metar_time = '10:56'
|
28
|
+
@metar_datetime = "#{@metar_date} #{@metar_time}"
|
29
|
+
@station = stub( 'station', :name => 'Airport 1',
|
30
|
+
:country => 'Wwwwww' )
|
31
|
+
@parser = stub( 'parser', :station_code => @station_code,
|
32
|
+
:date => Date.parse( @metar_date ),
|
33
|
+
:time => Time.parse( @metar_datetime ),
|
34
|
+
:observer => :real )
|
35
|
+
Metar::Station.stub( :find_by_cccc ).with( @station_code ).and_return( @station )
|
36
|
+
end
|
37
|
+
|
38
|
+
subject { Metar::Report.new( @parser ) }
|
39
|
+
|
40
|
+
after :each do
|
41
|
+
I18n.locale = @locale
|
42
|
+
end
|
43
|
+
|
44
|
+
context '#date' do
|
45
|
+
|
46
|
+
it 'localizes' do
|
47
|
+
I18n.locale = :en
|
48
|
+
|
49
|
+
subject.date. should == '06/05/2008'
|
50
|
+
|
51
|
+
I18n.locale = :it
|
52
|
+
|
53
|
+
subject.date. should == '06/05/2008'
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
context '#time' do
|
59
|
+
specify { subject.time. should == @metar_time }
|
60
|
+
end
|
61
|
+
|
62
|
+
context '#observer' do
|
63
|
+
specify { subject.observer. should == 'real' }
|
64
|
+
end
|
65
|
+
|
66
|
+
specify { subject.station_name.
|
67
|
+
should == 'Airport 1' }
|
68
|
+
|
69
|
+
specify { subject.station_country.
|
70
|
+
should == 'Wwwwww' }
|
71
|
+
|
72
|
+
specify { subject.station_code.
|
73
|
+
should == @station_code }
|
74
|
+
|
75
|
+
context 'proxied from parser' do
|
76
|
+
|
77
|
+
context 'singly' do
|
78
|
+
[
|
79
|
+
:wind,
|
80
|
+
:variable_wind,
|
81
|
+
:visibility,
|
82
|
+
:vertical_visibility,
|
83
|
+
:temperature,
|
84
|
+
:dew_point,
|
85
|
+
:sea_level_pressure,
|
86
|
+
].each do | attribute |
|
87
|
+
example attribute do
|
88
|
+
@attr = stub( attribute.to_s )
|
89
|
+
@parser.stub!( attribute => @attr )
|
90
|
+
|
91
|
+
@attr. should_receive( :to_s )
|
92
|
+
|
93
|
+
subject.send( attribute )
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context '#sky_summary' do
|
98
|
+
|
99
|
+
it 'returns the summary' do
|
100
|
+
@skies1 = stub('sky_conditions')
|
101
|
+
@parser.stub!( :sky_conditions => [@skies1] )
|
102
|
+
|
103
|
+
@skies1. should_receive( :to_summary ).
|
104
|
+
and_return( 'skies1' )
|
105
|
+
|
106
|
+
subject.sky_summary. should == 'skies1'
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'clear skies when missing' do
|
110
|
+
@parser.stub!( :sky_conditions => [] )
|
111
|
+
|
112
|
+
subject.sky_summary. should == 'clear skies'
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'uses the last, if there is more than 1' do
|
116
|
+
@skies1 = stub('sky_conditions1' )
|
117
|
+
@skies2 = stub('sky_conditions2' )
|
118
|
+
@parser.stub!( :sky_conditions => [ @skies1, @skies2 ] )
|
119
|
+
|
120
|
+
@skies2. should_receive( :to_summary ).
|
121
|
+
and_return( 'skies2' )
|
122
|
+
|
123
|
+
subject.sky_summary. should == 'skies2'
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'joined' do
|
129
|
+
|
130
|
+
it '#runway_visible_range' do
|
131
|
+
@rvr1 = stub( 'rvr1', :to_s => 'rvr1' )
|
132
|
+
@rvr2 = stub( 'rvr2', :to_s => 'rvr2' )
|
133
|
+
@parser.stub!( :runway_visible_range => [ @rvr1, @rvr2 ] )
|
134
|
+
|
135
|
+
subject.runway_visible_range.
|
136
|
+
should == 'rvr1, rvr2'
|
137
|
+
end
|
138
|
+
|
139
|
+
it '#present_weather' do
|
140
|
+
@parser.stub!( :present_weather => [ 'pw1', 'pw2' ] )
|
141
|
+
|
142
|
+
subject.present_weather.should == 'pw1, pw2'
|
143
|
+
end
|
144
|
+
|
145
|
+
it '#remarks' do
|
146
|
+
@parser.stub!( :remarks => [ 'rem1', 'rem2' ] )
|
147
|
+
|
148
|
+
subject.remarks. should == 'rem1, rem2'
|
149
|
+
end
|
150
|
+
|
151
|
+
it '#sky_conditions' do
|
152
|
+
sky1 = stub( 'sky1', :to_s => 'sky1' )
|
153
|
+
sky2 = stub( 'sky2', :to_s => 'sky2' )
|
154
|
+
@parser.stub!( :sky_conditions => [ sky1, sky2 ] )
|
155
|
+
|
156
|
+
subject.sky_conditions. should == 'sky1, sky2'
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
it '#to_s' do
|
164
|
+
sky1 = stub( 'sky1', :to_summary => 'sky1' )
|
165
|
+
sky2 = stub( 'sky2', :to_summary => 'sky2' )
|
166
|
+
@parser.stub!( :wind => 'wind',
|
167
|
+
:visibility => 'visibility',
|
168
|
+
:present_weather => ['pw'],
|
169
|
+
:sky_conditions => [ sky1, sky2 ],
|
170
|
+
:temperature => 'temp' )
|
171
|
+
expected = <<EOT
|
172
|
+
name: Airport 1
|
173
|
+
country: Wwwwww
|
174
|
+
time: #{@metar_time}
|
175
|
+
wind: wind
|
176
|
+
visibility: visibility
|
177
|
+
weather: pw
|
178
|
+
sky: sky2
|
179
|
+
temperature: temp
|
180
|
+
EOT
|
181
|
+
subject.to_s. should == expected
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
load File.expand_path( '../spec_helper.rb', File.dirname(__FILE__) )
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
RSpec::Matchers.define :be_runway_visible_range do | designator, visibility1, visibility2, tendency |
|
5
|
+
match do | rvr |
|
6
|
+
if rvr.nil? && designator.nil?
|
7
|
+
true
|
8
|
+
elsif rvr.nil? != designator.nil?
|
9
|
+
false
|
10
|
+
elsif rvr.visibility1.nil? != visibility1.nil?
|
11
|
+
false
|
12
|
+
elsif rvr.visibility2.nil? != visibility2.nil?
|
13
|
+
false
|
14
|
+
elsif rvr.tendency.nil? != tendency.nil?
|
15
|
+
false
|
16
|
+
elsif ! visibility1.nil? &&
|
17
|
+
( ( rvr.visibility1.distance.value - visibility1[0] ).abs > 0.01 ||
|
18
|
+
rvr.visibility1.comparator != visibility1[ 2 ] )
|
19
|
+
false
|
20
|
+
elsif ! visibility2.nil? &&
|
21
|
+
( ( rvr.visibility2.distance.value - visibility2[0] ).abs > 0.02 ||
|
22
|
+
rvr.visibility2.comparator != visibility2[2] )
|
23
|
+
false
|
24
|
+
elsif tendency != rvr.tendency
|
25
|
+
false
|
26
|
+
else
|
27
|
+
true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe Metar::RunwayVisibleRange do
|
33
|
+
|
34
|
+
context '.parse' do
|
35
|
+
|
36
|
+
[
|
37
|
+
[ 'understands R + nn + / + nnnn', 'R12/3400', [ '12', [3400.00, nil, nil], nil, nil ] ],
|
38
|
+
[ 'understands runway positions: RLC', 'R12L/3400', [ '12L', [3400.00, nil, nil], nil, nil ] ],
|
39
|
+
[ 'understands comparators: PM', 'R12/P3400', [ '12', [3400.00, nil, :more_than], nil, nil ] ],
|
40
|
+
[ 'understands tendencies: NUD', 'R12/3400U', [ '12', [3400.00, nil, nil], nil, :improving ] ],
|
41
|
+
[ 'understands feet', 'R12/3400FT', [ '12', [1036.32, nil, nil], nil, nil ] ],
|
42
|
+
[ 'understands second visibilties', 'R12/3400V1800FT', [ '12', [1036.32, nil, nil], [548.64, nil, nil], nil ] ],
|
43
|
+
[ 'returns nil for nil', nil, [ nil, nil, nil, nil ] ],
|
44
|
+
].each do | docstring, raw, expected |
|
45
|
+
example docstring do
|
46
|
+
Metar::RunwayVisibleRange.parse( raw ).should be_runway_visible_range( *expected )
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
context '#to_s' do
|
53
|
+
|
54
|
+
before :all do
|
55
|
+
@locale = I18n.locale
|
56
|
+
I18n.locale = :it
|
57
|
+
end
|
58
|
+
|
59
|
+
after :all do
|
60
|
+
I18n.locale = @locale
|
61
|
+
end
|
62
|
+
|
63
|
+
[
|
64
|
+
[ 'v1', :en, [ [3400.00, nil, nil], nil, nil ], 'runway 14: 3400m' ],
|
65
|
+
[ 'v1 and v2', :en, [ [3400.00, nil, nil], [1900.00, nil, nil], nil ], 'runway 14: from 3400m to 1900m' ],
|
66
|
+
[ 'v1 and tendency', :en, [ [3400.00, nil, nil], nil, :improving ], 'runway 14: 3400m improving' ],
|
67
|
+
].each do | docstring, locale, ( visibility1, visibility2, tendency ), expected |
|
68
|
+
d1 = Metar::Distance.new( visibility1[0] )
|
69
|
+
v1 = Metar::Visibility.new( d1, visibility1[1], visibility1[2] )
|
70
|
+
v2 =
|
71
|
+
if ! visibility2.nil?
|
72
|
+
d2 = Metar::Distance.new( visibility2[0] )
|
73
|
+
Metar::Visibility.new( d2, visibility2[1], visibility2[2] )
|
74
|
+
else
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
|
78
|
+
example docstring + " (#{locale})" do
|
79
|
+
I18n.locale = locale
|
80
|
+
Metar::RunwayVisibleRange.new( '14', v1, v2, tendency ).to_s.
|
81
|
+
should == expected
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|