smart_message 0.0.13 → 0.0.16

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +120 -0
  4. data/Gemfile.lock +3 -3
  5. data/README.md +71 -25
  6. data/docs/index.md +2 -0
  7. data/docs/reference/transports.md +46 -21
  8. data/docs/transports/memory-transport.md +2 -1
  9. data/docs/transports/multi-transport.md +484 -0
  10. data/examples/file/00_run_all_file_demos.rb +260 -0
  11. data/examples/file/01_basic_file_transport_demo.rb +237 -0
  12. data/examples/file/02_fifo_transport_demo.rb +289 -0
  13. data/examples/file/03_file_watching_demo.rb +332 -0
  14. data/examples/file/04_multi_transport_file_demo.rb +432 -0
  15. data/examples/file/README.md +257 -0
  16. data/examples/memory/00_run_all_demos.rb +317 -0
  17. data/examples/memory/01_message_deduplication_demo.rb +18 -30
  18. data/examples/memory/02_dead_letter_queue_demo.rb +9 -9
  19. data/examples/memory/03_point_to_point_orders.rb +3 -3
  20. data/examples/memory/04_publish_subscribe_events.rb +15 -15
  21. data/examples/memory/05_many_to_many_chat.rb +19 -19
  22. data/examples/memory/06_stdout_publish_only.rb +145 -0
  23. data/examples/memory/07_proc_handlers_demo.rb +13 -13
  24. data/examples/memory/08_custom_logger_demo.rb +136 -136
  25. data/examples/memory/09_error_handling_demo.rb +7 -7
  26. data/examples/memory/10_entity_addressing_basic.rb +25 -25
  27. data/examples/memory/11_entity_addressing_with_filtering.rb +32 -32
  28. data/examples/memory/12_regex_filtering_microservices.rb +10 -10
  29. data/examples/memory/14_global_configuration_demo.rb +12 -12
  30. data/examples/memory/README.md +34 -17
  31. data/examples/memory/log/demo_app.log.1 +100 -0
  32. data/examples/memory/log/demo_app.log.2 +100 -0
  33. data/examples/multi_transport_example.rb +114 -0
  34. data/examples/redis/01_smart_home_iot_demo.rb +20 -20
  35. data/examples/utilities/box_it.rb +12 -0
  36. data/examples/utilities/doing.rb +19 -0
  37. data/examples/utilities/temp.md +28 -0
  38. data/lib/smart_message/base.rb +5 -7
  39. data/lib/smart_message/errors.rb +3 -0
  40. data/lib/smart_message/header.rb +1 -1
  41. data/lib/smart_message/logger/default.rb +1 -1
  42. data/lib/smart_message/messaging.rb +36 -6
  43. data/lib/smart_message/plugins.rb +46 -4
  44. data/lib/smart_message/serializer/base.rb +1 -1
  45. data/lib/smart_message/serializer.rb +3 -2
  46. data/lib/smart_message/subscription.rb +18 -20
  47. data/lib/smart_message/transport/async_publish_queue.rb +284 -0
  48. data/lib/smart_message/transport/fifo_operations.rb +264 -0
  49. data/lib/smart_message/transport/file_operations.rb +200 -0
  50. data/lib/smart_message/transport/file_transport.rb +149 -0
  51. data/lib/smart_message/transport/file_watching.rb +72 -0
  52. data/lib/smart_message/transport/partitioned_files.rb +46 -0
  53. data/lib/smart_message/transport/stdout_transport.rb +50 -36
  54. data/lib/smart_message/transport/stdout_transport.rb.backup +88 -0
  55. data/lib/smart_message/version.rb +1 -1
  56. metadata +24 -10
  57. data/ideas/README.md +0 -41
  58. data/ideas/agents.md +0 -1001
  59. data/ideas/database_transport.md +0 -980
  60. data/ideas/improvement.md +0 -359
  61. data/ideas/meshage.md +0 -1788
  62. data/ideas/message_discovery.md +0 -178
  63. data/ideas/message_schema.md +0 -1381
  64. data/lib/smart_message/wrapper.rb.bak +0 -132
  65. /data/examples/memory/{06_pretty_print_demo.rb → 16_pretty_print_demo.rb} +0 -0
@@ -0,0 +1,289 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ # frozen_string_literal: true
4
+
5
+ # FIFO Transport Demo
6
+ #
7
+ # This example demonstrates SmartMessage's FileTransport with FIFO (named pipes).
8
+ # FIFOs enable real-time, process-to-process communication through the filesystem.
9
+
10
+ require_relative '../../lib/smart_message'
11
+ require 'tempfile'
12
+ require 'fileutils'
13
+
14
+ # Define message classes
15
+ Dir.mkdir('messages') unless Dir.exist?('messages')
16
+
17
+ class PipeMessage < SmartMessage::Base
18
+ property :sender, required: true
19
+ property :content, required: true
20
+ property :timestamp, default: -> { Time.now.iso8601 }
21
+ property :priority, default: 'normal'
22
+
23
+ from 'fifo_transport_demo'
24
+ end
25
+
26
+ class StatusUpdate < SmartMessage::Base
27
+ property :service, required: true
28
+ property :status, required: true, valid: %w[starting running stopping stopped error]
29
+ property :details, default: {}
30
+ property :timestamp, default: -> { Time.now.iso8601 }
31
+
32
+ from 'fifo_transport_demo'
33
+ end
34
+
35
+ puts "=== SmartMessage FIFO Transport Demo ==="
36
+ puts
37
+
38
+ # Check if FIFOs are supported on this system
39
+ def fifo_supported?
40
+ return @fifo_supported if defined?(@fifo_supported)
41
+
42
+ test_dir = Dir.mktmpdir('fifo_test')
43
+ test_fifo = File.join(test_dir, 'test.fifo')
44
+
45
+ result = system("mkfifo #{test_fifo} 2>/dev/null")
46
+ @fifo_supported = result && File.exist?(test_fifo) && File.ftype(test_fifo) == "fifo"
47
+
48
+ FileUtils.rm_rf(test_dir)
49
+ @fifo_supported
50
+ rescue
51
+ @fifo_supported = false
52
+ end
53
+
54
+ unless fifo_supported?
55
+ puts "❌ FIFOs are not supported on this system"
56
+ puts "This demo requires Unix-like systems with mkfifo support"
57
+ exit 1
58
+ end
59
+
60
+ demo_dir = Dir.mktmpdir('fifo_demo')
61
+ puts "Demo directory: #{demo_dir}"
62
+
63
+ begin
64
+ # Example 1: Basic FIFO Communication
65
+ puts "\n1. Basic FIFO Communication"
66
+ puts "=" * 35
67
+
68
+ fifo_path = File.join(demo_dir, 'message_pipe.fifo')
69
+ system("mkfifo #{fifo_path}")
70
+
71
+ puts "✓ Created FIFO: #{fifo_path}"
72
+ puts "FIFO type: #{File.ftype(fifo_path)}"
73
+
74
+ # Configure FIFO transport
75
+ fifo_transport = SmartMessage::Transport::FileTransport.new(
76
+ file_path: fifo_path,
77
+ file_type: :fifo,
78
+ format: :json,
79
+ enable_subscriptions: true,
80
+ subscription_mode: :fifo_blocking
81
+ )
82
+
83
+ PipeMessage.class_eval do
84
+ transport fifo_transport
85
+ end
86
+
87
+ # Set up a reader in a separate process
88
+ reader_pid = fork do
89
+ puts "[Reader] Starting FIFO reader process..."
90
+
91
+ # Subscribe to messages
92
+ received_messages = []
93
+ fifo_transport.subscribe('PipeMessage', ->(msg) {
94
+ received_messages << msg
95
+ puts "[Reader] Received: #{msg.inspect}"
96
+ }, {})
97
+
98
+ # Keep reading for a short time
99
+ sleep 3
100
+
101
+ puts "[Reader] Received #{received_messages.length} messages"
102
+ exit 0
103
+ end
104
+
105
+ # Give reader time to start
106
+ sleep 0.5
107
+
108
+ # Send messages from parent process
109
+ puts "[Writer] Sending messages through FIFO..."
110
+
111
+ PipeMessage.new(
112
+ sender: 'process_1',
113
+ content: 'Hello from the writer process!'
114
+ ).publish
115
+
116
+ PipeMessage.new(
117
+ sender: 'process_1',
118
+ content: 'This is message #2',
119
+ priority: 'high'
120
+ ).publish
121
+
122
+ PipeMessage.new(
123
+ sender: 'process_1',
124
+ content: 'Final message before closing'
125
+ ).publish
126
+
127
+ # Wait for reader to finish
128
+ Process.wait(reader_pid)
129
+ puts "✓ FIFO communication completed"
130
+
131
+ # Example 2: Non-blocking FIFO Operations
132
+ puts "\n2. Non-blocking FIFO Operations"
133
+ puts "=" * 35
134
+
135
+ nb_fifo_path = File.join(demo_dir, 'nonblocking.fifo')
136
+ system("mkfifo #{nb_fifo_path}")
137
+
138
+ # Configure non-blocking transport
139
+ nb_transport = SmartMessage::Transport::FileTransport.new(
140
+ file_path: nb_fifo_path,
141
+ file_type: :fifo,
142
+ format: :json,
143
+ fifo_mode: :non_blocking
144
+ )
145
+
146
+ StatusUpdate.class_eval do
147
+ transport nb_transport
148
+ end
149
+
150
+ puts "✓ Created non-blocking FIFO: #{nb_fifo_path}"
151
+
152
+ # Start a background reader
153
+ reader_thread = Thread.new do
154
+ sleep 0.2 # Let writer get ahead
155
+
156
+ begin
157
+ File.open(nb_fifo_path, 'r') do |fifo|
158
+ puts "[Reader] Opened FIFO for reading"
159
+ while line = fifo.gets
160
+ puts "[Reader] Read: #{line.chomp}"
161
+ end
162
+ end
163
+ rescue => e
164
+ puts "[Reader] Error: #{e.message}"
165
+ end
166
+ end
167
+
168
+ # Send status updates
169
+ puts "[Writer] Sending status updates..."
170
+
171
+ %w[starting running stopping stopped].each_with_index do |status, i|
172
+ StatusUpdate.new(
173
+ service: 'web_server',
174
+ status: status,
175
+ details: { pid: 1234 + i, memory: "#{50 + i * 10}MB" }
176
+ ).publish
177
+
178
+ sleep 0.1 # Small delay between messages
179
+ end
180
+
181
+ # Close the FIFO from writer side to signal EOF
182
+ nb_transport.disconnect
183
+
184
+ reader_thread.join(2) # Wait up to 2 seconds
185
+ puts "✓ Non-blocking FIFO operations completed"
186
+
187
+ # Example 3: FIFO Concepts Demonstration (Non-blocking)
188
+ puts "\n3. FIFO Concepts Demonstration (Non-blocking)"
189
+ puts "=" * 50
190
+
191
+ simple_fifo_path = File.join(demo_dir, 'simple.fifo')
192
+ system("mkfifo #{simple_fifo_path}")
193
+
194
+ puts "✓ Created FIFO: #{simple_fifo_path}"
195
+ puts "✓ Verified FIFO type: #{File.ftype(simple_fifo_path)}"
196
+
197
+ puts "\nFIFO Characteristics:"
198
+ puts " - Named pipes for inter-process communication"
199
+ puts " - Blocking by default (writers wait for readers)"
200
+ puts " - First In, First Out data flow"
201
+ puts " - Persistent in filesystem until deleted"
202
+
203
+ puts "\nDemonstrating FIFO creation and properties..."
204
+ puts " - FIFO exists: #{File.exist?(simple_fifo_path)}"
205
+ puts " - FIFO permissions: #{File.stat(simple_fifo_path).mode.to_s(8)}"
206
+ puts " - FIFO size: #{File.size(simple_fifo_path)} bytes (always 0 for FIFOs)"
207
+
208
+ # Show transport configuration without actual message sending
209
+ simple_transport = SmartMessage::Transport::FileTransport.new(
210
+ file_path: simple_fifo_path,
211
+ file_type: :fifo,
212
+ format: :json
213
+ )
214
+
215
+ puts "\nFileTransport FIFO Configuration:"
216
+ puts " - File path: #{simple_transport.instance_variable_get(:@file_path) rescue 'configured'}"
217
+ puts " - Format: JSON"
218
+ puts " - Type: FIFO (named pipe)"
219
+ puts " - Note: Actual message sending requires concurrent reader process"
220
+
221
+ puts "\n✓ FIFO concepts demonstration completed"
222
+
223
+ # Example 4: Message Priority Classification (File-based)
224
+ puts "\n4. Message Priority Classification (File-based)"
225
+ puts "=" * 55
226
+
227
+ # Use regular files instead of FIFOs to avoid blocking
228
+ priority_file = File.join(demo_dir, 'priority_messages.log')
229
+
230
+ priority_transport = SmartMessage::Transport::FileTransport.new(
231
+ file_path: priority_file,
232
+ format: :json
233
+ )
234
+
235
+ # Create a priority message class
236
+ class PriorityMessage < SmartMessage::Base
237
+ property :level, required: true, valid: %w[low normal high critical]
238
+ property :message, required: true
239
+ property :component, required: true
240
+
241
+ from 'fifo_transport_demo'
242
+ end
243
+
244
+ PriorityMessage.class_eval do
245
+ transport priority_transport
246
+ end
247
+
248
+ puts "Demonstrating priority message classification and logging..."
249
+
250
+ # Send mixed priority messages
251
+ messages = [
252
+ { level: 'low', message: 'Debug information', component: 'logger' },
253
+ { level: 'normal', message: 'User login successful', component: 'auth' },
254
+ { level: 'high', message: 'Database connection slow', component: 'db' },
255
+ { level: 'normal', message: 'Processing request', component: 'api' },
256
+ { level: 'critical', message: 'Out of memory!', component: 'system' },
257
+ { level: 'high', message: 'Security alert detected', component: 'security' },
258
+ { level: 'low', message: 'Cache miss', component: 'cache' }
259
+ ]
260
+
261
+ puts "Processing and classifying priority messages..."
262
+ high_priority_count = 0
263
+ messages.each_with_index do |msg_data, i|
264
+ msg = PriorityMessage.new(**msg_data)
265
+ msg.publish
266
+
267
+ if %w[high critical].include?(msg_data[:level])
268
+ high_priority_count += 1
269
+ puts " → [HIGH PRIORITY] #{msg_data[:level].upcase}: #{msg_data[:component]} - #{msg_data[:message]}"
270
+ else
271
+ puts " → [#{msg_data[:level]}]: #{msg_data[:component]} - #{msg_data[:message]}"
272
+ end
273
+ end
274
+
275
+ puts "\nPriority Log Contents:"
276
+ puts File.read(priority_file)
277
+
278
+ puts "✓ Processed #{messages.length} messages (#{high_priority_count} high priority)"
279
+ puts "✓ Message priority classification completed"
280
+
281
+ rescue => e
282
+ puts "Error: #{e.message}"
283
+ puts e.backtrace.first(3)
284
+ ensure
285
+ # Cleanup files
286
+ FileUtils.rm_rf(demo_dir) if Dir.exist?(demo_dir)
287
+ FileUtils.rm_rf('messages') if Dir.exist?('messages')
288
+ puts "\n✓ FIFO demo completed and cleaned up"
289
+ end
@@ -0,0 +1,332 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ # frozen_string_literal: true
4
+
5
+ # File Watching Demo
6
+ #
7
+ # This example demonstrates SmartMessage's FileTransport file watching capabilities.
8
+ # It shows how to monitor files for changes and automatically process new content.
9
+
10
+ require_relative '../../lib/smart_message'
11
+ require 'tempfile'
12
+ require 'fileutils'
13
+
14
+ # Define message classes
15
+ Dir.mkdir('messages') unless Dir.exist?('messages')
16
+
17
+ class LogEntry < SmartMessage::Base
18
+ property :timestamp, required: true
19
+ property :level, required: true
20
+ property :message, required: true
21
+ property :source_file, required: true
22
+
23
+ from 'file_watching_demo'
24
+ end
25
+
26
+ class ConfigChange < SmartMessage::Base
27
+ property :setting, required: true
28
+ property :old_value
29
+ property :new_value, required: true
30
+ property :changed_at, default: -> { Time.now.iso8601 }
31
+
32
+ from 'file_watching_demo'
33
+ end
34
+
35
+ puts "=== SmartMessage File Watching Demo ==="
36
+ puts
37
+
38
+ demo_dir = Dir.mktmpdir('file_watching_demo')
39
+ puts "Demo directory: #{demo_dir}"
40
+
41
+ begin
42
+ # Example 1: Basic File Watching
43
+ puts "\n1. Basic File Watching"
44
+ puts "=" * 30
45
+
46
+ watch_file = File.join(demo_dir, 'application.log')
47
+ File.write(watch_file, "") # Create empty file
48
+
49
+ # Configure file watching transport
50
+ watching_transport = SmartMessage::Transport::FileTransport.new(
51
+ file_path: watch_file,
52
+ enable_subscriptions: true,
53
+ subscription_mode: :poll_changes,
54
+ poll_interval: 0.5, # Check every 500ms
55
+ read_from: :end # Only read new content
56
+ )
57
+
58
+ received_entries = []
59
+
60
+ # Subscribe to file changes
61
+ watching_transport.subscribe('LogEntry', ->(entry) {
62
+ received_entries << entry
63
+ puts "[Watcher] New log entry: #{entry.message}"
64
+ }, {})
65
+
66
+ puts "✓ Started watching: #{watch_file}"
67
+ puts "✓ Polling interval: 500ms"
68
+
69
+ # Simulate log entries being written to the file
70
+ Thread.new do
71
+ sleep 1 # Let watcher start
72
+
73
+ log_entries = [
74
+ "2024-01-15T10:00:00Z INFO Application started",
75
+ "2024-01-15T10:00:01Z DEBUG Loading configuration",
76
+ "2024-01-15T10:00:02Z INFO Database connected",
77
+ "2024-01-15T10:00:03Z WARN Deprecated API used",
78
+ "2024-01-15T10:00:04Z ERROR Connection timeout"
79
+ ]
80
+
81
+ log_entries.each do |entry|
82
+ File.open(watch_file, 'a') do |f|
83
+ f.puts entry
84
+ f.flush
85
+ end
86
+ puts "[Writer] Added: #{entry}"
87
+ sleep 0.8 # Slower than poll interval
88
+ end
89
+ end
90
+
91
+ # Let the watcher run for a few seconds
92
+ sleep 6
93
+ watching_transport.disconnect
94
+
95
+ puts "✓ Watched file changes, received #{received_entries.length} entries"
96
+
97
+ # Example 2: Configuration File Monitoring
98
+ puts "\n2. Configuration File Monitoring"
99
+ puts "=" * 40
100
+
101
+ config_file = File.join(demo_dir, 'app_config.ini')
102
+
103
+ # Initial configuration
104
+ initial_config = <<~CONFIG
105
+ [database]
106
+ host=localhost
107
+ port=5432
108
+ pool_size=10
109
+
110
+ [logging]
111
+ level=INFO
112
+ format=json
113
+ CONFIG
114
+
115
+ File.write(config_file, initial_config)
116
+
117
+ config_transport = SmartMessage::Transport::FileTransport.new(
118
+ file_path: config_file,
119
+ enable_subscriptions: true,
120
+ subscription_mode: :poll_changes,
121
+ poll_interval: 0.3,
122
+ read_from: :beginning # Re-read entire file on changes
123
+ )
124
+
125
+ current_config = {}
126
+
127
+ # Monitor configuration changes
128
+ config_transport.subscribe('ConfigChange', ->(change) {
129
+ puts "[Config Monitor] Setting '#{change.setting}' changed: #{change.old_value} → #{change.new_value}"
130
+ current_config[change.setting] = change.new_value
131
+ }, {})
132
+
133
+ puts "✓ Started monitoring config file: #{config_file}"
134
+
135
+ # Simulate configuration changes
136
+ Thread.new do
137
+ sleep 1
138
+
139
+ changes = [
140
+ { file: config_file, content: initial_config.gsub('pool_size=10', 'pool_size=20') },
141
+ { file: config_file, content: initial_config.gsub('pool_size=10', 'pool_size=20').gsub('level=INFO', 'level=DEBUG') },
142
+ { file: config_file, content: initial_config.gsub('pool_size=10', 'pool_size=15').gsub('level=INFO', 'level=WARN').gsub('format=json', 'format=text') }
143
+ ]
144
+
145
+ changes.each_with_index do |change, i|
146
+ File.write(change[:file], change[:content])
147
+ puts "[Config Writer] Applied configuration change #{i + 1}"
148
+ sleep 1.2
149
+ end
150
+ end
151
+
152
+ sleep 5
153
+ config_transport.disconnect
154
+
155
+ puts "✓ Configuration monitoring completed"
156
+
157
+ # Example 3: Log Rotation Handling
158
+ puts "\n3. Log Rotation Handling"
159
+ puts "=" * 30
160
+
161
+ rotating_log = File.join(demo_dir, 'rotating.log')
162
+ File.write(rotating_log, "")
163
+
164
+ rotation_transport = SmartMessage::Transport::FileTransport.new(
165
+ file_path: rotating_log,
166
+ enable_subscriptions: true,
167
+ subscription_mode: :poll_changes,
168
+ poll_interval: 0.4,
169
+ read_from: :end,
170
+ handle_rotation: true # Handle file rotation
171
+ )
172
+
173
+ rotation_messages = []
174
+
175
+ rotation_transport.subscribe('LogEntry', ->(entry) {
176
+ rotation_messages << entry
177
+ puts "[Rotation Watcher] #{entry.message}"
178
+ }, {})
179
+
180
+ puts "✓ Started rotation-aware watching: #{rotating_log}"
181
+
182
+ # Simulate log rotation
183
+ Thread.new do
184
+ sleep 1
185
+
186
+ # Write some initial entries
187
+ 3.times do |i|
188
+ File.open(rotating_log, 'a') { |f| f.puts "Entry #{i + 1} before rotation" }
189
+ sleep 0.5
190
+ end
191
+
192
+ # Simulate rotation
193
+ puts "[Rotator] Rotating log file..."
194
+ FileUtils.mv(rotating_log, "#{rotating_log}.1")
195
+ File.write(rotating_log, "") # New file
196
+
197
+ # Write entries to new file
198
+ 3.times do |i|
199
+ File.open(rotating_log, 'a') { |f| f.puts "Entry #{i + 1} after rotation" }
200
+ sleep 0.5
201
+ end
202
+ end
203
+
204
+ sleep 5
205
+ rotation_transport.disconnect
206
+
207
+ puts "✓ Log rotation handling completed, processed #{rotation_messages.length} messages"
208
+
209
+ # Example 4: Multiple File Watching
210
+ puts "\n4. Multiple File Watching"
211
+ puts "=" * 30
212
+
213
+ files_to_watch = %w[app1.log app2.log app3.log].map { |name| File.join(demo_dir, name) }
214
+
215
+ # Create empty files
216
+ files_to_watch.each { |file| File.write(file, "") }
217
+
218
+ watchers = []
219
+ all_messages = []
220
+
221
+ files_to_watch.each_with_index do |file, index|
222
+ transport = SmartMessage::Transport::FileTransport.new(
223
+ file_path: file,
224
+ enable_subscriptions: true,
225
+ subscription_mode: :poll_changes,
226
+ poll_interval: 0.3,
227
+ read_from: :end
228
+ )
229
+
230
+ transport.subscribe('LogEntry', ->(entry) {
231
+ all_messages << entry
232
+ puts "[Multi-Watcher] #{File.basename(entry.source_file)}: #{entry.message}"
233
+ }, {})
234
+
235
+ watchers << transport
236
+ end
237
+
238
+ puts "✓ Started watching #{files_to_watch.length} files"
239
+
240
+ # Simulate activity on multiple files
241
+ Thread.new do
242
+ sleep 1
243
+
244
+ 10.times do |i|
245
+ # Randomly pick a file to write to
246
+ file = files_to_watch.sample
247
+ message = "Message #{i + 1} from #{File.basename(file)}"
248
+
249
+ File.open(file, 'a') { |f| f.puts message }
250
+ puts "[Multi-Writer] → #{File.basename(file)}: #{message}"
251
+
252
+ sleep rand(0.2..0.6)
253
+ end
254
+ end
255
+
256
+ sleep 8
257
+ watchers.each(&:disconnect)
258
+
259
+ puts "✓ Multiple file watching completed, total messages: #{all_messages.length}"
260
+
261
+ # Example 5: File Content Processing
262
+ puts "\n5. File Content Processing with Patterns"
263
+ puts "=" * 45
264
+
265
+ pattern_file = File.join(demo_dir, 'structured.log')
266
+ File.write(pattern_file, "")
267
+
268
+ # Custom processor that parses log lines
269
+ pattern_transport = SmartMessage::Transport::FileTransport.new(
270
+ file_path: pattern_file,
271
+ enable_subscriptions: true,
272
+ subscription_mode: :poll_changes,
273
+ poll_interval: 0.4,
274
+ read_from: :end,
275
+ line_processor: ->(line) {
276
+ # Parse structured log lines: "TIMESTAMP LEVEL MESSAGE"
277
+ if match = line.match(/^(\S+)\s+(\S+)\s+(.+)$/)
278
+ {
279
+ timestamp: match[1],
280
+ level: match[2],
281
+ message: match[3],
282
+ source_file: pattern_file
283
+ }
284
+ else
285
+ nil # Skip malformed lines
286
+ end
287
+ }
288
+ )
289
+
290
+ processed_logs = []
291
+
292
+ pattern_transport.subscribe('LogEntry', ->(entry) {
293
+ processed_logs << entry
294
+ puts "[Pattern Processor] [#{entry.level}] #{entry.message}"
295
+ }, {})
296
+
297
+ puts "✓ Started pattern-based processing: #{pattern_file}"
298
+
299
+ # Write structured log entries
300
+ Thread.new do
301
+ sleep 1
302
+
303
+ entries = [
304
+ "2024-01-15T10:00:00Z INFO Application started successfully",
305
+ "2024-01-15T10:00:01Z DEBUG Loading user preferences",
306
+ "invalid log line that will be skipped",
307
+ "2024-01-15T10:00:02Z WARN Database connection slow",
308
+ "2024-01-15T10:00:03Z ERROR Failed to process request",
309
+ "another invalid line",
310
+ "2024-01-15T10:00:04Z INFO Request processed successfully"
311
+ ]
312
+
313
+ entries.each do |entry|
314
+ File.open(pattern_file, 'a') { |f| f.puts entry }
315
+ sleep 0.6
316
+ end
317
+ end
318
+
319
+ sleep 5
320
+ pattern_transport.disconnect
321
+
322
+ puts "✓ Pattern processing completed, valid entries: #{processed_logs.length}"
323
+
324
+ rescue => e
325
+ puts "Error: #{e.message}"
326
+ puts e.backtrace.first(3)
327
+ ensure
328
+ # Cleanup
329
+ FileUtils.rm_rf(demo_dir) if Dir.exist?(demo_dir)
330
+ FileUtils.rm_rf('messages') if Dir.exist?('messages')
331
+ puts "\n✓ File watching demo completed and cleaned up"
332
+ end