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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +10 -0
  3. data/.gitignore +2 -0
  4. data/.rubocop.yml +25 -16
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG +17 -0
  7. data/CLAUDE.md +89 -0
  8. data/Dockerfile +21 -0
  9. data/Gemfile +10 -0
  10. data/Gemfile.lock +50 -36
  11. data/README.md +38 -5
  12. data/Rakefile +1 -1
  13. data/docker-entrypoint.sh +14 -0
  14. data/free_zipcode_data.gemspec +8 -14
  15. data/lib/etl/common.rb +1 -0
  16. data/lib/etl/csv_source.rb +4 -4
  17. data/lib/free_zipcode_data/country_table.rb +10 -2
  18. data/lib/free_zipcode_data/county_table.rb +14 -6
  19. data/lib/free_zipcode_data/data_source.rb +2 -2
  20. data/lib/free_zipcode_data/db_table.rb +54 -7
  21. data/lib/free_zipcode_data/logger.rb +8 -12
  22. data/lib/free_zipcode_data/runner.rb +2 -2
  23. data/lib/free_zipcode_data/state_table.rb +37 -5
  24. data/lib/free_zipcode_data/version.rb +1 -1
  25. data/lib/free_zipcode_data/zipcode_table.rb +15 -5
  26. data/lib/free_zipcode_data.rb +3 -3
  27. data/lib/tasks/version.rake +27 -24
  28. data/spec/etl/csv_source_spec.rb +57 -0
  29. data/spec/etl/free_zipcode_data_job_spec.rb +135 -0
  30. data/spec/fixtures/.free_zipcode_data.yml +1 -0
  31. data/spec/fixtures/US.txt +5 -0
  32. data/spec/fixtures/US.zip +0 -0
  33. data/spec/fixtures/test_data.csv +7 -0
  34. data/spec/fixtures/test_data.txt +5 -0
  35. data/spec/free_zipcode_data/country_table_spec.rb +52 -0
  36. data/spec/free_zipcode_data/county_table_spec.rb +84 -0
  37. data/spec/free_zipcode_data/data_source_spec.rb +131 -0
  38. data/spec/free_zipcode_data/db_table_spec.rb +164 -0
  39. data/spec/free_zipcode_data/logger_spec.rb +78 -0
  40. data/spec/free_zipcode_data/options_spec.rb +37 -0
  41. data/spec/free_zipcode_data/runner_spec.rb +91 -0
  42. data/spec/free_zipcode_data/sqlite_ram_spec.rb +64 -0
  43. data/spec/free_zipcode_data/state_table_spec.rb +112 -0
  44. data/spec/free_zipcode_data/zipcode_table_spec.rb +102 -0
  45. data/spec/free_zipcode_data_spec.rb +38 -0
  46. data/spec/spec_helper.rb +23 -2
  47. data/spec/support/database_helpers.rb +48 -0
  48. 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
- require 'pry'
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')].sort.each { |f| require f }
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.6
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: 2025-09-30 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: bundler
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: '3.7'
63
- type: :development
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: '3.7'
26
+ version: '1.2'
70
27
  - !ruby/object:Gem::Dependency
71
- name: rubocop
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: :development
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: ruby-prof
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: '1.2'
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: '1.2'
54
+ version: '4.0'
126
55
  - !ruby/object:Gem::Dependency
127
- name: kiba
56
+ name: logger
128
57
  requirement: !ruby/object:Gem::Requirement
129
58
  requirements:
130
- - - "~>"
59
+ - - ">="
131
60
  - !ruby/object:Gem::Version
132
- version: '4.0'
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: '4.0'
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.0.2
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.2.22
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: []