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