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 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
@@ -0,0 +1,3 @@
1
+ --require spec_helper
2
+ --format documentation
3
+ --color
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,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ task default: %i[]
@@ -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!"