geonames_local 1.0.0 → 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.
- 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
|