worlddb-models 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/HISTORY.md +4 -0
  4. data/Manifest.txt +43 -0
  5. data/README.md +85 -0
  6. data/Rakefile +44 -0
  7. data/lib/worlddb/deleter.rb +32 -0
  8. data/lib/worlddb/matcher.rb +143 -0
  9. data/lib/worlddb/models/city.rb +240 -0
  10. data/lib/worlddb/models/city_comp.rb +27 -0
  11. data/lib/worlddb/models/continent.rb +41 -0
  12. data/lib/worlddb/models/continent_comp.rb +24 -0
  13. data/lib/worlddb/models/country.rb +328 -0
  14. data/lib/worlddb/models/country_code.rb +41 -0
  15. data/lib/worlddb/models/country_comp.rb +35 -0
  16. data/lib/worlddb/models/forward.rb +57 -0
  17. data/lib/worlddb/models/lang.rb +18 -0
  18. data/lib/worlddb/models/lang_comp.rb +23 -0
  19. data/lib/worlddb/models/name.rb +13 -0
  20. data/lib/worlddb/models/place.rb +16 -0
  21. data/lib/worlddb/models/region.rb +176 -0
  22. data/lib/worlddb/models/region_comp.rb +26 -0
  23. data/lib/worlddb/models/tagdb/tag.rb +16 -0
  24. data/lib/worlddb/models/tagdb/tagging.rb +15 -0
  25. data/lib/worlddb/models/usage.rb +17 -0
  26. data/lib/worlddb/models.rb +200 -0
  27. data/lib/worlddb/patterns.rb +54 -0
  28. data/lib/worlddb/reader.rb +224 -0
  29. data/lib/worlddb/reader_file.rb +86 -0
  30. data/lib/worlddb/reader_zip.rb +160 -0
  31. data/lib/worlddb/readers/city.rb +81 -0
  32. data/lib/worlddb/readers/country.rb +78 -0
  33. data/lib/worlddb/readers/lang.rb +107 -0
  34. data/lib/worlddb/readers/region.rb +79 -0
  35. data/lib/worlddb/readers/usage.rb +98 -0
  36. data/lib/worlddb/schema.rb +202 -0
  37. data/lib/worlddb/stats.rb +31 -0
  38. data/lib/worlddb/version.rb +23 -0
  39. data/test/helper.rb +26 -0
  40. data/test/test_fixture_matchers.rb +112 -0
  41. data/test/test_model_city.rb +60 -0
  42. data/test/test_model_comp.rb +48 -0
  43. data/test/test_model_country.rb +53 -0
  44. data/test/test_model_region.rb +50 -0
  45. data/test/test_models.rb +35 -0
  46. 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
@@ -0,0 +1,4 @@
1
+ ### 0.0.1 / 2012-11-05
2
+
3
+ * Everything is new. First release
4
+
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
+