free_zipcode_data 1.0.6 → 1.2.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 +4 -4
- data/.dockerignore +10 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +25 -16
- data/.ruby-version +1 -1
- data/CHANGELOG +17 -0
- data/CLAUDE.md +89 -0
- data/Dockerfile +21 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +50 -36
- data/README.md +38 -5
- data/Rakefile +1 -1
- data/docker-entrypoint.sh +14 -0
- data/free_zipcode_data.gemspec +8 -14
- data/lib/etl/common.rb +1 -0
- data/lib/etl/csv_source.rb +4 -4
- data/lib/free_zipcode_data/country_table.rb +10 -2
- data/lib/free_zipcode_data/county_table.rb +14 -6
- data/lib/free_zipcode_data/data_source.rb +2 -2
- data/lib/free_zipcode_data/db_table.rb +54 -7
- data/lib/free_zipcode_data/logger.rb +8 -12
- data/lib/free_zipcode_data/runner.rb +2 -2
- data/lib/free_zipcode_data/state_table.rb +37 -5
- data/lib/free_zipcode_data/version.rb +1 -1
- data/lib/free_zipcode_data/zipcode_table.rb +15 -5
- data/lib/free_zipcode_data.rb +3 -3
- data/lib/tasks/version.rake +27 -24
- data/spec/etl/csv_source_spec.rb +57 -0
- data/spec/etl/free_zipcode_data_job_spec.rb +135 -0
- data/spec/fixtures/.free_zipcode_data.yml +1 -0
- data/spec/fixtures/US.txt +5 -0
- data/spec/fixtures/US.zip +0 -0
- data/spec/fixtures/test_data.csv +7 -0
- data/spec/fixtures/test_data.txt +5 -0
- data/spec/free_zipcode_data/country_table_spec.rb +52 -0
- data/spec/free_zipcode_data/county_table_spec.rb +84 -0
- data/spec/free_zipcode_data/data_source_spec.rb +131 -0
- data/spec/free_zipcode_data/db_table_spec.rb +164 -0
- data/spec/free_zipcode_data/logger_spec.rb +78 -0
- data/spec/free_zipcode_data/options_spec.rb +37 -0
- data/spec/free_zipcode_data/runner_spec.rb +91 -0
- data/spec/free_zipcode_data/sqlite_ram_spec.rb +64 -0
- data/spec/free_zipcode_data/state_table_spec.rb +112 -0
- data/spec/free_zipcode_data/zipcode_table_spec.rb +102 -0
- data/spec/free_zipcode_data_spec.rb +38 -0
- data/spec/spec_helper.rb +23 -2
- data/spec/support/database_helpers.rb +48 -0
- metadata +41 -91
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'tempfile'
|
|
4
|
+
require 'free_zipcode_data/sqlite_ram'
|
|
5
|
+
|
|
6
|
+
RSpec.describe SqliteRam do
|
|
7
|
+
let(:tmpdir) { Dir.mktmpdir('sqlite_ram_test') }
|
|
8
|
+
let(:db_path) { File.join(tmpdir, 'test_db.sqlite3') }
|
|
9
|
+
let(:sqlite_ram) { described_class.new(db_path) }
|
|
10
|
+
|
|
11
|
+
after do
|
|
12
|
+
FileUtils.rm_rf(tmpdir)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe '#initialize' do
|
|
16
|
+
it 'creates an in-memory database connection' do
|
|
17
|
+
expect(sqlite_ram.conn).to be_a(SQLite3::Database)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'stores the filename' do
|
|
21
|
+
expect(sqlite_ram.filename).to eq(db_path)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe '#save_to_disk' do
|
|
26
|
+
it 'persists in-memory data to the file database' do
|
|
27
|
+
sqlite_ram.conn.execute('CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)')
|
|
28
|
+
sqlite_ram.conn.execute("INSERT INTO test (name) VALUES ('hello')")
|
|
29
|
+
sqlite_ram.save_to_disk
|
|
30
|
+
|
|
31
|
+
file_db = SQLite3::Database.new(db_path)
|
|
32
|
+
rows = file_db.execute('SELECT name FROM test')
|
|
33
|
+
expect(rows).to eq([['hello']])
|
|
34
|
+
file_db.close
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe '#dump_tables' do
|
|
39
|
+
it 'exports each table to a CSV file in the given directory' do
|
|
40
|
+
sqlite_ram.conn.execute('CREATE TABLE widgets (id INTEGER PRIMARY KEY, name TEXT, weight REAL)')
|
|
41
|
+
sqlite_ram.conn.execute("INSERT INTO widgets (name, weight) VALUES ('gear', 1.5)")
|
|
42
|
+
sqlite_ram.conn.execute("INSERT INTO widgets (name, weight) VALUES ('bolt', 0.3)")
|
|
43
|
+
|
|
44
|
+
sqlite_ram.dump_tables(tmpdir)
|
|
45
|
+
|
|
46
|
+
csv_path = File.join(tmpdir, 'widgets.csv')
|
|
47
|
+
expect(File.exist?(csv_path)).to be true
|
|
48
|
+
|
|
49
|
+
csv = CSV.read(csv_path)
|
|
50
|
+
expect(csv[0]).to eq(%w[id name weight])
|
|
51
|
+
expect(csv.length).to eq(3) # header + 2 rows
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'exports multiple tables' do
|
|
55
|
+
sqlite_ram.conn.execute('CREATE TABLE a (id INTEGER PRIMARY KEY)')
|
|
56
|
+
sqlite_ram.conn.execute('CREATE TABLE b (id INTEGER PRIMARY KEY)')
|
|
57
|
+
|
|
58
|
+
sqlite_ram.dump_tables(tmpdir)
|
|
59
|
+
|
|
60
|
+
expect(File.exist?(File.join(tmpdir, 'a.csv'))).to be true
|
|
61
|
+
expect(File.exist?(File.join(tmpdir, 'b.csv'))).to be true
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'free_zipcode_data/state_table'
|
|
4
|
+
|
|
5
|
+
RSpec.describe FreeZipcodeData::StateTable do
|
|
6
|
+
let(:db) { create_test_database(line_count: 5) }
|
|
7
|
+
let(:table) { described_class.new(database: db, tablename: 'states') }
|
|
8
|
+
|
|
9
|
+
before do
|
|
10
|
+
seed_countries(db)
|
|
11
|
+
table.build
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe '#build' do
|
|
15
|
+
it 'creates the states table' do
|
|
16
|
+
tables = db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='states'")
|
|
17
|
+
expect(tables.length).to eq(1)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'creates the unique_state index' do
|
|
21
|
+
indexes = db.execute("SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='states'")
|
|
22
|
+
index_names = indexes.map(&:first)
|
|
23
|
+
expect(index_names).to include('unique_state')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'creates the state_name index' do
|
|
27
|
+
indexes = db.execute("SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='states'")
|
|
28
|
+
index_names = indexes.map(&:first)
|
|
29
|
+
expect(index_names).to include('state_name')
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'creates columns for country_id, abbr, and name' do
|
|
33
|
+
columns = db.execute("PRAGMA table_info('states')").map { |c| c[1] }
|
|
34
|
+
expect(columns).to include('country_id', 'abbr', 'name')
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe '#write' do
|
|
39
|
+
it 'inserts a state row' do
|
|
40
|
+
table.write({ country: 'US', short_state: 'NY', state: 'New York' })
|
|
41
|
+
rows = db.execute('SELECT abbr, name FROM states')
|
|
42
|
+
expect(rows.length).to eq(1)
|
|
43
|
+
expect(rows[0]).to eq(['NY', 'New York'])
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'links the state to its country' do
|
|
47
|
+
table.write({ country: 'US', short_state: 'NY', state: 'New York' })
|
|
48
|
+
country_id = db.execute("SELECT id FROM countries WHERE alpha2 = 'US'")[0][0]
|
|
49
|
+
state_country_id = db.execute('SELECT country_id FROM states')[0][0]
|
|
50
|
+
expect(state_country_id).to eq(country_id)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'creates a state from country lookup when short_state is nil' do
|
|
54
|
+
row = { country: 'US', short_state: nil, state: 'Unknown' }
|
|
55
|
+
table.write(row)
|
|
56
|
+
rows = db.execute("SELECT abbr, name FROM states WHERE abbr = 'US'")
|
|
57
|
+
expect(rows.length).to eq(1)
|
|
58
|
+
expect(rows[0]).to eq(['US', 'United States of America'])
|
|
59
|
+
# Verify row mutation for downstream Kiba destinations
|
|
60
|
+
expect(row[:short_state]).to eq('US')
|
|
61
|
+
expect(row[:state]).to eq('United States of America')
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'creates a state from country lookup when short_state is empty' do
|
|
65
|
+
table.write({ country: 'US', short_state: '', state: 'Unknown' })
|
|
66
|
+
rows = db.execute("SELECT abbr, name FROM states WHERE abbr = 'US'")
|
|
67
|
+
expect(rows.length).to eq(1)
|
|
68
|
+
expect(rows[0]).to eq(['US', 'United States of America'])
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'returns nil when short_state is nil and country is unknown' do
|
|
72
|
+
result = table.write({ country: 'ZZ', short_state: nil, state: 'Unknown' })
|
|
73
|
+
expect(result).to be_nil
|
|
74
|
+
rows = db.execute('SELECT COUNT(*) FROM states')
|
|
75
|
+
expect(rows[0][0]).to eq(0)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'returns nil when country is not in the countries table' do
|
|
79
|
+
result = table.write({ country: 'DE', short_state: 'BY', state: 'Bavaria' })
|
|
80
|
+
expect(result).to be_nil
|
|
81
|
+
rows = db.execute('SELECT COUNT(*) FROM states')
|
|
82
|
+
expect(rows[0][0]).to eq(0)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'silently ignores duplicate state entries' do
|
|
86
|
+
table.write({ country: 'US', short_state: 'NY', state: 'New York' })
|
|
87
|
+
expect { table.write({ country: 'US', short_state: 'NY', state: 'New York' }) }.not_to raise_error
|
|
88
|
+
rows = db.execute('SELECT COUNT(*) FROM states')
|
|
89
|
+
expect(rows[0][0]).to eq(1)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it 'handles the Marshall Islands edge case' do
|
|
93
|
+
table.write({ country: 'US', short_state: 'MH', state: nil })
|
|
94
|
+
rows = db.execute("SELECT name FROM states WHERE abbr = 'MH'")
|
|
95
|
+
expect(rows[0][0]).to eq('Marshall Islands')
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'handles state names with single quotes' do
|
|
99
|
+
# Some international state names can have apostrophes
|
|
100
|
+
table.write({ country: 'US', short_state: 'TX', state: "Cote d'Ivoire" })
|
|
101
|
+
rows = db.execute("SELECT name FROM states WHERE abbr = 'TX'")
|
|
102
|
+
expect(rows[0][0]).to eq("Cote d'Ivoire")
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it 'allows states with the same name in different countries' do
|
|
106
|
+
table.write({ country: 'US', short_state: 'BC', state: 'British Columbia' })
|
|
107
|
+
table.write({ country: 'CA', short_state: 'BC', state: 'British Columbia' })
|
|
108
|
+
rows = db.execute("SELECT COUNT(*) FROM states WHERE name = 'British Columbia'")
|
|
109
|
+
expect(rows[0][0]).to eq(2)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'free_zipcode_data/zipcode_table'
|
|
4
|
+
|
|
5
|
+
RSpec.describe FreeZipcodeData::ZipcodeTable do
|
|
6
|
+
let(:db) { create_test_database(line_count: 5) }
|
|
7
|
+
let(:table) { described_class.new(database: db, tablename: 'zipcodes') }
|
|
8
|
+
|
|
9
|
+
before do
|
|
10
|
+
seed_countries(db)
|
|
11
|
+
seed_states(db)
|
|
12
|
+
table.build
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe '#build' do
|
|
16
|
+
it 'creates the zipcodes table' do
|
|
17
|
+
tables = db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='zipcodes'")
|
|
18
|
+
expect(tables.length).to eq(1)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'creates the unique_zipcode index' do
|
|
22
|
+
indexes = db.execute("SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='zipcodes'")
|
|
23
|
+
index_names = indexes.map(&:first)
|
|
24
|
+
expect(index_names).to include('unique_zipcode')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'creates columns for code, state_id, city, area_code, lat, lon, accuracy' do
|
|
28
|
+
columns = db.execute("PRAGMA table_info('zipcodes')").map { |c| c[1] }
|
|
29
|
+
expect(columns).to include('code', 'state_id', 'city', 'area_code', 'lat', 'lon', 'accuracy')
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe '#write' do
|
|
34
|
+
let(:row) do
|
|
35
|
+
{
|
|
36
|
+
country: 'US',
|
|
37
|
+
postal_code: '60601',
|
|
38
|
+
short_state: 'IL',
|
|
39
|
+
state: 'Illinois',
|
|
40
|
+
city: 'Chicago',
|
|
41
|
+
latitude: '41.8819',
|
|
42
|
+
longitude: '-87.6278',
|
|
43
|
+
accuracy: '4'
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'inserts a zipcode row' do
|
|
48
|
+
table.write(row)
|
|
49
|
+
rows = db.execute('SELECT code, city FROM zipcodes')
|
|
50
|
+
expect(rows.length).to eq(1)
|
|
51
|
+
expect(rows[0]).to eq(%w[60601 Chicago])
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'stores latitude and longitude' do
|
|
55
|
+
table.write(row)
|
|
56
|
+
rows = db.execute('SELECT lat, lon FROM zipcodes')
|
|
57
|
+
expect(rows[0][0].to_s).to start_with('41.88')
|
|
58
|
+
expect(rows[0][1].to_s).to start_with('-87.62')
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'links the zipcode to its state' do
|
|
62
|
+
table.write(row)
|
|
63
|
+
state_id = db.execute("SELECT id FROM states WHERE abbr = 'IL'")[0][0]
|
|
64
|
+
zipcode_state_id = db.execute('SELECT state_id FROM zipcodes')[0][0]
|
|
65
|
+
expect(zipcode_state_id.to_i).to eq(state_id)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'returns nil and skips when postal_code is nil' do
|
|
69
|
+
result = table.write(row.merge(postal_code: nil))
|
|
70
|
+
expect(result).to be_nil
|
|
71
|
+
rows = db.execute('SELECT COUNT(*) FROM zipcodes')
|
|
72
|
+
expect(rows[0][0]).to eq(0)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'returns nil and skips when state cannot be found' do
|
|
76
|
+
result = table.write(row.merge(short_state: 'ZZ', state: 'Nonexistent'))
|
|
77
|
+
expect(result).to be_nil
|
|
78
|
+
rows = db.execute('SELECT COUNT(*) FROM zipcodes')
|
|
79
|
+
expect(rows[0][0]).to eq(0)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'silently ignores duplicate zipcode entries' do
|
|
83
|
+
table.write(row)
|
|
84
|
+
expect { table.write(row) }.not_to raise_error
|
|
85
|
+
rows = db.execute('SELECT COUNT(*) FROM zipcodes')
|
|
86
|
+
expect(rows[0][0]).to eq(1)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'handles city names with single quotes' do
|
|
90
|
+
table.write(row.merge(city: "Coeur d'Alene", postal_code: '83814'))
|
|
91
|
+
rows = db.execute('SELECT city FROM zipcodes')
|
|
92
|
+
expect(rows[0][0]).to eq("Coeur d'Alene")
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it 'inserts multiple different zipcodes' do
|
|
96
|
+
table.write(row)
|
|
97
|
+
table.write(row.merge(postal_code: '10001', city: 'New York', short_state: 'NY', state: 'New York'))
|
|
98
|
+
rows = db.execute('SELECT COUNT(*) FROM zipcodes')
|
|
99
|
+
expect(rows[0][0]).to eq(2)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe FreeZipcodeData do
|
|
4
|
+
describe '.root' do
|
|
5
|
+
it 'returns a Pathname to the project root' do
|
|
6
|
+
expect(described_class.root).to be_a(Pathname)
|
|
7
|
+
expect(described_class.root.join('lib', 'free_zipcode_data.rb')).to exist
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
describe '.current_environment' do
|
|
12
|
+
it 'returns "test" when APP_ENV is set to test' do
|
|
13
|
+
expect(described_class.current_environment).to eq('test')
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'defaults to "development" when APP_ENV is not set' do
|
|
17
|
+
allow(ENV).to receive(:fetch).with('APP_ENV', 'development').and_return('development')
|
|
18
|
+
expect(described_class.current_environment).to eq('development')
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe '.config_file' do
|
|
23
|
+
it 'returns spec/fixtures path in test environment' do
|
|
24
|
+
path = described_class.config_file
|
|
25
|
+
expect(path.to_s).to include('spec/fixtures/.free_zipcode_data.yml')
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe '.os' do
|
|
30
|
+
it 'returns :normal on non-Windows platforms' do
|
|
31
|
+
expect(described_class.os).to eq(:normal)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'has a version number' do
|
|
36
|
+
expect(FreeZipcodeData::VERSION).to match(/\A\d+\.\d+\.\d+\z/)
|
|
37
|
+
end
|
|
38
|
+
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -2,11 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
ENV['APP_ENV'] = 'test'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
begin
|
|
6
|
+
require 'pry'
|
|
7
|
+
rescue NameError, LoadError
|
|
8
|
+
# pry may not be compatible with current Ruby version
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
require 'ostruct'
|
|
12
|
+
require 'free_zipcode_data'
|
|
13
|
+
require 'free_zipcode_data/runner'
|
|
6
14
|
|
|
7
|
-
Dir[Pathname.new(File.dirname(__FILE__)).parent.join('spec/support/**/*.rb')].
|
|
15
|
+
Dir[Pathname.new(File.dirname(__FILE__)).parent.join('spec/support/**/*.rb')].each { |f| require f }
|
|
8
16
|
|
|
9
17
|
RSpec.configure do |config|
|
|
18
|
+
config.include DatabaseHelpers
|
|
19
|
+
|
|
20
|
+
# Silence progress bar and logger output during tests
|
|
21
|
+
config.before do
|
|
22
|
+
allow(ProgressBar).to receive(:create).and_wrap_original do |method, **args|
|
|
23
|
+
method.call(**args, output: StringIO.new)
|
|
24
|
+
end
|
|
25
|
+
FreeZipcodeData::Logger.instance.log_provider = Logger.new(StringIO.new)
|
|
26
|
+
FreeZipcodeData::Options.instance.initialize_hash(
|
|
27
|
+
OpenStruct.new(verbose: false)
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
10
31
|
config.expect_with :rspec do |expectations|
|
|
11
32
|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
|
12
33
|
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'sqlite3'
|
|
4
|
+
|
|
5
|
+
module DatabaseHelpers
|
|
6
|
+
def create_test_database(line_count: 5)
|
|
7
|
+
db = SQLite3::Database.new(':memory:')
|
|
8
|
+
db.execute_batch(<<-SQL)
|
|
9
|
+
CREATE TABLE meta (
|
|
10
|
+
id integer not null primary key,
|
|
11
|
+
name varchar(255),
|
|
12
|
+
value varchar(255)
|
|
13
|
+
)
|
|
14
|
+
SQL
|
|
15
|
+
db.execute("INSERT INTO meta (name, value) VALUES ('line_count', #{line_count})")
|
|
16
|
+
db
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def seed_countries(db, tablename: 'countries')
|
|
20
|
+
table = FreeZipcodeData::CountryTable.new(database: db, tablename: tablename)
|
|
21
|
+
table.build
|
|
22
|
+
[
|
|
23
|
+
{ country: 'US' },
|
|
24
|
+
{ country: 'CA' },
|
|
25
|
+
{ country: 'GB' }
|
|
26
|
+
].each { |row| table.write(row) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def seed_states(db, tablename: 'states')
|
|
30
|
+
table = FreeZipcodeData::StateTable.new(database: db, tablename: tablename)
|
|
31
|
+
table.build
|
|
32
|
+
[
|
|
33
|
+
{ country: 'US', short_state: 'NY', state: 'New York' },
|
|
34
|
+
{ country: 'US', short_state: 'CA', state: 'California' },
|
|
35
|
+
{ country: 'US', short_state: 'IL', state: 'Illinois' }
|
|
36
|
+
].each { |row| table.write(row) }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def seed_counties(db, tablename: 'counties')
|
|
40
|
+
table = FreeZipcodeData::CountyTable.new(database: db, tablename: tablename)
|
|
41
|
+
table.build
|
|
42
|
+
[
|
|
43
|
+
{ country: 'US', county: 'New York', short_county: '061', short_state: 'NY', state: 'New York' },
|
|
44
|
+
{ country: 'US', county: 'Los Angeles', short_county: '037', short_state: 'CA', state: 'California' },
|
|
45
|
+
{ country: 'US', county: 'Cook', short_county: '031', short_state: 'IL', state: 'Illinois' }
|
|
46
|
+
].each { |row| table.write(row) }
|
|
47
|
+
end
|
|
48
|
+
end
|
metadata
CHANGED
|
@@ -1,80 +1,37 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: free_zipcode_data
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chris Blackburn
|
|
8
8
|
- Chris McKnight
|
|
9
|
-
autorequire:
|
|
10
9
|
bindir: bin
|
|
11
10
|
cert_chain: []
|
|
12
|
-
date:
|
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
13
12
|
dependencies:
|
|
14
13
|
- !ruby/object:Gem::Dependency
|
|
15
|
-
name:
|
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
|
17
|
-
requirements:
|
|
18
|
-
- - ">="
|
|
19
|
-
- !ruby/object:Gem::Version
|
|
20
|
-
version: '0'
|
|
21
|
-
type: :development
|
|
22
|
-
prerelease: false
|
|
23
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
24
|
-
requirements:
|
|
25
|
-
- - ">="
|
|
26
|
-
- !ruby/object:Gem::Version
|
|
27
|
-
version: '0'
|
|
28
|
-
- !ruby/object:Gem::Dependency
|
|
29
|
-
name: pry-nav
|
|
30
|
-
requirement: !ruby/object:Gem::Requirement
|
|
31
|
-
requirements:
|
|
32
|
-
- - "~>"
|
|
33
|
-
- !ruby/object:Gem::Version
|
|
34
|
-
version: '0.2'
|
|
35
|
-
type: :development
|
|
36
|
-
prerelease: false
|
|
37
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
38
|
-
requirements:
|
|
39
|
-
- - "~>"
|
|
40
|
-
- !ruby/object:Gem::Version
|
|
41
|
-
version: '0.2'
|
|
42
|
-
- !ruby/object:Gem::Dependency
|
|
43
|
-
name: rake
|
|
44
|
-
requirement: !ruby/object:Gem::Requirement
|
|
45
|
-
requirements:
|
|
46
|
-
- - "~>"
|
|
47
|
-
- !ruby/object:Gem::Version
|
|
48
|
-
version: '13.0'
|
|
49
|
-
type: :development
|
|
50
|
-
prerelease: false
|
|
51
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
52
|
-
requirements:
|
|
53
|
-
- - "~>"
|
|
54
|
-
- !ruby/object:Gem::Version
|
|
55
|
-
version: '13.0'
|
|
56
|
-
- !ruby/object:Gem::Dependency
|
|
57
|
-
name: rspec
|
|
14
|
+
name: colored
|
|
58
15
|
requirement: !ruby/object:Gem::Requirement
|
|
59
16
|
requirements:
|
|
60
17
|
- - "~>"
|
|
61
18
|
- !ruby/object:Gem::Version
|
|
62
|
-
version: '
|
|
63
|
-
type: :
|
|
19
|
+
version: '1.2'
|
|
20
|
+
type: :runtime
|
|
64
21
|
prerelease: false
|
|
65
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
66
23
|
requirements:
|
|
67
24
|
- - "~>"
|
|
68
25
|
- !ruby/object:Gem::Version
|
|
69
|
-
version: '
|
|
26
|
+
version: '1.2'
|
|
70
27
|
- !ruby/object:Gem::Dependency
|
|
71
|
-
name:
|
|
28
|
+
name: csv
|
|
72
29
|
requirement: !ruby/object:Gem::Requirement
|
|
73
30
|
requirements:
|
|
74
31
|
- - ">="
|
|
75
32
|
- !ruby/object:Gem::Version
|
|
76
33
|
version: '0'
|
|
77
|
-
type: :
|
|
34
|
+
type: :runtime
|
|
78
35
|
prerelease: false
|
|
79
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
80
37
|
requirements:
|
|
@@ -82,61 +39,33 @@ dependencies:
|
|
|
82
39
|
- !ruby/object:Gem::Version
|
|
83
40
|
version: '0'
|
|
84
41
|
- !ruby/object:Gem::Dependency
|
|
85
|
-
name:
|
|
86
|
-
requirement: !ruby/object:Gem::Requirement
|
|
87
|
-
requirements:
|
|
88
|
-
- - "~>"
|
|
89
|
-
- !ruby/object:Gem::Version
|
|
90
|
-
version: '0.17'
|
|
91
|
-
type: :development
|
|
92
|
-
prerelease: false
|
|
93
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
94
|
-
requirements:
|
|
95
|
-
- - "~>"
|
|
96
|
-
- !ruby/object:Gem::Version
|
|
97
|
-
version: '0.17'
|
|
98
|
-
- !ruby/object:Gem::Dependency
|
|
99
|
-
name: simplecov
|
|
100
|
-
requirement: !ruby/object:Gem::Requirement
|
|
101
|
-
requirements:
|
|
102
|
-
- - "~>"
|
|
103
|
-
- !ruby/object:Gem::Version
|
|
104
|
-
version: '0.16'
|
|
105
|
-
type: :development
|
|
106
|
-
prerelease: false
|
|
107
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
108
|
-
requirements:
|
|
109
|
-
- - "~>"
|
|
110
|
-
- !ruby/object:Gem::Version
|
|
111
|
-
version: '0.16'
|
|
112
|
-
- !ruby/object:Gem::Dependency
|
|
113
|
-
name: colored
|
|
42
|
+
name: kiba
|
|
114
43
|
requirement: !ruby/object:Gem::Requirement
|
|
115
44
|
requirements:
|
|
116
45
|
- - "~>"
|
|
117
46
|
- !ruby/object:Gem::Version
|
|
118
|
-
version: '
|
|
47
|
+
version: '4.0'
|
|
119
48
|
type: :runtime
|
|
120
49
|
prerelease: false
|
|
121
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
122
51
|
requirements:
|
|
123
52
|
- - "~>"
|
|
124
53
|
- !ruby/object:Gem::Version
|
|
125
|
-
version: '
|
|
54
|
+
version: '4.0'
|
|
126
55
|
- !ruby/object:Gem::Dependency
|
|
127
|
-
name:
|
|
56
|
+
name: logger
|
|
128
57
|
requirement: !ruby/object:Gem::Requirement
|
|
129
58
|
requirements:
|
|
130
|
-
- - "
|
|
59
|
+
- - ">="
|
|
131
60
|
- !ruby/object:Gem::Version
|
|
132
|
-
version: '
|
|
61
|
+
version: '0'
|
|
133
62
|
type: :runtime
|
|
134
63
|
prerelease: false
|
|
135
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
136
65
|
requirements:
|
|
137
|
-
- - "
|
|
66
|
+
- - ">="
|
|
138
67
|
- !ruby/object:Gem::Version
|
|
139
|
-
version: '
|
|
68
|
+
version: '0'
|
|
140
69
|
- !ruby/object:Gem::Dependency
|
|
141
70
|
name: optimist
|
|
142
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -204,13 +133,16 @@ executables:
|
|
|
204
133
|
extensions: []
|
|
205
134
|
extra_rdoc_files: []
|
|
206
135
|
files:
|
|
136
|
+
- ".dockerignore"
|
|
207
137
|
- ".gitignore"
|
|
208
138
|
- ".rspec"
|
|
209
139
|
- ".rubocop.yml"
|
|
210
140
|
- ".ruby-version"
|
|
211
141
|
- CHANGELOG
|
|
142
|
+
- CLAUDE.md
|
|
212
143
|
- CODE_OF_CONDUCT.md
|
|
213
144
|
- CONTRIBUTING.md
|
|
145
|
+
- Dockerfile
|
|
214
146
|
- Gemfile
|
|
215
147
|
- Gemfile.lock
|
|
216
148
|
- ISSUE_TEMPLATE.md
|
|
@@ -225,6 +157,7 @@ files:
|
|
|
225
157
|
- bin/free_zipcode_data
|
|
226
158
|
- counties_states_zipcodes.sql
|
|
227
159
|
- country_lookup_table.yml
|
|
160
|
+
- docker-entrypoint.sh
|
|
228
161
|
- free_zipcode_data.gemspec
|
|
229
162
|
- lib/etl/common.rb
|
|
230
163
|
- lib/etl/csv_source.rb
|
|
@@ -242,13 +175,31 @@ files:
|
|
|
242
175
|
- lib/free_zipcode_data/version.rb
|
|
243
176
|
- lib/free_zipcode_data/zipcode_table.rb
|
|
244
177
|
- lib/tasks/version.rake
|
|
178
|
+
- spec/etl/csv_source_spec.rb
|
|
179
|
+
- spec/etl/free_zipcode_data_job_spec.rb
|
|
180
|
+
- spec/fixtures/.free_zipcode_data.yml
|
|
181
|
+
- spec/fixtures/US.txt
|
|
182
|
+
- spec/fixtures/US.zip
|
|
183
|
+
- spec/fixtures/test_data.csv
|
|
184
|
+
- spec/fixtures/test_data.txt
|
|
185
|
+
- spec/free_zipcode_data/country_table_spec.rb
|
|
186
|
+
- spec/free_zipcode_data/county_table_spec.rb
|
|
187
|
+
- spec/free_zipcode_data/data_source_spec.rb
|
|
188
|
+
- spec/free_zipcode_data/db_table_spec.rb
|
|
189
|
+
- spec/free_zipcode_data/logger_spec.rb
|
|
190
|
+
- spec/free_zipcode_data/options_spec.rb
|
|
191
|
+
- spec/free_zipcode_data/runner_spec.rb
|
|
192
|
+
- spec/free_zipcode_data/sqlite_ram_spec.rb
|
|
193
|
+
- spec/free_zipcode_data/state_table_spec.rb
|
|
194
|
+
- spec/free_zipcode_data/zipcode_table_spec.rb
|
|
195
|
+
- spec/free_zipcode_data_spec.rb
|
|
245
196
|
- spec/spec_helper.rb
|
|
197
|
+
- spec/support/database_helpers.rb
|
|
246
198
|
homepage: https://github.com/midwire/free_zipcode_data
|
|
247
199
|
licenses:
|
|
248
200
|
- MIT
|
|
249
201
|
metadata:
|
|
250
202
|
rubygems_mfa_required: 'true'
|
|
251
|
-
post_install_message:
|
|
252
203
|
rdoc_options: []
|
|
253
204
|
require_paths:
|
|
254
205
|
- lib
|
|
@@ -256,15 +207,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
256
207
|
requirements:
|
|
257
208
|
- - ">="
|
|
258
209
|
- !ruby/object:Gem::Version
|
|
259
|
-
version: 3.
|
|
210
|
+
version: 3.4.8
|
|
260
211
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
261
212
|
requirements:
|
|
262
213
|
- - ">="
|
|
263
214
|
- !ruby/object:Gem::Version
|
|
264
215
|
version: '0'
|
|
265
216
|
requirements: []
|
|
266
|
-
rubygems_version: 3.
|
|
267
|
-
signing_key:
|
|
217
|
+
rubygems_version: 3.6.9
|
|
268
218
|
specification_version: 4
|
|
269
219
|
summary: Free US and world-wide postal codes in SQLite and CSV format
|
|
270
220
|
test_files: []
|