worlddb 1.6.4 → 1.6.5

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.
data/.gemtest ADDED
File without changes
data/Manifest.txt CHANGED
@@ -23,3 +23,5 @@ lib/worlddb/schema.rb
23
23
  lib/worlddb/stats.rb
24
24
  lib/worlddb/utils.rb
25
25
  lib/worlddb/version.rb
26
+ test/helper.rb
27
+ test/test_values.rb
@@ -3,6 +3,14 @@
3
3
  module WorldDb::Models
4
4
 
5
5
  class City < ActiveRecord::Base
6
+
7
+ extend WorldDb::TagHelper # will add self.find_tags, self.find_tags_in_hash!, etc.
8
+
9
+ # NB: use extend - is_<type>? become class methods e.g. self.is_<type>? for use in
10
+ # self.create_or_update_from_values
11
+ extend TextUtils::ValueHelper # e.g. is_year?, is_region?, is_address?, is_taglist? etc.
12
+
13
+
6
14
  self.table_name = 'cities'
7
15
 
8
16
  belongs_to :country, :class_name => 'Country', :foreign_key => 'country_id'
@@ -109,6 +117,152 @@ class City < ActiveRecord::Base
109
117
  end # each city
110
118
  end
111
119
 
120
+
121
+ def self.create_or_update_from_titles( titles, more_attributes = {} ) # ary of titles e.g. ['Wien', 'Graz'] etc.
122
+
123
+ # fix: add/configure logger for ActiveRecord!!!
124
+ logger = LogKernel::Logger.root
125
+
126
+ titles.each do |title|
127
+
128
+ new_attributes = {}
129
+ key = TextUtils.title_to_key( title ) # auto generate key from title
130
+
131
+ # check if it exists
132
+ # todo/fix: add country_id for lookup?
133
+ city = City.find_by_key( key )
134
+ if city.present?
135
+ logger.debug "update city #{city.id}-#{city.key}:"
136
+ else
137
+ logger.debug "create city:"
138
+ city = City.new
139
+ new_attributes[ :key ] = key
140
+ end
141
+
142
+ new_attributes[ :title ] = title
143
+
144
+ ## merge in passed in attribes (e.g. country_id etc.)
145
+ new_attributes.merge!( more_attributes )
146
+
147
+ logger.debug new_attributes.to_json
148
+
149
+ city.update_attributes!( new_attributes )
150
+
151
+ ### todo/fix: add captial ref to country/region
152
+ end # each city
153
+ end # method create_or_update_from_titles
154
+
155
+
156
+ def self.create_or_update_from_values( new_attributes, values, opts={} )
157
+
158
+ ## opts e.g. :skip_tags true|false
159
+
160
+ ## fix: add/configure logger for ActiveRecord!!!
161
+ logger = LogKernel::Logger.root
162
+
163
+ value_numbers = []
164
+ value_tag_keys = []
165
+
166
+ ### check for "default" tags - that is, if present new_attributes[:tags] remove from hash
167
+ value_tag_keys += find_tags_in_hash!( new_attributes )
168
+
169
+ new_attributes[ :c ] = true # assume city type by default (use metro,district to change in fixture)
170
+
171
+ ## check for optional values
172
+ values.each_with_index do |value,index|
173
+ if value =~ /^region:/ ## region:
174
+ value_region_key = value[7..-1] ## cut off region: prefix
175
+ ## NB: requires country_id to make unique!
176
+ value_region = Region.find_by_key_and_country_id!( value_region_key, new_attributes[:country_id] )
177
+ new_attributes[ :region_id ] = value_region.id
178
+ elsif value =~ /^metro$/ ## metro(politan area)
179
+ new_attributes[ :c ] = false # turn off default c|city flag; make it m|metro only
180
+ new_attributes[ :m ] = true
181
+ elsif value =~ /^country:/ ## country:
182
+ value_country_key = value[8..-1] ## cut off country: prefix
183
+ value_country = Country.find_by_key!( value_country_key )
184
+ new_attributes[ :country_id ] = value_country.id
185
+ elsif value =~ /^metro:/ ## metro:
186
+ value_city_key = value[6..-1] ## cut off metro: prefix
187
+ value_city = City.find_by_key!( value_city_key )
188
+ new_attributes[ :city_id ] = value_city.id
189
+ elsif value =~ /^city:/ ## city:
190
+ value_city_key = value[5..-1] ## cut off city: prefix
191
+ value_city = City.find_by_key!( value_city_key )
192
+ new_attributes[ :city_id ] = value_city.id
193
+ new_attributes[ :c ] = false # turn off default c|city flag; make it d|district only
194
+ new_attributes[ :d ] = true
195
+ elsif value =~ /^m:/ ## m:
196
+ value_popm_str = value[2..-1] ## cut off m: prefix
197
+ value_popm = value_popm_str.gsub(/[ _]/, '').to_i
198
+ new_attributes[ :popm ] = value_popm
199
+ new_attributes[ :m ] = true # auto-mark city as m|metro too
200
+ elsif is_region?( value ) ## assume region code e.g. TX for city
201
+ value_region = Region.find_by_key_and_country_id!( value.downcase, new_attributes[:country_id] )
202
+ new_attributes[ :region_id ] = value_region.id
203
+ elsif value =~ /^[A-Z]{2,3}$/ ## assume two or three-letter code
204
+ new_attributes[ :code ] = value
205
+ elsif value =~ /^([0-9][0-9 _]+[0-9]|[0-9]{1,2})(?:\s*(?:km2|km²)\s*)$/
206
+ ## allow numbers like 453 km²
207
+ value_numbers << value.gsub( 'km2', '').gsub( 'km²', '' ).gsub(/[ _]/, '').to_i
208
+ elsif value =~ /^([0-9][0-9 _]+[0-9])|([0-9]{1,2})$/ ## numeric (nb: can use any _ or spaces inside digits e.g. 1_000_000 or 1 000 000)
209
+ value_numbers << value.gsub(/[ _]/, '').to_i
210
+ elsif (values.size==(index+1)) && is_taglist?( value ) # tags must be last entry
211
+ logger.debug " found tags: >>#{value}<<"
212
+ value_tag_keys += find_tags( value )
213
+ else
214
+ # issue warning: unknown type for value
215
+ logger.warn "unknown type for value >#{value}<"
216
+ end
217
+ end # each value
218
+
219
+ if value_numbers.size > 0
220
+ new_attributes[ :pop ] = value_numbers[0] # assume first number is pop for cities
221
+ new_attributes[ :area ] = value_numbers[1]
222
+ end # if value_numbers.size > 0
223
+
224
+ rec = City.find_by_key( new_attributes[ :key ] )
225
+
226
+ if rec.present?
227
+ logger.debug "update City #{rec.id}-#{rec.key}:"
228
+ else
229
+ logger.debug "create City:"
230
+ rec = City.new
231
+ end
232
+
233
+ logger.debug new_attributes.to_json
234
+
235
+ rec.update_attributes!( new_attributes )
236
+
237
+ ##################
238
+ ## add taggings
239
+
240
+ ## todo/fix: reuse - move add taggings into method etc.
241
+
242
+ if value_tag_keys.size > 0
243
+
244
+ if opts[:skip_tags].present?
245
+ logger.debug " skipping add taggings (flag skip_tag)"
246
+ else
247
+ value_tag_keys.uniq! # remove duplicates
248
+ logger.debug " adding #{value_tag_keys.size} taggings: >>#{value_tag_keys.join('|')}<<..."
249
+
250
+ ### fix/todo: check tag_ids and only update diff (add/remove ids)
251
+
252
+ value_tag_keys.each do |key|
253
+ tag = Tag.find_by_key( key )
254
+ if tag.nil? # create tag if it doesn't exit
255
+ logger.debug " creating tag >#{key}<"
256
+ tag = Tag.create!( key: key )
257
+ end
258
+ rec.tags << tag
259
+ end
260
+ end
261
+ end
262
+
263
+ rec
264
+ end # method create_or_update_from_values
265
+
112
266
  end # class Cities
113
267
 
114
268
  end # module WorldDb::Models
@@ -3,6 +3,14 @@
3
3
  module WorldDb::Models
4
4
 
5
5
  class Country < ActiveRecord::Base
6
+
7
+ extend WorldDb::TagHelper # will add self.find_tags, self.find_tags_in_hash!, etc.
8
+
9
+ # NB: use extend - is_<type>? become class methods e.g. self.is_<type>? for use in
10
+ # self.create_or_update_from_values
11
+ extend TextUtils::ValueHelper # e.g. is_year?, is_region?, is_address?, is_taglist? etc.
12
+
13
+
6
14
  self.table_name = 'countries'
7
15
 
8
16
  belongs_to :continent, :class_name => 'Continent', :foreign_key => 'continent_id'
@@ -95,6 +103,153 @@ class Country < ActiveRecord::Base
95
103
  end # each country
96
104
  end
97
105
 
106
+
107
+ def self.create_or_update_from_values( new_attributes, values, opts={} )
108
+
109
+ ## opts e.g. :skip_tags true|false
110
+
111
+ ## fix: add/configure logger for ActiveRecord!!!
112
+ logger = LogKernel::Logger.root
113
+
114
+ value_numbers = []
115
+ value_tag_keys = []
116
+ value_cities = []
117
+
118
+ ### check for "default" tags - that is, if present new_attributes[:tags] remove from hash
119
+ value_tag_keys += find_tags_in_hash!( new_attributes )
120
+
121
+
122
+ new_attributes[ :c ] = true # assume country type by default (use supra,depend to change)
123
+
124
+ ## check for optional values
125
+ values.each_with_index do |value,index|
126
+ if value =~ /^supra$/ ## supra(national)
127
+ new_attributes[ :c ] = false # turn off default c|country flag; make it s|supra only
128
+ new_attributes[ :s ] = true
129
+ ## auto-add tag supra
130
+ value_tag_keys << 'supra'
131
+ elsif value =~ /^supra:/ ## supra:
132
+ value_country_key = value[6..-1] ## cut off supra: prefix
133
+ value_country = Country.find_by_key!( value_country_key )
134
+ new_attributes[ :country_id ] = value_country.id
135
+ elsif value =~ /^country:/ ## country:
136
+ value_country_key = value[8..-1] ## cut off country: prefix
137
+ value_country = Country.find_by_key!( value_country_key )
138
+ new_attributes[ :country_id ] = value_country.id
139
+ new_attributes[ :c ] = false # turn off default c|country flag; make it d|depend only
140
+ new_attributes[ :d ] = true
141
+ ## auto-add tag supra
142
+ value_tag_keys << 'territory' # rename tag to dependency? why? why not?
143
+ elsif value =~ /^[A-Z]{2,3}$/ ## assume two or three-letter code
144
+ new_attributes[ :code ] = value
145
+ elsif value =~ /^([0-9][0-9 _]+[0-9]|[0-9]{1,2})(?:\s*(?:km2|km²)\s*)$/
146
+ ## allow numbers like 453 km²
147
+ value_numbers << value.gsub( 'km2', '').gsub( 'km²', '' ).gsub(/[ _]/, '').to_i
148
+ elsif value =~ /^([0-9][0-9 _]+[0-9])|([0-9]{1,2})$/ ## numeric (nb: can use any _ or spaces inside digits e.g. 1_000_000 or 1 000 000)
149
+ value_numbers << value.gsub(/[ _]/, '').to_i
150
+ elsif (values.size==(index+1)) && is_taglist?( value ) # tags must be last entry
151
+ logger.debug " found tags: >>#{value}<<"
152
+ value_tag_keys += find_tags( value )
153
+ else
154
+
155
+ ### assume it is the capital city - mark it for auto add
156
+ value_cities << value
157
+ next
158
+
159
+ # issue warning: unknown type for value
160
+ # logger.warn "unknown type for value >#{value}<"
161
+ end
162
+ end # each value
163
+
164
+ if value_numbers.size > 0
165
+ new_attributes[ :area ] = value_numbers[0]
166
+ new_attributes[ :pop ] = value_numbers[1]
167
+ end
168
+
169
+ =begin
170
+ # auto-add tags
171
+ area = value_numbers[0]
172
+ pop = value_numbers[1]
173
+
174
+ # categorize into brackets
175
+ if area >= 1_000_000
176
+ value_tag_keys << 'area_1_000_000_n_up'
177
+ elsif area >= 100_000
178
+ value_tag_keys << 'area_100_000_to_1_000_000'
179
+ elsif area >= 1000
180
+ value_tag_keys << 'area_1_000_to_100_000'
181
+ else
182
+ value_tag_keys << 'area_1_000_n_less' # microstate
183
+ end
184
+
185
+ # include all
186
+ value_tag_keys << 'area_100_000_n_up' if area >= 100_000
187
+ value_tag_keys << 'area_1_000_n_up' if area >= 1_000
188
+
189
+
190
+ # categorize into brackets
191
+ if pop >= 100_000_000
192
+ value_tag_keys << 'pop_100m_n_up'
193
+ elsif pop >= 10_000_000
194
+ value_tag_keys << 'pop_10m_to_100m'
195
+ elsif pop >= 1_000_000
196
+ value_tag_keys << 'pop_1m_to_10m'
197
+ else
198
+ value_tag_keys << 'pop_1m_n_less'
199
+ end
200
+
201
+ # include all
202
+ value_tag_keys << 'pop_10m_n_up' if pop >= 10_000_000
203
+ value_tag_keys << 'pop_1m_n_up' if pop >= 1_000_000
204
+ =end
205
+
206
+ rec = Country.find_by_key( new_attributes[ :key ] )
207
+
208
+ if rec.present?
209
+ logger.debug "update Country #{rec.id}-#{rec.key}:"
210
+ else
211
+ logger.debug "create Country:"
212
+ rec = Country.new
213
+ end
214
+
215
+ logger.debug new_attributes.to_json
216
+
217
+ rec.update_attributes!( new_attributes )
218
+
219
+ #################
220
+ ## auto add capital cities
221
+
222
+ City.create_or_update_from_titles( value_cities, country_id: rec.id )
223
+
224
+ ##################
225
+ ## add taggings
226
+
227
+ if value_tag_keys.size > 0
228
+
229
+ if opts[:skip_tags].present?
230
+ logger.debug " skipping add taggings (flag skip_tag)"
231
+ else
232
+ value_tag_keys.uniq! # remove duplicates
233
+ logger.debug " adding #{value_tag_keys.size} taggings: >>#{value_tag_keys.join('|')}<<..."
234
+
235
+ ### fix/todo: check tag_ids and only update diff (add/remove ids)
236
+
237
+ value_tag_keys.each do |key|
238
+ tag = Tag.find_by_key( key )
239
+ if tag.nil? # create tag if it doesn't exit
240
+ logger.debug " creating tag >#{key}<"
241
+ tag = Tag.create!( key: key )
242
+ end
243
+ rec.tags << tag
244
+ end
245
+ end
246
+ end
247
+
248
+ rec
249
+
250
+ end # method create_or_update_from_values
251
+
252
+
98
253
  end # class Country
99
254
 
100
255
 
@@ -1,7 +1,16 @@
1
+ # encoding: UTF-8
2
+
1
3
  module WorldDb::Models
2
4
 
3
5
  class Region < ActiveRecord::Base
4
6
 
7
+ extend WorldDb::TagHelper # will add self.find_tags, self.find_tags_in_hash!, etc.
8
+
9
+ # NB: use extend - is_<type>? become class methods e.g. self.is_<type>? for use in
10
+ # self.create_or_update_from_values
11
+ extend TextUtils::ValueHelper # e.g. is_year?, is_region?, is_address?, is_taglist? etc.
12
+
13
+
5
14
  belongs_to :country, :class_name => 'Country', :foreign_key => 'country_id'
6
15
 
7
16
  has_many :cities, :class_name => 'City', :foreign_key => 'region_id'
@@ -9,7 +18,7 @@ class Region < ActiveRecord::Base
9
18
  has_many :taggings, :as => :taggable
10
19
  has_many :tags, :through => :taggings
11
20
 
12
- validates :key, :format => { :with => /^[a-z]{2,}$/, :message => 'expected two or more lowercase letters a-z' }
21
+ validates :key, :format => { :with => /^[a-z]+$/, :message => 'expected one or more lowercase letters a-z' }
13
22
  validates :code, :format => { :with => /^[A-Z_]{2,3}$/, :message => 'expected two or three uppercase letters A-Z (and _)' }, :allow_nil => true
14
23
 
15
24
 
@@ -48,6 +57,109 @@ class Region < ActiveRecord::Base
48
57
  end # each region
49
58
  end
50
59
 
60
+
61
+ def self.create_or_update_from_values( new_attributes, values, opts={} )
62
+
63
+ ## opts e.g. :skip_tags true|false
64
+
65
+ ## fix: add/configure logger for ActiveRecord!!!
66
+ logger = LogKernel::Logger.root
67
+
68
+ value_numbers = []
69
+ value_tag_keys = []
70
+ value_cities = []
71
+
72
+ ### check for "default" tags - that is, if present new_attributes[:tags] remove from hash
73
+ value_tag_keys += find_tags_in_hash!( new_attributes )
74
+
75
+ ## check for optional values
76
+ values.each_with_index do |value,index|
77
+ if value =~ /^country:/ ## country:
78
+ value_country_key = value[8..-1] ## cut off country: prefix
79
+ value_country = Country.find_by_key!( value_country_key )
80
+ new_attributes[ :country_id ] = value_country.id
81
+ elsif value =~ /^[A-Z]{2,3}$/ ## assume two or three-letter code
82
+ new_attributes[ :code ] = value
83
+ elsif value =~ /^([0-9][0-9 _]+[0-9]|[0-9]{1,2})(?:\s*(?:km2|km²)\s*)$/
84
+ ## allow numbers like 453 km²
85
+ value_numbers << value.gsub( 'km2', '').gsub( 'km²', '' ).gsub(/[ _]/, '').to_i
86
+ elsif value =~ /^([0-9][0-9 _]+[0-9])|([0-9]{1,2})$/ ## numeric (nb: can use any _ or spaces inside digits e.g. 1_000_000 or 1 000 000)
87
+ value_numbers << value.gsub(/[ _]/, '').to_i
88
+ elsif (values.size==(index+1)) && is_taglist?( value ) # tags must be last entry
89
+ logger.debug " found tags: >>#{value}<<"
90
+ value_tag_keys += find_tags( value )
91
+ else
92
+
93
+ ### assume it is the capital city - mark it for auto add
94
+ value_cities << value
95
+ next
96
+
97
+ # issue warning: unknown type for value
98
+ # logger.warn "unknown type for value >#{value}<"
99
+ end
100
+ end # each value
101
+
102
+ if value_numbers.size > 0
103
+ new_attributes[ :area ] = value_numbers[0]
104
+ new_attributes[ :pop ] = value_numbers[1]
105
+ end # if value_numbers.size > 0
106
+
107
+ ## todo: assert that country_id is present/valid, that is, NOT null
108
+ rec = Region.find_by_key_and_country_id( new_attributes[ :key ], new_attributes[ :country_id] )
109
+
110
+ if rec.present?
111
+ logger.debug "update Region #{rec.id}-#{rec.key}:"
112
+ else
113
+ logger.debug "create Region:"
114
+ rec = Region.new
115
+ end
116
+
117
+ logger.debug new_attributes.to_json
118
+
119
+ rec.update_attributes!( new_attributes )
120
+
121
+ #################
122
+ # auto add capital cities
123
+
124
+ City.create_or_update_from_titles( value_cities,
125
+ region_id: rec.id,
126
+ country_id: rec.country_id )
127
+
128
+ ### todo/fix: add captial ref to country/region
129
+
130
+
131
+ ##################
132
+ # add taggings
133
+
134
+ ## todo/fix: reuse - move add taggings into method etc.
135
+
136
+ if value_tag_keys.size > 0
137
+
138
+ if opts[:skip_tags].present?
139
+ logger.debug " skipping add taggings (flag skip_tag)"
140
+ else
141
+ value_tag_keys.uniq! # remove duplicates
142
+ logger.debug " adding #{value_tag_keys.size} taggings: >>#{value_tag_keys.join('|')}<<..."
143
+
144
+ ### fix/todo: check tag_ids and only update diff (add/remove ids)
145
+
146
+ value_tag_keys.each do |key|
147
+ tag = Tag.find_by_key( key )
148
+ if tag.nil? # create tag if it doesn't exit
149
+ logger.debug " creating tag >#{key}<"
150
+ tag = Tag.create!( key: key )
151
+ end
152
+ rec.tags << tag
153
+ end
154
+ end
155
+ end
156
+
157
+ rec
158
+
159
+ end # method create_or_update_from_values
160
+
161
+
162
+
51
163
  end # class Region
52
164
 
53
165
  end # module WorldDb::Models
@@ -2,6 +2,40 @@
2
2
 
3
3
  module WorldDb
4
4
 
5
+
6
+ ### fix: move to textutils
7
+ ## PlusReaderWrapper find a better name than Plus?
8
+ #
9
+ # todo: also add a ValuesReaderPlus and use it
10
+
11
+ class HashReaderPlus
12
+ include LogUtils::Logging
13
+
14
+ def initialize( name, include_path )
15
+ @name = name
16
+ @include_path = include_path
17
+ end
18
+
19
+ attr_reader :name
20
+ attr_reader :include_path
21
+
22
+ def each
23
+ path = "#{include_path}/#{name}.yml"
24
+ reader = HashReader.new( path )
25
+
26
+ logger.info "parsing data '#{name}' (#{path})..."
27
+
28
+ reader.each do |key, value|
29
+ yield( key, value )
30
+ end
31
+ Prop.create_from_fixture!( name, path )
32
+ end
33
+
34
+ end # class HashReaderPlus
35
+
36
+
37
+
38
+
5
39
  class Reader
6
40
 
7
41
  include LogUtils::Logging
@@ -114,22 +148,16 @@ class Reader
114
148
 
115
149
 
116
150
  def load_regions_xxx( country_key, xxx, name )
117
- path = "#{include_path}/#{name}.yml"
118
-
119
- logger.info "parsing data '#{name}' (#{path})..."
120
-
121
151
  country = Country.find_by_key!( country_key )
122
152
  logger.debug "Country #{country.key} >#{country.title} (#{country.code})<"
123
153
 
124
- reader = HashReader.new( path )
154
+ reader = HashReaderPlus.new( name, include_path )
125
155
 
126
156
  reader.each do |key, value|
127
157
  region = Region.find_by_country_id_and_key!( country.id, key )
128
158
  region.send( "#{xxx}=", value )
129
159
  region.save!
130
160
  end
131
-
132
- Prop.create_from_fixture!( name, path )
133
161
  end
134
162
 
135
163
 
@@ -141,27 +169,8 @@ class Reader
141
169
  end
142
170
 
143
171
 
144
-
145
- def with_path_for( name )
146
- ## todo: find a better name?
147
- # e.g. find_path_for or open_fixture_for ??
148
-
149
- path = "#{include_path}/#{name}.yml"
150
-
151
- logger.info "parsing data '#{name}' (#{path})..."
152
-
153
- yield( path )
154
-
155
- Prop.create_from_fixture!( name, path )
156
- end
157
-
158
-
159
172
  def load_continent_refs( name )
160
- path = "#{include_path}/#{name}.yml"
161
-
162
- logger.info "parsing data '#{name}' (#{path})..."
163
-
164
- reader = HashReader.new( path )
173
+ reader = HashReaderPlus.new( name, include_path )
165
174
 
166
175
  reader.each do |key, value|
167
176
  country = Country.find_by_key!( key )
@@ -169,10 +178,9 @@ class Reader
169
178
  country.continent_id = continent.id
170
179
  country.save!
171
180
  end
172
-
173
- Prop.create_from_fixture!( name, path )
174
181
  end
175
182
 
183
+
176
184
  def load_continent_defs( name, more_values={} )
177
185
  path = "#{include_path}/#{name}.txt"
178
186
 
@@ -208,11 +216,9 @@ class Reader
208
216
 
209
217
  def load_langs( name )
210
218
 
211
- with_path_for( name ) do |path|
212
-
213
- reader = HashReader.new( path )
219
+ reader = HashReaderPlus.new( name, include_path )
214
220
 
215
- reader.each do |key, value|
221
+ reader.each do |key, value|
216
222
 
217
223
  logger.debug "adding lang >>#{key}<< >>#{value}<<..."
218
224
 
@@ -236,17 +242,14 @@ class Reader
236
242
  logger.debug lang_attribs.to_json
237
243
 
238
244
  lang.update_attributes!( lang_attribs )
239
- end # each key,value
240
- end # with_path_for
245
+ end # each key,value
241
246
 
242
247
  end # method load_langs
243
248
 
244
249
 
245
250
  def load_tags( name, more_values={} )
246
251
 
247
- with_path_for( name ) do |path|
248
-
249
- reader = HashReader.new( path )
252
+ reader = HashReaderPlus.new( name, include_path )
250
253
 
251
254
  grade = 1
252
255
 
@@ -291,19 +294,13 @@ class Reader
291
294
 
292
295
  tag.update_attributes!( tag_attribs )
293
296
  end
294
- end # each key,value
295
-
296
- end # with_path_for
297
-
297
+ end # each key,value
298
+
298
299
  end # method load_tags
299
300
 
300
301
 
301
302
  def load_usages( name )
302
- path = "#{include_path}/#{name}.yml"
303
-
304
- logger.info "parsing data '#{name}' (#{path})..."
305
-
306
- reader = HashReader.new( path )
303
+ reader = HashReaderPlus.new( name, include_path )
307
304
 
308
305
  reader.each do |key, value|
309
306
  logger.debug " adding langs >>#{value}<<to country >>#{key}<<"
@@ -322,28 +319,19 @@ class Reader
322
319
  Usage.create!( country_id: country.id, lang_id: lang.id, official: true, minor: false )
323
320
  end
324
321
  end
325
-
326
- Prop.create_from_fixture!( name, path )
327
322
  end
328
323
 
329
324
 
330
325
  def load_xxx( xxx, name )
331
- path = "#{include_path}/#{name}.yml"
332
-
333
- logger.info "parsing data '#{name}' (#{path})..."
334
-
335
- reader = HashReader.new( path )
326
+ reader = HashReaderPlus.new( name, include_path )
336
327
 
337
328
  reader.each do |key, value|
338
329
  country = Country.find_by_key!( key )
339
330
  country.send( "#{xxx}=", value )
340
331
  country.save!
341
332
  end
342
-
343
- Prop.create_from_fixture!( name, path )
344
333
  end
345
334
 
346
-
347
335
  private
348
336
  def load_fixtures_for( clazz, name, more_values={} ) # load from file system
349
337
  path = "#{include_path}/#{name}.txt"
@@ -352,267 +340,13 @@ private
352
340
 
353
341
  reader = ValuesReader.new( path, more_values )
354
342
 
355
- load_fixtures_worker_for( clazz, reader )
343
+ reader.each_line do |new_attributes, values|
344
+ opts = { skip_tags: skip_tags? }
345
+ clazz.create_or_update_from_values( new_attributes, values, opts )
346
+ end
356
347
 
357
348
  Prop.create_from_fixture!( name, path )
358
349
  end
359
-
360
-
361
- def load_fixtures_worker_for( clazz, reader )
362
-
363
- ## NB: assumes active activerecord db connection
364
- ##
365
-
366
- reader.each_line do |attribs, values|
367
-
368
- value_numbers = []
369
- value_tag_keys = []
370
- value_cities = []
371
-
372
- ### check for "default" tags - that is, if present attribs[:tags] remove from hash
373
-
374
- if attribs[:tags].present?
375
- more_tag_keys = attribs[:tags].split('|')
376
- attribs.delete(:tags)
377
-
378
- ## unify; replace _w/ space; remove leading n trailing whitespace
379
- more_tag_keys = more_tag_keys.map do |key|
380
- key = key.gsub( '_', ' ' )
381
- key = key.strip
382
- key
383
- end
384
-
385
- value_tag_keys += more_tag_keys
386
- end
387
-
388
-
389
- if clazz == City
390
- attribs[ :c ] = true # assume city type by default (use metro,district to change in fixture)
391
- elsif clazz == Country
392
- attribs[ :c ] = true # assume country type by default (use supra,depend to change)
393
- end
394
-
395
- ## check for optional values
396
- values.each_with_index do |value,index|
397
- if value =~ /^region:/ ## region:
398
- value_region_key = value[7..-1] ## cut off region: prefix
399
- ## NB: requires country_id to make unique!
400
- value_region = Region.find_by_key_and_country_id!( value_region_key, attribs[:country_id] )
401
- attribs[ :region_id ] = value_region.id
402
- elsif value =~ /^metro$/ ## metro(politan area)
403
- attribs[ :c ] = false # turn off default c|city flag; make it m|metro only
404
- attribs[ :m ] = true
405
- elsif value =~ /^supra$/ ## supra(national)
406
- attribs[ :c ] = false # turn off default c|country flag; make it s|supra only
407
- attribs[ :s ] = true
408
- ## auto-add tag supra
409
- value_tag_keys << 'supra'
410
- elsif value =~ /^supra:/ ## supra:
411
- value_country_key = value[6..-1] ## cut off supra: prefix
412
- value_country = Country.find_by_key!( value_country_key )
413
- attribs[ :country_id ] = value_country.id
414
- elsif value =~ /^country:/ ## country:
415
- value_country_key = value[8..-1] ## cut off country: prefix
416
- value_country = Country.find_by_key!( value_country_key )
417
- attribs[ :country_id ] = value_country.id
418
- attribs[ :c ] = false # turn off default c|country flag; make it d|depend only
419
- attribs[ :d ] = true
420
- ## auto-add tag supra
421
- value_tag_keys << 'territory' # rename tag to dependency? why? why not?
422
- elsif value =~ /^metro:/ ## metro:
423
- value_city_key = value[6..-1] ## cut off metro: prefix
424
- value_city = City.find_by_key!( value_city_key )
425
- attribs[ :city_id ] = value_city.id
426
- elsif value =~ /^city:/ ## city:
427
- value_city_key = value[5..-1] ## cut off city: prefix
428
- value_city = City.find_by_key!( value_city_key )
429
- attribs[ :city_id ] = value_city.id
430
- attribs[ :c ] = false # turn off default c|city flag; make it d|district only
431
- attribs[ :d ] = true
432
- elsif value =~ /^m:/ ## m:
433
- value_popm_str = value[2..-1] ## cut off m: prefix
434
- value_popm = value_popm_str.gsub(/[ _]/, '').to_i
435
- attribs[ :popm ] = value_popm
436
- attribs[ :m ] = true # auto-mark city as m|metro too
437
- elsif is_region?( value ) && clazz == City ## assume region code e.g. TX for city
438
- value_region = Region.find_by_key_and_country_id!( value.downcase, attribs[:country_id] )
439
- attribs[ :region_id ] = value_region.id
440
- elsif value =~ /^[A-Z]{2,3}$/ ## assume two or three-letter code
441
- attribs[ :code ] = value
442
- elsif value =~ /^([0-9][0-9 _]+[0-9]|[0-9]{1,2})(?:\s*(?:km2|km²)\s*)$/
443
- ## allow numbers like 453 km²
444
- value_numbers << value.gsub( 'km2', '').gsub( 'km²', '' ).gsub(/[ _]/, '').to_i
445
- elsif value =~ /^([0-9][0-9 _]+[0-9])|([0-9]{1,2})$/ ## numeric (nb: can use any _ or spaces inside digits e.g. 1_000_000 or 1 000 000)
446
- value_numbers << value.gsub(/[ _]/, '').to_i
447
- elsif (values.size==(index+1)) && is_taglist?( value ) # tags must be last entry
448
-
449
- logger.debug " found tags: >>#{value}<<"
450
-
451
- tag_keys = value.split('|')
452
-
453
- ## unify; replace _w/ space; remove leading n trailing whitespace
454
- tag_keys = tag_keys.map do |key|
455
- key = key.gsub( '_', ' ' )
456
- key = key.strip
457
- key
458
- end
459
-
460
- value_tag_keys += tag_keys
461
- else
462
-
463
- if clazz == Country || clazz == Region
464
- ### assume it is the capital city - mark it for auto add
465
- value_cities << value
466
- next
467
- end
468
-
469
- # issue warning: unknown type for value
470
- logger.warn "unknown type for value >#{value}<"
471
- end
472
- end # each value
473
-
474
-
475
- if value_numbers.size > 0
476
- if clazz == City
477
- attribs[ :pop ] = value_numbers[0] # assume first number is pop for cities
478
- attribs[ :area ] = value_numbers[1]
479
- else # countries,regions
480
- attribs[ :area ] = value_numbers[0]
481
- attribs[ :pop ] = value_numbers[1]
482
-
483
-
484
- =begin
485
- if clazz == Country
486
- # auto-add tags
487
- area = value_numbers[0]
488
- pop = value_numbers[1]
489
-
490
- # categorize into brackets
491
- if area >= 1_000_000
492
- value_tag_keys << 'area_1_000_000_n_up'
493
- elsif area >= 100_000
494
- value_tag_keys << 'area_100_000_to_1_000_000'
495
- elsif area >= 1000
496
- value_tag_keys << 'area_1_000_to_100_000'
497
- else
498
- value_tag_keys << 'area_1_000_n_less' # microstate
499
- end
500
-
501
- # include all
502
- value_tag_keys << 'area_100_000_n_up' if area >= 100_000
503
- value_tag_keys << 'area_1_000_n_up' if area >= 1_000
504
-
505
-
506
- # categorize into brackets
507
- if pop >= 100_000_000
508
- value_tag_keys << 'pop_100m_n_up'
509
- elsif pop >= 10_000_000
510
- value_tag_keys << 'pop_10m_to_100m'
511
- elsif pop >= 1_000_000
512
- value_tag_keys << 'pop_1m_to_10m'
513
- else
514
- value_tag_keys << 'pop_1m_n_less'
515
- end
516
-
517
- # include all
518
- value_tag_keys << 'pop_10m_n_up' if pop >= 10_000_000
519
- value_tag_keys << 'pop_1m_n_up' if pop >= 1_000_000
520
- end
521
- =end
522
-
523
-
524
- end
525
- end # if value_numbers.size > 0
526
-
527
- rec = nil
528
-
529
- if clazz == Region ## requires country_id
530
- ## todo: assert that country_id is present/valid, that is, NOT null
531
- rec = clazz.find_by_key_and_country_id( attribs[ :key ], attribs[ :country_id] )
532
- else
533
- rec = clazz.find_by_key( attribs[ :key ] )
534
- end
535
-
536
- if rec.present?
537
- ## nb: [17..-1] cut off WorldDB::Models:: in name
538
- logger.debug "update #{clazz.name[17..-1].downcase} #{rec.id}-#{rec.key}:"
539
- else
540
- logger.debug "create #{clazz.name[17..-1].downcase}:"
541
- rec = clazz.new
542
- end
543
-
544
- logger.debug attribs.to_json
545
-
546
- rec.update_attributes!( attribs )
547
-
548
- #################
549
- ## auto add capital cities
550
-
551
- value_cities.each do |city_title|
552
-
553
- city_attribs = {}
554
- city_key = TextUtils.title_to_key( city_title )
555
-
556
- ## check if it exists
557
- ## todo/fix: add country_id for lookup?
558
- city = City.find_by_key( city_key )
559
- if city.present?
560
- logger.debug "update city #{city.id}-#{city.key}:"
561
- else
562
- logger.debug "create city:"
563
- city = City.new
564
- city_attribs[ :key ] = city_key
565
- end
566
-
567
- city_attribs[ :title ] = city_title
568
-
569
- if clazz == Country
570
- city_attribs[ :country_id ] = rec.id
571
- elsif clazz == Region
572
- city_attribs[ :region_id ] = rec.id
573
- city_attribs[ :country_id ] = rec.country_id
574
- else
575
- ## issue warning: unknown type for city!!!
576
- end
577
-
578
- logger.debug city_attribs.to_json
579
-
580
- city.update_attributes!( city_attribs )
581
-
582
- ### todo/fix: add captial ref to country/region
583
-
584
- end # each city
585
-
586
-
587
- ##################
588
- ## add taggings
589
-
590
- if value_tag_keys.size > 0
591
-
592
- if skip_tags?
593
- logger.debug " skipping add taggings (flag skip_tag)"
594
- else
595
- value_tag_keys.uniq! # remove duplicates
596
- logger.debug " adding #{value_tag_keys.size} taggings: >>#{value_tag_keys.join('|')}<<..."
597
-
598
- ### fix/todo: check tag_ids and only update diff (add/remove ids)
599
-
600
- value_tag_keys.each do |key|
601
- tag = Tag.find_by_key( key )
602
- if tag.nil? # create tag if it doesn't exit
603
- logger.debug " creating tag >#{key}<"
604
- tag = Tag.create!( key: key )
605
- end
606
- rec.tags << tag
607
- end
608
- end
609
- end
610
-
611
-
612
- end # each_line
613
-
614
- end # method load_fixture_worker_for
615
-
616
350
 
617
351
  end # class Reader
618
352
  end # module WorldDb
data/lib/worlddb/utils.rb CHANGED
@@ -16,3 +16,39 @@ class Time
16
16
  end
17
17
 
18
18
  end # class Time
19
+
20
+
21
+ ##### fix/todo: move to helper folder - use one file per module/helper
22
+
23
+ module WorldDb
24
+ module TagHelper
25
+
26
+ def find_tags( value )
27
+ # logger.debug " found tags: >>#{value}<<"
28
+
29
+ tag_keys = value.split('|')
30
+
31
+ ## unify; replace _w/ space; remove leading n trailing whitespace
32
+ tag_keys = tag_keys.map do |key|
33
+ key = key.gsub( '_', ' ' )
34
+ key = key.strip
35
+ key
36
+ end
37
+
38
+ tag_keys # return tag keys as ary
39
+ end
40
+
41
+ def find_tags_in_hash!( h )
42
+ # NB: will remove :tags from hash
43
+
44
+ if h[:tags].present?
45
+ tag_keys = find_tags( h[:tags] )
46
+ h.delete(:tags)
47
+ tag_keys # return tag keys as ary
48
+ else
49
+ [] # nothing found; return empty ary
50
+ end
51
+ end
52
+
53
+ end # module
54
+ end # module WorldDb
@@ -1,6 +1,6 @@
1
1
 
2
2
  module WorldDb
3
- VERSION = '1.6.4' # sync version w/ sport.db - why? why not?
3
+ VERSION = '1.6.5' # sync version w/ sport.db - why? why not?
4
4
  end
5
5
 
6
6
  ###########################################
data/lib/worlddb.rb CHANGED
@@ -26,6 +26,7 @@ require 'textutils'
26
26
 
27
27
  require 'worlddb/version'
28
28
 
29
+ require 'worlddb/utils'
29
30
  require 'worlddb/models/prop'
30
31
  require 'worlddb/models/continent'
31
32
  require 'worlddb/models/country'
@@ -36,7 +37,6 @@ require 'worlddb/models/tagging'
36
37
  require 'worlddb/models/lang'
37
38
  require 'worlddb/models/usage'
38
39
  require 'worlddb/schema' # NB: requires worlddb/models (include WorldDB::Models)
39
- require 'worlddb/utils'
40
40
  require 'worlddb/reader'
41
41
  require 'worlddb/deleter'
42
42
  require 'worlddb/stats'
data/test/helper.rb ADDED
@@ -0,0 +1,56 @@
1
+
2
+ ## $:.unshift(File.dirname(__FILE__))
3
+
4
+ ## minitest setup
5
+
6
+ # require 'minitest/unit'
7
+ require 'minitest/autorun'
8
+
9
+ # include MiniTest::Unit # lets us use TestCase instead of MiniTest::Unit::TestCase
10
+
11
+
12
+ # ruby stdlibs
13
+
14
+ require 'json'
15
+ require 'uri'
16
+ require 'pp'
17
+
18
+ # ruby gems
19
+
20
+ require 'active_record'
21
+
22
+ # our own code
23
+
24
+ require 'worlddb'
25
+ require 'logutils/db' # NB: explict require required for LogDb (not automatic)
26
+
27
+ Country = WorldDb::Models::Country
28
+ Region = WorldDb::Models::Region
29
+ City = WorldDb::Models::City
30
+
31
+
32
+ def setup_in_memory_db
33
+ # Database Setup & Config
34
+
35
+ db_config = {
36
+ adapter: 'sqlite3',
37
+ database: ':memory:'
38
+ }
39
+
40
+ pp db_config
41
+
42
+ ActiveRecord::Base.logger = Logger.new( STDOUT )
43
+ ## ActiveRecord::Base.colorize_logging = false - no longer exists - check new api/config setting?
44
+
45
+ ## NB: every connect will create a new empty in memory db
46
+ ActiveRecord::Base.establish_connection( db_config )
47
+
48
+
49
+ ## build schema
50
+
51
+ LogDb.create
52
+ WorldDb.create
53
+ end
54
+
55
+
56
+ setup_in_memory_db()
@@ -0,0 +1,114 @@
1
+ # encoding: utf-8
2
+
3
+ ###
4
+ # to run use
5
+ # ruby -I ./lib -I ./test test/test_helper.rb
6
+ # or better
7
+ # rake test
8
+
9
+ require 'helper'
10
+
11
+ class TestValues < MiniTest::Unit::TestCase
12
+
13
+ def setup
14
+ # delete all countries, regions, cities in in-memory only db
15
+ WorldDb.delete!
16
+ end
17
+
18
+ def test_load_country_values
19
+
20
+ new_attributes = {
21
+ key: 'at',
22
+ title: 'Austria',
23
+ synonyms: ''
24
+ }
25
+
26
+ values = [
27
+ 'AUT',
28
+ '83_871',
29
+ '8_414_638',
30
+ 'un|fifa|uefa|eu|euro|schengen|central_europe|western_europe'
31
+ ]
32
+
33
+ c = Country.create_or_update_from_values( new_attributes, values )
34
+
35
+ c2 = Country.find_by_key!( new_attributes[:key] )
36
+ assert( c.id == c2.id )
37
+
38
+ assert( c.title == new_attributes[:title] )
39
+ assert( c.pop == 8_414_638 )
40
+ assert( c.area == 83_871 )
41
+ ## todo: assert tag count; add supra:eu etc.
42
+ end
43
+
44
+ def test_load_region_values
45
+
46
+ at = Country.create!( key: 'at',
47
+ title: 'Austria',
48
+ code: 'AUT',
49
+ pop: 8_414_638,
50
+ area: 83_871 )
51
+
52
+ new_attributes = {
53
+ key: 'w',
54
+ title: 'Wien',
55
+ synonyms: '',
56
+ country_id: at.id
57
+ }
58
+
59
+ values = [
60
+ '415 km²',
61
+ 'eastern austria'
62
+ ]
63
+
64
+ r = Region.create_or_update_from_values( new_attributes, values )
65
+
66
+ r2 = Region.find_by_key!( new_attributes[:key] )
67
+ assert( r.id == r2.id )
68
+
69
+ assert( r.title == new_attributes[:title] )
70
+ assert( r.area == 415 )
71
+ ## todo: assert country_id & country.title for assoc
72
+ end
73
+
74
+ def test_load_city_values
75
+
76
+ at = Country.create!( key: 'at',
77
+ title: 'Austria',
78
+ code: 'AUT',
79
+ pop: 8_414_638,
80
+ area: 83_871 )
81
+
82
+ w = Region.create!( key: 'w',
83
+ title: 'Wien',
84
+ country_id: at.id )
85
+
86
+ new_attributes = {
87
+ key: 'wien',
88
+ title: 'Wien',
89
+ synonyms: '',
90
+ country_id: at.id
91
+ }
92
+
93
+ values = [
94
+ 'W',
95
+ '1_731_236',
96
+ 'm:1_724_000'
97
+ ]
98
+
99
+ c = City.create_or_update_from_values( new_attributes, values )
100
+
101
+ c2 = City.find_by_key!( new_attributes[:key] )
102
+ assert( c.id == c2.id )
103
+
104
+ assert( c.title == new_attributes[:title] )
105
+ assert( c.pop == 1_731_236 )
106
+ assert( c.popm == 1_724_000 )
107
+ assert( c.m == true )
108
+ assert( c.region_id == w.id )
109
+ assert( c.country_id == at.id )
110
+ end
111
+
112
+
113
+ end # class TestValues
114
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: worlddb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.4
4
+ version: 1.6.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-09 00:00:00.000000000 Z
12
+ date: 2013-05-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: textutils
16
- requirement: &83827050 !ruby/object:Gem::Requirement
16
+ requirement: &83860400 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0.5'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *83827050
24
+ version_requirements: *83860400
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: commander
27
- requirement: &83826830 !ruby/object:Gem::Requirement
27
+ requirement: &83860180 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 4.1.3
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *83826830
35
+ version_requirements: *83860180
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: activerecord
38
- requirement: &83826620 !ruby/object:Gem::Requirement
38
+ requirement: &83876350 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '3.2'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *83826620
46
+ version_requirements: *83876350
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rdoc
49
- requirement: &83826400 !ruby/object:Gem::Requirement
49
+ requirement: &83876130 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '3.10'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *83826400
57
+ version_requirements: *83876130
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: hoe
60
- requirement: &83826180 !ruby/object:Gem::Requirement
60
+ requirement: &83875910 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,7 +65,7 @@ dependencies:
65
65
  version: '3.3'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *83826180
68
+ version_requirements: *83875910
69
69
  description: worlddb - world.db command line tool
70
70
  email: opensport@googlegroups.com
71
71
  executables:
@@ -99,6 +99,9 @@ files:
99
99
  - lib/worlddb/stats.rb
100
100
  - lib/worlddb/utils.rb
101
101
  - lib/worlddb/version.rb
102
+ - test/helper.rb
103
+ - test/test_values.rb
104
+ - .gemtest
102
105
  homepage: https://github.com/geraldb/world.db.ruby
103
106
  licenses:
104
107
  - Public Domain
@@ -126,4 +129,5 @@ rubygems_version: 1.8.17
126
129
  signing_key:
127
130
  specification_version: 3
128
131
  summary: worlddb - world.db command line tool
129
- test_files: []
132
+ test_files:
133
+ - test/test_values.rb