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.
@@ -0,0 +1,447 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'README Examples Integration Tests' do
6
+ describe 'Basic Usage Example' do
7
+ it 'demonstrates basic event creation and publishing' do
8
+ # Create an event manager
9
+ manager = EventSystem.create_manager
10
+
11
+ # Create and publish an event
12
+ event = manager.publish_event(:user_created, self, { user_id: 123, name: "John Doe" })
13
+
14
+ expect(event).to be_a(EventSystem::Event)
15
+ expect(event.type).to eq('user_created')
16
+ expect(event.data[:user_id]).to eq(123)
17
+ expect(event.data[:name]).to eq('John Doe')
18
+ end
19
+
20
+ it 'demonstrates event subscription' do
21
+ manager = EventSystem.create_manager
22
+
23
+ # Subscribe to events
24
+ class UserNotifier
25
+ include EventSystem::EventSubscriber
26
+
27
+ attr_reader :events_received
28
+
29
+ def initialize
30
+ @events_received = []
31
+ end
32
+
33
+ def handle_event(event)
34
+ @events_received << event
35
+ end
36
+ end
37
+
38
+ notifier = UserNotifier.new
39
+ manager.subscribe(:user_created, notifier)
40
+
41
+ # Publish event
42
+ manager.publish_event(:user_created, self, { user_id: 123, name: "John Doe" })
43
+
44
+ expect(notifier.events_received.length).to eq(1)
45
+ expect(notifier.events_received.first.type).to eq('user_created')
46
+ end
47
+ end
48
+
49
+ describe 'Configuration Example' do
50
+ it 'demonstrates file storage configuration' do
51
+ # Configure with file storage
52
+ config = {
53
+ storage_type: :file,
54
+ storage_options: { directory: @temp_dir },
55
+ session_id: "my_app_session"
56
+ }
57
+
58
+ manager = EventSystem.create_manager(config)
59
+
60
+ expect(manager.config.storage_type).to eq(:file)
61
+ expect(manager.config.storage_options[:directory]).to eq(@temp_dir)
62
+ expect(manager.config.session_id).to eq("my_app_session")
63
+ end
64
+
65
+ it 'demonstrates memory storage for testing' do
66
+ manager = EventSystem.create_manager(storage_type: :memory)
67
+ expect(manager.config.storage_type).to eq(:memory)
68
+ end
69
+ end
70
+
71
+ describe 'Event Creation Example' do
72
+ it 'demonstrates manual event creation' do
73
+ manager = EventSystem.create_manager
74
+
75
+ # Create an event manually
76
+ event = EventSystem.create_event(:order_placed, self, {
77
+ order_id: 456,
78
+ customer_id: 789,
79
+ amount: 99.99
80
+ })
81
+
82
+ expect(event.type).to eq('order_placed')
83
+ expect(event.data[:order_id]).to eq(456)
84
+ expect(event.data[:customer_id]).to eq(789)
85
+ expect(event.data[:amount]).to eq(99.99)
86
+
87
+ # Publish the event
88
+ manager.publish(event)
89
+ end
90
+ end
91
+
92
+ describe 'Event Subscribers Example' do
93
+ it 'demonstrates order processing with multiple event types' do
94
+ manager = EventSystem.create_manager
95
+
96
+ class OrderProcessor
97
+ include EventSystem::EventSubscriber
98
+
99
+ attr_reader :processed_orders, :fulfilled_orders
100
+
101
+ def initialize
102
+ @processed_orders = []
103
+ @fulfilled_orders = []
104
+ end
105
+
106
+ def handle_event(event)
107
+ case event.type
108
+ when 'order_placed'
109
+ process_order(event.data)
110
+ when 'payment_received'
111
+ fulfill_order(event.data)
112
+ end
113
+ end
114
+
115
+ private
116
+
117
+ def process_order(data)
118
+ @processed_orders << data
119
+ end
120
+
121
+ def fulfill_order(data)
122
+ @fulfilled_orders << data
123
+ end
124
+ end
125
+
126
+ processor = OrderProcessor.new
127
+ manager.subscribe(:order_placed, processor)
128
+ manager.subscribe(:payment_received, processor)
129
+
130
+ # Publish events
131
+ manager.publish_event(:order_placed, 'OrderService', { order_id: 123, amount: 50.0 })
132
+ manager.publish_event(:payment_received, 'PaymentService', { order_id: 123, amount: 50.0 })
133
+
134
+ expect(processor.processed_orders.length).to eq(1)
135
+ expect(processor.fulfilled_orders.length).to eq(1)
136
+ expect(processor.processed_orders.first[:order_id]).to eq(123)
137
+ end
138
+
139
+ it 'demonstrates subscribe_all functionality' do
140
+ manager = EventSystem.create_manager
141
+
142
+ class UniversalSubscriber
143
+ include EventSystem::EventSubscriber
144
+
145
+ attr_reader :all_events
146
+
147
+ def initialize
148
+ @all_events = []
149
+ end
150
+
151
+ def handle_event(event)
152
+ @all_events << event
153
+ end
154
+ end
155
+
156
+ subscriber = UniversalSubscriber.new
157
+ manager.subscribe_all(subscriber)
158
+
159
+ # Publish different event types
160
+ manager.publish_event(:event1, 'Source1', { data: 'value1' })
161
+ manager.publish_event(:event2, 'Source2', { data: 'value2' })
162
+
163
+ expect(subscriber.all_events.length).to eq(2)
164
+ expect(subscriber.all_events.map(&:type)).to include('event1', 'event2')
165
+ end
166
+ end
167
+
168
+ describe 'Event Querying Example' do
169
+ it 'demonstrates various querying capabilities' do
170
+ manager = create_file_manager
171
+
172
+ # Publish various events
173
+ manager.publish_event(:order_placed, 'OrderService', { order_id: 1, amount: 100 })
174
+ manager.publish_event(:order_placed, 'OrderService', { order_id: 2, amount: 200 })
175
+ manager.publish_event(:user_created, 'UserService', { user_id: 1, name: 'John' })
176
+
177
+ # Query events by type
178
+ orders = manager.query_events(type: 'order_placed')
179
+ expect(orders.length).to eq(2)
180
+ expect(orders.all? { |e| e.type == 'order_placed' }).to be true
181
+
182
+ # Query events by time range
183
+ start_time = Time.now - 3600 # 1 hour in seconds
184
+ end_time = Time.now + 3600 # 1 hour in seconds
185
+ recent_events = manager.query_events(start_time: start_time, end_time: end_time)
186
+ expect(recent_events.length).to eq(3)
187
+
188
+ # Query with limit
189
+ last_2_events = manager.query_events(limit: 2)
190
+ expect(last_2_events.length).to eq(2)
191
+
192
+ # Query from specific session
193
+ session_events = manager.query_events(session_id: manager.current_session)
194
+ expect(session_events.length).to eq(3)
195
+ end
196
+ end
197
+
198
+ describe 'Storage Backends Example' do
199
+ it 'demonstrates memory storage for testing' do
200
+ manager = EventSystem.create_manager(storage_type: :memory)
201
+
202
+ # Switch sessions
203
+ manager.switch_session('test_session_1')
204
+ manager.publish_event(:test_event, self, { data: 'test' })
205
+
206
+ # Create new session
207
+ new_session = manager.create_session('my_new_session')
208
+ expect(new_session).to eq('my_new_session')
209
+ expect(manager.current_session).to eq('my_new_session')
210
+ end
211
+
212
+ it 'demonstrates file storage persistence' do
213
+ config = {
214
+ storage_type: :file,
215
+ storage_options: { directory: @temp_dir }
216
+ }
217
+ manager = EventSystem.create_manager(config)
218
+
219
+ # Events are automatically saved to JSONL files
220
+ manager.publish_event(:user_action, self, { action: 'login' })
221
+
222
+ # Verify file was created
223
+ session_file = File.join(@temp_dir, "events_#{manager.current_session}.jsonl")
224
+ expect(File.exist?(session_file)).to be true
225
+
226
+ # Verify content
227
+ content = File.read(session_file)
228
+ expect(content).to include('user_action')
229
+ expect(content).to include('login')
230
+ end
231
+ end
232
+
233
+ describe 'Visualization Example' do
234
+ it 'demonstrates timeline generation' do
235
+ manager = create_file_manager
236
+
237
+ # Add some events
238
+ manager.publish_event(:user_created, 'UserService', { user_id: 123, name: 'John Doe' })
239
+ manager.publish_event(:order_placed, 'OrderService', { order_id: 456, amount: 99.99 })
240
+
241
+ # Generate timeline visualization
242
+ generator = EventSystem::Visualization::TimelineGenerator.new(manager.storage)
243
+ html_file = generator.generate_timeline(nil, 'timeline.html')
244
+
245
+ expect(html_file).to be_a(String)
246
+ expect(File.exist?(html_file)).to be true
247
+ expect(html_file).to end_with('.html')
248
+ end
249
+
250
+ it 'demonstrates event summary' do
251
+ manager = create_file_manager
252
+
253
+ # Add events
254
+ manager.publish_event(:user_created, 'UserService', { user_id: 1 })
255
+ manager.publish_event(:user_created, 'UserService', { user_id: 2 })
256
+ manager.publish_event(:order_placed, 'OrderService', { order_id: 1 })
257
+
258
+ generator = EventSystem::Visualization::TimelineGenerator.new(manager.storage)
259
+ summary = generator.event_summary
260
+
261
+ expect(summary).to be_a(Hash)
262
+ expect(summary['user_created']).to eq(2)
263
+ expect(summary['order_placed']).to eq(1)
264
+ end
265
+
266
+ it 'demonstrates timeline data export' do
267
+ manager = create_file_manager
268
+
269
+ manager.publish_event(:test_event, 'TestSource', { key: 'value' })
270
+
271
+ generator = EventSystem::Visualization::TimelineGenerator.new(manager.storage)
272
+ timeline_data = generator.timeline_data
273
+
274
+ expect(timeline_data).to be_an(Array)
275
+ expect(timeline_data.length).to eq(1)
276
+
277
+ event_data = timeline_data.first
278
+ expect(event_data[:type]).to eq('test_event')
279
+ expect(event_data[:source]).to eq('TestSource')
280
+ expect(event_data[:data][:key]).to eq('value')
281
+ end
282
+ end
283
+
284
+ describe 'Advanced Configuration Example' do
285
+ it 'demonstrates custom configuration' do
286
+ # Custom configuration
287
+ config = EventSystem::Configuration.new
288
+ config.storage_type = :file
289
+ config.storage_options = { directory: @temp_dir }
290
+ config.session_id = 'production_session'
291
+ config.auto_flush = true
292
+
293
+ manager = EventSystem.create_manager(config)
294
+
295
+ expect(manager.config.storage_type).to eq(:file)
296
+ expect(manager.config.storage_options[:directory]).to eq(@temp_dir)
297
+ expect(manager.config.session_id).to eq('production_session')
298
+ expect(manager.config.auto_flush).to be true
299
+ end
300
+
301
+ it 'demonstrates custom logger configuration' do
302
+ require 'logger'
303
+ log_io = StringIO.new
304
+ config = EventSystem::Configuration.new
305
+ config.logger = Logger.new(log_io)
306
+
307
+ manager = EventSystem.create_manager(config)
308
+
309
+ # Publish an event to trigger logging
310
+ manager.publish_event(:test_event, self, { data: 'test' })
311
+
312
+ # Check that logging occurred
313
+ log_content = log_io.string
314
+ expect(log_content).to include('EventSystem initialized')
315
+ expect(log_content).to include('Publishing event')
316
+ end
317
+ end
318
+
319
+ describe 'Error Handling Example' do
320
+ it 'demonstrates safe subscriber error handling' do
321
+ manager = EventSystem.create_manager
322
+
323
+ class SafeSubscriber
324
+ include EventSystem::EventSubscriber
325
+
326
+ attr_reader :errors_handled
327
+
328
+ def initialize
329
+ @errors_handled = []
330
+ end
331
+
332
+ def handle_event(event)
333
+ if event.data[:should_fail]
334
+ raise StandardError, 'Test error'
335
+ end
336
+ end
337
+ end
338
+
339
+ safe_subscriber = SafeSubscriber.new
340
+ manager.subscribe(:test_event, safe_subscriber)
341
+
342
+ # Publish event that should not fail
343
+ manager.publish_event(:test_event, self, { should_fail: false })
344
+
345
+ # Publish event that should fail (but be handled gracefully)
346
+ expect {
347
+ manager.publish_event(:test_event, self, { should_fail: true })
348
+ }.not_to raise_error
349
+ end
350
+ end
351
+
352
+ describe 'Web Application Example' do
353
+ it 'demonstrates web application event handling' do
354
+ class WebApp
355
+ attr_reader :event_manager, :events_logged
356
+
357
+ def initialize
358
+ @event_manager = EventSystem.create_manager(
359
+ storage_type: :file,
360
+ storage_options: { directory: @temp_dir }
361
+ )
362
+ @events_logged = []
363
+
364
+ # Subscribe to web events
365
+ @event_manager.subscribe(:request_received, self)
366
+ @event_manager.subscribe(:response_sent, self)
367
+ end
368
+
369
+ def handle_request(request)
370
+ @event_manager.publish_event(:request_received, self, {
371
+ path: request[:path],
372
+ method: request[:method],
373
+ ip: request[:ip]
374
+ })
375
+ end
376
+
377
+ def handle_event(event)
378
+ @events_logged << event
379
+ end
380
+ end
381
+
382
+ web_app = WebApp.new
383
+
384
+ # Simulate request
385
+ request = { path: '/api/users', method: 'GET', ip: '127.0.0.1' }
386
+ web_app.handle_request(request)
387
+
388
+ expect(web_app.events_logged.length).to eq(1)
389
+ event = web_app.events_logged.first
390
+ expect(event.type).to eq('request_received')
391
+ expect(event.data[:path]).to eq('/api/users')
392
+ expect(event.data[:method]).to eq('GET')
393
+ end
394
+ end
395
+
396
+ describe 'Microservice Communication Example' do
397
+ it 'demonstrates microservice event communication' do
398
+ class OrderService
399
+ attr_reader :event_manager, :orders_fulfilled
400
+
401
+ def initialize
402
+ @event_manager = EventSystem.create_manager
403
+ @orders_fulfilled = []
404
+ @event_manager.subscribe(:payment_processed, self)
405
+ end
406
+
407
+ def place_order(order_data)
408
+ @event_manager.publish_event(:order_created, self, order_data)
409
+ end
410
+
411
+ def handle_event(event)
412
+ if event.type == 'payment_processed'
413
+ fulfill_order(event.data[:order_id])
414
+ end
415
+ end
416
+
417
+ private
418
+
419
+ def fulfill_order(order_id)
420
+ @orders_fulfilled << order_id
421
+ end
422
+ end
423
+
424
+ order_service = OrderService.new
425
+
426
+ # Place an order
427
+ order_service.place_order({ customer_id: 123, amount: 100 })
428
+
429
+ # Simulate payment processing
430
+ order_service.event_manager.publish_event(:payment_processed, 'PaymentService', { order_id: 1 })
431
+
432
+ expect(order_service.orders_fulfilled).to include(1)
433
+ end
434
+ end
435
+
436
+ describe 'Testing Example' do
437
+ it 'demonstrates testing with memory storage' do
438
+ event_manager = EventSystem.create_manager(storage_type: :memory)
439
+
440
+ # Mock the publish_event method to verify it's called
441
+ expect(event_manager).to receive(:publish_event).with(:order_created, anything, anything)
442
+
443
+ # Simulate service behavior
444
+ event_manager.publish_event(:order_created, 'TestService', { customer_id: 123 })
445
+ end
446
+ end
447
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'event_system'
5
+ require 'rspec'
6
+ require 'tempfile'
7
+ require 'fileutils'
8
+
9
+ RSpec.configure do |config|
10
+ # Enable flags like --only-failures and --next-failure
11
+ config.example_status_persistence_file_path = '.rspec_status'
12
+
13
+ # Disable RSpec exposing methods globally on `Module` and `main`
14
+ config.disable_monkey_patching!
15
+
16
+ config.expect_with :rspec do |c|
17
+ c.syntax = :expect
18
+ end
19
+
20
+ # Clean up temporary files after each test
21
+ config.after(:each) do
22
+ # Clean up any temporary files created during tests
23
+ if defined?(@temp_dir) && @temp_dir && Dir.exist?(@temp_dir)
24
+ FileUtils.rm_rf(@temp_dir)
25
+ end
26
+ end
27
+
28
+ # Helper method to create a temporary directory for file storage tests
29
+ config.before(:each) do
30
+ @temp_dir = Dir.mktmpdir('event_system_test')
31
+ end
32
+ end
33
+
34
+ # Helper methods for testing
35
+ module EventSystemTestHelpers
36
+ def create_test_event(type = :test_event, source = 'TestSource', data = {})
37
+ EventSystem::Event.new(type, source, data)
38
+ end
39
+
40
+ def create_test_manager(storage_type = :memory, options = {})
41
+ config = {
42
+ storage_type: storage_type,
43
+ storage_options: options
44
+ }
45
+ EventSystem.create_manager(config)
46
+ end
47
+
48
+ def create_file_manager(temp_dir = nil)
49
+ temp_dir ||= @temp_dir
50
+ create_test_manager(:file, { directory: temp_dir })
51
+ end
52
+
53
+ def create_test_subscriber(name = 'TestSubscriber')
54
+ Class.new do
55
+ include EventSystem::EventSubscriber
56
+ attr_reader :name, :events_received
57
+
58
+ def initialize(name)
59
+ @name = name
60
+ @events_received = []
61
+ end
62
+
63
+ def handle_event(event)
64
+ @events_received << event
65
+ end
66
+
67
+ def handles_event_type?(event_type)
68
+ true
69
+ end
70
+
71
+ def event_priority
72
+ 0
73
+ end
74
+ end.new(name)
75
+ end
76
+ end
77
+
78
+ RSpec.configure do |config|
79
+ config.include EventSystemTestHelpers
80
+ end
metadata ADDED
@@ -0,0 +1,171 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: event_system
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - David Silva
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: json
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rspec
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rubocop
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: yard
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.9'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.9'
68
+ - !ruby/object:Gem::Dependency
69
+ name: simplecov
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.21'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.21'
82
+ - !ruby/object:Gem::Dependency
83
+ name: redis
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '4.0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '4.0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: pg
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '1.0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '1.0'
110
+ description: Event-driven architecture with pluggable storage backends, visualization
111
+ tools, and comprehensive logging. Perfect for building decoupled, maintainable applications.
112
+ email:
113
+ - davidslv@users.noreply.github.com
114
+ executables:
115
+ - event_system
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - ".rspec"
120
+ - README.md
121
+ - Rakefile
122
+ - docs/event_visualizations/example_timeline.html
123
+ - docs/example_logs/events_20251027_151744.jsonl
124
+ - docs/example_logs/events_20251027_151802.jsonl
125
+ - docs/examples/basic_usage.rb
126
+ - exe/event_system
127
+ - lib/event_system.rb
128
+ - lib/event_system/configuration.rb
129
+ - lib/event_system/event.rb
130
+ - lib/event_system/event_manager.rb
131
+ - lib/event_system/event_subscriber.rb
132
+ - lib/event_system/storage/base.rb
133
+ - lib/event_system/storage/file_store.rb
134
+ - lib/event_system/storage/memory_store.rb
135
+ - lib/event_system/version.rb
136
+ - lib/event_system/visualization/timeline_generator.rb
137
+ - spec/event_system/configuration_spec.rb
138
+ - spec/event_system/event_manager_spec.rb
139
+ - spec/event_system/event_spec.rb
140
+ - spec/event_system/event_subscriber_spec.rb
141
+ - spec/event_system/storage/file_store_spec.rb
142
+ - spec/event_system/storage/memory_store_spec.rb
143
+ - spec/event_system/visualization/timeline_generator_spec.rb
144
+ - spec/event_system_spec.rb
145
+ - spec/integration/readme_examples_spec.rb
146
+ - spec/spec_helper.rb
147
+ homepage: https://github.com/davidslv/event_system
148
+ licenses:
149
+ - MIT
150
+ metadata:
151
+ allowed_push_host: https://rubygems.org
152
+ homepage_uri: https://github.com/davidslv/event_system
153
+ source_code_uri: https://github.com/davidslv/event_system
154
+ rdoc_options: []
155
+ require_paths:
156
+ - lib
157
+ required_ruby_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: 2.7.0
162
+ required_rubygems_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ requirements: []
168
+ rubygems_version: 3.6.9
169
+ specification_version: 4
170
+ summary: A flexible, agnostic event system for Ruby applications
171
+ test_files: []