rails_pulse 0.4.0.pre.1 → 0.4.0.pre.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: 19b495bf5ea042672667ee099947d337731557f880af30f7d2dc26f507cbea0f
4
- data.tar.gz: cb8946047ebb55a662742d42d143f47b5b3f3b982779fb289e4abfe418ac1c59
3
+ metadata.gz: b102fc4776d7fe701b47011e02d682f47a2784500103f3cb7d7598a757b174ff
4
+ data.tar.gz: 34f4055047c1bb9151c9e811f14c4e90fe83e52508c61ea4433929b2b431d568
5
5
  SHA512:
6
- metadata.gz: 428db96f32b2624638e99aacad09ef5dbeac23b6e11df316f7db66757c997777e846907830993657f4c76c0ea81dbfc92246ad93e1f5f3cc1c5b53ca80cb6065
7
- data.tar.gz: 3f73d50c25352fc8a3dbaabe8cee59a51624fbebb1d23dfc58208bf42add67a33a56003d98769610559cd8211f8dab76a7a478da6cac0883f7a0d0e6d07f6ebb
6
+ metadata.gz: 974fcf20833ab11e46a00db96d21590f32a699a73de8d2af229d1fa5559f0817c107ba684f4ada34cdb067a77f6293ecc11ee3d24f7911f9fe074353a2991d79
7
+ data.tar.gz: 507afdae03f11fef23d0bbd7cc5ee8e8123656e41efbc219a10f8b6682602bfb7dcfa91e9ac424352d68ad05b3bceed4070eead220d053713f56aa1ade7bec01
data/CHANGELOG.md CHANGED
@@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.0.pre.2] - 2026-09-04
11
+
12
+ ### Fixed
13
+
14
+ - **`rails generate rails_pulse:upgrade` wrote a migration that would not parse.**
15
+ The schema parser matched a column comment with `comment: "([^"]*)"`, which stops
16
+ at the first escaped quote. The `http_methods` comment embeds a `["GET","POST"]`
17
+ example, so it was truncated mid-escape and the trailing backslash escaped the
18
+ closing quote of the generated `comment:` string — the copied
19
+ `upgrade_rails_pulse_tables.rb` then failed to load with
20
+ `unterminated string meets end of file`. Comments are now matched as complete Ruby
21
+ string literals, decoded with `String#undump`, and re-escaped with `String#inspect`
22
+ when the migration is rendered.
23
+
24
+ - **Background tracking writes corrupted the host app's test database connection.**
25
+ Under `use_transactional_tests` Rails pins one connection per pool and hands that
26
+ same connection to every thread, so the tracker's writer thread interleaved its
27
+ INSERTs with the test's own statements on a single socket. PostgreSQL reported
28
+ this as `message type 0x5a arrived from server while idle` and
29
+ `server sent data ("D" message) without prior row description`, and suites hung
30
+ or failed at random. The gem's own dummy app avoided it with
31
+ `config.async = false if Rails.env.test?`, but the installed initializer never
32
+ shipped that line. The tracker now writes inline whenever the pool's connection
33
+ is shared across threads, and `rails generate rails_pulse:install` adds the
34
+ test-environment line to the initializer. Existing installs should add
35
+ `config.async = false if Rails.env.test?` to `config/initializers/rails_pulse.rb`.
36
+
10
37
  ## [0.4.0.pre.1] - 2026-09-03
11
38
 
12
39
  This release contains a **breaking schema change** and requires a one-time data
@@ -48,6 +48,25 @@ module RailsPulse
48
48
 
49
49
  private
50
50
 
51
+ # Column definitions look like: t.text :column_name, comment: "..."
52
+ # The comment capture must tolerate escaped quotes — the http_methods
53
+ # comment embeds a JSON example, [\"GET\",\"POST\"] — so it matches a
54
+ # full Ruby string literal (quotes included) rather than stopping at the
55
+ # first quote character.
56
+ COLUMN_DEFINITION = /t\.(\w+)\s+:([a-zA-Z_][a-zA-Z0-9_]*)(?:.*?comment:\s*("(?:[^"\\]|\\.)*"))?/
57
+
58
+ # Turn the matched Ruby string literal back into the string it denotes, so
59
+ # callers hold the real comment text and re-escape it themselves (the
60
+ # upgrade migration template uses String#inspect). Falls back to stripping
61
+ # the quotes if the literal uses an escape String#undump rejects.
62
+ def decode_comment(literal)
63
+ return nil if literal.nil?
64
+
65
+ literal.undump
66
+ rescue RuntimeError
67
+ literal.delete_prefix('"').delete_suffix('"')
68
+ end
69
+
51
70
  # Parse column definitions from a table block
52
71
  # Returns hash of column_name => { type:, comment: }
53
72
  def parse_table_columns(table_block)
@@ -55,9 +74,10 @@ module RailsPulse
55
74
 
56
75
  table_block.split("\n").each do |line|
57
76
  # Match column definitions: t.text :column_name, comment: "..."
58
- next unless match = line.match(/t\.(\w+)\s+:([a-zA-Z_][a-zA-Z0-9_]*)(?:.*?comment:\s*"([^"]*)")?/)
77
+ next unless match = line.match(COLUMN_DEFINITION)
59
78
 
60
- column_type, column_name, comment = match.captures
79
+ column_type, column_name, comment_literal = match.captures
80
+ comment = decode_comment(comment_literal)
61
81
 
62
82
  # Skip timestamps and references (Rails handles these)
63
83
  next if %w[timestamps references].include?(column_type)
@@ -4,7 +4,7 @@ class UpgradeRailsPulseTables < ActiveRecord::Migration[<%= @migration_version %
4
4
  <% @missing_columns.each do |table_name, columns| %>
5
5
  <% columns.each do |column_name, definition| %>
6
6
  # Add <%= column_name %> column to <%= table_name %>
7
- add_column :<%= table_name %>, :<%= column_name %>, :<%= definition[:type] %><% if definition[:comment] %>, comment: "<%= definition[:comment] %>"<% end %>
7
+ add_column :<%= table_name %>, :<%= column_name %>, :<%= definition[:type] %><% if definition[:comment] %>, comment: <%= definition[:comment].inspect %><% end %>
8
8
  <% end %>
9
9
  <% end %>
10
10
  end
@@ -6,6 +6,11 @@ RailsPulse.configure do |config|
6
6
  # Enable or disable Rails Pulse
7
7
  config.enabled = true
8
8
 
9
+ # Tracking writes happen on a background thread by default (see `config.async`
10
+ # under ADVANCED). Transactional tests share one database connection across
11
+ # threads, so write inline there to keep the writer off the test's connection.
12
+ config.async = false if Rails.env.test?
13
+
9
14
  # ====================================================================================================
10
15
  # THRESHOLDS
11
16
  # ====================================================================================================
@@ -11,7 +11,7 @@ module RailsPulse
11
11
  def track_request(data)
12
12
  return if RequestStore.store[:skip_recording_rails_pulse_activity]
13
13
 
14
- if RailsPulse.configuration.async
14
+ if RailsPulse.configuration.async && !connection_shared_across_threads?
15
15
  Thread.new do
16
16
  perform_tracking(data)
17
17
  rescue => e
@@ -62,6 +62,22 @@ module RailsPulse
62
62
  RequestStore.store[:skip_recording_rails_pulse_activity] = false
63
63
  end
64
64
 
65
+ # Transactional tests pin one connection per pool and hand that same
66
+ # connection to every thread that asks for one. A background writer would
67
+ # then interleave its statements with the test's on a single socket, which
68
+ # PostgreSQL reports as "message type 0x5a arrived from server while idle".
69
+ # Rails 7.2+ records the pin in the pool's @pinned_connection; 7.1 sets a
70
+ # @lock_thread on the pool instead. Neither is public API, so read both
71
+ # defensively and treat any failure as "not shared".
72
+ def connection_shared_across_threads?
73
+ pool = RailsPulse::ApplicationRecord.connection_pool
74
+ return true if pool.instance_variable_get(:@pinned_connection)
75
+
76
+ pool.instance_variable_get(:@lock_thread) ? true : false
77
+ rescue StandardError
78
+ false
79
+ end
80
+
65
81
  def clear_aborted_transaction(conn)
66
82
  raw = conn.raw_connection
67
83
  # Non-PostgreSQL adapters don't expose transaction_status, so this is a no-op for them.
@@ -1,3 +1,3 @@
1
1
  module RailsPulse
2
- VERSION = "0.4.0.pre.1"
2
+ VERSION = "0.4.0.pre.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_pulse
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0.pre.1
4
+ version: 0.4.0.pre.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Harvey
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-09-03 00:00:00.000000000 Z
11
+ date: 2026-09-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -543,7 +543,7 @@ metadata:
543
543
  bug_tracker_uri: https://github.com/railspulse/rails_pulse/issues
544
544
  rubygems_mfa_required: 'true'
545
545
  post_install_message: |
546
- Rails Pulse 0.4.0.pre.1 installed.
546
+ Rails Pulse 0.4.0.pre.2 installed.
547
547
 
548
548
  UPGRADING from any 0.3.x? This release changes route identity and DROPS
549
549
  rails_pulse_routes.method. Back up your database first, then run: