worlddb-models 2.3.1 → 2.3.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.
- checksums.yaml +4 -4
- data/Manifest.txt +2 -2
- data/Rakefile +1 -1
- data/lib/worlddb/models.rb +1 -1
- data/lib/worlddb/models/city.rb +0 -10
- data/lib/worlddb/models/city_base.rb +18 -4
- data/lib/worlddb/models/country.rb +1 -1
- data/lib/worlddb/models/name.rb +0 -67
- data/lib/worlddb/models/state_base.rb +8 -13
- data/lib/worlddb/reports/country_report.rb +63 -0
- data/lib/worlddb/schema.rb +1 -16
- data/lib/worlddb/version.rb +1 -1
- data/test/test_report_country.rb +34 -0
- data/test/test_state_tree_reader_at.rb +2 -2
- metadata +7 -7
- data/lib/worlddb/tree_reader.rb +0 -97
- data/test/test_tree_reader.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af7ca070ea7895fb6595ea3c09d9081945bb9810
|
4
|
+
data.tar.gz: eabe0f34bb806d94ea6a24fe69fcf8656b9aad22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31b2084fb1d59c8e663d36e25be45aa03111f51af310923369b64336f3d628436686a7c39fa8044dd39cb98eb1830fce55fafa41362a55499ebcc4b300e23675
|
7
|
+
data.tar.gz: e2651f5001624057d10444c333c4d4a497ec4044f02a8cf2ad67183a19a974b866ffbe4e50f28030296a9fbed841d75620c1e3ff9df9771cde814f5c4415ebdc
|
data/Manifest.txt
CHANGED
@@ -32,9 +32,9 @@ lib/worlddb/readers/lang.rb
|
|
32
32
|
lib/worlddb/readers/state.rb
|
33
33
|
lib/worlddb/readers/state_tree.rb
|
34
34
|
lib/worlddb/readers/usage.rb
|
35
|
+
lib/worlddb/reports/country_report.rb
|
35
36
|
lib/worlddb/schema.rb
|
36
37
|
lib/worlddb/stats.rb
|
37
|
-
lib/worlddb/tree_reader.rb
|
38
38
|
lib/worlddb/version.rb
|
39
39
|
test/adm/test_fixture_matcher_adm2.rb
|
40
40
|
test/adm/test_fixture_matcher_adm3.rb
|
@@ -71,6 +71,6 @@ test/test_name_parser.rb
|
|
71
71
|
test/test_parse_city.rb
|
72
72
|
test/test_parse_country.rb
|
73
73
|
test/test_parse_state.rb
|
74
|
+
test/test_report_country.rb
|
74
75
|
test/test_state_tree_reader_at.rb
|
75
76
|
test/test_state_tree_reader_de.rb
|
76
|
-
test/test_tree_reader.rb
|
data/Rakefile
CHANGED
@@ -17,7 +17,7 @@ Hoe.spec 'worlddb-models' do
|
|
17
17
|
self.extra_deps = [
|
18
18
|
['props', '>= 1.1.2'], # settings / prop(ertie)s / env / INI
|
19
19
|
['logutils', '>= 0.6.1'], # logging
|
20
|
-
['textutils', '>= 1.
|
20
|
+
['textutils', '>= 1.3.0'],
|
21
21
|
|
22
22
|
['tagutils', '>= 0.3.0'], # tags n categories for activerecord
|
23
23
|
['activerecord-utils', '>= 0.2.0'],
|
data/lib/worlddb/models.rb
CHANGED
data/lib/worlddb/models/city.rb
CHANGED
@@ -19,16 +19,6 @@ class City < CityBase
|
|
19
19
|
'CITY'
|
20
20
|
end
|
21
21
|
|
22
|
-
def self.create_or_update_from_titles( titles, more_attribs = {} )
|
23
|
-
# ary of titles e.g. ['Wien', 'Graz'] etc.
|
24
|
-
|
25
|
-
titles.each do |title|
|
26
|
-
values = [title]
|
27
|
-
City.create_or_update_from_values( values, more_attribs )
|
28
|
-
end # each city
|
29
|
-
end # method create_or_update_from_titles
|
30
|
-
|
31
|
-
|
32
22
|
end # class City
|
33
23
|
|
34
24
|
|
@@ -71,13 +71,27 @@ class CityBase < ActiveRecord::Base
|
|
71
71
|
end
|
72
72
|
|
73
73
|
|
74
|
-
|
75
74
|
def self.parse( *args )
|
76
75
|
## remove (extract) attribs hash (if last arg is a hash n present)
|
77
76
|
more_attribs = args.last.is_a?(Hash) ? args.pop : {} ## extract_options!
|
78
|
-
|
79
|
-
|
80
|
-
|
77
|
+
|
78
|
+
## check if array passed in for batch (multi record) parse
|
79
|
+
## - fix/todo: add to all other parse model methods too!!!! it's a standard (feature)
|
80
|
+
if args.size == 1 && args[0].is_a?(Array)
|
81
|
+
## e.g. City.parse( [ 'Vienna', 'Salzburg' ] )
|
82
|
+
## note: works for now only w/ *single* name/title records
|
83
|
+
## add support for array in array too - why? why not?
|
84
|
+
## e.g. City.parse( [['Vienna', 'VIE'],
|
85
|
+
## ['Salzbrug', 'SZB']] )
|
86
|
+
ary = args[0]
|
87
|
+
## note: return array of new objs
|
88
|
+
ary.map { |value| self.create_or_update_from_values( [value], more_attribs ) }
|
89
|
+
else
|
90
|
+
## standard (sinlge) record case
|
91
|
+
## e.g. City.parse( 'Vienna', 'VIE', '1 800 000' )
|
92
|
+
values = args
|
93
|
+
self.create_or_update_from_values( values, more_attribs )
|
94
|
+
end
|
81
95
|
end
|
82
96
|
|
83
97
|
|
@@ -311,7 +311,7 @@ class Country < ActiveRecord::Base
|
|
311
311
|
#################
|
312
312
|
## auto add capital cities
|
313
313
|
|
314
|
-
City.
|
314
|
+
City.parse( value_cities, country_id: rec.id )
|
315
315
|
|
316
316
|
##################
|
317
317
|
## add taggings
|
data/lib/worlddb/models/name.rb
CHANGED
@@ -1,73 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
|
4
|
-
##
|
5
|
-
# todo: move to textutils!!!
|
6
|
-
|
7
|
-
class NameParser
|
8
|
-
|
9
|
-
include LogUtils::Logging
|
10
|
-
|
11
|
-
def parse( chunks )
|
12
|
-
## todo/fix: (re)use nameparser - for now "simple" inline version
|
13
|
-
## fix!!! - note: for now lang gets ignored
|
14
|
-
## fix: add hanlde
|
15
|
-
## Leuven[nl]|Louvain[fr] Löwen[de]
|
16
|
-
## Antwerpen[nl]|Anvers[fr] [Antwerp]
|
17
|
-
## Brussel[nl]•Bruxelles[fr] -> official bi-lingual name
|
18
|
-
## etc.
|
19
|
-
|
20
|
-
## values - split into names (name n lang pairs)
|
21
|
-
## note: assumes (default) lang from more_attribs unless otherwise marked e.g. [] assume en etc.
|
22
|
-
|
23
|
-
## split chunks into values
|
24
|
-
values = []
|
25
|
-
chunks.each do |chunk|
|
26
|
-
next if chunk.nil? || chunk.blank? ## skip nil or empty/blank chunks
|
27
|
-
|
28
|
-
parts = chunk.split( '|' ) # 1) split |
|
29
|
-
|
30
|
-
parts.each do |part|
|
31
|
-
s = StringScanner.new( part )
|
32
|
-
s.skip( /[ \t]+/) # skip whitespaces
|
33
|
-
|
34
|
-
while s.eos? == false
|
35
|
-
if s.check( /\[/ )
|
36
|
-
## scan everything until the end of bracket (e.g.])
|
37
|
-
## fix!!! - note: for now lang gets ignored
|
38
|
-
value = s.scan( /\[[^\]]+\]/)
|
39
|
-
value = value[1...-1] # strip enclosing [] e.g. [Bavaria] => Bavaria
|
40
|
-
else
|
41
|
-
## scan everything until the begin of bracket (e.g.[)
|
42
|
-
value = s.scan( /[^\[]+/)
|
43
|
-
value = value.strip
|
44
|
-
end
|
45
|
-
values << value
|
46
|
-
|
47
|
-
s.skip( /[ \t]+/) # skip whitespaces
|
48
|
-
logger.debug( "[NameParser] eos?: #{s.eos?}, rest: >#{s.rest}<" )
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
logger.debug( "[NameParser] values=#{values.inspect}")
|
54
|
-
|
55
|
-
names = []
|
56
|
-
values.each do |value|
|
57
|
-
name = value
|
58
|
-
## todo: split by bullet ? (official multilang name) e.g. Brussel • Bruxelles
|
59
|
-
## todo: process variants w/ () e.g. Krems (a. d. Donau) etc. ??
|
60
|
-
names << name
|
61
|
-
end
|
62
|
-
|
63
|
-
logger.debug( "[NameParser] names=#{names.inspect}")
|
64
|
-
|
65
|
-
names
|
66
|
-
end # method parse
|
67
|
-
end # class NameParser
|
68
|
-
|
69
|
-
|
70
|
-
|
71
4
|
module WorldDb
|
72
5
|
module Model
|
73
6
|
|
@@ -21,11 +21,6 @@ class StateBase < ActiveRecord::Base
|
|
21
21
|
validates :key, format: { with: /#{STATE_KEY_PATTERN}/, message: STATE_KEY_PATTERN_MESSAGE }
|
22
22
|
validates :code, format: { with: /#{STATE_CODE_PATTERN}/, message: STATE_CODE_PATTERN_MESSAGE }, allow_nil: true
|
23
23
|
|
24
|
-
### recursive self-reference - use "generic" node??
|
25
|
-
## has_many :nodes, class_name: 'State', foreign_key: 'state_id'
|
26
|
-
# belongs_to :parent, class_name: 'State', foreign_key: 'state_id'
|
27
|
-
# has_many :states, class_name: 'State', foreign_key: 'state_id' ## substates
|
28
|
-
|
29
24
|
## begin compat
|
30
25
|
def title() name; end
|
31
26
|
def title=(value) self.name = value; end
|
@@ -143,13 +138,13 @@ class StateBase < ActiveRecord::Base
|
|
143
138
|
# note: was self.find_by_key_and_country_id
|
144
139
|
if self == State
|
145
140
|
## note: state scoped by country (all others by top-level state and NOT country)
|
146
|
-
rec = self.
|
141
|
+
rec = self.find_by(
|
147
142
|
key: new_attributes[ :key ],
|
148
|
-
country_id: new_attributes[ :country_id] )
|
143
|
+
country_id: new_attributes[ :country_id] )
|
149
144
|
else
|
150
|
-
rec = self.
|
145
|
+
rec = self.find_by(
|
151
146
|
key: new_attributes[ :key ],
|
152
|
-
state_id: new_attributes[ :state_id] )
|
147
|
+
state_id: new_attributes[ :state_id] )
|
153
148
|
end
|
154
149
|
|
155
150
|
if rec.present?
|
@@ -168,12 +163,12 @@ class StateBase < ActiveRecord::Base
|
|
168
163
|
# auto add capital cities
|
169
164
|
# - note: get added to top-level state (e.g. adm1)
|
170
165
|
|
171
|
-
City.
|
172
|
-
|
173
|
-
|
166
|
+
City.parse( value_cities,
|
167
|
+
state_id: rec.state_id,
|
168
|
+
country_id: rec.country_id ) # note: will return an array of (new) city records
|
174
169
|
|
175
170
|
### todo/fix: add captial ref to country/state
|
176
|
-
## todo/fix:
|
171
|
+
## todo/fix: only allow one capital city (now it's an array)
|
177
172
|
|
178
173
|
|
179
174
|
##################
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module WorldDb
|
4
|
+
|
5
|
+
class CountryReport
|
6
|
+
|
7
|
+
include Models
|
8
|
+
|
9
|
+
def initialize( key ) # pass in country model - why, why not ??
|
10
|
+
@key = key
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def report
|
15
|
+
buf = ''
|
16
|
+
|
17
|
+
c = Country.find_by!( key: @key)
|
18
|
+
|
19
|
+
buf << "Country Report for #{c.name} (#{c.key}), "
|
20
|
+
buf << "#{c.states.count} states"
|
21
|
+
buf << "\n\n" # print newline
|
22
|
+
|
23
|
+
## loop over states
|
24
|
+
parts_count = 0
|
25
|
+
counties_count = 0
|
26
|
+
munis_count = 0
|
27
|
+
cities_count = 0
|
28
|
+
c.states.each do |state|
|
29
|
+
buf << "%-36s |" % ["#{state.name} (#{state.key})"]
|
30
|
+
buf << " %3d parts" % [state.parts.count]
|
31
|
+
buf << " %3d counties" % [state.counties.count]
|
32
|
+
buf << " %3d munis" % [state.munis.count]
|
33
|
+
buf << " %3d cities" % [state.cities.count]
|
34
|
+
buf << "\n" # print newline
|
35
|
+
|
36
|
+
parts_count += state.parts.count
|
37
|
+
counties_count += state.counties.count
|
38
|
+
munis_count += state.munis.count
|
39
|
+
cities_count += state.cities.count
|
40
|
+
|
41
|
+
state.parts.each do |part|
|
42
|
+
buf << " %-34s |" % ["#{part.name} (#{part.key})"]
|
43
|
+
buf << " %3d counties" % [part.counties.count]
|
44
|
+
## buf << " %3d munis" % [state.munis.count] -- add munis possilbe??
|
45
|
+
buf << "\n" # print newline
|
46
|
+
end # each part
|
47
|
+
end # each state
|
48
|
+
|
49
|
+
buf << "\n" # print newline
|
50
|
+
buf << "Total: "
|
51
|
+
buf << " #{parts_count} parts, "
|
52
|
+
buf << " #{counties_count} counties, "
|
53
|
+
buf << " #{munis_count} munis, "
|
54
|
+
buf << " #{cities_count} cities"
|
55
|
+
buf << "\n" # print newline
|
56
|
+
|
57
|
+
puts buf
|
58
|
+
end
|
59
|
+
|
60
|
+
end # class CountryReport
|
61
|
+
|
62
|
+
end # module WorldDb
|
63
|
+
|
data/lib/worlddb/schema.rb
CHANGED
@@ -146,16 +146,9 @@ create_table :states do |t|
|
|
146
146
|
t.string :alt_names # comma separated list of alternate names (synonyms)
|
147
147
|
|
148
148
|
t.references :country, null: false
|
149
|
-
t.references :state ## parent state (optional for now - may be null for top level e.g. state/province)
|
150
149
|
t.integer :level, null: false, default: 1 # default assumes 1 e.g. state/province/etc.
|
151
150
|
### change to l (instead of level)!!!! - shorter, why, why not???
|
152
151
|
|
153
|
-
## flags (use single int named flags - why? why not?
|
154
|
-
### fix: use a generic kind string type flag!!!!!!
|
155
|
-
## t.boolean :s, null: false, default: false # state flag (use adm1? or a1)
|
156
|
-
## t.boolean :p, null: false, default: false # governmental district falg (use adm2? or a2) - check is Oberfranken/Oberbayern admin2 in Bayern (DE) ?? - note: might be optional (than adm3 becomes adm2)
|
157
|
-
## t.boolean :c, null: false, default: false # county (or bezirk etc.) (use adm3? or a3?)
|
158
|
-
|
159
152
|
t.integer :pop # optional population count
|
160
153
|
t.integer :area # optional area in square km (sq. km)
|
161
154
|
t.timestamps
|
@@ -249,12 +242,6 @@ create_table :zones do |t|
|
|
249
242
|
# to be done
|
250
243
|
end
|
251
244
|
|
252
|
-
## create_table :city_rels do |t| ## city relationships (w/ states/admins) -- part of state/zone
|
253
|
-
## t.references :city, null: false
|
254
|
-
## t.references :state ## optional ?? either state/admin or zone ?? use polymorphic assoc or use node w/ kind for place?
|
255
|
-
## t.references :zone ## tourist zone e.g. fraenkische schweiz, wachau, steigerwald, etc. - use own join table???
|
256
|
-
## end
|
257
|
-
|
258
245
|
|
259
246
|
create_table :cities do |t|
|
260
247
|
t.string :name, null: false
|
@@ -262,6 +249,7 @@ create_table :cities do |t|
|
|
262
249
|
t.references :place, null: false
|
263
250
|
t.string :code # short three letter code (ITAT/airport code e.g. NYC or VIE)
|
264
251
|
t.string :alt_names # comma separated list of alternate names (synonyms)
|
252
|
+
|
265
253
|
t.references :country, null: false
|
266
254
|
t.references :state # optional for now (e.g. state, bundesland, etc.) -- ADM1
|
267
255
|
t.references :part # optional for now (e.g. regierungsbezirk, etc.) -- x /ADM2
|
@@ -273,9 +261,6 @@ create_table :cities do |t|
|
|
273
261
|
t.integer :pop # optional population count (city proper); see metro for metro pop
|
274
262
|
t.integer :area # optional area in square km (sq. km)
|
275
263
|
|
276
|
-
## t.float :lat # optional for now -- FIX: remove?? moved to places
|
277
|
-
## t.float :lng # optional for now -- FIX: remove?? moved to places
|
278
|
-
|
279
264
|
### t.boolean :capital, null: false, default: false # is national captial?
|
280
265
|
|
281
266
|
t.timestamps
|
data/lib/worlddb/version.rb
CHANGED
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
###
|
4
|
+
# to run use
|
5
|
+
# ruby -I ./lib -I ./test test/test_report_country.rb
|
6
|
+
|
7
|
+
|
8
|
+
require 'helper'
|
9
|
+
|
10
|
+
class TestReportCountry < MiniTest::Test
|
11
|
+
|
12
|
+
## note: special tests
|
13
|
+
## run on its own - will NOT run with test suite
|
14
|
+
## requires its own connection
|
15
|
+
|
16
|
+
def xxx_test_at
|
17
|
+
|
18
|
+
## connect to ./austria.db
|
19
|
+
WorldDb.connect( adapter: 'sqlite3', database: './austria.db' )
|
20
|
+
|
21
|
+
r = WorldDb::CountryReport.new( 'at' )
|
22
|
+
r.report
|
23
|
+
end
|
24
|
+
|
25
|
+
def xxx_test_de
|
26
|
+
## connect to ./deutschland.db
|
27
|
+
WorldDb.connect( adapter: 'sqlite3', database: './deutschland.db' )
|
28
|
+
|
29
|
+
r = WorldDb::CountryReport.new( 'de' )
|
30
|
+
r.report
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
end # class TestReportCountry
|
@@ -31,7 +31,7 @@ class TestStateTreeReaderAt < MiniTest::Test
|
|
31
31
|
reader.read
|
32
32
|
|
33
33
|
n = State.find_by!( key: 'n' )
|
34
|
-
assert_equal 'Niederösterreich
|
34
|
+
assert_equal 'Niederösterreich', n.name
|
35
35
|
assert_equal 25, n.counties.count
|
36
36
|
|
37
37
|
ks = County.find_by!( key: 'ks' )
|
@@ -47,7 +47,7 @@ class TestStateTreeReaderAt < MiniTest::Test
|
|
47
47
|
assert_equal 'Gneixendorf', gneixendorf.name
|
48
48
|
assert_equal 'Krems an der Donau', gneixendorf.muni.name
|
49
49
|
assert_equal 'Krems an der Donau (Stadt)', gneixendorf.muni.county.name
|
50
|
-
assert_equal 'Niederösterreich
|
50
|
+
assert_equal 'Niederösterreich', gneixendorf.muni.county.state.name
|
51
51
|
end
|
52
52
|
|
53
53
|
end # class TestStateTreeReaderAt
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: worlddb-models
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.3.
|
4
|
+
version: 2.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-06-
|
11
|
+
date: 2015-06-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: props
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 1.
|
47
|
+
version: 1.3.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 1.
|
54
|
+
version: 1.3.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: tagutils
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -208,9 +208,9 @@ files:
|
|
208
208
|
- lib/worlddb/readers/state.rb
|
209
209
|
- lib/worlddb/readers/state_tree.rb
|
210
210
|
- lib/worlddb/readers/usage.rb
|
211
|
+
- lib/worlddb/reports/country_report.rb
|
211
212
|
- lib/worlddb/schema.rb
|
212
213
|
- lib/worlddb/stats.rb
|
213
|
-
- lib/worlddb/tree_reader.rb
|
214
214
|
- lib/worlddb/version.rb
|
215
215
|
- test/adm/test_fixture_matcher_adm2.rb
|
216
216
|
- test/adm/test_fixture_matcher_adm3.rb
|
@@ -247,9 +247,9 @@ files:
|
|
247
247
|
- test/test_parse_city.rb
|
248
248
|
- test/test_parse_country.rb
|
249
249
|
- test/test_parse_state.rb
|
250
|
+
- test/test_report_country.rb
|
250
251
|
- test/test_state_tree_reader_at.rb
|
251
252
|
- test/test_state_tree_reader_de.rb
|
252
|
-
- test/test_tree_reader.rb
|
253
253
|
homepage: https://github.com/worlddb/world.db.models
|
254
254
|
licenses:
|
255
255
|
- Public Domain
|
@@ -286,11 +286,11 @@ test_files:
|
|
286
286
|
- test/adm/test_fixture_matcher_adm3.rb
|
287
287
|
- test/test_parse_country.rb
|
288
288
|
- test/test_model_state.rb
|
289
|
-
- test/test_tree_reader.rb
|
290
289
|
- test/test_model_city.rb
|
291
290
|
- test/test_fixture_matchers.rb
|
292
291
|
- test/test_model_country.rb
|
293
292
|
- test/test_parse_city.rb
|
293
|
+
- test/test_report_country.rb
|
294
294
|
- test/test_model_compat.rb
|
295
295
|
- test/test_model_states_de.rb
|
296
296
|
- test/test_state_tree_reader_at.rb
|
data/lib/worlddb/tree_reader.rb
DELETED
@@ -1,97 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
|
4
|
-
# fix: move into TextUtils namespace/module!!
|
5
|
-
|
6
|
-
class TreeReader
|
7
|
-
|
8
|
-
include LogUtils::Logging
|
9
|
-
|
10
|
-
def self.from_file( path )
|
11
|
-
## nb: assume/enfore utf-8 encoding (with or without BOM - byte order mark)
|
12
|
-
## - see textutils/utils.rb
|
13
|
-
text = File.read_utf8( path )
|
14
|
-
self.from_string( text )
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.from_string( text )
|
18
|
-
self.new( text )
|
19
|
-
end
|
20
|
-
|
21
|
-
def initialize( text )
|
22
|
-
@text = text
|
23
|
-
end
|
24
|
-
|
25
|
-
TreeItem = Struct.new( :level, :key, :value )
|
26
|
-
|
27
|
-
KEY_REGEX = /
|
28
|
-
([0-9][0-9A-Za-z]*) ## key starting with a nummer
|
29
|
-
|
|
30
|
-
([a-z]+) ## key all lowercase e.g. bt,n,etc.
|
31
|
-
|
|
32
|
-
([A-Z]+) ## key all uppercase e.g. BT,N,etc
|
33
|
-
/x
|
34
|
-
|
35
|
-
LEVEL_REGEX = /\.+/ ## e.g. .. or .... etc.
|
36
|
-
|
37
|
-
|
38
|
-
def each_line
|
39
|
-
stack = [] # note: last_level => stack.size; starts w/ 0
|
40
|
-
times = 2 # assume two indents factor (e.g. .. =2, ....=3 etc. ) for now
|
41
|
-
|
42
|
-
reader = LineReader.from_string( @text )
|
43
|
-
reader.each_line do |line|
|
44
|
-
|
45
|
-
logger.debug "[TreeReader] line (before) => >#{line}<"
|
46
|
-
|
47
|
-
s = StringScanner.new( line )
|
48
|
-
s.skip( /[ \t]+/ ) # remove whitespace
|
49
|
-
|
50
|
-
key = s.scan( KEY_REGEX )
|
51
|
-
if key
|
52
|
-
s.skip( /[ \t]+/ ) # remove whitespace
|
53
|
-
end
|
54
|
-
|
55
|
-
level_str = s.scan( LEVEL_REGEX )
|
56
|
-
if level_str
|
57
|
-
## FIX!! todo/check: make sure level_str.size is a multiple of two !! (e.g. 2,4,6,etc.)
|
58
|
-
level = (level_str.size/times)+1
|
59
|
-
s.skip( /[ \t]+/ ) # remove whitespace
|
60
|
-
else
|
61
|
-
level = 1 ## no level found; assume top level (start w/ 1)
|
62
|
-
end
|
63
|
-
|
64
|
-
## assume rest is record
|
65
|
-
rest = s.rest ## was: s.scan( /.+/ )
|
66
|
-
|
67
|
-
level_diff = level - stack.size
|
68
|
-
|
69
|
-
if level_diff > 0
|
70
|
-
logger.debug "[TreeReader] up +#{level_diff}"
|
71
|
-
## FIX!!! todo/check/verify/assert: always must be +1
|
72
|
-
elsif level_diff < 0
|
73
|
-
logger.debug "[TreeReader] down #{level_diff}"
|
74
|
-
level_diff.abs.times { stack.pop }
|
75
|
-
stack.pop
|
76
|
-
else
|
77
|
-
## same level
|
78
|
-
stack.pop
|
79
|
-
end
|
80
|
-
|
81
|
-
item = TreeItem.new
|
82
|
-
item.level = level
|
83
|
-
item.key = key
|
84
|
-
item.value = rest
|
85
|
-
|
86
|
-
stack.push( item )
|
87
|
-
|
88
|
-
## for debugging - show tree item (note) hierarchy
|
89
|
-
names = stack.map { |it| "(#{it.level}) #{it.value}" }
|
90
|
-
logger.debug "[TreeReader] #{names.join( ' › ' )} -- key: >#{key}<, level: >#{level}<, rest: >#{rest}<"
|
91
|
-
|
92
|
-
yield( stack )
|
93
|
-
end
|
94
|
-
end # method each_line
|
95
|
-
|
96
|
-
end # class TreeReader
|
97
|
-
|
data/test/test_tree_reader.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
###
|
4
|
-
# to run use
|
5
|
-
# ruby -I ./lib -I ./test test/test_tree_reader.rb
|
6
|
-
|
7
|
-
|
8
|
-
require 'helper'
|
9
|
-
|
10
|
-
class TestTreeReader < MiniTest::Test
|
11
|
-
|
12
|
-
def setup
|
13
|
-
# delete all countries, states, cities in in-memory only db
|
14
|
-
WorldDb.delete!
|
15
|
-
end
|
16
|
-
|
17
|
-
|
18
|
-
def test_oberfranken
|
19
|
-
reader = TreeReader.from_file( "#{WorldDb.root}/test/data/de-deutschland/3--by-bayern/4--oberfranken/orte.txt" )
|
20
|
-
|
21
|
-
reader.each_line do |_|
|
22
|
-
## do nothing for now
|
23
|
-
end
|
24
|
-
|
25
|
-
assert true ## assume everything ok if we get here
|
26
|
-
end
|
27
|
-
|
28
|
-
def test_de
|
29
|
-
reader = TreeReader.from_file( "#{WorldDb.root}/test/data/de-deutschland/orte.txt" )
|
30
|
-
|
31
|
-
reader.each_line do |_|
|
32
|
-
## do nothing for now
|
33
|
-
end
|
34
|
-
|
35
|
-
assert true ## assume everything ok if we get here
|
36
|
-
end
|
37
|
-
|
38
|
-
end # class TestTreeReader
|
39
|
-
|