activesupport-minimizer 0.1.1 → 0.1.2
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/lib/as_minimizer/unroller.rb +45 -20
- data/lib/as_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: 8bb3bd538359f52f42e0b8e2f591c3ea3b4b3cabf2740ce4ce5d51fbd810817c
|
|
4
|
+
data.tar.gz: 598f2c2e3897cef1e12d3bcdcd02d95a760ac695c202084c08a83dae1346a4b1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f183c2881281f4687a85312464e95e954383507e4d7bc1d3fe2cd4263b99c977a827b3e16b9b7059621ffede37536f357e84858d2c7a5aeb1467b2710c5b41d4
|
|
7
|
+
data.tar.gz: a0b386f48da881a5df8a74afdcaa7491afb12f0e16cffbb5ee143d10193ab0e64a033f6d9fe6f12cc4d864a5da71a054c77bd4607384382792c0f6d262885991
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.2 (2026-10-18)
|
|
4
|
+
|
|
5
|
+
- Rails main: the controller terminator (now `AbstractController::Callbacks::TERMINATOR`, a lambda in 8.1) is recognized again, so a before_action is a plain call followed by `performed?` instead of a lambda passed to the terminator: one block less per callback to compile, one allocation less per call.
|
|
6
|
+
- The part of a chain an around callback wraps is a method of its own, which the around gets as a one-line block, instead of a proc holding the rest of the chain: no callbacks in blocks nested in blocks, and no proc allocated per around per run.
|
|
7
|
+
|
|
3
8
|
## 0.1.1 (2026-10-18)
|
|
4
9
|
|
|
5
10
|
- Unrolled callbacks: one method per class and chain instead of one per controller action (`only:`/`except:` become a Set lookup), and classes whose chains are the same share one method body. Fewer methods for the JIT to compile.
|
|
@@ -38,6 +38,11 @@ module AsMinimizer
|
|
|
38
38
|
# skipped after a halt when the chain says so. For controllers, `only:` and
|
|
39
39
|
# `except:` are a lookup of action_name in the filter's action set.
|
|
40
40
|
#
|
|
41
|
+
# The part of the chain an around callback wraps is a method of its own
|
|
42
|
+
# (__as_save_1, ...), which the around gets as a one-line block: the
|
|
43
|
+
# callbacks run in plain methods rather than blocks nested in blocks, and
|
|
44
|
+
# nothing is allocated for an around callback that only yields.
|
|
45
|
+
#
|
|
41
46
|
# One method per class and chain (not per controller action), and one body
|
|
42
47
|
# per distinct chain: classes whose chains come out the same (subclasses of
|
|
43
48
|
# a controller that add no callbacks of their own, ...) get the same method
|
|
@@ -48,6 +53,8 @@ module AsMinimizer
|
|
|
48
53
|
module Unroller
|
|
49
54
|
# Filters and conditions that aren't symbols are referenced from here.
|
|
50
55
|
OBJECTS = []
|
|
56
|
+
# What a nested level returns when the chain halted in it.
|
|
57
|
+
HALTED = Object.new.freeze
|
|
51
58
|
# [kind, body] => a module holding the generated method; classes with the
|
|
52
59
|
# same chain define theirs from its UnboundMethod, sharing the body.
|
|
53
60
|
SHARED = {}
|
|
@@ -99,16 +106,16 @@ module AsMinimizer
|
|
|
99
106
|
generator = Generator.new(klass, kind, chain)
|
|
100
107
|
body = generator.source
|
|
101
108
|
warn_missing(klass, kind, generator.targets)
|
|
102
|
-
name = :"__as_#{kind}"
|
|
103
109
|
shared = SHARED[[kind, body]] ||= begin
|
|
104
110
|
label = "#{klass.name || "anonymous"}##{kind}"
|
|
105
|
-
code = "# #{label} callbacks (and every class whose chain is the same), unrolled by activesupport-minimizer\n"
|
|
106
|
-
"def #{name}\n#{body}\nend\n"
|
|
111
|
+
code = "# #{label} callbacks (and every class whose chain is the same), unrolled by activesupport-minimizer\n#{body}"
|
|
107
112
|
Module.new.tap { it.module_eval(code, file_for(klass, kind, label, code), 1) }
|
|
108
113
|
end
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
114
|
+
shared.instance_methods(false).each do |name|
|
|
115
|
+
klass.send(:define_method, name, shared.instance_method(name))
|
|
116
|
+
klass.send(:private, name)
|
|
117
|
+
end
|
|
118
|
+
:"__as_#{kind}"
|
|
112
119
|
rescue Unsupported
|
|
113
120
|
nil
|
|
114
121
|
end
|
|
@@ -159,13 +166,15 @@ module AsMinimizer
|
|
|
159
166
|
@kind = kind
|
|
160
167
|
@chain = chain
|
|
161
168
|
@config = chain.config
|
|
162
|
-
@lambdas = 0
|
|
163
169
|
@targets = []
|
|
164
170
|
end
|
|
165
171
|
|
|
172
|
+
# The method definitions: __as_<kind>, and one more per around callback.
|
|
166
173
|
def source
|
|
167
174
|
terminator!
|
|
168
|
-
|
|
175
|
+
@methods = []
|
|
176
|
+
@methods << "def __as_#{@kind}(&)\nhalted = false\nvalue = nil\n#{level(levels)}value\nend\n"
|
|
177
|
+
@methods.join
|
|
169
178
|
end
|
|
170
179
|
|
|
171
180
|
private
|
|
@@ -175,7 +184,7 @@ module AsMinimizer
|
|
|
175
184
|
@terminator =
|
|
176
185
|
if terminator.equal?(ActiveSupport::Callbacks::CallbackChain.const_get(:DEFAULT_TERMINATOR))
|
|
177
186
|
:abort
|
|
178
|
-
elsif
|
|
187
|
+
elsif performed_terminator?(terminator)
|
|
179
188
|
:performed
|
|
180
189
|
else
|
|
181
190
|
object(terminator)
|
|
@@ -186,6 +195,15 @@ module AsMinimizer
|
|
|
186
195
|
@inline_actions = !(@klass.respond_to?(:raise_on_missing_callback_actions) && @klass.raise_on_missing_callback_actions)
|
|
187
196
|
end
|
|
188
197
|
|
|
198
|
+
# Rails 8.1's controller terminator is a lambda; main's is TERMINATOR.
|
|
199
|
+
def performed_terminator?(terminator)
|
|
200
|
+
if terminator.is_a?(Proc)
|
|
201
|
+
terminator.source_location&.first&.end_with?("abstract_controller/callbacks.rb")
|
|
202
|
+
else
|
|
203
|
+
defined?(AbstractController::Callbacks::TERMINATOR) && terminator.equal?(AbstractController::Callbacks::TERMINATOR)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
189
207
|
# What CallbackChain#compile builds: each around opens a nested level;
|
|
190
208
|
# befores keep definition order, afters run last-defined first.
|
|
191
209
|
def levels
|
|
@@ -234,16 +252,22 @@ module AsMinimizer
|
|
|
234
252
|
check.empty? ? "#{call}\n" : "if #{check.join(" && ")}\n #{call}\nend\n"
|
|
235
253
|
end
|
|
236
254
|
|
|
237
|
-
# The rest of the chain
|
|
238
|
-
#
|
|
255
|
+
# The rest of the chain is a method of its own: the around callback gets
|
|
256
|
+
# a block calling it, and it's called directly when the around is
|
|
257
|
+
# skipped. It returns the chain's value, or HALTED.
|
|
239
258
|
def around(callback, inner)
|
|
259
|
+
name = "__as_#{@kind}_#{@methods.size}"
|
|
260
|
+
@methods << "def #{name}(halted, &)\nvalue = nil\n#{inner}halted ? ::AsMinimizer::Unroller::HALTED : value\nend\n"
|
|
240
261
|
cond = conditions(callback)
|
|
241
|
-
|
|
242
|
-
code =
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
262
|
+
direct = "inner = #{name}(halted, &)\n"
|
|
263
|
+
code =
|
|
264
|
+
if cond
|
|
265
|
+
skip = cond == "true" ? "halted" : "halted || !(#{cond})"
|
|
266
|
+
"inner = nil\nif #{skip}\n #{direct.chomp}\nelse\n #{call(callback, callback.filter, "{ inner = #{name}(halted, &) }")}\nend\n"
|
|
267
|
+
else
|
|
268
|
+
direct
|
|
269
|
+
end
|
|
270
|
+
code << "if ::AsMinimizer::Unroller::HALTED.equal?(inner)\n halted = true\n value = false\nelse\n value = inner\nend\n"
|
|
247
271
|
end
|
|
248
272
|
|
|
249
273
|
# Ruby for the callback's :if/:unless, "true" when there are none, nil
|
|
@@ -288,18 +312,19 @@ module AsMinimizer
|
|
|
288
312
|
end
|
|
289
313
|
end
|
|
290
314
|
|
|
315
|
+
# block: a block literal ("{ ... }") an around callback runs the rest of the chain with.
|
|
291
316
|
def call(callback, filter, block = nil)
|
|
292
317
|
case filter
|
|
293
|
-
when Symbol then (@targets << [filter, :filter, callback.kind]; "self.#{method_call(filter)}#{"
|
|
318
|
+
when Symbol then (@targets << [filter, :filter, callback.kind]; "self.#{method_call(filter)}#{" #{block}" if block}")
|
|
294
319
|
when Proc
|
|
295
320
|
case filter.arity
|
|
296
321
|
when 2
|
|
297
322
|
raise Unsupported, "a before/after proc with 2 parameters" unless block # ActiveSupport raises ArgumentError here
|
|
298
|
-
"instance_exec(self, #{block}, &#{object(filter)})"
|
|
323
|
+
"instance_exec(self, proc #{block}, &#{object(filter)})"
|
|
299
324
|
when 1, -2 then "instance_exec(self, &#{object(filter)})"
|
|
300
325
|
else "instance_exec(&#{object(filter)})"
|
|
301
326
|
end
|
|
302
|
-
else "#{object(filter)}.#{scope_method(callback)}(#{
|
|
327
|
+
else "#{object(filter)}.#{scope_method(callback)}(self)#{" #{block}" if block}"
|
|
303
328
|
end
|
|
304
329
|
end
|
|
305
330
|
|
data/lib/as_minimizer/version.rb
CHANGED