geonames_local 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/README.rdoc +11 -25
- data/bin/geonames +2 -1
- data/geonames.yml +3 -3
- data/lib/geonames_local.rb +5 -10
- data/lib/geonames_local/cli.rb +31 -54
- data/lib/geonames_local/data/dump.rb +24 -11
- data/lib/geonames_local/data/shp.rb +1 -1
- data/lib/geonames_local/features/spot.rb +8 -33
- data/lib/geonames_local/geonames.rb +0 -5
- data/lib/geonames_local/models/mongodb.rb +157 -39
- data/lib/geonames_local/version.rb +1 -1
- data/spec/geonames_local/adapters/{postgres_spec.rb → postgis_spec.rb} +2 -2
- data/spec/geonames_local/features/spot_spec.rb +5 -7
- data/spec/geonames_local/models/{mongo_spec.rb → mongodb_spec.rb} +12 -9
- data/spec/geonames_local/models/postgis_spec.rb +73 -0
- data/spec/geonames_local_spec.rb +1 -0
- data/spec/spec_ar_helper.rb +96 -96
- data/spec/spec_helper.rb +1 -1
- metadata +8 -16
- data/lib/geonames_ar.rb +0 -12
- data/lib/geonames_cli.rb +0 -14
- data/lib/geonames_local/adapters/mongodb.rb +0 -109
- data/lib/geonames_local/adapters/postgres.rb +0 -108
- data/lib/geonames_local/adapters/tokyo.rb +0 -109
- data/lib/geonames_local/data/sync.rb +0 -74
- data/spec/geonames_local/models/ar_spec.rb +0 -73
- data/spec/spec_mongo_helper.rb +0 -6
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= Geonames Local
|
2
2
|
|
3
|
-
|
3
|
+
Downloads and store Geonames.org data locally (MongoDB, PostGIS, Tokyo).
|
4
4
|
Making every Geoname API operation possible on your servers.
|
5
5
|
No hit limit, fast as possible.
|
6
6
|
|
@@ -10,7 +10,7 @@ No hit limit, fast as possible.
|
|
10
10
|
To use one adapter, install the corresponding gem:
|
11
11
|
|
12
12
|
PostgreSQL => pg
|
13
|
-
MongoDB =>
|
13
|
+
MongoDB => mongoid (optional: mongo_ext)
|
14
14
|
Tokyo => tokyocabinet
|
15
15
|
|
16
16
|
You will also need in your system:
|
@@ -19,17 +19,6 @@ You will also need in your system:
|
|
19
19
|
* curl
|
20
20
|
|
21
21
|
|
22
|
-
=== PostgreSQL
|
23
|
-
|
24
|
-
Be sure to use a database based on the PostGIS template.
|
25
|
-
|
26
|
-
|
27
|
-
=== MongoDB
|
28
|
-
|
29
|
-
MongoDB 2D support is new, only mongo >= 1.3.3 mongodb gem >= 0.19.2
|
30
|
-
http://github.com/mongodb/mongo-ruby-driver
|
31
|
-
|
32
|
-
|
33
22
|
=== Config YML
|
34
23
|
|
35
24
|
geonames conf
|
@@ -45,26 +34,23 @@ If you are not sure your country code, use:
|
|
45
34
|
|
46
35
|
geonames list <search>
|
47
36
|
|
37
|
+
To populate the countries database for the first time use:
|
48
38
|
|
49
|
-
|
39
|
+
geonames -c geoconfig.yml countries
|
50
40
|
|
51
|
-
When using PG, this gem will (try) to relational map Geonames
|
52
|
-
data on your scheme.
|
53
41
|
|
54
|
-
==
|
42
|
+
== Mongoid
|
55
43
|
|
56
|
-
|
57
|
-
|
58
|
-
I`m wondering the best way (and if it`s a good idea) to auto include'em on rails.
|
59
|
-
For now, just copy as you see fit.
|
44
|
+
So, supposing Mongoid, something like this is possible:
|
60
45
|
|
46
|
+
City.first.province.country.abbr
|
47
|
+
=> "BR"
|
61
48
|
|
62
|
-
== PostgreSQL
|
63
49
|
|
64
|
-
|
50
|
+
== Postgis
|
65
51
|
|
66
|
-
|
67
|
-
|
52
|
+
TBD (by someone else)
|
53
|
+
Be sure to use a database based on the PostGIS template.
|
68
54
|
|
69
55
|
|
70
56
|
=== Migration
|
data/bin/geonames
CHANGED
data/geonames.yml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#
|
2
2
|
# Geonames Local Config Example
|
3
3
|
#
|
4
|
-
:store: mongodb #
|
4
|
+
:store: mongodb # postgis / tokyo
|
5
5
|
:codes: [br] # to store all countries informations in the countries table, just write [country]
|
6
6
|
:level: city
|
7
7
|
:min_pop: 100000
|
@@ -10,7 +10,7 @@
|
|
10
10
|
:geom: true
|
11
11
|
:db:
|
12
12
|
:host: localhost
|
13
|
-
:
|
13
|
+
:name: geonames_test
|
14
14
|
:user:
|
15
|
-
:
|
15
|
+
:pass:
|
16
16
|
:purge: false
|
data/lib/geonames_local.rb
CHANGED
@@ -5,14 +5,9 @@ $:.unshift(File.dirname(__FILE__)) unless
|
|
5
5
|
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
6
6
|
|
7
7
|
# Require Libs
|
8
|
-
require 'geonames_local/
|
9
|
-
|
10
|
-
require 'geonames_local/
|
11
|
-
|
12
|
-
module Geonames
|
8
|
+
require 'geonames_local/features/spot'
|
9
|
+
require 'geonames_local/features/road'
|
10
|
+
require 'geonames_local/features/zone'
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
require 'geonames_local/models/mongo'
|
12
|
+
# Require Main
|
13
|
+
require 'geonames_local/geonames'
|
data/lib/geonames_local/cli.rb
CHANGED
@@ -2,6 +2,14 @@
|
|
2
2
|
# Geonames Local
|
3
3
|
#
|
4
4
|
require 'optparse'
|
5
|
+
# Require CLI Stuff
|
6
|
+
require 'geonames_local/geonames'
|
7
|
+
require 'geonames_local/data/shp'
|
8
|
+
require 'geonames_local/data/dump'
|
9
|
+
require 'geonames_local/data/export'
|
10
|
+
require 'geonames_local/cli'
|
11
|
+
|
12
|
+
|
5
13
|
module Geonames
|
6
14
|
class CLI
|
7
15
|
def self.parse_options(argv)
|
@@ -113,76 +121,44 @@ BANNER
|
|
113
121
|
# Do the magic! Import Geonames Data
|
114
122
|
#
|
115
123
|
else
|
116
|
-
|
124
|
+
load_adapter(Opt[:store])
|
117
125
|
info "Using adapter #{Opt[:store]}.."
|
118
126
|
|
119
|
-
if
|
120
|
-
Geonames::Dump.
|
127
|
+
if argv[0] =~ /coun|nati/
|
128
|
+
dump = Geonames::Dump.new(:country, :dump)
|
129
|
+
info "\n---\nTotal #{dump.data.length} parsed."
|
130
|
+
|
131
|
+
info "Writing to DB"
|
132
|
+
Geonames::Models::Country.from_batch(dump.data)
|
121
133
|
else
|
122
|
-
Geonames::Dump.
|
123
|
-
Geonames::Dump.
|
124
|
-
|
134
|
+
zip = Geonames::Dump.new(Opt[:codes], :zip).data
|
135
|
+
dump = Geonames::Dump.new(Opt[:codes], :dump).data
|
136
|
+
info "\n---\nTotal #{dump.size} parsed. #{zip.size} zips."
|
137
|
+
|
138
|
+
info "Join dump << zip"
|
139
|
+
dump = unify!(dump, zip).group_by(&:kind)
|
125
140
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
141
|
+
info "Writing provinces..."
|
142
|
+
Geonames::Models::Province.from_batch dump[:province]
|
143
|
+
info "Writing cities..."
|
144
|
+
Geonames::Models::City.from_batch dump[:city]
|
145
|
+
end
|
131
146
|
end
|
132
147
|
end
|
133
148
|
|
134
149
|
def load_adapter(name)
|
135
150
|
begin
|
136
|
-
require "geonames_local/adapters/#{name}"
|
137
151
|
require "geonames_local/models/#{name}"
|
138
|
-
Geonames.class_eval(name.capitalize).new(Opt[:db])
|
139
152
|
rescue LoadError
|
140
|
-
puts "Can't find adapter #{name}"
|
153
|
+
puts "Can't find adapter for #{name}"
|
141
154
|
stop!
|
142
155
|
end
|
143
156
|
end
|
144
157
|
|
145
|
-
def
|
146
|
-
if Opt[:codes][0] == "country"
|
147
|
-
Cache[:countries] = Cache[:dump]
|
148
|
-
do_write(db, Cache[:countries])
|
149
|
-
else
|
150
|
-
groups = Cache[:dump].group_by(&:kind)
|
151
|
-
|
152
|
-
Cache[:provinces] = groups[:provinces]
|
153
|
-
Cache[:cities] = groups[:cities]
|
154
|
-
|
155
|
-
do_write(db, Cache[:provinces])
|
156
|
-
do_write(db, Cache[:cities])
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
def do_write(db, values)
|
161
|
-
return if values.empty?
|
162
|
-
|
163
|
-
if Opt[:codes][0] == "country"
|
164
|
-
key = table = "countries"
|
165
|
-
else
|
166
|
-
key = values[0].table
|
167
|
-
end
|
168
|
-
start = Time.now
|
169
|
-
writt = 0
|
170
|
-
info "\nWriting #{values.length} #{key}..."
|
171
|
-
values.each do |val|
|
172
|
-
meth = val.respond_to?(:gid) ? [val.gid] : [val.name, true]
|
173
|
-
unless db.find(table || val.table, *meth)
|
174
|
-
db.insert(table || val.table, val)
|
175
|
-
writt += 1
|
176
|
-
end
|
177
|
-
end
|
178
|
-
total = Time.now - start
|
179
|
-
info "#{writt} #{key} written in #{total} sec (#{(writt/total).to_i}/s)"
|
180
|
-
end
|
181
|
-
|
182
|
-
def unify!
|
158
|
+
def unify! dump, zip
|
183
159
|
start = Time.now
|
184
|
-
|
185
|
-
if other =
|
160
|
+
dump.map! do |spot|
|
161
|
+
if other = zip.find { |d| d.code == spot.code }
|
186
162
|
spot.zip = other.zip
|
187
163
|
spot
|
188
164
|
else
|
@@ -190,6 +166,7 @@ BANNER
|
|
190
166
|
end
|
191
167
|
end
|
192
168
|
info "Done. #{(Time.now-start).to_i}s"
|
169
|
+
dump
|
193
170
|
end
|
194
171
|
|
195
172
|
def stop!
|
@@ -3,23 +3,36 @@ module Geonames
|
|
3
3
|
URL = "http://download.geonames.org/export/"
|
4
4
|
TMP = "/tmp/geonames/"
|
5
5
|
|
6
|
-
def self.work(codes=:all, kind=:dump)
|
7
|
-
new(codes, kind)
|
8
|
-
end
|
9
|
-
|
10
6
|
def initialize(codes, kind)
|
11
7
|
@codes = codes
|
12
8
|
@kind = kind
|
9
|
+
@data = []
|
13
10
|
if codes.respond_to? :each
|
14
11
|
for code in codes
|
15
|
-
|
16
|
-
file = get_file(code)
|
17
|
-
download file
|
18
|
-
uncompress file unless code == "country"
|
19
|
-
parse file
|
12
|
+
work code
|
20
13
|
end
|
14
|
+
elsif codes == :country
|
15
|
+
countries
|
21
16
|
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def countries
|
20
|
+
info "\nDumping country database"
|
21
|
+
file = get_file('country')
|
22
|
+
download file
|
23
|
+
parse file
|
24
|
+
end
|
25
|
+
|
26
|
+
def work code
|
27
|
+
info "\nWorking on #{@kind} for #{code}"
|
28
|
+
file = get_file(code)
|
29
|
+
download file
|
30
|
+
uncompress file
|
31
|
+
parse file
|
32
|
+
end
|
22
33
|
|
34
|
+
def data
|
35
|
+
@data
|
23
36
|
end
|
24
37
|
|
25
38
|
def get_file(code)
|
@@ -43,7 +56,7 @@ module Geonames
|
|
43
56
|
return if l =~ /^#|^iso/i
|
44
57
|
if @kind == :dump
|
45
58
|
if l =~ /^\D/
|
46
|
-
return
|
59
|
+
return l
|
47
60
|
else
|
48
61
|
if Opt[:level] != "all"
|
49
62
|
return unless l =~ /ADM\d/ # ADM2 => cities
|
@@ -59,7 +72,7 @@ module Geonames
|
|
59
72
|
File.open("/tmp/geonames/#{@kind}/#{file.gsub("zip", "txt")}") do |f|
|
60
73
|
while line = f.gets
|
61
74
|
if record = parse_line(line)
|
62
|
-
|
75
|
+
@data << record
|
63
76
|
red += 1
|
64
77
|
end
|
65
78
|
end
|
@@ -11,16 +11,16 @@ module Geonames
|
|
11
11
|
# = Geonames Spot
|
12
12
|
#
|
13
13
|
# Every geoname type will be parsed as a spot
|
14
|
-
def initialize(params=nil,
|
14
|
+
def initialize(params = nil, kind = nil)
|
15
15
|
return unless params.instance_of? String
|
16
|
-
|
16
|
+
kind == :zip ? parse_zip(params) : parse(params)
|
17
17
|
if @kind == :province
|
18
18
|
@name.gsub!(/Estado d\w\s/, "")
|
19
19
|
@abbr = get_abbr
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
# Geonames
|
23
|
+
# Geonames does not have province/state abbr..#fail!
|
24
24
|
# This works 75% of the time in brazil heh
|
25
25
|
def get_abbr
|
26
26
|
s = @name.split(" ")
|
@@ -49,14 +49,11 @@ module Geonames
|
|
49
49
|
#
|
50
50
|
# Parse Geonames Zip Export
|
51
51
|
def parse_zip(row)
|
52
|
-
# country, zip, @name, province, cc, dunno, adm1, adm2, lat, lon = row.split(/\t/)
|
53
52
|
# country, zip, @name, state, state_code, procince, province_code, community, community_code, lat, lon, acc = row.split(/\t/)
|
54
|
-
country, zip, @name,
|
53
|
+
country, zip, @name, a1, @code, a2, a2c, a3, a3c, lat, lon, acc = row.split(/\t/)
|
55
54
|
parse_geom(lat, lon)
|
56
55
|
# @code = adm1
|
57
|
-
|
58
|
-
@code = adm1_code
|
59
|
-
@kind = :cities
|
56
|
+
@kind = :city
|
60
57
|
@zip = zip.split("-")[0]
|
61
58
|
end
|
62
59
|
|
@@ -82,39 +79,17 @@ module Geonames
|
|
82
79
|
Time.utc(*@up.split("-"))
|
83
80
|
end
|
84
81
|
|
85
|
-
#
|
86
|
-
def to_hash
|
87
|
-
{ "id" => @geoname_id, "gid" => @geoname_id.to_s, "kind" => @kind.to_s,
|
88
|
-
"name" => @name, "ascii" => @ascii, "country" => @country,
|
89
|
-
"geom" => [@geom.x, @geom.y], "tz" => @tz }
|
90
|
-
end
|
91
|
-
|
82
|
+
# Translate geonames ADMx to models
|
92
83
|
def human_code(code)
|
93
84
|
case code
|
94
|
-
when 'ADM1' then :
|
95
|
-
when 'ADM2', 'ADM3', 'ADM4' then :
|
85
|
+
when 'ADM1' then :province
|
86
|
+
when 'ADM2', 'ADM3', 'ADM4' then :city
|
96
87
|
else :other
|
97
88
|
end
|
98
89
|
end
|
99
90
|
|
100
91
|
class << self
|
101
92
|
|
102
|
-
def all
|
103
|
-
Adapter.all(@coll)
|
104
|
-
end
|
105
|
-
|
106
|
-
def first
|
107
|
-
from_hash(Adapter.first(@coll))
|
108
|
-
end
|
109
|
-
|
110
|
-
def find(id)
|
111
|
-
Adapter.find(@coll, id)
|
112
|
-
end
|
113
|
-
|
114
|
-
def find_by_name(name)
|
115
|
-
Adapter.find_by_name(@coll, name).map { |hsh| from_hash(hsh) }
|
116
|
-
end
|
117
|
-
|
118
93
|
def nearest(x,y)
|
119
94
|
from_hash(Adapter.find_near(@coll, x, y, 1)[0])
|
120
95
|
end
|
@@ -1,11 +1,6 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'logger'
|
3
2
|
require 'yaml'
|
4
3
|
|
5
|
-
require 'geonames_local/features/spot'
|
6
|
-
require 'geonames_local/features/road'
|
7
|
-
require 'geonames_local/features/zone'
|
8
|
-
|
9
4
|
module Geonames
|
10
5
|
Opt = {}
|
11
6
|
Cache = {:dump => [], :zip => [], :roads => [], :zones => []}
|
@@ -1,62 +1,180 @@
|
|
1
|
+
require 'mongoid'
|
2
|
+
require 'mongoid_geospatial'
|
3
|
+
|
4
|
+
Mongoid.configure do |config|
|
5
|
+
#config.master = Mongo::Connection.new.db("symbolize_test")
|
6
|
+
config.connect_to(Opt[:db][:name])
|
7
|
+
end
|
8
|
+
|
1
9
|
module Geonames
|
2
10
|
module Models
|
3
|
-
module Mongo
|
4
11
|
|
5
|
-
|
6
|
-
|
12
|
+
|
13
|
+
class City < Geonames::Spot
|
14
|
+
include Mongoid::Document
|
15
|
+
include Mongoid::Geospatial
|
16
|
+
store_in :collection => "cities"
|
17
|
+
|
18
|
+
field :ascii, type: String
|
19
|
+
field :slug, type: String
|
20
|
+
field :name, type: String
|
21
|
+
field :abbr, type: String
|
22
|
+
field :area
|
23
|
+
field :gid, type: Integer
|
24
|
+
field :zip, type: Integer
|
25
|
+
field :geom, type: Point, spatial: true
|
26
|
+
|
27
|
+
belongs_to :province
|
28
|
+
belongs_to :country, index: true
|
29
|
+
# has_many :hoods
|
30
|
+
|
31
|
+
before_validation :set_defaults
|
32
|
+
|
33
|
+
validates :name, :country, presence: true
|
34
|
+
validates :name, :uniqueness => { :scope => :province_id }
|
35
|
+
|
36
|
+
index name: 1
|
37
|
+
index slug: 1
|
38
|
+
index geom: '2d'
|
39
|
+
|
40
|
+
#spatial_index :geom
|
41
|
+
|
42
|
+
scope :ordered, order_by(name: 1)
|
43
|
+
|
44
|
+
def set_defaults
|
45
|
+
self.country ||= province.try(:country)
|
46
|
+
self.slug ||= name.try(:downcase) # don't use slugize
|
7
47
|
end
|
8
48
|
|
9
|
-
|
10
|
-
|
11
|
-
|
49
|
+
def self.search(search, page)
|
50
|
+
cities = search ? where(:field => /#{search}/i) : all
|
51
|
+
cities.page(page)
|
52
|
+
end
|
12
53
|
|
13
|
-
|
14
|
-
|
15
|
-
|
54
|
+
def <=> other
|
55
|
+
self.name <=> other.name
|
56
|
+
end
|
16
57
|
|
17
|
-
|
18
|
-
|
19
|
-
|
58
|
+
def to_s
|
59
|
+
"#{name}/#{abbr}"
|
60
|
+
end
|
20
61
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
62
|
+
def self.from_batch data
|
63
|
+
data.each do |city|
|
64
|
+
info "Writing city #{city.name}"
|
65
|
+
next unless city.country
|
66
|
+
city = new.parse(city)
|
67
|
+
city.country = city.province.country
|
68
|
+
city.save
|
69
|
+
end
|
70
|
+
end
|
26
71
|
|
27
|
-
|
28
|
-
|
29
|
-
|
72
|
+
def parse(spot)
|
73
|
+
self.name, self.ascii = spot.name, spot.ascii
|
74
|
+
self.code, self.gid = spot.code, spot.gid
|
75
|
+
self.province = Province.find_by(code: spot.province)
|
76
|
+
self.geom = [spot.lon, spot.lat]
|
77
|
+
self.abbr = province.abbr
|
78
|
+
self
|
79
|
+
end
|
30
80
|
|
31
|
-
def to_hash
|
32
|
-
{ "gid" => @gid.to_s, "name" => @name, "kind" => "country", "code" => @code}
|
33
81
|
end
|
34
82
|
|
35
|
-
def export
|
36
|
-
[@gid, @code, @name]
|
37
|
-
end
|
38
83
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
84
|
+
class Province < Geonames::Spot
|
85
|
+
include Mongoid::Document
|
86
|
+
store_in :collection => "provinces"
|
87
|
+
|
88
|
+
field :gid, type: Integer # geonames id
|
89
|
+
field :code, type: String
|
90
|
+
field :name, type: String
|
91
|
+
field :abbr, type: String
|
92
|
+
field :codes, type: Array # phone code
|
93
|
+
|
94
|
+
belongs_to :country
|
95
|
+
has_many :cities
|
96
|
+
|
97
|
+
validates :name, presence: true
|
98
|
+
validates :country, presence: true
|
43
99
|
|
44
|
-
|
45
|
-
|
100
|
+
index name: 1
|
101
|
+
index codes: 1
|
46
102
|
|
47
|
-
|
48
|
-
|
49
|
-
|
103
|
+
scope :ordered, order_by(name: 1)
|
104
|
+
|
105
|
+
def self.from_batch data
|
106
|
+
data.each do |province|
|
107
|
+
info "Writing province #{province.name}"
|
108
|
+
next unless province.country
|
109
|
+
province = new.parse(province)
|
110
|
+
province.country = Country.find_by(abbr: /#{province.country}/i)
|
111
|
+
province.save!
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def parse(spot)
|
116
|
+
self.code, self.name = spot.province, spot.name
|
117
|
+
self.gid = spot.gid
|
118
|
+
self
|
50
119
|
end
|
120
|
+
|
51
121
|
end
|
52
122
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
123
|
+
|
124
|
+
class Country < Geonames::Spot
|
125
|
+
include Mongoid::Document
|
126
|
+
store_in :collection => "countries"
|
127
|
+
|
128
|
+
field :gid, type: Integer # geonames id
|
129
|
+
field :name, type: String
|
130
|
+
field :abbr, type: String
|
131
|
+
field :code # optional phone/whatever code
|
132
|
+
|
133
|
+
has_many :cities
|
134
|
+
has_many :provinces
|
135
|
+
|
136
|
+
validates :abbr, :name, presence: true, uniqueness: true
|
137
|
+
|
138
|
+
index name: 1
|
139
|
+
index code: 1
|
140
|
+
|
141
|
+
scope :ordered, order_by(name: 1)
|
142
|
+
|
143
|
+
|
144
|
+
def parse row
|
145
|
+
self.abbr, @iso3, @ison, @fips, self.name, @capital, @area, @pop, continent, tld,
|
146
|
+
currency, phone, postal, langs, gid, neighbours = row.split(/\t/)
|
147
|
+
self
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.from_batch data
|
151
|
+
data.each do |spot|
|
152
|
+
new.parse(spot).save
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def to_hash
|
157
|
+
{ "gid" => @gid.to_s, "name" => @name, "kind" => "country", "code" => @code}
|
158
|
+
end
|
159
|
+
|
160
|
+
def export
|
161
|
+
[@gid, @code, @name]
|
162
|
+
end
|
163
|
+
|
164
|
+
def export_header
|
165
|
+
["gid", "code", "name"]
|
166
|
+
end
|
57
167
|
end
|
58
168
|
|
59
|
-
|
169
|
+
|
170
|
+
|
171
|
+
class Zip
|
172
|
+
include Mongoid::Document
|
173
|
+
|
174
|
+
field :code
|
175
|
+
belongs_to :city
|
176
|
+
|
60
177
|
end
|
178
|
+
|
61
179
|
end
|
62
180
|
end
|