us_zipcode 0.1.1
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.
- 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
|