worlddb-models 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a6f4e2c609b98e4e47364d364685e82cdb315ab7
|
4
|
+
data.tar.gz: 9329d22376b1a85b186780ff0db0884de86f85af
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ac729c3828ed5e4ffd2c54cf3e700bb1f6f20e0be8fc670b2b7c524700e50ef2544aa0fd1838ca72a7d16581fbc0def39628afd9cdb02a6c1c27b92e0321b6bf
|
7
|
+
data.tar.gz: 3c8e225f39c7481fca92ec4395f2ef262c8c91bc7065e85f69e8662bbdc67c6d4883d4a1b5b3cf4866653106bc3182111666b62b66b2238da2c33a5bdc3d0a59
|
data/.gemtest
ADDED
File without changes
|
data/HISTORY.md
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
HISTORY.md
|
2
|
+
Manifest.txt
|
3
|
+
README.md
|
4
|
+
Rakefile
|
5
|
+
lib/worlddb/deleter.rb
|
6
|
+
lib/worlddb/matcher.rb
|
7
|
+
lib/worlddb/models.rb
|
8
|
+
lib/worlddb/models/city.rb
|
9
|
+
lib/worlddb/models/city_comp.rb
|
10
|
+
lib/worlddb/models/continent.rb
|
11
|
+
lib/worlddb/models/continent_comp.rb
|
12
|
+
lib/worlddb/models/country.rb
|
13
|
+
lib/worlddb/models/country_code.rb
|
14
|
+
lib/worlddb/models/country_comp.rb
|
15
|
+
lib/worlddb/models/forward.rb
|
16
|
+
lib/worlddb/models/lang.rb
|
17
|
+
lib/worlddb/models/lang_comp.rb
|
18
|
+
lib/worlddb/models/name.rb
|
19
|
+
lib/worlddb/models/place.rb
|
20
|
+
lib/worlddb/models/region.rb
|
21
|
+
lib/worlddb/models/region_comp.rb
|
22
|
+
lib/worlddb/models/tagdb/tag.rb
|
23
|
+
lib/worlddb/models/tagdb/tagging.rb
|
24
|
+
lib/worlddb/models/usage.rb
|
25
|
+
lib/worlddb/patterns.rb
|
26
|
+
lib/worlddb/reader.rb
|
27
|
+
lib/worlddb/reader_file.rb
|
28
|
+
lib/worlddb/reader_zip.rb
|
29
|
+
lib/worlddb/readers/city.rb
|
30
|
+
lib/worlddb/readers/country.rb
|
31
|
+
lib/worlddb/readers/lang.rb
|
32
|
+
lib/worlddb/readers/region.rb
|
33
|
+
lib/worlddb/readers/usage.rb
|
34
|
+
lib/worlddb/schema.rb
|
35
|
+
lib/worlddb/stats.rb
|
36
|
+
lib/worlddb/version.rb
|
37
|
+
test/helper.rb
|
38
|
+
test/test_fixture_matchers.rb
|
39
|
+
test/test_model_city.rb
|
40
|
+
test/test_model_comp.rb
|
41
|
+
test/test_model_country.rb
|
42
|
+
test/test_model_region.rb
|
43
|
+
test/test_models.rb
|
data/README.md
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# worlddb-models
|
2
|
+
|
3
|
+
worlddb-models gem - world.db schema & models for easy (re)use
|
4
|
+
|
5
|
+
* home :: [github.com/worlddb/world.db.models](https://github.com/worlddb/world.db.models)
|
6
|
+
* bugs :: [github.com/worlddb/world.db.models/issues](https://github.com/worlddb/world.db.models/issues)
|
7
|
+
* gem :: [rubygems.org/gems/worlddb-models](https://rubygems.org/gems/worlddb-models)
|
8
|
+
* rdoc :: [rubydoc.info/gems/worlddb-models](http://rubydoc.info/gems/worlddb-models)
|
9
|
+
* forum :: [groups.google.com/group/openmundi](https://groups.google.com/group/openmundi)
|
10
|
+
|
11
|
+
|
12
|
+
## Usage Models
|
13
|
+
|
14
|
+
`Country` Model - Example:
|
15
|
+
|
16
|
+
at = Country.find_by! key: 'at'
|
17
|
+
at.name
|
18
|
+
# => 'Austria'
|
19
|
+
at.pop
|
20
|
+
# => 8_414_638
|
21
|
+
at.area
|
22
|
+
# => 83_871
|
23
|
+
|
24
|
+
at.regions.count
|
25
|
+
# => 9
|
26
|
+
at.regions
|
27
|
+
# => [ 'Wien', 'Niederösterreich', 'Oberösterreich', ... ]
|
28
|
+
|
29
|
+
at.cities.by_pop
|
30
|
+
# => [ 'Wien', 'Graz', 'Linz', 'Salzburg', 'Innsbruck' ... ]
|
31
|
+
|
32
|
+
|
33
|
+
`City` Model - Example:
|
34
|
+
|
35
|
+
c = City.find_by! key: 'wien'
|
36
|
+
c.name
|
37
|
+
# => 'Wien'
|
38
|
+
c.country.name
|
39
|
+
# => 'Austria'
|
40
|
+
c.country.continent.name
|
41
|
+
# => 'Europe'
|
42
|
+
|
43
|
+
la = City.find_by! key: 'losangeles'
|
44
|
+
la.name
|
45
|
+
# => 'Los Angeles'
|
46
|
+
la.region.name
|
47
|
+
# => 'California'
|
48
|
+
la.region.key
|
49
|
+
# => 'ca'
|
50
|
+
la.country.name
|
51
|
+
# => 'United States'
|
52
|
+
la.country.key
|
53
|
+
# => 'us'
|
54
|
+
la.country.continent.name
|
55
|
+
# => 'North America'
|
56
|
+
|
57
|
+
|
58
|
+
`Tag` Model - Example:
|
59
|
+
|
60
|
+
euro = Tag.find_by! key: 'euro'
|
61
|
+
euro.countries.count
|
62
|
+
# => 17
|
63
|
+
euro.countries
|
64
|
+
# => ['Austria, 'Belgium', 'Cyprus', ... ]
|
65
|
+
|
66
|
+
flanders = Tag.find_by! key: 'flanders'
|
67
|
+
flanders.regions.count
|
68
|
+
# => 5
|
69
|
+
flanders.regions
|
70
|
+
# => ['Antwerpen', 'Brabant Wallon', 'Limburg', 'Oost-Vlaanderen', 'West-Vlaanderen']
|
71
|
+
flanders.regions.first.country.name
|
72
|
+
# => 'Belgium'
|
73
|
+
|
74
|
+
and so on.
|
75
|
+
|
76
|
+
|
77
|
+
## License
|
78
|
+
|
79
|
+
The `worlddb-models` scripts are dedicated to the public domain.
|
80
|
+
Use it as you please with no restrictions whatsoever.
|
81
|
+
|
82
|
+
## Questions? Comments?
|
83
|
+
|
84
|
+
Send them along to the [Open Mundi (world.db) Database Forum/Mailing List](http://groups.google.com/group/openmundi).
|
85
|
+
Thanks!
|
data/Rakefile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'hoe'
|
2
|
+
require './lib/worlddb/version.rb'
|
3
|
+
|
4
|
+
|
5
|
+
Hoe.spec 'worlddb-models' do
|
6
|
+
|
7
|
+
self.version = WorldDb::VERSION
|
8
|
+
|
9
|
+
self.summary = "worlddb - world.db schema & models for easy (re)use"
|
10
|
+
self.description = summary
|
11
|
+
|
12
|
+
self.urls = ['https://github.com/worlddb/world.db.models']
|
13
|
+
|
14
|
+
self.author = 'Gerald Bauer'
|
15
|
+
self.email = 'openmundi@googlegroups.com'
|
16
|
+
|
17
|
+
self.extra_deps = [
|
18
|
+
['props'], # settings / prop(ertie)s / env / INI
|
19
|
+
['logutils'], # logging
|
20
|
+
['textutils', '>= 0.9.9'], # e.g. >= 0.6 && <= 1.0 ## will include logutils, props
|
21
|
+
|
22
|
+
['tagutils'], # tags n categories for activerecord
|
23
|
+
['activerecord-utils'],
|
24
|
+
['props-activerecord'],
|
25
|
+
['logutils-activerecord'],
|
26
|
+
|
27
|
+
## 3rd party
|
28
|
+
['rubyzip'], ## todo: pull in via textutils ??
|
29
|
+
['activerecord'] # NB: will include activesupport,etc.
|
30
|
+
]
|
31
|
+
|
32
|
+
# switch extension to .markdown for gihub formatting
|
33
|
+
# -- NB: auto-changed when included in manifest
|
34
|
+
self.readme_file = 'README.md'
|
35
|
+
self.history_file = 'HISTORY.md'
|
36
|
+
|
37
|
+
|
38
|
+
self.licenses = ['Public Domain']
|
39
|
+
|
40
|
+
self.spec_extras = {
|
41
|
+
required_ruby_version: '>= 1.9.2'
|
42
|
+
}
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module WorldDb
|
4
|
+
|
5
|
+
class Deleter
|
6
|
+
|
7
|
+
## make models available in worlddb module by default with namespace
|
8
|
+
# e.g. lets you use City instead of Models::City
|
9
|
+
include WorldDb::Models
|
10
|
+
|
11
|
+
def run
|
12
|
+
# for now delete all tables
|
13
|
+
|
14
|
+
## Tagging.delete_all # - use TagDb.delete!
|
15
|
+
## Tag.delete_all
|
16
|
+
|
17
|
+
CountryCode.delete_all
|
18
|
+
Name.delete_all
|
19
|
+
Place.delete_all
|
20
|
+
City.delete_all
|
21
|
+
Region.delete_all
|
22
|
+
Country.delete_all
|
23
|
+
Continent.delete_all
|
24
|
+
Usage.delete_all
|
25
|
+
Lang.delete_all
|
26
|
+
|
27
|
+
# Prop.delete_all # - use ConfDb.delete!
|
28
|
+
end
|
29
|
+
|
30
|
+
end # class Deleter
|
31
|
+
|
32
|
+
end # module WorldDb
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module WorldDb
|
4
|
+
|
5
|
+
module Matcher
|
6
|
+
|
7
|
+
def match_xxx_for_country( name, xxx ) # xxx e.g. cities|regions|beers|breweries
|
8
|
+
# auto-add required country code (from folder structure)
|
9
|
+
# note: always let match_xxx_for_country_n_region go first
|
10
|
+
|
11
|
+
# note: allow /cities and /1--hokkaido--cities
|
12
|
+
xxx_pattern = "(?:#{xxx}|[0-9]+--[^\\/]+?--#{xxx})" # note: double escape \\ required for backslash
|
13
|
+
|
14
|
+
##
|
15
|
+
## todo: add $-anchor at the end of pattern - why? why not?? (will include .txt or .yaml??)
|
16
|
+
|
17
|
+
if name =~ /(?:^|\/)([a-z]{2,3})-[^\/]+\/#{xxx_pattern}/ || # (1)
|
18
|
+
name =~ /(?:^|\/)[0-9]+--([a-z]{2,3})-[^\/]+\/#{xxx_pattern}/ || # (2)
|
19
|
+
name =~ /(?:^|\/)([a-z]{2,3})\/#{xxx_pattern}/ || # (3)
|
20
|
+
name =~ /(?:^|\/)([a-z]{2,3})-[^\/]+\/[0-9]+--[^\/]+\/#{xxx_pattern}/ || # (4)
|
21
|
+
name =~ /(?:^|\/)([a-z]{2,3})-[^\/]+--#{xxx}/ # (5)
|
22
|
+
|
23
|
+
country_key = $1.dup
|
24
|
+
yield( country_key )
|
25
|
+
true # bingo - match found
|
26
|
+
|
27
|
+
######
|
28
|
+
# (1) new style: e.g. /at-austria/beers or ^at-austria!/cities
|
29
|
+
#
|
30
|
+
# (2) new-new style e.g. /1--at-austria--central/cities
|
31
|
+
#
|
32
|
+
# (3) classic style: e.g. /at/beers (europe/at/cities)
|
33
|
+
#
|
34
|
+
# (4) new style w/ region w/o abbrev/code e.g. /ja-japon/1--hokkaido/cities
|
35
|
+
#
|
36
|
+
# (5) compact style (country part of filename):
|
37
|
+
# e.g. /at-austria--cities or /europe/at-austria--cities
|
38
|
+
else
|
39
|
+
false # no match found
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def match_xxx_for_country_n_region( name, xxx ) # xxx e.g. wine|wineries
|
45
|
+
|
46
|
+
# auto-add required country n region code (from folder structure)
|
47
|
+
|
48
|
+
## -- allow opt_folders after long regions (e.g. additional subregion/zone)
|
49
|
+
## -- allow anything (prefixes) before -- for xxx
|
50
|
+
# e.g. at-austria!/1--n-niederoesterreich--eastern/wagram--wines
|
51
|
+
# at-austria!/1--n-niederoesterreich--eastern/wagram--wagram--wines
|
52
|
+
|
53
|
+
# note: allow /cities and /1--hokkaido--cities and /hokkaido--cities too
|
54
|
+
xxx_pattern = "(?:#{xxx}|[^\\/]+--#{xxx})" # note: double escape \\ required for backslash
|
55
|
+
|
56
|
+
## allow optional folders -- TODO: add restriction ?? e.g. must be 4+ alphas ???
|
57
|
+
opt_folders_pattern = "(?:\/[^\/]+)*"
|
58
|
+
## note: for now only (style #2) n (style #3) that is long region allow opt folders
|
59
|
+
|
60
|
+
if name =~ /(?:^|\/)([a-z]{2,3})-[^\/]+\/([a-z]{1,3})-[^\/]+\/#{xxx_pattern}/ || # (1)
|
61
|
+
name =~ /(?:^|\/)[0-9]+--([a-z]{2,3})-[^\/]+\/[0-9]+--([a-z]{1,3})-[^\/]+#{opt_folders_pattern}\/#{xxx_pattern}/ || # (2)
|
62
|
+
name =~ /(?:^|\/)([a-z]{2,3})-[^\/]+\/[0-9]+--([a-z]{1,3})-[^\/]+#{opt_folders_pattern}\/#{xxx_pattern}/ || # (3)
|
63
|
+
name =~ /(?:^|\/)[0-9]+--([a-z]{2,3})-[^\/]+\/([a-z]{1,3})-[^\/]+\/#{xxx_pattern}/ # (4)
|
64
|
+
|
65
|
+
#######
|
66
|
+
# nb: country must start name (^) or coming after / e.g. europe/at-austria/...
|
67
|
+
# (1)
|
68
|
+
# new style: e.g. /at-austria/w-wien/cities or
|
69
|
+
# ^at-austria!/w-wien/cities
|
70
|
+
# (2)
|
71
|
+
# new new style e.g. /1--at-austria--central/1--w-wien--eastern/cities
|
72
|
+
#
|
73
|
+
# (3)
|
74
|
+
# new new mixed style e.g. /at-austria/1--w-wien--eastern/cities
|
75
|
+
# "classic" country plus new new region
|
76
|
+
#
|
77
|
+
# (4)
|
78
|
+
# new new mixed style e.g. /1--at-austria--central/w-wien/cities
|
79
|
+
# new new country plus "classic" region
|
80
|
+
|
81
|
+
country_key = $1.dup
|
82
|
+
region_key = $2.dup
|
83
|
+
yield( country_key, region_key )
|
84
|
+
true # bingo - match found
|
85
|
+
else
|
86
|
+
false # no match found
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
def match_cities_for_country( name, &blk )
|
92
|
+
## todo: check if there's a better (more ruby way) to pass along code block ??
|
93
|
+
## e.g. try
|
94
|
+
## match_xxx_for_country( name, 'cities') { |country_key| yield(country_key) }
|
95
|
+
|
96
|
+
match_xxx_for_country( name, 'cities', &blk )
|
97
|
+
end
|
98
|
+
|
99
|
+
def match_regions_for_country( name, &blk )
|
100
|
+
## also try synonyms e.g. old regions (if not match for states)
|
101
|
+
found = match_xxx_for_country( name, 'states', &blk )
|
102
|
+
found = match_xxx_for_country( name, 'regions', &blk ) unless found
|
103
|
+
found
|
104
|
+
end
|
105
|
+
|
106
|
+
def match_regions_abbr_for_country( name, &blk ) # NB: . gets escaped for regex, that is, \.
|
107
|
+
## also try synonyms e.g. old regions (if not match for states)
|
108
|
+
found = match_xxx_for_country( name, 'states\.abbr', &blk )
|
109
|
+
found = match_xxx_for_country( name, 'regions\.abbr', &blk ) unless found
|
110
|
+
found
|
111
|
+
end
|
112
|
+
|
113
|
+
def match_regions_iso_for_country( name, &blk ) # NB: . gets escaped for regex, that is, \.
|
114
|
+
## also try synonyms e.g. old regions (if not match for states)
|
115
|
+
found = match_xxx_for_country( name, 'states\.iso', &blk )
|
116
|
+
found = match_xxx_for_country( name, 'regions\.iso', &blk ) unless found
|
117
|
+
found
|
118
|
+
end
|
119
|
+
|
120
|
+
def match_regions_nuts_for_country( name, &blk ) # NB: . gets escaped for regex, that is, \.
|
121
|
+
## also try synonyms e.g. old regions (if not match for states)
|
122
|
+
found = match_xxx_for_country( name, 'states\.nuts', &blk )
|
123
|
+
found = match_xxx_for_country( name, 'regions\.nuts', &blk ) unless found
|
124
|
+
found
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
def match_countries_for_continent( name )
|
129
|
+
if name =~ /^([a-z][a-z\-_]+[a-z])\/countries/ # e.g. africa/countries or america/countries
|
130
|
+
### NB: continent changed to regions (e.g. middle-east, caribbean, north-america, etc.)
|
131
|
+
## auto-add continent (from folder structure) as tag
|
132
|
+
## fix: allow dash/hyphen/minus in tag
|
133
|
+
continent = $1.dup
|
134
|
+
yield( continent )
|
135
|
+
true
|
136
|
+
else
|
137
|
+
false # no match found
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end # module Matcher
|
142
|
+
|
143
|
+
end # module WorldDb
|
@@ -0,0 +1,240 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module WorldDb
|
4
|
+
module Model
|
5
|
+
|
6
|
+
###
|
7
|
+
## Todo:
|
8
|
+
## use four classes instead of one ?
|
9
|
+
# e.g. Use class class Metro n class City n class District n class CityBase ?? - why? why not?
|
10
|
+
#
|
11
|
+
# find a better name for CityBase ??
|
12
|
+
# Locality ??
|
13
|
+
# or CityCore or CityStd or CityAll or CityGeneric
|
14
|
+
# or CityLike or CityTable or CityTbl or ???
|
15
|
+
|
16
|
+
class City < ActiveRecord::Base
|
17
|
+
|
18
|
+
extend TextUtils::TagHelper # will add self.find_tags, self.find_tags_in_attribs!, etc.
|
19
|
+
|
20
|
+
# NB: use extend - is_<type>? become class methods e.g. self.is_<type>? for use in
|
21
|
+
# self.create_or_update_from_values
|
22
|
+
extend TextUtils::ValueHelper # e.g. self.is_year?, self.is_region?, self.is_address?, self.is_taglist? etc.
|
23
|
+
|
24
|
+
|
25
|
+
self.table_name = 'cities'
|
26
|
+
|
27
|
+
belongs_to :place, class_name: 'Place', foreign_key: 'place_id'
|
28
|
+
belongs_to :country, class_name: 'Country', foreign_key: 'country_id'
|
29
|
+
belongs_to :region, class_name: 'Region', foreign_key: 'region_id'
|
30
|
+
|
31
|
+
## self referencing hierachy within cities e.g. m|metro > c|city > d|district
|
32
|
+
|
33
|
+
## fix: use condition check for m|d|c flag?? why? why not? (NB: flags are NOT exclusive e.g. possible metro|city)
|
34
|
+
|
35
|
+
## (1) metro - level up
|
36
|
+
has_many :cities, class_name: 'City', foreign_key: 'city_id'
|
37
|
+
|
38
|
+
## (2) city
|
39
|
+
belongs_to :metro, class_name: 'City', foreign_key: 'city_id' ## for now alias for parent - use parent?
|
40
|
+
has_many :districts, class_name: 'City', foreign_key: 'city_id' ## for now alias for cities - use cities?
|
41
|
+
|
42
|
+
## (3) district - level down
|
43
|
+
belongs_to :city, class_name: 'City', foreign_key: 'city_id' ## for now alias for parent - use parent?
|
44
|
+
|
45
|
+
has_many_tags
|
46
|
+
|
47
|
+
###
|
48
|
+
# NB: use is_ for flags to avoid conflict w/ assocs (e.g. metro?, city? etc.)
|
49
|
+
|
50
|
+
def is_metro?() m? == true; end
|
51
|
+
def is_city?() c? == true; end
|
52
|
+
def is_district?() d? == true; end
|
53
|
+
|
54
|
+
before_create :on_create
|
55
|
+
before_update :on_update
|
56
|
+
|
57
|
+
def on_create
|
58
|
+
place_rec = Place.create!( name: name, kind: place_kind )
|
59
|
+
self.place_id = place_rec.id
|
60
|
+
end
|
61
|
+
|
62
|
+
def on_update
|
63
|
+
## fix/todo: check - if name or kind changed - only update if changed ?? why? why not??
|
64
|
+
place.update_attributes!( name: name, kind: place_kind )
|
65
|
+
end
|
66
|
+
|
67
|
+
def place_kind # use place_kind_of_code ??
|
68
|
+
### fix/todo: make sure city records won't overlap (e.g. using metro n city flag at the same time; use separate records)
|
69
|
+
#//////////////////////////////////
|
70
|
+
#// fix: add nested record syntax e.g. city w/ metro population
|
71
|
+
#// use (metro: 4444) e.g. must start with (<nested_type>: props) !!! or similar
|
72
|
+
#//
|
73
|
+
if is_metro?
|
74
|
+
'MTRO'
|
75
|
+
elsif is_district?
|
76
|
+
'DIST'
|
77
|
+
else
|
78
|
+
'CITY'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
validates :key, format: { with: /#{CITY_KEY_PATTERN}/, message: CITY_KEY_PATTERN_MESSAGE }
|
84
|
+
validates :code, format: { with: /#{CITY_CODE_PATTERN}/, message: CITY_CODE_PATTERN_MESSAGE }, allow_nil: true
|
85
|
+
|
86
|
+
|
87
|
+
scope :by_key, ->{ order( 'key asc' ) } # order by key (a-z)
|
88
|
+
scope :by_name, ->{ order( 'name asc' ) } # order by title (a-z)
|
89
|
+
scope :by_pop, ->{ order( 'pop desc' ) } # order by pop(ulation)
|
90
|
+
scope :by_popm, ->{ order( 'popm desc' ) } # order by pop(ulation) metropolitan area
|
91
|
+
scope :by_area, ->{ order( 'area desc' ) } # order by area (in square km)
|
92
|
+
|
93
|
+
|
94
|
+
def all_names( opts={} )
|
95
|
+
### fix:
|
96
|
+
## allow to passing in sep or separator e.g. | or other
|
97
|
+
|
98
|
+
return name if alt_names.blank?
|
99
|
+
|
100
|
+
buf = ''
|
101
|
+
buf << name
|
102
|
+
buf << ' | '
|
103
|
+
buf << alt_names.split('|').join(' | ')
|
104
|
+
buf
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def self.create_or_update_from_values( values, more_attribs={} )
|
109
|
+
## key & title & country required
|
110
|
+
|
111
|
+
attribs, more_values = find_key_n_title( values )
|
112
|
+
attribs = attribs.merge( more_attribs )
|
113
|
+
|
114
|
+
## check for optional values
|
115
|
+
City.create_or_update_from_attribs( attribs, more_values )
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
def self.create_or_update_from_titles( titles, more_attribs = {} )
|
120
|
+
# ary of titles e.g. ['Wien', 'Graz'] etc.
|
121
|
+
|
122
|
+
titles.each do |title|
|
123
|
+
values = [title]
|
124
|
+
City.create_or_update_from_values( values, more_attribs )
|
125
|
+
end # each city
|
126
|
+
end # method create_or_update_from_titles
|
127
|
+
|
128
|
+
|
129
|
+
|
130
|
+
def self.create_or_update_from_attribs( new_attributes, values, opts={} )
|
131
|
+
# attribs -> key/value pairs e.g. hash
|
132
|
+
# values -> ary of string values/strings (key not yet known; might be starting of value e.g. city:wien)
|
133
|
+
|
134
|
+
## opts e.g. :skip_tags true|false
|
135
|
+
|
136
|
+
## fix: add/configure logger for ActiveRecord!!!
|
137
|
+
logger = LogKernel::Logger.root
|
138
|
+
|
139
|
+
value_numbers = []
|
140
|
+
value_tag_keys = []
|
141
|
+
|
142
|
+
### check for "default" tags - that is, if present new_attributes[:tags] remove from hash
|
143
|
+
value_tag_keys += find_tags_in_attribs!( new_attributes )
|
144
|
+
|
145
|
+
new_attributes[ :c ] = true # assume city type by default (use metro,district to change in fixture)
|
146
|
+
|
147
|
+
## check for optional values
|
148
|
+
|
149
|
+
values.each_with_index do |value,index|
|
150
|
+
if match_region_for_country( value, new_attributes[:country_id] ) do |region|
|
151
|
+
new_attributes[ :region_id ] = region.id
|
152
|
+
end
|
153
|
+
elsif match_country( value ) do |country|
|
154
|
+
new_attributes[ :country_id ] = country.id
|
155
|
+
end
|
156
|
+
elsif match_metro( value ) do |city|
|
157
|
+
new_attributes[ :city_id ] = city.id
|
158
|
+
end
|
159
|
+
elsif match_metro_pop( value ) do |num| # m:
|
160
|
+
new_attributes[ :popm ] = num
|
161
|
+
new_attributes[ :m ] = true # auto-mark city as m|metro too
|
162
|
+
end
|
163
|
+
elsif match_metro_flag( value ) do |_| # metro(politan area)
|
164
|
+
new_attributes[ :c ] = false # turn off default c|city flag; make it m|metro only
|
165
|
+
new_attributes[ :m ] = true
|
166
|
+
end
|
167
|
+
elsif match_city( value ) do |city| # parent city for district
|
168
|
+
new_attributes[ :city_id ] = city.id
|
169
|
+
new_attributes[ :c ] = false # turn off default c|city flag; make it d|district only
|
170
|
+
new_attributes[ :d ] = true
|
171
|
+
end
|
172
|
+
elsif match_km_squared( value ) do |num| # allow numbers like 453 km²
|
173
|
+
value_numbers << num
|
174
|
+
end
|
175
|
+
elsif match_number( value ) do |num| # numeric (nb: can use any _ or spaces inside digits e.g. 1_000_000 or 1 000 000)
|
176
|
+
value_numbers << num
|
177
|
+
end
|
178
|
+
elsif value =~ /#{CITY_CODE_PATTERN}/ ## assume three-letter code
|
179
|
+
new_attributes[ :code ] = value
|
180
|
+
elsif (values.size==(index+1)) && is_taglist?( value ) # tags must be last entry
|
181
|
+
logger.debug " found tags: >>#{value}<<"
|
182
|
+
value_tag_keys += find_tags( value )
|
183
|
+
else
|
184
|
+
# issue warning: unknown type for value
|
185
|
+
logger.warn "unknown type for value >#{value}<"
|
186
|
+
end
|
187
|
+
end # each value
|
188
|
+
|
189
|
+
if value_numbers.size > 0
|
190
|
+
new_attributes[ :pop ] = value_numbers[0] # assume first number is pop for cities
|
191
|
+
new_attributes[ :area ] = value_numbers[1]
|
192
|
+
end
|
193
|
+
|
194
|
+
rec = City.find_by_key( new_attributes[ :key ] )
|
195
|
+
|
196
|
+
if rec.present?
|
197
|
+
logger.debug "update City #{rec.id}-#{rec.key}:"
|
198
|
+
else
|
199
|
+
logger.debug "create City:"
|
200
|
+
rec = City.new
|
201
|
+
end
|
202
|
+
|
203
|
+
logger.debug new_attributes.to_json
|
204
|
+
|
205
|
+
rec.update_attributes!( new_attributes )
|
206
|
+
|
207
|
+
##################
|
208
|
+
## add taggings
|
209
|
+
|
210
|
+
## todo/fix: reuse - move add taggings into method etc.
|
211
|
+
|
212
|
+
if value_tag_keys.size > 0
|
213
|
+
|
214
|
+
if opts[:skip_tags].present?
|
215
|
+
logger.debug " skipping add taggings (flag skip_tag)"
|
216
|
+
else
|
217
|
+
value_tag_keys.uniq! # remove duplicates
|
218
|
+
logger.debug " adding #{value_tag_keys.size} taggings: >>#{value_tag_keys.join('|')}<<..."
|
219
|
+
|
220
|
+
### fix/todo: check tag_ids and only update diff (add/remove ids)
|
221
|
+
|
222
|
+
value_tag_keys.each do |key|
|
223
|
+
tag = Tag.find_by_key( key )
|
224
|
+
if tag.nil? # create tag if it doesn't exit
|
225
|
+
logger.debug " creating tag >#{key}<"
|
226
|
+
tag = Tag.create!( key: key )
|
227
|
+
end
|
228
|
+
rec.tags << tag
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
rec
|
234
|
+
end # method create_or_update_from_values
|
235
|
+
|
236
|
+
|
237
|
+
end # class Cities
|
238
|
+
|
239
|
+
end # module Model
|
240
|
+
end # module WorldDb
|
@@ -0,0 +1,27 @@
|
|
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 City
|
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
|
+
|
18
|
+
def synonyms() alt_names; end
|
19
|
+
def synonyms=(value) self.alt_names = value; end
|
20
|
+
|
21
|
+
def title_w_synonyms( opts={} ) all_names( opts ); end # depreciated: use all_names instead
|
22
|
+
|
23
|
+
end # class Cities
|
24
|
+
|
25
|
+
end # module Model
|
26
|
+
end # module WorldDb
|
27
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module WorldDb
|
4
|
+
module Model
|
5
|
+
|
6
|
+
class Continent < ActiveRecord::Base
|
7
|
+
|
8
|
+
belongs_to :place, class_name: 'Place', foreign_key: 'place_id'
|
9
|
+
has_many :countries
|
10
|
+
|
11
|
+
# NB: allow dots in keys e.g. concacaf.naf etc.
|
12
|
+
|
13
|
+
before_create :on_create
|
14
|
+
before_update :on_update
|
15
|
+
|
16
|
+
def on_create
|
17
|
+
place_rec = Place.create!( name: name, kind: place_kind )
|
18
|
+
self.place_id = place_rec.id
|
19
|
+
|
20
|
+
self.slug = TextUtils.slugify( name ) if slug.blank?
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_update
|
24
|
+
## fix/todo: check - if name or kind changed - only update if changed ?? why? why not??
|
25
|
+
place.update_attributes!( name: name, kind: place_kind )
|
26
|
+
|
27
|
+
## check if name changed -- possible?
|
28
|
+
## update slug too??
|
29
|
+
end
|
30
|
+
|
31
|
+
def place_kind # use place_kind_of_code ??
|
32
|
+
'CONT'
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
end # class Continent
|
37
|
+
|
38
|
+
|
39
|
+
end # module Model
|
40
|
+
end # module WorldDb
|
41
|
+
|