smart_message 0.0.12 → 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 (117) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +155 -1
  4. data/Gemfile.lock +6 -6
  5. data/README.md +71 -25
  6. data/docs/core-concepts/architecture.md +5 -10
  7. data/docs/getting-started/examples.md +0 -12
  8. data/docs/getting-started/quick-start.md +4 -9
  9. data/docs/index.md +6 -4
  10. data/docs/reference/serializers.md +160 -488
  11. data/docs/reference/transports.md +47 -146
  12. data/docs/transports/memory-transport.md +2 -1
  13. data/docs/transports/multi-transport.md +484 -0
  14. data/docs/transports/redis-transport-comparison.md +215 -350
  15. data/docs/transports/redis-transport.md +3 -22
  16. data/examples/README.md +6 -9
  17. data/examples/city_scenario/README.md +1 -1
  18. data/examples/city_scenario/messages/emergency_911_message.rb +0 -1
  19. data/examples/city_scenario/messages/emergency_resolved_message.rb +0 -1
  20. data/examples/city_scenario/messages/fire_dispatch_message.rb +0 -1
  21. data/examples/city_scenario/messages/fire_emergency_message.rb +0 -1
  22. data/examples/city_scenario/messages/health_check_message.rb +0 -1
  23. data/examples/city_scenario/messages/health_status_message.rb +0 -1
  24. data/examples/city_scenario/messages/police_dispatch_message.rb +0 -1
  25. data/examples/city_scenario/messages/silent_alarm_message.rb +0 -1
  26. data/examples/file/00_run_all_file_demos.rb +260 -0
  27. data/examples/file/01_basic_file_transport_demo.rb +237 -0
  28. data/examples/file/02_fifo_transport_demo.rb +289 -0
  29. data/examples/file/03_file_watching_demo.rb +332 -0
  30. data/examples/file/04_multi_transport_file_demo.rb +432 -0
  31. data/examples/file/README.md +257 -0
  32. data/examples/memory/00_run_all_demos.rb +317 -0
  33. data/examples/memory/01_message_deduplication_demo.rb +18 -32
  34. data/examples/memory/02_dead_letter_queue_demo.rb +9 -12
  35. data/examples/memory/03_point_to_point_orders.rb +3 -5
  36. data/examples/memory/04_publish_subscribe_events.rb +15 -16
  37. data/examples/memory/05_many_to_many_chat.rb +19 -22
  38. data/examples/memory/06_stdout_publish_only.rb +145 -0
  39. data/examples/memory/07_proc_handlers_demo.rb +13 -14
  40. data/examples/memory/08_custom_logger_demo.rb +136 -140
  41. data/examples/memory/09_error_handling_demo.rb +7 -10
  42. data/examples/memory/10_entity_addressing_basic.rb +25 -31
  43. data/examples/memory/11_entity_addressing_with_filtering.rb +32 -36
  44. data/examples/memory/12_regex_filtering_microservices.rb +10 -11
  45. data/examples/memory/13_header_block_configuration.rb +0 -5
  46. data/examples/memory/14_global_configuration_demo.rb +12 -14
  47. data/examples/memory/15_logger_demo.rb +0 -1
  48. data/examples/memory/README.md +37 -20
  49. data/examples/memory/log/demo_app.log.1 +100 -0
  50. data/examples/memory/log/demo_app.log.2 +100 -0
  51. data/examples/multi_transport_example.rb +114 -0
  52. data/examples/redis/01_smart_home_iot_demo.rb +20 -24
  53. data/examples/redis/README.md +0 -2
  54. data/examples/utilities/box_it.rb +12 -0
  55. data/examples/utilities/doing.rb +19 -0
  56. data/examples/utilities/temp.md +28 -0
  57. data/lib/smart_message/base.rb +24 -17
  58. data/lib/smart_message/configuration.rb +2 -23
  59. data/lib/smart_message/dead_letter_queue.rb +1 -1
  60. data/lib/smart_message/errors.rb +3 -0
  61. data/lib/smart_message/header.rb +1 -1
  62. data/lib/smart_message/logger/default.rb +1 -1
  63. data/lib/smart_message/messaging.rb +37 -66
  64. data/lib/smart_message/plugins.rb +42 -41
  65. data/lib/smart_message/serializer/base.rb +1 -1
  66. data/lib/smart_message/serializer.rb +3 -2
  67. data/lib/smart_message/subscription.rb +18 -20
  68. data/lib/smart_message/transport/async_publish_queue.rb +284 -0
  69. data/lib/smart_message/transport/base.rb +42 -8
  70. data/lib/smart_message/transport/fifo_operations.rb +264 -0
  71. data/lib/smart_message/transport/file_operations.rb +200 -0
  72. data/lib/smart_message/transport/file_transport.rb +149 -0
  73. data/lib/smart_message/transport/file_watching.rb +72 -0
  74. data/lib/smart_message/transport/memory_transport.rb +23 -4
  75. data/lib/smart_message/transport/partitioned_files.rb +46 -0
  76. data/lib/smart_message/transport/redis_transport.rb +11 -0
  77. data/lib/smart_message/transport/registry.rb +0 -1
  78. data/lib/smart_message/transport/stdout_transport.rb +73 -41
  79. data/lib/smart_message/transport/stdout_transport.rb.backup +88 -0
  80. data/lib/smart_message/transport.rb +0 -1
  81. data/lib/smart_message/version.rb +1 -1
  82. metadata +25 -37
  83. data/docs/guides/redis-queue-getting-started.md +0 -697
  84. data/docs/guides/redis-queue-patterns.md +0 -889
  85. data/docs/guides/redis-queue-production.md +0 -1091
  86. data/docs/transports/redis-enhanced-transport.md +0 -524
  87. data/docs/transports/redis-queue-transport.md +0 -1304
  88. data/examples/redis_enhanced/README.md +0 -319
  89. data/examples/redis_enhanced/enhanced_01_basic_patterns.rb +0 -233
  90. data/examples/redis_enhanced/enhanced_02_fluent_api.rb +0 -331
  91. data/examples/redis_enhanced/enhanced_03_dual_publishing.rb +0 -281
  92. data/examples/redis_enhanced/enhanced_04_advanced_routing.rb +0 -419
  93. data/examples/redis_queue/01_basic_messaging.rb +0 -221
  94. data/examples/redis_queue/01_comprehensive_examples.rb +0 -508
  95. data/examples/redis_queue/02_pattern_routing.rb +0 -405
  96. data/examples/redis_queue/03_fluent_api.rb +0 -422
  97. data/examples/redis_queue/04_load_balancing.rb +0 -486
  98. data/examples/redis_queue/05_microservices.rb +0 -735
  99. data/examples/redis_queue/06_emergency_alerts.rb +0 -777
  100. data/examples/redis_queue/07_queue_management.rb +0 -587
  101. data/examples/redis_queue/README.md +0 -366
  102. data/examples/redis_queue/enhanced_01_basic_patterns.rb +0 -233
  103. data/examples/redis_queue/enhanced_02_fluent_api.rb +0 -331
  104. data/examples/redis_queue/enhanced_03_dual_publishing.rb +0 -281
  105. data/examples/redis_queue/enhanced_04_advanced_routing.rb +0 -419
  106. data/examples/redis_queue/redis_queue_architecture.svg +0 -148
  107. data/ideas/README.md +0 -41
  108. data/ideas/agents.md +0 -1001
  109. data/ideas/database_transport.md +0 -980
  110. data/ideas/improvement.md +0 -359
  111. data/ideas/meshage.md +0 -1788
  112. data/ideas/message_discovery.md +0 -178
  113. data/ideas/message_schema.md +0 -1381
  114. data/lib/smart_message/transport/redis_enhanced_transport.rb +0 -399
  115. data/lib/smart_message/transport/redis_queue_transport.rb +0 -555
  116. data/lib/smart_message/wrapper.rb.bak +0 -132
  117. /data/examples/memory/{06_pretty_print_demo.rb → 16_pretty_print_demo.rb} +0 -0
@@ -0,0 +1,200 @@
1
+ # lib/smart_message/transport/file_operations.rb
2
+ # encoding: utf-8
3
+ # frozen_string_literal: true
4
+
5
+ module SmartMessage
6
+ module Transport
7
+ # Module for shared file operations, including buffering, rotation, and basic I/O.
8
+ module FileOperations
9
+ def configure_file_output
10
+ ensure_directory_exists if @options[:create_directories]
11
+ @file_handle = open_file_handle
12
+ @write_buffer = []
13
+ @last_flush = Time.now
14
+ setup_rotation_timer if rotation_enabled?
15
+ @file_mutex = Mutex.new # For thread-safety
16
+ end
17
+
18
+ def write_to_file(serialized_message)
19
+ content = prepare_file_content(serialized_message)
20
+
21
+ @file_mutex.synchronize do
22
+ if buffered_mode?
23
+ buffer_write(content)
24
+ else
25
+ direct_write(content)
26
+ end
27
+
28
+ rotate_file_if_needed
29
+ end
30
+ end
31
+
32
+ def flush_buffer
33
+ return if @write_buffer.empty?
34
+
35
+ # Only synchronize if we're not already holding the lock
36
+ if @file_mutex.owned?
37
+ @file_handle.write(@write_buffer.join)
38
+ @file_handle.flush
39
+ @write_buffer.clear
40
+ @last_flush = Time.now
41
+ else
42
+ @file_mutex.synchronize do
43
+ @file_handle.write(@write_buffer.join)
44
+ @file_handle.flush
45
+ @write_buffer.clear
46
+ @last_flush = Time.now
47
+ end
48
+ end
49
+ end
50
+
51
+ def close_file_handle
52
+ flush_buffer if buffered_mode?
53
+ if @file_mutex
54
+ @file_mutex.synchronize do
55
+ @file_handle&.close
56
+ @file_handle = nil
57
+ end
58
+ else
59
+ @file_handle&.close
60
+ @file_handle = nil
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def prepare_file_content(serialized_message)
67
+ case @options[:file_format]
68
+ when :lines
69
+ "#{serialized_message}\n"
70
+ when :raw
71
+ serialized_message
72
+ else
73
+ "#{serialized_message}\n" # default to lines
74
+ end
75
+ end
76
+
77
+ def open_file_handle
78
+ File.open(current_file_path, file_mode, encoding: @options[:encoding])
79
+ end
80
+
81
+ def ensure_directory_exists
82
+ return unless @options[:create_directories]
83
+
84
+ dir = File.dirname(current_file_path)
85
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
86
+ end
87
+
88
+ def current_file_path
89
+ if rotation_enabled? && time_based_rotation?
90
+ timestamped_file_path
91
+ else
92
+ @options[:file_path]
93
+ end
94
+ end
95
+
96
+ def file_mode
97
+ @options[:file_mode] || 'a' # append by default
98
+ end
99
+
100
+ def buffered_mode?
101
+ @options[:buffer_size] && @options[:buffer_size] > 0
102
+ end
103
+
104
+ def buffer_write(content)
105
+ @write_buffer << content
106
+
107
+ if buffer_full? || flush_interval_exceeded?
108
+ flush_buffer
109
+ end
110
+ end
111
+
112
+ def direct_write(content)
113
+ @file_handle.write(content)
114
+ @file_handle.flush if @options[:auto_flush]
115
+ end
116
+
117
+ def buffer_full?
118
+ @write_buffer.join.bytesize >= @options[:buffer_size]
119
+ end
120
+
121
+ def flush_interval_exceeded?
122
+ @options[:flush_interval] &&
123
+ (Time.now - @last_flush) >= @options[:flush_interval]
124
+ end
125
+
126
+ def rotation_enabled?
127
+ @options[:rotate_size] || @options[:rotate_time]
128
+ end
129
+
130
+ def time_based_rotation?
131
+ @options[:rotate_time]
132
+ end
133
+
134
+ def should_rotate?
135
+ size_rotation_needed? || time_rotation_needed?
136
+ end
137
+
138
+ def size_rotation_needed?
139
+ @options[:rotate_size] &&
140
+ File.exist?(current_file_path) && File.size(current_file_path) >= @options[:rotate_size]
141
+ end
142
+
143
+ def time_rotation_needed?
144
+ return false unless @options[:rotate_time]
145
+
146
+ case @options[:rotate_time]
147
+ when :hourly
148
+ Time.now.min == 0 && Time.now.sec == 0
149
+ when :daily
150
+ Time.now.hour == 0 && Time.now.min == 0 && Time.now.sec == 0
151
+ else
152
+ false
153
+ end
154
+ end
155
+
156
+ def rotate_file_if_needed
157
+ return unless should_rotate?
158
+
159
+ close_current_file
160
+ archive_current_file
161
+ @file_handle = open_file_handle
162
+ end
163
+
164
+ def close_current_file
165
+ flush_buffer if buffered_mode?
166
+ @file_handle&.close
167
+ @file_handle = nil
168
+ end
169
+
170
+ def archive_current_file
171
+ return unless File.exist?(current_file_path)
172
+
173
+ timestamp = Time.now.strftime(@options[:timestamp_format] || '%Y%m%d_%H%M%S')
174
+ base = File.basename(@options[:file_path], '.*')
175
+ ext = File.extname(@options[:file_path])
176
+ dir = File.dirname(@options[:file_path])
177
+ archive_path = File.join(dir, "#{base}_#{timestamp}#{ext}")
178
+
179
+ FileUtils.mv(current_file_path, archive_path)
180
+
181
+ # Maintain rotation count
182
+ if @options[:rotate_count]
183
+ files = Dir.glob(File.join(dir, "#{base}_*#{ext}")).sort
184
+ while files.size > @options[:rotate_count]
185
+ File.delete(files.shift)
186
+ end
187
+ end
188
+ end
189
+
190
+ def timestamped_file_path
191
+ base = File.basename(@options[:file_path], '.*')
192
+ ext = File.extname(@options[:file_path])
193
+ dir = File.dirname(@options[:file_path])
194
+ timestamp = Time.now.strftime(@options[:timestamp_format] || '%Y%m%d_%H%M%S')
195
+
196
+ File.join(dir, "#{base}_#{timestamp}#{ext}")
197
+ end
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,149 @@
1
+ # lib/smart_message/transport/file_transport.rb
2
+ # encoding: utf-8
3
+ # frozen_string_literal: true
4
+
5
+ require_relative 'file_operations'
6
+ require_relative 'file_watching'
7
+ require_relative 'partitioned_files'
8
+ require_relative 'async_publish_queue'
9
+ require_relative 'fifo_operations'
10
+
11
+ module SmartMessage
12
+ module Transport
13
+ class FileTransport < Base
14
+ include FileOperations
15
+ include FileWatching
16
+ include PartitionedFiles
17
+ include AsyncPublishQueue
18
+ include FifoOperations
19
+
20
+ # @param path [String, IO, Pathname] file path or IO-like object
21
+ # @param mode [String] file open mode ("a" for append, etc.)
22
+ # @param encoding [String, nil] file encoding
23
+ def initialize(options = {})
24
+ @current_message_class = nil
25
+ super(**options)
26
+ end
27
+
28
+ def default_options
29
+ {
30
+ file_path: 'messages.log',
31
+ file_mode: 'a',
32
+ encoding: nil,
33
+ file_format: :lines,
34
+ buffer_size: 0,
35
+ flush_interval: nil,
36
+ auto_flush: true,
37
+ rotate_size: nil,
38
+ rotate_time: nil,
39
+ rotate_count: 5,
40
+ timestamp_format: '%Y%m%d_%H%M%S',
41
+ create_directories: true,
42
+ async: false,
43
+ max_queue: nil,
44
+ drop_when_full: false,
45
+ queue_overflow_strategy: :block,
46
+ max_retries: 3,
47
+ max_retry_delay: 30,
48
+ worker_timeout: 5,
49
+ shutdown_timeout: 10,
50
+ queue_warning_threshold: 0.8,
51
+ enable_queue_monitoring: true,
52
+ drain_queue_on_shutdown: true,
53
+ send_dropped_to_dlq: false,
54
+ read_from_end: true,
55
+ poll_interval: 1.0,
56
+ file_type: :regular,
57
+ create_fifo: false,
58
+ fifo_mode: :blocking,
59
+ fifo_permissions: 0644,
60
+ fallback_transport: nil,
61
+ enable_subscriptions: false,
62
+ subscription_mode: :polling,
63
+ filename_selector: nil,
64
+ directory: nil,
65
+ subscription_file_path: nil
66
+ }
67
+ end
68
+
69
+ def configure
70
+ # Call parent configuration first
71
+ super if defined?(super)
72
+
73
+ # Then configure our file-specific features
74
+ if @options[:async]
75
+ configure_async_publishing
76
+ elsif @options[:file_type] == :fifo
77
+ configure_fifo
78
+ else
79
+ configure_file_output
80
+ end
81
+ end
82
+
83
+ def publish(payload)
84
+ do_publish(nil, payload)
85
+ end
86
+
87
+ def do_publish(message_class, serialized_message)
88
+ @current_message_class = message_class
89
+ if @options[:async]
90
+ async_publish(message_class, serialized_message)
91
+ else
92
+ if @options[:filename_selector] || @options[:directory]
93
+ header = { message_class_name: message_class.to_s }
94
+ path = determine_file_path(serialized_message, header)
95
+ @file_handle = get_or_open_partition_handle(path)
96
+ write_to_file(serialized_message)
97
+ elsif @options[:file_type] == :fifo
98
+ write_to_fifo(serialized_message)
99
+ else
100
+ write_to_file(serialized_message)
101
+ end
102
+ end
103
+ end
104
+
105
+ def subscribe(message_class, process_method, filter_options = {})
106
+ unless @options[:enable_subscriptions]
107
+ logger.warn { "[FileTransport] Subscriptions disabled - set enable_subscriptions: true" }
108
+ return
109
+ end
110
+
111
+ if @options[:file_type] == :fifo
112
+ start_fifo_reader(message_class, process_method, filter_options)
113
+ else
114
+ start_file_polling(message_class, process_method, filter_options)
115
+ end
116
+
117
+ super(message_class, process_method, filter_options)
118
+ end
119
+
120
+ def connected?
121
+ case @options[:file_type]
122
+ when :fifo
123
+ subscription_active? || fifo_active?
124
+ else
125
+ (@file_handle && !@file_handle.closed?) || subscription_active?
126
+ end
127
+ end
128
+
129
+ def disconnect
130
+ stop_file_subscriptions
131
+ stop_fifo_operations if @options[:file_type] == :fifo
132
+ stop_async_publishing if @options[:async]
133
+ close_partition_handles if @options[:filename_selector] || @options[:directory]
134
+ close_file_handle
135
+ end
136
+
137
+ private
138
+
139
+ def subscription_active?
140
+ @polling_thread&.alive? || fifo_active?
141
+ end
142
+
143
+ def stop_file_subscriptions
144
+ @polling_thread&.kill
145
+ @polling_thread&.join(5)
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,72 @@
1
+ # lib/smart_message/transport/file_watching.rb
2
+ # encoding: utf-8
3
+ # frozen_string_literal: true
4
+
5
+ module SmartMessage
6
+ module Transport
7
+ # Module for file watching and subscription support (reading/tailing).
8
+ module FileWatching
9
+
10
+ private
11
+
12
+ def logger
13
+ # Ensure we have a proper logger, not just an IO object
14
+ if @logger && @logger.respond_to?(:error) && @logger.respond_to?(:info) && @logger.respond_to?(:warn)
15
+ return @logger
16
+ end
17
+ @logger = SmartMessage::Logger.default
18
+ end
19
+
20
+ public
21
+ def start_file_polling(message_class, process_method, filter_options)
22
+ poll_interval = filter_options[:poll_interval] || @options[:poll_interval] || 1.0
23
+ file_path = subscription_file_path(message_class, filter_options)
24
+
25
+ @polling_thread = Thread.new do
26
+ Thread.current.name = "FileTransport-Poller"
27
+ last_position = @options[:read_from_end] ? (File.exist?(file_path) ? File.size(file_path) : 0) : 0
28
+
29
+ loop do
30
+ sleep poll_interval
31
+ next unless File.exist?(file_path)
32
+
33
+ current_size = File.size(file_path)
34
+ if current_size > last_position
35
+ process_new_file_content(file_path, last_position, current_size, message_class)
36
+ last_position = current_size
37
+ end
38
+ end
39
+ rescue => e
40
+ logger.error { "[FileTransport] Polling thread error: #{e.message}" }
41
+ end
42
+ end
43
+
44
+ def process_new_file_content(file_path, start_pos, end_pos, message_class)
45
+ File.open(file_path, 'r', encoding: @options[:encoding]) do |file|
46
+ file.seek(start_pos)
47
+ content = file.read(end_pos - start_pos)
48
+
49
+ content.each_line do |line|
50
+ next if line.strip.empty?
51
+
52
+ begin
53
+ receive(message_class, line.strip)
54
+ rescue => e
55
+ begin
56
+ logger.error { "[FileTransport] Error processing message: #{e.message}" }
57
+ rescue
58
+ # Fallback if logger is not available
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ def subscription_file_path(message_class, filter_options)
66
+ filter_options[:file_path] ||
67
+ @options[:subscription_file_path] ||
68
+ File.join(File.dirname(@options[:file_path]), "#{message_class.to_s.downcase}.jsonl")
69
+ end
70
+ end
71
+ end
72
+ end
@@ -16,30 +16,49 @@ module SmartMessage
16
16
  }
17
17
  end
18
18
 
19
+ # Memory transport doesn't need serialization
20
+ def default_serializer
21
+ nil
22
+ end
23
+
19
24
  def configure
20
25
  @messages = []
21
26
  @message_mutex = Mutex.new
22
27
  end
23
28
 
24
- # Publish message to memory queue
29
+ # Implement do_publish for memory transport (no serialization needed)
25
30
  def do_publish(message_class, serialized_message)
31
+ # For memory transport, serialized_message is actually the message object
32
+ message = serialized_message
33
+
26
34
  @message_mutex.synchronize do
27
35
  # Prevent memory overflow
28
36
  @messages.shift if @messages.size >= @options[:max_messages]
29
37
 
38
+ # Store the actual message object, no serialization needed
30
39
  @messages << {
31
40
  message_class: message_class,
32
- serialized_message: serialized_message,
41
+ message: message.dup, # Store a copy to prevent mutation
33
42
  published_at: Time.now
34
43
  }
35
44
  end
36
45
 
37
46
  # Auto-process if enabled
38
47
  if @options[:auto_process]
39
- receive(message_class, serialized_message)
48
+ # Route directly without serialization
49
+ @dispatcher.route(message)
40
50
  end
41
51
  end
42
52
 
53
+ # Override encode_message to return the message object directly
54
+ def encode_message(message)
55
+ # Update header with serializer info (even though we don't serialize)
56
+ message._sm_header.serializer = 'none'
57
+
58
+ # Return the message object itself (no encoding needed)
59
+ message
60
+ end
61
+
43
62
  # Get all stored messages
44
63
  def all_messages
45
64
  @message_mutex.synchronize { @messages.dup }
@@ -59,7 +78,7 @@ module SmartMessage
59
78
  def process_all
60
79
  messages_to_process = @message_mutex.synchronize { @messages.dup }
61
80
  messages_to_process.each do |msg|
62
- receive(msg[:message_class], msg[:serialized_message])
81
+ @dispatcher.route(msg[:message])
63
82
  end
64
83
  end
65
84
 
@@ -0,0 +1,46 @@
1
+ # lib/smart_message/transport/partitioned_files.rb
2
+ # encoding: utf-8
3
+ # frozen_string_literal: true
4
+
5
+ module SmartMessage
6
+ module Transport
7
+ # Module for fan-out/partitioned file support.
8
+ module PartitionedFiles
9
+ def determine_file_path(payload, header)
10
+ if @options[:filename_selector]&.respond_to?(:call)
11
+ @options[:filename_selector].call(payload, header)
12
+ elsif @options[:directory]
13
+ File.join(@options[:directory], "#{header[:message_class_name].to_s.downcase}.log")
14
+ else
15
+ @options[:file_path]
16
+ end
17
+ end
18
+
19
+ def get_or_open_partition_handle(full_path)
20
+ @partition_handles ||= {}
21
+ @partition_mutexes ||= {}
22
+
23
+ unless @partition_handles[full_path]
24
+ ensure_directory_exists_for(full_path)
25
+ @partition_handles[full_path] = File.open(full_path, file_mode, encoding: @options[:encoding])
26
+ @partition_mutexes[full_path] = Mutex.new
27
+ end
28
+
29
+ @partition_handles[full_path]
30
+ end
31
+
32
+ def ensure_directory_exists_for(path)
33
+ dir = File.dirname(path)
34
+ FileUtils.mkdir_p(dir) if @options[:create_directories] && !Dir.exist?(dir)
35
+ end
36
+
37
+ def close_partition_handles
38
+ @partition_handles&.each do |_, handle|
39
+ handle.close
40
+ end
41
+ @partition_handles = {}
42
+ @partition_mutexes = {}
43
+ end
44
+ end
45
+ end
46
+ end
@@ -24,6 +24,17 @@ module SmartMessage
24
24
  }
25
25
  end
26
26
 
27
+ # Default to MessagePack for Redis (efficient binary format)
28
+ def default_serializer
29
+ # Try MessagePack first, fall back to JSON
30
+ begin
31
+ require 'smart_message/serializer/message_pack'
32
+ SmartMessage::Serializer::MessagePack.new
33
+ rescue LoadError
34
+ SmartMessage::Serializer::Json.new
35
+ end
36
+ end
37
+
27
38
  def configure
28
39
  @redis_pub = Redis.new(url: @options[:url], db: @options[:db])
29
40
  @redis_sub = Redis.new(url: @options[:url], db: @options[:db])
@@ -54,7 +54,6 @@ module SmartMessage
54
54
  register(:stdout, SmartMessage::Transport::StdoutTransport)
55
55
  register(:memory, SmartMessage::Transport::MemoryTransport)
56
56
  register(:redis, SmartMessage::Transport::RedisTransport)
57
- register(:redis_queue, SmartMessage::Transport::RedisQueueTransport)
58
57
  end
59
58
  end
60
59
  end