smart_message 0.0.10 → 0.0.12

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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/deploy-github-pages.yml +38 -0
  3. data/.gitignore +5 -0
  4. data/CHANGELOG.md +30 -0
  5. data/Gemfile.lock +35 -4
  6. data/README.md +169 -71
  7. data/Rakefile +29 -4
  8. data/docs/assets/images/ddq_architecture.svg +130 -0
  9. data/docs/assets/images/dlq_architecture.svg +115 -0
  10. data/docs/assets/images/enhanced-dual-publishing.svg +136 -0
  11. data/docs/assets/images/enhanced-fluent-api.svg +149 -0
  12. data/docs/assets/images/enhanced-microservices-routing.svg +115 -0
  13. data/docs/assets/images/enhanced-pattern-matching.svg +107 -0
  14. data/docs/assets/images/fluent-api-demo.svg +59 -0
  15. data/docs/assets/images/performance-comparison.svg +161 -0
  16. data/docs/assets/images/redis-basic-architecture.svg +53 -0
  17. data/docs/assets/images/redis-enhanced-architecture.svg +88 -0
  18. data/docs/assets/images/redis-queue-architecture.svg +101 -0
  19. data/docs/assets/images/smart_message.jpg +0 -0
  20. data/docs/assets/images/smart_message_walking.jpg +0 -0
  21. data/docs/assets/images/smartmessage_architecture_overview.svg +173 -0
  22. data/docs/assets/images/transport-comparison-matrix.svg +171 -0
  23. data/docs/assets/javascripts/mathjax.js +17 -0
  24. data/docs/assets/stylesheets/extra.css +51 -0
  25. data/docs/{addressing.md → core-concepts/addressing.md} +5 -7
  26. data/docs/{architecture.md → core-concepts/architecture.md} +78 -138
  27. data/docs/{dispatcher.md → core-concepts/dispatcher.md} +21 -21
  28. data/docs/{message_filtering.md → core-concepts/message-filtering.md} +2 -3
  29. data/docs/{message_processing.md → core-concepts/message-processing.md} +17 -17
  30. data/docs/{troubleshooting.md → development/troubleshooting.md} +7 -7
  31. data/docs/{examples.md → getting-started/examples.md} +115 -89
  32. data/docs/{getting-started.md → getting-started/quick-start.md} +47 -18
  33. data/docs/guides/redis-queue-getting-started.md +697 -0
  34. data/docs/guides/redis-queue-patterns.md +889 -0
  35. data/docs/guides/redis-queue-production.md +1091 -0
  36. data/docs/index.md +64 -0
  37. data/docs/{dead_letter_queue.md → reference/dead-letter-queue.md} +2 -3
  38. data/docs/{logging.md → reference/logging.md} +1 -1
  39. data/docs/{message_deduplication.md → reference/message-deduplication.md} +1 -0
  40. data/docs/{proc_handlers_summary.md → reference/proc-handlers.md} +7 -6
  41. data/docs/{serializers.md → reference/serializers.md} +3 -5
  42. data/docs/{transports.md → reference/transports.md} +133 -11
  43. data/docs/transports/memory-transport.md +374 -0
  44. data/docs/transports/redis-enhanced-transport.md +524 -0
  45. data/docs/transports/redis-queue-transport.md +1304 -0
  46. data/docs/transports/redis-transport-comparison.md +496 -0
  47. data/docs/transports/redis-transport.md +509 -0
  48. data/examples/README.md +98 -5
  49. data/examples/city_scenario/911_emergency_call_flow.svg +99 -0
  50. data/examples/city_scenario/README.md +515 -0
  51. data/examples/city_scenario/ai_visitor_intelligence_flow.svg +108 -0
  52. data/examples/city_scenario/citizen.rb +195 -0
  53. data/examples/city_scenario/city_diagram.svg +125 -0
  54. data/examples/city_scenario/common/health_monitor.rb +80 -0
  55. data/examples/city_scenario/common/logger.rb +30 -0
  56. data/examples/city_scenario/emergency_dispatch_center.rb +270 -0
  57. data/examples/city_scenario/fire_department.rb +446 -0
  58. data/examples/city_scenario/fire_emergency_flow.svg +95 -0
  59. data/examples/city_scenario/health_department.rb +100 -0
  60. data/examples/city_scenario/health_monitoring_system.svg +130 -0
  61. data/examples/city_scenario/house.rb +244 -0
  62. data/examples/city_scenario/local_bank.rb +217 -0
  63. data/examples/city_scenario/messages/emergency_911_message.rb +81 -0
  64. data/examples/city_scenario/messages/emergency_resolved_message.rb +43 -0
  65. data/examples/city_scenario/messages/fire_dispatch_message.rb +43 -0
  66. data/examples/city_scenario/messages/fire_emergency_message.rb +45 -0
  67. data/examples/city_scenario/messages/health_check_message.rb +22 -0
  68. data/examples/city_scenario/messages/health_status_message.rb +35 -0
  69. data/examples/city_scenario/messages/police_dispatch_message.rb +46 -0
  70. data/examples/city_scenario/messages/silent_alarm_message.rb +38 -0
  71. data/examples/city_scenario/police_department.rb +316 -0
  72. data/examples/city_scenario/redis_monitor.rb +129 -0
  73. data/examples/city_scenario/redis_stats.rb +743 -0
  74. data/examples/city_scenario/room_for_improvement.md +240 -0
  75. data/examples/city_scenario/security_emergency_flow.svg +95 -0
  76. data/examples/city_scenario/service_internal_architecture.svg +154 -0
  77. data/examples/city_scenario/smart_message_ai_agent.rb +364 -0
  78. data/examples/city_scenario/start_demo.sh +236 -0
  79. data/examples/city_scenario/stop_demo.sh +106 -0
  80. data/examples/city_scenario/visitor.rb +631 -0
  81. data/examples/{10_message_deduplication.rb → memory/01_message_deduplication_demo.rb} +1 -1
  82. data/examples/{09_dead_letter_queue_demo.rb → memory/02_dead_letter_queue_demo.rb} +13 -40
  83. data/examples/{01_point_to_point_orders.rb → memory/03_point_to_point_orders.rb} +1 -1
  84. data/examples/{02_publish_subscribe_events.rb → memory/04_publish_subscribe_events.rb} +2 -2
  85. data/examples/{03_many_to_many_chat.rb → memory/05_many_to_many_chat.rb} +4 -4
  86. data/examples/{show_me.rb → memory/06_pretty_print_demo.rb} +1 -1
  87. data/examples/{05_proc_handlers.rb → memory/07_proc_handlers_demo.rb} +2 -2
  88. data/examples/{06_custom_logger_example.rb → memory/08_custom_logger_demo.rb} +17 -14
  89. data/examples/{07_error_handling_scenarios.rb → memory/09_error_handling_demo.rb} +4 -4
  90. data/examples/{08_entity_addressing_basic.rb → memory/10_entity_addressing_basic.rb} +8 -8
  91. data/examples/{08_entity_addressing_with_filtering.rb → memory/11_entity_addressing_with_filtering.rb} +6 -6
  92. data/examples/{09_regex_filtering_microservices.rb → memory/12_regex_filtering_microservices.rb} +2 -2
  93. data/examples/{10_header_block_configuration.rb → memory/13_header_block_configuration.rb} +6 -6
  94. data/examples/{11_global_configuration_example.rb → memory/14_global_configuration_demo.rb} +19 -8
  95. data/examples/{show_logger.rb → memory/15_logger_demo.rb} +1 -1
  96. data/examples/memory/README.md +163 -0
  97. data/examples/memory/memory_transport_architecture.svg +90 -0
  98. data/examples/memory/point_to_point_pattern.svg +94 -0
  99. data/examples/memory/publish_subscribe_pattern.svg +125 -0
  100. data/examples/{04_redis_smart_home_iot.rb → redis/01_smart_home_iot_demo.rb} +5 -5
  101. data/examples/redis/README.md +230 -0
  102. data/examples/redis/alert_system_flow.svg +127 -0
  103. data/examples/redis/dashboard_status_flow.svg +107 -0
  104. data/examples/redis/device_command_flow.svg +113 -0
  105. data/examples/redis/redis_transport_architecture.svg +115 -0
  106. data/examples/{smart_home_iot_dataflow.md → redis/smart_home_iot_dataflow.md} +4 -116
  107. data/examples/redis/smart_home_system_architecture.svg +133 -0
  108. data/examples/redis_enhanced/README.md +319 -0
  109. data/examples/redis_enhanced/enhanced_01_basic_patterns.rb +233 -0
  110. data/examples/redis_enhanced/enhanced_02_fluent_api.rb +331 -0
  111. data/examples/redis_enhanced/enhanced_03_dual_publishing.rb +281 -0
  112. data/examples/redis_enhanced/enhanced_04_advanced_routing.rb +419 -0
  113. data/examples/redis_queue/01_basic_messaging.rb +221 -0
  114. data/examples/redis_queue/01_comprehensive_examples.rb +508 -0
  115. data/examples/redis_queue/02_pattern_routing.rb +405 -0
  116. data/examples/redis_queue/03_fluent_api.rb +422 -0
  117. data/examples/redis_queue/04_load_balancing.rb +486 -0
  118. data/examples/redis_queue/05_microservices.rb +735 -0
  119. data/examples/redis_queue/06_emergency_alerts.rb +777 -0
  120. data/examples/redis_queue/07_queue_management.rb +587 -0
  121. data/examples/redis_queue/README.md +366 -0
  122. data/examples/redis_queue/enhanced_01_basic_patterns.rb +233 -0
  123. data/examples/redis_queue/enhanced_02_fluent_api.rb +331 -0
  124. data/examples/redis_queue/enhanced_03_dual_publishing.rb +281 -0
  125. data/examples/redis_queue/enhanced_04_advanced_routing.rb +419 -0
  126. data/examples/redis_queue/redis_queue_architecture.svg +148 -0
  127. data/ideas/README.md +41 -0
  128. data/ideas/agents.md +1001 -0
  129. data/ideas/database_transport.md +980 -0
  130. data/ideas/improvement.md +359 -0
  131. data/ideas/meshage.md +1788 -0
  132. data/ideas/message_discovery.md +178 -0
  133. data/ideas/message_schema.md +1381 -0
  134. data/lib/smart_message/.idea/.gitignore +8 -0
  135. data/lib/smart_message/.idea/markdown.xml +6 -0
  136. data/lib/smart_message/.idea/misc.xml +4 -0
  137. data/lib/smart_message/.idea/modules.xml +8 -0
  138. data/lib/smart_message/.idea/smart_message.iml +16 -0
  139. data/lib/smart_message/.idea/vcs.xml +6 -0
  140. data/lib/smart_message/addressing.rb +15 -0
  141. data/lib/smart_message/base.rb +0 -2
  142. data/lib/smart_message/configuration.rb +1 -1
  143. data/lib/smart_message/logger.rb +15 -4
  144. data/lib/smart_message/plugins.rb +5 -2
  145. data/lib/smart_message/serializer.rb +14 -0
  146. data/lib/smart_message/transport/redis_enhanced_transport.rb +399 -0
  147. data/lib/smart_message/transport/redis_queue_transport.rb +555 -0
  148. data/lib/smart_message/transport/registry.rb +1 -0
  149. data/lib/smart_message/transport.rb +34 -1
  150. data/lib/smart_message/version.rb +1 -1
  151. data/lib/smart_message.rb +5 -52
  152. data/mkdocs.yml +184 -0
  153. data/p2p_plan.md +326 -0
  154. data/p2p_roadmap.md +287 -0
  155. data/smart_message.gemspec +2 -0
  156. data/smart_message.svg +51 -0
  157. metadata +170 -44
  158. data/docs/README.md +0 -57
  159. data/examples/dead_letters.jsonl +0 -12
  160. data/examples/temp.txt +0 -94
  161. data/examples/tmux_chat/README.md +0 -283
  162. data/examples/tmux_chat/bot_agent.rb +0 -278
  163. data/examples/tmux_chat/human_agent.rb +0 -199
  164. data/examples/tmux_chat/room_monitor.rb +0 -160
  165. data/examples/tmux_chat/shared_chat_system.rb +0 -328
  166. data/examples/tmux_chat/start_chat_demo.sh +0 -190
  167. data/examples/tmux_chat/stop_chat_demo.sh +0 -22
  168. /data/docs/{properties.md → core-concepts/properties.md} +0 -0
  169. /data/docs/{ideas_to_think_about.md → development/ideas.md} +0 -0
@@ -0,0 +1,1381 @@
1
+ ## Message Schema Registry
2
+
3
+ ### Overview
4
+
5
+ SmartMessage can automatically register and track message schemas, enabling:
6
+
7
+ - **Automatic schema registration** when message classes are defined
8
+ - **Schema evolution tracking** across versions and deployments
9
+ - **Schema storage** in a centralized database for persistence
10
+ - **Schema versioning** to track changes over time
11
+ - **Compliance tracking** for audit requirements
12
+
13
+ ### Database Schema for Message Registry
14
+
15
+ ```sql
16
+ CREATE TABLE smart_message_schemas (
17
+ id BIGSERIAL PRIMARY KEY,
18
+
19
+ -- Class identification
20
+ class_name VARCHAR NOT NULL,
21
+ class_version INTEGER NOT NULL DEFAULT 1,
22
+ class_description TEXT,
23
+
24
+ -- Complete schema storage
25
+ schema_definition JSONB NOT NULL, -- Full serialized class definition
26
+ properties_schema JSONB NOT NULL, -- Properties for quick access/filtering
27
+ validations_schema JSONB, -- Validation rules and constraints
28
+ configuration_schema JSONB, -- Transport/serializer configs
29
+
30
+ -- Registration metadata
31
+ registered_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
32
+ registered_by VARCHAR NOT NULL, -- Service/application identifier
33
+ deployment_environment VARCHAR DEFAULT 'unknown', -- dev/staging/prod
34
+ ruby_version VARCHAR,
35
+ framework_version VARCHAR,
36
+
37
+ -- Schema evolution tracking
38
+ parent_schema_id BIGINT REFERENCES smart_message_schemas(id),
39
+ schema_hash VARCHAR(64) NOT NULL, -- SHA256 of schema for change detection
40
+ schema_fingerprint VARCHAR(32), -- Short hash for quick comparison
41
+
42
+ -- Lifecycle management
43
+ status VARCHAR DEFAULT 'active' CHECK (status IN ('active', 'deprecated', 'archived')),
44
+ deprecated_at TIMESTAMP WITH TIME ZONE,
45
+ archived_at TIMESTAMP WITH TIME ZONE,
46
+
47
+ -- Performance constraints
48
+ UNIQUE(class_name, class_version, registered_by),
49
+ INDEX idx_schema_discovery (class_name, status, registered_by),
50
+ INDEX idx_schema_evolution (parent_schema_id, registered_at),
51
+ INDEX idx_schema_fingerprint (schema_fingerprint),
52
+ INDEX idx_active_schemas (status, registered_at DESC) WHERE status = 'active'
53
+ );
54
+ ```
55
+
56
+ ### Automatic Schema Registration
57
+
58
+ ```ruby
59
+ # Enhanced SmartMessage::Base with automatic schema registration
60
+ class Base < Hashie::Dash
61
+ # Hook that fires when any class inherits from SmartMessage::Base
62
+ def self.inherited(subclass)
63
+ super(subclass)
64
+
65
+ # Set up deferred registration after class is fully loaded
66
+ subclass.define_singleton_method(:method_added) do |method_name|
67
+ # Trigger registration after class is complete
68
+ if method_name == :initialize && !@schema_registered
69
+ @schema_registered = true
70
+ register_schema_async
71
+ end
72
+ end
73
+ end
74
+
75
+ # Extract complete class schema for serialization
76
+ def self.serialize_schema
77
+ {
78
+ class_name: name,
79
+ class_version: version || 1,
80
+ class_description: description,
81
+ properties_schema: extract_properties_schema,
82
+ validations_schema: extract_validations_schema,
83
+ configuration_schema: extract_configuration_schema,
84
+
85
+ # Metadata
86
+ created_at: Time.current.iso8601,
87
+ ruby_version: RUBY_VERSION,
88
+ framework_version: SmartMessage::VERSION,
89
+
90
+ # Schema fingerprinting
91
+ schema_hash: calculate_schema_hash
92
+ }
93
+ end
94
+
95
+ private
96
+
97
+ def self.register_schema_async
98
+ # Non-blocking registration to avoid slowing class loading
99
+ Thread.new do
100
+ begin
101
+ SmartMessage::SchemaRegistry.register(self)
102
+ logger.debug "[SmartMessage] Registered schema: #{name} v#{version || 1}"
103
+ rescue => e
104
+ logger.warn "[SmartMessage] Failed to register schema for #{name}: #{e.message}"
105
+ end
106
+ end
107
+ end
108
+
109
+ def self.extract_properties_schema
110
+ return [] unless respond_to?(:properties)
111
+
112
+ properties.map do |name, opts|
113
+ {
114
+ name: name.to_s,
115
+ type: opts[:type]&.name || infer_property_type(name),
116
+ required: opts[:required] || false,
117
+ default: opts[:default],
118
+ description: property_description(name),
119
+ validation_rule: extract_property_validation(name),
120
+ constraints: extract_property_constraints(name)
121
+ }
122
+ end
123
+ end
124
+
125
+ def self.extract_configuration_schema
126
+ {
127
+ transport: transport.class.name,
128
+ serializer: serializer.class.name,
129
+ logger: logger.class.name,
130
+ plugins: extract_plugin_configuration
131
+ }
132
+ rescue
133
+ {}
134
+ end
135
+
136
+ def self.calculate_schema_hash
137
+ schema_content = {
138
+ name: name,
139
+ version: version || 1,
140
+ properties: properties&.keys&.sort,
141
+ validations: extract_validation_keys
142
+ }
143
+ Digest::SHA256.hexdigest(schema_content.to_json)
144
+ end
145
+ end
146
+ ```
147
+
148
+ ### Schema Registry Implementation
149
+
150
+ ```ruby
151
+ module SmartMessage
152
+ class SchemaRegistry
153
+ extend self
154
+
155
+ def register(message_class)
156
+ return unless database_available?
157
+
158
+ schema = message_class.serialize_schema
159
+
160
+ connection_pool.with do |conn|
161
+ # Check if schema already exists with same hash
162
+ existing = conn.execute(<<~SQL, [schema[:class_name], schema[:schema_hash]]).first
163
+ SELECT id FROM smart_message_schemas
164
+ WHERE class_name = $1 AND schema_hash = $2 AND status = 'active'
165
+ SQL
166
+
167
+ return if existing # Schema already registered
168
+
169
+ # Find parent schema (previous version)
170
+ parent = find_parent_schema(schema[:class_name], schema[:class_version])
171
+
172
+ # Insert new schema
173
+ conn.execute(<<~SQL, schema_insert_params(schema, parent))
174
+ INSERT INTO smart_message_schemas (
175
+ class_name, class_version, class_description, schema_definition,
176
+ properties_schema, validations_schema, configuration_schema,
177
+ registered_by, deployment_environment, ruby_version, framework_version,
178
+ parent_schema_id, schema_hash, schema_fingerprint
179
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
180
+ SQL
181
+ end
182
+ end
183
+
184
+ # Schema evolution: compare versions
185
+ def schema_evolution(class_name)
186
+ connection_pool.with do |conn|
187
+ conn.execute(<<~SQL, [class_name])
188
+ WITH RECURSIVE evolution AS (
189
+ -- Start with latest version
190
+ SELECT id, class_name, class_version, parent_schema_id,
191
+ schema_hash, registered_at, 0 as depth
192
+ FROM smart_message_schemas
193
+ WHERE class_name = $1 AND status = 'active'
194
+ ORDER BY class_version DESC LIMIT 1
195
+
196
+ UNION ALL
197
+
198
+ -- Follow parent chain
199
+ SELECT s.id, s.class_name, s.class_version, s.parent_schema_id,
200
+ s.schema_hash, s.registered_at, e.depth + 1
201
+ FROM smart_message_schemas s
202
+ JOIN evolution e ON s.id = e.parent_schema_id
203
+ )
204
+ SELECT * FROM evolution ORDER BY depth ASC;
205
+ SQL
206
+ end
207
+ end
208
+
209
+ private
210
+
211
+ def database_available?
212
+ defined?(SmartMessage::Transport::DatabaseTransport) &&
213
+ SmartMessage.configuration.schema_registry_enabled?
214
+ end
215
+
216
+ def connection_pool
217
+ @connection_pool ||= SmartMessage::Transport::DatabaseTransport.default.connection_pool
218
+ end
219
+
220
+ def find_parent_schema(class_name, class_version)
221
+ return nil if class_version <= 1
222
+
223
+ connection_pool.with do |conn|
224
+ conn.execute(<<~SQL, [class_name, class_version - 1]).first
225
+ SELECT id FROM smart_message_schemas
226
+ WHERE class_name = $1 AND class_version = $2 AND status = 'active'
227
+ ORDER BY registered_at DESC LIMIT 1
228
+ SQL
229
+ end
230
+ end
231
+
232
+ def schema_insert_params(schema, parent)
233
+ [
234
+ schema[:class_name],
235
+ schema[:class_version],
236
+ schema[:class_description],
237
+ schema.to_json,
238
+ schema[:properties_schema].to_json,
239
+ schema[:validations_schema]&.to_json,
240
+ schema[:configuration_schema]&.to_json,
241
+ SmartMessage.configuration.service_name || 'unknown',
242
+ SmartMessage.configuration.environment || 'unknown',
243
+ schema[:ruby_version],
244
+ schema[:framework_version],
245
+ parent&.fetch('id'),
246
+ schema[:schema_hash],
247
+ schema[:schema_hash][0..7] # First 8 chars as fingerprint
248
+ ]
249
+ end
250
+ end
251
+ end
252
+ ```
253
+
254
+ ### Schema Evolution Tracking
255
+
256
+ ```ruby
257
+ # See how OrderMessage has evolved over time
258
+ evolution = SmartMessage::SchemaRegistry.schema_evolution('OrderMessage')
259
+
260
+ evolution.each do |version_info|
261
+ puts "Version #{version_info['class_version']}: #{version_info['registered_at']}"
262
+ puts " Hash: #{version_info['schema_hash'][0..7]}..."
263
+ puts " Parent: #{version_info['parent_schema_id']}"
264
+ end
265
+
266
+ # Compare schemas between versions
267
+ def compare_schemas(class_name, version1, version2)
268
+ schema1 = fetch_schema(class_name, version1)
269
+ schema2 = fetch_schema(class_name, version2)
270
+
271
+ {
272
+ properties_added: schema2['properties'] - schema1['properties'],
273
+ properties_removed: schema1['properties'] - schema2['properties'],
274
+ validations_changed: schema1['validations'] != schema2['validations']
275
+ }
276
+ end
277
+ ```
278
+
279
+ ### JSON Schema Representation
280
+
281
+ SmartMessage classes can be represented as standard JSON Schema documents, providing interoperability with other systems and languages.
282
+
283
+ ```ruby
284
+ class Base < Hashie::Dash
285
+ def self.to_json_schema
286
+ {
287
+ "$schema" => "https://json-schema.org/draft/2020-12/schema",
288
+ "$id" => "https://smartmessage.io/schemas/#{name.underscore}/v#{version || 1}",
289
+
290
+ "title" => name,
291
+ "description" => description || "#{name} message class",
292
+ "type" => "object",
293
+ "version" => version || 1,
294
+
295
+ # Message metadata
296
+ "x-smart-message" => {
297
+ "class_name" => name,
298
+ "version" => version || 1,
299
+ "transport" => transport&.class&.name,
300
+ "serializer" => serializer&.class&.name,
301
+ "registered_at" => Time.current.iso8601,
302
+ "ruby_version" => RUBY_VERSION,
303
+ "framework_version" => SmartMessage::VERSION
304
+ },
305
+
306
+ # Properties with descriptions and validations
307
+ "properties" => properties_to_json_schema,
308
+ "required" => extract_required_properties,
309
+ "additionalProperties" => false
310
+ }
311
+ end
312
+
313
+ private
314
+
315
+ def self.properties_to_json_schema
316
+ return {} unless respond_to?(:properties)
317
+
318
+ schema_props = {}
319
+
320
+ properties.each do |name, opts|
321
+ prop_schema = {
322
+ "description" => property_description(name) || "Property: #{name}"
323
+ }
324
+
325
+ # Infer JSON Schema type from Ruby type
326
+ if opts[:type]
327
+ prop_schema["type"] = ruby_to_json_type(opts[:type])
328
+ end
329
+
330
+ # Add validation constraints
331
+ if validation = property_validation(name)
332
+ case validation
333
+ when Regexp
334
+ prop_schema["pattern"] = validation.source
335
+ when Range
336
+ prop_schema["minimum"] = validation.min if validation.min
337
+ prop_schema["maximum"] = validation.max if validation.max
338
+ when Array
339
+ prop_schema["enum"] = validation
340
+ when Proc, Method
341
+ prop_schema["x-custom-validation"] = validation.source_location.join(":")
342
+ end
343
+ end
344
+
345
+ # Add default value if present
346
+ if opts[:default]
347
+ prop_schema["default"] = opts[:default]
348
+ end
349
+
350
+ # Add format hints for common patterns
351
+ if name.to_s.include?("email")
352
+ prop_schema["format"] = "email"
353
+ elsif name.to_s.include?("date")
354
+ prop_schema["format"] = "date-time"
355
+ elsif name.to_s.include?("url") || name.to_s.include?("uri")
356
+ prop_schema["format"] = "uri"
357
+ end
358
+
359
+ schema_props[name.to_s] = prop_schema
360
+ end
361
+
362
+ # Add SmartMessage header as a property
363
+ schema_props["_sm_header"] = {
364
+ "type" => "object",
365
+ "description" => "SmartMessage routing and metadata header",
366
+ "properties" => {
367
+ "uuid" => {"type" => "string", "format" => "uuid"},
368
+ "from" => {"type" => "string", "description" => "Message sender identifier"},
369
+ "to" => {"type" => ["string", "null"], "description" => "Message recipient identifier"},
370
+ "version" => {"type" => "integer", "description" => "Schema version"},
371
+ "published_at" => {"type" => "string", "format" => "date-time"},
372
+ "message_class" => {"type" => "string"},
373
+ "thread_id" => {"type" => ["string", "null"]},
374
+ "correlation_id" => {"type" => ["string", "null"]}
375
+ }
376
+ }
377
+
378
+ schema_props
379
+ end
380
+
381
+ def self.ruby_to_json_type(ruby_type)
382
+ case ruby_type.name
383
+ when "String" then "string"
384
+ when "Integer", "Fixnum", "Bignum" then "integer"
385
+ when "Float", "BigDecimal" then "number"
386
+ when "TrueClass", "FalseClass", "Boolean" then "boolean"
387
+ when "Array" then "array"
388
+ when "Hash" then "object"
389
+ when "NilClass" then "null"
390
+ else "string" # Default fallback
391
+ end
392
+ end
393
+
394
+ def self.extract_required_properties
395
+ return [] unless respond_to?(:properties)
396
+
397
+ properties.select { |_, opts| opts[:required] }.keys.map(&:to_s)
398
+ end
399
+ end
400
+ ```
401
+
402
+ #### Example: OrderMessage as JSON Schema
403
+
404
+ ```ruby
405
+ class OrderMessage < SmartMessage::Base
406
+ version 2
407
+ description "Represents a customer order in the e-commerce system"
408
+
409
+ property :order_id,
410
+ required: true,
411
+ description: "Unique identifier for the order"
412
+
413
+ property :customer_email,
414
+ required: true,
415
+ validate: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i,
416
+ description: "Customer's email address for order notifications"
417
+
418
+ property :amount,
419
+ required: true,
420
+ type: Float,
421
+ validate: ->(v) { v > 0 },
422
+ description: "Total order amount in the specified currency"
423
+
424
+ property :currency,
425
+ default: "USD",
426
+ validate: ["USD", "EUR", "GBP", "CAD"],
427
+ description: "ISO 4217 currency code"
428
+
429
+ property :items,
430
+ type: Array,
431
+ description: "List of items in the order"
432
+
433
+ property :created_at,
434
+ type: Time,
435
+ description: "Timestamp when the order was created"
436
+ end
437
+
438
+ # Generate JSON Schema
439
+ puts JSON.pretty_generate(OrderMessage.to_json_schema)
440
+ ```
441
+
442
+ Output:
443
+ ```json
444
+ {
445
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
446
+ "$id": "https://smartmessage.io/schemas/order_message/v2",
447
+ "title": "OrderMessage",
448
+ "description": "Represents a customer order in the e-commerce system",
449
+ "type": "object",
450
+ "version": 2,
451
+ "x-smart-message": {
452
+ "class_name": "OrderMessage",
453
+ "version": 2,
454
+ "transport": "SmartMessage::Transport::RabbitMQ",
455
+ "serializer": "SmartMessage::Serializer::JSON",
456
+ "registered_at": "2024-01-15T10:30:00Z",
457
+ "ruby_version": "3.2.0",
458
+ "framework_version": "1.0.0"
459
+ },
460
+ "properties": {
461
+ "order_id": {
462
+ "description": "Unique identifier for the order",
463
+ "type": "string"
464
+ },
465
+ "customer_email": {
466
+ "description": "Customer's email address for order notifications",
467
+ "type": "string",
468
+ "format": "email",
469
+ "pattern": "[\\w+\\-.]+@[a-z\\d\\-]+(\\.[a-z\\d\\-]+)*\\.[a-z]+"
470
+ },
471
+ "amount": {
472
+ "description": "Total order amount in the specified currency",
473
+ "type": "number",
474
+ "x-custom-validation": "order_message.rb:15"
475
+ },
476
+ "currency": {
477
+ "description": "ISO 4217 currency code",
478
+ "type": "string",
479
+ "default": "USD",
480
+ "enum": ["USD", "EUR", "GBP", "CAD"]
481
+ },
482
+ "items": {
483
+ "description": "List of items in the order",
484
+ "type": "array"
485
+ },
486
+ "created_at": {
487
+ "description": "Timestamp when the order was created",
488
+ "type": "string",
489
+ "format": "date-time"
490
+ },
491
+ "_sm_header": {
492
+ "type": "object",
493
+ "description": "SmartMessage routing and metadata header",
494
+ "properties": {
495
+ "uuid": {"type": "string", "format": "uuid"},
496
+ "from": {"type": "string", "description": "Message sender identifier"},
497
+ "to": {"type": ["string", "null"], "description": "Message recipient identifier"},
498
+ "version": {"type": "integer", "description": "Schema version"},
499
+ "published_at": {"type": "string", "format": "date-time"},
500
+ "message_class": {"type": "string"},
501
+ "thread_id": {"type": ["string", "null"]},
502
+ "correlation_id": {"type": ["string", "null"]}
503
+ }
504
+ }
505
+ },
506
+ "required": ["order_id", "customer_email", "amount"],
507
+ "additionalProperties": false
508
+ }
509
+ ```
510
+
511
+ ### JSON Schema Integration Benefits
512
+
513
+ 1. **Standard Compliance**: Uses JSON Schema draft 2020-12 for maximum compatibility
514
+ 2. **Rich Descriptions**: Both message-level and property-level descriptions included
515
+ 3. **Validation Portability**: Validation rules translated to JSON Schema constraints
516
+ 4. **Type Safety**: Ruby types mapped to JSON Schema types with format hints
517
+ 5. **API Documentation**: Can generate OpenAPI/AsyncAPI specs from schemas
518
+ 6. **Cross-Language Support**: Other languages can validate messages using the schema
519
+ 7. **Tooling Integration**: Works with JSON Schema validators and code generators
520
+
521
+ ### Dynamic Class Reconstruction from JSON Schema
522
+
523
+ SmartMessage can dynamically rebuild Ruby classes from stored JSON Schemas, enabling complete round-trip conversion:
524
+
525
+ ```ruby
526
+ module SmartMessage
527
+ class SchemaRegistry
528
+ # Create a Ruby class from a JSON Schema stored in database
529
+ def self.from_json_schema(json_schema, namespace = Object)
530
+ schema = json_schema.is_a?(String) ? JSON.parse(json_schema) : json_schema
531
+
532
+ # Extract class name from schema
533
+ class_name = schema.dig("x-smart-message", "class_name") ||
534
+ schema["title"] ||
535
+ raise(ArgumentError, "No class name found in schema")
536
+
537
+ simple_class_name = class_name.split('::').last
538
+
539
+ # Create new class inheriting from SmartMessage::Base
540
+ dynamic_class = Class.new(SmartMessage::Base) do
541
+ # Set class metadata
542
+ version schema.dig("x-smart-message", "version") || schema["version"]
543
+ description schema["description"] if schema["description"]
544
+
545
+ # Configure plugins if specified
546
+ if transport_name = schema.dig("x-smart-message", "transport")
547
+ transport transport_name.constantize.new rescue nil
548
+ end
549
+
550
+ if serializer_name = schema.dig("x-smart-message", "serializer")
551
+ serializer serializer_name.constantize.new rescue nil
552
+ end
553
+
554
+ # Process properties from JSON Schema
555
+ if properties = schema["properties"]
556
+ required_fields = schema["required"] || []
557
+
558
+ properties.each do |prop_name, prop_schema|
559
+ # Skip the header property
560
+ next if prop_name == "_sm_header"
561
+
562
+ # Build property options
563
+ prop_options = {}
564
+
565
+ # Set required flag
566
+ prop_options[:required] = true if required_fields.include?(prop_name)
567
+
568
+ # Set Ruby type from JSON Schema type
569
+ if json_type = prop_schema["type"]
570
+ prop_options[:type] = json_to_ruby_type(json_type)
571
+ end
572
+
573
+ # Set default value
574
+ if prop_schema.key?("default")
575
+ prop_options[:default] = prop_schema["default"]
576
+ end
577
+
578
+ # Set description
579
+ prop_options[:description] = prop_schema["description"] if prop_schema["description"]
580
+
581
+ # Set validation from JSON Schema constraints
582
+ validation = extract_validation_from_json_schema(prop_schema)
583
+ prop_options[:validate] = validation if validation
584
+
585
+ # Set validation message if custom validation exists
586
+ if prop_schema["x-custom-validation"]
587
+ prop_options[:validation_message] = "Value failed custom validation"
588
+ end
589
+
590
+ # Define the property
591
+ property prop_name.to_sym, **prop_options
592
+ end
593
+ end
594
+
595
+ # Mark as dynamically created from JSON Schema
596
+ define_singleton_method(:dynamically_created?) { true }
597
+ define_singleton_method(:source_json_schema) { schema }
598
+
599
+ # Override to_json_schema to return the original
600
+ define_singleton_method(:to_json_schema) { schema }
601
+ end
602
+
603
+ # Set the class constant in the namespace
604
+ namespace.const_set(simple_class_name, dynamic_class)
605
+ dynamic_class
606
+ end
607
+
608
+ private
609
+
610
+ def self.json_to_ruby_type(json_type)
611
+ type_map = {
612
+ "string" => String,
613
+ "integer" => Integer,
614
+ "number" => Float,
615
+ "boolean" => TrueClass,
616
+ "array" => Array,
617
+ "object" => Hash,
618
+ "null" => NilClass
619
+ }
620
+
621
+ # Handle array of types (e.g., ["string", "null"])
622
+ if json_type.is_a?(Array)
623
+ # Find first non-null type
624
+ non_null_type = json_type.find { |t| t != "null" }
625
+ type_map[non_null_type] || String
626
+ else
627
+ type_map[json_type] || String
628
+ end
629
+ end
630
+
631
+ def self.extract_validation_from_json_schema(prop_schema)
632
+ # Enum validation
633
+ if enum_values = prop_schema["enum"]
634
+ return enum_values
635
+ end
636
+
637
+ # Pattern validation
638
+ if pattern = prop_schema["pattern"]
639
+ return Regexp.new(pattern)
640
+ end
641
+
642
+ # Range validation for numbers
643
+ if prop_schema["minimum"] || prop_schema["maximum"]
644
+ min = prop_schema["minimum"] || -Float::INFINITY
645
+ max = prop_schema["maximum"] || Float::INFINITY
646
+ return (min..max)
647
+ end
648
+
649
+ # Format-based validation
650
+ case prop_schema["format"]
651
+ when "email"
652
+ return /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
653
+ when "uri", "url"
654
+ return /\A#{URI::regexp}\z/
655
+ when "uuid"
656
+ return /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/i
657
+ end
658
+
659
+ nil
660
+ end
661
+ end
662
+ end
663
+ ```
664
+
665
+ ### Round-Trip Example
666
+
667
+ ```ruby
668
+ # Original message class
669
+ class OrderMessage < SmartMessage::Base
670
+ version 2
671
+ description "Represents a customer order in the e-commerce system"
672
+
673
+ property :order_id,
674
+ required: true,
675
+ description: "Unique identifier for the order"
676
+
677
+ property :customer_email,
678
+ required: true,
679
+ validate: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i,
680
+ description: "Customer's email address for order notifications"
681
+
682
+ property :amount,
683
+ required: true,
684
+ type: Float,
685
+ description: "Total order amount in the specified currency"
686
+
687
+ property :currency,
688
+ default: "USD",
689
+ validate: ["USD", "EUR", "GBP", "CAD"],
690
+ description: "ISO 4217 currency code"
691
+ end
692
+
693
+ # Step 1: Convert to JSON Schema and save to database
694
+ json_schema = OrderMessage.to_json_schema
695
+ connection.execute(
696
+ "INSERT INTO message_schemas (class_name, schema_json) VALUES ($1, $2)",
697
+ ["OrderMessage", json_schema.to_json]
698
+ )
699
+
700
+ # Step 2: Later, retrieve and reconstruct the class
701
+ stored_schema = connection.execute(
702
+ "SELECT schema_json FROM message_schemas WHERE class_name = $1",
703
+ ["OrderMessage"]
704
+ ).first["schema_json"]
705
+
706
+ # Step 3: Dynamically create the class from JSON Schema
707
+ ReconstructedOrderMessage = SmartMessage::SchemaRegistry.from_json_schema(
708
+ stored_schema
709
+ )
710
+
711
+ # Step 4: Use the reconstructed class
712
+ order = ReconstructedOrderMessage.new(
713
+ order_id: "ORD-123",
714
+ customer_email: "customer@example.com",
715
+ amount: 99.99,
716
+ currency: "USD"
717
+ )
718
+
719
+ # Verify it works
720
+ order.valid? # => true
721
+ order.publish # Works with configured transport/serializer
722
+ ```
723
+
724
+ ### Schema Storage Enhancement
725
+
726
+ The schema registry can store both the internal format and JSON Schema:
727
+
728
+ ```ruby
729
+ def self.serialize_schema
730
+ {
731
+ class_name: name,
732
+ class_version: version || 1,
733
+ class_description: description,
734
+ properties_schema: extract_properties_schema,
735
+ validations_schema: extract_validations_schema,
736
+ configuration_schema: extract_configuration_schema,
737
+ json_schema: to_json_schema, # Add JSON Schema representation
738
+
739
+ # Metadata
740
+ created_at: Time.current.iso8601,
741
+ ruby_version: RUBY_VERSION,
742
+ framework_version: SmartMessage::VERSION,
743
+
744
+ # Schema fingerprinting
745
+ schema_hash: calculate_schema_hash
746
+ }
747
+ end
748
+ ```
749
+
750
+ ### Dynamic Schema Management Use Cases
751
+
752
+ With bidirectional JSON Schema conversion, applications gain powerful capabilities:
753
+
754
+ ```ruby
755
+ # Save any message class to database
756
+ json = MyMessage.to_json_schema
757
+ DB.execute("INSERT INTO schemas (name, definition) VALUES (?, ?)",
758
+ [MyMessage.name, json.to_json])
759
+
760
+ # Later, recreate the class without the original Ruby code
761
+ stored_json = DB.execute("SELECT definition FROM schemas WHERE name = ?",
762
+ ["MyMessage"]).first
763
+ MyMessage = SmartMessage.from_json_schema(stored_json)
764
+
765
+ # The recreated class is fully functional
766
+ msg = MyMessage.new(data: "test")
767
+ msg.publish
768
+ ```
769
+
770
+ #### Key Capabilities Enabled
771
+
772
+ 1. **Schema Marketplace**
773
+ ```ruby
774
+ # Service A publishes its message schemas
775
+ OrderMessage.to_json_schema.tap do |schema|
776
+ SchemaRegistry.publish(schema, visibility: :public)
777
+ end
778
+
779
+ # Service B discovers and uses them
780
+ available_schemas = SchemaRegistry.browse(tags: ["ecommerce"])
781
+ OrderMessage = SmartMessage.from_json_schema(available_schemas.first)
782
+ ```
783
+
784
+ 2. **Runtime Schema Updates**
785
+ ```ruby
786
+ # Admin UI updates schema definition
787
+ updated_schema = modify_schema_via_ui(current_schema)
788
+ DB.execute("UPDATE schemas SET definition = ? WHERE name = ?",
789
+ [updated_schema.to_json, "OrderMessage"])
790
+
791
+ # Application reloads the class without restart
792
+ Object.send(:remove_const, :OrderMessage) if defined?(OrderMessage)
793
+ OrderMessage = SmartMessage.from_json_schema(updated_schema)
794
+ ```
795
+
796
+ 3. **Multi-Tenant Schemas**
797
+ ```ruby
798
+ # Each tenant can have custom message schemas
799
+ tenant_schema = DB.execute(
800
+ "SELECT definition FROM tenant_schemas WHERE tenant_id = ? AND name = ?",
801
+ [tenant.id, "InvoiceMessage"]
802
+ ).first
803
+
804
+ # Dynamically create tenant-specific class
805
+ tenant_class = SmartMessage.from_json_schema(
806
+ tenant_schema,
807
+ namespace: "Tenant#{tenant.id}".constantize
808
+ )
809
+ ```
810
+
811
+ 4. **Schema Versioning & Migration**
812
+ ```ruby
813
+ # Store multiple versions
814
+ versions = DB.execute(
815
+ "SELECT version, definition FROM schema_versions WHERE name = ? ORDER BY version",
816
+ ["PaymentMessage"]
817
+ )
818
+
819
+ # Create version-specific classes
820
+ versions.each do |row|
821
+ version_class = SmartMessage.from_json_schema(row['definition'])
822
+ const_set("PaymentMessageV#{row['version']}", version_class)
823
+ end
824
+
825
+ # Handle messages from different versions
826
+ def process_payment(raw_message)
827
+ version = raw_message['_sm_header']['version']
828
+ handler = const_get("PaymentMessageV#{version}")
829
+ handler.new(raw_message).process
830
+ end
831
+ ```
832
+
833
+ 5. **Schema-Driven Development**
834
+ ```ruby
835
+ # Define schemas in a UI or configuration file
836
+ schema_config = YAML.load_file("message_schemas.yml")
837
+
838
+ # Generate all message classes at startup
839
+ schema_config.each do |name, definition|
840
+ json_schema = build_json_schema(definition)
841
+ const_set(name, SmartMessage.from_json_schema(json_schema))
842
+ end
843
+
844
+ # No Ruby message class files needed!
845
+ ```
846
+
847
+ 6. **Cross-Language Schema Sharing**
848
+ ```ruby
849
+ # Export schemas for other languages
850
+ File.write("schemas/order_message.json", OrderMessage.to_json_schema.to_json)
851
+
852
+ # Python service can validate using standard JSON Schema
853
+ import jsonschema
854
+ schema = json.load(open("schemas/order_message.json"))
855
+ jsonschema.validate(message_data, schema)
856
+
857
+ # Then Ruby service can reconstruct the class
858
+ OrderMessage = SmartMessage.from_json_schema(
859
+ File.read("schemas/order_message.json")
860
+ )
861
+ ```
862
+
863
+ 7. **A/B Testing Message Formats**
864
+ ```ruby
865
+ # Store experimental schema variants
866
+ variants = {
867
+ control: fetch_schema("OrderMessage", variant: "control"),
868
+ test: fetch_schema("OrderMessage", variant: "test")
869
+ }
870
+
871
+ # Dynamically select variant
872
+ variant = ab_test.variant_for(user)
873
+ OrderMessage = SmartMessage.from_json_schema(variants[variant])
874
+ ```
875
+
876
+ 8. **Schema Compliance & Governance**
877
+ ```ruby
878
+ # Central schema repository with approval workflow
879
+ pending_schema = SchemaApproval.find(id).schema_definition
880
+
881
+ # Preview the schema before approval
882
+ PreviewClass = SmartMessage.from_json_schema(pending_schema)
883
+ preview_msg = PreviewClass.new(sample_data)
884
+ validate_compliance(preview_msg)
885
+
886
+ # Once approved, deploy to production
887
+ if approved?
888
+ ProductionMessage = SmartMessage.from_json_schema(pending_schema)
889
+ cache_class(ProductionMessage)
890
+ end
891
+ ```
892
+
893
+ This "code as data" approach fundamentally changes how message contracts are managed, enabling:
894
+ - **No-code schema management** via UIs
895
+ - **Runtime flexibility** without deployments
896
+ - **Schema portability** across services and languages
897
+ - **Centralized governance** with distributed execution
898
+ - **Version coexistence** without code duplication
899
+
900
+ ### JSON Schema vs Ruby Marshal/DRb Comparison
901
+
902
+ Ruby provides built-in serialization via Marshal and distributed communication via DRb. Here's how the JSON Schema approach differs:
903
+
904
+ #### Ruby Marshal/DRb Approach
905
+
906
+ ```ruby
907
+ # Marshal serializes Ruby objects including their class definition
908
+ class OrderMessage
909
+ attr_accessor :order_id, :amount, :customer_email
910
+
911
+ def initialize(order_id, amount, customer_email)
912
+ @order_id = order_id
913
+ @amount = amount
914
+ @customer_email = customer_email
915
+ end
916
+ end
917
+
918
+ # Serialize the entire object
919
+ order = OrderMessage.new("123", 99.99, "test@example.com")
920
+ serialized = Marshal.dump(order)
921
+
922
+ # Deserialize - requires the OrderMessage class to exist
923
+ restored = Marshal.load(serialized) # Needs OrderMessage class loaded!
924
+
925
+ # DRb can share objects between Ruby processes
926
+ require 'drb/drb'
927
+ DRb.start_service("druby://localhost:8787", order)
928
+ ```
929
+
930
+ #### JSON Schema Approach
931
+
932
+ ```ruby
933
+ # Schema defines the structure, not the instance
934
+ schema = OrderMessage.to_json_schema # Just the schema, not data
935
+
936
+ # Store schema separately from instances
937
+ DB.save_schema(schema)
938
+
939
+ # Later, recreate the class definition itself
940
+ OrderMessage = SmartMessage.from_json_schema(schema)
941
+
942
+ # Now create instances
943
+ order = OrderMessage.new(order_id: "123", amount: 99.99)
944
+ ```
945
+
946
+ #### Key Differences
947
+
948
+ | Aspect | Marshal/DRb | JSON Schema |
949
+ |--------|------------|-------------|
950
+ | **What's Serialized** | Object instances with data | Class structure/definition |
951
+ | **Cross-Language** | Ruby only | Any language supporting JSON Schema |
952
+ | **Schema Evolution** | Breaks on class changes | Versions tracked explicitly |
953
+ | **Security** | Can execute arbitrary code | Safe, declarative only |
954
+ | **Storage Size** | Includes Ruby internals | Compact, standard JSON |
955
+ | **Validation** | None built-in | JSON Schema validation |
956
+ | **Documentation** | Not included | Descriptions embedded |
957
+ | **Human Readable** | Binary format | Plain JSON |
958
+ | **Class Required** | Must exist before deserializing | Creates class from schema |
959
+
960
+ #### When to Use Each
961
+
962
+ **Use Marshal/DRb when:**
963
+ - Working exclusively in Ruby ecosystem
964
+ - Need to serialize complex object graphs
965
+ - Want to preserve exact Ruby object state
966
+ - Building Ruby-only distributed systems
967
+ - Performance is critical (binary is faster)
968
+
969
+ **Use JSON Schema when:**
970
+ - Need cross-language compatibility
971
+ - Want human-readable, editable schemas
972
+ - Building microservices in multiple languages
973
+ - Need schema versioning and evolution
974
+ - Want to generate documentation
975
+ - Security is a concern (no code execution)
976
+ - Building schema management tools/UIs
977
+
978
+ #### Hybrid Approach
979
+
980
+ SmartMessage can actually use both:
981
+
982
+ ```ruby
983
+ # Use JSON Schema for class definition
984
+ OrderMessage = SmartMessage.from_json_schema(stored_schema)
985
+
986
+ # Use Marshal for high-performance Ruby-to-Ruby communication
987
+ order = OrderMessage.new(data)
988
+ Marshal.dump(order) # Fast binary serialization of instances
989
+
990
+ # Or use JSON for cross-language communication
991
+ order.to_json # Standard JSON for other languages
992
+
993
+ # Schema stays portable while instances can use optimal serialization
994
+ ```
995
+
996
+ #### Security Consideration
997
+
998
+ ```ruby
999
+ # Marshal can execute code - DANGEROUS with untrusted data
1000
+ class EvilClass
1001
+ def marshal_load(data)
1002
+ system("rm -rf /") # This would execute!
1003
+ end
1004
+ end
1005
+
1006
+ Marshal.load(untrusted_data) # Security risk!
1007
+
1008
+ # JSON Schema is safe - it's just data
1009
+ SmartMessage.from_json_schema(untrusted_schema) # Safe, no code execution
1010
+ # Worst case: invalid schema that fails to create a working class
1011
+ ```
1012
+
1013
+ The JSON Schema approach is **better for**:
1014
+ - Schema management and governance
1015
+ - Cross-language systems
1016
+ - API documentation
1017
+ - Security-sensitive environments
1018
+ - Long-term schema evolution
1019
+
1020
+ The Marshal/DRb approach is **better for**:
1021
+ - Pure Ruby systems
1022
+ - High-performance requirements
1023
+ - Complex object graphs
1024
+ - Temporary serialization
1025
+ - Ruby-specific features
1026
+
1027
+ They solve different problems: Marshal serializes **instances**, JSON Schema serializes **class definitions**
1028
+
1029
+ ### Cross-Language Interoperability with JSON Schema
1030
+
1031
+ The JSON Schema approach enables true cross-language message contracts. Any language can consume SmartMessage schemas and generate equivalent message classes:
1032
+
1033
+ #### Rust Implementation
1034
+
1035
+ ```rust
1036
+ // Rust can generate structs from JSON Schema using schemars/serde
1037
+ use serde::{Deserialize, Serialize};
1038
+ use schemars::JsonSchema;
1039
+ use serde_json::Value;
1040
+ use validator::Validate;
1041
+
1042
+ // Generated from SmartMessage JSON Schema
1043
+ #[derive(Debug, Serialize, Deserialize, JsonSchema, Validate)]
1044
+ pub struct OrderMessage {
1045
+ #[serde(rename = "_sm_header")]
1046
+ pub sm_header: SmartMessageHeader,
1047
+
1048
+ #[validate(length(min = 1))]
1049
+ pub order_id: String,
1050
+
1051
+ #[validate(email)]
1052
+ pub customer_email: String,
1053
+
1054
+ #[validate(range(min = 0.01))]
1055
+ pub amount: f64,
1056
+
1057
+ #[serde(default = "default_currency")]
1058
+ #[validate(custom = "validate_currency")]
1059
+ pub currency: String,
1060
+
1061
+ pub items: Vec<OrderItem>,
1062
+
1063
+ pub created_at: chrono::DateTime<chrono::Utc>,
1064
+ }
1065
+
1066
+ // Rust macro to generate from JSON Schema at compile time
1067
+ use json_schema_to_rust::generate_struct;
1068
+ generate_struct!("schemas/order_message.json");
1069
+
1070
+ // Or runtime generation using a schema loader
1071
+ pub fn load_message_schema(schema_json: &str) -> Result<MessageType, Error> {
1072
+ let schema: Value = serde_json::from_str(schema_json)?;
1073
+ // Generate validation functions from schema
1074
+ let validator = JSONSchema::compile(&schema)?;
1075
+
1076
+ MessageType::new(schema, validator)
1077
+ }
1078
+ ```
1079
+
1080
+ #### Python Implementation
1081
+
1082
+ ```python
1083
+ # Python can use jsonschema and dataclasses
1084
+ from dataclasses import dataclass, field
1085
+ from typing import List, Optional
1086
+ from datetime import datetime
1087
+ import jsonschema
1088
+ from dataclasses_jsonschema import JsonSchemaMixin
1089
+
1090
+ # Generate from SmartMessage JSON Schema
1091
+ @dataclass
1092
+ class OrderMessage(JsonSchemaMixin):
1093
+ """Generated from SmartMessage schema"""
1094
+ _sm_header: SmartMessageHeader
1095
+ order_id: str
1096
+ customer_email: str
1097
+ amount: float
1098
+ currency: str = "USD"
1099
+ items: List[OrderItem] = field(default_factory=list)
1100
+ created_at: datetime = field(default_factory=datetime.now)
1101
+
1102
+ def validate(self):
1103
+ schema = self.json_schema()
1104
+ jsonschema.validate(self.to_dict(), schema)
1105
+
1106
+ @classmethod
1107
+ def from_json_schema(cls, schema_path: str):
1108
+ """Dynamically create class from JSON Schema"""
1109
+ with open(schema_path) as f:
1110
+ schema = json.load(f)
1111
+
1112
+ # Use pydantic for dynamic model generation
1113
+ from pydantic import create_model
1114
+ return create_model(
1115
+ schema['title'],
1116
+ **parse_schema_properties(schema['properties'])
1117
+ )
1118
+ ```
1119
+
1120
+ #### TypeScript/JavaScript Implementation
1121
+
1122
+ ```typescript
1123
+ // TypeScript can generate interfaces from JSON Schema
1124
+ import { FromSchema } from "json-schema-to-ts";
1125
+ import Ajv from "ajv";
1126
+
1127
+ // Type generated from JSON Schema at compile time
1128
+ type OrderMessage = FromSchema<typeof orderMessageSchema>;
1129
+
1130
+ // Or use json-schema-to-typescript for code generation
1131
+ import { compile } from 'json-schema-to-typescript';
1132
+
1133
+ async function generateFromSmartMessage(schemaJson: string) {
1134
+ const ts = await compile(JSON.parse(schemaJson), 'OrderMessage');
1135
+ // Generates TypeScript interface code
1136
+ }
1137
+
1138
+ // Runtime validation using the same schema
1139
+ class SmartMessage<T> {
1140
+ private ajv = new Ajv();
1141
+ private validator: any;
1142
+
1143
+ constructor(private schema: object) {
1144
+ this.validator = this.ajv.compile(schema);
1145
+ }
1146
+
1147
+ create(data: unknown): T {
1148
+ if (!this.validator(data)) {
1149
+ throw new Error(this.validator.errors);
1150
+ }
1151
+ return data as T;
1152
+ }
1153
+ }
1154
+
1155
+ // Use the SmartMessage schema directly
1156
+ const OrderMessage = new SmartMessage<OrderMessageType>(
1157
+ await fetch('/schemas/order_message.json').then(r => r.json())
1158
+ );
1159
+ ```
1160
+
1161
+ #### Go Implementation
1162
+
1163
+ ```go
1164
+ // Go can generate structs from JSON Schema
1165
+ package messages
1166
+
1167
+ import (
1168
+ "github.com/xeipuuv/gojsonschema"
1169
+ "encoding/json"
1170
+ )
1171
+
1172
+ // Generated from SmartMessage JSON Schema using go-jsonschema
1173
+ type OrderMessage struct {
1174
+ SmHeader SmartMessageHeader `json:"_sm_header"`
1175
+ OrderID string `json:"order_id" validate:"required"`
1176
+ CustomerEmail string `json:"customer_email" validate:"required,email"`
1177
+ Amount float64 `json:"amount" validate:"required,min=0.01"`
1178
+ Currency string `json:"currency" default:"USD"`
1179
+ Items []OrderItem `json:"items"`
1180
+ CreatedAt time.Time `json:"created_at"`
1181
+ }
1182
+
1183
+ // Validate using the JSON Schema
1184
+ func (m *OrderMessage) Validate() error {
1185
+ schemaLoader := gojsonschema.NewReferenceLoader("file://./schemas/order_message.json")
1186
+ documentLoader := gojsonschema.NewGoLoader(m)
1187
+
1188
+ result, err := gojsonschema.Validate(schemaLoader, documentLoader)
1189
+ if err != nil {
1190
+ return err
1191
+ }
1192
+
1193
+ if !result.Valid() {
1194
+ return fmt.Errorf("validation failed: %v", result.Errors())
1195
+ }
1196
+ return nil
1197
+ }
1198
+ ```
1199
+
1200
+ #### Java Implementation
1201
+
1202
+ ```java
1203
+ // Java can use jsonschema2pojo or similar tools
1204
+ import com.fasterxml.jackson.annotation.JsonProperty;
1205
+ import javax.validation.constraints.*;
1206
+ import com.networknt.schema.JsonSchema;
1207
+ import com.networknt.schema.JsonSchemaFactory;
1208
+
1209
+ // Generated from SmartMessage JSON Schema
1210
+ public class OrderMessage {
1211
+ @JsonProperty("_sm_header")
1212
+ private SmartMessageHeader smHeader;
1213
+
1214
+ @NotBlank
1215
+ private String orderId;
1216
+
1217
+ @NotBlank
1218
+ @Email
1219
+ private String customerEmail;
1220
+
1221
+ @NotNull
1222
+ @DecimalMin("0.01")
1223
+ private BigDecimal amount;
1224
+
1225
+ @Pattern(regexp = "USD|EUR|GBP|CAD")
1226
+ private String currency = "USD";
1227
+
1228
+ private List<OrderItem> items;
1229
+
1230
+ private Instant createdAt;
1231
+
1232
+ // Validate against JSON Schema
1233
+ public void validate() throws ValidationException {
1234
+ JsonSchemaFactory factory = JsonSchemaFactory.getInstance();
1235
+ JsonSchema schema = factory.getSchema(
1236
+ getClass().getResourceAsStream("/schemas/order_message.json")
1237
+ );
1238
+
1239
+ Set<ValidationMessage> errors = schema.validate(this.toJson());
1240
+ if (!errors.isEmpty()) {
1241
+ throw new ValidationException(errors.toString());
1242
+ }
1243
+ }
1244
+ }
1245
+ ```
1246
+
1247
+ #### Shared Schema Registry
1248
+
1249
+ ```yaml
1250
+ # All services can share schemas via a central registry
1251
+ services:
1252
+ ruby_service:
1253
+ language: ruby
1254
+ schema_fetch: |
1255
+ schema = fetch_schema("OrderMessage")
1256
+ OrderMessage = SmartMessage.from_json_schema(schema)
1257
+
1258
+ rust_service:
1259
+ language: rust
1260
+ schema_fetch: |
1261
+ let schema = fetch_schema("OrderMessage")?;
1262
+ generate_struct!(schema);
1263
+
1264
+ python_service:
1265
+ language: python
1266
+ schema_fetch: |
1267
+ schema = fetch_schema("OrderMessage")
1268
+ OrderMessage = create_model_from_schema(schema)
1269
+
1270
+ node_service:
1271
+ language: typescript
1272
+ schema_fetch: |
1273
+ const schema = await fetchSchema("OrderMessage");
1274
+ const OrderMessage = new SmartMessage(schema);
1275
+ ```
1276
+
1277
+ #### Key Cross-Language Benefits
1278
+
1279
+ 1. **Single Source of Truth**: One schema defines the contract for all languages
1280
+ 2. **Automatic Code Generation**: Most languages have JSON Schema → code generators
1281
+ 3. **Consistent Validation**: All services validate messages the same way
1282
+ 4. **Type Safety**: Strongly-typed languages get compile-time checking
1283
+ 5. **Documentation**: Schema includes descriptions for all languages
1284
+ 6. **Evolution Tracking**: Version changes are visible to all services
1285
+ 7. **No Manual Sync**: Changes propagate automatically through the schema
1286
+
1287
+ #### Example: Polyglot Microservices
1288
+
1289
+ ```ruby
1290
+ # Ruby service publishes schema
1291
+ OrderMessage.to_json_schema.tap do |schema|
1292
+ Redis.set("schema:OrderMessage:v2", schema.to_json)
1293
+ AMQP.publish("schema.updated", {name: "OrderMessage", version: 2})
1294
+ end
1295
+
1296
+ # Rust service receives update and regenerates
1297
+ let schema_json = redis.get("schema:OrderMessage:v2")?;
1298
+ rebuild_message_types(schema_json);
1299
+
1300
+ # Python service validates incoming message
1301
+ schema = redis.get("schema:OrderMessage:v2")
1302
+ validator = Draft7Validator(json.loads(schema))
1303
+ validator.validate(incoming_message)
1304
+
1305
+ # All services stay in sync automatically!
1306
+ ```
1307
+
1308
+ This enables true **polyglot microservices** where:
1309
+ - Ruby defines the canonical message schema
1310
+ - Rust gets memory-safe, zero-cost abstractions
1311
+ - Python gets dynamic typing with validation
1312
+ - TypeScript gets compile-time type checking
1313
+ - Go gets efficient JSON marshaling
1314
+ - Java gets enterprise integration
1315
+
1316
+ All from the same SmartMessage JSON Schema!
1317
+
1318
+ ### Implementation Roadmap
1319
+
1320
+ To implement this schema registry system:
1321
+
1322
+ 1. **Phase 1: Core Schema Generation**
1323
+ - Add `to_json_schema` method to SmartMessage::Base
1324
+ - Include property descriptions and validations
1325
+ - Map Ruby types to JSON Schema types
1326
+ - Generate standard-compliant JSON Schema documents
1327
+
1328
+ 2. **Phase 2: Schema Persistence**
1329
+ - Create database table for schema storage
1330
+ - Implement automatic registration hooks
1331
+ - Add schema versioning and evolution tracking
1332
+ - Store both internal format and JSON Schema
1333
+
1334
+ 3. **Phase 3: Dynamic Class Creation**
1335
+ - Implement `from_json_schema` method
1336
+ - Support validation reconstruction from JSON Schema
1337
+ - Enable round-trip conversion (Ruby → JSON Schema → Ruby)
1338
+ - Preserve transport and serializer configuration
1339
+
1340
+ 4. **Phase 4: Cross-Language Support**
1341
+ - Document schema consumption patterns for each language
1342
+ - Create example implementations for Rust, Python, TypeScript, Go, Java
1343
+ - Build shared schema registry for polyglot services
1344
+ - Enable automatic schema synchronization
1345
+
1346
+ 5. **Phase 5: Advanced Features**
1347
+ - Schema marketplace for sharing between services
1348
+ - Runtime schema updates without deployment
1349
+ - Multi-tenant schema support
1350
+ - A/B testing of message formats
1351
+ - Schema compliance and governance tools
1352
+
1353
+ ### Summary
1354
+
1355
+ The SmartMessage Schema Registry transforms message definitions from static code into dynamic, manageable data. By leveraging JSON Schema as a universal contract language, it enables:
1356
+
1357
+ - **Code as Data**: Message schemas become first-class data entities that can be stored, versioned, and managed independently of application code
1358
+ - **Polyglot Interoperability**: Any language can consume and implement SmartMessage contracts through standard JSON Schema
1359
+ - **Runtime Flexibility**: Classes can be created, updated, and versioned without code deployment
1360
+ - **Schema Governance**: Central management with distributed execution across services
1361
+ - **Zero-Code Development**: Business users can define message schemas through UIs without writing Ruby code
1362
+
1363
+ This approach fundamentally shifts how distributed systems manage message contracts, providing the flexibility of dynamic languages with the safety of schema validation, all while maintaining cross-language compatibility through industry-standard JSON Schema.
1364
+
1365
+ ### Benefits
1366
+
1367
+ 1. **Automatic Registration**: Schemas are captured automatically when classes load
1368
+ 2. **Schema Evolution**: Track how message formats change over time
1369
+ 3. **Change Detection**: SHA256 hashing detects any schema modifications
1370
+ 4. **Audit Trail**: Complete history of all message schemas for compliance
1371
+ 5. **Version Management**: Track parent-child relationships between versions
1372
+ 6. **Environment Tracking**: Know which schemas are used in which environments
1373
+ 7. **Performance**: Indexed for fast lookups and discovery operations
1374
+ 8. **JSON Schema Export**: Standard format for cross-platform compatibility
1375
+ 9. **Rich Documentation**: Descriptions at message and property levels
1376
+ 10. **Dynamic Class Creation**: Rebuild Ruby classes from stored schemas
1377
+ 11. **Cross-Language Support**: Generate equivalent classes in any language
1378
+ 12. **Runtime Updates**: Modify schemas without redeploying code
1379
+ 13. **Schema Marketplace**: Share and discover schemas across services
1380
+ 14. **Security**: Safe schema sharing without code execution risks
1381
+ 15. **Governance**: Centralized schema management with approval workflows