pilipinas 1.0.0 → 1.1.1

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: 4a0d4e9bce22ec277d0dc496465c5f78ac9f6470c8ea579832191b2bcecba289
4
- data.tar.gz: 188a8a2c3d853c01da1eaa3d0609688b0a6612cd0db46fe600b5754d58864753
3
+ metadata.gz: 989723b7dcd5133a2d2240aa124e0c5400a981f521d0904d8b2858fb71f15e9e
4
+ data.tar.gz: c01c0377bf9572381844a38d1c77dbe45aed0e554076159a9964371f5f7aa851
5
5
  SHA512:
6
- metadata.gz: c4d3910367c067e84aaae374d27a39de44acaf1eba461150f84e019f8e9e0f6fb0ff2341fe44101e4cfb3c7c3ac0b7a23495d8d285adf7ad53af96bccc51285e
7
- data.tar.gz: f17c36621d5afce588b5bcf95eea17cea461abb0d5c2dfc6caac8b8905c5e9fe71827f33e469dc3616ca40f188f030544776393c921af7e6fa40f0d9339af658
6
+ metadata.gz: b2589f13688bd95ad3ad176d9dccc2412a0df9a3484a173129e3dc66e7f78dad67ea1f7920f7bee9136f412b5c6221a7776b2f7a682ef1e0fdb55d2ffd1cd307
7
+ data.tar.gz: 73bc97b32dcaa44f0a9181e63d450770535e12f73254fcea500551a46bb788a0dfe2bf9ed7de897a84ab1c22f0a19545d050e2b7a1c8cd76c7d3515a55b60d0d
data/CHANGELOG.md ADDED
@@ -0,0 +1,79 @@
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
+ ---
9
+
10
+ ## [1.1.0] - 2026-05-13
11
+
12
+ ### Added
13
+
14
+ - `enforce_readonly` class attribute on the `StaticRecord` concern (default: `true`).
15
+ Subclasses can opt out of the read-only guard without stubbing:
16
+ ```ruby
17
+ class Locations::Barangay < Pilipinas::Db::Barangay
18
+ self.enforce_readonly = false
19
+ end
20
+ ```
21
+ - `lib/pilipinas/testing/rspec.rb` — a ready-made RSpec helper that disables
22
+ the read-only guard on all four DB models for the entire test suite.
23
+ Require it once in `rails_helper.rb`:
24
+ ```ruby
25
+ require 'pilipinas/testing/rspec'
26
+ ```
27
+ - Spec coverage for `StaticRecord` — 8 examples covering default behaviour,
28
+ `enforce_readonly = false`, subclass inheritance, and class-level isolation.
29
+
30
+ ### Changed
31
+
32
+ - `readonly?` now gates on `self.class.enforce_readonly && !new_record?` instead
33
+ of unconditionally returning `!new_record?`.
34
+
35
+ ---
36
+
37
+ ## [1.0.0] - 2026-05-12
38
+
39
+ Complete rewrite of the gem. Zero runtime dependencies.
40
+
41
+ ### Added
42
+
43
+ - In-memory layer with thread-safe `Pilipinas::Cache` (Mutex + double-checked
44
+ locking) and O(1) look-ups via separate code/name hash indices.
45
+ - Immutable value objects — every entity instance is frozen.
46
+ - `Pilipinas::Region`, `Province`, `City`, `Barangay` with `.all`, `.count`,
47
+ `.first`, `.last`, `.find_by`, `.find_by_code`, `.find_by_name`.
48
+ - Hierarchy traversal: `region.provinces`, `province.cities`, `city.barangays`.
49
+ - Optional ActiveRecord layer (`Pilipinas::Db::*`) with memory-efficient scopes
50
+ (`.lite`, `.by_code`, `.by_name`, `.find_lite_by_code`, `.find_lite_by_name`).
51
+ - `StaticRecord` concern: disables STI, adds lean SELECT scopes, enforces
52
+ read-only on persisted records.
53
+ - Migration generator (`rails generate pilipinas:migration`) and
54
+ `rake pilipinas:load` seeding task.
55
+ - Full RSpec suite (57 examples).
56
+ - GitHub Actions CI pipeline.
57
+
58
+ ### Changed
59
+
60
+ - Requires Ruby ≥ 3.4 (developed against Ruby 4.0).
61
+ - Removed all runtime gem dependencies (previously depended on `yaml_db` and
62
+ others).
63
+
64
+ ---
65
+
66
+ ## [0.0.1] - 2019-01-26
67
+
68
+ ### Added
69
+
70
+ - Initial release.
71
+ - YAML data for Philippine regions, provinces, cities, and barangays.
72
+ - ActiveRecord models backed by `pilipinas_*` tables.
73
+ - Migration template and Rake loader task.
74
+ - Rails generator for migrations.
75
+ - Railtie for automatic Rake task loading in Rails apps.
76
+
77
+ [1.1.0]: https://github.com/denmarkmeralpis/pilipinas/compare/v1.0.0...v1.1.0
78
+ [1.0.0]: https://github.com/denmarkmeralpis/pilipinas/compare/v0.0.1...v1.0.0
79
+ [0.0.1]: https://github.com/denmarkmeralpis/pilipinas/releases/tag/v0.0.1
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pilipinas (1.0.0)
4
+ pilipinas (1.1.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -76,7 +76,7 @@ GEM
76
76
  prism (>= 1.3.0)
77
77
  rdoc (>= 4.0.0)
78
78
  reline (>= 0.4.2)
79
- json (2.19.5)
79
+ json (2.19.8)
80
80
  language_server-protocol (3.17.0.5)
81
81
  lint_roller (1.1.0)
82
82
  logger (1.7.0)
@@ -110,7 +110,7 @@ GEM
110
110
  prettyprint
111
111
  prettyprint (0.2.0)
112
112
  prism (1.9.0)
113
- psych (5.3.1)
113
+ psych (5.4.0)
114
114
  date
115
115
  stringio
116
116
  public_suffix (7.0.5)
@@ -162,7 +162,7 @@ GEM
162
162
  diff-lcs (>= 1.2.0, < 2.0)
163
163
  rspec-support (~> 3.13.0)
164
164
  rspec-support (3.13.7)
165
- rubocop (1.86.1)
165
+ rubocop (1.87.0)
166
166
  json (~> 2.3)
167
167
  language_server-protocol (~> 3.17.0.2)
168
168
  lint_roller (~> 1.1.0)
@@ -179,9 +179,10 @@ GEM
179
179
  rubocop-rake (0.7.1)
180
180
  lint_roller (~> 1.1)
181
181
  rubocop (>= 1.72.1)
182
- rubocop-rspec (3.9.0)
182
+ rubocop-rspec (3.10.2)
183
183
  lint_roller (~> 1.1)
184
- rubocop (~> 1.81)
184
+ regexp_parser (>= 2.0)
185
+ rubocop (~> 1.86, >= 1.86.2)
185
186
  ruby-progressbar (1.13.0)
186
187
  securerandom (0.4.1)
187
188
  shoulda-matchers (6.5.0)
@@ -196,14 +197,14 @@ GEM
196
197
  terminal-table
197
198
  simplecov-html (0.13.2)
198
199
  simplecov_json_formatter (0.1.4)
199
- sqlite3 (2.9.4-aarch64-linux-gnu)
200
- sqlite3 (2.9.4-aarch64-linux-musl)
201
- sqlite3 (2.9.4-arm-linux-gnu)
202
- sqlite3 (2.9.4-arm-linux-musl)
203
- sqlite3 (2.9.4-arm64-darwin)
204
- sqlite3 (2.9.4-x86_64-darwin)
205
- sqlite3 (2.9.4-x86_64-linux-gnu)
206
- sqlite3 (2.9.4-x86_64-linux-musl)
200
+ sqlite3 (2.9.5-aarch64-linux-gnu)
201
+ sqlite3 (2.9.5-aarch64-linux-musl)
202
+ sqlite3 (2.9.5-arm-linux-gnu)
203
+ sqlite3 (2.9.5-arm-linux-musl)
204
+ sqlite3 (2.9.5-arm64-darwin)
205
+ sqlite3 (2.9.5-x86_64-darwin)
206
+ sqlite3 (2.9.5-x86_64-linux-gnu)
207
+ sqlite3 (2.9.5-x86_64-linux-musl)
207
208
  stringio (3.2.0)
208
209
  terminal-table (4.0.0)
209
210
  unicode-display_width (>= 1.1.1, < 4)
@@ -221,7 +222,7 @@ GEM
221
222
  addressable (>= 2.8.0)
222
223
  crack (>= 0.3.2)
223
224
  hashdiff (>= 0.4.0, < 2.0.0)
224
- zeitwerk (2.7.5)
225
+ zeitwerk (2.8.2)
225
226
 
226
227
  PLATFORMS
227
228
  aarch64-linux-gnu
@@ -279,7 +280,7 @@ CHECKSUMS
279
280
  i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5
280
281
  io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
281
282
  irb (1.18.0) sha256=de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3
282
- json (2.19.5) sha256=218a18553e4801d579ca7e0f5bc72bafd776d7397238a1fb4e74db5b0a812c59
283
+ json (2.19.8) sha256=6354310fd76ef69b87d5bd1f38b40d730613baf90b6803d2d0a48f618d32dfaa
283
284
  language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
284
285
  lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
285
286
  logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
@@ -295,11 +296,11 @@ CHECKSUMS
295
296
  nokogiri (1.19.3-x86_64-linux-musl) sha256=248c906d2166eca5efb56d52fdee5f9a1f51d69a72e2b64fdac647b4ce39ea3f
296
297
  parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356
297
298
  parser (3.3.11.1) sha256=d17ace7aabe3e72c3cc94043714be27cc6f852f104d81aa284c2281aecc65d54
298
- pilipinas (1.0.0)
299
+ pilipinas (1.1.1)
299
300
  pp (0.6.3) sha256=2951d514450b93ccfeb1df7d021cae0da16e0a7f95ee1e2273719669d0ab9df6
300
301
  prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
301
302
  prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
302
- psych (5.3.1) sha256=eb7a57cef10c9d70173ff74e739d843ac3b2c019a003de48447b2963d81b1974
303
+ psych (5.4.0) sha256=14f72d69a611af663d7d70e4a7b67d9eb1f3ae9f8d916b478961d5a0075ba5b7
303
304
  public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
304
305
  racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
305
306
  rack (3.2.6) sha256=5ed78e1f73b2e25679bec7d45ee2d4483cc4146eb1be0264fc4d94cb5ef212c2
@@ -320,10 +321,10 @@ CHECKSUMS
320
321
  rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
321
322
  rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
322
323
  rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
323
- rubocop (1.86.1) sha256=44415f3f01d01a21e01132248d2fd0867572475b566ca188a0a42133a08d4531
324
+ rubocop (1.87.0) sha256=b9d9ddf55116a513f8ef2c7ae660662d8b49301f118d3f0df61865b33a5c188d
324
325
  rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035
325
326
  rubocop-rake (0.7.1) sha256=3797f2b6810c3e9df7376c26d5f44f3475eda59eb1adc38e6f62ecf027cbae4d
326
- rubocop-rspec (3.9.0) sha256=8fa70a3619408237d789aeecfb9beef40576acc855173e60939d63332fdb55e2
327
+ rubocop-rspec (3.10.2) sha256=0b3e2ecc592cd10ecbf0095bb58d1e357905276e069643523cc19eb7495f65e2
327
328
  ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
328
329
  securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
329
330
  shoulda-matchers (6.5.0) sha256=ef6b572b2bed1ac4aba6ab2c5ff345a24b6d055a93a3d1c3bfc86d9d499e3f44
@@ -331,14 +332,14 @@ CHECKSUMS
331
332
  simplecov-console (0.9.5) sha256=b1108bcfff5f210143e2b8301698c367b01586f20d25a73e95475a5df6fc6ff6
332
333
  simplecov-html (0.13.2) sha256=bd0b8e54e7c2d7685927e8d6286466359b6f16b18cb0df47b508e8d73c777246
333
334
  simplecov_json_formatter (0.1.4) sha256=529418fbe8de1713ac2b2d612aa3daa56d316975d307244399fa4838c601b428
334
- sqlite3 (2.9.4-aarch64-linux-gnu) sha256=ecabed721e6eaad54601d2685f09029d90025efc8d931040dc89cb3f8a2080ec
335
- sqlite3 (2.9.4-aarch64-linux-musl) sha256=ffb4255947fb54c8c3eeca97460c9702b40de91ce390455ef7367ca6a3929a31
336
- sqlite3 (2.9.4-arm-linux-gnu) sha256=9ee2008b9fbec984c3c165b0d7eedd2bd2a415100b761bfa3a4c6fbec9208bf6
337
- sqlite3 (2.9.4-arm-linux-musl) sha256=8dc1fe4da6977992cd62decf4a93ccf6cc2e124a5e6a340160d52092f70e837a
338
- sqlite3 (2.9.4-arm64-darwin) sha256=1d5aad413a815d236e96d43f05a1acc600b6cd086800770342a3f9c2877499ff
339
- sqlite3 (2.9.4-x86_64-darwin) sha256=f280c476e360b73e86165a5e59b72801385b4a6c3a47f8af5ecefb9d90bec17f
340
- sqlite3 (2.9.4-x86_64-linux-gnu) sha256=537a3eda71b1df1336d0055cbebe55a7317c34870c192c7b6b9d8d0be6871847
341
- sqlite3 (2.9.4-x86_64-linux-musl) sha256=3fc5e865b4be9a85d998203ef8d0c0fdcb92f20acf34a254346ff8a19088efec
335
+ sqlite3 (2.9.5-aarch64-linux-gnu) sha256=78075b6337d3d182c6d2b4691049ed45cd220826160c9ea18946bf6a1de200dc
336
+ sqlite3 (2.9.5-aarch64-linux-musl) sha256=18c801185deb4adc01ddb281e8f672a39e3d1729979ca91e39439cd3eac0402d
337
+ sqlite3 (2.9.5-arm-linux-gnu) sha256=1bdfca0c7d63998c60b0f4a8e3c8df2d33800ccc4abd2d612eddbbbc92a4c48b
338
+ sqlite3 (2.9.5-arm-linux-musl) sha256=bae1109d12b2e9f588455967729b008e1ff4feb7761749df695019c9079913c6
339
+ sqlite3 (2.9.5-arm64-darwin) sha256=d0cf444a70fc9395d513cfbcc1e6719e224aa645314e3824cb0474c721425aa2
340
+ sqlite3 (2.9.5-x86_64-darwin) sha256=8e9caae38bd7ebb29cbeee3e7ab1d12dc2327d9a1b92c7fcf0dda05589627a81
341
+ sqlite3 (2.9.5-x86_64-linux-gnu) sha256=233dbcb6714148dd23bc5aeb33e8efd6eac974969564ddd5794c23d5f52b231e
342
+ sqlite3 (2.9.5-x86_64-linux-musl) sha256=e7d3a7474e8af0f96150c21abc203fbab5437206bfcdf11deab7741c0ca516f2
342
343
  stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1
343
344
  terminal-table (4.0.0) sha256=f504793203f8251b2ea7c7068333053f0beeea26093ec9962e62ea79f94301d2
344
345
  thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
@@ -350,7 +351,7 @@ CHECKSUMS
350
351
  uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
351
352
  useragent (0.16.11) sha256=700e6413ad4bb954bb63547fa098dddf7b0ebe75b40cc6f93b8d54255b173844
352
353
  webmock (3.26.2) sha256=774556f2ea6371846cca68c01769b2eac0d134492d21f6d0ab5dd643965a4c90
353
- zeitwerk (2.7.5) sha256=d8da92128c09ea6ec62c949011b00ed4a20242b255293dd66bf41545398f73dd
354
+ zeitwerk (2.8.2) sha256=7212a61311083c604184b1ea2574b9aa05cd14f855a0841c06985cabe9181d12
354
355
 
355
356
  BUNDLED WITH
356
357
  4.0.6
data/README.md CHANGED
@@ -18,6 +18,8 @@ Region → Province → City / Municipality → Barangay
18
18
  - **O(1) look-ups** — separate hash indices keyed by code and by name; no linear scans.
19
19
  - **Immutable value objects** — every entity instance is frozen. Safe to share across threads and fibers without copying.
20
20
  - **Optional ActiveRecord integration** — a migration generator and Rake task seed the four `pilipinas_*` tables from the bundled YAML data.
21
+ - **Read-only by default** — persisted AR model instances raise `ActiveRecord::ReadOnlyRecord` on accidental writes; opt out per-class via `enforce_readonly = false`.
22
+ - **Test helper included** — `require 'pilipinas/testing/rspec'` disables the read-only guard for the entire RSpec suite with one line.
21
23
  - **Ruby ≥ 3.4** required; developed against Ruby 4.0.
22
24
 
23
25
  ---
@@ -172,6 +174,20 @@ This creates four tables: `pilipinas_regions`, `pilipinas_provinces`, `pilipinas
172
174
  rake pilipinas:load
173
175
  ```
174
176
 
177
+ > **Upgrading from an older version?**
178
+ >
179
+ > If `rake pilipinas:load` raises
180
+ > `Pilipinas::Error: pilipinas:load requires a unique index on the code column`,
181
+ > your database was created with an earlier migration that did not include those
182
+ > indexes. Add them with the dedicated generator:
183
+ >
184
+ > ```sh
185
+ > rails generate pilipinas:code_indexes
186
+ > rails db:migrate
187
+ > ```
188
+ >
189
+ > Then re-run `rake pilipinas:load`.
190
+
175
191
  ### 3. Use the AR models
176
192
 
177
193
  ```ruby
@@ -184,6 +200,48 @@ province.cities # has_many association
184
200
 
185
201
  > **Note:** The AR models are auto-loaded and require `activerecord` to be available.
186
202
 
203
+ ### Read-only behaviour
204
+
205
+ All four `Pilipinas::Db::*` models are **read-only by default**. Any attempt to call `update!`, `save`, or `destroy` on a persisted record raises `ActiveRecord::ReadOnlyRecord`. This is intentional — the pilipinas tables are static reference data that should never be mutated after seeding.
206
+
207
+ New (unsaved) records are always writable, so `create!` works normally in the Loader and in test factories.
208
+
209
+ #### Opting a subclass out of read-only enforcement
210
+
211
+ If your application inherits from a Pilipinas DB model and legitimately needs write access, set `enforce_readonly` to `false` on the subclass:
212
+
213
+ ```ruby
214
+ class Locations::Barangay < Pilipinas::Db::Barangay
215
+ self.enforce_readonly = false
216
+ end
217
+ ```
218
+
219
+ This does not affect the parent class or any other model.
220
+
221
+ ---
222
+
223
+ ## Testing
224
+
225
+ The gem ships a ready-made RSpec helper that turns off the read-only guard for the entire test suite — no stubbing required.
226
+
227
+ ```ruby
228
+ # spec/rails_helper.rb (or spec/support/pilipinas.rb)
229
+ require 'pilipinas/testing/rspec'
230
+ ```
231
+
232
+ This sets `enforce_readonly = false` on all four models (`Region`, `Province`, `City`, `Barangay`) inside a `before(:suite)` hook, so FactoryBot factories, fixtures, and any spec that writes to pilipinas tables work without extra setup.
233
+
234
+ If you only need writable records in a specific context:
235
+
236
+ ```ruby
237
+ around do |example|
238
+ Locations::Barangay.enforce_readonly = false
239
+ example.run
240
+ ensure
241
+ Locations::Barangay.enforce_readonly = true
242
+ end
243
+ ```
244
+
187
245
  ---
188
246
 
189
247
  ## Advanced
@@ -238,54 +296,8 @@ The gem is available as open source under the terms of the [MIT License](LICENSE
238
296
 
239
297
  ## Acknowledgements
240
298
 
241
- The geographic data used in this gem is derived from the Philippine Standard Geographic Code (PSGC) published by the Philippine Statistics Authority.
242
-
243
-
244
- ## Installation
245
-
246
- Add this line to your application's Gemfile:
247
-
248
- ```ruby
249
- gem 'pilipinas'
250
- ```
251
-
252
- And then execute:
253
-
254
- $ bundle
255
-
256
- Or install it yourself as:
257
-
258
- $ gem install pilipinas
259
-
260
- ## Usage
261
-
262
- ```ruby
263
- # All Regions
264
- Pilipinas::Region.all
265
-
266
- # All Provinces
267
- Pilipinas::Province.all
268
-
269
- # All Cities/Municipalities
270
- Pilipinas::City.all
271
-
272
- # All Barangays
273
- Pilipinas::Barangay.all
274
-
275
- # Finding record thru find_by_(code/name) method
276
- region = Pilipinas::Region.find_by_name("REGION V (Bicol Region)")
277
-
278
- # Get provinces by region
279
- region.provinces
280
- ```
281
- ## Acknowledgement
282
-
283
299
  The data used in this gem is from `gem pinas`. Kudos!
284
300
 
285
- ## TODO
286
-
287
- * Add a form helper
288
-
289
301
  ## Development
290
302
 
291
303
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators/base'
4
+ require 'rails/generators/active_record'
5
+
6
+ module Pilipinas
7
+ # Rails generator that adds unique indexes on the +code+ column to all four
8
+ # pilipinas_* tables.
9
+ #
10
+ # Run this if your database was created with an older version of the gem
11
+ # that did not include these indexes, and +rake pilipinas:load+ raises:
12
+ #
13
+ # ArgumentError: No unique index found for code
14
+ #
15
+ # @example
16
+ # rails generate pilipinas:code_indexes
17
+ # rails db:migrate
18
+ #
19
+ class CodeIndexesGenerator < Rails::Generators::Base
20
+ include Rails::Generators::Migration
21
+
22
+ source_root File.expand_path('..', __dir__)
23
+
24
+ def generate_migration
25
+ migration_template 'templates/add_pilipinas_code_indexes.rb',
26
+ 'db/migrate/add_pilipinas_code_indexes.rb'
27
+ end
28
+
29
+ # @param _dir [String] unused (required by the interface)
30
+ # @return [String]
31
+ def self.next_migration_number(_dir)
32
+ Time.now.utc.strftime('%Y%m%d%H%M%S')
33
+ end
34
+
35
+ private
36
+
37
+ # @return [String] migration version bracket, e.g. "[8.0]"
38
+ def migration_version
39
+ "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,8 @@
1
+ class AddPilipinasCodeIndexes < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ add_index :pilipinas_regions, :code, unique: true, if_not_exists: true
4
+ add_index :pilipinas_provinces, :code, unique: true, if_not_exists: true
5
+ add_index :pilipinas_cities, :code, unique: true, if_not_exists: true
6
+ add_index :pilipinas_barangays, :code, unique: true, if_not_exists: true
7
+ end
8
+ end
@@ -25,10 +25,14 @@ module Pilipinas
25
25
  #
26
26
  # == Optimizations applied
27
27
  #
28
- # * +readonly?+ returns +true+ for persisted records only — accidental
28
+ # * +readonly?+ returns +true+ for persisted records when
29
+ # +enforce_readonly+ is +true+ (the default) — accidental
29
30
  # +update!/save!/destroy+ on fetched rows raise
30
31
  # +ActiveRecord::ReadOnlyRecord+; new records stay writable so the
31
32
  # Loader and test fixtures can still call +create!+.
33
+ # * +enforce_readonly+ class attribute: opt a subclass out of the
34
+ # read-only guard without touching production models — useful in test
35
+ # environments where factories need to write to pilipinas tables.
32
36
  # * +self.inheritance_column = :_sti_disabled+: removes STI type-column
33
37
  # look-up from every query.
34
38
  # * +.lite+ scope: selects only +id+, +location_id+, +code+, +name+ —
@@ -50,6 +54,11 @@ module Pilipinas
50
54
  # province.cities # SELECT id, location_id, parent_id, code, name …
51
55
  # city.barangays # SELECT id, location_id, parent_id, code, name …
52
56
  #
57
+ # @example Opting a subclass out of read-only enforcement (e.g. in tests)
58
+ # class Locations::Barangay < Pilipinas::Db::Barangay
59
+ # self.enforce_readonly = false
60
+ # end
61
+ #
53
62
  module StaticRecord
54
63
  extend ActiveSupport::Concern
55
64
 
@@ -64,6 +73,16 @@ module Pilipinas
64
73
  # Removes the hidden "type" column check AR performs on every query.
65
74
  self.inheritance_column = :_sti_disabled
66
75
 
76
+ # ── Read-only enforcement ─────────────────────────────────────────
77
+ # Defaults to +true+. Set to +false+ on a subclass (e.g. in a test
78
+ # environment or for a model that legitimately needs write access)
79
+ # without touching production behaviour:
80
+ #
81
+ # class Locations::Barangay < Pilipinas::Db::Barangay
82
+ # self.enforce_readonly = false
83
+ # end
84
+ class_attribute :enforce_readonly, instance_writer: false, default: true
85
+
67
86
  # ── Memory-efficient query scopes ─────────────────────────────────
68
87
 
69
88
  # Select only the four columns needed for display and look-up.
@@ -97,19 +116,25 @@ module Pilipinas
97
116
  scope :find_lite_by_name, ->(name) { lite.by_name(name).first }
98
117
  end
99
118
 
100
- # Persisted instances loaded from a pilipinas_* table are permanently
101
- # read-only: +update!+, +save+, and +destroy+ all raise
102
- # +ActiveRecord::ReadOnlyRecord+. New (unsaved) objects are writable
103
- # so that the Loader can still call +create!+ on the fallback path and
104
- # so that specs can build fixtures with +create!+.
119
+ # Guards against accidental writes to static reference data.
120
+ #
121
+ # Returns +true+ (making the record read-only) when both conditions hold:
122
+ # 1. The record is persisted (+new_record?+ is +false+).
123
+ # 2. The class-level +enforce_readonly+ flag is +true+ (the default).
124
+ #
125
+ # New (unsaved) objects are always writable so the Loader and test
126
+ # factories can call +create!+. To allow writes on a subclass — for
127
+ # example in a test environment — set:
128
+ #
129
+ # self.enforce_readonly = false
105
130
  #
106
131
  # Overriding +readonly?+ is more efficient than an +after_initialize+
107
- # callback because it is only called when AR is about to perform a
108
- # write operation it adds zero per-record overhead on reads.
132
+ # callback: it is only invoked when AR is about to perform a write
133
+ # operation, so it adds zero overhead on read paths.
109
134
  #
110
- # @return [Boolean] +true+ for persisted records, +false+ for new ones
135
+ # @return [Boolean]
111
136
  def readonly?
112
- !new_record?
137
+ self.class.enforce_readonly && !new_record?
113
138
  end
114
139
  end
115
140
  end
@@ -73,7 +73,17 @@ module Pilipinas
73
73
  # @return [void]
74
74
  def bulk_insert(model, batch)
75
75
  if model.respond_to?(:upsert_all)
76
- model.upsert_all(batch, unique_by: :code)
76
+ begin
77
+ model.upsert_all(batch, unique_by: :code)
78
+ rescue ArgumentError
79
+ raise Pilipinas::Error,
80
+ 'pilipinas:load requires a unique index on the `code` column, ' \
81
+ "which is missing from #{model.table_name}. " \
82
+ 'Your database was likely created with an older version of the gem. ' \
83
+ "Run the following to add the missing indexes and retry:\n\n " \
84
+ "rails generate pilipinas:code_indexes\n " \
85
+ "rails db:migrate\n"
86
+ end
77
87
  elsif model.respond_to?(:insert_all)
78
88
  model.insert_all(batch)
79
89
  else
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pilipinas'
4
+
5
+ # Pilipinas::Testing::RSpec
6
+ #
7
+ # Optional RSpec helper that disables the read-only guard on all Pilipinas DB
8
+ # models for the duration of the test suite. Require this file once (e.g. in
9
+ # +rails_helper.rb+ or +spec/support/pilipinas.rb+) and FactoryBot factories,
10
+ # fixtures, or any spec that needs to write to pilipinas_* tables will work
11
+ # without stubbing or subclass overrides.
12
+ #
13
+ # == Usage
14
+ #
15
+ # # spec/rails_helper.rb (or spec/support/pilipinas.rb)
16
+ # require 'pilipinas/testing/rspec'
17
+ #
18
+ # == What it does
19
+ #
20
+ # Calls +enforce_readonly = false+ on every Pilipinas DB model inside a
21
+ # +before(:suite)+ hook so the flag is set once, before any example runs.
22
+ # The models remain writable for the entire test process, which is the correct
23
+ # behaviour for a test environment where factories seed the pilipinas_* tables.
24
+ #
25
+ # If you need write access only in a specific context, set the flag manually
26
+ # with an +around+ hook instead of requiring this file globally.
27
+ #
28
+ # == Models affected
29
+ #
30
+ # * Pilipinas::Db::Region
31
+ # * Pilipinas::Db::Province
32
+ # * Pilipinas::Db::City
33
+ # * Pilipinas::Db::Barangay
34
+
35
+ require 'rspec/core'
36
+
37
+ RSpec.configure do |config|
38
+ config.before(:suite) do
39
+ [
40
+ Pilipinas::Db::Region,
41
+ Pilipinas::Db::Province,
42
+ Pilipinas::Db::City,
43
+ Pilipinas::Db::Barangay
44
+ ].each { |model| model.enforce_readonly = false }
45
+ end
46
+ end
@@ -4,5 +4,5 @@ module Pilipinas
4
4
  # Semantic version of the gem.
5
5
  #
6
6
  # Follows {https://semver.org Semantic Versioning}: MAJOR.MINOR.PATCH.
7
- VERSION = '1.0.0'
7
+ VERSION = '1.1.1'
8
8
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pilipinas
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nujian Den Mark Meralpis
@@ -143,6 +143,7 @@ executables: []
143
143
  extensions: []
144
144
  extra_rdoc_files: []
145
145
  files:
146
+ - CHANGELOG.md
146
147
  - CODE_OF_CONDUCT.md
147
148
  - Gemfile
148
149
  - Gemfile.lock
@@ -1908,7 +1909,9 @@ files:
1908
1909
  - lib/data/regions/41165.yml
1909
1910
  - lib/data/regions/6398.yml
1910
1911
  - lib/data/regions/8808.yml
1912
+ - lib/generators/pilipinas/code_indexes_generator.rb
1911
1913
  - lib/generators/pilipinas/migration_generator.rb
1914
+ - lib/generators/templates/add_pilipinas_code_indexes.rb
1912
1915
  - lib/generators/templates/migration.rb
1913
1916
  - lib/pilipinas.rb
1914
1917
  - lib/pilipinas/barangay.rb
@@ -1924,6 +1927,7 @@ files:
1924
1927
  - lib/pilipinas/province.rb
1925
1928
  - lib/pilipinas/railtie.rb
1926
1929
  - lib/pilipinas/region.rb
1930
+ - lib/pilipinas/testing/rspec.rb
1927
1931
  - lib/pilipinas/version.rb
1928
1932
  - lib/tasks/pilipinas_tasks.rake
1929
1933
  - pilipinas.gemspec