aipp 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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