event_system 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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/README.md +362 -0
- data/Rakefile +4 -0
- data/docs/event_visualizations/example_timeline.html +142 -0
- data/docs/example_logs/events_20251027_151744.jsonl +3 -0
- data/docs/example_logs/events_20251027_151802.jsonl +3 -0
- data/docs/examples/basic_usage.rb +64 -0
- data/exe/event_system +139 -0
- data/lib/event_system/configuration.rb +125 -0
- data/lib/event_system/event.rb +139 -0
- data/lib/event_system/event_manager.rb +191 -0
- data/lib/event_system/event_subscriber.rb +29 -0
- data/lib/event_system/storage/base.rb +64 -0
- data/lib/event_system/storage/file_store.rb +187 -0
- data/lib/event_system/storage/memory_store.rb +134 -0
- data/lib/event_system/version.rb +5 -0
- data/lib/event_system/visualization/timeline_generator.rb +237 -0
- data/lib/event_system.rb +37 -0
- data/spec/event_system/configuration_spec.rb +197 -0
- data/spec/event_system/event_manager_spec.rb +341 -0
- data/spec/event_system/event_spec.rb +193 -0
- data/spec/event_system/event_subscriber_spec.rb +295 -0
- data/spec/event_system/storage/file_store_spec.rb +341 -0
- data/spec/event_system/storage/memory_store_spec.rb +248 -0
- data/spec/event_system/visualization/timeline_generator_spec.rb +252 -0
- data/spec/event_system_spec.rb +57 -0
- data/spec/integration/readme_examples_spec.rb +447 -0
- data/spec/spec_helper.rb +80 -0
- metadata +171 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: fd219cc35083def134eddfddc6a57c5220828d20132ad7fdd05f4417c34520b6
|
|
4
|
+
data.tar.gz: 12cae4ff290aecb243ccbda04f5302863f530bf427644c9b6ce22831069c505b
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 29b5cd4b592288b6752f377fea9c6e0df8e616adf48edc36513abcb112444a0e9e319c4115ab9cf80f16fba4a25d638b2cdbb4bbbc2a24bcec97996d32d992ce
|
|
7
|
+
data.tar.gz: bda5cb72cd2c946fe0524b35bf8a845627a989c5f58e79fdcbfb6b4cc8b974552ea5db3ad83c71b1abe7d9aaa65700ad9a1cd96b0131edce7bc5ddeae7fdac9c
|
data/.rspec
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# EventSystem
|
|
2
|
+
|
|
3
|
+
A flexible, agnostic event system for Ruby applications. EventSystem provides event-driven architecture with pluggable storage backends, visualization tools, and comprehensive logging. Perfect for building decoupled, maintainable applications.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎯 **Event-Driven Architecture**: Clean separation of concerns through events
|
|
8
|
+
- 💾 **Pluggable Storage**: Memory, file, and extensible storage backends
|
|
9
|
+
- 📊 **Visualization Tools**: HTML timeline generation for debugging
|
|
10
|
+
- 🔧 **Flexible Configuration**: Easy setup and customization
|
|
11
|
+
- 🚀 **High Performance**: Optimized for both development and production
|
|
12
|
+
- 📝 **Comprehensive Logging**: Built-in logging with configurable levels
|
|
13
|
+
- 🔍 **Event Querying**: Filter and search events by type, time, and more
|
|
14
|
+
- 🎨 **Modern API**: Clean, intuitive Ruby API
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Add this line to your application's Gemfile:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
gem 'event_system'
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
And then execute:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
bundle install
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or install it directly:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
gem install event_system
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
### Basic Usage
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
require 'event_system'
|
|
42
|
+
|
|
43
|
+
# Create an event manager
|
|
44
|
+
manager = EventSystem.create_manager
|
|
45
|
+
|
|
46
|
+
# Create and publish an event
|
|
47
|
+
manager.publish_event(:user_created, self, { user_id: 123, name: "John Doe" })
|
|
48
|
+
|
|
49
|
+
# Subscribe to events
|
|
50
|
+
class UserNotifier
|
|
51
|
+
include EventSystem::EventSubscriber
|
|
52
|
+
|
|
53
|
+
def handle_event(event)
|
|
54
|
+
puts "User event: #{event.type} - #{event.data}"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
notifier = UserNotifier.new
|
|
59
|
+
manager.subscribe(:user_created, notifier)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Configuration
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
# Configure with file storage
|
|
66
|
+
config = {
|
|
67
|
+
storage_type: :file,
|
|
68
|
+
storage_options: { directory: "logs/events" },
|
|
69
|
+
session_id: "my_app_session"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
manager = EventSystem.create_manager(config)
|
|
73
|
+
|
|
74
|
+
# Or use memory storage for testing
|
|
75
|
+
manager = EventSystem.create_manager(storage_type: :memory)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Usage
|
|
79
|
+
|
|
80
|
+
### Creating Events
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
# Create an event manually
|
|
84
|
+
event = EventSystem.create_event(:order_placed, self, {
|
|
85
|
+
order_id: 456,
|
|
86
|
+
customer_id: 789,
|
|
87
|
+
amount: 99.99
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
# Publish the event
|
|
91
|
+
manager.publish(event)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Event Subscribers
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
class OrderProcessor
|
|
98
|
+
include EventSystem::EventSubscriber
|
|
99
|
+
|
|
100
|
+
def handle_event(event)
|
|
101
|
+
case event.type
|
|
102
|
+
when 'order_placed'
|
|
103
|
+
process_order(event.data)
|
|
104
|
+
when 'payment_received'
|
|
105
|
+
fulfill_order(event.data)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
private
|
|
110
|
+
|
|
111
|
+
def process_order(data)
|
|
112
|
+
puts "Processing order #{data[:order_id]}"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def fulfill_order(data)
|
|
116
|
+
puts "Fulfilling order #{data[:order_id]}"
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Subscribe to specific events
|
|
121
|
+
processor = OrderProcessor.new
|
|
122
|
+
manager.subscribe(:order_placed, processor)
|
|
123
|
+
manager.subscribe(:payment_received, processor)
|
|
124
|
+
|
|
125
|
+
# Subscribe to all events
|
|
126
|
+
manager.subscribe_all(processor)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Event Querying
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
# Query events by type
|
|
133
|
+
orders = manager.query_events(type: 'order_placed')
|
|
134
|
+
|
|
135
|
+
# Query events by time range
|
|
136
|
+
recent_events = manager.query_events(
|
|
137
|
+
start_time: 1.hour.ago,
|
|
138
|
+
end_time: Time.now
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Query with limit
|
|
142
|
+
last_10_events = manager.query_events(limit: 10)
|
|
143
|
+
|
|
144
|
+
# Query from specific session
|
|
145
|
+
session_events = manager.query_events(session_id: 'session_123')
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Storage Backends
|
|
149
|
+
|
|
150
|
+
#### Memory Storage (Default)
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
# Perfect for testing and development
|
|
154
|
+
manager = EventSystem.create_manager(storage_type: :memory)
|
|
155
|
+
|
|
156
|
+
# Switch sessions
|
|
157
|
+
manager.switch_session('test_session_1')
|
|
158
|
+
manager.publish_event(:test_event, self, { data: 'test' })
|
|
159
|
+
|
|
160
|
+
# Create new session
|
|
161
|
+
new_session = manager.create_session('my_new_session')
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### File Storage
|
|
165
|
+
|
|
166
|
+
```ruby
|
|
167
|
+
# Persistent storage to disk
|
|
168
|
+
config = {
|
|
169
|
+
storage_type: :file,
|
|
170
|
+
storage_options: { directory: 'event_logs' }
|
|
171
|
+
}
|
|
172
|
+
manager = EventSystem.create_manager(config)
|
|
173
|
+
|
|
174
|
+
# Events are automatically saved to JSONL files
|
|
175
|
+
manager.publish_event(:user_action, self, { action: 'login' })
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Visualization
|
|
179
|
+
|
|
180
|
+
```ruby
|
|
181
|
+
# Generate HTML timeline visualization
|
|
182
|
+
generator = EventSystem::Visualization::TimelineGenerator.new(manager.storage)
|
|
183
|
+
html_file = generator.generate_timeline('session_123', 'timeline.html')
|
|
184
|
+
|
|
185
|
+
# Get event summary
|
|
186
|
+
summary = generator.event_summary('session_123')
|
|
187
|
+
puts summary
|
|
188
|
+
# => {"user_created" => 5, "order_placed" => 3, "payment_received" => 2}
|
|
189
|
+
|
|
190
|
+
# Get timeline data for custom visualizations
|
|
191
|
+
timeline_data = generator.timeline_data('session_123')
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### CLI Tool
|
|
195
|
+
|
|
196
|
+
The gem includes a command-line tool for managing events:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
# Generate timeline visualization
|
|
200
|
+
event_system visualize session_123 timeline.html
|
|
201
|
+
|
|
202
|
+
# Show statistics
|
|
203
|
+
event_system stats
|
|
204
|
+
|
|
205
|
+
# List available sessions
|
|
206
|
+
event_system list
|
|
207
|
+
|
|
208
|
+
# Query events
|
|
209
|
+
event_system query session_123 order_placed
|
|
210
|
+
|
|
211
|
+
# Use file storage
|
|
212
|
+
event_system -s file -d /path/to/logs visualize
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Advanced Configuration
|
|
216
|
+
|
|
217
|
+
```ruby
|
|
218
|
+
# Custom configuration
|
|
219
|
+
config = EventSystem::Configuration.new
|
|
220
|
+
config.storage_type = :file
|
|
221
|
+
config.storage_options = { directory: 'custom_logs' }
|
|
222
|
+
config.session_id = 'production_session'
|
|
223
|
+
config.auto_flush = true
|
|
224
|
+
|
|
225
|
+
# Custom logger
|
|
226
|
+
require 'logger'
|
|
227
|
+
config.logger = Logger.new('event_system.log')
|
|
228
|
+
|
|
229
|
+
manager = EventSystem.create_manager(config)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Error Handling
|
|
233
|
+
|
|
234
|
+
```ruby
|
|
235
|
+
# Subscribers with error handling
|
|
236
|
+
class SafeSubscriber
|
|
237
|
+
include EventSystem::EventSubscriber
|
|
238
|
+
|
|
239
|
+
def handle_event(event)
|
|
240
|
+
# Your event handling logic
|
|
241
|
+
process_event(event)
|
|
242
|
+
rescue => e
|
|
243
|
+
# Errors are automatically logged by the event manager
|
|
244
|
+
puts "Error processing event: #{e.message}"
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## API Reference
|
|
250
|
+
|
|
251
|
+
### EventSystem Module
|
|
252
|
+
|
|
253
|
+
- `EventSystem.create_manager(config = nil)` - Create a new event manager
|
|
254
|
+
- `EventSystem.create_event(type, source = nil, data = {})` - Create a new event
|
|
255
|
+
- `EventSystem.version` - Get the current version
|
|
256
|
+
|
|
257
|
+
### EventManager Class
|
|
258
|
+
|
|
259
|
+
- `#publish(event)` - Publish an event
|
|
260
|
+
- `#publish_event(type, source = nil, data = {})` - Create and publish an event
|
|
261
|
+
- `#subscribe(event_type, subscriber)` - Subscribe to specific event type
|
|
262
|
+
- `#subscribe_all(subscriber)` - Subscribe to all events
|
|
263
|
+
- `#unsubscribe(event_type, subscriber)` - Unsubscribe from event type
|
|
264
|
+
- `#query_events(options = {})` - Query events with filters
|
|
265
|
+
- `#switch_session(session_id)` - Switch to different session
|
|
266
|
+
- `#create_session(session_id = nil)` - Create new session
|
|
267
|
+
- `#stats` - Get system statistics
|
|
268
|
+
- `#close` - Close the event manager
|
|
269
|
+
|
|
270
|
+
### Event Class
|
|
271
|
+
|
|
272
|
+
- `#id` - Unique event identifier
|
|
273
|
+
- `#type` - Event type
|
|
274
|
+
- `#source` - Event source
|
|
275
|
+
- `#data` - Event data
|
|
276
|
+
- `#timestamp` - Event timestamp
|
|
277
|
+
- `#to_json` - JSON representation
|
|
278
|
+
- `#to_h` - Hash representation
|
|
279
|
+
- `Event.from_json(json)` - Create event from JSON
|
|
280
|
+
|
|
281
|
+
### Storage Backends
|
|
282
|
+
|
|
283
|
+
- `EventSystem::Storage::MemoryStore` - In-memory storage
|
|
284
|
+
- `EventSystem::Storage::FileStore` - File-based storage
|
|
285
|
+
- `EventSystem::Storage::Base` - Abstract base class for custom storage
|
|
286
|
+
|
|
287
|
+
## Examples
|
|
288
|
+
|
|
289
|
+
### Web Application
|
|
290
|
+
|
|
291
|
+
```ruby
|
|
292
|
+
class WebApp
|
|
293
|
+
def initialize
|
|
294
|
+
@event_manager = EventSystem.create_manager(
|
|
295
|
+
storage_type: :file,
|
|
296
|
+
storage_options: { directory: 'logs/events' }
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
# Subscribe to web events
|
|
300
|
+
@event_manager.subscribe(:request_received, self)
|
|
301
|
+
@event_manager.subscribe(:response_sent, self)
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def handle_request(request)
|
|
305
|
+
@event_manager.publish_event(:request_received, self, {
|
|
306
|
+
path: request.path,
|
|
307
|
+
method: request.method,
|
|
308
|
+
ip: request.ip
|
|
309
|
+
})
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def handle_event(event)
|
|
313
|
+
# Log web events
|
|
314
|
+
puts "Web event: #{event.type} - #{event.data}"
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Microservice Communication
|
|
320
|
+
|
|
321
|
+
```ruby
|
|
322
|
+
class OrderService
|
|
323
|
+
def initialize
|
|
324
|
+
@event_manager = EventSystem.create_manager
|
|
325
|
+
@event_manager.subscribe(:payment_processed, self)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def place_order(order_data)
|
|
329
|
+
@event_manager.publish_event(:order_created, self, order_data)
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def handle_event(event)
|
|
333
|
+
if event.type == 'payment_processed'
|
|
334
|
+
fulfill_order(event.data[:order_id])
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Testing
|
|
341
|
+
|
|
342
|
+
```ruby
|
|
343
|
+
RSpec.describe OrderService do
|
|
344
|
+
let(:event_manager) { EventSystem.create_manager(storage_type: :memory) }
|
|
345
|
+
let(:service) { OrderService.new(event_manager) }
|
|
346
|
+
|
|
347
|
+
it 'publishes order created event' do
|
|
348
|
+
expect(event_manager).to receive(:publish_event).with(:order_created, service, anything)
|
|
349
|
+
service.place_order({ customer_id: 123 })
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## Development
|
|
355
|
+
|
|
356
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
357
|
+
|
|
358
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
359
|
+
|
|
360
|
+
## Contributing
|
|
361
|
+
|
|
362
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/davidslv/event_system.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Event Timeline - 20251027_151802</title>
|
|
7
|
+
<style>
|
|
8
|
+
body { font-family: Arial, sans-serif; margin: 20px; background-color: #f5f5f5; }
|
|
9
|
+
.container { max-width: 1200px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
|
|
10
|
+
.header { text-align: center; margin-bottom: 30px; }
|
|
11
|
+
.stats { display: flex; justify-content: space-around; margin-bottom: 30px; padding: 20px; background: #f8f9fa; border-radius: 5px; }
|
|
12
|
+
.stat { text-align: center; }
|
|
13
|
+
.stat-number { font-size: 2em; font-weight: bold; color: #007bff; }
|
|
14
|
+
.stat-label { color: #666; }
|
|
15
|
+
.timeline { position: relative; }
|
|
16
|
+
.timeline-item { margin: 20px 0; padding: 15px; border-left: 4px solid #007bff; background: #f8f9fa; border-radius: 0 5px 5px 0; }
|
|
17
|
+
.timeline-item:hover { background: #e9ecef; }
|
|
18
|
+
.event-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
|
|
19
|
+
.event-type { font-weight: bold; color: #007bff; font-size: 1.1em; }
|
|
20
|
+
.event-time { color: #666; font-size: 0.9em; }
|
|
21
|
+
.event-source { color: #28a745; font-weight: 500; }
|
|
22
|
+
.event-data { background: white; padding: 10px; border-radius: 3px; margin-top: 10px; font-family: monospace; font-size: 0.9em; }
|
|
23
|
+
.event-id { color: #6c757d; font-size: 0.8em; }
|
|
24
|
+
.filter-controls { margin-bottom: 20px; padding: 15px; background: #e9ecef; border-radius: 5px; }
|
|
25
|
+
.filter-controls input, .filter-controls select { margin: 0 10px; padding: 5px; }
|
|
26
|
+
.no-events { text-align: center; color: #666; font-style: italic; padding: 40px; }
|
|
27
|
+
</style>
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
<div class="container">
|
|
31
|
+
<div class="header">
|
|
32
|
+
<h1>Event Timeline</h1>
|
|
33
|
+
<p>Session: 20251027_151802</p>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="stats">
|
|
37
|
+
<div class="stat">
|
|
38
|
+
<div class="stat-number">3</div>
|
|
39
|
+
<div class="stat-label">Total Events</div>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="stat">
|
|
42
|
+
<div class="stat-number">3</div>
|
|
43
|
+
<div class="stat-label">Event Types</div>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="stat">
|
|
46
|
+
<div class="stat-number">3</div>
|
|
47
|
+
<div class="stat-label">Sources</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<div class="filter-controls">
|
|
52
|
+
<label>Filter by type:</label>
|
|
53
|
+
<select id="typeFilter">
|
|
54
|
+
<option value="">All Types</option>
|
|
55
|
+
<option value="user_created">user_created</option><option value="order_placed">order_placed</option><option value="payment_received">payment_received</option>
|
|
56
|
+
</select>
|
|
57
|
+
<label>Search:</label>
|
|
58
|
+
<input type="text" id="searchFilter" placeholder="Search events...">
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<div class="timeline" id="timeline">
|
|
62
|
+
<div class="timeline-item">
|
|
63
|
+
<div class="event-header">
|
|
64
|
+
<span class="event-type">user_created</span>
|
|
65
|
+
<span class="event-time">2025-10-27 15:18:02.333</span>
|
|
66
|
+
</div>
|
|
67
|
+
<div class="event-source">Source: UserService</div>
|
|
68
|
+
<div class="event-data">{
|
|
69
|
+
"user_id": 123,
|
|
70
|
+
"name": "John Doe"
|
|
71
|
+
}</div>
|
|
72
|
+
<div class="event-id">ID: ac90fa0e-e1c1-4904-862c-c23f248a4bd0</div>
|
|
73
|
+
</div>
|
|
74
|
+
<div class="timeline-item">
|
|
75
|
+
<div class="event-header">
|
|
76
|
+
<span class="event-type">order_placed</span>
|
|
77
|
+
<span class="event-time">2025-10-27 15:18:02.333</span>
|
|
78
|
+
</div>
|
|
79
|
+
<div class="event-source">Source: OrderService</div>
|
|
80
|
+
<div class="event-data">{
|
|
81
|
+
"order_id": 456,
|
|
82
|
+
"amount": 99.99
|
|
83
|
+
}</div>
|
|
84
|
+
<div class="event-id">ID: c118793c-d13d-4b5a-996b-d81ffefe3ce1</div>
|
|
85
|
+
</div>
|
|
86
|
+
<div class="timeline-item">
|
|
87
|
+
<div class="event-header">
|
|
88
|
+
<span class="event-type">payment_received</span>
|
|
89
|
+
<span class="event-time">2025-10-27 15:18:02.333</span>
|
|
90
|
+
</div>
|
|
91
|
+
<div class="event-source">Source: PaymentService</div>
|
|
92
|
+
<div class="event-data">{
|
|
93
|
+
"payment_id": 789,
|
|
94
|
+
"amount": 99.99
|
|
95
|
+
}</div>
|
|
96
|
+
<div class="event-id">ID: 7aa9eafb-b029-4c4c-8bb0-7e39c9a18a2e</div>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<script>
|
|
103
|
+
const events = [{"id":"ac90fa0e-e1c1-4904-862c-c23f248a4bd0","type":"user_created","timestamp":"2025-10-27T15:18:02.333Z","source":"UserService","data":{"user_id":123,"name":"John Doe"}},{"id":"c118793c-d13d-4b5a-996b-d81ffefe3ce1","type":"order_placed","timestamp":"2025-10-27T15:18:02.333Z","source":"OrderService","data":{"order_id":456,"amount":99.99}},{"id":"7aa9eafb-b029-4c4c-8bb0-7e39c9a18a2e","type":"payment_received","timestamp":"2025-10-27T15:18:02.333Z","source":"PaymentService","data":{"payment_id":789,"amount":99.99}}];
|
|
104
|
+
|
|
105
|
+
function filterEvents() {
|
|
106
|
+
const typeFilter = document.getElementById('typeFilter').value;
|
|
107
|
+
const searchFilter = document.getElementById('searchFilter').value.toLowerCase();
|
|
108
|
+
const timeline = document.getElementById('timeline');
|
|
109
|
+
|
|
110
|
+
const filteredEvents = events.filter(event => {
|
|
111
|
+
const typeMatch = !typeFilter || event.type === typeFilter;
|
|
112
|
+
const searchMatch = !searchFilter ||
|
|
113
|
+
event.type.toLowerCase().includes(searchFilter) ||
|
|
114
|
+
event.source.toLowerCase().includes(searchFilter) ||
|
|
115
|
+
JSON.stringify(event.data).toLowerCase().includes(searchFilter);
|
|
116
|
+
return typeMatch && searchMatch;
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
timeline.innerHTML = filteredEvents.length === 0 ?
|
|
120
|
+
'<div class="no-events">No events match the current filters</div>' :
|
|
121
|
+
filteredEvents.map(event => generateEventHTML(event)).join('');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function generateEventHTML(event) {
|
|
125
|
+
return `
|
|
126
|
+
<div class="timeline-item">
|
|
127
|
+
<div class="event-header">
|
|
128
|
+
<span class="event-type">${event.type}</span>
|
|
129
|
+
<span class="event-time">${new Date(event.timestamp).toLocaleString()}</span>
|
|
130
|
+
</div>
|
|
131
|
+
<div class="event-source">Source: ${event.source}</div>
|
|
132
|
+
<div class="event-data">${JSON.stringify(event.data, null, 2)}</div>
|
|
133
|
+
<div class="event-id">ID: ${event.id}</div>
|
|
134
|
+
</div>
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
document.getElementById('typeFilter').addEventListener('change', filterEvents);
|
|
139
|
+
document.getElementById('searchFilter').addEventListener('input', filterEvents);
|
|
140
|
+
</script>
|
|
141
|
+
</body>
|
|
142
|
+
</html>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
{"id":"3f914a9e-e799-49b3-869f-17ce78f03aa9","type":"user_created","source":"UserService","timestamp":"2025-10-27T15:17:44.021Z","data":{"user_id":123,"name":"John Doe"}}
|
|
2
|
+
{"id":"b26e2fa2-1d51-46b2-82f4-5fee6b308530","type":"order_placed","source":"OrderService","timestamp":"2025-10-27T15:17:44.022Z","data":{"order_id":456,"amount":99.99}}
|
|
3
|
+
{"id":"4ede7100-a47d-4f57-8dca-9741c33f67fc","type":"payment_received","source":"PaymentService","timestamp":"2025-10-27T15:17:44.022Z","data":{"payment_id":789,"amount":99.99}}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
{"id":"ac90fa0e-e1c1-4904-862c-c23f248a4bd0","type":"user_created","source":"UserService","timestamp":"2025-10-27T15:18:02.333Z","data":{"user_id":123,"name":"John Doe"}}
|
|
2
|
+
{"id":"c118793c-d13d-4b5a-996b-d81ffefe3ce1","type":"order_placed","source":"OrderService","timestamp":"2025-10-27T15:18:02.333Z","data":{"order_id":456,"amount":99.99}}
|
|
3
|
+
{"id":"7aa9eafb-b029-4c4c-8bb0-7e39c9a18a2e","type":"payment_received","source":"PaymentService","timestamp":"2025-10-27T15:18:02.333Z","data":{"payment_id":789,"amount":99.99}}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative '../lib/event_system'
|
|
5
|
+
|
|
6
|
+
puts "EventSystem Basic Usage Example"
|
|
7
|
+
puts "==============================="
|
|
8
|
+
|
|
9
|
+
# Create an event manager with file storage
|
|
10
|
+
config = {
|
|
11
|
+
storage_type: :file,
|
|
12
|
+
storage_options: { directory: 'example_logs' }
|
|
13
|
+
}
|
|
14
|
+
manager = EventSystem.create_manager(config)
|
|
15
|
+
|
|
16
|
+
# Create a simple subscriber
|
|
17
|
+
class ExampleSubscriber
|
|
18
|
+
include EventSystem::EventSubscriber
|
|
19
|
+
|
|
20
|
+
def initialize(name)
|
|
21
|
+
@name = name
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def handle_event(event)
|
|
25
|
+
puts "[#{@name}] Received: #{event.type} - #{event.data}"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Create subscribers
|
|
30
|
+
subscriber1 = ExampleSubscriber.new("Subscriber 1")
|
|
31
|
+
subscriber2 = ExampleSubscriber.new("Subscriber 2")
|
|
32
|
+
|
|
33
|
+
# Subscribe to events
|
|
34
|
+
manager.subscribe(:user_created, subscriber1)
|
|
35
|
+
manager.subscribe(:order_placed, subscriber2)
|
|
36
|
+
manager.subscribe_all(subscriber1) # This will receive all events
|
|
37
|
+
|
|
38
|
+
# Publish some events
|
|
39
|
+
puts "\nPublishing events..."
|
|
40
|
+
manager.publish_event(:user_created, "UserService", { user_id: 123, name: "John Doe" })
|
|
41
|
+
manager.publish_event(:order_placed, "OrderService", { order_id: 456, amount: 99.99 })
|
|
42
|
+
manager.publish_event(:payment_received, "PaymentService", { payment_id: 789, amount: 99.99 })
|
|
43
|
+
|
|
44
|
+
# Query events
|
|
45
|
+
puts "\nQuerying events..."
|
|
46
|
+
orders = manager.query_events(type: 'order_placed')
|
|
47
|
+
puts "Found #{orders.length} order events"
|
|
48
|
+
|
|
49
|
+
# Generate timeline visualization
|
|
50
|
+
puts "\nGenerating timeline visualization..."
|
|
51
|
+
generator = EventSystem::Visualization::TimelineGenerator.new(manager.storage)
|
|
52
|
+
timeline_file = generator.generate_timeline(nil, 'example_timeline.html')
|
|
53
|
+
puts "Timeline saved to: #{timeline_file}"
|
|
54
|
+
|
|
55
|
+
# Show statistics
|
|
56
|
+
puts "\nEvent System Statistics:"
|
|
57
|
+
stats = manager.stats
|
|
58
|
+
puts "Storage type: #{stats[:storage][:type]}"
|
|
59
|
+
puts "Total sessions: #{stats[:storage][:sessions]}"
|
|
60
|
+
puts "Subscribers: #{stats[:subscribers].values.sum}"
|
|
61
|
+
|
|
62
|
+
# Clean up
|
|
63
|
+
manager.close
|
|
64
|
+
puts "\nExample completed!"
|