worlddb 1.8.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/{History.md → HISTORY.md} +0 -0
  3. data/Manifest.txt +22 -5
  4. data/README.md +62 -2
  5. data/Rakefile +14 -6
  6. data/lib/worlddb.rb +34 -12
  7. data/lib/worlddb/cli/main.rb +159 -111
  8. data/lib/worlddb/cli/opts.rb +15 -48
  9. data/lib/worlddb/console.rb +6 -5
  10. data/lib/worlddb/deleter.rb +5 -3
  11. data/lib/worlddb/matcher.rb +16 -5
  12. data/lib/worlddb/models/city.rb +66 -30
  13. data/lib/worlddb/models/city_comp.rb +27 -0
  14. data/lib/worlddb/models/continent.rb +30 -8
  15. data/lib/worlddb/models/continent_comp.rb +24 -0
  16. data/lib/worlddb/models/country.rb +60 -36
  17. data/lib/worlddb/models/country_comp.rb +29 -0
  18. data/lib/worlddb/models/forward.rb +53 -0
  19. data/lib/worlddb/models/lang.rb +9 -7
  20. data/lib/worlddb/models/lang_comp.rb +23 -0
  21. data/lib/worlddb/models/name.rb +13 -0
  22. data/lib/worlddb/models/place.rb +16 -0
  23. data/lib/worlddb/models/region.rb +34 -12
  24. data/lib/worlddb/models/region_comp.rb +26 -0
  25. data/lib/worlddb/models/tagdb/tag.rb +16 -0
  26. data/lib/worlddb/models/tagdb/tagging.rb +15 -0
  27. data/lib/worlddb/models/usage.rb +10 -6
  28. data/lib/worlddb/reader.rb +31 -158
  29. data/lib/worlddb/readers/base.rb +41 -0
  30. data/lib/worlddb/readers/city.rb +18 -0
  31. data/lib/worlddb/readers/country.rb +17 -0
  32. data/lib/worlddb/readers/lang.rb +43 -0
  33. data/lib/worlddb/readers/region.rb +17 -0
  34. data/lib/worlddb/readers/usage.rb +35 -0
  35. data/lib/worlddb/schema.rb +100 -65
  36. data/lib/worlddb/stats.rb +9 -3
  37. data/lib/worlddb/utils.rb +3 -0
  38. data/lib/worlddb/version.rb +1 -6
  39. data/test/helper.rb +13 -3
  40. data/test/test_model_city.rb +60 -0
  41. data/test/test_model_comp.rb +48 -0
  42. data/test/test_model_country.rb +43 -0
  43. data/test/test_model_region.rb +50 -0
  44. data/test/test_models.rb +35 -0
  45. metadata +113 -37
  46. data/lib/worlddb/models/prop.rb +0 -32
  47. data/lib/worlddb/models/tag.rb +0 -33
  48. data/lib/worlddb/models/tagging.rb +0 -13
  49. data/test/test_values.rb +0 -114
@@ -2,66 +2,33 @@ module WorldDb
2
2
 
3
3
  class Opts
4
4
 
5
- def merge_commander_options!( options = {} )
6
- @db_path = options[:dbpath] if options[:dbpath].present?
7
- @db_name = options[:dbname] if options[:dbname].present?
8
-
9
- @data_path = options[:include] if options[:include].present?
10
-
11
- @country = options[:country] if options[:country].present?
12
-
13
- @countries = options[:countries] if options[:countries].present?
14
- @regions = options[:regions] if options[:regions].present?
15
- @cities = options[:cities] if options[:cities].present?
16
- end
17
-
18
-
19
- def db_path
20
- @db_path || '.'
21
- end
22
-
23
- def db_name
24
- @db_name || 'world.db'
25
- end
5
+ def merge_gli_options!( options = {} )
6
+ @db_path = options[:dbpath] if options[:dbpath].present?
7
+ @db_name = options[:dbname] if options[:dbname].present?
26
8
 
9
+ @verbose = true if options[:verbose] == true
27
10
 
28
-
29
- def country=(value)
30
- @country = value
31
- end
32
-
33
- def country
34
- @country # NB: option has no default; return nil ## || '.'
11
+ @data_path = options[:include] if options[:include].present?
35
12
  end
36
13
 
37
14
 
38
- def countries=(boolean)
39
- @countries = boolean
15
+ def verbose=(boolean) # add: alias for debug ??
16
+ @verbose = boolean
40
17
  end
41
18
 
42
- def countries?
43
- return false if @countries.nil? # default countries flag is false
44
- @countries == true
19
+ def verbose?
20
+ return false if @verbose.nil? # default verbose/debug flag is false
21
+ @verbose == true
45
22
  end
46
23
 
47
- def regions=(boolean)
48
- @regions = boolean
49
- end
50
-
51
- def regions?
52
- return false if @regions.nil? # default regions flag is false
53
- @regions == true
54
- end
55
-
56
- def cities=(boolean)
57
- @cities = boolean
24
+ def db_path
25
+ @db_path || '.'
58
26
  end
59
27
 
60
- def cities?
61
- return false if @cities.nil? # default cities flag is false
62
- @cities == true
28
+ def db_name
29
+ @db_name || 'world.db'
63
30
  end
64
-
31
+
65
32
 
66
33
  def data_path=(value)
67
34
  @data_path = value
@@ -14,13 +14,15 @@ require 'yaml'
14
14
 
15
15
  ## shortcuts for models
16
16
 
17
- Tag = WorldDb::Model::Tag
18
- Tagging = WorldDb::Model::Tagging
17
+ Prop = ConfDb::Model::Prop
18
+
19
+ Tag = TagDb::Model::Tag
20
+ Tagging = TagDb::Model::Tagging
21
+
19
22
  Continent = WorldDb::Model::Continent
20
23
  Country = WorldDb::Model::Country
21
24
  Region = WorldDb::Model::Region
22
25
  City = WorldDb::Model::City
23
- Prop = WorldDb::Model::Prop
24
26
 
25
27
  ## connect to db
26
28
 
@@ -33,12 +35,11 @@ pp DB_CONFIG
33
35
  ActiveRecord::Base.establish_connection( DB_CONFIG )
34
36
 
35
37
 
36
-
37
38
  ## test drive
38
39
 
39
40
  puts "Welcome to world.db, version #{WorldDb::VERSION}!"
40
41
 
41
- WorldDb.stats
42
+ WorldDb.tables
42
43
 
43
44
  puts "Ready."
44
45
 
@@ -11,15 +11,17 @@ module WorldDb
11
11
  def run
12
12
  # for now delete all tables
13
13
 
14
- Tagging.delete_all
15
- Tag.delete_all
14
+ ## Tagging.delete_all # - use TagDb.delete!
15
+ ## Tag.delete_all
16
+ Name.delete_all
17
+ Place.delete_all
16
18
  City.delete_all
17
19
  Region.delete_all
18
20
  Country.delete_all
19
21
  Continent.delete_all
20
22
  Usage.delete_all
21
23
  Lang.delete_all
22
- Prop.delete_all
24
+ # Prop.delete_all # - use ConfDb.delete!
23
25
  end
24
26
 
25
27
  end # class Deleter
@@ -33,15 +33,26 @@ module Matcher
33
33
  end
34
34
  end
35
35
 
36
- def match_xxx_for_country_n_region( name, xxx ) # xxx e.g. beers|breweries
36
+
37
+ def match_xxx_for_country_n_region( name, xxx ) # xxx e.g. wine|wineries
38
+
37
39
  # auto-add required country n region code (from folder structure)
38
40
 
39
- # note: allow /cities and /1--hokkaido--cities
40
- xxx_pattern = "(?:#{xxx}|[0-9]+--[^\\/]+?--#{xxx})" # note: double escape \\ required for backslash
41
+ ## -- allow opt_folders after long regions (e.g. additional subregion/zone)
42
+ ## -- allow anything (prefixes) before -- for xxx
43
+ # e.g. at-austria!/1--n-niederoesterreich--eastern/wagram--wines
44
+ # at-austria!/1--n-niederoesterreich--eastern/wagram--wagram--wines
45
+
46
+ # note: allow /cities and /1--hokkaido--cities and /hokkaido--cities too
47
+ xxx_pattern = "(?:#{xxx}|[^\\/]+--#{xxx})" # note: double escape \\ required for backslash
48
+
49
+ ## allow optional folders -- TODO: add restriction ?? e.g. must be 4+ alphas ???
50
+ opt_folders_pattern = "(?:\/[^\/]+)*"
51
+ ## note: for now only (style #2) n (style #3) that is long region allow opt folders
41
52
 
42
53
  if name =~ /(?:^|\/)([a-z]{2,3})-[^\/]+\/([a-z]{1,3})-[^\/]+\/#{xxx_pattern}/ || # (1)
43
- name =~ /(?:^|\/)[0-9]+--([a-z]{2,3})-[^\/]+\/[0-9]+--([a-z]{1,3})-[^\/]+\/#{xxx_pattern}/ || # (2)
44
- name =~ /(?:^|\/)([a-z]{2,3})-[^\/]+\/[0-9]+--([a-z]{1,3})-[^\/]+\/#{xxx_pattern}/ || # (3)
54
+ name =~ /(?:^|\/)[0-9]+--([a-z]{2,3})-[^\/]+\/[0-9]+--([a-z]{1,3})-[^\/]+#{opt_folders_pattern}\/#{xxx_pattern}/ || # (2)
55
+ name =~ /(?:^|\/)([a-z]{2,3})-[^\/]+\/[0-9]+--([a-z]{1,3})-[^\/]+#{opt_folders_pattern}\/#{xxx_pattern}/ || # (3)
45
56
  name =~ /(?:^|\/)[0-9]+--([a-z]{2,3})-[^\/]+\/([a-z]{1,3})-[^\/]+\/#{xxx_pattern}/ # (4)
46
57
 
47
58
  #######
@@ -1,6 +1,17 @@
1
1
  # encoding: utf-8
2
2
 
3
- module WorldDb::Model
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 ???
4
15
 
5
16
  class City < ActiveRecord::Base
6
17
 
@@ -13,59 +24,83 @@ class City < ActiveRecord::Base
13
24
 
14
25
  self.table_name = 'cities'
15
26
 
16
- belongs_to :country, :class_name => 'Country', :foreign_key => 'country_id'
17
- belongs_to :region, :class_name => 'Region', :foreign_key => 'region_id'
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'
18
30
 
19
31
  ## self referencing hierachy within cities e.g. m|metro > c|city > d|district
20
32
 
21
33
  ## fix: use condition check for m|d|c flag?? why? why not? (NB: flags are NOT exclusive e.g. possible metro|city)
22
34
 
23
35
  ## (1) metro - level up
24
- has_many :cities, :class_name => 'City', :foreign_key => 'city_id'
36
+ has_many :cities, class_name: 'City', foreign_key: 'city_id'
25
37
 
26
38
  ## (2) city
27
- belongs_to :metro, :class_name => 'City', :foreign_key => 'city_id' ## for now alias for parent - use parent?
28
- has_many :districts, :class_name => 'City', :foreign_key => 'city_id' ## for now alias for cities - use cities?
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?
29
41
 
30
42
  ## (3) district - level down
31
- belongs_to :city, :class_name => 'City', :foreign_key => 'city_id' ## for now alias for parent - use parent?
43
+ belongs_to :city, class_name: 'City', foreign_key: 'city_id' ## for now alias for parent - use parent?
32
44
 
45
+ has_many_tags
33
46
 
34
47
  ###
35
48
  # NB: use is_ for flags to avoid conflict w/ assocs (e.g. metro?, city? etc.)
36
49
 
37
- def is_metro?
38
- m? == true
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
39
60
  end
40
-
41
- def is_city?
42
- c? == true
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 )
43
65
  end
44
-
45
- def is_district?
46
- d? == true
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
47
80
  end
48
81
 
49
-
50
- has_many :taggings, :as => :taggable
51
- has_many :tags, :through => :taggings
52
82
 
53
- validates :key, :format => { :with => /^[a-z]{3,}$/, :message => 'expected three or more lowercase letters a-z' }
54
- validates :code, :format => { :with => /^[A-Z_]{3}$/, :message => 'expected three uppercase letters A-Z (and _)' }, :allow_nil => true
83
+ validates :key, format: { with: /^[a-z]{3,}$/, message: 'expected three or more lowercase letters a-z' }
84
+ validates :code, format: { with: /^[A-Z_]{3}$/, message: 'expected three uppercase letters A-Z (and _)' }, :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
+
55
93
 
56
- scope :by_key, order( 'key asc' ) # order by key (a-z)
57
- scope :by_title, order( 'title asc' ) # order by title (a-z)
58
- scope :by_pop, order( 'pop desc' ) # order by pop(ulation)
59
- scope :by_popm, order( 'popm desc' ) # order by pop(ulation) metropolitan area
60
- scope :by_area, order( 'area desc' ) # order by area (in square km)
94
+ def all_names( opts={} )
95
+ ### fix:
96
+ ## allow to passing in sep or separator e.g. | or other
61
97
 
62
- def title_w_synonyms
63
- return title if synonyms.blank?
98
+ return name if alt_names.blank?
64
99
 
65
100
  buf = ''
66
- buf << title
101
+ buf << name
67
102
  buf << ' | '
68
- buf << synonyms.split('|').join(' | ')
103
+ buf << alt_names.split('|').join(' | ')
69
104
  buf
70
105
  end
71
106
 
@@ -201,4 +236,5 @@ class City < ActiveRecord::Base
201
236
 
202
237
  end # class Cities
203
238
 
204
- end # module WorldDb::Model
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
+
@@ -1,14 +1,36 @@
1
1
  # encoding: utf-8
2
2
 
3
- module WorldDb::Model
3
+ module WorldDb
4
+ module Model
4
5
 
5
- class Continent < ActiveRecord::Base
6
-
7
- has_many :countries
6
+ class Continent < ActiveRecord::Base
8
7
 
9
- # NB: allow dots in keys e.g. concacaf.naf etc.
10
-
11
- end # class Continent
8
+ belongs_to :place, class_name: 'Place', foreign_key: 'place_id'
9
+ has_many :countries
12
10
 
13
- end # module WorldDb::Model
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
+ end
20
+
21
+ def on_update
22
+ ## fix/todo: check - if name or kind changed - only update if changed ?? why? why not??
23
+ place.update_attributes!( name: name, kind: place_kind )
24
+ end
25
+
26
+ def place_kind # use place_kind_of_code ??
27
+ 'CONT'
28
+ end
29
+
30
+
31
+ end # class Continent
32
+
33
+
34
+ end # module Model
35
+ end # module WorldDb
14
36
 
@@ -0,0 +1,24 @@
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 Continent
11
+
12
+ def title() name; end
13
+ def title=(value) self.name = value; end
14
+
15
+ scope :by_title, ->{ order( 'name asc' ) } # order by title (a-z)
16
+
17
+ def synonyms() alt_names; end
18
+ def synonyms=(value) self.alt_names = value; end
19
+
20
+ end # class Continent
21
+
22
+
23
+ end # module Model
24
+ end # module WorldDb
@@ -1,6 +1,10 @@
1
1
  # encoding: utf-8
2
2
 
3
- module WorldDb::Model
3
+ module WorldDb
4
+ module Model
5
+
6
+ ########
7
+ # Country / Supra (e.g. European Union) / Territory (e.g. Puerto Rico) or Dependency (e.g. Dependent territory)
4
8
 
5
9
  class Country < ActiveRecord::Base
6
10
 
@@ -13,58 +17,77 @@ class Country < ActiveRecord::Base
13
17
 
14
18
  self.table_name = 'countries'
15
19
 
16
- belongs_to :continent, :class_name => 'Continent', :foreign_key => 'continent_id'
17
-
20
+ belongs_to :place, class_name: 'Place', foreign_key: 'place_id'
21
+ belongs_to :continent, class_name: 'Continent', foreign_key: 'continent_id'
22
+
18
23
  has_many :usages
19
24
  has_many :langs, :through => :usages # lang(uage)s through usages (that is, countries_langs) join table
20
25
 
21
- has_many :regions, :class_name => 'Region', :foreign_key => 'country_id'
22
- has_many :cities, :class_name => 'City', :foreign_key => 'country_id'
26
+ has_many :regions, class_name: 'Region', foreign_key: 'country_id'
27
+ has_many :cities, class_name: 'City', foreign_key: 'country_id'
23
28
 
24
29
  ## self referencing hierachy within countries e.g. EU > GB > EN
25
- belongs_to :parent, :class_name => 'Country', :foreign_key => 'country_id'
26
- has_many :countries, :class_name => 'Country', :foreign_key => 'country_id'
30
+ belongs_to :parent, class_name: 'Country', foreign_key: 'country_id'
31
+ has_many :countries, class_name: 'Country', foreign_key: 'country_id'
27
32
 
28
- has_many :taggings, :as => :taggable
29
- has_many :tags, :through => :taggings
33
+ has_many_tags
30
34
 
31
- validates :key, :format => { :with => /^[a-z]{2}$/, :message => 'expected two lowercase letters a-z' }
32
- validates :code, :format => { :with => /^[A-Z_]{3}$/, :message => 'expected three uppercase letters A-Z (and _)' }
35
+ validates :key, format: { with: /^[a-z]{2}$/, message: 'expected two lowercase letters a-z' }
36
+ validates :code, format: { with: /^[A-Z_]{3}$/, message: 'expected three uppercase letters A-Z (and _)' }
33
37
 
34
- scope :by_key, order( 'key asc' ) # order by key (a-z)
35
- scope :by_title, order( 'title asc' ) # order by title (a-z)
36
- scope :by_code, order( 'code asc' ) # order by code (a-z)
37
- scope :by_pop, order( 'pop desc' ) # order by pop(ulation)
38
- scope :by_area, order( 'area desc') # order by area (in square km)
39
-
40
- ###
41
- # NB: use is_ for flags to avoid conflict w/ assocs
42
-
43
- def is_supra?
44
- s? == true
38
+
39
+ scope :by_key, ->{ order( 'key asc' ) } # order by key (a-z)
40
+ scope :by_name, ->{ order( 'name asc' ) } # order by name (a-z)
41
+ scope :by_code, ->{ order( 'code asc' ) } # order by code (a-z)
42
+ scope :by_pop, ->{ order( 'pop desc' ) } # order by pop(ulation)
43
+ scope :by_area, ->{ order( 'area desc') } # order by area (in square km)
44
+
45
+ before_create :on_create
46
+ before_update :on_update
47
+
48
+ def on_create
49
+ place_rec = Place.create!( name: name, kind: place_kind )
50
+ self.place_id = place_rec.id
45
51
  end
46
-
47
- def is_country?
48
- c? == true
52
+
53
+ def on_update
54
+ ## fix/todo: check - if name or kind changed - only update if changed ?? why? why not??
55
+ place.update_attributes!( name: name, kind: place_kind )
49
56
  end
50
-
51
- def is_dependency?
52
- d? == true
57
+
58
+ def place_kind # use place_kind_of_code ??
59
+ if is_supra?
60
+ 'SUPR'
61
+ elsif is_dependency?
62
+ 'TERR'
63
+ else
64
+ 'CNTY'
65
+ end
53
66
  end
54
67
 
55
-
56
-
57
- def title_w_synonyms
58
- return title if synonyms.blank?
68
+ ###
69
+ # NB: use is_ for flags to avoid conflict w/ assocs
70
+
71
+ def is_supra?() s? == true; end
72
+ def is_country?() c? == true; end
73
+ def is_dependency?() d? == true; end
74
+
75
+
76
+ def all_names( opts={} )
77
+ ### fix:
78
+ ## allow to passing in sep or separator e.g. | or other
79
+
80
+ return name if alt_names.blank?
59
81
 
60
82
  buf = ''
61
- buf << title
83
+ buf << name
62
84
  buf << ' | '
63
- buf << synonyms.split('|').join(' | ')
85
+ buf << alt_names.split('|').join(' | ')
64
86
  buf
65
87
  end
66
88
 
67
89
 
90
+
68
91
  def self.create_or_update_from_values( values, more_attribs={} )
69
92
 
70
93
  ## key & title
@@ -175,6 +198,7 @@ class Country < ActiveRecord::Base
175
198
  value_tag_keys << 'pop_1m_n_up' if pop >= 1_000_000
176
199
  =end
177
200
 
201
+
178
202
  rec = Country.find_by_key( new_attributes[ :key ] )
179
203
 
180
204
  if rec.present?
@@ -224,5 +248,5 @@ class Country < ActiveRecord::Base
224
248
 
225
249
  end # class Country
226
250
 
227
-
228
- end # module WorldDb::Model
251
+ end # module Model
252
+ end # module WorldDb