activerecord-minimizer 0.1.3 → 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: fd5b6b67995d6c1cbf665637d1c06d7bba3a481906a2dfab3478c1f7e9e303b0
4
- data.tar.gz: f9a99422b743b0942a7e07d8de0fb43273f4ec2fdc9e9f1e34c0021e196a6aee
3
+ metadata.gz: 7993bf021f8de2ed44f37b3dd541d6644b1fd1d38817acaecf424852713acb2a
4
+ data.tar.gz: 0c520dcb7ca0543b4ac891ea9abced9968d0fec36edd258c9a47dc7ea33f962d
5
5
  SHA512:
6
- metadata.gz: 1bc117e578665279e838e5ce2ea05d915c271a435224b21824e4ce51af8ac01a53248c2c7c3532f9fca3c46c2927927ff81d5b65a1db5dc93aa4dd267836fbb2
7
- data.tar.gz: 8895e58f378594e9b92df76e98c72d6d377f6b6c2e1f9a77441677efc1ecdef23a15d2640cac6584940c0d7c5128ac8f563d225bbd471d5d1e5f8d58aa6652c2
6
+ metadata.gz: faa673f2b296a034bbca5c927642dcde2f3f38c6e689b825c8f6884fbe0810a7ec0514f3669a685fa6f81487b578d4a41345f38de5ca3f327851c0d195abd1c2
7
+ data.tar.gz: '09b436a70b52965449931ebc407b9396ca7068233644025de33516f56148f0c31507cd5f265a4a3884cec901482fe9c1dc3ee02e9b42e28accd41f077b332d73'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0 (2026-10-20)
4
+
5
+ - With `load_time`, the manifest's index (10-20 MB on a large app) is let go once an eager-loading boot is done, before a preloading server forks; a file loaded later indexes the manifest again.
6
+ - README: strict mode and enums are the core; compiling call sites (`load_time`, `views`, `ar_minimizer:build`, `guards`) and small records are experimental, with the production results. The manifest is only needed by the experimental parts.
7
+
3
8
  ## 0.1.3 (2026-10-18)
4
9
 
5
10
  - `ar_minimizer:manifest` stops, saying why, when no model's table exists (a database not set up yet), instead of writing a manifest that describes no models.
data/README.md CHANGED
@@ -4,8 +4,7 @@ Active Record generates a reader, a writer and one method per attribute
4
4
  method pattern for every column of every model: `name?`, `name_changed?`,
5
5
  `name_was`, `saved_change_to_name?`, `name_before_type_cast`,
6
6
  `restore_name!`, ... 21 patterns in Rails 8.1, so 21 methods per column.
7
- activerecord-minimizer generates the readers and writers only, and compiles
8
- the call sites that used the rest into the calls they stood for. Part of
7
+ activerecord-minimizer generates the readers and writers only. Part of
9
8
  [rails-minify](../rails-minify).
10
9
 
11
10
  ```ruby
@@ -57,7 +56,21 @@ first call to one generates it on that model.
57
56
  Turn `slim` on in development and tests as well as production: the
58
57
  errors then show up while you work and in CI. Slim follows code reloads.
59
58
 
60
- ## Compiling call sites
59
+ ## Enums
60
+
61
+ `config.ar_minimizer.enums = true` replaces the closures Rails defines for
62
+ enum predicates and bang methods, which build and intern the
63
+ `<name>_for_database` symbol on every call, with ordinary methods carrying
64
+ the enum's values: `def published? = public_send(:status_for_database) == 1`.
65
+ 1.3–1.6× faster predicates; non-finite float values keep Rails' closures.
66
+
67
+ ## Experimental
68
+
69
+ Each is off unless set. They are faster per call in benchmarks, but made no
70
+ measurable difference on a production app (see [Numbers](#numbers)), and the
71
+ compiling parts cost memory in every worker.
72
+
73
+ ### Compiling call sites
61
74
 
62
75
  Calls whose receiver is known to be a record of a model with that column are
63
76
  compiled to what the generated method would have done:
@@ -92,7 +105,7 @@ Where it compiles:
92
105
  `app/` and `lib/` in place instead, like explode_sqlite. Production only (or
93
106
  `FORCE=1`); `DRY_RUN=1` previews.
94
107
 
95
- All three read the **manifest**, `config/ar_minimizer.json`, written by
108
+ All three read the **manifest** (strict mode and enums don't need one), `config/ar_minimizer.json`, written by
96
109
  `bin/rails ar_minimizer:manifest`: per model its columns and their query kind,
97
110
  the attribute method patterns, hand-written methods, superclass, app modules,
98
111
  associations, scopes, and names it defines another way (an enum predicate
@@ -103,19 +116,14 @@ stale, and an eager-loading boot logs an error if it is.
103
116
  Never compiled: methods any model defines by hand, names used as columns,
104
117
  primary key methods, `defined?(...)`, calls with blocks, heredoc arguments.
105
118
 
106
- ## More
119
+ ### More
107
120
 
108
- * **Enums** (`config.ar_minimizer.enums = true`) replaces the closures Rails
109
- defines for enum predicates and bang methods, which build and intern the
110
- `<name>_for_database` symbol on every call, with ordinary methods carrying
111
- the enum's values: `def published? = public_send(:status_for_database) == 1`.
112
- 1.3–1.6× faster predicates; non-finite float values keep Rails' closures.
113
121
  * **Small records** (`config.ar_minimizer.small_records = true`). A loaded
114
122
  record owns two Hashes most records never put anything in, 160 bytes each:
115
123
  the association cache and the attribute set's Attribute objects. Both start
116
124
  as one shared frozen empty Hash and get their own on the first insert. A
117
125
  read-only record goes from 1,480 to 1,160 bytes (23 columns), at the same
118
- speed. Destroyed and frozen records still raise on writes.
126
+ speed. Only worth it on pages that load thousands of records. Destroyed and frozen records still raise on writes.
119
127
  * **Census** (`ArMinimizer::Census`, run by rails-minify's
120
128
  `bin/rails minify:census`): how many call sites compile, and why the others
121
129
  don't.
@@ -140,6 +148,17 @@ On [Japan Voyage](https://japanvoyage.jp) (191 models, 2009 columns):
140
148
  * Test suite (4317 runs) in strict mode, after rubocop-minify's rewrites and
141
149
  with a 41-line keep list: the same results as the untouched app.
142
150
 
151
+ In production there (Falcon, 6 workers, half the workers with a part and half
152
+ without, the same traffic for an hour, October 2026):
153
+
154
+ | part | result |
155
+ |---|---|
156
+ | strict mode | no call to a removed method; the same latency and memory |
157
+ | enums | the same median latency; no change in allocations on these pages |
158
+ | small records | no difference: tens of records per request |
159
+ | compiling in views | no faster, ~22 MB more per worker |
160
+ | compiling at load time | ~12% more CPU per request, not reproduced locally; measured before the app was preloaded, so worth measuring again |
161
+
143
162
  ## Requirements and caveats
144
163
 
145
164
  * Ruby 3.4+, Rails 8.0, 8.1 and main (`rake test:rails`).
@@ -15,7 +15,7 @@ module ArMinimizer
15
15
  # version: changes when the index does (Manifest.digest of the file).
16
16
  def install!(index:, patterns:, exclude_patterns: nil, version: nil, guards: false)
17
17
  require "require-hooks/setup"
18
- @index = index
18
+ @index = @index_source = index
19
19
  @guards = guards
20
20
  @mutex = Mutex.new
21
21
  if defined?(RequireHooks::Bootsnap) && RequireHooks::Bootsnap.respond_to?(:add_version_hash)
@@ -33,6 +33,13 @@ module ArMinimizer
33
33
  @index
34
34
  end
35
35
 
36
+ # Lets go of the index (10-20 MB on a large app) once app/ and lib/ are
37
+ # loaded, before a preloading server forks: a file loaded later indexes
38
+ # the manifest again.
39
+ def release_index!
40
+ @mutex.synchronize { @index = @index_source } if @index_source.respond_to?(:call)
41
+ end
42
+
36
43
  # nil leaves the file as it is.
37
44
  def transform(path, source)
38
45
  result = Rewriter.new(index: index, path: path, marker: false, guards: @guards).rewrite(source || File.read(path))
@@ -44,6 +44,7 @@ module ArMinimizer
44
44
  # File.fnmatch? without FNM_PATHNAME: `*` crosses directories.
45
45
  ArMinimizer::Loader.install!(index: manifest, patterns: roots.map { "#{it}/*.rb" }, guards: !!options.guards,
46
46
  version: ArMinimizer::Manifest.digest(manifest_json))
47
+ app.config.after_initialize { ArMinimizer::Loader.release_index! if app.config.eager_load }
47
48
  end
48
49
 
49
50
  # Every model is loaded after eager loading: say so if the manifest no
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ArMinimizer
4
- VERSION = "0.1.3"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-minimizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sampo Kuokkanen