aixm 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +14 -0
  4. data/README.md +3 -1
  5. data/lib/aixm/a.rb +29 -15
  6. data/lib/aixm/association.rb +2 -1
  7. data/lib/aixm/classes.rb +4 -0
  8. data/lib/aixm/component/address.rb +15 -9
  9. data/lib/aixm/component/approach_lighting.rb +28 -25
  10. data/lib/aixm/component/fato.rb +38 -26
  11. data/lib/aixm/component/frequency.rb +32 -20
  12. data/lib/aixm/component/geometry/arc.rb +16 -3
  13. data/lib/aixm/component/geometry/border.rb +8 -1
  14. data/lib/aixm/component/geometry/circle.rb +14 -2
  15. data/lib/aixm/component/geometry/point.rb +8 -1
  16. data/lib/aixm/component/geometry/rhumb_line.rb +8 -1
  17. data/lib/aixm/component/geometry.rb +20 -10
  18. data/lib/aixm/component/helipad.rb +41 -20
  19. data/lib/aixm/component/layer.rb +31 -20
  20. data/lib/aixm/component/lighting.rb +22 -24
  21. data/lib/aixm/component/runway.rb +32 -25
  22. data/lib/aixm/component/service.rb +11 -17
  23. data/lib/aixm/component/surface.rb +47 -14
  24. data/lib/aixm/component/timesheet.rb +178 -0
  25. data/lib/aixm/component/timetable.rb +32 -13
  26. data/lib/aixm/component/vasis.rb +36 -6
  27. data/lib/aixm/component/vertical_limit.rb +26 -4
  28. data/lib/aixm/component.rb +4 -1
  29. data/lib/aixm/concerns/hash_equality.rb +21 -0
  30. data/lib/aixm/concerns/intensity.rb +30 -0
  31. data/lib/aixm/concerns/marking.rb +21 -0
  32. data/lib/aixm/concerns/remarks.rb +21 -0
  33. data/lib/aixm/concerns/timetable.rb +22 -0
  34. data/lib/aixm/d.rb +20 -14
  35. data/lib/aixm/document.rb +22 -5
  36. data/lib/aixm/f.rb +29 -17
  37. data/lib/aixm/feature/airport.rb +91 -45
  38. data/lib/aixm/feature/airspace.rb +28 -5
  39. data/lib/aixm/feature/navigational_aid/designated_point.rb +8 -1
  40. data/lib/aixm/feature/navigational_aid/dme.rb +12 -2
  41. data/lib/aixm/feature/navigational_aid/marker.rb +9 -2
  42. data/lib/aixm/feature/navigational_aid/ndb.rb +15 -3
  43. data/lib/aixm/feature/navigational_aid/vor.rb +20 -3
  44. data/lib/aixm/feature/navigational_aid.rb +29 -20
  45. data/lib/aixm/feature/obstacle.rb +105 -29
  46. data/lib/aixm/feature/obstacle_group.rb +3 -7
  47. data/lib/aixm/feature/organisation.rb +23 -14
  48. data/lib/aixm/feature/unit.rb +23 -11
  49. data/lib/aixm/feature.rb +23 -4
  50. data/lib/aixm/memoize.rb +3 -3
  51. data/lib/aixm/p.rb +20 -14
  52. data/lib/aixm/payload_hash.rb +5 -2
  53. data/lib/aixm/r.rb +15 -12
  54. data/lib/aixm/refinements.rb +42 -2
  55. data/lib/aixm/schedule/date.rb +181 -0
  56. data/lib/aixm/schedule/day.rb +114 -0
  57. data/lib/aixm/schedule/time.rb +255 -0
  58. data/lib/aixm/shortcuts.rb +3 -0
  59. data/lib/aixm/version.rb +1 -1
  60. data/lib/aixm/w.rb +20 -13
  61. data/lib/aixm/xy.rb +36 -25
  62. data/lib/aixm/z.rb +29 -17
  63. data/lib/aixm.rb +13 -0
  64. data.tar.gz.sig +0 -0
  65. metadata +22 -13
  66. metadata.gz.sig +0 -0
@@ -0,0 +1,181 @@
1
+ using AIXM::Refinements
2
+
3
+ module AIXM
4
+ module Schedule
5
+
6
+ # Dates suitable for schedules
7
+ #
8
+ # This class implements the bare minimum of stdlib +Date+ and adds some
9
+ # extensions:
10
+ #
11
+ # * yearless dates
12
+ # * {covered_by?} to check whether (yearless) date falls within (yearless)
13
+ # date range
14
+ #
15
+ # @example
16
+ # date = AIXM.date('2022-04-20') # => 2022-04-20
17
+ # from = AIXM.date('03-20') # => XXXX-03-20
18
+ # date.covered_by?(from..AIXM.date('05-20')) # => true
19
+ class Date
20
+ include AIXM::Concerns::HashEquality
21
+ include Comparable
22
+ extend Forwardable
23
+
24
+ YEARLESS_YEAR = 0
25
+
26
+ # @api private
27
+ attr_accessor :date
28
+
29
+ # Parse the given representation of (yearless) date.
30
+ #
31
+ # @param date [Date, Time, String] either stdlib Date/Time, "YYYY-MM-DD",
32
+ # "XXXX-MM-DD" or "MM-DD" (yearless date)
33
+ def initialize(date)
34
+ @date = case date.to_s[0, 10]
35
+ when /\A\d{4}-\d{2}-\d{2}\z/
36
+ ::Date.strptime(date.to_s)
37
+ when /\A(?:XXXX-)?(\d{2}-\d{2})\z/
38
+ ::Date.strptime("#{YEARLESS_YEAR}-#{$1}")
39
+ else
40
+ fail ArgumentError
41
+ end
42
+ rescue ::Date::Error
43
+ raise ArgumentError
44
+ end
45
+
46
+ # Human readable representation such as "2002-05-19" or "XXXX-05-19"
47
+ #
48
+ # All formats from {strftime}[https://www.rubydoc.info/stdlib/date/Date#strftime-instance_method]
49
+ # are supported, however, +%Y+ is replaced with "XXXX" for yearless dates.
50
+ # Any other formats containing the year won't do so and should be avoided!
51
+ #
52
+ # @param format [String] see {strftime}[https://www.rubydoc.info/stdlib/date/Date#strftime-instance_method]
53
+ # @return [String]
54
+ def to_s(format='%Y-%m-%d')
55
+ @date.strftime(yearless? ? format.gsub('%Y', 'XXXX') : format)
56
+ end
57
+
58
+ def inspect
59
+ %Q(#<#{self.class} #{to_s}>)
60
+ end
61
+
62
+ # Creates a new date with the given parts altered.
63
+ #
64
+ # @example
65
+ # date = AIXM.date('2000-12-22')
66
+ # date.at(month: 4) # => 2000-04-22
67
+ # date.at(year: 2020, day: 5) # => 2020-12-05
68
+ # date.at(month: 1) # => 2020-01-22
69
+ # date.at(month: 1, wrap: true) # => 2021-01-22 (year incremented)
70
+ #
71
+ # @param year [Integer] new year
72
+ # @param month [Integer] new month
73
+ # @param day [Integer] new day
74
+ # @param wrap [Boolean] whether to increment month when crossing month
75
+ # boundary and year when crossing year boundary
76
+ # @return [AIXM::Schedule::Date]
77
+ def at(year: nil, month: nil, day: nil, wrap: false)
78
+ return self unless year || month || day
79
+ wrap_month, wrap_year = day&.<(date.day), month&.<(date.month)
80
+ date = ::Date.new(year || self.year || YEARLESS_YEAR, month || self.month, day || self.day)
81
+ date = date.next_month if wrap && wrap_month && !month
82
+ date = date.next_year if wrap && wrap_year && !year
83
+ self.class.new(date.strftime(yearless? ? '%m-%d' : '%F'))
84
+ end
85
+
86
+ # Create new date one day after this one.
87
+ #
88
+ # @return [AIXM::Schedule::Date]
89
+ def succ
90
+ self.class.new(date.next_day).at(year: (YEARLESS_YEAR if yearless?))
91
+ end
92
+
93
+ # Convert date to day
94
+ #
95
+ # @raise [RuntimeError] if date is yearless
96
+ # @return [AIXM::Schedule::Day]
97
+ def to_day
98
+ fail "cannot convert yearless date" if yearless?
99
+ AIXM.day(date.wday)
100
+ end
101
+
102
+ # Stdlib Date equivalent using the value of {YEARLESS_YEAR} to represent a
103
+ # yearless date.
104
+ #
105
+ # @return [Date]
106
+ def to_date
107
+ @date
108
+ end
109
+
110
+ # Whether the other schedule date can be compared to this one.
111
+ #
112
+ # @param other [AIXM::Schedule::Date]
113
+ # @return [Boolean]
114
+ def comparable_to?(other)
115
+ other.instance_of?(self.class) && yearless? == other.yearless?
116
+ end
117
+
118
+ def <=>(other)
119
+ fail "not comparable" unless comparable_to? other
120
+ @date.jd <=> other.to_date.jd
121
+ end
122
+
123
+ # @see Object#hash
124
+ def hash
125
+ [self.class, @date.jd].hash
126
+ end
127
+
128
+ # Whether this schedule date is yearless or not.
129
+ #
130
+ # @return [Boolean]
131
+ def yearless?
132
+ @date.year == YEARLESS_YEAR
133
+ end
134
+
135
+ # Yearless duplicate of self
136
+ #
137
+ # @return [AIXM::Schedule::Date]
138
+ def to_yearless
139
+ yearless? ? self : self.class.new(to_s[5..])
140
+ end
141
+
142
+ # @return [Integer] year or +nil+ if yearless
143
+ def year
144
+ @date.year unless yearless?
145
+ end
146
+
147
+ # @!method month
148
+ # @return [Integer]
149
+ # @!method day
150
+ # @return [Integer] day of month
151
+ def_delegators :@date, :month, :day
152
+
153
+ # Whether this schedule date falls within the given range of schedule dates
154
+ #
155
+ # @note It is possible to compare dates as well as days.
156
+ #
157
+ # @param other [AIXM::Schedule::Date, Range<AIXM::Schedule::Date>,
158
+ # AIXM::Schedule::Day, Range<AIXM::Schedule::Day>] single schedule
159
+ # date/day or range of schedule dates/days
160
+ # @return [Boolean]
161
+ def covered_by?(other)
162
+ range = Range.from(other)
163
+ case
164
+ when range.first.instance_of?(AIXM::Schedule::Day)
165
+ range.first.any? || to_day.covered_by?(range)
166
+ when range.first.yearless?
167
+ yearless? ? covered_by_yearless_date?(range) : to_yearless.covered_by?(range)
168
+ else
169
+ yearless? ? covered_by_yearless_date?(range.first.to_yearless..range.last.to_yearless) : range.cover?(self)
170
+ end
171
+ end
172
+
173
+ private
174
+
175
+ def covered_by_yearless_date?(range)
176
+ range.min ? range.cover?(self) : range.first <= self || self <= range.last
177
+ end
178
+ end
179
+
180
+ end
181
+ end
@@ -0,0 +1,114 @@
1
+ using AIXM::Refinements
2
+
3
+ module AIXM
4
+ module Schedule
5
+
6
+ # Days suitable for schedules
7
+ #
8
+ # @example
9
+ # from = AIXM.day(:monday) # => :monday
10
+ # to = AIXM.day(4) # => :thursday
11
+ # AIXM.day(:tuesday).covered_by?(from..to) # => true
12
+ class Day
13
+ include AIXM::Concerns::HashEquality
14
+ include Comparable
15
+
16
+ DAYS = %i(sunday monday tuesday wednesday thursday friday saturday workday day_preceding_workday day_following_workday holiday day_preceding_holiday day_following_holiday any).freeze
17
+ SORTABLE_DAYS = DAYS[0, 7]
18
+
19
+ # Day of the week or special named day
20
+ #
21
+ # @return [Symbol] any from {DAYS}
22
+ attr_reader :day
23
+
24
+ # Set the given day of the week or special named day.
25
+ #
26
+ # @param day [Symbol, String, Integer] any from {DAYS} or 0=Monday to
27
+ # 6=Sunday
28
+ def initialize(day=:any)
29
+ case day
30
+ when Symbol, String
31
+ self.day = day
32
+ when Integer
33
+ fail ArgumentError unless day.between?(0, 6)
34
+ self.day = SORTABLE_DAYS[day]
35
+ else
36
+ fail ArgumentError
37
+ end
38
+ end
39
+
40
+ # Human readable representation such as "monday" or "day preceding workday"
41
+ #
42
+ # @return [String]
43
+ def to_s
44
+ day.to_s.gsub('_', ' ')
45
+ end
46
+
47
+ def inspect
48
+ %Q(#<#{self.class} #{to_s}>)
49
+ end
50
+
51
+ # Whether two days are equal.
52
+ #
53
+ # @return [Boolean]
54
+ def ==(other)
55
+ day == other.day
56
+ end
57
+
58
+ # @see Object#hash
59
+ def hash
60
+ [self.class, day].hash
61
+ end
62
+
63
+ # Whether the day is set to :any
64
+ #
65
+ # @return [Boolean]
66
+ def any?
67
+ day == :any
68
+ end
69
+
70
+ # Whether this schedule day sortable.
71
+ #
72
+ # @return [Boolean]
73
+ def sortable?
74
+ SORTABLE_DAYS.include? day
75
+ end
76
+
77
+ # Whether this schedule day falls within the given range of schedule
78
+ # days.
79
+ #
80
+ # @note Only weekdays and +:any+ can be computed!
81
+ #
82
+ # @param other [AIXM::Schedule::Day, Range<AIXM::Schedule::Day>] single
83
+ # schedule day or range of schedule days
84
+ # @raise RuntimeError if anything but workdays or +:any+ are involved
85
+ # @return [Boolean]
86
+ def covered_by?(other)
87
+ range = Range.from other
88
+ case
89
+ when any? || range.first.any? || range.last.any?
90
+ true
91
+ when !sortable? || !range.first.sortable? || !range.last.sortable?
92
+ fail "includes unsortables"
93
+ when range.min
94
+ range.cover? self
95
+ else
96
+ range.first <= self || self <= range.last
97
+ end
98
+ end
99
+
100
+ private
101
+
102
+ def day=(value)
103
+ @day = value.to_s.to_sym
104
+ fail ArgumentError unless DAYS.include? @day
105
+ end
106
+
107
+ # @note Necessary to use this class in Range.
108
+ def <=>(other)
109
+ DAYS.index(day) <=> DAYS.index(other.day) || day.to_s <=> other.to_s
110
+ end
111
+ end
112
+
113
+ end
114
+ end
@@ -0,0 +1,255 @@
1
+ using AIXM::Refinements
2
+
3
+ module AIXM
4
+ module Schedule
5
+
6
+ # Times suitable for schedules
7
+ #
8
+ # This class implements the bare minimum of stdlib +Time+ and adds some
9
+ # extensions:
10
+ #
11
+ # * converts to UTC
12
+ # * date, seconds and milliseconds are ignored
13
+ # * {covered_by?} to check whether schedule time falls within range of times
14
+ #
15
+ # @note The {DATELESS_DATE} is used to mark the date of the internal +Time"
16
+ # object irrelevant. However, Ruby does not persist end of days as 24:00,
17
+ # therefore {DATELESS_DATE} + 1 marks this case.
18
+ #
19
+ # @example
20
+ # time = AIXM.time('21:30') # => 21:30
21
+ # time.covered_by?(AIXM.time('20:00')..AIXM.time('02:00')) # => true
22
+ class Time
23
+ include AIXM::Concerns::HashEquality
24
+ extend Forwardable
25
+
26
+ EVENTS = %i(sunrise sunset).freeze
27
+ PRECEDENCES = { first: :min, last: :max }.freeze
28
+ DATELESS_DATE = ::Date.parse('0000-01-01').freeze
29
+
30
+ # @api private
31
+ attr_accessor :time
32
+
33
+ # Event or alternative to time
34
+ #
35
+ # @return [Symbol, nil] any from {EVENTS}
36
+ attr_reader :event
37
+
38
+ # Minutes added or subtracted from event
39
+ #
40
+ # @return [Integer, nil]
41
+ attr_reader :delta
42
+
43
+ # Precedence of time vs. event
44
+ #
45
+ # @return [Symbol, nil] any key of {PRECEDENCES}
46
+ attr_reader :precedence
47
+
48
+ # Parse the given representation of time.
49
+ #
50
+ # @example
51
+ # AIXM.time('08:00')
52
+ # AIXM.time(:sunrise)
53
+ # AIXM.time(:sunrise, plus: 30)
54
+ # AIXM.time('08:00', or: :sunrise)
55
+ # AIXM.time('08:00', or: :sunrise, plus: 30)
56
+ # AIXM.time('08:00', or: :sunrise, minus: 15)
57
+ # AIXM.time('08:00', or: :sunrise, whichever_comes: :last)
58
+ #
59
+ # @param time_or_event [Time, DateTime, String, Symbol] either time as
60
+ # stdlib Time or DateTime, "HH:MM" (implicitly UTC), "HH:MM [+-]00:00",
61
+ # "HH:MM UTC" or event from {EVENTS} as Symbol
62
+ # @param or [Symbol] alternative event from {EVENTS}
63
+ # @param plus [Integer] minutes added to event
64
+ # @param minus [Integer] minutes subtracted from event
65
+ # @param whichever_comes [Symbol] any key from {PRECEDENCES}
66
+ def initialize(time_or_event, or: nil, plus: 0, minus: 0, whichever_comes: :first)
67
+ alternative_event = binding.local_variable_get(:or) # necessary since "or" is a keyword
68
+ @time = @event = @precedence = nil
69
+ case time_or_event
70
+ when Symbol
71
+ self.event = time_or_event
72
+ when ::Time, DateTime
73
+ time_or_event = time_or_event.to_time
74
+ set_time(time_or_event.hour, time_or_event.min, time_or_event.utc_offset)
75
+ when /\A(\d{2}):?(\d{2}) ?([+-]\d{2}:?\d{2}|UTC)?\z/
76
+ set_time($1, $2, $3)
77
+ else
78
+ fail(ArgumentError, "time or event not recognized")
79
+ end
80
+ fail(ArgumentError, "only one event allowed") if event && alternative_event
81
+ self.event ||= alternative_event
82
+ @delta = event ? plus - minus : 0
83
+ if @time && event
84
+ self.precedence = whichever_comes
85
+ fail(ArgumentError, "mandatory precedence missing") unless precedence
86
+ end
87
+ end
88
+
89
+ # Human readable representation
90
+ #
91
+ # The format recognises does the following interpolations:
92
+ # * +%R+ - "HH:MM" in UTC if time is present, "" otherwise
93
+ # * +%z+ - "UTC" if time is present, "" otherwise
94
+ # * +%o+ - "or" if both time and event are present, "" otherwise
95
+ # * +%E+ - "sunrise-15min" if no event is present, "" otherwise
96
+ # * +%P+ - "whichever comes first" if precedence is present, "" otherwise
97
+ #
98
+ # @param format [String]
99
+ # @return [String]
100
+ def to_s(format='%R %z %o %E %P')
101
+ format.gsub(/%[RzoEP]/,
102
+ '%R' => (sprintf("%02d:%02d", hour, min) if @time),
103
+ '%z' => ('UTC' if @time),
104
+ '%o' => ('or' if @time && event),
105
+ '%E' => "#{event}#{sprintf("%+dmin", delta) unless delta.zero?}",
106
+ '%P' => ("whichever comes #{precedence}" if precedence)
107
+ ).compact
108
+ end
109
+
110
+ def inspect
111
+ %Q(#<#{self.class} #{to_s}>)
112
+ end
113
+
114
+ # Creates a new time with the given parts altered.
115
+ #
116
+ # @example
117
+ # time = AIXM.time('22:12')
118
+ # time.at(min: 0) # => 22:00
119
+ # time.at(min: 0 wrap: true) # => 2021-01-22 (year incremented)
120
+ #
121
+ # @param hour [Integer] new hour
122
+ # @param min [Integer] new minutes
123
+ # @param wrap [Boolean] whether to increment hour when crossing minutes
124
+ # boundary
125
+ # @return [AIXM::Schedule::Date]
126
+ def at(hour: nil, min: nil, wrap: false)
127
+ return self unless hour || min
128
+ min ||= time.min
129
+ hour ||= time.hour
130
+ hour = (hour + 1) % 24 if wrap && min < time.min
131
+ self.class.new("%02d:%02d" % [hour, min])
132
+ end
133
+
134
+ # Resolve event to simple time
135
+ #
136
+ # * If +self+ doesn't have any event, +self+ is returned.
137
+ # * Otherwise a new time is created with the event resolved for the
138
+ # given date and geographical location.
139
+ #
140
+ # @example
141
+ # time = AIXM.time('21:00', or: :sunset, minus: 30, whichever_cones: first)
142
+ # time.resolve(on: AIXM.date('2000-08-01'), at: AIXM.xy(lat: 48.8584, long: 2.2945))
143
+ # # => 20:50
144
+ #
145
+ # @param on [AIXM::Date] defaults to today
146
+ # @param xy [AIXM::XY]
147
+ # @return [AIXM::Schedule::Time, self]
148
+ def resolve(on:, xy:)
149
+ if resolved?
150
+ self
151
+ else
152
+ sun_time = self.class.new(Sun.send(event, on.to_date, xy.lat, xy.long).utc + (delta * 60))
153
+ if time
154
+ self.class.new([sun_time.time, self.time].send(PRECEDENCES.fetch(precedence)))
155
+ else
156
+ sun_time
157
+ end
158
+ end
159
+ end
160
+
161
+ # Whether this time is resolved and doesn't contain an event (anymore).
162
+ #
163
+ # @return [Boolean]
164
+ def resolved?
165
+ !event
166
+ end
167
+
168
+ # Stdlib Time equivalent using the value of {DATELESS_DATE} to represent a
169
+ # time only.
170
+ #
171
+ # @return [Time]
172
+ def to_time
173
+ @time
174
+ end
175
+
176
+ # Hour from 0 (beginning of day) to 24 (end of day)
177
+ #
178
+ # @return [Integer]
179
+ def hour
180
+ @time.hour + (end_of_day? ? 24 : 0)
181
+ end
182
+
183
+ # @!method min
184
+ # @return [Integer]
185
+ def_delegators :@time, :min
186
+
187
+ # Whether two times are equal.
188
+ #
189
+ # @return [Boolean]
190
+ def ==(other)
191
+ to_s == other.to_s
192
+ end
193
+
194
+ # Whether this schedule time is sortable.
195
+ #
196
+ # @return [Boolean]
197
+ def sortable?
198
+ !event
199
+ end
200
+
201
+ # Whether this schedule time falls within the given range of schedule
202
+ # times.
203
+ #
204
+ # @param other [AIXM::Schedule::Time, Range<AIXM::Schedule::Time>] single
205
+ # schedule time or range of schedule times
206
+ # @raise RuntimeError if either self is or the range contains an
207
+ # unsortable time with event
208
+ # @return [Boolean]
209
+ def covered_by?(other)
210
+ range = Range.from(other)
211
+ case
212
+ when !sortable? || !range.first.sortable? || !range.last.sortable?
213
+ fail "includes unsortables"
214
+ when range.min
215
+ range.first.to_s <= self.to_s && self.to_s <= range.last.to_s
216
+ else
217
+ range.first.to_s <= self.to_s || self.to_s <= range.last.to_s
218
+ end
219
+ end
220
+
221
+ private
222
+
223
+ # Set the +@time+ instance variable.
224
+ #
225
+ # @param hour [Integer, String]
226
+ # @param min [Integer, String]
227
+ # @param offset [Integer, String] either UTC offset in seconds
228
+ # (default: 0) or 'UTC'
229
+ # @return [Time]
230
+ def set_time(hour, min, offset)
231
+ @time = ::Time.new(DATELESS_DATE.year, DATELESS_DATE.month, DATELESS_DATE.day, hour, min, 0, offset || 0).utc
232
+ end
233
+
234
+ def event=(value)
235
+ fail ArgumentError if value && !EVENTS.include?(value)
236
+ @event = value
237
+ end
238
+
239
+ def precedence=(value)
240
+ fail ArgumentError if value && !PRECEDENCES.has_key?(value)
241
+ @precedence = value
242
+ end
243
+
244
+ def end_of_day?
245
+ @time.day > DATELESS_DATE.day
246
+ end
247
+
248
+ # @note Necessary to use this class in Range.
249
+ def <=>(other)
250
+ to_time <=> other.to_time || to_s <=> other.to_s
251
+ end
252
+ end
253
+
254
+ end
255
+ end
@@ -18,4 +18,7 @@ module AIXM
18
18
  # Timetable used to signal "always active"
19
19
  H24 = timetable(code: :H24).freeze
20
20
 
21
+ # Day to signal "whatever date or day"
22
+ ANY_DAY = AIXM.day(:any).freeze
23
+
21
24
  end
data/lib/aixm/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module AIXM
2
- VERSION = "1.1.0".freeze
2
+ VERSION = "1.2.0".freeze
3
3
  end
data/lib/aixm/w.rb CHANGED
@@ -7,6 +7,7 @@ module AIXM
7
7
  # @example
8
8
  # AIXM.w(2.9, :t)
9
9
  class W
10
+ include AIXM::Concerns::HashEquality
10
11
  include Comparable
11
12
  extend Forwardable
12
13
 
@@ -17,16 +18,29 @@ module AIXM
17
18
  ton: { kg: 907.18474, t: 0.90718474, lb: 2000.00000013718828 }
18
19
  }.freeze
19
20
 
21
+ # Whether weight is zero.
22
+ #
20
23
  # @!method zero?
21
- # @return [Boolean] whether weight is zero
24
+ # @return [Boolean]
22
25
  def_delegator :@wgt, :zero?
23
26
 
24
- # @return [Float] weight
27
+ # Weight
28
+ #
29
+ # @overload wgt
30
+ # @return [Float]
31
+ # @overload wgt=(value)
32
+ # @param value [Float]
25
33
  attr_reader :wgt
26
34
 
27
- # @return [Symbol] unit (see {UNITS})
35
+ # Unit
36
+ #
37
+ # @overload unit
38
+ # @return [Float] any of {UNITS}
39
+ # @overload unit=(value)
40
+ # @param value [Float] any of {UNITS}
28
41
  attr_reader :unit
29
42
 
43
+ # See the {overview}[AIXM::W] for examples.
30
44
  def initialize(wgt, unit)
31
45
  self.wgt, self.unit = wgt, unit
32
46
  end
@@ -52,12 +66,13 @@ module AIXM
52
66
  fail(ArgumentError, "invalid unit") unless UNITS.has_key? @unit
53
67
  end
54
68
 
69
+ # Convert weight
70
+ #
55
71
  # @!method to_kg
56
72
  # @!method to_t
57
73
  # @!method to_lb
58
74
  # @!method to_ton
59
- #
60
- # @return [AIXM::W] convert weight
75
+ # @return [AIXM::W] converted weight
61
76
  UNITS.each_key do |target_unit|
62
77
  define_method "to_#{target_unit}" do
63
78
  return self if unit == target_unit
@@ -66,22 +81,14 @@ module AIXM
66
81
  end
67
82
 
68
83
  # @see Object#<=>
69
- # @return [Integer]
70
84
  def <=>(other)
71
85
  wgt <=> other.send(:"to_#{unit}").wgt
72
86
  end
73
87
 
74
88
  # @see Object#==
75
- # @return [Boolean]
76
89
  def ==(other)
77
90
  self.class === other && (self <=> other).zero?
78
91
  end
79
- alias_method :eql?, :==
80
92
 
81
- # @see Object#hash
82
- # @return [Integer]
83
- def hash
84
- to_s.hash
85
- end
86
93
  end
87
94
  end