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 +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +30 -11
- data/lib/ar_minimizer/loader.rb +8 -1
- data/lib/ar_minimizer/railtie.rb +1 -0
- data/lib/ar_minimizer/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7993bf021f8de2ed44f37b3dd541d6644b1fd1d38817acaecf424852713acb2a
|
|
4
|
+
data.tar.gz: 0c520dcb7ca0543b4ac891ea9abced9968d0fec36edd258c9a47dc7ea33f962d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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
|
-
##
|
|
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
|
|
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
|
-
|
|
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`).
|
data/lib/ar_minimizer/loader.rb
CHANGED
|
@@ -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))
|
data/lib/ar_minimizer/railtie.rb
CHANGED
|
@@ -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
|
data/lib/ar_minimizer/version.rb
CHANGED