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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b015df981e76956005ec4dbec6f10808c1fe61a2be2d17395be4bc622ae0541d
4
- data.tar.gz: c569ebbecfa90d6b135966578016eb725e9b705e8bb3896443a87fa096c9c340
3
+ metadata.gz: 8bb3bd538359f52f42e0b8e2f591c3ea3b4b3cabf2740ce4ce5d51fbd810817c
4
+ data.tar.gz: 598f2c2e3897cef1e12d3bcdcd02d95a760ac695c202084c08a83dae1346a4b1
5
5
  SHA512:
6
- metadata.gz: a65151b39365a9eaeefb4bf743edfd103e20f0c9e3cdb9efb38384ed61a4b6f771263df98a8c15e766d00072686fd25fea6abf7187f62b70bce6ec42db751112
7
- data.tar.gz: c9f8740139908120958cec43192c1b241cdb7df945aa2bf01851dc0c05060bd67e1ff8f8b32170c56fcabbe8f5e3b872f01bcbe1c07fe402f00df6acb9de35a3
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
- klass.send(:define_method, name, shared.instance_method(name))
110
- klass.send(:private, name)
111
- name
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
- "halted = false\nvalue = nil\n#{level(levels)}value"
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 terminator.is_a?(Proc) && terminator.source_location&.first&.end_with?("abstract_controller/callbacks.rb")
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 goes in a proc: the around callback gets it as
238
- # its block, and it runs directly when the around is skipped.
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
- inner_name = "inner#{@lambdas += 1}"
242
- code = "#{inner_name} = proc do\n#{inner}end\n" # a Proc, as ActiveSupport passes: extra arguments are ignored
243
- return code << "#{inner_name}.()\n" unless cond
244
-
245
- skip = cond == "true" ? "halted" : "halted || !(#{cond})"
246
- code << "if #{skip}\n #{inner_name}.()\nelse\n #{call(callback, callback.filter, inner_name)}\nend\n"
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)}#{"(&#{block})" if block}")
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)}(#{["self", ("&#{block}" if block)].compact.join(", ")})"
327
+ else "#{object(filter)}.#{scope_method(callback)}(self)#{" #{block}" if block}"
303
328
  end
304
329
  end
305
330
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AsMinimizer
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activesupport-minimizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sampo Kuokkanen