metar-parser 1.5.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -48
  3. data/Rakefile +2 -1
  4. data/lib/metar/data/base.rb +16 -10
  5. data/lib/metar/data/density_altitude.rb +16 -10
  6. data/lib/metar/data/direction.rb +10 -4
  7. data/lib/metar/data/distance.rb +27 -20
  8. data/lib/metar/data/lightning.rb +69 -60
  9. data/lib/metar/data/observer.rb +26 -20
  10. data/lib/metar/data/pressure.rb +28 -22
  11. data/lib/metar/data/remark.rb +146 -130
  12. data/lib/metar/data/runway_visible_range.rb +98 -78
  13. data/lib/metar/data/sky_condition.rb +68 -57
  14. data/lib/metar/data/speed.rb +21 -14
  15. data/lib/metar/data/station_code.rb +8 -4
  16. data/lib/metar/data/temperature.rb +21 -14
  17. data/lib/metar/data/temperature_and_dew_point.rb +22 -16
  18. data/lib/metar/data/time.rb +57 -47
  19. data/lib/metar/data/variable_wind.rb +30 -19
  20. data/lib/metar/data/vertical_visibility.rb +27 -21
  21. data/lib/metar/data/visibility.rb +91 -79
  22. data/lib/metar/data/visibility_remark.rb +16 -5
  23. data/lib/metar/data/weather_phenomenon.rb +92 -74
  24. data/lib/metar/data/wind.rb +105 -93
  25. data/lib/metar/data.rb +25 -23
  26. data/lib/metar/i18n.rb +5 -2
  27. data/lib/metar/parser.rb +46 -21
  28. data/lib/metar/raw.rb +32 -44
  29. data/lib/metar/report.rb +31 -20
  30. data/lib/metar/station.rb +29 -20
  31. data/lib/metar/version.rb +3 -1
  32. data/lib/metar.rb +2 -1
  33. data/locales/de.yml +1 -0
  34. data/locales/en.yml +1 -0
  35. data/locales/it.yml +1 -0
  36. data/locales/pt-BR.yml +1 -0
  37. data/spec/data/density_altitude_spec.rb +2 -1
  38. data/spec/data/distance_spec.rb +2 -1
  39. data/spec/data/lightning_spec.rb +26 -9
  40. data/spec/data/pressure_spec.rb +2 -0
  41. data/spec/data/remark_spec.rb +26 -9
  42. data/spec/data/runway_visible_range_spec.rb +71 -35
  43. data/spec/data/sky_condition_spec.rb +63 -19
  44. data/spec/data/speed_spec.rb +2 -0
  45. data/spec/data/temperature_spec.rb +2 -1
  46. data/spec/data/variable_wind_spec.rb +2 -0
  47. data/spec/data/vertical_visibility_spec.rb +4 -4
  48. data/spec/data/visibility_remark_spec.rb +2 -1
  49. data/spec/data/visibility_spec.rb +46 -25
  50. data/spec/data/weather_phenomenon_spec.rb +79 -24
  51. data/spec/data/wind_spec.rb +156 -38
  52. data/spec/i18n_spec.rb +2 -0
  53. data/spec/parser_spec.rb +192 -64
  54. data/spec/raw_spec.rb +40 -68
  55. data/spec/report_spec.rb +27 -25
  56. data/spec/spec_helper.rb +5 -6
  57. data/spec/station_spec.rb +92 -52
  58. metadata +53 -39
@@ -1,86 +1,104 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "metar/i18n"
2
4
 
3
- class Metar::Data::WeatherPhenomenon < Metar::Data::Base
4
- Modifiers = {
5
- '+' => 'heavy',
6
- '-' => 'light',
7
- 'VC' => 'nearby',
8
- '-VC' => 'nearby light',
9
- '+VC' => 'nearby heavy',
10
- }
5
+ module Metar
6
+ module Data
7
+ class WeatherPhenomenon < Metar::Data::Base
8
+ MODIFIERS = {
9
+ '+' => 'heavy',
10
+ '-' => 'light',
11
+ 'VC' => 'nearby',
12
+ '-VC' => 'nearby light',
13
+ '+VC' => 'nearby heavy'
14
+ }.freeze
11
15
 
12
- Descriptors = {
13
- 'BC' => 'patches of',
14
- 'BL' => 'blowing',
15
- 'DR' => 'low drifting',
16
- 'FZ' => 'freezing',
17
- 'MI' => 'shallow',
18
- 'PR' => 'partial',
19
- 'SH' => 'shower of',
20
- 'TS' => 'thunderstorm and',
21
- }
16
+ DESCRIPTORS = {
17
+ 'BC' => 'patches of',
18
+ 'BL' => 'blowing',
19
+ 'DR' => 'low drifting',
20
+ 'FZ' => 'freezing',
21
+ 'MI' => 'shallow',
22
+ 'PR' => 'partial',
23
+ 'SH' => 'shower of',
24
+ 'TS' => 'thunderstorm and'
25
+ }.freeze
22
26
 
23
- Phenomena = {
24
- 'BR' => 'mist',
25
- 'DU' => 'dust',
26
- 'DZ' => 'drizzle',
27
- 'FG' => 'fog',
28
- 'FU' => 'smoke',
29
- 'GR' => 'hail',
30
- 'GS' => 'small hail',
31
- 'HZ' => 'haze',
32
- 'IC' => 'ice crystals',
33
- 'PL' => 'ice pellets',
34
- 'PO' => 'dust whirls',
35
- 'PY' => 'spray', # US only
36
- 'RA' => 'rain',
37
- 'SA' => 'sand',
38
- 'SH' => 'shower',
39
- 'SN' => 'snow',
40
- 'SG' => 'snow grains',
41
- 'SQ' => 'squall',
42
- 'UP' => 'unknown phenomenon', # => AUTO
43
- 'VA' => 'volcanic ash',
44
- 'FC' => 'funnel cloud',
45
- 'SS' => 'sand storm',
46
- 'DS' => 'dust storm',
47
- 'TS' => 'thunderstorm',
48
- }
27
+ PHENOMENA = {
28
+ 'BR' => 'mist',
29
+ 'DU' => 'dust',
30
+ 'DZ' => 'drizzle',
31
+ 'FG' => 'fog',
32
+ 'FU' => 'smoke',
33
+ 'GR' => 'hail',
34
+ 'GS' => 'small hail',
35
+ 'HZ' => 'haze',
36
+ 'IC' => 'ice crystals',
37
+ 'PL' => 'ice pellets',
38
+ 'PO' => 'dust whirls',
39
+ 'PY' => 'spray', # US only
40
+ 'RA' => 'rain',
41
+ 'SA' => 'sand',
42
+ 'SH' => 'shower',
43
+ 'SN' => 'snow',
44
+ 'SG' => 'snow grains',
45
+ 'SQ' => 'squall',
46
+ 'UP' => 'unknown phenomenon', # => AUTO
47
+ 'VA' => 'volcanic ash',
48
+ 'FC' => 'funnel cloud',
49
+ 'SS' => 'sand storm',
50
+ 'DS' => 'dust storm',
51
+ 'TS' => 'thunderstorm'
52
+ }.freeze
49
53
 
50
- # Accepts all standard (and some non-standard) present weather codes
51
- def self.parse(raw)
52
- phenomena = Phenomena.keys.join('|')
53
- descriptors = Descriptors.keys.join('|')
54
- modifiers = Modifiers.keys.join('|')
55
- modifiers.gsub!(/([\+\-])/) { |m| "\\#{m}" }
56
- rxp = Regexp.new("^(RE)?(#{modifiers})?(#{descriptors})?((?:#{phenomena}){1,2})$")
57
- m = rxp.match(raw)
58
- return nil if m.nil?
54
+ # Accepts all standard (and some non-standard) present weather codes
55
+ def self.parse(raw)
56
+ modifiers = MODIFIERS.keys.join('|')
57
+ modifiers.gsub!(/([\+\-])/) { |m| "\\#{m}" }
59
58
 
60
- recent = m[1] == "RE"
61
- modifier_code = m[2]
62
- descriptor_code = m[3]
63
- phenomena_codes = m[4].scan(/../)
64
- phenomena_phrase = phenomena_codes.map { |c| Phenomena[c] }.join(' and ')
59
+ descriptors = DESCRIPTORS.keys.join('|')
65
60
 
66
- new(
67
- raw,
68
- phenomenon: phenomena_phrase,
69
- modifier: Modifiers[modifier_code],
70
- descriptor: Descriptors[descriptor_code]
71
- )
72
- end
61
+ phenomena = PHENOMENA.keys.join('|')
73
62
 
74
- attr_reader :phenomenon, :modifier, :descriptor, :recent
63
+ rxp = Regexp.new(
64
+ "^(RE)?(#{modifiers})?(#{descriptors})?((?:#{phenomena}){1,2})$"
65
+ )
75
66
 
76
- def initialize(raw, phenomenon:, modifier: nil, descriptor: nil, recent: false)
77
- @raw = raw
78
- @phenomenon, @modifier, @descriptor = phenomenon, modifier, descriptor
79
- @recent = recent
80
- end
67
+ m = rxp.match(raw)
68
+ return nil if m.nil?
69
+
70
+ recent = m[1] == "RE"
71
+ modifier_code = m[2]
72
+ descriptor_code = m[3]
73
+ phenomena_codes = m[4].scan(/../)
74
+ phenomena = phenomena_codes.map { |c| PHENOMENA[c] }
75
+ phenomena_phrase = phenomena.join(' and ')
76
+
77
+ new(
78
+ raw,
79
+ phenomenon: phenomena_phrase,
80
+ modifier: MODIFIERS[modifier_code],
81
+ descriptor: DESCRIPTORS[descriptor_code],
82
+ recent: recent
83
+ )
84
+ end
85
+
86
+ attr_reader :phenomenon, :modifier, :descriptor, :recent
87
+
88
+ def initialize(
89
+ raw, phenomenon:, modifier: nil, descriptor: nil, recent: false
90
+ )
91
+ @raw = raw
92
+ @phenomenon = phenomenon
93
+ @modifier = modifier
94
+ @descriptor = descriptor
95
+ @recent = recent
96
+ end
81
97
 
82
- def to_s
83
- key = [modifier, descriptor, phenomenon].compact.join(' ')
84
- I18n.t("metar.present_weather.%s" % key)
98
+ def to_s
99
+ key = [modifier, descriptor, phenomenon].compact.join(' ')
100
+ I18n.t("metar.present_weather.#{key}")
101
+ end
102
+ end
85
103
  end
86
104
  end
@@ -1,108 +1,120 @@
1
- class Metar::Data::Wind < Metar::Data::Base
2
- def self.parse(raw, strict: false)
3
- return nil if raw.nil?
4
-
5
- plain_match =
6
- if strict
7
- /^(\d{3})(\d{2}(|MPS|KMH|KT))$/
8
- else
9
- /^(\d{3})(\d{2,3}(|MPS|KMH|KT))$/
10
- end
1
+ # frozen_string_literal: true
11
2
 
12
- m1 = raw.match(plain_match)
13
- if m1
14
- return nil if m1[1].to_i > 360
15
- return new(
16
- raw,
17
- direction: Metar::Data::Direction.new(m1[1]),
18
- speed: Metar::Data::Speed.parse(m1[2])
19
- )
20
- end
3
+ module Metar
4
+ module Data
5
+ class Wind < Metar::Data::Base
6
+ def self.parse(raw, strict: false)
7
+ return nil if raw.nil?
21
8
 
22
- m2 = raw.match(/^(\d{3})(\d{2})G(\d{2,3}(|MPS|KMH|KT))$/)
23
- if m2
24
- return nil if m2[1].to_i > 360
25
- return new(
26
- raw,
27
- direction: Metar::Data::Direction.new(m2[1]),
28
- speed: Metar::Data::Speed.parse(m2[2] + m2[4]),
29
- gusts: Metar::Data::Speed.parse(m2[3])
30
- )
31
- end
9
+ plain_match =
10
+ if strict
11
+ /^(\d{3})(\d{2}(|MPS|KMH|KT))$/
12
+ else
13
+ /^(\d{3})(\d{2,3}(|MPS|KMH|KT))$/
14
+ end
32
15
 
33
- m3 = raw.match(/^VRB(\d{2})G(\d{2,3})(|MPS|KMH|KT)$/)
34
- if m3
35
- speed = m3[1] + m3[3]
36
- gusts = m3[2] + m3[3]
37
- return new(
38
- raw,
39
- direction: :variable_direction,
40
- speed: Metar::Data::Speed.parse(speed),
41
- gusts: Metar::Data::Speed.parse(gusts)
42
- )
43
- end
16
+ m1 = raw.match(plain_match)
17
+ if m1
18
+ return nil if m1[1].to_i > 360
44
19
 
45
- m4 = raw.match(/^VRB(\d{2}(|MPS|KMH|KT))$/)
46
- if m4
47
- speed = Metar::Data::Speed.parse(m4[1])
48
- return new(raw, direction: :variable_direction, speed: speed)
49
- end
20
+ return new(
21
+ raw,
22
+ direction: Metar::Data::Direction.new(m1[1]),
23
+ speed: Metar::Data::Speed.parse(m1[2])
24
+ )
25
+ end
50
26
 
51
- m5 = raw.match(/^\/{3}(\d{2}(|MPS|KMH|KT))$/)
52
- if m5
53
- speed = Metar::Data::Speed.parse(m5[1])
54
- return new(raw, direction: :unknown_direction, speed: speed)
55
- end
27
+ m2 = raw.match(/^(\d{3})(\d{2})G(\d{2,3}(|MPS|KMH|KT))$/)
28
+ if m2
29
+ return nil if m2[1].to_i > 360
56
30
 
57
- m6 = raw.match(%r(^/////(|MPS|KMH|KT)$))
58
- if m6
59
- return new(raw, direction: :unknown_direction, speed: :unknown_speed)
60
- end
31
+ return new(
32
+ raw,
33
+ direction: Metar::Data::Direction.new(m2[1]),
34
+ speed: Metar::Data::Speed.parse(m2[2] + m2[4]),
35
+ gusts: Metar::Data::Speed.parse(m2[3])
36
+ )
37
+ end
61
38
 
62
- nil
63
- end
39
+ m3 = raw.match(/^VRB(\d{2})G(\d{2,3})(|MPS|KMH|KT)$/)
40
+ if m3
41
+ speed = m3[1] + m3[3]
42
+ gusts = m3[2] + m3[3]
43
+ return new(
44
+ raw,
45
+ direction: :variable_direction,
46
+ speed: Metar::Data::Speed.parse(speed),
47
+ gusts: Metar::Data::Speed.parse(gusts)
48
+ )
49
+ end
64
50
 
65
- attr_reader :direction, :speed, :gusts
51
+ m4 = raw.match(/^VRB(\d{2}(|MPS|KMH|KT))$/)
52
+ if m4
53
+ speed = Metar::Data::Speed.parse(m4[1])
54
+ return new(raw, direction: :variable_direction, speed: speed)
55
+ end
66
56
 
67
- def initialize(raw, direction:, speed:, gusts: nil)
68
- @raw = raw
69
- @direction, @speed, @gusts = direction, speed, gusts
70
- end
57
+ m5 = raw.match(%r{^/{3}(\d{2}(|MPS|KMH|KT))$})
58
+ if m5
59
+ speed = Metar::Data::Speed.parse(m5[1])
60
+ return new(raw, direction: :unknown_direction, speed: speed)
61
+ end
62
+
63
+ m6 = raw.match(%r{^/////(|MPS|KMH|KT)$})
64
+ if m6
65
+ return new(raw, direction: :unknown_direction, speed: :unknown_speed)
66
+ end
67
+
68
+ nil
69
+ end
71
70
 
72
- def to_s(options = {})
73
- options = {
74
- direction_units: :compass,
75
- speed_units: :kilometers_per_hour,
76
- }.merge(options)
77
- speed =
78
- case @speed
79
- when :unknown_speed
80
- I18n.t('metar.wind.unknown_speed')
81
- else
82
- @speed.to_s(
83
- abbreviated: true,
84
- precision: 0,
85
- units: options[:speed_units]
86
- )
71
+ attr_reader :direction, :speed, :gusts
72
+
73
+ def initialize(raw, direction:, speed:, gusts: nil)
74
+ @raw = raw
75
+ @direction = direction
76
+ @speed = speed
77
+ @gusts = gusts
87
78
  end
88
- direction =
89
- case @direction
90
- when :variable_direction
91
- I18n.t('metar.wind.variable_direction')
92
- when :unknown_direction
93
- I18n.t('metar.wind.unknown_direction')
94
- else
95
- @direction.to_s(units: options[:direction_units])
79
+
80
+ def to_s(options = {})
81
+ options = {
82
+ direction_units: :compass,
83
+ speed_units: :kilometers_per_hour
84
+ }.merge(options)
85
+ speed =
86
+ case @speed
87
+ when :unknown_speed
88
+ I18n.t('metar.wind.unknown_speed')
89
+ else
90
+ @speed.to_s(
91
+ abbreviated: true,
92
+ precision: 0,
93
+ units: options[:speed_units]
94
+ )
95
+ end
96
+ direction =
97
+ case @direction
98
+ when :variable_direction
99
+ I18n.t('metar.wind.variable_direction')
100
+ when :unknown_direction
101
+ I18n.t('metar.wind.unknown_direction')
102
+ else
103
+ @direction.to_s(units: options[:direction_units])
104
+ end
105
+ s = "#{speed} #{direction}"
106
+
107
+ if !@gusts.nil?
108
+ g = @gusts.to_s(
109
+ abbreviated: true,
110
+ precision: 0,
111
+ units: options[:speed_units]
112
+ )
113
+ s += " #{I18n.t('metar.wind.gusts')} #{g}"
114
+ end
115
+
116
+ s
96
117
  end
97
- s = "#{speed} #{direction}"
98
- if not @gusts.nil?
99
- g = @gusts.to_s(
100
- abbreviated: true,
101
- precision: 0,
102
- units: options[:speed_units]
103
- )
104
- s += " #{I18n.t('metar.wind.gusts')} #{g}"
105
118
  end
106
- s
107
119
  end
108
120
  end
data/lib/metar/data.rb CHANGED
@@ -1,25 +1,27 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- module Metar::Data
4
- autoload :Base, "metar/data/base"
5
- autoload :DensityAltitude, "metar/data/density_altitude"
6
- autoload :Direction, "metar/data/direction"
7
- autoload :Distance, "metar/data/distance"
8
- autoload :Lightning, "metar/data/lightning"
9
- autoload :Observer, "metar/data/observer"
10
- autoload :Pressure, "metar/data/pressure"
11
- autoload :Remark, "metar/data/remark"
12
- autoload :RunwayVisibleRange, "metar/data/runway_visible_range"
13
- autoload :SkyCondition, "metar/data/sky_condition"
14
- autoload :Speed, "metar/data/speed"
15
- autoload :StationCode, "metar/data/station_code"
16
- autoload :Temperature, "metar/data/temperature"
17
- autoload :TemperatureAndDewPoint, "metar/data/temperature_and_dew_point"
18
- autoload :Time, "metar/data/time"
19
- autoload :VariableWind, "metar/data/variable_wind"
20
- autoload :VerticalVisibility, "metar/data/vertical_visibility"
21
- autoload :Visibility, "metar/data/visibility"
22
- autoload :VisibilityRemark, "metar/data/visibility_remark"
23
- autoload :WeatherPhenomenon, "metar/data/weather_phenomenon"
24
- autoload :Wind, "metar/data/wind"
3
+ module Metar
4
+ module Data
5
+ autoload :Base, "metar/data/base"
6
+ autoload :DensityAltitude, "metar/data/density_altitude"
7
+ autoload :Direction, "metar/data/direction"
8
+ autoload :Distance, "metar/data/distance"
9
+ autoload :Lightning, "metar/data/lightning"
10
+ autoload :Observer, "metar/data/observer"
11
+ autoload :Pressure, "metar/data/pressure"
12
+ autoload :Remark, "metar/data/remark"
13
+ autoload :RunwayVisibleRange, "metar/data/runway_visible_range"
14
+ autoload :SkyCondition, "metar/data/sky_condition"
15
+ autoload :Speed, "metar/data/speed"
16
+ autoload :StationCode, "metar/data/station_code"
17
+ autoload :Temperature, "metar/data/temperature"
18
+ autoload :TemperatureAndDewPoint, "metar/data/temperature_and_dew_point"
19
+ autoload :Time, "metar/data/time"
20
+ autoload :VariableWind, "metar/data/variable_wind"
21
+ autoload :VerticalVisibility, "metar/data/vertical_visibility"
22
+ autoload :Visibility, "metar/data/visibility"
23
+ autoload :VisibilityRemark, "metar/data/visibility_remark"
24
+ autoload :WeatherPhenomenon, "metar/data/weather_phenomenon"
25
+ autoload :Wind, "metar/data/wind"
26
+ end
25
27
  end
data/lib/metar/i18n.rb CHANGED
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "i18n"
2
4
 
3
- locales_path = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "locales"))
5
+ locales_path = File.expand_path(
6
+ File.join(File.dirname(__FILE__), "..", "..", "locales")
7
+ )
4
8
  I18n.load_path += Dir.glob("#{locales_path}/*.yml")
5
9
  I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
6
-
data/lib/metar/parser.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "m9t"
2
4
 
3
5
  require "metar/data"
@@ -13,7 +15,7 @@ module Metar
13
15
  new(raw)
14
16
  end
15
17
 
16
- COMPLIANCE = [:strict, :loose]
18
+ COMPLIANCE = %i(strict loose).freeze
17
19
 
18
20
  def self.thread_attributes
19
21
  Thread.current[:metar_parser] ||= {}
@@ -25,6 +27,7 @@ module Metar
25
27
 
26
28
  def self.compliance=(compliance)
27
29
  raise 'Unknown compliance' unless COMPLIANCE.find(compliance)
30
+
28
31
  thread_attributes[:compliance] = compliance
29
32
  end
30
33
 
@@ -65,11 +68,13 @@ module Metar
65
68
 
66
69
  def temperature
67
70
  return nil if @temperature_and_dew_point.nil?
71
+
68
72
  @temperature_and_dew_point.temperature
69
73
  end
70
74
 
71
75
  def dew_point
72
76
  return nil if @temperature_and_dew_point.nil?
77
+
73
78
  @temperature_and_dew_point.dew_point
74
79
  end
75
80
 
@@ -77,7 +82,7 @@ module Metar
77
82
  attr = {
78
83
  metar: metar,
79
84
  datetime: @time.raw,
80
- station_code: station_code,
85
+ station_code: station_code
81
86
  }
82
87
  %i(
83
88
  minimum_visibility
@@ -106,6 +111,7 @@ module Metar
106
111
  value = send(attribute)
107
112
  return hash if value.nil?
108
113
  return hash if value.raw.nil?
114
+
109
115
  hash[attribute] = value.raw
110
116
  hash
111
117
  end
@@ -113,7 +119,8 @@ module Metar
113
119
  def add_raw_if_not_empty(hash, attribute)
114
120
  values = send(attribute)
115
121
  raws = values.map(&:raw).compact
116
- return hash if raws.size == 0
122
+ return hash if raws.empty?
123
+
117
124
  hash[attribute] = raws.join(" ")
118
125
  hash
119
126
  end
@@ -121,8 +128,8 @@ module Metar
121
128
  def analyze
122
129
  @chunks = @metar.split(' ')
123
130
  # Strip final '='
124
- if !strict? && @chunks.length > 0
125
- @chunks[-1].gsub!(/\s?=$/, '')
131
+ if !strict?
132
+ @chunks[-1].gsub!(/\s?=$/, '') if !@chunks.empty?
126
133
  end
127
134
 
128
135
  @station_code = nil
@@ -167,7 +174,8 @@ module Metar
167
174
  def seek_station_code
168
175
  @station_code = Metar::Data::StationCode.parse(@chunks[0])
169
176
  if @station_code.nil?
170
- raise ParseError.new("Expecting location, found '#{ @chunks[0] }' in #{@metar}")
177
+ message = "Expecting location, found '#{@chunks[0]}' in #{@metar}"
178
+ raise ParseError, message
171
179
  end
172
180
  @chunks.shift
173
181
  @station_code
@@ -178,9 +186,11 @@ module Metar
178
186
  @time = Metar::Data::Time.parse(
179
187
  datetime, year: raw.time.year, month: raw.time.month, strict: strict?
180
188
  )
189
+
181
190
  if !@time
182
- raise ParseError.new("Expecting datetime, found '#{datetime}' in #{@metar}")
191
+ raise ParseError, "Expecting datetime, found '#{datetime}' in #{@metar}"
183
192
  end
193
+
184
194
  @time
185
195
  end
186
196
 
@@ -228,17 +238,17 @@ module Metar
228
238
  end
229
239
  end
230
240
 
231
- if @chunks[0] == '1' or @chunks[0] == '2'
232
- @visibility = Metar::Data::Visibility.parse(@chunks[0] + ' ' + @chunks[1])
241
+ if @chunks[0] == '1' || @chunks[0] == '2'
242
+ @visibility = Metar::Data::Visibility.parse(
243
+ @chunks[0] + ' ' + @chunks[1]
244
+ )
233
245
  if @visibility
234
246
  @chunks.shift
235
247
  @chunks.shift
236
248
  end
237
249
  else
238
250
  @visibility = Metar::Data::Visibility.parse(@chunks[0])
239
- if @visibility
240
- @chunks.shift
241
- end
251
+ @chunks.shift if @visibility
242
252
  end
243
253
  @visibility
244
254
  end
@@ -254,6 +264,7 @@ module Metar
254
264
  loop do
255
265
  rvr = Metar::Data::RunwayVisibleRange.parse(@chunks[0])
256
266
  break if rvr.nil?
267
+
257
268
  @chunks.shift
258
269
  @runway_visible_range << rvr
259
270
  end
@@ -271,11 +282,21 @@ module Metar
271
282
  end
272
283
  end
273
284
 
285
+ if @chunks[0] == 'NSW'
286
+ @present_weather << Metar::Data::WeatherPhenomenon.new(
287
+ nil, phenomenon: "no significant weather"
288
+ )
289
+ @chunks.shift
290
+ return
291
+ end
292
+
274
293
  loop do
275
- break if @chunks.size == 0
294
+ break if @chunks.empty?
276
295
  break if @chunks[0].start_with?("RE")
296
+
277
297
  wtp = Metar::Data::WeatherPhenomenon.parse(@chunks[0])
278
298
  break if wtp.nil?
299
+
279
300
  @chunks.shift
280
301
  @present_weather << wtp
281
302
  end
@@ -284,7 +305,7 @@ module Metar
284
305
  # Repeatable: 15.9.1.3
285
306
  def seek_sky_conditions
286
307
  if observer.value == :auto # WMO 15.4
287
- if @chunks[0] == '///' or @chunks[0] == '//////'
308
+ if @chunks[0] == '///' || @chunks[0] == '//////'
288
309
  @chunks.shift # Simply dispose of it
289
310
  return
290
311
  end
@@ -293,6 +314,7 @@ module Metar
293
314
  loop do
294
315
  sky_condition = Metar::Data::SkyCondition.parse(@chunks[0])
295
316
  break if sky_condition.nil?
317
+
296
318
  @chunks.shift
297
319
  @sky_conditions << sky_condition
298
320
  end
@@ -321,10 +343,12 @@ module Metar
321
343
 
322
344
  def seek_recent_weather
323
345
  loop do
324
- return if @chunks.size == 0
346
+ return if @chunks.empty?
325
347
  break if !@chunks[0].start_with?("RE")
348
+
326
349
  recent_weather = Metar::Data::WeatherPhenomenon.parse(@chunks[0])
327
350
  break if recent_weather.nil?
351
+
328
352
  @chunks.shift
329
353
  @recent_weather << recent_weather
330
354
  end
@@ -333,11 +357,11 @@ module Metar
333
357
 
334
358
  def seek_to_remarks
335
359
  if strict?
336
- if @chunks.size > 0 and @chunks[0] != 'RMK'
337
- raise ParseError.new("Unparsable text found: '#{@chunks.join(' ')}'")
360
+ if !@chunks.empty? && @chunks[0] != 'RMK'
361
+ raise ParseError, "Unparsable text found: '#{@chunks.join(' ')}'"
338
362
  end
339
363
  else
340
- while @chunks.size > 0 and @chunks[0] != 'RMK' do
364
+ while !@chunks.empty? && @chunks[0] != 'RMK' do
341
365
  @unparsed << @chunks.shift
342
366
  end
343
367
  end
@@ -345,13 +369,14 @@ module Metar
345
369
 
346
370
  # WMO: 15.15
347
371
  def seek_remarks
348
- return if @chunks.size == 0
372
+ return if @chunks.empty?
349
373
  raise 'seek_remarks called without remark' if @chunks[0] != 'RMK'
350
374
 
351
375
  @chunks.shift # Drop 'RMK'
352
376
  @remarks = []
353
377
  loop do
354
- break if @chunks.size == 0
378
+ break if @chunks.empty?
379
+
355
380
  r = Metar::Data::Remark.parse(@chunks[0])
356
381
  if r
357
382
  if r.is_a?(Array)
@@ -362,7 +387,7 @@ module Metar
362
387
  @chunks.shift
363
388
  next
364
389
  end
365
- if @chunks[0] == 'VIS' and @chunks.size >= 3 and @chunks[1] == 'MIN'
390
+ if @chunks[0] == 'VIS' && @chunks.size >= 3 && @chunks[1] == 'MIN'
366
391
  @chunks.shift(2)
367
392
  r = Metar::Data::VisibilityRemark.parse(@chunks[0])
368
393
  @remarks << r