usa 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +38 -0
- data/LICENSE.txt +21 -0
- data/README.md +120 -0
- data/app/models/concerns/usa/seeded.rb +22 -0
- data/app/models/usa/city.rb +32 -0
- data/app/models/usa/city_county.rb +23 -0
- data/app/models/usa/county.rb +42 -0
- data/app/models/usa/record.rb +6 -0
- data/app/models/usa/state.rb +39 -0
- data/app/models/usa/zip.rb +29 -0
- data/db/migrate/20260914120000_create_usa_states.rb +12 -0
- data/db/migrate/20260914120001_create_usa_counties.rb +12 -0
- data/db/migrate/20260914120002_create_usa_zips.rb +12 -0
- data/db/migrate/20260914120003_create_usa_cities.rb +12 -0
- data/db/migrate/20260914120004_create_usa_city_counties.rb +9 -0
- data/db/migrate/20260914120005_seed_usa.rb +5 -0
- data/db/seeds/cities.csv +32059 -0
- data/db/seeds/counties.csv +3145 -0
- data/db/seeds/states.csv +52 -0
- data/db/seeds/zips.csv +40978 -0
- data/lib/generators/usa/install/install_generator.rb +26 -0
- data/lib/tasks/usa.rake +8 -0
- data/lib/usa/engine.rb +9 -0
- data/lib/usa/version.rb +4 -0
- data/lib/usa.rb +23 -0
- metadata +97 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 5cf7ef01b60f4c0bc0cc59c9e565b999ee8e705c4435e548f61e5b5b187fc545
|
|
4
|
+
data.tar.gz: 4e7153ac68f27f05cfe8865ea4b227d1857792be082d65808794379048906d50
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 69b120cbcbc243dd1d840426c6a38f760c0f77b6a5e11dff1e571330ecae47a80c76397b79b6827a6dcdaa87821060750d7c26591a75611367cbf63f8b99cb10
|
|
7
|
+
data.tar.gz: b5ce850cfe280fe816e740952441583e6936b96599e772c1c14b571e8ad733ffe2c971aa131dc955d3b9000fb4fda43b30a5d80b85c64cfd8896040fe5430bf0
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
For more information about changelogs, check [Keep a Changelog](http://keepachangelog.com) and
|
|
6
|
+
[Vandamme](http://tech-angels.github.io/vandamme).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## 0.1.0 - 2026-09-14
|
|
11
|
+
|
|
12
|
+
* [Feature] `USA::State`, `USA::County` and `USA::ZIP`, on `usa_states`, `usa_counties` and
|
|
13
|
+
`usa_zips`: the three tables three apps were each keeping, each with a CSV and a backfill of
|
|
14
|
+
its own. A state has a code, a FIPS and a name; a county a FIPS, a name and a state; a ZIP a
|
|
15
|
+
code, the city it is addressed as, a time zone named as Rails names one, and one county
|
|
16
|
+
* [Feature] `USA::City` on `usa_cities`, and `USA::CityCounty` on `usa_city_counties`: a city
|
|
17
|
+
belongs to one state and lies in one or more counties, so `city.counties` and `county.cities`
|
|
18
|
+
both answer. The place FIPS is unique within a state rather than nationally, which is what the
|
|
19
|
+
index says
|
|
20
|
+
* [Feature] `google_place_id` on all four, filled for every county and blank elsewhere until a
|
|
21
|
+
release fills it -- which is a reason `db:usa:seed` exists
|
|
22
|
+
* [Feature] `bin/rails g usa:install`, which copies the six migrations into the host under
|
|
23
|
+
timestamps of its own, and writes nothing else: there is nothing here to configure
|
|
24
|
+
* [Feature] `USA.seed`, and a `seed` on each model: every write is an upsert keyed on the code
|
|
25
|
+
or the FIPS, so a second run inserts what is missing, leaves the id of every row a host
|
|
26
|
+
already had, and does not touch the `updated_at` of a row that did not change
|
|
27
|
+
* [Feature] `bin/rails db:usa:seed`, which is how a database made from `db/schema.rb` gets its
|
|
28
|
+
rows -- a dump carries none -- and how one catches up with a release that added some
|
|
29
|
+
* [Feature] `usa_states.counties_count` and `usa_counties.zips_count`, counted from the rows
|
|
30
|
+
after a seed, since an upsert runs no callback, and only where the count moved
|
|
31
|
+
* [Feature] The acronyms `USA`, `ZIP` and `FIPS`, registered before the engine loads, so
|
|
32
|
+
`USA::ZIP` is the class, `usa_zips` the table and 'ZIP' the heading
|
|
33
|
+
* [Feature] A load hook per model -- `ActiveSupport.on_load(:usa_zip) { ... }` -- and
|
|
34
|
+
`:usa_record`, where a host says how these tables connect
|
|
35
|
+
|
|
36
|
+
The data is the current Census vintage: the counties include Connecticut's nine planning
|
|
37
|
+
regions and Alaska's current census areas, and the places come from the 2025 Gazetteer rather
|
|
38
|
+
than the 2010 list one of the three apps was still carrying.
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 HouseAccount
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# USA
|
|
2
|
+
|
|
3
|
+
Every state, county, city and ZIP code of the United States, as five tables a Rails app joins
|
|
4
|
+
to. One copy, held here, rather than a CSV and a backfill in each app that needs an address to
|
|
5
|
+
mean something.
|
|
6
|
+
|
|
7
|
+
## How to install
|
|
8
|
+
|
|
9
|
+
To install on your system, run
|
|
10
|
+
|
|
11
|
+
gem install usa
|
|
12
|
+
|
|
13
|
+
To use inside a bundled Ruby project, add this line to the `Gemfile`:
|
|
14
|
+
|
|
15
|
+
gem 'usa', '~> 0.1.0'
|
|
16
|
+
|
|
17
|
+
Below 1.0 the pin stops at the next minor rather than the next major, because that is where a
|
|
18
|
+
breaking change may still land. It becomes `~> 1.0` once the API is settled on purpose.
|
|
19
|
+
|
|
20
|
+
## Getting the tables
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
bin/rails g usa:install # six migrations, copied into your app and yours to keep
|
|
24
|
+
bin/rails db:migrate # the five tables, and every row in them
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The last one takes about a minute: it writes 51 states, 3,144 counties, some 32,000 cities and
|
|
28
|
+
some 41,000 ZIPs.
|
|
29
|
+
|
|
30
|
+
**A database made from `db/schema.rb` has the tables and none of the rows.** A dump carries no
|
|
31
|
+
data, so `db:schema:load`, `db:test:prepare` and a fresh clone all leave the five tables empty.
|
|
32
|
+
Run `bin/rails db:usa:seed`, or call `USA.seed` from your own `db/seeds.rb`. The same command is
|
|
33
|
+
how a database catches up with a release that added rows: every write is an upsert keyed on the
|
|
34
|
+
code or the FIPS, so it inserts what is missing, updates what this gem owns, and leaves the id
|
|
35
|
+
of every row you already had exactly where it was.
|
|
36
|
+
|
|
37
|
+
## What you get
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
zip = USA::ZIP.find_by code: '90210' # => #<USA::ZIP>
|
|
41
|
+
zip.city # => 'Beverly Hills'
|
|
42
|
+
zip.time_zone # => 'Pacific Time (US & Canada)'
|
|
43
|
+
zip.county # => #<USA::County>
|
|
44
|
+
zip.county.fips # => '06037'
|
|
45
|
+
zip.county.state.code # => 'CA'
|
|
46
|
+
|
|
47
|
+
county = zip.county
|
|
48
|
+
county.zips_count # => 508
|
|
49
|
+
county.cities # => [#<USA::City>, ...]
|
|
50
|
+
|
|
51
|
+
state = county.state
|
|
52
|
+
state.counties_count # => 58
|
|
53
|
+
state.counties # => [#<USA::County>, ...]
|
|
54
|
+
state.cities # => [#<USA::City>, ...]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
A state has a `code`, a `fips` and a `name`. A county has a `fips`, a `name` and a state. A city
|
|
58
|
+
has a `fips` -- a Census place code, unique within its state rather than nationally -- a `name`,
|
|
59
|
+
a state, and the one or more counties it lies in, since a city may cross a county line. A ZIP has
|
|
60
|
+
a `code`, the `city` it is addressed as, a `time_zone` named as Rails names one, and one county.
|
|
61
|
+
Every one of the four also has a `google_place_id`, filled for counties and blank elsewhere until
|
|
62
|
+
a release fills it.
|
|
63
|
+
|
|
64
|
+
Two counter caches are kept by the seed rather than by a callback: `usa_states.counties_count`
|
|
65
|
+
and `usa_counties.zips_count`.
|
|
66
|
+
|
|
67
|
+
## Making them yours
|
|
68
|
+
|
|
69
|
+
Each model runs a load hook, so an app adds to it without reopening a file it does not own:
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
# config/initializers/usa.rb
|
|
73
|
+
ActiveSupport.on_load(:usa_zip) do
|
|
74
|
+
has_many :bookings, dependent: :destroy
|
|
75
|
+
|
|
76
|
+
scope :served, -> { where.not markets_count: 0 }
|
|
77
|
+
end
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`:usa_state`, `:usa_county`, `:usa_city` and `:usa_zip` are the four, and `:usa_record` is where
|
|
81
|
+
an app says how all of them connect -- a reading role, say, which these models otherwise know
|
|
82
|
+
nothing about:
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
ActiveSupport.on_load(:usa_record) { connects_to database: { writing: :primary, reading: :reader } }
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Columns of your own go on these tables in a migration of your own. They survive every seed: this
|
|
89
|
+
gem writes only the columns it ships.
|
|
90
|
+
|
|
91
|
+
## Adopting it in an app that already has these tables
|
|
92
|
+
|
|
93
|
+
The gem registers `USA`, `ZIP` and `FIPS` as acronyms, so a class of yours called `Zip` stops
|
|
94
|
+
being found the day you install it -- rename it, or reach `USA::ZIP` instead. Point your foreign
|
|
95
|
+
keys at `usa_zips.id` rather than at a `zips` of your own, and where your rows were loaded with
|
|
96
|
+
explicit ids, run `setval` on the sequence before the first seed, or the first insert collides.
|
|
97
|
+
|
|
98
|
+
The gem does not support an app that sets `ActiveRecord::Base.table_name_prefix`: its migrations
|
|
99
|
+
name `usa_states` while the models would look for the prefix and yours together.
|
|
100
|
+
|
|
101
|
+
## Development
|
|
102
|
+
|
|
103
|
+
`bin/setup` gets a clone working, `bin/console` opens a prompt with the library loaded, and
|
|
104
|
+
`bundle exec rake` runs the suite, the linter and the two size limits. The dummy app under
|
|
105
|
+
`test/dummy` is SQLite on purpose: it is a fixture rather than an app, and running the seed's
|
|
106
|
+
upserts and the counter statements on a second adapter, with no server to start, is worth more
|
|
107
|
+
than resembling the apps that install this.
|
|
108
|
+
|
|
109
|
+
`bin/geocode` is the maintainer's, not the gem's: it fills the blank place ids in one CSV from
|
|
110
|
+
the Google Geocoding API and is not packaged.
|
|
111
|
+
|
|
112
|
+
## Reference
|
|
113
|
+
|
|
114
|
+
The API reference is built from what RubyGems holds, at
|
|
115
|
+
[rubydoc.info/gems/usa](https://rubydoc.info/gems/usa). The source is at
|
|
116
|
+
[github.com/claudiob/usa](https://github.com/claudiob/usa).
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT, see [LICENSE.txt](LICENSE.txt).
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# What every table this gem ships knows how to do: write its own rows, twice over if asked.
|
|
2
|
+
module USA::Seeded extend ActiveSupport::Concern
|
|
3
|
+
# Rows per statement. An upsert inlines its values rather than binding them, so this bounds
|
|
4
|
+
# how long one statement runs and how many rows are held at once, not a placeholder count.
|
|
5
|
+
SLICE = 1000
|
|
6
|
+
|
|
7
|
+
class_methods do
|
|
8
|
+
# Writes the rows this release holds that are missing, and the columns of the rest.
|
|
9
|
+
# @return [void]
|
|
10
|
+
def seed
|
|
11
|
+
seeds.each_slice(SLICE) do |slice|
|
|
12
|
+
upsert_all slice, unique_by: natural_key, record_timestamps: true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def csv(name)
|
|
19
|
+
CSV.foreach USA::Engine.root.join("db/seeds/#{name}.csv"), headers: true
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# A city, town or census designated place: what a street address is addressed to.
|
|
2
|
+
class USA::City < USA::Record
|
|
3
|
+
include USA::Seeded
|
|
4
|
+
|
|
5
|
+
belongs_to :state
|
|
6
|
+
|
|
7
|
+
has_many :city_counties, dependent: :delete_all
|
|
8
|
+
has_many :counties, through: :city_counties
|
|
9
|
+
|
|
10
|
+
validates :fips, presence: true, length: { is: 5 },
|
|
11
|
+
uniqueness: { scope: :state_id }
|
|
12
|
+
validates :name, presence: true
|
|
13
|
+
|
|
14
|
+
# @return [String] the default representation (used in views).
|
|
15
|
+
def to_s = "#{name} (#{state.code})"
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def natural_key = %i[state_id fips]
|
|
21
|
+
|
|
22
|
+
def seeds
|
|
23
|
+
states = USA::State.pluck(:code, :id).to_h
|
|
24
|
+
csv('cities').lazy.map do |row|
|
|
25
|
+
{ fips: row['fips'], name: row['name'], state_id: states.fetch(row['state']),
|
|
26
|
+
google_place_id: row['google_place_id'], }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
ActiveSupport.run_load_hooks :usa_city, USA::City
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Where a city lies: one row per county it reaches into, since a city may span several.
|
|
2
|
+
class USA::CityCounty < USA::Record
|
|
3
|
+
include USA::Seeded
|
|
4
|
+
|
|
5
|
+
belongs_to :city
|
|
6
|
+
belongs_to :county
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def natural_key = %i[city_id county_id]
|
|
12
|
+
|
|
13
|
+
def seeds
|
|
14
|
+
cities = USA::City.joins(:state).pluck('usa_states.code', :fips, 'usa_cities.id').
|
|
15
|
+
to_h { |code, fips, id| [ [ code, fips ], id ] }
|
|
16
|
+
counties = USA::County.pluck(:fips, :id).to_h
|
|
17
|
+
csv('cities').lazy.flat_map do |row|
|
|
18
|
+
city_id = cities.fetch [ row['state'], row['fips'] ]
|
|
19
|
+
row['counties'].split.map { |fips| { city_id:, county_id: counties.fetch(fips) } }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# A county, parish, borough or census area: the division every ZIP is counted under.
|
|
2
|
+
class USA::County < USA::Record
|
|
3
|
+
include USA::Seeded
|
|
4
|
+
|
|
5
|
+
belongs_to :state, counter_cache: true
|
|
6
|
+
|
|
7
|
+
has_many :city_counties, dependent: :delete_all
|
|
8
|
+
has_many :cities, through: :city_counties
|
|
9
|
+
has_many :zips, dependent: :destroy
|
|
10
|
+
|
|
11
|
+
validates :fips, presence: true, length: { is: 5 }, uniqueness: true
|
|
12
|
+
validates :name, presence: true
|
|
13
|
+
|
|
14
|
+
# How many ZIPs a county holds, asked of the rows: an upsert runs no callback to keep it.
|
|
15
|
+
ZIPS = 'SELECT COUNT(*) FROM usa_zips WHERE usa_zips.county_id = usa_counties.id'
|
|
16
|
+
|
|
17
|
+
# Counts the ZIPs of every county the count is wrong for, and touches none of the others.
|
|
18
|
+
# @return [void]
|
|
19
|
+
def self.recount_zips
|
|
20
|
+
where("zips_count <> (#{ZIPS})").
|
|
21
|
+
update_all [ "zips_count = (#{ZIPS}), updated_at = ?", Time.current ]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @return [String] the default representation (used in views).
|
|
25
|
+
def to_s = "#{name} (#{state.code})"
|
|
26
|
+
|
|
27
|
+
class << self
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def natural_key = :fips
|
|
31
|
+
|
|
32
|
+
def seeds
|
|
33
|
+
states = USA::State.pluck(:code, :id).to_h
|
|
34
|
+
csv('counties').lazy.map do |row|
|
|
35
|
+
{ fips: row['fips'], name: row['name'], state_id: states.fetch(row['state']),
|
|
36
|
+
google_place_id: row['google_place_id'], }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
ActiveSupport.run_load_hooks :usa_county, USA::County
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# A state of the United States, or the District of Columbia.
|
|
2
|
+
class USA::State < USA::Record
|
|
3
|
+
include USA::Seeded
|
|
4
|
+
|
|
5
|
+
has_many :counties, dependent: :destroy
|
|
6
|
+
has_many :cities, dependent: :destroy
|
|
7
|
+
|
|
8
|
+
validates :code, presence: true, length: { is: 2 }, uniqueness: true
|
|
9
|
+
validates :fips, presence: true, length: { is: 2 }, uniqueness: true
|
|
10
|
+
validates :name, presence: true, uniqueness: true
|
|
11
|
+
|
|
12
|
+
# How many counties a state holds, asked of the rows: an upsert runs no callback to keep it.
|
|
13
|
+
COUNTIES = 'SELECT COUNT(*) FROM usa_counties WHERE usa_counties.state_id = usa_states.id'
|
|
14
|
+
|
|
15
|
+
# Counts the counties of every state the count is wrong for, and touches none of the others.
|
|
16
|
+
# @return [void]
|
|
17
|
+
def self.recount_counties
|
|
18
|
+
where("counties_count <> (#{COUNTIES})").
|
|
19
|
+
update_all [ "counties_count = (#{COUNTIES}), updated_at = ?", Time.current ]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @return [String] the default representation (used in views).
|
|
23
|
+
def to_s = name
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def natural_key = :code
|
|
29
|
+
|
|
30
|
+
def seeds
|
|
31
|
+
csv('states').lazy.map do |row|
|
|
32
|
+
{ code: row['code'], fips: row['fips'], name: row['name'],
|
|
33
|
+
google_place_id: row['google_place_id'], }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
ActiveSupport.run_load_hooks :usa_state, USA::State
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# A ZIP code, and the city, time zone and county the Postal Service delivers it in.
|
|
2
|
+
class USA::ZIP < USA::Record
|
|
3
|
+
include USA::Seeded
|
|
4
|
+
|
|
5
|
+
belongs_to :county, counter_cache: true
|
|
6
|
+
|
|
7
|
+
validates :code, presence: true, length: { is: 5 }, uniqueness: true
|
|
8
|
+
validates :city, presence: true
|
|
9
|
+
validates :time_zone, inclusion: { in: ActiveSupport::TimeZone.us_zones.map(&:name) }
|
|
10
|
+
|
|
11
|
+
# @return [String] the default representation (used in views).
|
|
12
|
+
def to_s = code
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def natural_key = :code
|
|
18
|
+
|
|
19
|
+
def seeds
|
|
20
|
+
counties = USA::County.pluck(:fips, :id).to_h
|
|
21
|
+
csv('zips').lazy.map do |row|
|
|
22
|
+
{ code: row['code'], city: row['city'], time_zone: row['time_zone'],
|
|
23
|
+
county_id: counties.fetch(row['county']), google_place_id: row['google_place_id'], }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
ActiveSupport.run_load_hooks :usa_zip, USA::ZIP
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class CreateUSAStates < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :usa_states do |t|
|
|
4
|
+
t.string :code, limit: 2, null: false, index: { unique: true }
|
|
5
|
+
t.string :fips, limit: 2, null: false, index: { unique: true }
|
|
6
|
+
t.string :name, null: false, index: { unique: true }
|
|
7
|
+
t.integer :counties_count, default: 0, null: false
|
|
8
|
+
t.string :google_place_id
|
|
9
|
+
t.timestamps
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class CreateUSACounties < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :usa_counties do |t|
|
|
4
|
+
t.string :fips, limit: 5, null: false, index: { unique: true }
|
|
5
|
+
t.string :name, null: false, index: true
|
|
6
|
+
t.references :state, null: false, foreign_key: { to_table: :usa_states }
|
|
7
|
+
t.integer :zips_count, default: 0, null: false
|
|
8
|
+
t.string :google_place_id
|
|
9
|
+
t.timestamps
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class CreateUSAZips < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :usa_zips do |t|
|
|
4
|
+
t.string :code, limit: 5, null: false, index: { unique: true }
|
|
5
|
+
t.string :city, null: false, index: true
|
|
6
|
+
t.string :time_zone, null: false
|
|
7
|
+
t.references :county, null: false, foreign_key: { to_table: :usa_counties }
|
|
8
|
+
t.string :google_place_id
|
|
9
|
+
t.timestamps
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class CreateUSACities < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :usa_cities do |t|
|
|
4
|
+
t.string :fips, limit: 5, null: false
|
|
5
|
+
t.string :name, null: false, index: true
|
|
6
|
+
t.references :state, null: false, index: false, foreign_key: { to_table: :usa_states }
|
|
7
|
+
t.string :google_place_id
|
|
8
|
+
t.timestamps
|
|
9
|
+
t.index %i[state_id fips], unique: true
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
class CreateUSACityCounties < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :usa_city_counties, id: false do |t|
|
|
4
|
+
t.references :city, null: false, index: false, foreign_key: { to_table: :usa_cities }
|
|
5
|
+
t.references :county, null: false, foreign_key: { to_table: :usa_counties }
|
|
6
|
+
t.index %i[city_id county_id], unique: true
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|