worlddb-models 2.2.2 → 2.3.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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/Manifest.txt +31 -13
  3. data/README.md +7 -7
  4. data/Rakefile +1 -1
  5. data/lib/worlddb/deleter.rb +6 -1
  6. data/lib/worlddb/helpers/value_helper.rb +117 -0
  7. data/lib/worlddb/matcher.rb +99 -135
  8. data/lib/worlddb/matcher_adm.rb +82 -0
  9. data/lib/worlddb/models/city.rb +30 -208
  10. data/lib/worlddb/models/city_base.rb +220 -0
  11. data/lib/worlddb/models/continent.rb +9 -0
  12. data/lib/worlddb/models/country.rb +21 -4
  13. data/lib/worlddb/models/forward.rb +25 -9
  14. data/lib/worlddb/models/lang.rb +6 -0
  15. data/lib/worlddb/models/place.rb +1 -1
  16. data/lib/worlddb/models/state.rb +83 -0
  17. data/lib/worlddb/models/{region.rb → state_base.rb} +52 -36
  18. data/lib/worlddb/models/tagdb/tag.rb +1 -1
  19. data/lib/worlddb/models.rb +11 -8
  20. data/lib/worlddb/patterns.rb +4 -4
  21. data/lib/worlddb/reader.rb +68 -39
  22. data/lib/worlddb/reader_file.rb +36 -3
  23. data/lib/worlddb/reader_zip.rb +33 -3
  24. data/lib/worlddb/readers/base.rb +149 -0
  25. data/lib/worlddb/readers/city.rb +2 -65
  26. data/lib/worlddb/readers/country.rb +2 -63
  27. data/lib/worlddb/readers/lang.rb +3 -68
  28. data/lib/worlddb/readers/state.rb +61 -0
  29. data/lib/worlddb/readers/state_tree.rb +118 -0
  30. data/lib/worlddb/readers/usage.rb +2 -65
  31. data/lib/worlddb/schema.rb +142 -43
  32. data/lib/worlddb/stats.rb +7 -4
  33. data/lib/worlddb/tree_reader.rb +97 -0
  34. data/lib/worlddb/version.rb +2 -2
  35. data/test/adm/test_fixture_matcher_adm2.rb +73 -0
  36. data/test/{test_fixture_matcher_adm3.rb → adm/test_fixture_matcher_adm3.rb} +6 -6
  37. data/test/adm/test_fixture_matcher_tree.rb +52 -0
  38. data/test/{test_read_adm.rb → adm/test_read_adm.rb} +13 -20
  39. data/test/adm/test_read_tree.rb +63 -0
  40. data/test/data/at-austria/2--n-niederoesterreich/counties.txt +6 -4
  41. data/test/data/at-austria/orte.txt +23 -0
  42. data/test/data/at-austria/setups/tree.txt +9 -0
  43. data/test/data/de-deutschland/3--by-bayern/4--oberfranken/counties.txt +14 -13
  44. data/test/data/de-deutschland/3--by-bayern/4--oberfranken/orte.txt +104 -0
  45. data/test/data/de-deutschland/3--by-bayern/4--oberfranken/orte_ii.txt +17 -0
  46. data/test/data/de-deutschland/3--by-bayern/{districts.txt → parts.txt} +1 -1
  47. data/test/data/de-deutschland/orte.txt +12 -0
  48. data/test/data/de-deutschland/setups/adm.txt +1 -1
  49. data/test/data/de-deutschland/setups/tree.txt +9 -0
  50. data/test/helper.rb +8 -1
  51. data/test/test_fixture_matchers.rb +9 -10
  52. data/test/test_fixture_matchers_ii.rb +20 -19
  53. data/test/test_model_city.rb +26 -9
  54. data/test/{test_model_comp.rb → test_model_compat.rb} +15 -13
  55. data/test/test_model_country.rb +1 -1
  56. data/test/test_model_state.rb +54 -0
  57. data/test/test_model_states_at.rb +111 -0
  58. data/test/test_model_states_de.rb +147 -0
  59. data/test/test_models.rb +10 -3
  60. data/test/test_parse_city.rb +70 -0
  61. data/test/test_parse_country.rb +56 -0
  62. data/test/test_parse_state.rb +46 -0
  63. data/test/test_state_tree_reader_at.rb +54 -0
  64. data/test/test_state_tree_reader_de.rb +71 -0
  65. data/test/test_tree_reader.rb +39 -0
  66. metadata +50 -22
  67. data/lib/worlddb/models/city_compat.rb +0 -27
  68. data/lib/worlddb/models/continent_compat.rb +0 -24
  69. data/lib/worlddb/models/country_compat.rb +0 -35
  70. data/lib/worlddb/models/lang_compat.rb +0 -23
  71. data/lib/worlddb/models/region_compat.rb +0 -26
  72. data/lib/worlddb/readers/region.rb +0 -79
  73. data/test/test_fixture_matcher_adm2.rb +0 -62
  74. data/test/test_model_region.rb +0 -50
@@ -3,238 +3,60 @@
3
3
  module WorldDb
4
4
  module Model
5
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
6
 
7
+ class City < CityBase
24
8
 
25
9
  self.table_name = 'cities'
26
10
 
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
11
+ belongs_to :country, class_name: 'Country', foreign_key: 'country_id'
12
+ belongs_to :state, class_name: 'State', foreign_key: 'state_id'
13
+ belongs_to :muni, class_name: 'Muni', foreign_key: 'muni_id'
53
14
 
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
15
+ belongs_to :metro, class_name: 'Metro', foreign_key: 'metro_id'
16
+ has_many :districts, class_name: 'District', foreign_key: 'city_id'
66
17
 
67
18
  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
19
+ 'CITY'
80
20
  end
81
21
 
22
+ def self.create_or_update_from_titles( titles, more_attribs = {} )
23
+ # ary of titles e.g. ['Wien', 'Graz'] etc.
82
24
 
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)
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
92
30
 
93
31
 
94
- def all_names( opts={} )
95
- ### fix:
96
- ## allow to passing in sep or separator e.g. | or other
32
+ end # class City
97
33
 
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
34
 
35
+ class Metro < CityBase
107
36
 
108
- def self.create_or_update_from_values( values, more_attribs={} )
109
- ## key & title & country required
37
+ belongs_to :country, class_name: 'Country', foreign_key: 'country_id'
38
+ ## note: metro might be part of more than one (two,three) states (thus, cannot belong_to state)
39
+ ## collect (all) states from cities??
110
40
 
111
- attribs, more_values = find_key_n_title( values )
112
- attribs = attribs.merge( more_attribs )
41
+ has_many :cities, class_name: 'City', foreign_key: 'metro_id'
113
42
 
114
- ## check for optional values
115
- City.create_or_update_from_attribs( attribs, more_values )
43
+ def place_kind # use place_kind_of_code ??
44
+ 'MTRO'
116
45
  end
117
46
 
47
+ end # class Metro
118
48
 
119
- def self.create_or_update_from_titles( titles, more_attribs = {} )
120
- # ary of titles e.g. ['Wien', 'Graz'] etc.
121
49
 
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
50
+ class District < CityBase
51
+
52
+ belongs_to :city, class_name: 'City', foreign_key: 'city_id'
127
53
 
54
+ def place_kind # use place_kind_of_code ??
55
+ 'DIST'
56
+ end
128
57
 
58
+ end # class District
129
59
 
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
60
 
239
61
  end # module Model
240
62
  end # module WorldDb
@@ -0,0 +1,220 @@
1
+ # encoding: utf-8
2
+
3
+ module WorldDb
4
+ module Model
5
+
6
+
7
+ class CityBase < ActiveRecord::Base
8
+
9
+ self.abstract_class = true
10
+
11
+ extend TextUtils::TagHelper # will add self.find_tags, self.find_tags_in_attribs!, etc.
12
+
13
+ # NB: use extend - is_<type>? become class methods e.g. self.is_<type>? for use in
14
+ # self.create_or_update_from_values
15
+ extend TextUtils::ValueHelper # e.g. self.is_year?, self.is_address?, self.is_taglist? etc.
16
+
17
+ belongs_to :place, class_name: 'Place', foreign_key: 'place_id'
18
+
19
+ has_many_tags
20
+
21
+ ## begin compat
22
+ def title() name; end
23
+ def title=(value) self.name = value; end
24
+
25
+ def synonyms() alt_names; end
26
+ def synonyms=(value) self.alt_names = value; end
27
+ ## end
28
+
29
+ before_create :on_create
30
+ before_update :on_update
31
+
32
+ def on_create
33
+ place_rec = Place.create!( name: name, kind: place_kind )
34
+ self.place_id = place_rec.id
35
+ end
36
+
37
+ def on_update
38
+ ## fix/todo: check - if name or kind changed - only update if changed ?? why? why not??
39
+ place.update_attributes!( name: name, kind: place_kind )
40
+ end
41
+
42
+ validates :key, format: { with: /#{CITY_KEY_PATTERN}/, message: CITY_KEY_PATTERN_MESSAGE }
43
+ validates :code, format: { with: /#{CITY_CODE_PATTERN}/, message: CITY_CODE_PATTERN_MESSAGE }, allow_nil: true
44
+
45
+
46
+ scope :by_key, ->{ order( 'key asc' ) } # order by key (a-z)
47
+ scope :by_name, ->{ order( 'name asc' ) } # order by title (a-z)
48
+ scope :by_pop, ->{ order( 'pop desc' ) } # order by pop(ulation)
49
+ scope :by_area, ->{ order( 'area desc' ) } # order by area (in square km)
50
+
51
+
52
+ def all_names( opts={} )
53
+ ### fix:
54
+ ## allow to passing in sep or separator e.g. | or other
55
+
56
+ return name if alt_names.blank?
57
+
58
+ buf = ''
59
+ buf << name
60
+ buf << ' | '
61
+ buf << alt_names.split('|').join(' | ')
62
+ buf
63
+ end
64
+
65
+
66
+
67
+ def self.parse( *args )
68
+ ## remove (extract) attribs hash (if last arg is a hash n present)
69
+ more_attribs = args.last.is_a?(Hash) ? args.pop : {} ## extract_options!
70
+ values = args
71
+
72
+ self.create_or_update_from_values( values, more_attribs )
73
+ end
74
+
75
+
76
+ def self.create_or_update_from_values( values, more_attribs={} )
77
+ ## key & title & country required
78
+
79
+ attribs, more_values = find_key_n_title( values )
80
+ attribs = attribs.merge( more_attribs )
81
+
82
+ ## check for optional values
83
+ self.create_or_update_from_attribs( attribs, more_values )
84
+ end
85
+
86
+ def self.create_or_update_from_attribs( new_attributes, values, opts={} )
87
+ # attribs -> key/value pairs e.g. hash
88
+ # values -> ary of string values/strings (key not yet known; might be starting of value e.g. city:wien)
89
+
90
+ ## opts e.g. :skip_tags true|false
91
+
92
+ ## fix: add/configure logger for ActiveRecord!!!
93
+ logger = LogKernel::Logger.root
94
+
95
+ value_numbers = []
96
+ value_tag_keys = []
97
+
98
+ ### check for "default" tags - that is, if present new_attributes[:tags] remove from hash
99
+ value_tag_keys += find_tags_in_attribs!( new_attributes )
100
+
101
+ city_class = City # assume city type by default (use metro,district to change in fixture)
102
+ new_attributes_metro = {}
103
+
104
+ ## check for optional values
105
+
106
+ values.each_with_index do |value,index|
107
+ if match_state_for_country( value, new_attributes[:country_id] ) do |state|
108
+ new_attributes[ :state_id ] = state.id
109
+ end
110
+ elsif match_country( value ) do |country|
111
+ new_attributes[ :country_id ] = country.id
112
+ end
113
+ elsif match_metro( value ) do |metro|
114
+ new_attributes[ :metro_id ] = metro.id
115
+ end
116
+ elsif match_metro_pop( value ) do |num| # e.g m: 1 444 444
117
+ new_attributes_metro[ :pop ] = num # note: gets added to **metro** attributes; triggers auto-added metro record
118
+ end
119
+ elsif match_metro_flag( value ) do |_| # metro(politan area)
120
+ city_class = Metro # turn off default c|city flag; make it m|metro only
121
+ end
122
+ elsif match_city( value ) do |city| # parent city for district
123
+ new_attributes[ :city_id ] = city.id
124
+ city_class = District # turn off default c|city flag; make it d|district only
125
+ end
126
+ elsif match_km_squared( value ) do |num| # allow numbers like 453 km²
127
+ value_numbers << num
128
+ end
129
+ elsif match_number( value ) do |num| # numeric (nb: can use any _ or spaces inside digits e.g. 1_000_000 or 1 000 000)
130
+ value_numbers << num
131
+ end
132
+ elsif value =~ /#{CITY_CODE_PATTERN}/ ## assume three-letter code
133
+ new_attributes[ :code ] = value
134
+ elsif (values.size==(index+1)) && is_taglist?( value ) # tags must be last entry
135
+ logger.debug " found tags: >>#{value}<<"
136
+ value_tag_keys += find_tags( value )
137
+ else
138
+ # issue warning: unknown type for value
139
+ logger.warn "unknown type for value >#{value}<"
140
+ end
141
+ end # each value
142
+
143
+ if value_numbers.size > 0
144
+ new_attributes[ :pop ] = value_numbers[0] # assume first number is pop for cities
145
+ new_attributes[ :area ] = value_numbers[1]
146
+ end
147
+
148
+
149
+ #######
150
+ ## auto add metro if used M:/m: shortcut
151
+ if new_attributes_metro.empty? == false
152
+ new_attributes_metro[:key] = new_attributes[:key]
153
+ ## todo/fix: use title or name?
154
+ new_attributes_metro[:name] = new_attributes[:name] || new_attributes[:title]
155
+ new_attributes_metro[:country_id] = new_attributes[:country_id]
156
+
157
+ metro_rec = Metro.find_by_key( new_attributes_metro[ :key ] )
158
+
159
+ if metro_rec.present?
160
+ logger.debug "(auto-)update Metro #{metro_rec.id}-#{metro_rec.key}:"
161
+ else
162
+ logger.debug "(auto-)create Metro:"
163
+ metro_rec = Metro.new
164
+ end
165
+
166
+ logger.debug new_attributes_metro.to_json
167
+
168
+ metro_rec.update_attributes!( new_attributes_metro )
169
+ new_attributes[:metro_id] = metro_rec.id
170
+ end
171
+
172
+
173
+ rec = city_class.find_by_key( new_attributes[ :key ] )
174
+
175
+ if rec.present?
176
+ logger.debug "update #{city_class.name} #{rec.id}-#{rec.key}:"
177
+ else
178
+ logger.debug "create #{city_class.name}:"
179
+ rec = city_class.new
180
+ end
181
+
182
+ logger.debug new_attributes.to_json
183
+
184
+ rec.update_attributes!( new_attributes )
185
+
186
+
187
+ ##################
188
+ ## add taggings
189
+
190
+ ## todo/fix: reuse - move add taggings into method etc.
191
+
192
+ if value_tag_keys.size > 0
193
+
194
+ if opts[:skip_tags].present?
195
+ logger.debug " skipping add taggings (flag skip_tag)"
196
+ else
197
+ value_tag_keys.uniq! # remove duplicates
198
+ logger.debug " adding #{value_tag_keys.size} taggings: >>#{value_tag_keys.join('|')}<<..."
199
+
200
+ ### fix/todo: check tag_ids and only update diff (add/remove ids)
201
+
202
+ value_tag_keys.each do |key|
203
+ tag = Tag.find_by_key( key )
204
+ if tag.nil? # create tag if it doesn't exit
205
+ logger.debug " creating tag >#{key}<"
206
+ tag = Tag.create!( key: key )
207
+ end
208
+ rec.tags << tag
209
+ end
210
+ end
211
+ end
212
+
213
+ rec
214
+ end # method create_or_update_from_values
215
+
216
+
217
+ end # class Cities
218
+
219
+ end # module Model
220
+ end # module WorldDb
@@ -8,6 +8,15 @@ class Continent < ActiveRecord::Base
8
8
  belongs_to :place, class_name: 'Place', foreign_key: 'place_id'
9
9
  has_many :countries
10
10
 
11
+ ## begin compat
12
+ def title() name; end
13
+ def title=(value) self.name = value; end
14
+
15
+ def synonyms() alt_names; end
16
+ def synonyms=(value) self.alt_names = value; end
17
+ ## end
18
+
19
+
11
20
  # NB: allow dots in keys e.g. concacaf.naf etc.
12
21
 
13
22
  before_create :on_create
@@ -12,7 +12,7 @@ class Country < ActiveRecord::Base
12
12
 
13
13
  # NB: use extend - is_<type>? become class methods e.g. self.is_<type>? for use in
14
14
  # self.create_or_update_from_values
15
- extend TextUtils::ValueHelper # e.g. self.is_year?, self.is_region?, self.is_address?, is_taglist? etc.
15
+ extend TextUtils::ValueHelper # e.g. self.is_year?, self.is_address?, is_taglist? etc.
16
16
 
17
17
 
18
18
  self.table_name = 'countries'
@@ -23,14 +23,14 @@ class Country < ActiveRecord::Base
23
23
  has_many :usages
24
24
  has_many :langs, :through => :usages # lang(uage)s through usages (that is, countries_langs) join table
25
25
 
26
- has_many :regions, class_name: 'Region', foreign_key: 'country_id'
26
+ has_many :states, class_name: 'State', foreign_key: 'country_id'
27
27
  has_many :cities, class_name: 'City', foreign_key: 'country_id'
28
-
28
+
29
29
  ## self referencing hierachy within countries e.g. EU > GB > EN
30
30
  belongs_to :parent, class_name: 'Country', foreign_key: 'country_id'
31
31
  has_many :countries, class_name: 'Country', foreign_key: 'country_id'
32
32
  ### recursive self-reference - use node??
33
- ## has_many :nodes, class_name: 'Region', foregin_key: 'region_id'
33
+ ## has_many :nodes, class_name: 'State', foregin_key: 'state_id'
34
34
 
35
35
 
36
36
  has_many_tags
@@ -38,6 +38,14 @@ class Country < ActiveRecord::Base
38
38
  validates :key, format: { with: /#{COUNTRY_KEY_PATTERN}/, message: COUNTRY_KEY_PATTERN_MESSAGE }
39
39
  validates :code, format: { with: /#{COUNTRY_CODE_PATTERN}/, message: COUNTRY_CODE_PATTERN_MESSAGE }
40
40
 
41
+ ## begin compat
42
+ def title() name; end
43
+ def title=(value) self.name = value; end
44
+
45
+ def synonyms() alt_names; end
46
+ def synonyms=(value) self.alt_names = value; end
47
+ ## end
48
+
41
49
 
42
50
  scope :by_key, ->{ order( 'key asc' ) } # order by key (a-z)
43
51
  scope :by_name, ->{ order( 'name asc' ) } # order by name (a-z)
@@ -167,6 +175,15 @@ class Country < ActiveRecord::Base
167
175
  end
168
176
 
169
177
 
178
+
179
+ def self.parse( *args )
180
+ ## remove (extract) attribs hash (if last arg is a hash n present)
181
+ more_attribs = args.last.is_a?(Hash) ? args.pop : {} ## extract_options!
182
+ values = args
183
+
184
+ self.create_or_update_from_values( values, more_attribs )
185
+ end
186
+
170
187
  def self.create_or_update_from_values( values, more_attribs={} )
171
188
 
172
189
  ## key & title
@@ -15,15 +15,24 @@ Tagging = TagDb::Model::Tagging
15
15
  Tag = TagDb::Model::Tag
16
16
 
17
17
 
18
- class Name < ActiveRecord::Base ; end
19
- class Place < ActiveRecord::Base ; end
20
- class Continent < ActiveRecord::Base ; end
21
- class Country < ActiveRecord::Base ; end
22
- class Region < ActiveRecord::Base ; end
23
- class City < ActiveRecord::Base ; end
18
+ class Name < ActiveRecord::Base ; end
19
+ class Place < ActiveRecord::Base ; end
20
+ class Continent < ActiveRecord::Base ; end
21
+ class Country < ActiveRecord::Base ; end
24
22
 
25
- class Lang < ActiveRecord::Base ; end
26
- class Usage < ActiveRecord::Base ; end
23
+ class CityBase < ActiveRecord::Base ; end
24
+ class Metro < CityBase ; end
25
+ class City < CityBase ; end
26
+ class District < CityBase ; end
27
+
28
+ class StateBase < ActiveRecord::Base ; end
29
+ class State < StateBase ; end ## ADM1
30
+ class Part < StateBase ; end ## x /ADM2
31
+ class County < StateBase ; end ## ADM2/ADM3
32
+ class Muni < StateBase ; end ## ADM3/ADM4
33
+
34
+ class Lang < ActiveRecord::Base ; end
35
+ class Usage < ActiveRecord::Base ; end
27
36
 
28
37
  class CountryCode < ActiveRecord::Base ; end
29
38
 
@@ -45,8 +54,15 @@ Name = WorldDb::Model::Name
45
54
  Place = WorldDb::Model::Place
46
55
  Continent = WorldDb::Model::Continent
47
56
  Country = WorldDb::Model::Country
48
- Region = WorldDb::Model::Region
57
+
58
+ State = WorldDb::Model::State ## ADM1
59
+ Part = WorldDb::Model::Part ## x /ADM2
60
+ County = WorldDb::Model::County ## ADM2/ADM3
61
+ Muni = WorldDb::Model::Muni ## ADM3/ADM4
62
+
63
+ Metro = WorldDb::Model::Metro
49
64
  City = WorldDb::Model::City
65
+ District = WorldDb::Model::District
50
66
 
51
67
  Lang = WorldDb::Model::Lang
52
68
  Usage = WorldDb::Model::Usage
@@ -11,6 +11,12 @@ class Lang < ActiveRecord::Base
11
11
 
12
12
  validates :key, format: { with: /#{LANG_KEY_PATTERN}/, message: LANG_KEY_PATTERN_MESSAGE }
13
13
 
14
+ ## begin compat
15
+ def title() name; end
16
+ def title=(value) self.name = value; end
17
+ ## end
18
+
19
+
14
20
  end # class Lang
15
21
 
16
22
  end # module Model
@@ -6,7 +6,7 @@ module WorldDb
6
6
  class Place < ActiveRecord::Base
7
7
 
8
8
  ## todo: depending on type
9
- ## has_one continent, country, region, city etc.
9
+ ## has_one continent, country, state, city etc.
10
10
 
11
11
  end # class Place
12
12