thai_id_utils 0.1.1 → 0.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: d9194c3362b9038193a979c13e0d7723bb0c6f0c3ce41ff17523086ef8a04672
4
- data.tar.gz: dac42ef970424a31999019dc4c3d8d1fa5c5739c55427440906e118d4c2cecba
3
+ metadata.gz: a190bd99a1b5194f6dd8b8fb9b54ff187f8612acef24e17d86fbbe058f404e49
4
+ data.tar.gz: a13c400601b9ae3f4426fe8dfb619403cbd52ae511552b37c5617f0ae0518c8f
5
5
  SHA512:
6
- metadata.gz: c3f92740ba6ac0700b5cddb7abdc23a74cdc759a822a14936a1a36acbd43e021ad42f395889ec09d4db5cf3ed1f641599d454d1b8eb4cc17756365f26f853ab9
7
- data.tar.gz: 49bbfa319256583fd8156cea558995d2d555e17352b0391830046ff78cdb90034306c9cf5cb0d5eacf13195380d61c3196e14d7d7d27f38b266195b04c67240c
6
+ metadata.gz: fbfe9afb6a1f1d13f4b8baa45e04287ba46b2c28abdf192fff6a7dd6debc745f55fefc7ea332fd601df7d9e3fdc41194e90da539648eb8038c19e4c72ede7232
7
+ data.tar.gz: 3503c0c47a6b022689f4a46958b03caf7e269b18131422aa2e6db107ce64d8c51fb9da675084844ef27aee650af22beecd8313ba679d81e92db0960c76eaf13c
data/CHANGELOG.md ADDED
@@ -0,0 +1,41 @@
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.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.2.0] - 2025-06-15
9
+
10
+ ### Added
11
+ - Province code lookup (`PROVINCE_CODES`) mapping all 77 Thai provinces
12
+ - `province_name(code)` — return province name from 2-digit code
13
+ - Laser ID validation (`laser_id_valid?`) using format `XXN-NNNNNNN-NN`
14
+ - Laser ID decoding (`laser_id_decode`) into hardware version, box ID, and position
15
+ - Buddhist Era conversion: `be_to_ce(year)` and `ce_to_be(year)`
16
+ - `province_name` field included in `decode` output hash
17
+
18
+ ## [0.1.2] - 2025-06-15
19
+
20
+ ### Fixed
21
+ - Minor internal cleanup; no public API changes
22
+
23
+ ## [0.1.1] - 2025-06-15
24
+
25
+ ### Fixed
26
+ - Gemspec corrections and metadata updates
27
+
28
+ ## [0.1.0] - 2025-06-15
29
+
30
+ ### Added
31
+ - Initial release
32
+ - `valid?(id)` — checksum validation using Thailand's modulus-11 algorithm
33
+ - `decode(id)` — decode category, office code, province code, district code, sequence, and registration code
34
+ - `generate(...)` — generate a random valid 13-digit Thai national ID with optional overrides
35
+ - `category_description(category)` — human-readable description of ID category codes (0–8)
36
+ - `InvalidIDError` — raised on invalid IDs passed to `decode`
37
+
38
+ [0.2.0]: https://github.com/chayuto/thai_id_utils/compare/v0.1.2...v0.2.0
39
+ [0.1.2]: https://github.com/chayuto/thai_id_utils/compare/v0.1.1...v0.1.2
40
+ [0.1.1]: https://github.com/chayuto/thai_id_utils/compare/v0.1.0...v0.1.1
41
+ [0.1.0]: https://github.com/chayuto/thai_id_utils/releases/tag/v0.1.0
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Chayut Orapinpatipat
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -11,23 +11,24 @@ require "thai_id_utils"
11
11
 
12
12
  id = "3012304567082"
13
13
 
14
- # Validate checksum
14
+ # Validate checksum / ตรวจสอบความถูกต้องของ checksum
15
15
  if ThaiIdUtils.valid?(id)
16
16
  puts "Valid!"
17
17
  else
18
18
  puts "Invalid ID"
19
19
  end
20
20
 
21
- # 🔍 Decode components
21
+ # Decode components / ถอดรหัสส่วนประกอบ
22
22
  info = ThaiIdUtils.decode(id)
23
23
  # => { category: 1, office_code: "6099", district_code: "99", sequence: "00257" }
24
24
  puts info.inspect
25
25
 
26
- # 🏷️ Get category description
26
+ # Get category description / คำอธิบายประเภท
27
27
  desc = ThaiIdUtils.category_description(info[:category])
28
28
  # => "Thai nationals who were born after 1 January 1984 and had their birth notified within the given deadline (15 days)."
29
29
  puts desc
30
30
 
31
- # 🎲 Generate a new random valid ID
31
+ # Generate a new random valid ID / สร้างหมายเลขบัตรประชาชนใหม่แบบสุ่มที่ถูกต้อง
32
32
  new_id = ThaiIdUtils.generate
33
33
  puts new_id # => e.g. "3601205234518"
34
+ ```
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ThaiIdUtils
4
- VERSION = '0.1.1'
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/thai_id_utils.rb CHANGED
@@ -21,9 +21,58 @@ module ThaiIdUtils
21
21
  7 => 'Children of people of category 6 who were born in Thailand',
22
22
  8 => 'Foreign nationals who are living in Thailand permanently or Thai nationals by naturalization'
23
23
  }.freeze
24
+
25
+ # Mapping of 2-digit province codes (digits 2-3 of the ID) to province names
26
+ PROVINCE_CODES = {
27
+ '10' => 'Bangkok', '11' => 'Samut Prakan',
28
+ '12' => 'Nonthaburi', '13' => 'Pathum Thani',
29
+ '14' => 'Phra Nakhon Si Ayutthaya', '15' => 'Ang Thong',
30
+ '16' => 'Lopburi', '17' => 'Sing Buri',
31
+ '18' => 'Chainat', '19' => 'Saraburi',
32
+ '20' => 'Chonburi', '21' => 'Rayong',
33
+ '22' => 'Chanthaburi', '23' => 'Trat',
34
+ '24' => 'Chachoengsao', '25' => 'Prachin Buri',
35
+ '26' => 'Nakhon Nayok', '27' => 'Sa Kaeo',
36
+ '30' => 'Nakhon Ratchasima', '31' => 'Buri Ram',
37
+ '32' => 'Surin', '33' => 'Si Sa Ket',
38
+ '34' => 'Ubon Ratchathani', '35' => 'Yasothon',
39
+ '36' => 'Chaiyaphum', '37' => 'Amnat Charoen',
40
+ '38' => 'Bueng Kan', '39' => 'Nong Bua Lamphu',
41
+ '40' => 'Khon Kaen', '41' => 'Udon Thani',
42
+ '42' => 'Loei', '43' => 'Nong Khai',
43
+ '44' => 'Maha Sarakham', '45' => 'Roi Et',
44
+ '46' => 'Kalasin', '47' => 'Sakon Nakhon',
45
+ '48' => 'Nakhon Phanom', '49' => 'Mukdahan',
46
+ '50' => 'Chiang Mai', '51' => 'Lamphun',
47
+ '52' => 'Lampang', '53' => 'Uttaradit',
48
+ '54' => 'Phrae', '55' => 'Nan',
49
+ '56' => 'Phayao', '57' => 'Chiang Rai',
50
+ '58' => 'Mae Hong Son',
51
+ '60' => 'Nakhon Sawan', '61' => 'Uthai Thani',
52
+ '62' => 'Kamphaeng Phet', '63' => 'Tak',
53
+ '64' => 'Sukhothai', '65' => 'Phitsanulok',
54
+ '66' => 'Phichit', '67' => 'Phetchabun',
55
+ '70' => 'Ratchaburi', '71' => 'Kanchanaburi',
56
+ '72' => 'Suphanburi', '73' => 'Nakhon Pathom',
57
+ '74' => 'Samut Sakhon', '75' => 'Samut Songkhram',
58
+ '76' => 'Phetchaburi', '77' => 'Prachuap Khiri Khan',
59
+ '80' => 'Nakhon Si Thammarat', '81' => 'Krabi',
60
+ '82' => 'Phangnga', '83' => 'Phuket',
61
+ '84' => 'Surat Thani', '85' => 'Ranong',
62
+ '86' => 'Chumphon',
63
+ '90' => 'Songkhla', '91' => 'Satun',
64
+ '92' => 'Trang', '93' => 'Phatthalung',
65
+ '94' => 'Pattani', '95' => 'Yala',
66
+ '96' => 'Narathiwat'
67
+ }.freeze
24
68
  # rubocop:enable Layout/LineLength
25
69
 
26
- # Public: Validate checksum using Thailand’s modulus-11 algorithm
70
+ LASER_ID_FORMAT = /\A[A-Z]{2}\d-\d{7}-\d{2}\z/.freeze
71
+
72
+ # Validate a Thai national ID using Thailand’s modulus-11 checksum algorithm.
73
+ #
74
+ # @param id [String, Integer] 13-digit Thai national ID number
75
+ # @return [Boolean] true if the checksum is valid, false otherwise
27
76
  def self.valid?(id)
28
77
  digits = id.to_s.chars.map(&:to_i)
29
78
  return false unless digits.size == 13
@@ -34,12 +83,18 @@ module ThaiIdUtils
34
83
  false
35
84
  end
36
85
 
37
- # Public: Decode components present in a Thai ID
38
- # Returns a hash with keys:
39
- # :category => Integer
40
- # :office_code => String (4-digit registrar)
41
- # :district_code => String (last 2 of office_code)
42
- # :sequence => String (5-digit personal sequence)
86
+ # Decode the components encoded in a Thai national ID number.
87
+ #
88
+ # @param id [String, Integer] 13-digit Thai national ID number
89
+ # @return [Hash] decoded fields:
90
+ # - `:category` [Integer] registration category (0–8)
91
+ # - `:office_code` [String] — 4-digit registrar code (province + district)
92
+ # - `:province_code` [String] — first 2 digits of office_code
93
+ # - `:province_name` [String, nil] — province name, or nil if unknown
94
+ # - `:district_code` [String] — last 2 digits of office_code
95
+ # - `:sequence` [String] — 5-digit personal sequence number
96
+ # - `:registration_code` [String] — 2-digit chronological sequence marker
97
+ # @raise [InvalidIDError] if the ID fails checksum validation
43
98
  def self.decode(id)
44
99
  raise InvalidIDError, 'Invalid ID' unless valid?(id)
45
100
 
@@ -48,14 +103,23 @@ module ThaiIdUtils
48
103
  {
49
104
  category: d[0].to_i,
50
105
  office_code: d[1..4].join,
106
+ province_code: d[1..2].join,
107
+ province_name: PROVINCE_CODES[d[1..2].join],
51
108
  district_code: d[3..4].join,
52
- sequence: d[5..9].join
109
+ sequence: d[5..9].join,
110
+ registration_code: d[10..11].join
53
111
  }
54
112
  end
55
113
 
56
- # Public: Generate a random, valid 13-digit Thai national ID.
57
- # You can override any component or let it be randomized.
58
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
114
+ # Generate a random, valid 13-digit Thai national ID.
115
+ # Any component can be overridden; the rest is randomized and the checksum is computed.
116
+ #
117
+ # @param category [Integer] ID category (1–8), default: random 1–6
118
+ # @param office_code [Integer, String, nil] 4-digit registrar code, default: random
119
+ # @param district_code [String, nil] 2-digit district override within office_code
120
+ # @param sequence [Integer, String, nil] 5-digit personal sequence, default: random
121
+ # @return [String] a valid 13-digit Thai national ID
122
+ # rubocop:disable Metrics/AbcSize
59
123
  def self.generate(category: rand(1..6),
60
124
  office_code: nil,
61
125
  district_code: nil,
@@ -80,10 +144,65 @@ module ThaiIdUtils
80
144
 
81
145
  (digits + [check]).join
82
146
  end
83
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
147
+ # rubocop:enable Metrics/AbcSize
84
148
 
85
- # Public: Return a human-readable description for a category code
149
+ # Return the human-readable description for a Thai ID category code.
150
+ #
151
+ # @param category [Integer, String] category digit (0–8)
152
+ # @return [String] description, or "Unknown category" if not found
86
153
  def self.category_description(category)
87
154
  CATEGORY_DESCRIPTIONS[category.to_i] || 'Unknown category'
88
155
  end
156
+
157
+ # Return the province name for a 2-digit province code.
158
+ #
159
+ # @param code [String] 2-digit province code (e.g., "10" for Bangkok)
160
+ # @return [String, nil] province name, or nil if the code is not recognized
161
+ def self.province_name(code)
162
+ PROVINCE_CODES[code.to_s]
163
+ end
164
+
165
+ # Convert a Buddhist Era year to Common Era (subtract 543).
166
+ #
167
+ # @param year [Integer, String] Buddhist Era year (e.g., 2567)
168
+ # @return [Integer] Common Era year (e.g., 2024)
169
+ def self.be_to_ce(year)
170
+ year.to_i - 543
171
+ end
172
+
173
+ # Convert a Common Era year to Buddhist Era (add 543).
174
+ #
175
+ # @param year [Integer, String] Common Era year (e.g., 2024)
176
+ # @return [Integer] Buddhist Era year (e.g., 2567)
177
+ def self.ce_to_be(year)
178
+ year.to_i + 543
179
+ end
180
+
181
+ # Validate the format of a Thai ID card laser ID (printed on the card back).
182
+ # Expected format: XXN-NNNNNNN-NN (e.g., JC1-0002507-15)
183
+ #
184
+ # @param laser_id [String] the laser ID string to validate
185
+ # @return [Boolean] true if format matches, false otherwise
186
+ def self.laser_id_valid?(laser_id)
187
+ LASER_ID_FORMAT.match?(laser_id.to_s)
188
+ end
189
+
190
+ # Decode a Thai ID card laser ID into its components.
191
+ #
192
+ # @param laser_id [String] laser ID string (e.g., "JC1-0002507-15")
193
+ # @return [Hash] decoded fields:
194
+ # - `:hardware_version` [String] — chip generation code (e.g., "JC1")
195
+ # - `:box_id` [String] — distribution box number (e.g., "0002507")
196
+ # - `:position` [String] — slot within the box (e.g., "15")
197
+ # @raise [InvalidIDError] if the laser ID format is invalid
198
+ def self.laser_id_decode(laser_id)
199
+ raise InvalidIDError, 'Invalid laser ID' unless laser_id_valid?(laser_id)
200
+
201
+ parts = laser_id.to_s.split('-')
202
+ {
203
+ hardware_version: parts[0],
204
+ box_id: parts[1],
205
+ position: parts[2]
206
+ }
207
+ end
89
208
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thai_id_utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chayut Orapinpatipat
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-06-15 00:00:00.000000000 Z
11
+ date: 2026-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -36,15 +36,19 @@ executables: []
36
36
  extensions: []
37
37
  extra_rdoc_files: []
38
38
  files:
39
- - Gemfile
39
+ - CHANGELOG.md
40
+ - LICENSE
40
41
  - README.md
41
- - Rakefile
42
42
  - lib/thai_id_utils.rb
43
43
  - lib/thai_id_utils/version.rb
44
- homepage: https://github.com/chayuto/thai-id-utils
44
+ homepage: https://github.com/chayuto/thai_id_utils
45
45
  licenses:
46
46
  - MIT
47
- metadata: {}
47
+ metadata:
48
+ documentation_uri: https://rubydoc.info/gems/thai_id_utils
49
+ source_code_uri: https://github.com/chayuto/thai_id_utils
50
+ changelog_uri: https://github.com/chayuto/thai_id_utils/blob/main/CHANGELOG.md
51
+ rubygems_mfa_required: 'true'
48
52
  post_install_message:
49
53
  rdoc_options: []
50
54
  require_paths:
@@ -60,7 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
60
64
  - !ruby/object:Gem::Version
61
65
  version: '0'
62
66
  requirements: []
63
- rubygems_version: 3.5.11
67
+ rubygems_version: 3.5.22
64
68
  signing_key:
65
69
  specification_version: 4
66
70
  summary: Validate and decode Thai national ID numbers
data/Gemfile DELETED
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
-
5
- gemspec
data/Rakefile DELETED
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rake/testtask'
4
-
5
- Rake::TestTask.new do |t|
6
- t.libs << 'test'
7
- end
8
-
9
- task default: :test