us_zipcode 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.travis.yml +19 -0
- data/Gemfile +21 -0
- data/MIT-LICENSE +20 -0
- data/README.textile +179 -0
- data/README.textile~ +184 -0
- data/Rakefile +19 -0
- data/features/step_definitions/common_steps.rb +76 -0
- data/features/step_definitions/rails_setup_steps.rb +17 -0
- data/features/step_definitions/rails_setup_steps.rb~ +17 -0
- data/features/support/env.rb +10 -0
- data/features/support/string.rb +128 -0
- data/features/zipcodes.feature +24 -0
- data/features/zipcodes.feature~ +24 -0
- data/lib/generators/us_zipcode/models_generator.rb +41 -0
- data/lib/generators/us_zipcode/models_generator.rb~ +41 -0
- data/lib/generators/us_zipcode/templates/county_model.rb +17 -0
- data/lib/generators/us_zipcode/templates/county_model.rb~ +17 -0
- data/lib/generators/us_zipcode/templates/migration.rb +41 -0
- data/lib/generators/us_zipcode/templates/migration.rb~ +41 -0
- data/lib/generators/us_zipcode/templates/state_model.rb +15 -0
- data/lib/generators/us_zipcode/templates/state_model.rb~ +15 -0
- data/lib/generators/us_zipcode/templates/zipcode_model.rb +30 -0
- data/lib/generators/us_zipcode/templates/zipcode_model.rb~ +30 -0
- data/lib/generators/us_zipcode/templates/zipcodes.rake +163 -0
- data/lib/generators/us_zipcode/templates/zipcodes.rake~ +163 -0
- data/lib/generators/us_zipcode/version.rb +3 -0
- data/lib/generators/us_zipcode/version.rb~ +3 -0
- data/lib/my_zipcode_gem.rb~ +34 -0
- data/lib/us_zipcode.rb +29 -0
- data/lib/us_zipcode.rb~ +29 -0
- data/rails3_2.gemfile +21 -0
- data/rails4_0.gemfile +23 -0
- data/rails4_1.gemfile +23 -0
- data/us_zipcode.gemspec +24 -0
- data/us_zipcode.gemspec~ +24 -0
- metadata +108 -0
@@ -0,0 +1,128 @@
|
|
1
|
+
class String
|
2
|
+
|
3
|
+
class << self
|
4
|
+
def random(count = 6, ranges = [('a'..'z'),('A'..'Z'),('0'..'9')])
|
5
|
+
o = ranges.map{|i| i.to_a}.flatten;
|
6
|
+
string = (0..(count-1)).map{ o[rand(o.length)] }.join;
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def left(count)
|
11
|
+
self.slice(0,count)
|
12
|
+
end
|
13
|
+
|
14
|
+
def right(count)
|
15
|
+
self.slice(-count,count)
|
16
|
+
end
|
17
|
+
|
18
|
+
def left_trim
|
19
|
+
# remove leading whitespace
|
20
|
+
self.gsub(/^[\t\s]+/, '')
|
21
|
+
end
|
22
|
+
|
23
|
+
def right_trim
|
24
|
+
# remove trailing whitespace
|
25
|
+
self.gsub(/[\t\s]+$/, '')
|
26
|
+
end
|
27
|
+
|
28
|
+
def trim
|
29
|
+
# remove leading and trailing whitespace
|
30
|
+
self.left_trim.right_trim
|
31
|
+
end
|
32
|
+
|
33
|
+
# html = <<-stop.here_with_pipe
|
34
|
+
# |<!-- Begin: comment -->
|
35
|
+
# |<script type="text/javascript">
|
36
|
+
# stop
|
37
|
+
def here_with_pipe(linefeeds = false)
|
38
|
+
lines = self.split("\n")
|
39
|
+
lines.map! {|c| c.sub!(/\s*\|/, '')}
|
40
|
+
new_string = lines.join(linefeeds ? "\n" : " ")
|
41
|
+
self.replace(new_string)
|
42
|
+
end
|
43
|
+
|
44
|
+
def is_alpha_numeric?
|
45
|
+
regex = /^[a-zA-Z0-9]+$/
|
46
|
+
return (self =~ regex) == 0 ? true : false
|
47
|
+
end
|
48
|
+
|
49
|
+
def is_email_address?
|
50
|
+
# //Email address
|
51
|
+
# //Use this version to seek out email addresses in random documents and texts.
|
52
|
+
# //Does not match email addresses using an IP address instead of a domain name.
|
53
|
+
# //Does not match email addresses on new-fangled top-level domains with more than 4 letters such as .museum.
|
54
|
+
# //Including these increases the risk of false positives when applying the regex to random documents.
|
55
|
+
# '\b[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b'
|
56
|
+
#
|
57
|
+
# //Email address (anchored)
|
58
|
+
# //Use this anchored version to check if a valid email address was entered.
|
59
|
+
# //Does not match email addresses using an IP address instead of a domain name.
|
60
|
+
# //Does not match email addresses on new-fangled top-level domains with more than 4 letters such as .museum.
|
61
|
+
# //Requires the "case insensitive" option to be ON.
|
62
|
+
# '^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$'
|
63
|
+
#
|
64
|
+
# //Email address (anchored; no consecutive dots)
|
65
|
+
# //Use this anchored version to check if a valid email address was entered.
|
66
|
+
# //Improves on the original email address regex by excluding addresses with consecutive dots such as john@aol...com
|
67
|
+
# //Does not match email addresses using an IP address instead of a domain name.
|
68
|
+
# //Does not match email addresses on new-fangled top-level domains with more than 4 letters such as .museum.
|
69
|
+
# //Including these increases the risk of false positives when applying the regex to random documents.
|
70
|
+
# '^[A-Z0-9._%-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$'
|
71
|
+
#
|
72
|
+
# //Email address (no consecutive dots)
|
73
|
+
# //Use this version to seek out email addresses in random documents and texts.
|
74
|
+
# //Improves on the original email address regex by excluding addresses with consecutive dots such as john@aol...com
|
75
|
+
# //Does not match email addresses using an IP address instead of a domain name.
|
76
|
+
# //Does not match email addresses on new-fangled top-level domains with more than 4 letters such as .museum.
|
77
|
+
# //Including these increases the risk of false positives when applying the regex to random documents.
|
78
|
+
# '\b[A-Z0-9._%-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}\b'
|
79
|
+
#
|
80
|
+
# //Email address (specific TLDs)
|
81
|
+
# //Does not match email addresses using an IP address instead of a domain name.
|
82
|
+
# //Matches all country code top level domains, and specific common top level domains.
|
83
|
+
# '^[A-Z0-9._%-]+@[A-Z0-9.-]+\.(?:[A-Z]{2}|com|org|net|biz|info|name|aero|biz|info|jobs|museum|name)$'
|
84
|
+
#
|
85
|
+
# //Email address: Replace with HTML link
|
86
|
+
# '\b(?:mailto:)?([A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4})\b'
|
87
|
+
|
88
|
+
email_regex = %r{^[A-Z0-9._%-]+@[A-Z0-9.-]+\.(?:[A-Z]{2}|com|org|net|biz|info|name|aero|biz|info|jobs|museum|name)$}xi # Case insensitive
|
89
|
+
|
90
|
+
return (self =~ email_regex) == 0 ? true : false
|
91
|
+
end
|
92
|
+
|
93
|
+
def is_zipcode?
|
94
|
+
self =~ %r{^(\d{5})(-\d{4})?$}x ? true : false
|
95
|
+
end
|
96
|
+
|
97
|
+
def format_phone
|
98
|
+
'(' << slice(0..2) << ')' << slice(3..5) << '-' << slice(-4,4)
|
99
|
+
end
|
100
|
+
|
101
|
+
def is_numeric?
|
102
|
+
begin
|
103
|
+
Float(self)
|
104
|
+
rescue
|
105
|
+
false # not numeric
|
106
|
+
else
|
107
|
+
true # numeric
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def sanitize
|
112
|
+
clean_string = self.gsub(/[^a-z0-9,! \-\(\)\:\;\.\&\$]+/i, '')
|
113
|
+
#p "SAN: #{clean_string}"
|
114
|
+
clean_string
|
115
|
+
end
|
116
|
+
|
117
|
+
def shorten(count = 30)
|
118
|
+
if self.length >= count
|
119
|
+
shortened = self[0, count]
|
120
|
+
splitted = shortened.split(/\s/)
|
121
|
+
words = splitted.length
|
122
|
+
splitted[0, words-1].join(" ") + ' ...'
|
123
|
+
else
|
124
|
+
self
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Feature: Us ZipCode Gem
|
2
|
+
In order to manage zipcode resources
|
3
|
+
As a rails developer
|
4
|
+
I want to generate models for zipcode, county and state, and populate their tables
|
5
|
+
|
6
|
+
Scenario: Generate models and migration for zipcode, county and state
|
7
|
+
Given a new Rails app
|
8
|
+
Then I should see "us_zipcode:models" when running "rails g"
|
9
|
+
When I run "rails g us_zipcode:models"
|
10
|
+
Then I should see the following files
|
11
|
+
| app/models/zipcode.rb |
|
12
|
+
| app/models/state.rb |
|
13
|
+
| app/models/county.rb |
|
14
|
+
| lib/tasks/zipcodes.rake |
|
15
|
+
| db/migrate |
|
16
|
+
And I should see "gem "mocha"" in file "Gemfile"
|
17
|
+
And I should successfully run "rake db:migrate"
|
18
|
+
|
19
|
+
Scenario: Update data for zipcodes, counties and states tables
|
20
|
+
Given a new migrated Rails app
|
21
|
+
Then I should successfully run "rake zipcodes:update"
|
22
|
+
And I should see 51 records in the "states" table
|
23
|
+
And I should see 3142 records in the "counties" table
|
24
|
+
And I should see 42366 records in the "zipcodes" table
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Feature: My Zipcode Gem
|
2
|
+
In order to manage zipcode resources
|
3
|
+
As a rails developer
|
4
|
+
I want to generate models for zipcode, county and state, and populate their tables
|
5
|
+
|
6
|
+
Scenario: Generate models and migration for zipcode, county and state
|
7
|
+
Given a new Rails app
|
8
|
+
Then I should see "my_zipcode_gem:models" when running "rails g"
|
9
|
+
When I run "rails g my_zipcode_gem:models"
|
10
|
+
Then I should see the following files
|
11
|
+
| app/models/zipcode.rb |
|
12
|
+
| app/models/state.rb |
|
13
|
+
| app/models/county.rb |
|
14
|
+
| lib/tasks/zipcodes.rake |
|
15
|
+
| db/migrate |
|
16
|
+
And I should see "gem "mocha"" in file "Gemfile"
|
17
|
+
And I should successfully run "rake db:migrate"
|
18
|
+
|
19
|
+
Scenario: Update data for zipcodes, counties and states tables
|
20
|
+
Given a new migrated Rails app
|
21
|
+
Then I should successfully run "rake zipcodes:update"
|
22
|
+
And I should see 51 records in the "states" table
|
23
|
+
And I should see 3142 records in the "counties" table
|
24
|
+
And I should see 42366 records in the "zipcodes" table
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module UsZipcode
|
2
|
+
class ModelsGenerator < Base
|
3
|
+
include Rails::Generators::Migration
|
4
|
+
|
5
|
+
source_root File.expand_path('../templates', __FILE__)
|
6
|
+
|
7
|
+
def initialize(*args, &block)
|
8
|
+
super
|
9
|
+
migration_template 'migration.rb', "db/migrate/create_us_zipcode_models.rb"
|
10
|
+
end
|
11
|
+
|
12
|
+
def generate_models
|
13
|
+
# puts ">>> generate_zipcodes:"
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_gems
|
17
|
+
add_gem "mocha", :group => :test
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_models
|
21
|
+
template 'zipcode_model.rb', "app/models/zipcode.rb"
|
22
|
+
template 'county_model.rb', "app/models/county.rb"
|
23
|
+
template 'state_model.rb', "app/models/state.rb"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Implement the required interface for Rails::Generators::Migration.
|
27
|
+
# taken from http://github.com/rails/rails/blob/master/activerecord/lib/generators/active_record.rb
|
28
|
+
def self.next_migration_number(dirname)
|
29
|
+
if ActiveRecord::Base.timestamped_migrations
|
30
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
31
|
+
else
|
32
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_rakefile
|
37
|
+
template 'zipcodes.rake', "lib/tasks/zipcodes.rake"
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module MyZipcodeGem
|
2
|
+
class ModelsGenerator < Base
|
3
|
+
include Rails::Generators::Migration
|
4
|
+
|
5
|
+
source_root File.expand_path('../templates', __FILE__)
|
6
|
+
|
7
|
+
def initialize(*args, &block)
|
8
|
+
super
|
9
|
+
migration_template 'migration.rb', "db/migrate/create_my_zipcode_gem_models.rb"
|
10
|
+
end
|
11
|
+
|
12
|
+
def generate_models
|
13
|
+
# puts ">>> generate_zipcodes:"
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_gems
|
17
|
+
add_gem "mocha", :group => :test
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_models
|
21
|
+
template 'zipcode_model.rb', "app/models/zipcode.rb"
|
22
|
+
template 'county_model.rb', "app/models/county.rb"
|
23
|
+
template 'state_model.rb', "app/models/state.rb"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Implement the required interface for Rails::Generators::Migration.
|
27
|
+
# taken from http://github.com/rails/rails/blob/master/activerecord/lib/generators/active_record.rb
|
28
|
+
def self.next_migration_number(dirname)
|
29
|
+
if ActiveRecord::Base.timestamped_migrations
|
30
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
31
|
+
else
|
32
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_rakefile
|
37
|
+
template 'zipcodes.rake', "lib/tasks/zipcodes.rake"
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'memoist'
|
2
|
+
class County < ActiveRecord::Base
|
3
|
+
extend Memoist
|
4
|
+
|
5
|
+
belongs_to :state
|
6
|
+
has_many :zipcodes
|
7
|
+
|
8
|
+
validates :name, uniqueness: {:scope => :state_id, :case_sensitive => false}, presence: true
|
9
|
+
|
10
|
+
scope :without_zipcodes, -> { joins("LEFT JOIN zipcodes ON zipcodes.county_id = counties.id").where("zipcodes.county_id IS NULL")}
|
11
|
+
scope :without_state, -> {where("state_id IS NULL")}
|
12
|
+
|
13
|
+
def cities
|
14
|
+
zipcodes.map(&:city).sort.uniq
|
15
|
+
end
|
16
|
+
memoize :cities
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'memoist'
|
2
|
+
class County < ActiveRecord::Base
|
3
|
+
extend Memoist
|
4
|
+
|
5
|
+
belongs_to :state
|
6
|
+
has_many :zipcodes
|
7
|
+
|
8
|
+
validates :name, uniqueness: {:scope => :state_id, :case_sensitive => false}, :presence => true
|
9
|
+
|
10
|
+
scope :without_zipcodes, -> { joins("LEFT JOIN zipcodes ON zipcodes.county_id = counties.id").where("zipcodes.county_id IS NULL")}
|
11
|
+
scope :without_state, -> {where("state_id IS NULL")}
|
12
|
+
|
13
|
+
def cities
|
14
|
+
zipcodes.map(&:city).sort.uniq
|
15
|
+
end
|
16
|
+
memoize :cities
|
17
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class CreateUsZipcodeModels < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
# Zipcodes Table
|
4
|
+
create_table :zipcodes do |t|
|
5
|
+
t.string :code
|
6
|
+
t.string :city
|
7
|
+
t.integer :state_id, foreign_key: true
|
8
|
+
t.integer :county_id, foreign_key: true
|
9
|
+
t.decimal :lat, :precision => 15, :scale => 10
|
10
|
+
t.decimal :lon, :precision => 15, :scale => 10
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
add_index :zipcodes, :code
|
14
|
+
add_index :zipcodes, :county_id
|
15
|
+
add_index :zipcodes, :state_id
|
16
|
+
add_index :zipcodes, [:lat, :lon]
|
17
|
+
|
18
|
+
# States Table
|
19
|
+
create_table :states do |t|
|
20
|
+
t.string :abbr, :limit => 2
|
21
|
+
t.string :name
|
22
|
+
t.timestamps
|
23
|
+
end
|
24
|
+
add_index :states, :abbr
|
25
|
+
|
26
|
+
# Counties Table
|
27
|
+
create_table :counties do |t|
|
28
|
+
t.integer :state_id
|
29
|
+
t.string :name
|
30
|
+
t.timestamps
|
31
|
+
end
|
32
|
+
add_index :counties, :name
|
33
|
+
add_index :counties, :state_id
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.down
|
37
|
+
drop_table :counties
|
38
|
+
drop_table :states
|
39
|
+
drop_table :zipcodes
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class CreateMyZipcodeGemModels < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
# Zipcodes Table
|
4
|
+
create_table :zipcodes do |t|
|
5
|
+
t.string :code
|
6
|
+
t.string :city
|
7
|
+
t.integer :state_id, foreign_key: true
|
8
|
+
t.integer :county_id, foreign_key: true
|
9
|
+
t.decimal :lat, :precision => 15, :scale => 10
|
10
|
+
t.decimal :lon, :precision => 15, :scale => 10
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
add_index :zipcodes, :code
|
14
|
+
add_index :zipcodes, :county_id
|
15
|
+
add_index :zipcodes, :state_id
|
16
|
+
add_index :zipcodes, [:lat, :lon]
|
17
|
+
|
18
|
+
# States Table
|
19
|
+
create_table :states do |t|
|
20
|
+
t.string :abbr, :limit => 2
|
21
|
+
t.string :name
|
22
|
+
t.timestamps
|
23
|
+
end
|
24
|
+
add_index :states, :abbr
|
25
|
+
|
26
|
+
# Counties Table
|
27
|
+
create_table :counties do |t|
|
28
|
+
t.integer :state_id
|
29
|
+
t.string :name
|
30
|
+
t.timestamps
|
31
|
+
end
|
32
|
+
add_index :counties, :name
|
33
|
+
add_index :counties, :state_id
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.down
|
37
|
+
drop_table :counties
|
38
|
+
drop_table :states
|
39
|
+
drop_table :zipcodes
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'memoist'
|
2
|
+
class State < ActiveRecord::Base
|
3
|
+
extend Memoist
|
4
|
+
|
5
|
+
has_many :zipcodes
|
6
|
+
has_many :counties
|
7
|
+
|
8
|
+
validates :abbr, uniqueness: { :case_sensitive => false }, presence: true
|
9
|
+
validates :name, uniqueness: { :case_sensitive => false }, presence: true
|
10
|
+
|
11
|
+
def cities
|
12
|
+
zipcodes.map(&:city).sort.uniq
|
13
|
+
end
|
14
|
+
memoize :cities
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'memoist'
|
2
|
+
class State < ActiveRecord::Base
|
3
|
+
extend Memoist
|
4
|
+
|
5
|
+
has_many :zipcodes
|
6
|
+
has_many :counties
|
7
|
+
|
8
|
+
validates :abbr, uniqueness: { :case_sensitive => false }, :presence => true
|
9
|
+
validates :name, uniqueness: { :case_sensitive => false }, :presence => true
|
10
|
+
|
11
|
+
def cities
|
12
|
+
zipcodes.map(&:city).sort.uniq
|
13
|
+
end
|
14
|
+
memoize :cities
|
15
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Zipcode < ActiveRecord::Base
|
2
|
+
|
3
|
+
belongs_to :county
|
4
|
+
belongs_to :state
|
5
|
+
|
6
|
+
validates :code, uniqueness: true, presence: true
|
7
|
+
validates :state_id, :county_id, :city, presence: true
|
8
|
+
|
9
|
+
scope :without_county, -> { where("county_id IS NULL") }
|
10
|
+
scope :without_state, -> { where("state_id IS NULL") }
|
11
|
+
scope :ungeocoded, -> { where("lat IS NULL OR lon IS NULL") }
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def find_by_city(city)
|
15
|
+
includes(:county).where("city like '#{city}%'")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def county_and_zip
|
20
|
+
"#{county.name}: #{code}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def latlon
|
24
|
+
[lat, lon]
|
25
|
+
end
|
26
|
+
|
27
|
+
def is_geocoded?
|
28
|
+
(!lat.nil? && !lon.nil?)
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Zipcode < ActiveRecord::Base
|
2
|
+
|
3
|
+
belongs_to :county
|
4
|
+
belongs_to :state
|
5
|
+
|
6
|
+
validates :code, uniqueness: true, presence: true
|
7
|
+
validates :state_id, :county_id, :city, :presence => true
|
8
|
+
|
9
|
+
scope :without_county, -> { where("county_id IS NULL") }
|
10
|
+
scope :without_state, -> { where("state_id IS NULL") }
|
11
|
+
scope :ungeocoded, -> { where("lat IS NULL OR lon IS NULL") }
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def find_by_city(city)
|
15
|
+
includes(:county).where("city like '#{city}%'")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def county_and_zip
|
20
|
+
"#{county.name}: #{code}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def latlon
|
24
|
+
[lat, lon]
|
25
|
+
end
|
26
|
+
|
27
|
+
def is_geocoded?
|
28
|
+
(!lat.nil? && !lon.nil?)
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'csv'
|
3
|
+
namespace :zipcodes do
|
4
|
+
|
5
|
+
desc "Update states table"
|
6
|
+
task :update_states => :environment do
|
7
|
+
puts ">>> Begin update of states table..."
|
8
|
+
url = "https://github.com/midwire/free_zipcode_data/raw/master/all_us_states.csv"
|
9
|
+
data = open(url)
|
10
|
+
file = nil
|
11
|
+
if data.is_a? StringIO
|
12
|
+
file = Tempfile.new('all_us_states.csv')
|
13
|
+
file.write(data.read)
|
14
|
+
file.flush
|
15
|
+
file.close
|
16
|
+
else
|
17
|
+
file = data
|
18
|
+
end
|
19
|
+
CSV.foreach(file.path, :headers => true) do |row|
|
20
|
+
puts "Updating state: [#{row['name']}]"
|
21
|
+
state = State.where(abbr: row['abbr']).first_or_initialize
|
22
|
+
state.update_attribute(:name, row['name'])
|
23
|
+
end
|
24
|
+
data.close
|
25
|
+
puts ">>> End update of states table..."
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Update counties table"
|
29
|
+
task :update_counties => :update_states do
|
30
|
+
puts ">>> Begin update of counties table..."
|
31
|
+
url = "https://github.com/midwire/free_zipcode_data/raw/master/all_us_counties.csv"
|
32
|
+
data = open(url)
|
33
|
+
file = nil
|
34
|
+
if data.is_a? StringIO
|
35
|
+
file = Tempfile.new('all_us_counties.csv')
|
36
|
+
file.write(data.read)
|
37
|
+
file.flush
|
38
|
+
file.close
|
39
|
+
else
|
40
|
+
file = data
|
41
|
+
end
|
42
|
+
CSV.foreach(file.path, :headers => true) do |row|
|
43
|
+
puts "Updating county: [#{row['name']}]"
|
44
|
+
# lookup state
|
45
|
+
state = State.find_by_abbr!(row['state'])
|
46
|
+
county = County.where(name: row['name'], state_id: state.to_param).first_or_initialize
|
47
|
+
county.save
|
48
|
+
end
|
49
|
+
data.close
|
50
|
+
puts ">>> End update of counties table..."
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "Update zipcodes table"
|
54
|
+
task :update_zipcodes => :update_counties do
|
55
|
+
puts ">>> Begin update of zipcodes table..."
|
56
|
+
url = "https://github.com/midwire/free_zipcode_data/raw/master/all_us_zipcodes.csv"
|
57
|
+
data = open(url)
|
58
|
+
file = nil
|
59
|
+
if data.is_a? StringIO
|
60
|
+
file = Tempfile.new('all_us_zipcodes.csv')
|
61
|
+
file.write(data.read)
|
62
|
+
file.flush
|
63
|
+
file.close
|
64
|
+
else
|
65
|
+
file = data
|
66
|
+
end
|
67
|
+
CSV.foreach(file.path, :headers => true) do |row|
|
68
|
+
puts "Updating zipcode: [#{row['code']}], '#{row['city']}, #{row['state']}, #{row['county']}"
|
69
|
+
# lookup state
|
70
|
+
state = State.find_by_abbr!(row['state'])
|
71
|
+
begin
|
72
|
+
county = County.find_by_name_and_state_id!(row['county'], state.to_param)
|
73
|
+
rescue Exception => e
|
74
|
+
puts ">>> e: [#{e}]"
|
75
|
+
puts ">>>> No county found for zipcode: [#{row['code']}], '#{row['city']}, #{row['state']}, #{row['county']}... SKIPPING..."
|
76
|
+
next
|
77
|
+
end
|
78
|
+
zipcode = Zipcode.where(code: row['code']).first_or_initialize
|
79
|
+
zipcode.update_attributes!(
|
80
|
+
:city => row['city'].titleize,
|
81
|
+
:state_id => state.to_param,
|
82
|
+
:county_id => county.to_param,
|
83
|
+
:lat => row['lat'],
|
84
|
+
:lon => row['lon']
|
85
|
+
)
|
86
|
+
end
|
87
|
+
data.close
|
88
|
+
puts ">>> End update of zipcodes table..."
|
89
|
+
end
|
90
|
+
|
91
|
+
desc "Populate or update the zipcodes related tables"
|
92
|
+
task :update => :environment do
|
93
|
+
Rake::Task['zipcodes:update_zipcodes'].invoke
|
94
|
+
end
|
95
|
+
|
96
|
+
desc "Export US States to a .csv file"
|
97
|
+
task :export_states => :environment do
|
98
|
+
@states = State.order("name ASC")
|
99
|
+
csv_string = CSV.generate do |csv|
|
100
|
+
csv << ["abbr", "name"]
|
101
|
+
@states.each do |state|
|
102
|
+
csv << [
|
103
|
+
state.abbr,
|
104
|
+
state.name
|
105
|
+
]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
filename = "all_us_states.csv"
|
109
|
+
open("#{Rails.root}/db/#{filename}", 'w') do |f|
|
110
|
+
f.write(csv_string)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
desc "Export all US Counties to a .csv file"
|
115
|
+
task :export_counties => :environment do
|
116
|
+
@counties = County.order("name ASC")
|
117
|
+
csv_string = CSV.generate do |csv|
|
118
|
+
csv << ["name", "state", "county_seat"]
|
119
|
+
@counties.each do |county|
|
120
|
+
csv << [
|
121
|
+
county.name,
|
122
|
+
county.state.abbr,
|
123
|
+
county.county_seat
|
124
|
+
]
|
125
|
+
end
|
126
|
+
end
|
127
|
+
filename = "all_us_counties.csv"
|
128
|
+
open("#{Rails.root}/db/#{filename}", 'w') do |f|
|
129
|
+
f.write(csv_string)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
desc "Export the zipcodes with county and state data"
|
134
|
+
task :export_zipcodes => :environment do
|
135
|
+
@zipcodes = Zipcode.order("code ASC")
|
136
|
+
csv_string = CSV.generate do |csv|
|
137
|
+
csv << ["code", "city", "state", "county", "area_code", "lat", "lon"]
|
138
|
+
@zipcodes.each do |zip|
|
139
|
+
csv << [
|
140
|
+
zip.code,
|
141
|
+
zip.city,
|
142
|
+
zip.state.abbr,
|
143
|
+
zip.county.nil? ? '' : zip.county.name,
|
144
|
+
zip.area_code,
|
145
|
+
zip.lat,
|
146
|
+
zip.lon
|
147
|
+
]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
filename = "all_us_zipcodes.csv"
|
151
|
+
open("#{Rails.root}/db/#{filename}", 'w') do |f|
|
152
|
+
f.write(csv_string)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
desc "Export zipcodes, states and counties tables"
|
157
|
+
task :export => :environment do
|
158
|
+
Rake::Task['zipcodes:export_states'].invoke
|
159
|
+
Rake::Task['zipcodes:export_counties'].invoke
|
160
|
+
Rake::Task['zipcodes:export_zipcodes'].invoke
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|