aixm 1.1.0 → 1.2.0

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.
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