worlddb-models 2.1.0
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 +7 -0
- data/.gemtest +0 -0
- data/HISTORY.md +4 -0
- data/Manifest.txt +43 -0
- data/README.md +85 -0
- data/Rakefile +44 -0
- data/lib/worlddb/deleter.rb +32 -0
- data/lib/worlddb/matcher.rb +143 -0
- data/lib/worlddb/models/city.rb +240 -0
- data/lib/worlddb/models/city_comp.rb +27 -0
- data/lib/worlddb/models/continent.rb +41 -0
- data/lib/worlddb/models/continent_comp.rb +24 -0
- data/lib/worlddb/models/country.rb +328 -0
- data/lib/worlddb/models/country_code.rb +41 -0
- data/lib/worlddb/models/country_comp.rb +35 -0
- data/lib/worlddb/models/forward.rb +57 -0
- data/lib/worlddb/models/lang.rb +18 -0
- data/lib/worlddb/models/lang_comp.rb +23 -0
- data/lib/worlddb/models/name.rb +13 -0
- data/lib/worlddb/models/place.rb +16 -0
- data/lib/worlddb/models/region.rb +176 -0
- data/lib/worlddb/models/region_comp.rb +26 -0
- data/lib/worlddb/models/tagdb/tag.rb +16 -0
- data/lib/worlddb/models/tagdb/tagging.rb +15 -0
- data/lib/worlddb/models/usage.rb +17 -0
- data/lib/worlddb/models.rb +200 -0
- data/lib/worlddb/patterns.rb +54 -0
- data/lib/worlddb/reader.rb +224 -0
- data/lib/worlddb/reader_file.rb +86 -0
- data/lib/worlddb/reader_zip.rb +160 -0
- data/lib/worlddb/readers/city.rb +81 -0
- data/lib/worlddb/readers/country.rb +78 -0
- data/lib/worlddb/readers/lang.rb +107 -0
- data/lib/worlddb/readers/region.rb +79 -0
- data/lib/worlddb/readers/usage.rb +98 -0
- data/lib/worlddb/schema.rb +202 -0
- data/lib/worlddb/stats.rb +31 -0
- data/lib/worlddb/version.rb +23 -0
- data/test/helper.rb +26 -0
- data/test/test_fixture_matchers.rb +112 -0
- data/test/test_model_city.rb +60 -0
- data/test/test_model_comp.rb +48 -0
- data/test/test_model_country.rb +53 -0
- data/test/test_model_region.rb +50 -0
- data/test/test_models.rb +35 -0
- metadata +252 -0
@@ -0,0 +1,176 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module WorldDb
|
4
|
+
module Model
|
5
|
+
|
6
|
+
|
7
|
+
class Region < ActiveRecord::Base
|
8
|
+
|
9
|
+
extend TextUtils::TagHelper # will add self.find_tags, self.find_tags_in_attribs!, etc.
|
10
|
+
|
11
|
+
# NB: use extend - is_<type>? become class methods e.g. self.is_<type>? for use in
|
12
|
+
# self.create_or_update_from_values
|
13
|
+
extend TextUtils::ValueHelper # e.g. is_year?, is_region?, is_address?, is_taglist? etc.
|
14
|
+
|
15
|
+
belongs_to :place, class_name: 'Place', foreign_key: 'place_id'
|
16
|
+
belongs_to :country, class_name: 'Country', foreign_key: 'country_id'
|
17
|
+
|
18
|
+
has_many :cities, class_name: 'City', foreign_key: 'region_id'
|
19
|
+
|
20
|
+
has_many_tags
|
21
|
+
|
22
|
+
validates :key, format: { with: /#{REGION_KEY_PATTERN}/, message: REGION_KEY_PATTERN_MESSAGE }
|
23
|
+
validates :code, format: { with: /#{REGION_CODE_PATTERN}/, message: REGION_CODE_PATTERN_MESSAGE }, allow_nil: true
|
24
|
+
|
25
|
+
|
26
|
+
before_create :on_create
|
27
|
+
before_update :on_update
|
28
|
+
|
29
|
+
def on_create
|
30
|
+
place_rec = Place.create!( name: name, kind: place_kind )
|
31
|
+
self.place_id = place_rec.id
|
32
|
+
end
|
33
|
+
|
34
|
+
def on_update
|
35
|
+
## fix/todo: check - if name or kind changed - only update if changed ?? why? why not??
|
36
|
+
place.update_attributes!( name: name, kind: place_kind )
|
37
|
+
end
|
38
|
+
|
39
|
+
def place_kind # use place_kind_of_code ??
|
40
|
+
'ADM1'
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def all_names( opts={} )
|
45
|
+
### fix:
|
46
|
+
## allow to passing in sep or separator e.g. | or other
|
47
|
+
|
48
|
+
return name if alt_names.blank?
|
49
|
+
|
50
|
+
buf = ''
|
51
|
+
buf << name
|
52
|
+
buf << ' | '
|
53
|
+
buf << alt_names.split('|').join(' | ')
|
54
|
+
buf
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
def self.create_or_update_from_values( values, more_attribs={} )
|
60
|
+
|
61
|
+
## key & title & country required
|
62
|
+
attribs, more_values = find_key_n_title( values )
|
63
|
+
attribs = attribs.merge( more_attribs )
|
64
|
+
|
65
|
+
## check for optional values
|
66
|
+
Region.create_or_update_from_attribs( attribs, more_values )
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
def self.create_or_update_from_attribs( new_attributes, values, opts={} )
|
71
|
+
|
72
|
+
## opts e.g. :skip_tags true|false
|
73
|
+
|
74
|
+
## fix: add/configure logger for ActiveRecord!!!
|
75
|
+
logger = LogKernel::Logger.root
|
76
|
+
|
77
|
+
value_numbers = []
|
78
|
+
value_tag_keys = []
|
79
|
+
value_cities = []
|
80
|
+
|
81
|
+
### check for "default" tags - that is, if present new_attributes[:tags] remove from hash
|
82
|
+
value_tag_keys += find_tags_in_attribs!( new_attributes )
|
83
|
+
|
84
|
+
## check for optional values
|
85
|
+
values.each_with_index do |value,index|
|
86
|
+
if match_country( value ) do |country| # country:
|
87
|
+
new_attributes[ :country_id ] = country.id
|
88
|
+
end
|
89
|
+
elsif match_km_squared( value ) do |num| # allow numbers like 453 km²
|
90
|
+
value_numbers << num
|
91
|
+
end
|
92
|
+
elsif match_number( value ) do |num| # numeric (nb: can use any _ or spaces inside digits e.g. 1_000_000 or 1 000 000)
|
93
|
+
value_numbers << num
|
94
|
+
end
|
95
|
+
elsif value =~ /#{REGION_CODE_PATTERN}/ ## assume two or three-letter code
|
96
|
+
new_attributes[ :code ] = value
|
97
|
+
elsif (values.size==(index+1)) && is_taglist?( value ) # tags must be last entry
|
98
|
+
logger.debug " found tags: >>#{value}<<"
|
99
|
+
value_tag_keys += find_tags( value )
|
100
|
+
else
|
101
|
+
|
102
|
+
### assume it is the capital city - mark it for auto add
|
103
|
+
value_cities << value
|
104
|
+
next
|
105
|
+
|
106
|
+
# issue warning: unknown type for value
|
107
|
+
# logger.warn "unknown type for value >#{value}<"
|
108
|
+
end
|
109
|
+
end # each value
|
110
|
+
|
111
|
+
if value_numbers.size > 0
|
112
|
+
new_attributes[ :area ] = value_numbers[0]
|
113
|
+
new_attributes[ :pop ] = value_numbers[1]
|
114
|
+
end # if value_numbers.size > 0
|
115
|
+
|
116
|
+
## todo: assert that country_id is present/valid, that is, NOT null
|
117
|
+
rec = Region.find_by_key_and_country_id( new_attributes[ :key ], new_attributes[ :country_id] )
|
118
|
+
|
119
|
+
if rec.present?
|
120
|
+
logger.debug "update Region #{rec.id}-#{rec.key}:"
|
121
|
+
else
|
122
|
+
logger.debug "create Region:"
|
123
|
+
rec = Region.new
|
124
|
+
end
|
125
|
+
|
126
|
+
logger.debug new_attributes.to_json
|
127
|
+
|
128
|
+
rec.update_attributes!( new_attributes )
|
129
|
+
|
130
|
+
#################
|
131
|
+
# auto add capital cities
|
132
|
+
|
133
|
+
City.create_or_update_from_titles( value_cities,
|
134
|
+
region_id: rec.id,
|
135
|
+
country_id: rec.country_id )
|
136
|
+
|
137
|
+
### todo/fix: add captial ref to country/region
|
138
|
+
## todo/fix: use update_from_title and only allow one capital city
|
139
|
+
|
140
|
+
|
141
|
+
##################
|
142
|
+
# add taggings
|
143
|
+
|
144
|
+
## todo/fix: reuse - move add taggings into method etc.
|
145
|
+
|
146
|
+
if value_tag_keys.size > 0
|
147
|
+
|
148
|
+
if opts[:skip_tags].present?
|
149
|
+
logger.debug " skipping add taggings (flag skip_tag)"
|
150
|
+
else
|
151
|
+
value_tag_keys.uniq! # remove duplicates
|
152
|
+
logger.debug " adding #{value_tag_keys.size} taggings: >>#{value_tag_keys.join('|')}<<..."
|
153
|
+
|
154
|
+
### fix/todo: check tag_ids and only update diff (add/remove ids)
|
155
|
+
|
156
|
+
value_tag_keys.each do |key|
|
157
|
+
tag = Tag.find_by_key( key )
|
158
|
+
if tag.nil? # create tag if it doesn't exit
|
159
|
+
logger.debug " creating tag >#{key}<"
|
160
|
+
tag = Tag.create!( key: key )
|
161
|
+
end
|
162
|
+
rec.tags << tag
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
rec
|
168
|
+
|
169
|
+
end # method create_or_update_from_values
|
170
|
+
|
171
|
+
|
172
|
+
|
173
|
+
end # class Region
|
174
|
+
|
175
|
+
end # module Model
|
176
|
+
end # module WorldDb
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module WorldDb
|
4
|
+
module Model
|
5
|
+
|
6
|
+
#############################################################
|
7
|
+
# collect depreciated or methods for future removal here
|
8
|
+
# - keep for now for commpatibility (for old code)
|
9
|
+
|
10
|
+
class Region
|
11
|
+
|
12
|
+
def title() name; end
|
13
|
+
def title=(value) self.name = value; end
|
14
|
+
|
15
|
+
scope :by_title, ->{ order( 'name asc' ) } # order by title (a-z)
|
16
|
+
|
17
|
+
def synonyms() alt_names; end
|
18
|
+
def synonyms=(value) self.alt_names = value; end
|
19
|
+
|
20
|
+
def title_w_synonyms( opts={} ) all_names( opts ); end # depreciated: use all_names instead
|
21
|
+
|
22
|
+
|
23
|
+
end # class Region
|
24
|
+
|
25
|
+
end # module Model
|
26
|
+
end # module WorldDb
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module TagDb
|
4
|
+
module Model
|
5
|
+
|
6
|
+
class Tag
|
7
|
+
|
8
|
+
has_many :cities, :through => :taggings, :source => :taggable, source_type: 'WorldDb::Model::City', class_name: 'WorldDb::Model::City'
|
9
|
+
has_many :countries, :through => :taggings, :source => :taggable, source_type: 'WorldDb::Model::Country', class_name: 'WorldDb::Model::Country'
|
10
|
+
has_many :regions, :through => :taggings, :source => :taggable, source_type: 'WorldDb::Model::Region', class_name: 'WorldDb::Model::Region'
|
11
|
+
|
12
|
+
end # class Tag
|
13
|
+
|
14
|
+
end # module Model
|
15
|
+
end # module WorldDb
|
16
|
+
|
@@ -0,0 +1,200 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# core and stlibs
|
4
|
+
|
5
|
+
require 'pp'
|
6
|
+
require 'fileutils'
|
7
|
+
require 'uri'
|
8
|
+
require 'erb'
|
9
|
+
require 'json'
|
10
|
+
require 'yaml'
|
11
|
+
|
12
|
+
|
13
|
+
# 3rd party gems / libs
|
14
|
+
|
15
|
+
require 'zip' ## rubyzip gem
|
16
|
+
|
17
|
+
require 'props' # see github.com/rubylibs/props
|
18
|
+
require 'logutils' # see github.com/rubylibs/logutils
|
19
|
+
require 'textutils' # see github.com/rubylibs/textutils
|
20
|
+
|
21
|
+
|
22
|
+
require 'active_record' ## todo: add sqlite3? etc.
|
23
|
+
|
24
|
+
## add more activerecords addons/utils
|
25
|
+
require 'tagutils'
|
26
|
+
require 'activerecord/utils'
|
27
|
+
require 'props/activerecord' # includes ConfDb (ConfDb::Model::Prop, etc.)
|
28
|
+
require 'logutils/activerecord' # includes LogDb (LogDb::Model::Log, etc.)
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
# our own code
|
33
|
+
|
34
|
+
require 'worlddb/version' # always goes first
|
35
|
+
|
36
|
+
require 'worlddb/patterns' # regex patterns (constants)
|
37
|
+
require 'worlddb/models/forward'
|
38
|
+
|
39
|
+
require 'worlddb/models/name'
|
40
|
+
require 'worlddb/models/place'
|
41
|
+
require 'worlddb/models/continent'
|
42
|
+
require 'worlddb/models/continent_comp'
|
43
|
+
require 'worlddb/models/country'
|
44
|
+
require 'worlddb/models/country_comp'
|
45
|
+
require 'worlddb/models/country_code'
|
46
|
+
require 'worlddb/models/region'
|
47
|
+
require 'worlddb/models/region_comp'
|
48
|
+
require 'worlddb/models/city'
|
49
|
+
require 'worlddb/models/city_comp'
|
50
|
+
require 'worlddb/models/lang'
|
51
|
+
require 'worlddb/models/lang_comp'
|
52
|
+
require 'worlddb/models/usage'
|
53
|
+
|
54
|
+
require 'worlddb/models/tagdb/tag'
|
55
|
+
require 'worlddb/models/tagdb/tagging'
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
require 'worlddb/schema' # NB: requires worlddb/models (include WorldDB::Models)
|
60
|
+
require 'worlddb/matcher'
|
61
|
+
|
62
|
+
require 'worlddb/readers/lang'
|
63
|
+
require 'worlddb/readers/usage'
|
64
|
+
require 'worlddb/readers/country'
|
65
|
+
require 'worlddb/readers/region'
|
66
|
+
require 'worlddb/readers/city'
|
67
|
+
|
68
|
+
require 'worlddb/reader'
|
69
|
+
require 'worlddb/reader_file'
|
70
|
+
require 'worlddb/reader_zip'
|
71
|
+
require 'worlddb/deleter'
|
72
|
+
require 'worlddb/stats'
|
73
|
+
|
74
|
+
|
75
|
+
module WorldDb
|
76
|
+
|
77
|
+
def self.create
|
78
|
+
CreateDb.new.up
|
79
|
+
ConfDb::Model::Prop.create!( key: 'db.schema.world.version', value: VERSION )
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.create_all
|
83
|
+
LogDb.create # add logs table
|
84
|
+
ConfDb.create # add props table
|
85
|
+
TagDb.create # add tags, taggings table
|
86
|
+
WorldDb.create
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def self.read( ary, include_path )
|
91
|
+
reader = Reader.new( include_path )
|
92
|
+
ary.each do |name|
|
93
|
+
reader.load( name )
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def self.read_setup( setup, include_path, opts={} )
|
99
|
+
reader = Reader.new( include_path, opts )
|
100
|
+
reader.load_setup( setup )
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.read_setup_from_zip( zip_name, setup, include_path, opts={} ) ## todo/check - use a better (shorter) name ??
|
104
|
+
reader = ZipReader.new( zip_name, include_path, opts )
|
105
|
+
reader.load_setup( setup )
|
106
|
+
reader.close
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.read_all( include_path, opts={} ) # load all builtins (using plain text reader); helper for convenience
|
110
|
+
read_setup( 'setups/all', include_path, opts )
|
111
|
+
end # method read_all
|
112
|
+
|
113
|
+
|
114
|
+
# delete ALL records (use with care!)
|
115
|
+
def self.delete!
|
116
|
+
puts '*** deleting world table records/data...'
|
117
|
+
Deleter.new.run
|
118
|
+
end # method delete!
|
119
|
+
|
120
|
+
def self.delete_all!( opts={} )
|
121
|
+
LogDb.delete!
|
122
|
+
ConfDb.delete!
|
123
|
+
TagDb.delete!
|
124
|
+
WorldDb.delete!
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
####
|
129
|
+
## todo: remove stats ??? why? why not? better use .tables
|
130
|
+
def self.stats
|
131
|
+
Stats.new.tables
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.tables
|
135
|
+
Stats.new.tables
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
def self.connect( db_config={} )
|
140
|
+
|
141
|
+
if db_config.empty?
|
142
|
+
puts "ENV['DATBASE_URL'] - >#{ENV['DATABASE_URL']}<"
|
143
|
+
|
144
|
+
### change default to ./sport.db ?? why? why not?
|
145
|
+
db = URI.parse( ENV['DATABASE_URL'] || 'sqlite3:///world.db' )
|
146
|
+
|
147
|
+
if db.scheme == 'postgres'
|
148
|
+
config = {
|
149
|
+
adapter: 'postgresql',
|
150
|
+
host: db.host,
|
151
|
+
port: db.port,
|
152
|
+
username: db.user,
|
153
|
+
password: db.password,
|
154
|
+
database: db.path[1..-1],
|
155
|
+
encoding: 'utf8'
|
156
|
+
}
|
157
|
+
else # assume sqlite3
|
158
|
+
config = {
|
159
|
+
adapter: db.scheme, # sqlite3
|
160
|
+
database: db.path[1..-1] # world.db (NB: cut off leading /, thus 1..-1)
|
161
|
+
}
|
162
|
+
end
|
163
|
+
else
|
164
|
+
config = db_config # use passed in config hash
|
165
|
+
end
|
166
|
+
|
167
|
+
## todo/check: use if defined?( JRUBY_VERSION ) instead ??
|
168
|
+
if RUBY_PLATFORM =~ /java/ && config[:adapter] == 'sqlite3'
|
169
|
+
# quick hack for JRuby sqlite3 support via jdbc
|
170
|
+
puts "jruby quick hack - adding jdbc libs for jruby sqlite3 database support"
|
171
|
+
require 'jdbc/sqlite3'
|
172
|
+
require 'active_record/connection_adapters/jdbc_adapter'
|
173
|
+
require 'active_record/connection_adapters/jdbcsqlite3_adapter'
|
174
|
+
end
|
175
|
+
|
176
|
+
puts "Connecting to db using settings: "
|
177
|
+
pp config
|
178
|
+
ActiveRecord::Base.establish_connection( config )
|
179
|
+
# ActiveRecord::Base.logger = Logger.new( STDOUT )
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
def self.setup_in_memory_db
|
184
|
+
|
185
|
+
# Database Setup & Config
|
186
|
+
ActiveRecord::Base.logger = Logger.new( STDOUT )
|
187
|
+
## ActiveRecord::Base.colorize_logging = false - no longer exists - check new api/config setting?
|
188
|
+
|
189
|
+
self.connect( adapter: 'sqlite3',
|
190
|
+
database: ':memory:' )
|
191
|
+
|
192
|
+
## build schema
|
193
|
+
WorldDb.create_all
|
194
|
+
end # setup_in_memory_db (using SQLite :memory:)
|
195
|
+
|
196
|
+
end # module WorldDb
|
197
|
+
|
198
|
+
|
199
|
+
# say hello
|
200
|
+
puts WorldDb.banner if $DEBUG || (defined?($RUBYLIBS_DEBUG) && $RUBYLIBS_DEBUG)
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module WorldDb
|
4
|
+
|
5
|
+
# collection of regex patterns for reuse (WorldDb specific)
|
6
|
+
|
7
|
+
### todo: add a patterns.md page to github ??
|
8
|
+
## - add regexper pics??
|
9
|
+
|
10
|
+
############
|
11
|
+
# about ruby regexps
|
12
|
+
#
|
13
|
+
# try the rubular - Ruby regular expression editor and tester
|
14
|
+
# -> http://rubular.com
|
15
|
+
# code -> ?? by ??
|
16
|
+
#
|
17
|
+
#
|
18
|
+
# Jeff Avallone's Regexper - Shows State-Automata Diagrams
|
19
|
+
# try -> http://regexper.com
|
20
|
+
# code -> https://github.com/javallone/regexper
|
21
|
+
#
|
22
|
+
#
|
23
|
+
# Regular Expressions | The Bastards Book of Ruby by Dan Nguyen
|
24
|
+
# http://ruby.bastardsbook.com/chapters/regexes/
|
25
|
+
#
|
26
|
+
# move to notes regex|patterns on geraldb.github.io ??
|
27
|
+
#
|
28
|
+
|
29
|
+
COUNTRY_KEY_PATTERN = '\A[a-z]{2,3}\z' # allow two AND three letter keys e.g. at, mx, eng, sco, etc.
|
30
|
+
COUNTRY_KEY_PATTERN_MESSAGE = "expected two or three lowercase letters a-z /#{COUNTRY_KEY_PATTERN}/"
|
31
|
+
|
32
|
+
COUNTRY_CODE_PATTERN = '\A[A-Z_]{3}\z'
|
33
|
+
COUNTRY_CODE_PATTERN_MESSAGE = "expected three uppercase letters A-Z (and _) /#{COUNTRY_CODE_PATTERN}/"
|
34
|
+
|
35
|
+
|
36
|
+
REGION_KEY_PATTERN = '\A[a-z]+\z'
|
37
|
+
REGION_KEY_PATTERN_MESSAGE = "expected one or more lowercase letters a-z /#{REGION_KEY_PATTERN}/"
|
38
|
+
|
39
|
+
REGION_CODE_PATTERN = '\A[A-Z_]{2,3}\z'
|
40
|
+
REGION_CODE_PATTERN_MESSAGE = "expected two or three uppercase letters A-Z (and _) /#{REGION_CODE_PATTERN}/"
|
41
|
+
|
42
|
+
|
43
|
+
CITY_KEY_PATTERN = '\A[a-z]{3,}\z'
|
44
|
+
CITY_KEY_PATTERN_MESSAGE = "expected three or more lowercase letters a-z' /#{CITY_KEY_PATTERN}/"
|
45
|
+
|
46
|
+
CITY_CODE_PATTERN = '\A[A-Z_]{3}\z'
|
47
|
+
CITY_CODE_PATTERN_MESSAGE = "expected three uppercase letters A-Z (and _)' /#{CITY_CODE_PATTERN}/"
|
48
|
+
|
49
|
+
|
50
|
+
LANG_KEY_PATTERN = '\A[a-z]{2}\z'
|
51
|
+
LANG_KEY_PATTERN_MESSAGE = "expected two lowercase letters a-z' /#{LANG_KEY_PATTERN}/"
|
52
|
+
|
53
|
+
end # module WorldDb
|
54
|
+
|