worlddb 1.8.2 → 2.0.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 (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