rails_tracepoint_stack 0.4.0 → 0.5.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: 612ce09f08d0dfebf77cfddc8dfbe9712598aaf324579f688bac6148354c92fb
4
- data.tar.gz: aec94f6a1a148f16199b6835932a5b1fe91ff458a2915fab60efe937244b7cac
3
+ metadata.gz: 7853cf9027a5db8f962c4cf6753ae98cac10e3cdd0344b618fac59a112837e6d
4
+ data.tar.gz: 29beeae6b2e5eacbca3121a90bef1670e759477b5cfc48aa837dba6ebcb48fe0
5
5
  SHA512:
6
- metadata.gz: de2e858cfad1406ee8becdb5d3be65b956286c802a1f95023d8698eaa80500569ee603be1b28e06404845a41eebb25e364ea936cdd886fcdab02b14113caaf10
7
- data.tar.gz: 17b4beea42cb83b2058b32652acf0ddeec66a1b2fabe32e61a07fcf446738fd4c92b238dd6ce603d5426d68af4e1bbe397e23c7afd7c739c4385ed3d8cf61177
6
+ metadata.gz: 49af2952c9312ea180cbf2afde354b1d9c68a0a4f6d94a991534e8f19b6fed4f477afda01cdb4d93e766751a9195f183f12f93c0fdd51705b3c984127271cb7c
7
+ data.tar.gz: 79ff4005947c9a73f08dc03261894e6044653c3e8e1979043546e56425d0fb718587562707957c868c55930ad02606fca3abe5edd29d19b9d1b57d0b4b69938c
data/README.md CHANGED
@@ -100,14 +100,44 @@ A real request produces tens of thousands of traces, so captures are bounded.
100
100
  | `max_traces` | 5000 | Stops collecting; `session.truncated?` becomes true |
101
101
  | `max_string_length` | 200 | Shortens long strings |
102
102
  | `max_collection_size` | 20 | Shortens long arrays and hashes |
103
+ | `max_value_length` | 1000 | Replaces any single value larger than this with its size |
103
104
  | `capture_params` | `true` | Set false to show only the flow |
104
105
  | `capture_return` | `true` | Set false to show only the calls |
105
106
  | `threads` | `:current` | `:all` also records background threads |
107
+ | `blocks` | `false` | `true` also traces blocks — see below |
106
108
 
107
109
  ```ruby
108
110
  RailsTracepointStack.capture(max_depth: 3, capture_return: false) { ... }
109
111
  ```
110
112
 
113
+ ### Blocks and scopes
114
+
115
+ A Rails scope is a lambda, so the logic inside it is not a method call and does
116
+ not appear by default. `blocks: true` traces block entry and exit as well:
117
+
118
+ ```ruby
119
+ RailsTracepointStack.capture(blocks: true) { News.latest(User.current) }
120
+ ```
121
+
122
+ ```
123
+ News.latest (app/models/news.rb:88) {"user":"Anonymous"}
124
+ block { } (app/models/news.rb:46) {"user":"Anonymous"}
125
+ Project.allowed_to_condition (app/models/project.rb:189) {"permission":"view_news"}
126
+ ↻ 40× AccessControl.permission { } (lib/redmine/access_control.rb:37) {"p":"#<Permission …>"}
127
+ -> false
128
+ ```
129
+
130
+ It is off by default because a block written inside a loop runs once per
131
+ element: in Redmine, one call produced 48 block events and 43 of them came from
132
+ a single `detect` predicate. Runs of the same block at the same depth collapse
133
+ into one line with a `↻ N×` count, which keeps only the first run's arguments —
134
+ if you need every iteration, leave blocks off and read the loop instead.
135
+
136
+ Blocks are named after the method they were written in. A block with no
137
+ enclosing method — a scope, a lambda held in a constant — has neither a class
138
+ nor a method name to show, so it renders as `block { }` plus its location.
139
+ ```
140
+
111
141
  ## Tracing the whole process
112
142
 
113
143
  For code you cannot wrap in a block — boot, a rake task, a request handled by a
data/changelog.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.0
4
+
5
+ **Features**
6
+
7
+ - `capture(blocks: true)` traces block entry and exit, which is what makes a
8
+ Rails scope, a lambda held in a constant, or any yielded block visible.
9
+ Off by default: a block inside a loop runs once per element. Runs of the
10
+ same block at the same depth collapse into one record with a `↻ N×` count
11
+ - `max_value_length` (default 1000) caps any single value, whatever its shape
12
+
13
+ **BugFix**
14
+
15
+ - `to_tree` raised `Encoding::CompatibilityError` whenever a traced value held
16
+ bytes that are not text — a file read in binary mode, a digest, a response
17
+ body. Strings are now converted to UTF-8 with invalid bytes replaced
18
+ - A class method on an ActiveRecord model rendered as its singleton class,
19
+ which prints the model's entire schema for a single call. The class name now
20
+ comes from the module's own name
21
+ - Per-string and per-collection limits still let one value through at thousands
22
+ of characters, since twenty items truncated to two hundred each is four
23
+ thousand
24
+ - The block handed to `capture` was traced as part of the result and consumed a
25
+ level of depth, indenting everything under it
26
+
3
27
  ## 0.4.0
4
28
 
5
29
  **Features**
@@ -6,11 +6,13 @@ module RailsTracepointStack
6
6
  DEFAULT_MAX_TRACES = 5_000
7
7
  DEFAULT_MAX_STRING_LENGTH = 200
8
8
  DEFAULT_MAX_COLLECTION_SIZE = 20
9
+ DEFAULT_MAX_VALUE_LENGTH = 1_000
9
10
 
10
11
  attr_reader :max_depth,
11
12
  :max_traces,
12
13
  :max_string_length,
13
14
  :max_collection_size,
15
+ :max_value_length,
14
16
  :capture_params,
15
17
  :capture_return
16
18
 
@@ -19,6 +21,7 @@ module RailsTracepointStack
19
21
  max_traces: DEFAULT_MAX_TRACES,
20
22
  max_string_length: DEFAULT_MAX_STRING_LENGTH,
21
23
  max_collection_size: DEFAULT_MAX_COLLECTION_SIZE,
24
+ max_value_length: DEFAULT_MAX_VALUE_LENGTH,
22
25
  capture_params: true,
23
26
  capture_return: true
24
27
  )
@@ -26,6 +29,7 @@ module RailsTracepointStack
26
29
  @max_traces = max_traces
27
30
  @max_string_length = max_string_length
28
31
  @max_collection_size = max_collection_size
32
+ @max_value_length = max_value_length
29
33
  @capture_params = capture_params
30
34
  @capture_return = capture_return
31
35
  end
@@ -25,14 +25,36 @@ module RailsTracepointStack
25
25
  )
26
26
  end
27
27
 
28
+ REPLACEMENT = "�".freeze
29
+
28
30
  def self.stringify(value)
29
31
  return nil if value.nil?
30
32
 
31
- value.to_s
33
+ utf8(value.to_s)
32
34
  rescue SystemStackError, StandardError => error
33
35
  inspect_fallback(value, error)
34
36
  end
35
37
 
38
+ # Traced code holds bytes that are not text: a file read in binary mode, a
39
+ # digest, a response body. Left as they are, those strings poison every
40
+ # consumer downstream - JSON refuses to encode them, and joining them with
41
+ # the rest of the output raises Encoding::CompatibilityError. Normalizing
42
+ # here keeps the damage to the one value that caused it.
43
+ def self.utf8(value)
44
+ return value unless value.is_a?(String)
45
+
46
+ converted =
47
+ if value.encoding == Encoding::UTF_8
48
+ value
49
+ else
50
+ value.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: REPLACEMENT)
51
+ end
52
+
53
+ converted.valid_encoding? ? converted : converted.scrub(REPLACEMENT)
54
+ rescue SystemStackError, StandardError
55
+ value.scrub(REPLACEMENT)
56
+ end
57
+
36
58
  def self.safe_value(value, ancestry = {})
37
59
  case value
38
60
  when nil, true, false, Numeric
@@ -40,7 +62,7 @@ module RailsTracepointStack
40
62
  when String
41
63
  # Callers may keep the result around after the traced code moved on,
42
64
  # so a live reference to a mutable string would drift.
43
- value.frozen? ? value : value.dup.freeze
65
+ utf8(value).dup.freeze
44
66
  when Symbol
45
67
  value.to_s
46
68
  when Array
@@ -125,7 +147,7 @@ module RailsTracepointStack
125
147
  end
126
148
 
127
149
  def self.safe_object_string(value)
128
- value.inspect
150
+ utf8(value.inspect)
129
151
  rescue SystemStackError, StandardError => error
130
152
  inspect_fallback(value, error)
131
153
  end
@@ -146,7 +168,7 @@ module RailsTracepointStack
146
168
  when true, false, Numeric
147
169
  value.to_s
148
170
  when String
149
- value.inspect
171
+ utf8(value).inspect
150
172
  when Symbol
151
173
  ":#{value}"
152
174
  when Array
@@ -32,12 +32,39 @@ module RailsTracepointStack
32
32
  def self.line_for(record)
33
33
  case record.kind
34
34
  when :call then call_line(record)
35
- when :return then return_line(record)
35
+ when :b_call then block_line(record)
36
+ when :return, :b_return then return_line(record)
36
37
  when :raise then raise_line(record)
37
38
  end
38
39
  end
39
40
 
40
- SINGLETON_CLASS = /\A#<Class:([A-Z][\w:]*)>\z/
41
+ # A block has no name of its own. TracePoint reports the method it was
42
+ # written in, which is the useful label; a block written at class-body
43
+ # level - a scope, a lambda constant - has neither, so the location is
44
+ # all there is to show.
45
+ def self.block_line(record)
46
+ "#{indent(record.depth)}#{repeat_marker(record)}#{block_name(record)} { } " \
47
+ "(#{location(record)}) #{compact(record.params)}"
48
+ end
49
+
50
+ def self.block_name(record)
51
+ return "block" if record.class_name.nil? || record.method_name.nil?
52
+
53
+ qualified_name(record)
54
+ end
55
+
56
+ def self.repeat_marker(record)
57
+ count = record.repeats.to_i
58
+ return "" if count <= 1
59
+
60
+ "↻ #{count}× "
61
+ end
62
+
63
+ # Stops at the first character that cannot be part of a constant name, so
64
+ # a singleton class that printed extra detail after its name still
65
+ # resolves. Requires a leading capital, which keeps anonymous singletons
66
+ # like #<Class:0x00007f1234> out.
67
+ SINGLETON_CLASS = /\A#<Class:([A-Z][\w:]*)[\s(>]/
41
68
  TEMPLATE_FILE = /\.(erb|haml|slim|builder|jbuilder|rabl)\z/i
42
69
  # Everything a template receives besides the locals belongs to the
43
70
  # rendering machinery, not to the developer.
@@ -14,6 +14,8 @@ module RailsTracepointStack
14
14
  def initialize(session: RailsTracepointStack::TraceSession.new, limits: RailsTracepointStack::Limits.new)
15
15
  @session = session
16
16
  @limits = limits
17
+ @open_block = nil
18
+ @suppressed_returns = Hash.new(0)
17
19
  end
18
20
 
19
21
  def record(trace)
@@ -24,17 +26,54 @@ module RailsTracepointStack
24
26
  return
25
27
  end
26
28
 
27
- session.add(build_record(trace))
29
+ built = build_record(trace)
30
+ return if collapsed?(built)
31
+
32
+ session.add(built)
28
33
  end
29
34
 
30
35
  private
31
36
 
37
+ # A block written inside a loop runs once per element and reports the
38
+ # same location every time, which buries the trace it belongs to. Runs of
39
+ # the same block at the same depth become one record carrying a count,
40
+ # and the matching returns are dropped with them.
41
+ def collapsed?(record)
42
+ return drop_suppressed_return(record) if record.block_return?
43
+ return break_run(record) unless record.block_call?
44
+
45
+ key = [record.file_path, record.line_number, record.depth]
46
+
47
+ if @open_block && @open_block.first == key
48
+ @open_block.last.repeats += 1
49
+ @suppressed_returns[record.depth] += 1
50
+ return true
51
+ end
52
+
53
+ @open_block = [key, record]
54
+ false
55
+ end
56
+
57
+ def drop_suppressed_return(record)
58
+ return false unless @suppressed_returns[record.depth] > 0
59
+
60
+ @suppressed_returns[record.depth] -= 1
61
+ true
62
+ end
63
+
64
+ # Anything that is not part of the block's own call/return pair ends the
65
+ # run, so two separate loops over the same line stay separate.
66
+ def break_run(record)
67
+ @open_block = nil unless record.returned?
68
+ false
69
+ end
70
+
32
71
  def build_record(trace)
33
72
  exception = trace.exception
34
73
 
35
74
  RailsTracepointStack::TraceRecord.new(
36
75
  kind: trace.kind,
37
- class_name: RailsTracepointStack::LogFormatter.stringify(trace.class_name),
76
+ class_name: class_name_for(trace),
38
77
  method_name: trace.method_name,
39
78
  file_path: trace.file_path,
40
79
  line_number: trace.line_number,
@@ -42,10 +81,40 @@ module RailsTracepointStack
42
81
  return_value: return_value_for(trace),
43
82
  exception_class: exception && exception.class.to_s,
44
83
  exception_message: exception && RailsTracepointStack::LogFormatter.stringify(exception.message),
45
- depth: trace.depth || 0
84
+ depth: trace.depth || 0,
85
+ repeats: 1
46
86
  )
47
87
  end
48
88
 
89
+ # `to_s` on a class is whatever that class decided to print. ActiveRecord
90
+ # makes a model's singleton class print its entire schema, which turns one
91
+ # method call into hundreds of characters of column definitions. A module
92
+ # knows its own name, so ask for that instead and only fall back to `to_s`
93
+ # for the anonymous ones that have none.
94
+ def class_name_for(trace)
95
+ klass = trace.class_name
96
+ return RailsTracepointStack::LogFormatter.stringify(klass) unless klass.is_a?(Module)
97
+
98
+ name = klass.name
99
+ return name if name
100
+
101
+ attached = attached_module(klass)
102
+ return "#<Class:#{attached.name}>" if attached&.name
103
+
104
+ RailsTracepointStack::LogFormatter.stringify(klass)
105
+ end
106
+
107
+ # Singleton classes have no name of their own. Ruby 3.2 can hand back the
108
+ # object they belong to; older versions leave only the printed form.
109
+ def attached_module(klass)
110
+ return nil unless klass.respond_to?(:attached_object)
111
+
112
+ attached = klass.attached_object
113
+ attached.is_a?(Module) ? attached : nil
114
+ rescue SystemStackError, StandardError
115
+ nil
116
+ end
117
+
49
118
  def params_for(trace)
50
119
  return {} unless limits.capture_params
51
120
 
@@ -59,7 +128,7 @@ module RailsTracepointStack
59
128
  end
60
129
 
61
130
  def snapshot(value)
62
- RailsTracepointStack::Truncator.call(
131
+ RailsTracepointStack::Truncator.bounded(
63
132
  RailsTracepointStack::LogFormatter.safe_value(value),
64
133
  limits
65
134
  )
@@ -75,9 +75,29 @@ unbounded capture of a real request is tens of thousands of lines.
75
75
  | `max_traces:` | 5000 | Cap total lines |
76
76
  | `max_string_length:` | 200 | Keep big payloads readable |
77
77
  | `max_collection_size:` | 20 | Keep loaded associations readable |
78
+ | `max_value_length:` | 1000 | Cap any single value, whatever its shape |
78
79
  | `capture_params: false` | on | Show only the flow |
79
80
  | `capture_return: false` | on | Show only the calls |
80
81
  | `threads: :all` | current only | Include background threads |
82
+ | `blocks: true` | off | Also trace blocks — see below |
83
+
84
+ ### When a scope or lambda seems to do nothing
85
+
86
+ A Rails scope is a lambda, not a method, so by default nothing inside it shows
87
+ up — you see the call that used the scope and then its result, with no steps in
88
+ between. Same for any `yield`ed block. Add `blocks: true`:
89
+
90
+ ```ruby
91
+ RailsTracepointStack.capture(blocks: true) { News.latest(User.current) }
92
+ ```
93
+
94
+ Blocks render as `Class#method { }`, or as `block { }` plus a location when the
95
+ block has no enclosing method (which is what a scope looks like). A line like
96
+ `↻ 40× ... { }` means that block ran 40 times from the same place — a predicate
97
+ inside a loop — and only the first run's arguments were kept.
98
+
99
+ Leave it off unless you need it. A block inside a loop fires once per element,
100
+ so this costs far more traces than watching methods does.
81
101
 
82
102
  To narrow by file instead, set patterns before capturing:
83
103
 
@@ -20,14 +20,17 @@ module RailsTracepointStack
20
20
  trace_point.event
21
21
  end
22
22
 
23
+ ENTERING = [:call, :b_call].freeze
24
+ LEAVING = [:return, :b_return].freeze
25
+
23
26
  def params
24
- return {} unless kind == :call
27
+ return {} unless ENTERING.include?(kind)
25
28
 
26
29
  @params ||= fetch_params(trace_point)
27
30
  end
28
31
 
29
32
  def return_value
30
- return nil unless kind == :return
33
+ return nil unless LEAVING.include?(kind)
31
34
 
32
35
  trace_point.return_value
33
36
  end
@@ -13,6 +13,7 @@ module RailsTracepointStack
13
13
  :exception_class,
14
14
  :exception_message,
15
15
  :depth,
16
+ :repeats,
16
17
  keyword_init: true
17
18
  ) do
18
19
  def call?
@@ -26,5 +27,19 @@ module RailsTracepointStack
26
27
  def raise?
27
28
  kind == :raise
28
29
  end
30
+
31
+ def block_call?
32
+ kind == :b_call
33
+ end
34
+
35
+ def block_return?
36
+ kind == :b_return
37
+ end
38
+
39
+ # True for anything that produced a value on the way out, so callers do not
40
+ # have to know whether a method or a block produced it.
41
+ def returned?
42
+ return? || block_return?
43
+ end
29
44
  end
30
45
  end
@@ -23,10 +23,22 @@ module RailsTracepointStack
23
23
  # A nil thread watches every thread in the process, which is what the
24
24
  # global tracer wants. Passing one confines tracing to it, so a capture
25
25
  # running inside a threaded server does not pick up other requests.
26
- def initialize(sink: RailsTracepointStack::Sink::Log.new, events: DEFAULT_EVENTS, thread: nil)
26
+ # own_block is the block the caller wrapped around the code under
27
+ # inspection. It is the caller's own code, so it is skipped before a depth
28
+ # is assigned - otherwise it would indent the whole trace by a level it
29
+ # never shows.
30
+ def initialize(
31
+ sink: RailsTracepointStack::Sink::Log.new,
32
+ events: DEFAULT_EVENTS,
33
+ thread: nil,
34
+ own_block: nil
35
+ )
27
36
  @sink = sink
28
37
  @events = events
29
38
  @thread = thread
39
+ # Ruby has grown extra entries on source_location over time (columns, end
40
+ # positions), so only the file and the line are compared.
41
+ @own_block_location = own_block&.source_location&.first(2)
30
42
  @filtered_count = 0
31
43
  generate_tracer
32
44
  end
@@ -39,6 +51,8 @@ module RailsTracepointStack
39
51
 
40
52
  trace = RailsTracepointStack::Trace.new(trace_point: tracepoint)
41
53
 
54
+ next if own_block?(trace)
55
+
42
56
  if ignore_trace?(trace: trace)
43
57
  @filtered_count += 1
44
58
  next
@@ -49,6 +63,35 @@ module RailsTracepointStack
49
63
  end
50
64
  end
51
65
 
66
+ # Matching on location alone would also drop a block written on the same
67
+ # line as the capture call. The caller's block is the first to arrive from
68
+ # that location, so only that one is skipped, along with its return.
69
+ def own_block?(trace)
70
+ return false if @own_block_location.nil?
71
+
72
+ case trace.kind
73
+ when :b_call then claim_own_block(trace)
74
+ when :b_return then release_own_block(trace)
75
+ else false
76
+ end
77
+ end
78
+
79
+ def claim_own_block(trace)
80
+ return false if @own_block_seen
81
+ return false unless [trace.file_path, trace.line_number] == @own_block_location
82
+
83
+ @own_block_seen = true
84
+ @own_block_position = raw_stack_position
85
+ true
86
+ end
87
+
88
+ def release_own_block(trace)
89
+ return false unless @own_block_position && raw_stack_position == @own_block_position
90
+
91
+ @own_block_position = nil
92
+ true
93
+ end
94
+
52
95
  def out_of_scope_thread?
53
96
  !@thread.nil? && Thread.current != @thread
54
97
  end
@@ -61,8 +104,8 @@ module RailsTracepointStack
61
104
  raw_position = raw_stack_position
62
105
 
63
106
  case trace.kind
64
- when :call then tracker.enter(raw_position)
65
- when :return then tracker.leave(raw_position)
107
+ when :call, :b_call then tracker.enter(raw_position)
108
+ when :return, :b_return then tracker.leave(raw_position)
66
109
  when :raise then tracker.raised(raw_position)
67
110
  end
68
111
  end
@@ -1,3 +1,5 @@
1
+ require "json"
2
+
1
3
  module RailsTracepointStack
2
4
  # Shrinks an already-serialized value so one fat argument (a big payload, a
3
5
  # long SQL string, a loaded association) cannot dominate the output. Runs
@@ -6,6 +8,39 @@ module RailsTracepointStack
6
8
  module Truncator
7
9
  ELLIPSIS = "…".freeze
8
10
 
11
+ # Per-string and per-collection limits still leave room for one value to
12
+ # run to thousands of characters: twenty items each truncated to two
13
+ # hundred is still four thousand. This caps the value as a whole, and does
14
+ # it before the per-item truncation so the summary can report the real
15
+ # size rather than the already-shrunk one.
16
+ def self.bounded(value, limits)
17
+ capped = cap(value, limits)
18
+ return capped unless capped.equal?(value)
19
+
20
+ call(value, limits)
21
+ end
22
+
23
+ def self.cap(value, limits)
24
+ max = limits.max_value_length
25
+ return value if max.nil?
26
+ return value unless value.is_a?(String) || value.is_a?(Array) || value.is_a?(Hash)
27
+
28
+ size = JSON.generate(value).length
29
+ return value if size <= max
30
+
31
+ summarize(value, size)
32
+ rescue SystemStackError, StandardError
33
+ value
34
+ end
35
+
36
+ def self.summarize(value, size)
37
+ case value
38
+ when Array then "[#{value.size} items, #{size} chars — over max_value_length]"
39
+ when Hash then "{#{value.size} keys, #{size} chars — over max_value_length}"
40
+ else "[#{size} chars — over max_value_length]"
41
+ end
42
+ end
43
+
9
44
  def self.call(value, limits)
10
45
  case value
11
46
  when String then truncate_string(value, limits)
@@ -1,3 +1,3 @@
1
1
  module RailsTracepointStack
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -22,11 +22,16 @@ module RailsTracepointStack
22
22
  end
23
23
 
24
24
  CAPTURE_EVENTS = [:call, :return, :raise].freeze
25
+ BLOCK_EVENTS = [:b_call, :b_return].freeze
25
26
 
26
27
  # Traces a block and hands back everything that happened inside it, instead
27
28
  # of writing to a log. Meant to be run as a one-off: capture, read, done.
28
- def self.capture(threads: :current, **limit_options)
29
- raise ArgumentError, "Block not given to #capture" unless block_given?
29
+ # Blocks are off by default: a block written inside a loop fires once per
30
+ # element, so watching them costs more traces than watching methods does.
31
+ # Turn them on to see scopes, lambdas and anything else whose logic lives in
32
+ # a block rather than a method.
33
+ def self.capture(threads: :current, blocks: false, **limit_options, &traced)
34
+ raise ArgumentError, "Block not given to #capture" unless traced
30
35
 
31
36
  collector = RailsTracepointStack::Sink::Collector.new(
32
37
  limits: RailsTracepointStack::Limits.new(**limit_options)
@@ -34,13 +39,14 @@ module RailsTracepointStack
34
39
  session = collector.session
35
40
  tracer = RailsTracepointStack::Tracer.new(
36
41
  sink: collector,
37
- events: CAPTURE_EVENTS,
38
- thread: (threads == :all) ? nil : Thread.current
42
+ events: blocks ? CAPTURE_EVENTS + BLOCK_EVENTS : CAPTURE_EVENTS,
43
+ thread: (threads == :all) ? nil : Thread.current,
44
+ own_block: traced
39
45
  )
40
46
 
41
47
  tracer.enable
42
48
  begin
43
- session.result = yield(session)
49
+ session.result = traced.call(session)
44
50
  rescue Exception => error
45
51
  session.error = error
46
52
  raise
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_tracepoint_stack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carlos Daniel Pohlod
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-24 00:00:00.000000000 Z
11
+ date: 2026-07-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec