aipp 0.2.1 → 0.2.2

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +1 -2
  4. data/CHANGELOG.md +15 -0
  5. data/README.md +122 -37
  6. data/TODO.md +4 -0
  7. data/aipp.gemspec +8 -3
  8. data/lib/aipp.rb +14 -2
  9. data/lib/aipp/aip.rb +44 -29
  10. data/lib/aipp/downloader.rb +115 -0
  11. data/lib/aipp/executable.rb +6 -6
  12. data/lib/aipp/parser.rb +23 -23
  13. data/lib/aipp/patcher.rb +47 -0
  14. data/lib/aipp/pdf.rb +123 -0
  15. data/lib/aipp/regions/LF/AD-1.3.rb +162 -0
  16. data/lib/aipp/regions/LF/AD-1.3.yml +511 -0
  17. data/lib/aipp/regions/LF/AD-1.6.rb +31 -0
  18. data/lib/aipp/regions/LF/AD-2.rb +316 -0
  19. data/lib/aipp/regions/LF/AD-2.yml +185 -0
  20. data/lib/aipp/regions/LF/AD-3.1.rb-NEW +11 -0
  21. data/lib/aipp/regions/LF/ENR-2.1.rb +25 -24
  22. data/lib/aipp/regions/LF/ENR-4.1.rb +24 -23
  23. data/lib/aipp/regions/LF/ENR-4.3.rb +8 -6
  24. data/lib/aipp/regions/LF/ENR-5.1.rb +32 -22
  25. data/lib/aipp/regions/LF/ENR-5.5.rb-NEW +11 -0
  26. data/lib/aipp/regions/LF/helpers/AD_radio.rb +90 -0
  27. data/lib/aipp/regions/LF/helpers/URL.rb +26 -0
  28. data/lib/aipp/regions/LF/helpers/common.rb +186 -0
  29. data/lib/aipp/version.rb +1 -1
  30. data/lib/core_ext/enumerable.rb +52 -0
  31. data/lib/core_ext/nil_class.rb +10 -0
  32. data/lib/core_ext/object.rb +42 -0
  33. data/lib/core_ext/string.rb +105 -0
  34. data/spec/fixtures/archive.zip +0 -0
  35. data/spec/fixtures/document.pdf +0 -0
  36. data/spec/fixtures/document.pdf.json +1 -0
  37. data/spec/fixtures/new.html +6 -0
  38. data/spec/fixtures/new.pdf +0 -0
  39. data/spec/fixtures/new.txt +1 -0
  40. data/spec/lib/aipp/downloader_spec.rb +81 -0
  41. data/spec/lib/aipp/patcher_spec.rb +46 -0
  42. data/spec/lib/aipp/pdf_spec.rb +124 -0
  43. data/spec/lib/core_ext/enumberable_spec.rb +76 -0
  44. data/spec/lib/core_ext/nil_class_spec.rb +11 -0
  45. data/spec/lib/core_ext/string_spec.rb +88 -0
  46. data/spec/spec_helper.rb +1 -0
  47. metadata +123 -23
  48. data/lib/aipp/progress.rb +0 -40
  49. data/lib/aipp/refinements.rb +0 -114
  50. data/lib/aipp/regions/LF/helper.rb +0 -177
  51. data/spec/lib/aipp/refinements_spec.rb +0 -123
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 49d074cd575fab6f3620f201d538c6a2d8accf1d375438b7cd4fd2e2d77aca56
4
- data.tar.gz: 1e1ca6f26498e1033385c7b2dc8aeb558c19b28300df0a5521699956661dd026
3
+ metadata.gz: 8728d9b2f455af64005813f575779ce93a2bf19167afb0574ce603ef0ed3582f
4
+ data.tar.gz: 6d901cc39dee66f917e56508520bf78383863375f90a72264909b60996c7d57d
5
5
  SHA512:
6
- metadata.gz: 1212764315987bacfac6a5429742ef3e9faeff0249cb3bbb4e44554d8a74ef59407e66cf61c3c02475a0a9f316b445449c6adaff3f980a385ab829fbf6472a46
7
- data.tar.gz: 503d02d65c4ce0e161e0aebafada72110095104a90910131c87d33e8a91196e9c98289db9b5e0e2b7013a1ed664cd5cb550b01f5ddf4ca074e1cee706c3ba782
6
+ metadata.gz: 908575b22b787ca1743333146b113b89aea0a1e8919f0ae50abc2bf306bc11fcaec8a12780a56f3591eb96bb7790924a7959d15f0b4d06f68847e527f14782ec
7
+ data.tar.gz: 12a04dab59f3d229624938c21b64f1c05f15352ed1f5617c5fb05745b0230ef2b80a1fd54024d65375e452fdf8d79d7320ff5fa1d396f03ef1764c4ce4a91b63
@@ -1 +1 @@
1
- ruby-2.5.3
1
+ ruby-2.6.3
@@ -1,5 +1,4 @@
1
1
  ---
2
2
  language: ruby
3
3
  rvm:
4
- - 2.5.0
5
- before_install: gem install bundler -v 1.16.1
4
+ - 2.6.0
@@ -1,8 +1,22 @@
1
+ ## 0.2.2
2
+
3
+ #### Changes
4
+ * Helper modules instead of one monolythic `helper.rb`
5
+
6
+ #### Additions
7
+ * LF/AD-1.3
8
+ * LF/AD-1.6
9
+ * LF/AD-2
10
+
1
11
  ## 0.2.1
2
12
 
3
13
  #### Changes
14
+ * Require Ruby 2.6
4
15
  * Fix broken downloader
5
16
 
17
+ #### Additions
18
+ * Support for PDF files
19
+
6
20
  ## 0.2.0
7
21
 
8
22
  #### Changes
@@ -38,5 +52,6 @@
38
52
  ## 0.1.0
39
53
 
40
54
  #### Initial Implementation
55
+ * Require Ruby 2.5
41
56
  * Framework and aip2aixm executable
42
57
  * LF/ENR-5.1
data/README.md CHANGED
@@ -8,9 +8,10 @@
8
8
 
9
9
  Parser for Aeronautical Information Publication (AIP) available online.
10
10
 
11
- This gem incluces two executables to download and parse aeronautical data, then export is as [AIXM](https://github.com/svoop/aixm) or [OFMX](https://github.com/openflightmaps/ofmx/wiki).
11
+ This gem incluces two executables to download and parse aeronautical data as HTML or PDF, then export is as [AIXM](https://github.com/svoop/aixm) or [OFMX](https://github.com/openflightmaps/ofmx/wiki).
12
12
 
13
13
  * [Homepage](https://github.com/svoop/aipp)
14
+ * [Rubydoc](https://www.rubydoc.info/gems/aipp/AIPP)
14
15
  * Author: [Sven Schwyn - Bitcetera](http://www.bitcetera.com)
15
16
 
16
17
  ## Install
@@ -36,25 +37,7 @@ AIPP uses a storage directory for configuration, caching and in order to keep th
36
37
 
37
38
  The reference implementation is region "LF" (France).
38
39
 
39
- To implement a region, you have to create a new directory <tt>lib/aipp/regions/{REGION}</tt> and place the following files there:
40
-
41
- ### helper.rb
42
-
43
- Create the file <tt>helper.rb</tt> which defines the module `AIPP::LF::Helper` and usually contains the URL builder method `url_for` used by all AIP parsers:
44
-
45
- ```ruby
46
- module AIPP
47
- module LF
48
- module Helper
49
-
50
- def url_for(aip_file)
51
- # build and return the download URL for the aip file
52
- end
53
-
54
- end
55
- end
56
- end
57
- ```
40
+ To implement a region, you have to create a new directory <tt>lib/aipp/regions/{REGION}/</tt> and place the following files there:
58
41
 
59
42
  ### AIP Parsers
60
43
 
@@ -68,8 +51,9 @@ module AIPP
68
51
  DEPENDS = %w(ENR-2.1 ENR-2.2) # declare dependencies to other AIPs
69
52
 
70
53
  def parse
71
- html = load_html
72
- # read from "html" (Nokogiri::HTML::Document) and write to "aixm"
54
+ html = read # read the Nokogiri::HTML5 document
55
+ feature = (...) # build the feature
56
+ write(feature: feature) # write the feature to AIXM::Document
73
57
  end
74
58
 
75
59
  end
@@ -86,8 +70,9 @@ module AIPP
86
70
 
87
71
  def parse
88
72
  %i(one two three).each do |part|
89
- html = load_html(aip_file: "#{aip}.#{part}")
90
- # read from "html" (Nokogiri::HTML::Document) and write to "aixm"
73
+ html = read(aip_file: "#{aip}.#{part}") # read with a non-standard name
74
+ support_html = read(aip_file: 'AD-0.6') # maybe read necessary support documents
75
+ (...)
91
76
  end
92
77
  end
93
78
 
@@ -96,14 +81,99 @@ module AIPP
96
81
  end
97
82
  ```
98
83
 
99
- Inside the `parse` method, you have access to the following objects:
84
+ Inside the `parse` method, you have access to the following methods:
85
+
86
+ * [`read`](https://www.rubydoc.info/gems/aipp/AIPP/AIP#read-instance_method) – download and read an AIP file
87
+ * [`write`](https://www.rubydoc.info/gems/aipp/AIPP/AIP#write-instance_method) – write a [`AIXM::Feature`]([AIXM Rubygem](https://github.com/svoop/aixm)
88
+ * [`select`](https://www.rubydoc.info/gems/aipp/AIPP/AIP#find-instance_method – search previously written [`AIXM::Feature`s]([AIXM Rubygem](https://github.com/svoop/aixm)
89
+ * some core extensions from ActiveSupport – [`Object#blank`](https://www.rubydoc.info/gems/activesupport/Object#blank%3F-instance_method) and [`String`](https://www.rubydoc.info/gems/activesupport/String)
90
+ * core extensions from this gem – [`Object`](https://www.rubydoc.info/gems/aipp/Object), [`String`](https://www.rubydoc.info/gems/aipp/String), [`NilClass`](https://www.rubydoc.info/gems/aipp/NilClass) and [`Enumerable`](https://www.rubydoc.info/gems/aipp/Enumerable)
91
+
92
+ As well as the following objects:
100
93
 
101
- * `aixm` – target: instance of `AIXM::Document` (see [AIXM Rubygem](https://github.com/svoop/aixm))
102
94
  * `options` – arguments read from <tt>aip2aixm</tt> or <tt>aip2ofmx</tt> respectively
103
95
  * `config` – configuration read from <tt>config.yml</tt>
104
96
  * `cache` – virgin `OStruct` instance to make objects available across AIPs
105
97
 
106
- Furthermore, you have access to any method defined in <tt>helper.rb</tt> and you can overwrite any of them if need be (most notably `url_for`).
98
+ ### Helpers
99
+
100
+ Helpers are modules defined in the <tt>lib/aipp/regions/{REGION}/helpers/</tt> directory. All helper modules are required automatically.
101
+
102
+ There is one mandatory helper called `URL.rb` which must define the following method to build URLs from which to download AIPs:
103
+
104
+ ```ruby
105
+ module AIPP
106
+ module LF
107
+ module Helpers
108
+ module URL
109
+
110
+ def url_for(aip_file)
111
+ # build and return the download URL for the aip file
112
+ end
113
+
114
+ end
115
+ end
116
+ end
117
+ end
118
+ ```
119
+
120
+ Feel free to add more helpers to DRY code which is shared by multiple AIP parsers. Say you want to extract methods which are used by all AIP parsers:
121
+
122
+ ```ruby
123
+ module AIPP
124
+ module LF
125
+ module Helpers
126
+ module Common
127
+
128
+ def source(position:, aip_file: nil)
129
+ (...)
130
+ end
131
+
132
+ end
133
+ end
134
+ end
135
+ end
136
+ ```
137
+
138
+ To use this `source` method, simply include the helper module in the AIP parser:
139
+
140
+ ```ruby
141
+ module AIPP
142
+ module LF
143
+ class AD2 < AIP
144
+
145
+ include AIPP::LF::Helpers::Common
146
+
147
+ end
148
+ end
149
+ end
150
+ ```
151
+
152
+ ### Patches
153
+
154
+ When parsed data is faulty or missing, you might have to use a different data source instead such as static data from a fixture file. This is where patches come in. You can patch any AIXM attribute setter by defining a patch block inside the AIP parser:
155
+
156
+ ```ruby
157
+ module AIPP
158
+ module LF
159
+ class AD2 < AIP
160
+
161
+ patch AIXM::Component::Runway::Direction, :xy do |parser, object, value|
162
+ throw :abort unless value.nil?
163
+ @fixtures ||= YAML.load_file(Pathname(__FILE__).dirname.join('AD-1.3.yml'))
164
+ airport_id = parser.instance_variable_get(:@airport).id
165
+ direction_name = object.name.to_s
166
+ throw :abort if (xy = @fixtures.dig('runways', airport_id, direction_name, 'xy')).nil?
167
+ lat, long = xy.split(/\s+/)
168
+ AIXM.xy(lat: lat, long: long)
169
+ end
170
+
171
+ end
172
+ end
173
+ end
174
+ ```
175
+
176
+ The patch block receives the object and the current value. If this value is okay, `throw :abort` to leave the patch block without touching anything. Otherwise, have the patch block return a new value which will be used instead.
107
177
 
108
178
  ### Source File Line Numbers
109
179
 
@@ -113,11 +183,11 @@ In order to reference the source of an AIXM/OFMX feature, it's necessary to know
113
183
  tr.line
114
184
  ```
115
185
 
116
- :warning: Make sure you have build Nokogumbo `--with-libxml2`. Otherwise, all elements will report line number `0` and therefore render OFMX documents invalid. See the [Nokogumbo README](https://github.com/rubys/nokogumbo/blob/master/README.md#flavors-of-nokogumbo) for more on this.
186
+ ⚠️ Make sure you have build Nokogumbo `--with-libxml2`. Otherwise, all elements will report line number `0` and therefore render OFMX documents invalid. See the [Nokogumbo README](https://github.com/rubys/nokogumbo/blob/master/README.md#flavors-of-nokogumbo) for more on this.
117
187
 
118
188
  ### Errors
119
189
 
120
- You should `fail` on fatal problems. The `-E` command line argument will open a Pry session when such an error occurs. Issue errors as usual:
190
+ You should `fail` on fatal problems. The `-e` command line argument will open a Pry session when such an error occurs. Issue errors as usual:
121
191
 
122
192
  ```ruby
123
193
  fail "my message"
@@ -125,23 +195,38 @@ fail "my message"
125
195
 
126
196
  ### Warnings
127
197
 
128
- You should `warn` on non-fatal problems. The `-W ID` command line argument will open a Pry session when then warning with the given ID occurs. To issue a warning:
198
+ You should `warn` on non-fatal problems. The `-w ID` command line argument will open a Pry session when then warning with the given ID occurs. To issue a warning:
129
199
 
130
200
  ```ruby
131
- warn("my message", context: binding) # open Pry with binding context
132
- warn("my message", context: error) # open Pry with error context
201
+ warn("my message", pry: binding) # open Pry attached to the binding
202
+ warn("my message", pry: error) # open Pry attached to the error
133
203
  ```
134
204
 
135
- ### Informational Messages
205
+ ### Messages
136
206
 
137
- You may `info` any other useful information:
207
+ #### info
208
+
209
+ Use `info` for essential info messages:
138
210
 
139
211
  ```ruby
140
- info("my message") # show info only in verbose mode (-V)
141
- info("my message", force: true) # always show info
142
- info("my message", color: :green) # show info with this color
212
+ info("my message") # displays "my message" in black
213
+ info("my message", color: :green) # displays "my message" in green
143
214
  ```
144
215
 
216
+ #### debug
217
+
218
+ Use `debug` for in-depth info messages which are only shown if the `--verbose` mode is set:
219
+
220
+ ```ruby
221
+ debug("my message") # displays "my message" in blue
222
+ ```
223
+
224
+ ### Pry
225
+
226
+ Pry is loaded with stack explorer support. Type `help` in the Pry console to see all available commands. The most useful command in the context of this gem is `up` which beams you one frame up in the caller stack.
227
+
228
+ Note: It's not currently possible to use pry-byebug at this time since it [interferes with pry-rescue](https://github.com/ConradIrwin/pry-rescue/issues/71).
229
+
145
230
  ## AIRAC Date Calculations
146
231
 
147
232
  The `AIPP::AIRAC` class is used to calculate AIRAC cycles:
data/TODO.md ADDED
@@ -0,0 +1,4 @@
1
+ ## LF
2
+
3
+ ### AD-2
4
+ * Landing aids (LOC/GP/DME) as of AD-2 section 2.19
@@ -19,21 +19,26 @@ Gem::Specification.new do |spec|
19
19
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
20
  spec.require_paths = ['lib']
21
21
 
22
- spec.required_ruby_version = '>= 2.5'
22
+ spec.required_ruby_version = '>= 2.6'
23
23
 
24
- spec.add_development_dependency 'bundler'
25
24
  spec.add_development_dependency 'rake'
26
25
  spec.add_development_dependency 'minitest'
27
26
  spec.add_development_dependency 'minitest-reporters'
28
27
  spec.add_development_dependency 'minitest-sound'
29
28
  spec.add_development_dependency 'minitest-matchers'
29
+ spec.add_development_dependency 'spy'
30
30
  spec.add_development_dependency 'guard'
31
31
  spec.add_development_dependency 'guard-minitest'
32
32
 
33
- spec.add_runtime_dependency 'aixm', '~> 0', '>= 0.3.2'
33
+ spec.add_runtime_dependency 'aixm', '>= 0.3.4'
34
+ spec.add_runtime_dependency 'activesupport', '~> 5'
34
35
  spec.add_runtime_dependency 'nokogiri', '~> 1'
35
36
  spec.add_runtime_dependency 'nokogumbo', '~> 2'
37
+ spec.add_runtime_dependency 'pdf-reader', '~> 2'
38
+ spec.add_runtime_dependency 'json', '~> 2'
39
+ spec.add_runtime_dependency 'rubyzip', '~> 1'
36
40
  spec.add_runtime_dependency 'colorize', '~> 0'
37
41
  spec.add_runtime_dependency 'pry', '~> 0'
38
42
  spec.add_runtime_dependency 'pry-rescue', '~> 1'
43
+ spec.add_runtime_dependency 'pry-stack_explorer', '~> 0'
39
44
  end
@@ -12,16 +12,28 @@ require 'ostruct'
12
12
  require 'date'
13
13
  require 'nokogiri'
14
14
  require 'nokogumbo'
15
+ require 'pdf-reader'
16
+ require 'json'
17
+ require 'zip'
15
18
  require 'aixm'
16
19
 
20
+ require 'active_support'
21
+ require 'active_support/core_ext/object/blank'
22
+ require 'active_support/core_ext/string'
23
+ require_relative 'core_ext/object'
24
+ require_relative 'core_ext/string'
25
+ require_relative 'core_ext/nil_class'
26
+ require_relative 'core_ext/enumerable'
27
+
17
28
  require_relative 'aipp/version'
18
- require_relative 'aipp/refinements'
29
+ require_relative 'aipp/pdf'
19
30
  require_relative 'aipp/t_hash'
20
31
  require_relative 'aipp/executable'
21
- require_relative 'aipp/progress'
22
32
  require_relative 'aipp/airac'
33
+ require_relative 'aipp/patcher'
23
34
  require_relative 'aipp/aip'
24
35
  require_relative 'aipp/parser'
36
+ require_relative 'aipp/downloader'
25
37
 
26
38
  # Disable "did you mean?" suggestions
27
39
  module DidYouMean::Correctable
@@ -2,62 +2,77 @@ module AIPP
2
2
 
3
3
  # @abstract
4
4
  class AIP
5
- using AIPP::Refinements
6
- include AIPP::Progress
7
5
  extend Forwardable
6
+ include AIPP::Patcher
8
7
 
9
8
  DEPENDS = []
10
9
 
11
10
  # @return [String] AIP name (e.g. "ENR-2.1")
12
11
  attr_reader :aip
13
12
 
14
- # @return [AIXM::Document] target document
15
- attr_reader :aixm
13
+ # @!method close
14
+ # @see AIPP::Downloader#close
15
+ def_delegator :@downloader, :close
16
16
 
17
- # @!method aixm
18
- # @return (see AIPP::Parser#aixm)
19
17
  # @!method config
20
- # @return (see AIPP::Parser#config)
18
+ # @see AIPP::Parser#config
21
19
  # @!method options
22
- # @return (see AIPP::Parser#options)
20
+ # @see AIPP::Parser#options
23
21
  # @!method cache
24
- # @return (see AIPP::Parser#cache)
22
+ # @see AIPP::Parser#cache
25
23
  def_delegators :@parser, :aixm, :config, :options, :cache
24
+ private :aixm
26
25
 
27
- def initialize(aip:, parser:)
28
- @aip, @parser = aip, parser
29
- self.class.include [:AIPP, options[:region], :Helper].constantize
26
+ def initialize(aip:, downloader:, parser:)
27
+ @aip, @downloader, @parser = aip, downloader, parser
28
+ self.class.include ("AIPP::%s::Helpers::URL" % options[:region]).constantize
30
29
  end
31
30
 
32
- # Load an AIP source file
31
+ # Read an AIP source file
33
32
  #
34
33
  # Depending on whether a local copy of the file exists, either:
35
- # * download from URL to local storage and read from local storage
36
- # * read from local storage
34
+ # * download from URL to local storage and read from local archive
35
+ # * read from local archive
37
36
  #
38
37
  # An URL builder method +url_for(aip_file)+ must be defined either in
39
38
  # +helper.rb+ or in the AIP parser definition (e.g. +ENR-2.1.rb+).
40
39
  #
41
40
  # @param aip_file [String] e.g. "ENR-2.1" or "AD-2.LFMV" (default: +aip+)
42
- # @return [Nokogiri::HTML5] HTML document
43
- def load_html(aip_file: nil)
41
+ # @return [Nokogiri::HTML5::Document, String] HTML as Nokogiri document,
42
+ # PDF or TXT as String
43
+ def read(aip_file=nil)
44
44
  aip_file ||= aip
45
- unless (aip_path = storage_path(aip_file)).exist?
46
- info("Downloading #{aip_file}", force: true)
47
- storage_path.mkpath
48
- IO.copy_stream(open(url_for(aip_file)), aip_path)
49
- end
50
- Nokogiri::HTML5(aip_path)
45
+ @downloader.read(document: aip_file, url: url_for(aip_file))
51
46
  end
52
47
 
53
- private
54
-
55
- def storage_path(aip_file=nil)
56
- options[:storage].
57
- join(options[:airac].date.xmlschema).
58
- join(("#{aip_file}.html" if aip_file).to_s)
48
+ # Write feature to AIXM
49
+ #
50
+ # @param feature [AIXM::Feature] e.g. airport or airspace
51
+ def write(feature)
52
+ aixm.features << feature
59
53
  end
60
54
 
55
+ # Search features previously written to AIXM and return those matching the
56
+ # given class and attribute values
57
+ #
58
+ # @example
59
+ # select(:airport, id: "LFNT")
60
+ #
61
+ # @param klass [Class, Symbol] feature class like AIXM::Feature::Airport or
62
+ # AIXM::Feature::NavigationalAid::VOR, shorthand notations as symbols
63
+ # e.g. :airport or :vor as listed in AIXM::CLASSES are recognized as well
64
+ # @param attributes [Hash] filter by these attributes and their values
65
+ # @return [Array<AIXM::Feature>]
66
+ def select(klass, attributes={})
67
+ klass = AIXM::CLASSES.fetch(klass) if klass.is_a? Symbol
68
+ aixm.features.select do |feature|
69
+ if feature.is_a? klass
70
+ attributes.reduce(true) do |memo, (attribute, value)|
71
+ memo && feature.send(attribute) == value
72
+ end
73
+ end
74
+ end
75
+ end
61
76
  end
62
77
 
63
78
  end