smart_message 0.0.8 → 0.0.9

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.irbrc +24 -0
  4. data/CHANGELOG.md +96 -0
  5. data/Gemfile.lock +6 -1
  6. data/README.md +289 -15
  7. data/docs/README.md +3 -1
  8. data/docs/addressing.md +119 -13
  9. data/docs/architecture.md +68 -0
  10. data/docs/dead_letter_queue.md +673 -0
  11. data/docs/dispatcher.md +87 -0
  12. data/docs/examples.md +59 -1
  13. data/docs/getting-started.md +8 -1
  14. data/docs/logging.md +382 -326
  15. data/docs/message_filtering.md +451 -0
  16. data/examples/01_point_to_point_orders.rb +54 -53
  17. data/examples/02_publish_subscribe_events.rb +14 -10
  18. data/examples/03_many_to_many_chat.rb +16 -8
  19. data/examples/04_redis_smart_home_iot.rb +20 -10
  20. data/examples/05_proc_handlers.rb +12 -11
  21. data/examples/06_custom_logger_example.rb +95 -100
  22. data/examples/07_error_handling_scenarios.rb +4 -2
  23. data/examples/08_entity_addressing_basic.rb +18 -6
  24. data/examples/08_entity_addressing_with_filtering.rb +27 -9
  25. data/examples/09_dead_letter_queue_demo.rb +559 -0
  26. data/examples/09_regex_filtering_microservices.rb +407 -0
  27. data/examples/10_header_block_configuration.rb +263 -0
  28. data/examples/11_global_configuration_example.rb +219 -0
  29. data/examples/README.md +102 -0
  30. data/examples/dead_letters.jsonl +12 -0
  31. data/examples/performance_metrics/benchmark_results_ractor_20250818_205603.json +135 -0
  32. data/examples/performance_metrics/benchmark_results_ractor_20250818_205831.json +135 -0
  33. data/examples/performance_metrics/benchmark_results_test_20250818_204942.json +130 -0
  34. data/examples/performance_metrics/benchmark_results_threadpool_20250818_204942.json +130 -0
  35. data/examples/performance_metrics/benchmark_results_threadpool_20250818_204959.json +130 -0
  36. data/examples/performance_metrics/benchmark_results_threadpool_20250818_205044.json +130 -0
  37. data/examples/performance_metrics/benchmark_results_threadpool_20250818_205109.json +130 -0
  38. data/examples/performance_metrics/benchmark_results_threadpool_20250818_205252.json +130 -0
  39. data/examples/performance_metrics/benchmark_results_unknown_20250819_172852.json +130 -0
  40. data/examples/performance_metrics/compare_benchmarks.rb +519 -0
  41. data/examples/performance_metrics/dead_letters.jsonl +3100 -0
  42. data/examples/performance_metrics/performance_benchmark.rb +344 -0
  43. data/examples/show_logger.rb +367 -0
  44. data/examples/show_me.rb +145 -0
  45. data/examples/temp.txt +94 -0
  46. data/examples/tmux_chat/bot_agent.rb +4 -2
  47. data/examples/tmux_chat/human_agent.rb +4 -2
  48. data/examples/tmux_chat/room_monitor.rb +4 -2
  49. data/examples/tmux_chat/shared_chat_system.rb +6 -3
  50. data/lib/smart_message/addressing.rb +259 -0
  51. data/lib/smart_message/base.rb +121 -599
  52. data/lib/smart_message/circuit_breaker.rb +2 -1
  53. data/lib/smart_message/configuration.rb +199 -0
  54. data/lib/smart_message/dead_letter_queue.rb +27 -10
  55. data/lib/smart_message/dispatcher.rb +90 -49
  56. data/lib/smart_message/header.rb +5 -0
  57. data/lib/smart_message/logger/base.rb +21 -1
  58. data/lib/smart_message/logger/default.rb +88 -138
  59. data/lib/smart_message/logger/lumberjack.rb +324 -0
  60. data/lib/smart_message/logger/null.rb +81 -0
  61. data/lib/smart_message/logger.rb +17 -9
  62. data/lib/smart_message/messaging.rb +100 -0
  63. data/lib/smart_message/plugins.rb +132 -0
  64. data/lib/smart_message/serializer/base.rb +25 -8
  65. data/lib/smart_message/serializer/json.rb +5 -4
  66. data/lib/smart_message/subscription.rb +193 -0
  67. data/lib/smart_message/transport/base.rb +72 -41
  68. data/lib/smart_message/transport/memory_transport.rb +7 -5
  69. data/lib/smart_message/transport/redis_transport.rb +15 -45
  70. data/lib/smart_message/transport/stdout_transport.rb +18 -8
  71. data/lib/smart_message/transport.rb +1 -34
  72. data/lib/smart_message/utilities.rb +142 -0
  73. data/lib/smart_message/version.rb +1 -1
  74. data/lib/smart_message/versioning.rb +85 -0
  75. data/lib/smart_message/wrapper.rb.bak +132 -0
  76. data/lib/smart_message.rb +74 -28
  77. data/smart_message.gemspec +3 -0
  78. metadata +76 -3
  79. data/lib/smart_message/serializer.rb +0 -10
  80. data/lib/smart_message/wrapper.rb +0 -43
@@ -0,0 +1,132 @@
1
+ # lib/smart_message/wrapper.rb
2
+ # encoding: utf-8
3
+ # frozen_string_literal: true
4
+
5
+ require 'securerandom' # STDLIB
6
+ require_relative './header.rb'
7
+
8
+ module SmartMessage
9
+ module Wrapper
10
+ # Every smart message has a common wrapper format that contains
11
+ # information used to support the dispatching of subscribed
12
+ # messages upon receipt from a transport as well as the serialized
13
+ # payload.
14
+ #
15
+ # The wrapper consolidates header and payload into a single object
16
+ # for cleaner method signatures throughout the SmartMessage dataflow.
17
+ class Base < Hashie::Dash
18
+ include Hashie::Extensions::IndifferentAccess
19
+ include Hashie::Extensions::MethodAccess
20
+ include Hashie::Extensions::DeepMerge
21
+
22
+ # Core wrapper properties
23
+ # Using '_sm_' prefix to avoid collision with user message definitions
24
+ property :_sm_header,
25
+ required: true,
26
+ description: "SmartMessage header containing routing and metadata information"
27
+
28
+ property :_sm_payload,
29
+ required: true,
30
+ description: "Serialized message payload containing the business data"
31
+
32
+ # Create wrapper from header and payload
33
+ def initialize(header: nil, payload: nil, **props, &block)
34
+ # Handle different initialization patterns
35
+ if header && payload
36
+ attributes = {
37
+ _sm_header: header,
38
+ _sm_payload: payload
39
+ }
40
+ else
41
+ # Create default header if not provided
42
+ default_header = SmartMessage::Header.new(
43
+ uuid: SecureRandom.uuid,
44
+ message_class: 'SmartMessage::Wrapper::Base',
45
+ published_at: Time.now,
46
+ publisher_pid: Process.pid,
47
+ version: 1
48
+ )
49
+
50
+ attributes = {
51
+ _sm_header: default_header,
52
+ _sm_payload: nil
53
+ }.merge(props)
54
+ end
55
+
56
+ super(attributes, &block)
57
+ end
58
+
59
+ # Convenience accessors for header and payload
60
+ def header
61
+ _sm_header
62
+ end
63
+
64
+ def payload
65
+ _sm_payload
66
+ end
67
+
68
+ # Check if this is a broadcast message (to field is nil)
69
+ def broadcast?
70
+ _sm_header.to.nil?
71
+ end
72
+
73
+ # Check if this is a directed message (to field is present)
74
+ def directed?
75
+ !broadcast?
76
+ end
77
+
78
+ # Get message class from header
79
+ def message_class
80
+ _sm_header.message_class
81
+ end
82
+
83
+ # Get sender from header
84
+ def from
85
+ _sm_header.from
86
+ end
87
+
88
+ # Get recipient from header
89
+ def to
90
+ _sm_header.to
91
+ end
92
+
93
+ # Get reply destination from header
94
+ def reply_to
95
+ _sm_header.reply_to
96
+ end
97
+
98
+ # Get message version from header
99
+ def version
100
+ _sm_header.version
101
+ end
102
+
103
+ # Get UUID from header
104
+ def uuid
105
+ _sm_header.uuid
106
+ end
107
+
108
+ # Convert wrapper to hash for serialization/transport
109
+ def to_hash
110
+ {
111
+ '_sm_header' => _sm_header.to_hash,
112
+ '_sm_payload' => _sm_payload
113
+ }
114
+ end
115
+
116
+ alias_method :to_h, :to_hash
117
+
118
+ # Outer-level JSON serialization for the wrapper
119
+ # This is level 2 serialization - always JSON for routing/monitoring
120
+ def to_json(*args)
121
+ require 'json'
122
+ to_hash.to_json(*args)
123
+ end
124
+
125
+ # Split wrapper into header and payload components
126
+ # Enables destructuring assignment: header, payload = wrapper.split
127
+ def split
128
+ [_sm_header, _sm_payload]
129
+ end
130
+ end
131
+ end
132
+ end
data/lib/smart_message.rb CHANGED
@@ -2,6 +2,8 @@
2
2
  # encoding: utf-8
3
3
  # frozen_string_literal: true
4
4
 
5
+ require 'zeitwerk'
6
+
5
7
  # FIXME: handle this better
6
8
  class MilClass # IMO nil is the same as an empty String
7
9
  def to_s
@@ -9,48 +11,92 @@ class MilClass # IMO nil is the same as an empty String
9
11
  end
10
12
  end
11
13
 
12
-
13
14
  require 'active_support/core_ext/string/inflections'
14
15
  require 'date' # STDLIB
15
-
16
- # Production logging should use the logger framework, not debug_me
17
-
18
16
  require 'hashie' # Your friendly neighborhood hash library.
19
17
 
20
- require_relative './simple_stats'
21
-
22
- require_relative './smart_message/version'
23
- require_relative './smart_message/errors'
24
- require_relative './smart_message/circuit_breaker'
25
- require_relative './smart_message/dead_letter_queue'
18
+ # Set up Zeitwerk autoloader
19
+ loader = Zeitwerk::Loader.for_gem
20
+ loader.tag = "smart_message"
21
+ loader.ignore("#{__dir__}/simple_stats.rb")
22
+ loader.setup
26
23
 
27
- require_relative './smart_message/dispatcher.rb'
28
- require_relative './smart_message/transport.rb'
29
- require_relative './smart_message/base.rb'
24
+ # Load simple_stats manually since it's not following the naming convention
25
+ require_relative './simple_stats'
30
26
 
31
27
  # SmartMessage abstracts messages from the backend transport process
32
28
  module SmartMessage
33
- # The super class of all smart messages
34
- # class Base < Dash from the Hashie gem plus mixins
35
- # end
36
-
37
- # encapsulates the message transport plugin
38
- # module Transport is defined in transport.rb
39
-
40
- # encapsulates the message code/decode serializer
29
+ class << self
30
+ # Global configuration for SmartMessage
31
+ #
32
+ # Usage:
33
+ # SmartMessage.configure do |config|
34
+ # config.logger = MyApp::Logger.new
35
+ # config.transport = MyApp::Transport.new
36
+ # config.serializer = MyApp::Serializer.new
37
+ # end
38
+ def configure
39
+ yield(configuration)
40
+ end
41
+
42
+ # Get the global configuration instance
43
+ def configuration
44
+ @configuration ||= Configuration.new
45
+ end
46
+
47
+ # Reset global configuration to defaults
48
+ def reset_configuration!
49
+ @configuration = Configuration.new
50
+ end
51
+ end
52
+ # Module definitions for Zeitwerk to populate
41
53
  module Serializer
42
- # the super class of the message serializer
43
- class Base
54
+ class << self
55
+ def default
56
+ # Check global configuration first, then fall back to framework default
57
+ SmartMessage.configuration.default_serializer
58
+ end
44
59
  end
45
60
  end
46
61
 
47
- # encapsulates the message logging capability
48
62
  module Logger
49
- # the super class of the message logger
50
- class Base
63
+ class << self
64
+ def default
65
+ # Check global configuration first, then fall back to framework default
66
+ SmartMessage.configuration.default_logger
67
+ end
68
+ end
69
+ end
70
+
71
+ module Transport
72
+ class << self
73
+ def default
74
+ # Check global configuration first, then fall back to framework default
75
+ SmartMessage.configuration.default_transport
76
+ end
77
+
78
+ def registry
79
+ @registry ||= Registry.new
80
+ end
81
+
82
+ def register(name, transport_class)
83
+ registry.register(name, transport_class)
84
+ end
85
+
86
+ def get(name)
87
+ registry.get(name)
88
+ end
89
+
90
+ def create(name, **options)
91
+ transport_class = get(name)
92
+ transport_class&.new(**options)
93
+ end
94
+
95
+ def available
96
+ registry.list
97
+ end
51
98
  end
52
99
  end
53
100
  end # module SmartMessage
54
101
 
55
- require_relative './smart_message/serializer'
56
- require_relative './smart_message/logger'
102
+ # Don't eager load initially - let Zeitwerk handle lazy loading
@@ -41,6 +41,9 @@ Gem::Specification.new do |spec|
41
41
  spec.add_dependency 'concurrent-ruby'
42
42
  spec.add_dependency 'redis'
43
43
  spec.add_dependency 'breaker_machines'
44
+ spec.add_dependency 'zeitwerk'
45
+ spec.add_dependency 'lumberjack'
46
+ spec.add_dependency 'colorize'
44
47
 
45
48
  spec.add_development_dependency 'bundler'
46
49
  spec.add_development_dependency 'rake'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_message
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dewayne VanHoozer
@@ -79,6 +79,48 @@ dependencies:
79
79
  - - ">="
80
80
  - !ruby/object:Gem::Version
81
81
  version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: zeitwerk
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: lumberjack
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: colorize
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
82
124
  - !ruby/object:Gem::Dependency
83
125
  name: bundler
84
126
  requirement: !ruby/object:Gem::Requirement
@@ -203,6 +245,7 @@ extra_rdoc_files: []
203
245
  files:
204
246
  - ".envrc"
205
247
  - ".gitignore"
248
+ - ".irbrc"
206
249
  - ".travis.yml"
207
250
  - CHANGELOG.md
208
251
  - COMMITS.md
@@ -215,11 +258,13 @@ files:
215
258
  - docs/README.md
216
259
  - docs/addressing.md
217
260
  - docs/architecture.md
261
+ - docs/dead_letter_queue.md
218
262
  - docs/dispatcher.md
219
263
  - docs/examples.md
220
264
  - docs/getting-started.md
221
265
  - docs/ideas_to_think_about.md
222
266
  - docs/logging.md
267
+ - docs/message_filtering.md
223
268
  - docs/message_processing.md
224
269
  - docs/proc_handlers_summary.md
225
270
  - docs/properties.md
@@ -236,8 +281,28 @@ files:
236
281
  - examples/07_error_handling_scenarios.rb
237
282
  - examples/08_entity_addressing_basic.rb
238
283
  - examples/08_entity_addressing_with_filtering.rb
284
+ - examples/09_dead_letter_queue_demo.rb
285
+ - examples/09_regex_filtering_microservices.rb
286
+ - examples/10_header_block_configuration.rb
287
+ - examples/11_global_configuration_example.rb
239
288
  - examples/README.md
289
+ - examples/dead_letters.jsonl
290
+ - examples/performance_metrics/benchmark_results_ractor_20250818_205603.json
291
+ - examples/performance_metrics/benchmark_results_ractor_20250818_205831.json
292
+ - examples/performance_metrics/benchmark_results_test_20250818_204942.json
293
+ - examples/performance_metrics/benchmark_results_threadpool_20250818_204942.json
294
+ - examples/performance_metrics/benchmark_results_threadpool_20250818_204959.json
295
+ - examples/performance_metrics/benchmark_results_threadpool_20250818_205044.json
296
+ - examples/performance_metrics/benchmark_results_threadpool_20250818_205109.json
297
+ - examples/performance_metrics/benchmark_results_threadpool_20250818_205252.json
298
+ - examples/performance_metrics/benchmark_results_unknown_20250819_172852.json
299
+ - examples/performance_metrics/compare_benchmarks.rb
300
+ - examples/performance_metrics/dead_letters.jsonl
301
+ - examples/performance_metrics/performance_benchmark.rb
302
+ - examples/show_logger.rb
303
+ - examples/show_me.rb
240
304
  - examples/smart_home_iot_dataflow.md
305
+ - examples/temp.txt
241
306
  - examples/tmux_chat/README.md
242
307
  - examples/tmux_chat/bot_agent.rb
243
308
  - examples/tmux_chat/human_agent.rb
@@ -247,8 +312,10 @@ files:
247
312
  - examples/tmux_chat/stop_chat_demo.sh
248
313
  - lib/simple_stats.rb
249
314
  - lib/smart_message.rb
315
+ - lib/smart_message/addressing.rb
250
316
  - lib/smart_message/base.rb
251
317
  - lib/smart_message/circuit_breaker.rb
318
+ - lib/smart_message/configuration.rb
252
319
  - lib/smart_message/dead_letter_queue.rb
253
320
  - lib/smart_message/dispatcher.rb
254
321
  - lib/smart_message/dispatcher/.keep
@@ -257,19 +324,25 @@ files:
257
324
  - lib/smart_message/logger.rb
258
325
  - lib/smart_message/logger/base.rb
259
326
  - lib/smart_message/logger/default.rb
327
+ - lib/smart_message/logger/lumberjack.rb
328
+ - lib/smart_message/logger/null.rb
329
+ - lib/smart_message/messaging.rb
330
+ - lib/smart_message/plugins.rb
260
331
  - lib/smart_message/property_descriptions.rb
261
332
  - lib/smart_message/property_validations.rb
262
- - lib/smart_message/serializer.rb
263
333
  - lib/smart_message/serializer/base.rb
264
334
  - lib/smart_message/serializer/json.rb
335
+ - lib/smart_message/subscription.rb
265
336
  - lib/smart_message/transport.rb
266
337
  - lib/smart_message/transport/base.rb
267
338
  - lib/smart_message/transport/memory_transport.rb
268
339
  - lib/smart_message/transport/redis_transport.rb
269
340
  - lib/smart_message/transport/registry.rb
270
341
  - lib/smart_message/transport/stdout_transport.rb
342
+ - lib/smart_message/utilities.rb
271
343
  - lib/smart_message/version.rb
272
- - lib/smart_message/wrapper.rb
344
+ - lib/smart_message/versioning.rb
345
+ - lib/smart_message/wrapper.rb.bak
273
346
  - smart_message.gemspec
274
347
  homepage: https://github.com/MadBomber/smart_message
275
348
  licenses: []
@@ -1,10 +0,0 @@
1
- # lib/smart_message/serializer.rb
2
- # encoding: utf-8
3
- # frozen_string_literal: true
4
-
5
- require_relative 'serializer/base'
6
- require_relative 'serializer/json'
7
-
8
- module SmartMessage::Serializer
9
- # Serializer module for message encoding/decoding
10
- end # module SmartMessage::Serializer
@@ -1,43 +0,0 @@
1
- # lib/smart_message/wrapper.rb
2
- # encoding: utf-8
3
- # frozen_string_literal: true
4
-
5
- # TODO: consider having a serializer plugin for the wrapper that is
6
- # different than that used for the payload.
7
-
8
- require_relative './header.rb'
9
-
10
- module SmartMessage
11
- module Wrapper
12
- # Every smart message has a common wrapper format that contains
13
- # information used to support the dispatching of subscribed
14
- # messages upon receipt from a transport as well as the serialized
15
- # payload.
16
- class Base < Hashie::Dash
17
- include Hashie::Extensions::IndifferentAccess
18
- include Hashie::Extensions::MergeInitializer
19
- include Hashie::Extensions::MethodAccess
20
-
21
- # Common attributes of the smart message standard header
22
- # Using '_sm_' as a prefix to avoid potential collision with
23
- # a user's message definition.
24
- property :_sm_header
25
- property :_sm_payload
26
-
27
- def initialize(**props, &block)
28
- attributes = {
29
- _sm_header: SmartMessage::Header.new(
30
- uuid: SecureRandom.uuid,
31
- message_class: self.class.to_s,
32
- published_at: 2,
33
- publisher_pid: 3
34
- )
35
- }.merge(props)
36
-
37
- super(attributes, &block)
38
- end
39
-
40
-
41
- end
42
- end
43
- end