thai_geodata 0.1.0 → 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.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ThaiGeodata
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/thai_geodata.rb CHANGED
@@ -3,24 +3,105 @@
3
3
  require 'json'
4
4
  require_relative 'thai_geodata/version'
5
5
 
6
- # ThaiGeodata provides in-memory lookup of Thailand’s provinces, districts
7
- # and subdistricts (with postal codes) from static JSON data.
6
+ # ThaiGeodata provides in-memory lookup of Thailand’s provinces, districts,
7
+ # subdistricts, and combined geography records (with postal codes) from static JSON data.
8
8
  module ThaiGeodata
9
9
  DATA_PATH = File.join(__dir__, 'thai_geodata', 'data')
10
10
 
11
- def self.provinces
12
- @provinces ||= load_json('provinces.json')
13
- end
11
+ class << self
12
+ # Raw data loaders
13
+ def provinces
14
+ @provinces ||= load_json('provinces.json')
15
+ end
14
16
 
15
- def self.districts
16
- @districts ||= load_json('districts.json')
17
- end
17
+ def districts
18
+ @districts ||= load_json('districts.json')
19
+ end
18
20
 
19
- def self.subdistricts
20
- @subdistricts ||= load_json('subdistricts.json')
21
- end
21
+ def subdistricts
22
+ @subdistricts ||= load_json('subdistricts.json')
23
+ end
24
+
25
+ def geography
26
+ @geography ||= load_json('geography.json')
27
+ end
28
+
29
+ # Lookup helpers
30
+
31
+ # Find a province by code (Integer or String), or Thai/English name
32
+ def find_province(code_or_name)
33
+ provinces.find do |p|
34
+ match_code_or_name?(p, 'provinceCode', 'provinceNameTh', 'provinceNameEn', code_or_name)
35
+ end
36
+ end
37
+
38
+ # Find a district by code (Integer or String), or Thai/English name
39
+ def find_district(code_or_name)
40
+ districts.find do |d|
41
+ match_code_or_name?(d, 'districtCode', 'districtNameTh', 'districtNameEn', code_or_name)
42
+ end
43
+ end
44
+
45
+ # Find a subdistrict by code (Integer or String), or Thai/English name
46
+ def find_subdistrict(code_or_name)
47
+ subdistricts.find do |s|
48
+ match_code_or_name?(s, 'subdistrictCode', 'subdistrictNameTh', 'subdistrictNameEn', code_or_name)
49
+ end
50
+ end
51
+
52
+ # List all districts for a given province (code or name)
53
+ def districts_for_province(province_code_or_name)
54
+ prov = find_province(province_code_or_name)
55
+ return [] unless prov
56
+
57
+ districts.select { |d| d['provinceCode'] == prov['provinceCode'] }
58
+ end
59
+
60
+ # List all subdistricts for a given district (code or name)
61
+ def subdistricts_for_district(district_code_or_name)
62
+ dist = find_district(district_code_or_name)
63
+ return [] unless dist
64
+
65
+ subdistricts.select { |s| s['districtCode'] == dist['districtCode'] }
66
+ end
67
+
68
+ # Get the postal code for a given subdistrict (code or name)
69
+ def postal_code_for_subdistrict(code_or_name)
70
+ sub = find_subdistrict(code_or_name)
71
+ sub ? sub['postalCode'] : nil
72
+ end
73
+
74
+ # Get a full path (province, district, subdistrict) for a subdistrict
75
+ def location_path_for_subdistrict(code_or_name)
76
+ sub = find_subdistrict(code_or_name)
77
+ return nil unless sub
78
+
79
+ dist = find_district(sub['districtCode'])
80
+ prov = find_province(sub['provinceCode'])
81
+
82
+ {
83
+ province: prov ? prov['provinceNameTh'] : nil,
84
+ district: dist ? dist['districtNameTh'] : nil,
85
+ subdistrict: sub['subdistrictNameTh']
86
+ }
87
+ end
88
+
89
+ private
90
+
91
+ # Load JSON file from DATA_PATH
92
+ def load_json(filename)
93
+ path = File.join(DATA_PATH, filename)
94
+ JSON.parse(File.read(path))
95
+ end
22
96
 
23
- def self.load_json(file)
24
- JSON.parse(File.read(File.join(DATA_PATH, file)))
97
+ # Helper to match by numeric code or localized/English names
98
+ def match_code_or_name?(hash, code_key, th_key, en_key, code_or_name)
99
+ if code_or_name.to_s =~ /^\d+$/
100
+ hash[code_key] == code_or_name.to_i
101
+ else
102
+ hash[th_key] == code_or_name || hash[en_key].casecmp(code_or_name.to_s).zero?
103
+ end
104
+ end
105
+ private :match_code_or_name?
25
106
  end
26
107
  end
@@ -1,10 +1,63 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'minitest/autorun'
4
- require_relative '../lib/thai_geodata'
4
+ require 'thai_geodata'
5
5
 
6
6
  class ThaiGeodataTest < Minitest::Test
7
7
  def test_provinces_loaded
8
- assert ThaiGeodata.provinces.any?
8
+ refute_empty ThaiGeodata.provinces
9
+ end
10
+
11
+ def test_find_province_by_code
12
+ bangkok = ThaiGeodata.find_province(10)
13
+ assert_equal 'กรุงเทพมหานคร', bangkok['provinceNameTh']
14
+ end
15
+
16
+ def test_find_province_by_name_en
17
+ cm = ThaiGeodata.find_province('Chiang Mai')
18
+ assert_equal 50, cm['provinceCode']
19
+ end
20
+
21
+ def test_find_district_by_code
22
+ dist = ThaiGeodata.find_district(1001)
23
+ assert_equal 'พระนคร', dist['districtNameTh']
24
+ end
25
+
26
+ def test_find_subdistrict_by_code
27
+ sub = ThaiGeodata.find_subdistrict(100_101)
28
+ assert_equal 'พระบรมมหาราชวัง', sub['subdistrictNameTh']
29
+ end
30
+
31
+ def test_districts_for_province
32
+ dists = ThaiGeodata.districts_for_province('Bangkok')
33
+ assert dists.any?
34
+ assert_equal(dists, ThaiGeodata.districts.select { |d| d['provinceCode'] == 10 })
35
+ end
36
+
37
+ def test_subdistricts_for_district
38
+ subs = ThaiGeodata.subdistricts_for_district(1001)
39
+ assert subs.any?
40
+ assert_equal(subs, ThaiGeodata.subdistricts.select { |s| s['districtCode'] == 1001 })
41
+ end
42
+
43
+ def test_postal_code_for_subdistrict
44
+ assert_equal 10_200, ThaiGeodata.postal_code_for_subdistrict(100_101)
45
+ end
46
+
47
+ def test_location_path_for_subdistrict
48
+ path = ThaiGeodata.location_path_for_subdistrict(100_101)
49
+ expected = {
50
+ province: 'กรุงเทพมหานคร',
51
+ district: 'พระนคร',
52
+ subdistrict: 'พระบรมมหาราชวัง'
53
+ }
54
+ assert_equal expected, path
55
+ end
56
+
57
+ def test_geography_loaded
58
+ refute_empty ThaiGeodata.geography
59
+ first = ThaiGeodata.geography.first
60
+ assert first.key?('provinceCode')
61
+ assert first.key?('postalCode')
9
62
  end
10
63
  end
data/thai_geodata.gemspec CHANGED
@@ -17,4 +17,5 @@ Gem::Specification.new do |s|
17
17
  s.license = 'MIT'
18
18
 
19
19
  s.required_ruby_version = '>= 2.7'
20
+ s.add_development_dependency 'minitest', '~> 5.0'
20
21
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thai_geodata
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Your Name
@@ -9,7 +9,21 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
  date: 2025-06-15 00:00:00.000000000 Z
12
- dependencies: []
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
13
27
  description: A Ruby gem wrapping the MIT-licensed Thailand Geography JSON dataset,
14
28
  with lookup helpers.
15
29
  email:
@@ -25,6 +39,7 @@ files:
25
39
  - Rakefile
26
40
  - lib/thai_geodata.rb
27
41
  - lib/thai_geodata/data/districts.json
42
+ - lib/thai_geodata/data/geography.json
28
43
  - lib/thai_geodata/data/provinces.json
29
44
  - lib/thai_geodata/data/subdistricts.json
30
45
  - lib/thai_geodata/version.rb