ziptz 6.1.0 → 6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab5db4fd6e68687e6761f890c37122d5ba3c393f267c2efdf7320f66cdf81b9e
4
- data.tar.gz: 153766d2364660656ca42179ce52d649a179c600ea21ea1713d97381af10c874
3
+ metadata.gz: 8e148a6e535c92aee764520b3741c8c550719d2c430df9c18f84252e233eb17a
4
+ data.tar.gz: 1d56d7416206c48354f30f0b544639d99e86ed68908a9214ee654cdee7cbf70a
5
5
  SHA512:
6
- metadata.gz: 5680d6235c68c8d70d8c89b45d23844db662b3dad0a8090bbf9abed9c23be48f835ae6bbb260a55eac74af3bb11ea61a9ae3ab53adb88e7831b0b4292dddf5d5
7
- data.tar.gz: 3a04b0ee0c20e672059eef0122279b5cc8f082baab15b910cc547b39cb4af349065804605d83ee3e90c66d1b2e4ddef5077cb63f446ffad9f4ad187d26dad1a0
6
+ metadata.gz: 69974ccd6732bb9043f9c4c7ab5f77ec9d5cc4991b07170b7ea92630f9910a24df9eb7fdc941930f092c188d480b967dfc3e83a99eb1462fd6c1f2e2a4298fe8
7
+ data.tar.gz: 1665d43f017697702ed8d3c4d8468d06e33bd46bc6f3e172fb39a4f342f05bd95b9eb7b3a4da93cdfe581a38ba5e610ee71b11f23473302838eee56361fd503d
data/CHANGELOG.md ADDED
@@ -0,0 +1,78 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## 6.2.0 - 2026-08-14
9
+
10
+ ### Added
11
+
12
+ - Automated gem publication via
13
+ [RubyGems trusted publishing](https://guides.rubygems.org/trusted-publishing/):
14
+ pushing a `v*` tag builds and publishes the gem from GitHub Actions.
15
+ - Gemspec metadata links (`source_code_uri`, `changelog_uri`,
16
+ `bug_tracker_uri`) and this changelog.
17
+ - Zip codes may now be passed as Integers; they are zero-padded to 5 digits
18
+ (`z.time_zone_name(601)` looks up `00601`). Previously an Integer raised
19
+ `NoMethodError`.
20
+ - `Ziptz#close` closes the cached SQLite connection for the instance's
21
+ database on the current thread. Closing is idempotent and a new connection
22
+ is opened automatically on the next query.
23
+
24
+ ### Fixed
25
+
26
+ - Database connections are cached per database path, so multiple `Ziptz`
27
+ instances pointing at different databases no longer share a connection.
28
+ - The packaged gem no longer includes the spec suite and its 2.7 MB fixture
29
+ database, shrinking the gem from ~2.7 MB to a few KB.
30
+
31
+ ### Changed
32
+
33
+ - CI now enforces RuboCop and runs tests against Ruby 3.3, 3.4, and 4.0.
34
+ - `Ziptz::VERSION` moved from `lib/version.rb` to the conventional
35
+ `lib/ziptz/version.rb`.
36
+ - README documents the supported Rubies (3.3/3.4/4.0); copyright years
37
+ updated through 2026.
38
+
39
+ ### Removed
40
+
41
+ - Leftover development dependencies from the long-removed data-import
42
+ pipeline (activerecord, mysql2, tty-spinner).
43
+
44
+ ## 6.1.0 - 2026-04-09
45
+
46
+ ### Changed
47
+
48
+ - Unknown, invalid, and nil zip codes now raise `Ziptz::ZipNotFound` instead
49
+ of returning nil.
50
+
51
+ ### Removed
52
+
53
+ - Dropped support for Ruby 3.1 and 3.2; Ruby >= 3.3 is now required.
54
+
55
+ ## 6.0.0 - 2024-12-27
56
+
57
+ ### Changed
58
+
59
+ - **Breaking:** the gem no longer bundles zip code time zone data. Supply
60
+ your own SQLite database and pass its path to `Ziptz.new`. See the
61
+ [ZIP Code Time Zones Dataset](https://www.infused-data.com/zip-code-time-zones/).
62
+ - Database connections are cached per thread for thread safety.
63
+
64
+ ### Removed
65
+
66
+ - The `Ziptz.instance` class method.
67
+
68
+ ## 5.1.0 - 2024-04-28
69
+
70
+ ### Changed
71
+
72
+ - sqlite3 dependency updated from `~> 1.6` to `~> 2.0` (#11).
73
+
74
+ ## 5.0.0 - 2023-12-28
75
+
76
+ ### Removed
77
+
78
+ - Dropped support for Rubies older than 3.1.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2015-2024 Keith Morrison <keithm@infused.org>
1
+ Copyright (c) 2015-2026 Keith Morrison <keithm@infused.org>
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -12,7 +12,7 @@ Get time zone, daylight saving time, and base offset for any US ZIP code.
12
12
 
13
13
  Ziptz is tested to work with the following versions of Ruby:
14
14
 
15
- * Ruby 3.1.x, 3.2.x, 3.3.x
15
+ * Ruby 3.3.x, 3.4.x, 4.0.x
16
16
 
17
17
  ## Installation
18
18
 
@@ -37,6 +37,12 @@ Get the time zone name for any ZIP code:
37
37
 
38
38
  z.time_zone_name('97213') #=> "America/Los_Angeles"
39
39
 
40
+ ZIP codes may be passed as strings or integers. Integers are zero-padded
41
+ to 5 digits, and 9-digit ZIP+4 strings are truncated to their first 5 digits:
42
+
43
+ z.time_zone_name(601) #=> "America/Puerto_Rico" (looks up 00601)
44
+ z.time_zone_name('97213-4704') #=> "America/Los_Angeles"
45
+
40
46
  Get the time zone offset for any ZIP code:
41
47
 
42
48
  z.time_zone_offset('97213') #=> -28800
@@ -51,6 +57,17 @@ Get all the zip codes in a time zone:
51
57
 
52
58
  z.zips('Pacific/Chuuk') #=> ["96942", "96943"]
53
59
 
60
+ Lookups for unknown, invalid, or nil ZIP codes raise `Ziptz::ZipNotFound`
61
+ (since version 6.1.0; earlier versions returned nil):
62
+
63
+ z.time_zone_name('00000') #=> raises Ziptz::ZipNotFound
64
+
65
+ Database connections are opened lazily and cached per thread. Call `close`
66
+ to close the current thread's connection; the next lookup reopens it
67
+ automatically:
68
+
69
+ z.close
70
+
54
71
 
55
72
  ## Supported Time Zones
56
73
 
@@ -169,7 +186,7 @@ Time zone names now reflect standard tz-database names.
169
186
 
170
187
  ## License
171
188
 
172
- Copyright (c) 2015-2024 Keith Morrison <<keithm@infused.org>>
189
+ Copyright (c) 2015-2026 Keith Morrison <<keithm@infused.org>>
173
190
 
174
191
  Permission is hereby granted, free of charge, to any person
175
192
  obtaining a copy of this software and associated documentation
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Ziptz
4
- VERSION = '6.1.0'
4
+ VERSION = '6.2.0'
5
5
  end
data/lib/ziptz.rb CHANGED
@@ -1,31 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'sqlite3'
4
- require 'version'
4
+ require_relative 'ziptz/version'
5
5
 
6
6
  class Ziptz
7
7
  class ZipNotFound < StandardError; end
8
8
 
9
- def initialize(*args, db: nil)
10
- @db_path = args[0] || db
9
+ def initialize(path = nil, db: nil)
10
+ @db_path = path || db
11
11
  raise ArgumentError, 'Database path is required' if @db_path.nil? || @db_path.empty?
12
12
  raise ArgumentError, 'Database file does not exist' unless File.exist?(@db_path)
13
13
  end
14
14
 
15
15
  def db
16
- Thread.current[:ziptz_db] ||= begin
16
+ connections = Thread.current[:ziptz_db] ||= {}
17
+ connections[@db_path] ||= begin
17
18
  db = SQLite3::Database.open(@db_path)
18
19
  db.results_as_hash = true
19
20
  db
20
21
  end
21
22
  end
22
23
 
24
+ def close
25
+ Thread.current[:ziptz_db]&.delete(@db_path)&.close
26
+ nil
27
+ end
28
+
23
29
  def time_zone_name(zip)
24
- time_zone_info(zip).dig('time_zone')
30
+ time_zone_info(zip)['time_zone']
25
31
  end
26
32
 
27
33
  def time_zone_offset(zip)
28
- time_zone_info(zip).dig('offset')
34
+ time_zone_info(zip)['offset']
29
35
  end
30
36
 
31
37
  def time_zone_uses_dst?(zip)
@@ -46,12 +52,13 @@ class Ziptz
46
52
  "#<#{self.class}:#{object_id}>"
47
53
  end
48
54
 
49
- protected
55
+ private
50
56
 
51
57
  def time_zone_info(zip)
52
58
  raise ZipNotFound, "Zip code not found: #{zip.inspect}" if zip.nil?
53
59
 
54
- result = db.get_first_row('select * from zip_codes where zip_code = ? limit 1', zip[0, 5])
60
+ zip_code = zip.is_a?(Integer) ? format('%05d', zip) : zip.to_s[0, 5]
61
+ result = db.get_first_row('select * from zip_codes where zip_code = ? limit 1', zip_code)
55
62
  raise ZipNotFound, "Zip code not found: #{zip}" unless result
56
63
 
57
64
  result
data/ziptz.gemspec CHANGED
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path('lib', __dir__)
4
- $LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
5
- require 'version'
3
+ require_relative 'lib/ziptz/version'
6
4
 
7
5
  Gem::Specification.new do |s|
8
6
  s.name = 'ziptz'
@@ -10,16 +8,21 @@ Gem::Specification.new do |s|
10
8
  s.platform = Gem::Platform::RUBY
11
9
  s.authors = ['Keith Morrison']
12
10
  s.email = 'keithm@infused.org'
13
- s.homepage = 'http://github.com/infused/ziptz'
11
+ s.homepage = 'https://github.com/infused/ziptz'
14
12
  s.summary = 'TimeZone info for any 5-digit US zip code'
15
13
  s.description = 'Get timezone info for all 5-digit US zip codes'
16
14
  s.license = 'MIT'
17
15
 
18
16
  s.rdoc_options = ['--charset=UTF-8']
19
17
  s.extra_rdoc_files = ['README.md', 'LICENSE']
20
- s.files = Dir['README.md', 'LICENSE', '{data,lib,spec}/**/*', 'ziptz.gemspec']
18
+ s.files = Dir['README.md', 'CHANGELOG.md', 'LICENSE', 'lib/**/*', 'ziptz.gemspec']
21
19
  s.require_paths = ['lib']
22
20
  s.required_ruby_version = '>= 3.3.0'
23
- s.metadata['rubygems_mfa_required'] = 'true'
21
+ s.metadata = {
22
+ 'source_code_uri' => 'https://github.com/infused/ziptz',
23
+ 'changelog_uri' => 'https://github.com/infused/ziptz/blob/main/CHANGELOG.md',
24
+ 'bug_tracker_uri' => 'https://github.com/infused/ziptz/issues',
25
+ 'rubygems_mfa_required' => 'true'
26
+ }
24
27
  s.add_dependency 'sqlite3', '~> 2.0'
25
28
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ziptz
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.1.0
4
+ version: 6.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keith Morrison
@@ -31,18 +31,19 @@ extra_rdoc_files:
31
31
  - LICENSE
32
32
  - README.md
33
33
  files:
34
+ - CHANGELOG.md
34
35
  - LICENSE
35
36
  - README.md
36
- - lib/version.rb
37
37
  - lib/ziptz.rb
38
- - spec/fixtures/tz.db
39
- - spec/spec_helper.rb
40
- - spec/ziptz_spec.rb
38
+ - lib/ziptz/version.rb
41
39
  - ziptz.gemspec
42
- homepage: http://github.com/infused/ziptz
40
+ homepage: https://github.com/infused/ziptz
43
41
  licenses:
44
42
  - MIT
45
43
  metadata:
44
+ source_code_uri: https://github.com/infused/ziptz
45
+ changelog_uri: https://github.com/infused/ziptz/blob/main/CHANGELOG.md
46
+ bug_tracker_uri: https://github.com/infused/ziptz/issues
46
47
  rubygems_mfa_required: 'true'
47
48
  rdoc_options:
48
49
  - "--charset=UTF-8"
@@ -59,7 +60,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
59
60
  - !ruby/object:Gem::Version
60
61
  version: '0'
61
62
  requirements: []
62
- rubygems_version: 4.0.6
63
+ rubygems_version: 4.0.16
63
64
  specification_version: 4
64
65
  summary: TimeZone info for any 5-digit US zip code
65
66
  test_files: []
data/spec/fixtures/tz.db DELETED
Binary file
data/spec/spec_helper.rb DELETED
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'ziptz'
4
- require 'yaml'
5
- require 'rspec'
6
-
7
- RSpec.configure do |config|
8
- config.order = :random
9
- config.disable_monkey_patching!
10
- end
data/spec/ziptz_spec.rb DELETED
@@ -1,133 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- RSpec.describe Ziptz do
6
- let(:db_path) { File.join(File.dirname(__FILE__), 'fixtures', 'tz.db') }
7
- let(:ziptz) { described_class.new(db_path) }
8
-
9
- describe 'when inspected' do
10
- it 'does not show internal instance variables' do
11
- expect(ziptz.inspect).to match(/#<Ziptz:\d+>/)
12
- end
13
- end
14
-
15
- describe '#time_zone_name' do
16
- context 'when given a 5-digit zipcode' do
17
- it 'returns the time zone number' do
18
- expect(ziptz.time_zone_name('97034')).to eq 'America/Los_Angeles'
19
- end
20
- end
21
-
22
- context 'when given a 9-digit zipcode' do
23
- it 'returns the time zone number' do
24
- expect(ziptz.time_zone_name('97034-1234')).to eq 'America/Los_Angeles'
25
- end
26
- end
27
-
28
- context 'when there is no matching zipcode' do
29
- it 'raises ZipNotFound' do
30
- expect { ziptz.time_zone_name('xyz') }.to raise_error(Ziptz::ZipNotFound)
31
- end
32
- end
33
-
34
- context 'when nil is passed' do
35
- it 'raises ZipNotFound' do
36
- expect { ziptz.time_zone_name(nil) }.to raise_error(Ziptz::ZipNotFound)
37
- end
38
- end
39
- end
40
-
41
- describe '#time_zone_uses_dst?' do
42
- context 'when given a valid 5-digit zipcode' do
43
- it 'returns true' do
44
- expect(ziptz.time_zone_uses_dst?('97034')).to be true
45
- end
46
- end
47
-
48
- context 'when given an invalid 5-digit zipcode' do
49
- it 'returns false' do
50
- expect(ziptz.time_zone_uses_dst?('85004')).to be false
51
- end
52
- end
53
-
54
- context 'when given a valid 9-digit zipcode' do
55
- it 'returns a true' do
56
- expect(ziptz.time_zone_uses_dst?('97034-1234')).to be true
57
- end
58
- end
59
-
60
- context 'when given an invalid 9-digit zipcode' do
61
- it 'returns false' do
62
- expect(ziptz.time_zone_uses_dst?('85004-1234')).to be false
63
- end
64
- end
65
-
66
- context 'when there is no matching zipcode' do
67
- it 'raises ZipNotFound' do
68
- expect { ziptz.time_zone_uses_dst?('xyz') }.to raise_error(Ziptz::ZipNotFound)
69
- end
70
- end
71
- end
72
-
73
- describe '#time_zone_offset' do
74
- context 'when given a 5-digit zipcode' do
75
- it 'returns the time zone number' do
76
- expect(ziptz.time_zone_offset('97034')).to eq(-28_800)
77
- end
78
- end
79
-
80
- context 'when given a 9-digit zipcode' do
81
- it 'returns the time zone number' do
82
- expect(ziptz.time_zone_offset('97034-1234')).to eq(-28_800)
83
- end
84
- end
85
-
86
- context 'when there is no matching zipcode' do
87
- it 'raises ZipNotFound' do
88
- expect { ziptz.time_zone_offset('xyz') }.to raise_error(Ziptz::ZipNotFound)
89
- end
90
- end
91
- end
92
-
93
- describe '#zips' do
94
- context 'when given a time zone' do
95
- it 'returns an array of zip codes' do
96
- expect(ziptz.zips('Pacific/Pago_Pago')).to eq %w[96799]
97
- end
98
-
99
- it 'is case-insensitive' do
100
- expect(ziptz.zips('pacific/pago_pago')).to eq %w[96799]
101
- end
102
-
103
- it 'returns empty array for unknown time zones' do
104
- expect(ziptz.zips('Glark')).to be_empty
105
- end
106
- end
107
- end
108
-
109
- describe 'initialization' do
110
- it 'accepts database path as first argument' do
111
- expect { described_class.new(db_path) }.to_not raise_error
112
- end
113
-
114
- it 'accepts database path as keyword argument' do
115
- expect { described_class.new(db: db_path) }.to_not raise_error
116
- end
117
-
118
- it 'raises an error when database path is nil' do
119
- expect { described_class.new }.to raise_error(ArgumentError, 'Database path is required')
120
- expect { described_class.new(db: nil) }.to raise_error(ArgumentError, 'Database path is required')
121
- end
122
-
123
- it 'raises an error when database path is empty' do
124
- expect { described_class.new('') }.to raise_error(ArgumentError, 'Database path is required')
125
- expect { described_class.new(db: '') }.to raise_error(ArgumentError, 'Database path is required')
126
- end
127
-
128
- it 'raises an error when database file does not exist' do
129
- expect { described_class.new('nonexistent.db') }.to raise_error(ArgumentError, 'Database file does not exist')
130
- expect { described_class.new(db: 'nonexistent.db') }.to raise_error(ArgumentError, 'Database file does not exist')
131
- end
132
- end
133
- end