nats_wave 1.1.8 → 1.1.10

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.
@@ -18,7 +18,7 @@ module NatsWave
18
18
  def initialize(options = {})
19
19
  @nats_url = ENV['NATS_URL'] || "nats://localhost:4222"
20
20
  @service_name = ENV['NATS_SERVICE_NAME'] || "purplewave"
21
- @version = ENV['NATS_SERVICE_VERSION'] || "1.1.8"
21
+ @version = ENV['NATS_SERVICE_VERSION'] || "1.1.10"
22
22
  @instance_id = ENV['NATS_INSTANCE_ID'] || Socket.gethostname
23
23
  @database_url = ENV['NATS_DATABASE_URL'] || nil
24
24
  @connection_pool_size = (ENV['NATS_CONNECTION_POOL_SIZE'] || 10).to_i
@@ -1,3 +1,54 @@
1
+ # # frozen_string_literal: true
2
+ #
3
+ # module NatsWave
4
+ # class DatabaseConnector
5
+ # def initialize(config)
6
+ # @config = config
7
+ # @adapter = determine_adapter
8
+ # end
9
+ #
10
+ # def apply_change(model:, action:, data:, metadata:)
11
+ # case action.to_s.downcase
12
+ # when 'create'
13
+ # create_record(model, data, metadata)
14
+ # when 'update'
15
+ # update_record(model, data, metadata)
16
+ # when 'delete', 'destroy'
17
+ # delete_record(model, data, metadata)
18
+ # else
19
+ # NatsWave.logger.warn("Unknown action: #{action}")
20
+ # end
21
+ # rescue StandardError => e
22
+ # NatsWave.logger.error("Database operation failed: #{e.message}")
23
+ # raise DatabaseError, "Database operation failed: #{e.message}"
24
+ # end
25
+ #
26
+ # def connected?
27
+ # @adapter.connected?
28
+ # end
29
+ #
30
+ # private
31
+ #
32
+ # def determine_adapter
33
+ # raise ConfigurationError, 'No supported database adapter found' unless defined?(ActiveRecord)
34
+ #
35
+ # Adapters::ActiveRecord.new(@config)
36
+ # end
37
+ #
38
+ # def create_record(model, data, metadata)
39
+ # @adapter.create(model, data, metadata)
40
+ # end
41
+ #
42
+ # def update_record(model, data, metadata)
43
+ # @adapter.update(model, data, metadata)
44
+ # end
45
+ #
46
+ # def delete_record(model, data, metadata)
47
+ # @adapter.delete(model, data, metadata)
48
+ # end
49
+ # end
50
+ # end
51
+
1
52
  # frozen_string_literal: true
2
53
 
3
54
  module NatsWave
@@ -1,47 +1,150 @@
1
- # # frozen_string_literal: true
2
- #
3
- # require 'securerandom'
4
- # require 'json'
5
- #
6
- # # begin
7
- # # require 'nats/client'
8
- # # rescue LoadError
9
- # # # NATS not available - define a mock for testing
10
- # # module NATS
11
- # # def self.connect(url, options = {})
12
- # # NatsClient.new
13
- # # end
14
- # #
15
- # # class NatsClient
16
- # # def connected?
17
- # # false
18
- # # end
1
+ # # # frozen_string_literal: true
19
2
  # #
20
- # # def publish(subject, message)
21
- # # puts "NATS: Publishing to #{subject}: #{message}"
22
- # # end
3
+ # # require 'securerandom'
4
+ # # require 'json'
23
5
  # #
24
- # # def subscribe(subject, options = {})
25
- # # puts "NATS: Subscribing to #{subject}"
26
- # # yield('{"mock": "message"}') if block_given?
27
- # # Subscription.new
28
- # # end
6
+ # # # begin
7
+ # # # require 'nats/client'
8
+ # # # rescue LoadError
9
+ # # # # NATS not available - define a mock for testing
10
+ # # # module NATS
11
+ # # # def self.connect(url, options = {})
12
+ # # # NatsClient.new
13
+ # # # end
14
+ # # #
15
+ # # # class NatsClient
16
+ # # # def connected?
17
+ # # # false
18
+ # # # end
19
+ # # #
20
+ # # # def publish(subject, message)
21
+ # # # puts "NATS: Publishing to #{subject}: #{message}"
22
+ # # # end
23
+ # # #
24
+ # # # def subscribe(subject, options = {})
25
+ # # # puts "NATS: Subscribing to #{subject}"
26
+ # # # yield('{"mock": "message"}') if block_given?
27
+ # # # Subscription.new
28
+ # # # end
29
+ # # #
30
+ # # # def close
31
+ # # # true
32
+ # # # end
33
+ # # # end
34
+ # # #
35
+ # # # class Subscription
36
+ # # # def unsubscribe
37
+ # # # true
38
+ # # # end
39
+ # # # end
40
+ # # # end
41
+ # # # end
29
42
  # #
30
- # # def close
31
- # # true
32
- # # end
33
- # # end
34
- # #
35
- # # class Subscription
36
- # # def unsubscribe
37
- # # true
38
- # # end
39
- # # end
40
- # # end
41
- # # end
42
43
  #
44
+ # # frozen_string_literal: true
45
+ #
46
+ # module NatsWave
47
+ # class Publisher
48
+ # attr_reader :config, :client
49
+ #
50
+ # def initialize(config, client, middleware_stack = [])
51
+ # @config = config
52
+ # @client = client
53
+ # @message_transformer = MessageTransformer.new(config)
54
+ # @dead_letter_queue = DeadLetterQueue.new(config) if config.dead_letter_queue
55
+ # end
56
+ #
57
+ # def publish(subject:, model:, action:, data:, metadata: {})
58
+ # return unless @config.publishing_enabled
59
+ #
60
+ # message = build_message(subject, model, action, data, metadata)
61
+ # full_subject = build_full_subject(subject)
62
+ #
63
+ # if @config.async_publishing && defined?(Concurrent)
64
+ # publish_async(full_subject, message)
65
+ # else
66
+ # publish_sync(full_subject, message)
67
+ # end
68
+ #
69
+ # # Metrics.increment_published_messages(full_subject)
70
+ # rescue => e
71
+ # NatsWave.logger.error("Failed to publish message: #{e.message}")
72
+ # @dead_letter_queue&.store_failed_message(message, e, 0)
73
+ # raise PublishError, "Failed to publish message: #{e.message}"
74
+ # end
75
+ #
76
+ # def publish_batch(events)
77
+ # batch_message = {
78
+ # batch_id: SecureRandom.uuid,
79
+ # events: events,
80
+ # timestamp: Time.current.iso8601,
81
+ # source: build_source_info
82
+ # }
83
+ #
84
+ # subject = "#{@config.default_subject_prefix}.batch"
85
+ #
86
+ # @client.publish(subject, batch_message.to_json)
87
+ #
88
+ # # Metrics.increment_published_messages(subject)
89
+ # NatsWave.logger.info("Published batch with #{events.size} events")
90
+ # end
91
+ #
92
+ # def connected?
93
+ # @client&.connected?
94
+ # end
95
+ #
96
+ # def disconnect
97
+ # # Publisher cleanup if needed
98
+ # end
99
+ #
100
+ # private
101
+ #
102
+ # def build_message(subject, model, action, data, metadata)
103
+ # @message_transformer.build_standard_message(
104
+ # subject: subject,
105
+ # model: model,
106
+ # action: action,
107
+ # data: data,
108
+ # metadata: metadata,
109
+ # source: build_source_info
110
+ # )
111
+ # end
112
+ #
113
+ # def build_source_info
114
+ # {
115
+ # service: @config.service_name,
116
+ # version: @config.version,
117
+ # instance_id: @config.instance_id,
118
+ # environment: defined?(Rails) ? Rails.env : 'test'
119
+ # }
120
+ # end
121
+ #
122
+ # def build_full_subject(subject)
123
+ # if @config.default_subject_prefix && !subject.start_with?(@config.default_subject_prefix)
124
+ # "#{@config.default_subject_prefix}.#{subject}"
125
+ # else
126
+ # subject
127
+ # end
128
+ # end
129
+ #
130
+ # def publish_sync(subject, message)
131
+ # @client.publish(subject, message.to_json)
132
+ # NatsWave.logger.debug("Published sync message to #{subject}")
133
+ # end
134
+ #
135
+ # def publish_async(subject, message)
136
+ # if defined?(Concurrent)
137
+ # Concurrent::Future.execute do
138
+ # @client.publish(subject, message.to_json)
139
+ # NatsWave.logger.debug("Published async message to #{subject}")
140
+ # end
141
+ # else
142
+ # publish_sync(subject, message)
143
+ # end
144
+ # end
145
+ # end
146
+ # end
43
147
 
44
- # frozen_string_literal: true
45
148
 
46
149
  module NatsWave
47
150
  class Publisher
@@ -1,3 +1,123 @@
1
+ # # frozen_string_literal: true
2
+ #
3
+ # require 'rails/railtie'
4
+ #
5
+ # module NatsWave
6
+ # class Railtie < Rails::Railtie
7
+ # config.nats_wave = ActiveSupport::OrderedOptions.new
8
+ #
9
+ # initializer "nats_wave.configure" do |app|
10
+ # # Load configuration from Rails config
11
+ # if app.config.respond_to?(:nats_wave)
12
+ # NatsWave.configure do |config|
13
+ # app.config.nats_wave.each do |key, value|
14
+ # config.send("#{key}=", value) if config.respond_to?("#{key}=")
15
+ # end
16
+ # end
17
+ # end
18
+ #
19
+ # # Load YAML configuration if it exists
20
+ # config_file = Rails.root.join('config', 'nats_wave.yml')
21
+ # if File.exist?(config_file)
22
+ # begin
23
+ # erb_content = ERB.new(File.read(config_file)).result
24
+ # yaml_data = YAML.safe_load(erb_content)
25
+ # yaml_config = yaml_data&.dig(Rails.env.to_s) || yaml_data&.dig(Rails.env) || {}
26
+ #
27
+ # NatsWave.configure do |config|
28
+ # # NATS Configuration
29
+ # config.nats_url = yaml_config.dig('nats', 'url') if yaml_config.dig('nats', 'url')
30
+ # config.connection_pool_size = yaml_config.dig('nats', 'connection_pool_size') if yaml_config.dig('nats', 'connection_pool_size')
31
+ # config.timeout = yaml_config.dig('nats', 'timeout') if yaml_config.dig('nats', 'timeout')
32
+ # config.reconnect_attempts = yaml_config.dig('nats', 'reconnect_attempts') if yaml_config.dig('nats', 'reconnect_attempts')
33
+ #
34
+ # # Service Configuration
35
+ # config.service_name = yaml_config.dig('publishing', 'default_subject_prefix') if yaml_config.dig('publishing', 'default_subject_prefix')
36
+ # config.version = Rails.application.class.parent_name.underscore if defined?(Rails.application.class.parent_name)
37
+ #
38
+ # # Publishing Configuration
39
+ # config.publishing_enabled = yaml_config.dig('publishing', 'enabled') unless yaml_config.dig('publishing', 'enabled').nil?
40
+ # config.default_subject_prefix = yaml_config.dig('publishing', 'default_subject_prefix') if yaml_config.dig('publishing', 'default_subject_prefix')
41
+ # config.batch_size = yaml_config.dig('publishing', 'batch_size') if yaml_config.dig('publishing', 'batch_size')
42
+ # config.async_publishing = yaml_config.dig('publishing', 'async') unless yaml_config.dig('publishing', 'async').nil?
43
+ #
44
+ # # Subscription Configuration
45
+ # config.subscription_enabled = yaml_config.dig('subscription', 'enabled') unless yaml_config.dig('subscription', 'enabled').nil?
46
+ # config.queue_group = yaml_config.dig('subscription', 'queue_group') if yaml_config.dig('subscription', 'queue_group')
47
+ #
48
+ # # Middleware Configuration
49
+ # config.middleware_authentication_enabled = yaml_config.dig('middleware', 'authentication', 'enabled') unless yaml_config.dig('middleware', 'authentication', 'enabled').nil?
50
+ # config.middleware_validation_enabled = yaml_config.dig('middleware', 'validation', 'enabled') unless yaml_config.dig('middleware', 'validation', 'enabled').nil?
51
+ # config.middleware_logging_enabled = yaml_config.dig('middleware', 'logging', 'enabled') unless yaml_config.dig('middleware', 'logging', 'enabled').nil?
52
+ # config.auth_secret_key = yaml_config.dig('middleware', 'authentication', 'secret_key') if yaml_config.dig('middleware', 'authentication', 'secret_key')
53
+ # config.schema_registry_url = yaml_config.dig('middleware', 'validation', 'schema_registry') if yaml_config.dig('middleware', 'validation', 'schema_registry')
54
+ # config.log_level = yaml_config.dig('middleware', 'logging', 'level') if yaml_config.dig('middleware', 'logging', 'level')
55
+ #
56
+ # # Error Handling Configuration
57
+ # config.max_retries = yaml_config.dig('error_handling', 'max_retries') if yaml_config.dig('error_handling', 'max_retries')
58
+ # config.retry_delay = yaml_config.dig('error_handling', 'retry_delay') if yaml_config.dig('error_handling', 'retry_delay')
59
+ # config.dead_letter_queue = yaml_config.dig('error_handling', 'dead_letter_queue') if yaml_config.dig('error_handling', 'dead_letter_queue')
60
+ # end
61
+ # rescue => e
62
+ # Rails.logger.warn "Failed to load NATS Wave YAML configuration: #{e.message}"
63
+ # end
64
+ #
65
+ # # Environment-specific overrides
66
+ # if Rails.env.development?
67
+ # NatsWave.configure do |config|
68
+ # config.log_level = "debug"
69
+ # config.middleware_authentication_enabled = false
70
+ # end
71
+ # elsif Rails.env.test?
72
+ # NatsWave.configure do |config|
73
+ # config.publishing_enabled = false
74
+ # config.subscription_enabled = false
75
+ # end
76
+ # end
77
+ # end
78
+ # end
79
+ #
80
+ # # initializer "nats_wave.active_record" do
81
+ # # ActiveSupport.on_load(:active_record) do
82
+ # # # Load the ActiveRecord extension (this replaces the old publishable)
83
+ # # require 'nats_wave/adapters/active_record'
84
+ # # include NatsWave::ActiveRecord
85
+ # # end
86
+ # # end
87
+ #
88
+ # rake_tasks do
89
+ # load "tasks/nats_wave.rake"
90
+ # end
91
+ #
92
+ # generators do
93
+ # require 'generators/nats_wave/install_generator'
94
+ # end
95
+ #
96
+ # # Initialize client on Rails boot
97
+ # initializer "nats_wave.initialize_client", after: :load_config_initializers do
98
+ # Rails.application.config.after_initialize do
99
+ # if NatsWave.configuration&.publishing_enabled || NatsWave.configuration&.subscription_enabled
100
+ # Thread.new do
101
+ # sleep 2 # Give Rails time to fully boot
102
+ # begin
103
+ # NatsWave.client
104
+ # NatsWave.logger.info "NatsWave client initialized"
105
+ #
106
+ # # Start subscriber if enabled
107
+ # if NatsWave.configuration&.subscription_enabled
108
+ # NatsWave.client.start_subscriber
109
+ # NatsWave.logger.info "NatsWave subscriber started"
110
+ # end
111
+ # rescue => e
112
+ # NatsWave.logger.error "Failed to initialize NatsWave client: #{e.message}"
113
+ # end
114
+ # end
115
+ # end
116
+ # end
117
+ # end
118
+ # end
119
+ # end
120
+
1
121
  # frozen_string_literal: true
2
122
 
3
123
  require 'rails/railtie'
@@ -77,9 +197,16 @@ module NatsWave
77
197
  end
78
198
  end
79
199
 
200
+ # Load extensions before ActiveRecord loads models
201
+ initializer "nats_wave.load_extensions", before: :load_config_initializers do
202
+ # Load the extensions early so they're available when models are loaded
203
+ require 'nats_wave/active_record_extension'
204
+ require 'nats_wave/concerns/mappable'
205
+ end
206
+
80
207
  initializer "nats_wave.active_record" do
81
208
  ActiveSupport.on_load(:active_record) do
82
- require 'nats_wave/concerns/publishable'
209
+ # The modules are already loaded, just include them
83
210
  include NatsWave::ActiveRecordExtension
84
211
  end
85
212
  end
@@ -101,6 +228,12 @@ module NatsWave
101
228
  begin
102
229
  NatsWave.client
103
230
  NatsWave.logger.info "NatsWave client initialized"
231
+
232
+ # Start subscriber if enabled
233
+ if NatsWave.configuration&.subscription_enabled
234
+ NatsWave.client.start_subscriber
235
+ NatsWave.logger.info "NatsWave subscriber started"
236
+ end
104
237
  rescue => e
105
238
  NatsWave.logger.error "Failed to initialize NatsWave client: #{e.message}"
106
239
  end