rails_event_viewer 0.1.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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +14 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +451 -0
  5. data/Rakefile +16 -0
  6. data/app/assets/stylesheets/rails_event_viewer/application.css +32 -0
  7. data/app/assets/stylesheets/rails_event_viewer/tailwind.css +54 -0
  8. data/app/controllers/rails_event_viewer/analytics_controller.rb +78 -0
  9. data/app/controllers/rails_event_viewer/application_controller.rb +84 -0
  10. data/app/controllers/rails_event_viewer/dashboard_controller.rb +18 -0
  11. data/app/controllers/rails_event_viewer/event_types_controller.rb +16 -0
  12. data/app/controllers/rails_event_viewer/events_controller.rb +72 -0
  13. data/app/controllers/rails_event_viewer/groups_controller.rb +52 -0
  14. data/app/helpers/rails_event_viewer/application_helper.rb +163 -0
  15. data/app/models/rails_event_viewer/application_record.rb +5 -0
  16. data/app/models/rails_event_viewer/entry.rb +34 -0
  17. data/app/views/layouts/rails_event_viewer/application.html.erb +45 -0
  18. data/app/views/rails_event_viewer/analytics/overview.html.erb +134 -0
  19. data/app/views/rails_event_viewer/dashboard/index.html.erb +113 -0
  20. data/app/views/rails_event_viewer/event_types/index.html.erb +59 -0
  21. data/app/views/rails_event_viewer/event_types/show.html.erb +102 -0
  22. data/app/views/rails_event_viewer/events/_event_row.html.erb +42 -0
  23. data/app/views/rails_event_viewer/events/_filters_sidebar.html.erb +66 -0
  24. data/app/views/rails_event_viewer/events/index.html.erb +65 -0
  25. data/app/views/rails_event_viewer/events/show.html.erb +155 -0
  26. data/app/views/rails_event_viewer/groups/index.html.erb +115 -0
  27. data/app/views/rails_event_viewer/groups/show.html.erb +134 -0
  28. data/app/views/rails_event_viewer/shared/_navigation.html.erb +47 -0
  29. data/app/views/rails_event_viewer/shared/_pagination.html.erb +60 -0
  30. data/app/views/rails_event_viewer/shared/_stat_card.html.erb +36 -0
  31. data/config/routes.rb +18 -0
  32. data/db/migrate/20241208000001_create_rails_event_viewer_entries.rb +36 -0
  33. data/lib/generators/rails_event_viewer/install/USAGE +33 -0
  34. data/lib/generators/rails_event_viewer/install/install_generator.rb +86 -0
  35. data/lib/generators/rails_event_viewer/install/templates/create_rails_event_viewer_entries.rb.erb +35 -0
  36. data/lib/generators/rails_event_viewer/install/templates/initializer.rb.erb +90 -0
  37. data/lib/rails_event_viewer/adapter.rb +93 -0
  38. data/lib/rails_event_viewer/adapters/active_record.rb +227 -0
  39. data/lib/rails_event_viewer/adapters/concerns/in_memory_filtering.rb +136 -0
  40. data/lib/rails_event_viewer/adapters/memory.rb +128 -0
  41. data/lib/rails_event_viewer/adapters/null.rb +63 -0
  42. data/lib/rails_event_viewer/adapters/redis.rb +213 -0
  43. data/lib/rails_event_viewer/engine.rb +24 -0
  44. data/lib/rails_event_viewer/events_relation.rb +130 -0
  45. data/lib/rails_event_viewer/json_query.rb +111 -0
  46. data/lib/rails_event_viewer/pagy_countable.rb +31 -0
  47. data/lib/rails_event_viewer/subscriber.rb +255 -0
  48. data/lib/rails_event_viewer/time_utils.rb +24 -0
  49. data/lib/rails_event_viewer/version.rb +3 -0
  50. data/lib/rails_event_viewer.rb +122 -0
  51. data/lib/tasks/rails_event_viewer_tasks.rake +81 -0
  52. metadata +151 -0
@@ -0,0 +1,255 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsEventViewer
4
+ class Subscriber
5
+ NANOSECONDS_PER_SECOND = 1_000_000_000.0
6
+ SHUTDOWN_TIMEOUT_SECONDS = 5
7
+ ACTION_CABLE_CHANNEL = "rails_event_viewer:events"
8
+
9
+ def initialize
10
+ @buffer = Queue.new
11
+ @mutex = Mutex.new
12
+ @flusher_thread = nil
13
+ @shutdown_requested = false
14
+ @pid = Process.pid
15
+
16
+ ensure_flusher_thread!
17
+ end
18
+
19
+ def emit(event)
20
+ handle_fork_if_needed
21
+
22
+ return unless should_capture?(event)
23
+ return if sampled_out?
24
+
25
+ entry = build_entry(event)
26
+
27
+ if RailsEventViewer.async
28
+ buffer_event(entry)
29
+ else
30
+ write_immediately([entry])
31
+ end
32
+ rescue => e
33
+ handle_error(e, event)
34
+ end
35
+
36
+ def flush!
37
+ return if @buffer.empty?
38
+
39
+ events_to_write = drain_buffer
40
+
41
+ return if events_to_write.empty?
42
+
43
+ write_immediately(events_to_write)
44
+ broadcast_events(events_to_write)
45
+ end
46
+
47
+ def stop!
48
+ @mutex.synchronize do
49
+ @shutdown_requested = true
50
+ end
51
+
52
+ if @flusher_thread&.alive?
53
+ @flusher_thread.wakeup rescue nil
54
+ @flusher_thread.join(SHUTDOWN_TIMEOUT_SECONDS)
55
+ end
56
+
57
+ @mutex.synchronize do
58
+ @flusher_thread = nil
59
+ end
60
+
61
+ flush!
62
+ end
63
+
64
+ def running?
65
+ @flusher_thread&.alive? || false
66
+ end
67
+
68
+ def buffer_size
69
+ @buffer.size
70
+ end
71
+
72
+ private
73
+
74
+ def handle_fork_if_needed
75
+ # Threads don't survive forks, so we need to restart the flusher
76
+ @mutex.synchronize do
77
+ if @pid != Process.pid
78
+ @pid = Process.pid
79
+ @buffer = Queue.new
80
+ @flusher_thread = nil
81
+ @shutdown_requested = false
82
+ end
83
+ end
84
+ ensure_flusher_thread!
85
+ end
86
+
87
+ def drain_buffer
88
+ events = []
89
+ loop { events << @buffer.pop(true) }
90
+ rescue ThreadError
91
+ events
92
+ end
93
+
94
+ def buffer_event(entry)
95
+ @buffer << entry
96
+ ensure_flusher_thread!
97
+
98
+ flush! if @buffer.size >= RailsEventViewer.buffer_size
99
+ end
100
+
101
+ def ensure_flusher_thread!
102
+ return unless RailsEventViewer.async
103
+
104
+ @mutex.synchronize do
105
+ return if @flusher_thread&.alive?
106
+ return if @shutdown_requested
107
+
108
+ @flusher_thread = Thread.new { run_flusher_loop }
109
+ end
110
+ end
111
+
112
+ def run_flusher_loop
113
+ log_info("[RailsEventViewer] Flusher thread started (PID: #{Process.pid})")
114
+
115
+ until shutdown_requested?
116
+ begin
117
+ sleep(RailsEventViewer.flush_interval)
118
+ flush! unless @buffer.empty?
119
+ rescue => e
120
+ log_error("[RailsEventViewer] Flusher error: #{e.message}")
121
+ end
122
+ end
123
+
124
+ log_info("[RailsEventViewer] Flusher thread stopped (PID: #{Process.pid})")
125
+ end
126
+
127
+ def shutdown_requested?
128
+ @mutex.synchronize { @shutdown_requested }
129
+ end
130
+
131
+ def write_immediately(events)
132
+ return if events.blank?
133
+
134
+ RailsEventViewer.adapter.write_events(events)
135
+ rescue => e
136
+ log_error("[RailsEventViewer] Failed to write #{events.size} events: #{e.message}")
137
+ end
138
+
139
+ def broadcast_events(events)
140
+ return unless defined?(ActionCable)
141
+ return unless events.any?
142
+
143
+ ActionCable.server.broadcast(
144
+ ACTION_CABLE_CHANNEL,
145
+ {
146
+ action: "new_events",
147
+ count: events.size,
148
+ events: events.map { |e| { name: e[:name], occurred_at: e[:occurred_at]&.iso8601 } }
149
+ }
150
+ )
151
+ rescue => e
152
+ log_error("[RailsEventViewer] Broadcast error: #{e.message}")
153
+ end
154
+
155
+ def should_capture?(event)
156
+ return false unless event.is_a?(Hash)
157
+ return false if event[:name].blank?
158
+
159
+ name = event[:name].to_s
160
+ return false if ignored?(name)
161
+ return true if RailsEventViewer.captured_events.empty?
162
+
163
+ captured?(name)
164
+ end
165
+
166
+ def ignored?(name)
167
+ RailsEventViewer.ignored_events.any? do |pattern|
168
+ matches_pattern?(name, pattern)
169
+ end
170
+ end
171
+
172
+ def captured?(name)
173
+ RailsEventViewer.captured_events.any? do |pattern|
174
+ matches_pattern?(name, pattern)
175
+ end
176
+ end
177
+
178
+ def matches_pattern?(name, pattern)
179
+ case pattern
180
+ when String
181
+ name == pattern
182
+ when Regexp
183
+ name.match?(pattern)
184
+ else
185
+ false
186
+ end
187
+ end
188
+
189
+ def sampled_out?
190
+ rate = RailsEventViewer.sample_rate
191
+ return false if rate >= 1.0
192
+
193
+ rand > rate
194
+ end
195
+
196
+ def build_entry(event)
197
+ timestamp = event[:timestamp]
198
+ occurred_at = if timestamp
199
+ Time.at(timestamp / NANOSECONDS_PER_SECOND)
200
+ else
201
+ Time.current
202
+ end
203
+
204
+ {
205
+ name: event[:name],
206
+ payload: serialize_payload(event[:payload]),
207
+ tags: event[:tags] || {},
208
+ context: event[:context] || {},
209
+ source_file: event.dig(:source_location, :filepath),
210
+ source_line: event.dig(:source_location, :lineno),
211
+ source_label: event.dig(:source_location, :label),
212
+ occurred_at: occurred_at
213
+ }
214
+ end
215
+
216
+ def serialize_payload(payload)
217
+ case payload
218
+ when Hash
219
+ payload
220
+ when nil
221
+ {}
222
+ else
223
+ if payload.respond_to?(:to_h)
224
+ payload.to_h
225
+ elsif payload.respond_to?(:as_json)
226
+ payload.as_json
227
+ else
228
+ { value: payload.to_s }
229
+ end
230
+ end
231
+ end
232
+
233
+ def handle_error(error, event)
234
+ log_error("[RailsEventViewer] Failed to capture event '#{event[:name]}': #{error.message}")
235
+ end
236
+
237
+ def log_info(message)
238
+ effective_logger&.info(message)
239
+ end
240
+
241
+ def log_error(message)
242
+ logger = effective_logger
243
+
244
+ if logger
245
+ logger.error(message)
246
+ else
247
+ warn message
248
+ end
249
+ end
250
+
251
+ def effective_logger
252
+ RailsEventViewer.logger || (defined?(Rails) && Rails.logger)
253
+ end
254
+ end
255
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsEventViewer
4
+ module TimeUtils
5
+ module_function
6
+
7
+ def truncate_to_interval(time, interval)
8
+ return nil unless time
9
+
10
+ case interval
11
+ when :minute
12
+ time.beginning_of_minute
13
+ when :hour
14
+ time.beginning_of_hour
15
+ when :day
16
+ time.beginning_of_day
17
+ when :week
18
+ time.beginning_of_week
19
+ else
20
+ time.beginning_of_hour
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ module RailsEventViewer
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,122 @@
1
+ require "pagy"
2
+ require "chartkick"
3
+ require "groupdate"
4
+
5
+ require "rails_event_viewer/version"
6
+ require "rails_event_viewer/engine"
7
+
8
+ module RailsEventViewer
9
+ mattr_accessor :storage_adapter, default: :active_record
10
+ mattr_accessor :adapter_options, default: {}
11
+
12
+ mattr_accessor :async, default: true # Use buffered writes
13
+ mattr_accessor :buffer_size, default: 100 # Flush after N events
14
+ mattr_accessor :flush_interval, default: 2 # Flush every N seconds
15
+ mattr_accessor :sample_rate, default: 1.0 # 1.0 = 100%, 0.1 = 10%
16
+
17
+ mattr_accessor :retention_period, default: 7.days
18
+ mattr_accessor :captured_events, default: [] # Empty = capture all
19
+ mattr_accessor :ignored_events, default: [
20
+ /^active_record\./,
21
+ /^action_controller\./,
22
+ /^action_view\./,
23
+ /^active_job\./,
24
+ /^action_mailer\./,
25
+ /^active_storage\./,
26
+ /^action_cable\./,
27
+ /^rails\./,
28
+ /^turbo\./,
29
+ /^cache_/,
30
+ /\.sql$/,
31
+ /^solid_/
32
+ ]
33
+ mattr_accessor :per_page, default: 25
34
+
35
+ mattr_accessor :http_basic_auth_enabled, default: false
36
+ mattr_accessor :http_basic_auth_user
37
+ mattr_accessor :http_basic_auth_password
38
+ mattr_accessor :authentication, default: nil
39
+
40
+ mattr_accessor :group_keys, default: [:request_id]
41
+
42
+ mattr_accessor :logger
43
+
44
+ class << self
45
+ def configure
46
+ yield self
47
+ end
48
+
49
+ ADAPTER_LOCK = Mutex.new
50
+
51
+ def adapter
52
+ @adapter || ADAPTER_LOCK.synchronize { @adapter ||= build_adapter }
53
+ end
54
+
55
+ def reset_adapter!
56
+ ADAPTER_LOCK.synchronize { @adapter = nil }
57
+ end
58
+
59
+ def events
60
+ EventsRelation.new(adapter: adapter)
61
+ end
62
+
63
+ def subscriber
64
+ @subscriber ||= Subscriber.new
65
+ end
66
+
67
+ def subscribe!
68
+ return unless defined?(Rails.event)
69
+
70
+ Rails.event.subscribe(subscriber)
71
+ end
72
+
73
+ def shutdown!
74
+ subscriber.stop!
75
+ end
76
+
77
+ private
78
+
79
+ def build_adapter
80
+ adapter_instance = case storage_adapter
81
+ when :active_record
82
+ require "rails_event_viewer/adapters/active_record"
83
+ Adapters::ActiveRecord.new(**adapter_options)
84
+ when :redis
85
+ require "rails_event_viewer/adapters/redis"
86
+ Adapters::Redis.new(**adapter_options)
87
+ when :memory
88
+ require "rails_event_viewer/adapters/memory"
89
+ Adapters::Memory.new(**adapter_options)
90
+ when :null
91
+ require "rails_event_viewer/adapters/null"
92
+ Adapters::Null.new
93
+ when Class
94
+ storage_adapter.new(**adapter_options)
95
+ else
96
+ raise ArgumentError, "Unknown adapter: #{storage_adapter}. " \
97
+ "Valid options: :active_record, :redis, :memory, :null, or a custom adapter class"
98
+ end
99
+
100
+ if storage_adapter == :active_record && !adapter_instance.table_exists?
101
+ effective_logger.warn "[RailsEventViewer] Table 'rails_event_viewer_entries' not found. " \
102
+ "Run `rails rails_event_viewer:install:migrations` and `rails db:migrate`. " \
103
+ "Falling back to NullAdapter (events will not be persisted)."
104
+ require "rails_event_viewer/adapters/null"
105
+ Adapters::Null.new
106
+ else
107
+ adapter_instance
108
+ end
109
+ end
110
+
111
+ def effective_logger
112
+ logger || (defined?(Rails) && Rails.logger) || Logger.new($stdout)
113
+ end
114
+ end
115
+ end
116
+
117
+ require "rails_event_viewer/adapter"
118
+ require "rails_event_viewer/events_relation"
119
+ require "rails_event_viewer/subscriber"
120
+ require "rails_event_viewer/json_query"
121
+ require "rails_event_viewer/pagy_countable"
122
+ require "rails_event_viewer/time_utils"
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :rails_event_viewer do
4
+ desc "Delete events older than the configured retention period"
5
+ task cleanup: :environment do
6
+ retention_period = RailsEventViewer.retention_period
7
+ cutoff_time = Time.current - retention_period
8
+
9
+ puts "RailsEventViewer: Cleaning up events older than #{cutoff_time}..."
10
+
11
+ deleted_count = RailsEventViewer.adapter.delete_before(cutoff_time)
12
+
13
+ puts "RailsEventViewer: Deleted #{deleted_count || 'unknown number of'} events."
14
+ end
15
+
16
+ desc "Show event statistics"
17
+ task stats: :environment do
18
+ adapter = RailsEventViewer.adapter
19
+ adapter_name = adapter.class.name.split("::").last
20
+
21
+ puts ""
22
+ puts "RailsEventViewer Statistics"
23
+ puts "=" * 40
24
+ puts "Storage Adapter: #{adapter_name}"
25
+ puts ""
26
+
27
+ total = RailsEventViewer.events.count
28
+ puts "Total Events: #{total}"
29
+
30
+ last_hour = adapter.count_since(1.hour.ago) rescue "N/A"
31
+ last_day = adapter.count_since(1.day.ago) rescue "N/A"
32
+ last_week = adapter.count_since(1.week.ago) rescue "N/A"
33
+
34
+ puts ""
35
+ puts "Events by Time Period:"
36
+ puts " Last hour: #{last_hour}"
37
+ puts " Last day: #{last_day}"
38
+ puts " Last week: #{last_week}"
39
+
40
+ puts ""
41
+ puts "Top 10 Event Types:"
42
+ top_events = adapter.counts_by_name(limit: 10) rescue {}
43
+ if top_events.empty?
44
+ puts " (no events)"
45
+ else
46
+ top_events.each do |name, count|
47
+ puts " #{name}: #{count}"
48
+ end
49
+ end
50
+
51
+ puts ""
52
+ end
53
+
54
+ desc "Clear all events (use with caution!)"
55
+ task clear: :environment do
56
+ adapter = RailsEventViewer.adapter
57
+
58
+ if adapter.respond_to?(:clear!)
59
+ print "Are you sure you want to delete ALL events? [y/N] "
60
+ response = $stdin.gets.chomp.downcase
61
+
62
+ if response == "y"
63
+ adapter.clear!
64
+ puts "All events have been cleared."
65
+ else
66
+ puts "Cancelled."
67
+ end
68
+ else
69
+ puts "Clear operation not supported by #{adapter.class.name}"
70
+ end
71
+ end
72
+
73
+ desc "Flush any buffered events to storage"
74
+ task flush: :environment do
75
+ puts "Flushing buffered events..."
76
+
77
+ RailsEventViewer.subscriber.flush!
78
+
79
+ puts "Done."
80
+ end
81
+ end
metadata ADDED
@@ -0,0 +1,151 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_event_viewer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Keshav Biswa
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 8.1.0
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 8.1.0
26
+ - !ruby/object:Gem::Dependency
27
+ name: pagy
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '43.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '43.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: chartkick
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '5.0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '5.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: groupdate
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '6.0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '6.0'
68
+ description: RailsEventViewer provides a web interface to browse, search, and analyze
69
+ structured events emitted via Rails.event. Features include event listing, filtering,
70
+ search, and analytics dashboards.
71
+ email:
72
+ - keshavbiswa21@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - CHANGELOG.md
78
+ - MIT-LICENSE
79
+ - README.md
80
+ - Rakefile
81
+ - app/assets/stylesheets/rails_event_viewer/application.css
82
+ - app/assets/stylesheets/rails_event_viewer/tailwind.css
83
+ - app/controllers/rails_event_viewer/analytics_controller.rb
84
+ - app/controllers/rails_event_viewer/application_controller.rb
85
+ - app/controllers/rails_event_viewer/dashboard_controller.rb
86
+ - app/controllers/rails_event_viewer/event_types_controller.rb
87
+ - app/controllers/rails_event_viewer/events_controller.rb
88
+ - app/controllers/rails_event_viewer/groups_controller.rb
89
+ - app/helpers/rails_event_viewer/application_helper.rb
90
+ - app/models/rails_event_viewer/application_record.rb
91
+ - app/models/rails_event_viewer/entry.rb
92
+ - app/views/layouts/rails_event_viewer/application.html.erb
93
+ - app/views/rails_event_viewer/analytics/overview.html.erb
94
+ - app/views/rails_event_viewer/dashboard/index.html.erb
95
+ - app/views/rails_event_viewer/event_types/index.html.erb
96
+ - app/views/rails_event_viewer/event_types/show.html.erb
97
+ - app/views/rails_event_viewer/events/_event_row.html.erb
98
+ - app/views/rails_event_viewer/events/_filters_sidebar.html.erb
99
+ - app/views/rails_event_viewer/events/index.html.erb
100
+ - app/views/rails_event_viewer/events/show.html.erb
101
+ - app/views/rails_event_viewer/groups/index.html.erb
102
+ - app/views/rails_event_viewer/groups/show.html.erb
103
+ - app/views/rails_event_viewer/shared/_navigation.html.erb
104
+ - app/views/rails_event_viewer/shared/_pagination.html.erb
105
+ - app/views/rails_event_viewer/shared/_stat_card.html.erb
106
+ - config/routes.rb
107
+ - db/migrate/20241208000001_create_rails_event_viewer_entries.rb
108
+ - lib/generators/rails_event_viewer/install/USAGE
109
+ - lib/generators/rails_event_viewer/install/install_generator.rb
110
+ - lib/generators/rails_event_viewer/install/templates/create_rails_event_viewer_entries.rb.erb
111
+ - lib/generators/rails_event_viewer/install/templates/initializer.rb.erb
112
+ - lib/rails_event_viewer.rb
113
+ - lib/rails_event_viewer/adapter.rb
114
+ - lib/rails_event_viewer/adapters/active_record.rb
115
+ - lib/rails_event_viewer/adapters/concerns/in_memory_filtering.rb
116
+ - lib/rails_event_viewer/adapters/memory.rb
117
+ - lib/rails_event_viewer/adapters/null.rb
118
+ - lib/rails_event_viewer/adapters/redis.rb
119
+ - lib/rails_event_viewer/engine.rb
120
+ - lib/rails_event_viewer/events_relation.rb
121
+ - lib/rails_event_viewer/json_query.rb
122
+ - lib/rails_event_viewer/pagy_countable.rb
123
+ - lib/rails_event_viewer/subscriber.rb
124
+ - lib/rails_event_viewer/time_utils.rb
125
+ - lib/rails_event_viewer/version.rb
126
+ - lib/tasks/rails_event_viewer_tasks.rake
127
+ homepage: https://github.com/keshavbiswa/rails_event_viewer
128
+ licenses:
129
+ - MIT
130
+ metadata:
131
+ homepage_uri: https://github.com/keshavbiswa/rails_event_viewer
132
+ source_code_uri: https://github.com/keshavbiswa/rails_event_viewer
133
+ changelog_uri: https://github.com/keshavbiswa/rails_event_viewer/blob/main/CHANGELOG.md
134
+ rdoc_options: []
135
+ require_paths:
136
+ - lib
137
+ required_ruby_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '3.2'
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ requirements: []
148
+ rubygems_version: 3.6.7
149
+ specification_version: 4
150
+ summary: A Rails engine to view structured events from Rails Event Reporter
151
+ test_files: []