aixm 1.2.1 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +23 -3
  4. data/README.md +37 -2
  5. data/exe/ckmid +1 -7
  6. data/exe/mkmid +1 -7
  7. data/lib/aixm/classes.rb +3 -1
  8. data/lib/aixm/component/address.rb +12 -15
  9. data/lib/aixm/component/approach_lighting.rb +11 -16
  10. data/lib/aixm/component/fato.rb +22 -34
  11. data/lib/aixm/component/frequency.rb +10 -15
  12. data/lib/aixm/component/geometry/arc.rb +2 -3
  13. data/lib/aixm/component/geometry/border.rb +6 -10
  14. data/lib/aixm/component/geometry/circle.rb +4 -4
  15. data/lib/aixm/component/geometry/point.rb +4 -4
  16. data/lib/aixm/component/geometry/rhumb_line.rb +4 -4
  17. data/lib/aixm/component/geometry.rb +4 -4
  18. data/lib/aixm/component/helipad.rb +13 -20
  19. data/lib/aixm/component/layer.rb +6 -8
  20. data/lib/aixm/component/lighting.rb +12 -17
  21. data/lib/aixm/component/runway.rb +26 -38
  22. data/lib/aixm/component/service.rb +12 -16
  23. data/lib/aixm/component/surface.rb +8 -10
  24. data/lib/aixm/component/timesheet.rb +9 -10
  25. data/lib/aixm/component/timetable.rb +6 -7
  26. data/lib/aixm/component/vasis.rb +6 -8
  27. data/lib/aixm/component/vertical_limit.rb +8 -8
  28. data/lib/aixm/component.rb +3 -2
  29. data/lib/aixm/concerns/association.rb +381 -0
  30. data/lib/aixm/concerns/memoize.rb +107 -0
  31. data/lib/aixm/concerns/xml_builder.rb +34 -0
  32. data/lib/aixm/document.rb +52 -21
  33. data/lib/aixm/feature/airport.rb +44 -47
  34. data/lib/aixm/feature/airspace.rb +29 -34
  35. data/lib/aixm/feature/generic.rb +67 -0
  36. data/lib/aixm/feature/navigational_aid/designated_point.rb +11 -13
  37. data/lib/aixm/feature/navigational_aid/dme.rb +12 -15
  38. data/lib/aixm/feature/navigational_aid/marker.rb +12 -15
  39. data/lib/aixm/feature/navigational_aid/ndb.rb +13 -16
  40. data/lib/aixm/feature/navigational_aid/tacan.rb +15 -17
  41. data/lib/aixm/feature/navigational_aid/vor.rb +16 -19
  42. data/lib/aixm/feature/navigational_aid.rb +7 -7
  43. data/lib/aixm/feature/obstacle.rb +20 -21
  44. data/lib/aixm/feature/obstacle_group.rb +19 -20
  45. data/lib/aixm/feature/organisation.rb +11 -12
  46. data/lib/aixm/feature/unit.rb +16 -18
  47. data/lib/aixm/feature.rb +26 -7
  48. data/lib/aixm/object.rb +1 -1
  49. data/lib/aixm/refinements.rb +57 -0
  50. data/lib/aixm/schedule/date.rb +13 -1
  51. data/lib/aixm/schedule/date_time.rb +56 -0
  52. data/lib/aixm/schedule/time.rb +5 -1
  53. data/lib/aixm/shortcuts.rb +8 -2
  54. data/lib/aixm/version.rb +1 -1
  55. data/lib/aixm.rb +5 -3
  56. data/schemas/ofmx/0.1/OFMX-Snapshot.xsd +6 -1
  57. data.tar.gz.sig +2 -3
  58. metadata +26 -38
  59. metadata.gz.sig +2 -3
  60. data/lib/aixm/association.rb +0 -378
  61. data/lib/aixm/memoize.rb +0 -105
@@ -0,0 +1,56 @@
1
+ using AIXM::Refinements
2
+
3
+ module AIXM
4
+ module Schedule
5
+
6
+ # Datetimes suitable for schedules
7
+ #
8
+ # This class combines +AIXM::Schedule::Date+ and +AIXM::Schedule::Time+:
9
+ #
10
+ # @example
11
+ # datetime = AIXM.datetime('2022-04-20', '20:00') # => 2022-04-20 20:00
12
+ # datetime.date # => 2022-04-20
13
+ # datetime.date.class # => AIXM::Schedule::Date
14
+ # datetime.time # => 20:00
15
+ # datetime.time.class # => AIXM::Schedule::Time
16
+ class DateTime
17
+
18
+ # Date part
19
+ #
20
+ # @return [AIXM::Schedule::Date]
21
+ attr_reader :date
22
+
23
+ # Time part
24
+ #
25
+ # @return [AIXM::Schedule::Time]
26
+ attr_reader :time
27
+
28
+ # Parse the given representation of date and time.
29
+ #
30
+ # @param date [AIXM::Schedule::Date]
31
+ # @param time [AIXM::Schedule::Time]
32
+ def initialize(date, time)
33
+ fail(ArgumentError, 'invalid date') unless date.instance_of? AIXM::Schedule::Date
34
+ fail(ArgumentError, 'invalid time') unless time.instance_of? AIXM::Schedule::Time
35
+ @date, @time = date, time
36
+ end
37
+
38
+ # Human readable representation such as "2002-05-19 20:00"
39
+ #
40
+ # @return [String]
41
+ def to_s
42
+ [@date.to_s, @time.to_s].join(' ')
43
+ end
44
+
45
+ def inspect
46
+ %Q(#<#{self.class} #{to_s}>)
47
+ end
48
+
49
+ # @see Object#hash
50
+ def hash
51
+ [@date.hash, @time.hash].hash
52
+ end
53
+ end
54
+
55
+ end
56
+ end
@@ -19,6 +19,10 @@ module AIXM
19
19
  # @example
20
20
  # time = AIXM.time('21:30') # => 21:30
21
21
  # time.covered_by?(AIXM.time('20:00')..AIXM.time('02:00')) # => true
22
+ #
23
+ # ===Shortcuts:
24
+ # * +AIXM::BEGINNING_OF_DAY+ - midnight expressed as "00:00"
25
+ # * +AIXM::END_OF_DAY+ - midnight expressed as "24:00"
22
26
  class Time
23
27
  include AIXM::Concerns::HashEquality
24
28
  extend Forwardable
@@ -73,7 +77,7 @@ module AIXM
73
77
  case time_or_event
74
78
  when Symbol
75
79
  self.event = time_or_event
76
- when ::Time, DateTime
80
+ when ::Time, ::DateTime
77
81
  time_or_event = time_or_event.to_time
78
82
  set_time(time_or_event.hour, time_or_event.min, time_or_event.utc_offset)
79
83
  when /\A(\d{2}):?(\d{2}) ?([+-]\d{2}:?\d{2}|UTC)?\z/
@@ -15,10 +15,16 @@ module AIXM
15
15
  # Max flight level used to signal "no upper limit"
16
16
  UNLIMITED = z(999, :qne).freeze
17
17
 
18
+ # Day to signal "whatever date or day"
19
+ ANY_DAY = AIXM.day(:any).freeze
20
+
18
21
  # Timetable used to signal "always active"
19
22
  H24 = timetable(code: :H24).freeze
20
23
 
21
- # Day to signal "whatever date or day"
22
- ANY_DAY = AIXM.day(:any).freeze
24
+ # Time which marks midnight at beginning of the day
25
+ BEGINNING_OF_DAY = AIXM.time('00:00').freeze
26
+
27
+ # Time which marks midnight at end of the day
28
+ END_OF_DAY = AIXM.time('24:00').freeze
23
29
 
24
30
  end
data/lib/aixm/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module AIXM
2
- VERSION = "1.2.1".freeze
2
+ VERSION = "1.3.1".freeze
3
3
  end
data/lib/aixm.rb CHANGED
@@ -8,7 +8,6 @@ require 'forwardable'
8
8
  require 'digest'
9
9
  require 'optparse'
10
10
 
11
- require 'builder'
12
11
  require 'nokogiri'
13
12
  require 'dry/inflector'
14
13
  require 'sun'
@@ -22,10 +21,11 @@ require_relative 'aixm/errors'
22
21
 
23
22
  require_relative 'aixm/classes'
24
23
  require_relative 'aixm/constants'
25
- require_relative 'aixm/memoize'
26
- require_relative 'aixm/association'
27
24
  require_relative 'aixm/payload_hash'
28
25
 
26
+ require_relative 'aixm/concerns/memoize'
27
+ require_relative 'aixm/concerns/association'
28
+ require_relative 'aixm/concerns/xml_builder'
29
29
  require_relative 'aixm/concerns/hash_equality'
30
30
  require_relative 'aixm/concerns/timetable'
31
31
  require_relative 'aixm/concerns/intensity'
@@ -45,6 +45,7 @@ require_relative 'aixm/p'
45
45
  require_relative 'aixm/schedule/date'
46
46
  require_relative 'aixm/schedule/day'
47
47
  require_relative 'aixm/schedule/time'
48
+ require_relative 'aixm/schedule/date_time'
48
49
 
49
50
  require_relative 'aixm/component'
50
51
  require_relative 'aixm/component/address'
@@ -82,6 +83,7 @@ require_relative 'aixm/feature/obstacle'
82
83
  require_relative 'aixm/feature/obstacle_group'
83
84
  require_relative 'aixm/feature/organisation'
84
85
  require_relative 'aixm/feature/unit'
86
+ require_relative 'aixm/feature/generic'
85
87
 
86
88
  require_relative 'aixm/shortcuts'
87
89
  require_relative 'aixm/executables'
@@ -215,7 +215,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
215
215
  </xsd:attribute>
216
216
  <xsd:attribute name="effective" type="xsd:dateTime" use="required">
217
217
  <xsd:annotation>
218
- <xsd:documentation>The date and time used as criteria to select valid versions included in the message</xsd:documentation>
218
+ <xsd:documentation>The beginning date and time used as criteria to select valid versions included in the message</xsd:documentation>
219
+ </xsd:annotation>
220
+ </xsd:attribute>
221
+ <xsd:attribute name="expiration" type="xsd:dateTime">
222
+ <xsd:annotation>
223
+ <xsd:documentation>The optional end date and time used as criteria to select valid versions included in the message</xsd:documentation>
219
224
  </xsd:annotation>
220
225
  </xsd:attribute>
221
226
  </xsd:complexType>
data.tar.gz.sig CHANGED
@@ -1,3 +1,2 @@
1
- /5�D�����owD~
2
- Z��.W;6��(s���Γ��~3��=�P(˲n/�f��,v����RS��Bi��Lb@��:*�<Ɨ�٩}W���wcFv�ܼ�VC��
3
- uL��y4Du3��"�����jwnTT���m����O*���n�
1
+ ��W���c
2
+ 滺P~��[9�z'�:������w3w�41�S�/0�Н$f�t��w;�m�O[�l�ϭ�ώK�Wy��z6�t���L��3�����3xjB��d�!}P�9�hQe�-���%�*4��֎�_B��Ku�'Y���5�K��)4FX�}�����&�� 0�PR����B�ՠ�B32�cZ���ӎA8v^���w��Ӎ�����ɹ��M�7|&6;�%7\���4n�M��!�O����r�
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aixm
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sven Schwyn
@@ -10,42 +10,27 @@ bindir: exe
10
10
  cert_chain:
11
11
  - |
12
12
  -----BEGIN CERTIFICATE-----
13
- MIIDcDCCAligAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MQ0wCwYDVQQDDARydWJ5
14
- MRkwFwYKCZImiZPyLGQBGRYJYml0Y2V0ZXJhMRMwEQYKCZImiZPyLGQBGRYDY29t
15
- MB4XDTIxMTEwODE0MzIyM1oXDTIyMTEwODE0MzIyM1owPzENMAsGA1UEAwwEcnVi
16
- eTEZMBcGCgmSJomT8ixkARkWCWJpdGNldGVyYTETMBEGCgmSJomT8ixkARkWA2Nv
17
- bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANwuD4geNdhpSVNJTtHb
18
- fmVAoPxmER4oyGgaVJSidn/OjU5PcpdypMI/WIxfvjfFizq6kQYAsJZbCr6fG+UN
19
- 2dZGMXcAC/uKQL5nYESjCPJ4IJP/SC+fiiEpxHQk7PNFoiUVRUieUZIAfHZAdnY3
20
- ye1/niW7ud0vwKIMrysKWxjgkE0Y6Af1QLzV/6brVRRC5MvHIzYJd8BiSP+wY1O8
21
- VElw1f6d90KEz2vaQfX7vCxrzIbvAnYiSvM0AIPy/zigTqpW6w3w4sQxQj81oQ9U
22
- 9vDYtQzXj0c9VrSLvb0DgiGug2cU2bDjA4L3cBE1szX4tbfo8syYqMq51/kTGDxW
23
- YNUCAwEAAaN3MHUwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFJ8r
24
- wy1HraZDqg3Khf9UonMWMtJUMB0GA1UdEQQWMBSBEnJ1YnlAYml0Y2V0ZXJhLmNv
25
- bTAdBgNVHRIEFjAUgRJydWJ5QGJpdGNldGVyYS5jb20wDQYJKoZIhvcNAQEFBQAD
26
- ggEBACI7lJKRbnRjz0T4Wb9jH4SE0A2yaHAoBzj96luVDjNyoRT3688trEZS75Sg
27
- GKfChxqKncBrSpxJ0YfWbymNHfUrKhcdSifJ/TtUrTasm6LSnJYLOnLKDO3v8eL3
28
- gRTq8a5wA7Xtijx3MJEyzdeUh7N+UMKuPps/flPgH5yabUxgxrvuhrXF7Z96nrsP
29
- EOmNMTc8H7wo4BAKfuMcI/Gh2oCf+tAhr0bGsXyBikmJ6XA45mrOMgv19M1+aMpn
30
- 1+2Y1+i+4jd1B7qxIgOLxQTNIJiwE0sqU1itFfuesfgUACS7M0IV9u9Bp4hBGNEw
31
- 5JcY2h7owdMxXIvgk1oakgldFJc=
13
+ MIIDODCCAiCgAwIBAgIBATANBgkqhkiG9w0BAQsFADAjMSEwHwYDVQQDDBhydWJ5
14
+ L0RDPWJpdGNldGVyYS9EQz1jb20wHhcNMjIxMTA2MTIzNjUwWhcNMjMxMTA2MTIz
15
+ NjUwWjAjMSEwHwYDVQQDDBhydWJ5L0RDPWJpdGNldGVyYS9EQz1jb20wggEiMA0G
16
+ CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDcLg+IHjXYaUlTSU7R235lQKD8ZhEe
17
+ KMhoGlSUonZ/zo1OT3KXcqTCP1iMX743xYs6upEGALCWWwq+nxvlDdnWRjF3AAv7
18
+ ikC+Z2BEowjyeCCT/0gvn4ohKcR0JOzzRaIlFUVInlGSAHx2QHZ2N8ntf54lu7nd
19
+ L8CiDK8rClsY4JBNGOgH9UC81f+m61UUQuTLxyM2CXfAYkj/sGNTvFRJcNX+nfdC
20
+ hM9r2kH1+7wsa8yG7wJ2IkrzNACD8v84oE6qVusN8OLEMUI/NaEPVPbw2LUM149H
21
+ PVa0i729A4IhroNnFNmw4wOC93ARNbM1+LW36PLMmKjKudf5Exg8VmDVAgMBAAGj
22
+ dzB1MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBSfK8MtR62mQ6oN
23
+ yoX/VKJzFjLSVDAdBgNVHREEFjAUgRJydWJ5QGJpdGNldGVyYS5jb20wHQYDVR0S
24
+ BBYwFIEScnVieUBiaXRjZXRlcmEuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQAYG2na
25
+ ye8OE2DANQIFM/xDos/E4DaPWCJjX5xvFKNKHMCeQYPeZvLICCwyw2paE7Otwk6p
26
+ uvbg2Ks5ykXsbk5i6vxDoeeOLvmxCqI6m+tHb8v7VZtmwRJm8so0eSX0WvTaKnIf
27
+ CAn1bVUggczVdNoBXw9WAILKyw9bvh3Ft740XZrR74sd+m2pGwjCaM8hzLvrVbGP
28
+ DyYhlBeRWyQKQ0WDIsiTSRhzK8HwSTUWjvPwx7SEdIU/HZgyrk0ETObKPakVu6bH
29
+ kAyiRqgxF4dJviwtqI7mZIomWL63+kXLgjOjMe1SHxfIPo/0ji6+r1p4KYa7o41v
30
+ fwIwU1MKlFBdsjkd
32
31
  -----END CERTIFICATE-----
33
- date: 2022-04-23 00:00:00.000000000 Z
32
+ date: 2022-11-06 00:00:00.000000000 Z
34
33
  dependencies:
35
- - !ruby/object:Gem::Dependency
36
- name: builder
37
- requirement: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - "~>"
40
- - !ruby/object:Gem::Version
41
- version: '3'
42
- type: :runtime
43
- prerelease: false
44
- version_requirements: !ruby/object:Gem::Requirement
45
- requirements:
46
- - - "~>"
47
- - !ruby/object:Gem::Version
48
- version: '3'
49
34
  - !ruby/object:Gem::Dependency
50
35
  name: nokogiri
51
36
  requirement: !ruby/object:Gem::Requirement
@@ -222,7 +207,6 @@ files:
222
207
  - exe/mkmid
223
208
  - lib/aixm.rb
224
209
  - lib/aixm/a.rb
225
- - lib/aixm/association.rb
226
210
  - lib/aixm/classes.rb
227
211
  - lib/aixm/component.rb
228
212
  - lib/aixm/component/address.rb
@@ -245,11 +229,14 @@ files:
245
229
  - lib/aixm/component/timetable.rb
246
230
  - lib/aixm/component/vasis.rb
247
231
  - lib/aixm/component/vertical_limit.rb
232
+ - lib/aixm/concerns/association.rb
248
233
  - lib/aixm/concerns/hash_equality.rb
249
234
  - lib/aixm/concerns/intensity.rb
250
235
  - lib/aixm/concerns/marking.rb
236
+ - lib/aixm/concerns/memoize.rb
251
237
  - lib/aixm/concerns/remarks.rb
252
238
  - lib/aixm/concerns/timetable.rb
239
+ - lib/aixm/concerns/xml_builder.rb
253
240
  - lib/aixm/config.rb
254
241
  - lib/aixm/constants.rb
255
242
  - lib/aixm/d.rb
@@ -260,6 +247,7 @@ files:
260
247
  - lib/aixm/feature.rb
261
248
  - lib/aixm/feature/airport.rb
262
249
  - lib/aixm/feature/airspace.rb
250
+ - lib/aixm/feature/generic.rb
263
251
  - lib/aixm/feature/navigational_aid.rb
264
252
  - lib/aixm/feature/navigational_aid/designated_point.rb
265
253
  - lib/aixm/feature/navigational_aid/dme.rb
@@ -271,13 +259,13 @@ files:
271
259
  - lib/aixm/feature/obstacle_group.rb
272
260
  - lib/aixm/feature/organisation.rb
273
261
  - lib/aixm/feature/unit.rb
274
- - lib/aixm/memoize.rb
275
262
  - lib/aixm/object.rb
276
263
  - lib/aixm/p.rb
277
264
  - lib/aixm/payload_hash.rb
278
265
  - lib/aixm/r.rb
279
266
  - lib/aixm/refinements.rb
280
267
  - lib/aixm/schedule/date.rb
268
+ - lib/aixm/schedule/date_time.rb
281
269
  - lib/aixm/schedule/day.rb
282
270
  - lib/aixm/schedule/time.rb
283
271
  - lib/aixm/shortcuts.rb
@@ -324,7 +312,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
324
312
  - !ruby/object:Gem::Version
325
313
  version: '0'
326
314
  requirements: []
327
- rubygems_version: 3.3.12
315
+ rubygems_version: 3.3.25
328
316
  signing_key:
329
317
  specification_version: 4
330
318
  summary: Builder for AIXM/OFMX aeronautical information
metadata.gz.sig CHANGED
@@ -1,3 +1,2 @@
1
- }�j�%�1dZX{I������:�bq�K�@�bb��l��heP�c M��AV.蚲�%�i:OX�+�`<���En2��ʎT���!)b��@ЅZ>1�ח�
2
- S4�z���4D�qvh��܋w��%U7�(���ZoH@�Cv�ǟ������82ʿk�E(I@��>�[&���|�x4o�{�ǍqEP���v�$��&��aI �ټ�"�iz�;c���M�X����7(�؝
3
- F}Ue�h�0
1
+ Z���%ى��Z�0�~2[}O-'B<RgWG��Rf
2
+ ���(����@B!�NB~�d,��� ~��q4Q7WwV�E`fwA�`r�(e=���P��J
@@ -1,378 +0,0 @@
1
- using AIXM::Refinements
2
-
3
- module AIXM
4
-
5
- # Associate features and components with a minimalistic implementation of
6
- # +has_many+, +has_one+ and +belongs_to+ associations.
7
- #
8
- # When adding or assigning an object on the associator (where the +has_many+
9
- # or +has_one+ declaration is made), the object is verified and must be an
10
- # instance of the declared class or a superclass thereof.
11
- #
12
- # When assigning an object on the associated (where the +belongs_to+
13
- # declaration is made), the object is not verified. However, since the actual
14
- # assignment is always delegated to the associator, unacceptable objects will
15
- # raise errors.
16
- #
17
- # @example Simple +has_many+ association
18
- # class Blog
19
- # has_many :posts # :post has to be a key in AIXM::CLASSES
20
- # end
21
- # class Post
22
- # belongs_to :blog
23
- # end
24
- # blog, post = Blog.new, Post.new
25
- # # --either--
26
- # blog.add_post(post) # => Blog
27
- # blog.posts.count # => 1
28
- # blog.posts.first == post # => true
29
- # post.blog == blog # => true
30
- # blog.remove_post(post) # => Blog
31
- # blog.posts.count # => 0
32
- # # --or--
33
- # post.blog = blog # => Blog
34
- # blog.posts.count # => 1
35
- # blog.posts.first == post # => true
36
- # post.blog == blog # => true
37
- # post.blog = nil # => nil
38
- # blog.posts.count # => 0
39
- # # --or--
40
- # post_2 = Post.new
41
- # blog.add_posts([post, post_2])
42
- # blog.posts.count # => 2
43
- # blog.posts == [post, post_2] # => true
44
- # blog.remove_posts([post_2, post])
45
- # blog.posts.count # => 0
46
- #
47
- # @example Simple +has_one+ association
48
- # class Blog
49
- # has_one :posts # :post has to be a key in AIXM::CLASSES
50
- # end
51
- # class Post
52
- # belongs_to :blog
53
- # end
54
- # blog, post = Blog.new, Post.new
55
- # # --either--
56
- # blog.post = post # => Post (standard assignment)
57
- # blog.add_post(post) # => Blog (alternative for chaining)
58
- # blog.post == post # => true
59
- # post.blog == blog # => true
60
- # blog.post = nil # => nil
61
- # blog.post # => nil
62
- # post.blog # => nil
63
- # # --or--
64
- # post.blog = blog # => Blog (standard assignment)
65
- # post.add_blog(blog) # => Post (alternative for chaining)
66
- # post.blog == blog # => true
67
- # blog.post == post # => true
68
- # post.blog = nil # => nil
69
- # post.blog # => nil
70
- # blog.post # => nil
71
- #
72
- # @example Association with readonly +belongs_to+ (idem for +has_one+)
73
- # class Blog
74
- # has_many :posts # :post has to be a key in AIXM::CLASSES
75
- # end
76
- # class Post
77
- # belongs_to :blog, readonly: true
78
- # end
79
- # blog, post = Blog.new, Post.new
80
- # post.blog = blog # => NoMethodError
81
- #
82
- # @example Association with explicit class (idem for +has_one+)
83
- # class Blog
84
- # include AIXM::Association
85
- # has_many :posts, accept: 'Picture'
86
- # end
87
- # class Picture
88
- # include AIXM::Association
89
- # belongs_to :blog
90
- # end
91
- # blog, picture = Blog.new, Picture.new
92
- # blog.add_post(picture)
93
- # blog.posts.first == picture # => true
94
- #
95
- # @example Polymorphic associator (idem for +has_one+)
96
- # class Blog
97
- # has_many :posts, as: :postable
98
- # end
99
- # class Feed
100
- # has_many :posts, as: :postable
101
- # end
102
- # class Post
103
- # belongs_to :postable
104
- # end
105
- # blog, feed, post_1, post_2, post_3 = Blog.new, Feed.new, Post.new, Post.new, Post.new
106
- # blog.add_post(post_1)
107
- # post_1.postable == blog # => true
108
- # feed.add_post(post_2)
109
- # post_2.postable == feed # => true
110
- # post_3.postable = blog # => NoMethodError
111
- #
112
- # @example Polymorphic associated (idem for +has_one+)
113
- # class Blog
114
- # include AIXM::Association
115
- # has_many :items, accept: ['Post', :picture]
116
- # end
117
- # class Post
118
- # include AIXM::Association
119
- # belongs_to :blog, as: :item
120
- # end
121
- # class Picture
122
- # include AIXM::Association
123
- # belongs_to :blog, as: :item
124
- # end
125
- # blog, post, picture = Blog.new, Post.new, Picture.new
126
- # blog.add_item(post)
127
- # blog.add_item(picture)
128
- # blog.items.count # => 2
129
- # blog.items.first == post # => true
130
- # blog.items.last == picture # => true
131
- # post.blog == blog # => true
132
- # picture.blog == blog # => true
133
- #
134
- # @example Add method which enriches passed associated object (+has_many+ only)
135
- # class Blog
136
- # has_many :posts do |post, related_to: nil| # this defines the signature of add_post
137
- # post.related_to = related_to || @posts.last # executes in the context of the current blog
138
- # end
139
- # end
140
- # class Post
141
- # belongs_to :blog
142
- # attr_accessor :related_to
143
- # end
144
- # blog, post_1, post_2, post_3 = Blog.new, Post.new, Post.new, Post.new
145
- # blog.add_post(post_1)
146
- # post_1.related_to # => nil
147
- # blog.add_post(post_2)
148
- # post_2.related_to == post_1 # => true
149
- # blog.add_post(post_3, related_to: post_1)
150
- # post_3.related_to == post_1 # => true
151
- #
152
- # @example Add method which builds and yields new associated object (+has_many+ only)
153
- # class Blog
154
- # include AIXM::Association
155
- # has_many :posts do |post, title:| end
156
- # end
157
- # class Post
158
- # include AIXM::Association
159
- # belongs_to :blog
160
- # attr_accessor :title, :text
161
- # def initialize(title:) # same signature as "has_many" block above
162
- # @title = title
163
- # end
164
- # end
165
- # blog = Blog.new
166
- # blog.add_post(title: "title") do |post| # note that no post instance is passed
167
- # post.text = "text"
168
- # end
169
- # blog.posts.first.title # => "title"
170
- # blog.posts.first.text # => "text"
171
- module Association
172
- module ClassMethods
173
- attr_reader :has_many_attributes, :has_one_attributes, :belongs_to_attributes
174
-
175
- def has_many(attribute, as: nil, accept: nil, &association_block)
176
- association = attribute.to_s.inflect(:singularize)
177
- inversion = as || self.to_s.inflect(:demodulize, :tableize, :singularize)
178
- class_names = [accept || association].flatten.map { AIXM::CLASSES[_1.to_sym] || _1 }
179
- (@has_many_attributes ||= []) << attribute
180
- # features
181
- define_method(attribute) do
182
- instance_variable_get(:"@#{attribute}") || AIXM::Association::Array.new
183
- end
184
- # add_feature
185
- define_method(:"add_#{association}") do |object=nil, **options, &add_block|
186
- unless object
187
- fail(ArgumentError, "must pass object to add") if class_names.count > 1
188
- object = class_names.first.to_class.new(**options)
189
- add_block.call(object) if add_block
190
- end
191
- instance_exec(object, **options, &association_block) if association_block
192
- fail(ArgumentError, "#{object.__class__} not allowed") unless class_names.any? { |c| object.is_a?(c.to_class) }
193
- instance_eval("@#{attribute} ||= AIXM::Association::Array.new")
194
- send(attribute).send(:push, object)
195
- object.instance_variable_set(:"@#{inversion}", self)
196
- self
197
- end
198
- # add_features
199
- define_method(:"add_#{attribute}") do |objects=[], **options, &add_block|
200
- objects.each { send(:"add_#{association}", _1, **options, &add_block) }
201
- self
202
- end
203
- # remove_feature
204
- define_method(:"remove_#{association}") do |object|
205
- send(attribute).send(:delete, object)
206
- object.instance_variable_set(:"@#{inversion}", nil)
207
- self
208
- end
209
- # remove_features
210
- define_method(:"remove_#{attribute}") do |objects=[]|
211
- objects.each { send(:"remove_#{association}", _1) }
212
- self
213
- end
214
- end
215
-
216
- def has_one(attribute, as: nil, accept: nil, allow_nil: false)
217
- association = attribute.to_s
218
- inversion = (as || self.to_s.inflect(:demodulize, :tableize, :singularize)).to_s
219
- class_names = [accept || association].flatten.map { AIXM::CLASSES[_1.to_sym] || _1 }
220
- class_names << 'NilClass' if allow_nil
221
- (@has_one_attributes ||= []) << attribute
222
- # feature
223
- attr_reader attribute
224
- # feature=
225
- define_method(:"#{association}=") do |object|
226
- fail(ArgumentError, "#{object.__class__} not allowed") unless class_names.any? { |c| object.is_a?(c.to_class) }
227
- instance_variable_get(:"@#{attribute}")&.instance_variable_set(:"@#{inversion}", nil)
228
- instance_variable_set(:"@#{attribute}", object)
229
- object&.instance_variable_set(:"@#{inversion}", self)
230
- object
231
- end
232
- # add_feature
233
- define_method(:"add_#{association}") do |object|
234
- send("#{association}=", object)
235
- self
236
- end
237
- # remove_feature
238
- define_method(:"remove_#{association}") do |_|
239
- send(:"#{association}=", nil)
240
- self
241
- end
242
- end
243
-
244
- def belongs_to(attribute, as: nil, readonly: false)
245
- association = self.to_s.inflect(:demodulize, :tableize, :singularize)
246
- inversion = (as || association).to_s
247
- (@belongs_to_attributes ||= []) << attribute
248
- # feature
249
- attr_reader attribute
250
- unless readonly
251
- # feature=
252
- define_method(:"#{attribute}=") do |object|
253
- instance_variable_get(:"@#{attribute}")&.send(:"remove_#{inversion}", self)
254
- object&.send(:"add_#{inversion}", self)
255
- object
256
- end
257
- # add_feature
258
- define_method(:"add_#{attribute}") do |object|
259
- send("#{attribute}=", object)
260
- self
261
- end
262
- end
263
- end
264
- end
265
-
266
- def self.included(base)
267
- base.extend(ClassMethods)
268
- end
269
-
270
- class Array < ::Array
271
- private :<<, :push, :append, :unshift, :prepend
272
- private :delete, :pop, :shift
273
-
274
- # Find objects of the given class and optionally with the given
275
- # attribute values on a has_many association.
276
- #
277
- # The class can either be declared by passing the class itself or by
278
- # passing a shortcut symbol as listed in +AIXM::CLASSES+.
279
- #
280
- # @example
281
- # class Blog
282
- # include AIXM::Association
283
- # has_many :items, accept: %i(post picture)
284
- # end
285
- # class Post
286
- # include AIXM::Association
287
- # belongs_to :blog, as: :item
288
- # attr_accessor :title
289
- # end
290
- # class Picture
291
- # include AIXM::Association
292
- # belongs_to :blog, as: :item
293
- # end
294
- # blog, post, picture = Blog.new, Post.new, Picture.new
295
- # post.title = "title"
296
- # blog.add_item(post)
297
- # blog.add_item(picture)
298
- # blog.items.find_by(:post) == [post] # => true
299
- # blog.items.find_by(Post) == [post] # => true
300
- # blog.items.find_by(:post, title: "title") == [post] # => true
301
- # blog.items.find_by(Object) == [post, picture] # => true
302
- #
303
- # @param klass [Class, Symbol] class (e.g. AIXM::Feature::Airport,
304
- # AIXM::Feature::NavigationalAid::VOR) or shortcut symbol (e.g.
305
- # :airport or :vor) as listed in AIXM::CLASSES
306
- # @param attributes [Hash] search attributes by their values
307
- # @return [AIXM::Association::Array]
308
- def find_by(klass, attributes={})
309
- if klass.is_a? Symbol
310
- klass = AIXM::CLASSES[klass]&.to_class || fail(ArgumentError, "unknown class shortcut `#{klass}'")
311
- end
312
- self.class.new(
313
- select do |element|
314
- if element.kind_of? klass
315
- attributes.all? { |a, v| element.send(a) == v }
316
- end
317
- end
318
- )
319
- end
320
-
321
- # Find equal objects on a has_many association.
322
- #
323
- # This may seem redundant at first, but keep in mind that two instances
324
- # of +AIXM::CLASSES+ which implement `#to_uid` are considered equal if
325
- # they are instances of the same class and both their UIDs as calculated
326
- # by `#to_uid` are equal. Attributes which are not part of the `#to_uid`
327
- # calculation are irrelevant!
328
- #
329
- # @example
330
- # class Blog
331
- # include AIXM::Association
332
- # has_many :items, accept: %i(post picture)
333
- # end
334
- # class Post
335
- # include AIXM::Association
336
- # belongs_to :blog, as: :item
337
- # attr_accessor :title
338
- # end
339
- # blog, post = Blog.new, Post.new
340
- # blog.add_item(post)
341
- # blog.items.find(post) == [post] # => true
342
- #
343
- # @param object [Object] instance of class listed in AIXM::CLASSES
344
- # @return [AIXM::Association::Array]
345
- def find(object)
346
- klass = object.__class__
347
- self.class.new(
348
- select do |element|
349
- element.kind_of?(klass) && element == object
350
- end
351
- )
352
- end
353
-
354
- # Find equal or identical duplicates on a has_many association.
355
- #
356
- # @example
357
- # class Blog
358
- # include AIXM::Association
359
- # has_many :posts
360
- # end
361
- # class Post
362
- # include AIXM::Association
363
- # belongs_to :blog
364
- # end
365
- # blog, post = Blog.new, Post.new
366
- # duplicate_post = post.dup
367
- # blog.add_posts([post, duplicate_post])
368
- # blog.posts.duplicates # => [[post, duplicate_post]]
369
- #
370
- # @return [Array<Array<AIXM::Feature>>]
371
- def duplicates
372
- AIXM::Memoize.method :to_uid do
373
- group_by(&:to_uid).select { |_, a| a.count > 1 }.map(&:last)
374
- end
375
- end
376
- end
377
- end
378
- end