nats_wave 1.1.8 → 1.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 26e26c369d6d7d10d4f5c7bcd2f3cc9ba5560b759ff1c4478105c9e6e66853c7
4
- data.tar.gz: 9786a3a34f636185b056ca4aed597efc69cd1e07897d57a10636fc51b754b3f3
3
+ metadata.gz: 76f94cf28e8dfa1742a78ef25bdd1531c4424fdde3b61dfbc61daca7b361bd79
4
+ data.tar.gz: 0015c75cb71bcae3e0e5ea48c12b7953b838b3d2eb47303a5a5ecc885de5e313
5
5
  SHA512:
6
- metadata.gz: 10861846ae79ef25b5986752d5caa09a126c1b8d803f1a60df7cf95e2b2b4a288db150216cb827b2018ec7a2ea902188617dbd4beb1da7942ef0c6f052537e1c
7
- data.tar.gz: 4e4bdf076e56692299090ad6f9f5dd22387a821f7795f4c5ebc4dc320dc864b5d9159b3a92747ca4b9bf6f2fbab981707d48056af9741a3c1c36ad9df7373032
6
+ metadata.gz: c015a2d561b28c7526a55cc8e1af696f4df70281e9d82377d878e11fa11b28181323296843484f47bdea3527ebb55881f9a96c62195323d0ebbf79d94903b4d9
7
+ data.tar.gz: 8dbcbfde92b22e044e187a10ec94d29ff8d3db227015a61bbd0b56f2622de7dba6a01db81a3fa83d45bd4b5b0b737e566434395340333b460ce6d3799cdc49da
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nats_wave (1.1.8)
4
+ nats_wave (1.1.9)
5
5
  activerecord (>= 6.1, < 8.0)
6
6
  activesupport (>= 6.1, < 8.0)
7
7
  concurrent-ruby (~> 1.1)
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NatsWave
4
+ module ActiveRecordExtension
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def nats_publishable(options = {})
9
+ include NatsWave::NatsPublishable
10
+ class_attribute :nats_publishing_options
11
+ self.nats_publishing_options = {
12
+ enabled: true,
13
+ actions: %i[create update destroy],
14
+ skip_attributes: [],
15
+ include_associations: [],
16
+ async: true
17
+ }.merge(options)
18
+
19
+ # Register this model for publishing
20
+ NatsWave.configuration&.add_publication(name, nats_publishing_options[:actions])
21
+ end
22
+ end
23
+ end
24
+
25
+ module NatsPublishable
26
+ extend ActiveSupport::Concern
27
+
28
+ included do
29
+ after_commit :trigger_nats_wave_publish_on_create, on: :create, if: :should_publish_create?
30
+ after_commit :trigger_nats_wave_publish_on_update, on: :update, if: :should_publish_update?
31
+ after_commit :trigger_nats_wave_publish_on_destroy, on: :destroy, if: :should_publish_destroy?
32
+ end
33
+
34
+ class_methods do
35
+ def nats_wave_enabled?
36
+ nats_publishing_options[:enabled]
37
+ end
38
+
39
+ def nats_wave_actions
40
+ nats_publishing_options[:actions]
41
+ end
42
+
43
+ def nats_wave_skip_attributes
44
+ nats_publishing_options[:skip_attributes]
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def should_publish_create?
51
+ should_publish_action?(:create)
52
+ end
53
+
54
+ def should_publish_update?
55
+ should_publish_action?(:update) && has_significant_changes?
56
+ end
57
+
58
+ def should_publish_destroy?
59
+ should_publish_action?(:destroy)
60
+ end
61
+
62
+ def should_publish_action?(action)
63
+ return false unless self.class.nats_wave_enabled?
64
+ return false unless self.class.nats_wave_actions.include?(action)
65
+ return false if Thread.current[:skip_nats_wave_publishing]
66
+ return false if @skip_nats_wave_publishing
67
+
68
+ # Check conditional options
69
+ return false if nats_publishing_options[:if] && !instance_eval(&nats_publishing_options[:if])
70
+ return false if nats_publishing_options[:unless] && instance_eval(&nats_publishing_options[:unless])
71
+
72
+ true
73
+ end
74
+
75
+ def has_significant_changes?
76
+ skip_attrs = self.class.nats_wave_skip_attributes.map(&:to_s)
77
+ significant_changes = previous_changes.except(*skip_attrs, 'updated_at')
78
+ significant_changes.any?
79
+ end
80
+
81
+ def trigger_nats_wave_publish_on_create
82
+ nats_wave_publish('create')
83
+ end
84
+
85
+ def trigger_nats_wave_publish_on_update
86
+ nats_wave_publish('update')
87
+ end
88
+
89
+ def trigger_nats_wave_publish_on_destroy
90
+ nats_wave_publish('destroy')
91
+ end
92
+ end
93
+ end
@@ -1,3 +1,210 @@
1
+ # # frozen_string_literal: true
2
+ #
3
+ # module NatsWave
4
+ # module Adapters
5
+ # class ActiveRecord
6
+ # def initialize(config)
7
+ # @config = config
8
+ # end
9
+ #
10
+ # def create_from_external(external_model, data, metadata = {})
11
+ # mapping = ModelRegistry.local_models_for(external_model)
12
+ #
13
+ # mapping.each do |local_model_name, config|
14
+ # create_or_update_local_record(local_model_name, external_model, data, config, 'create')
15
+ # end
16
+ # end
17
+ #
18
+ # def update_from_external(external_model, data, metadata = {})
19
+ # mapping = ModelRegistry.local_models_for(external_model)
20
+ #
21
+ # mapping.each do |local_model_name, config|
22
+ # create_or_update_local_record(local_model_name, external_model, data, config, 'update')
23
+ # end
24
+ # end
25
+ #
26
+ # def destroy_from_external(external_model, data, metadata = {})
27
+ # mapping = ModelRegistry.local_models_for(external_model)
28
+ #
29
+ # mapping.each do |local_model_name, config|
30
+ # destroy_local_record(local_model_name, external_model, data, config)
31
+ # end
32
+ # end
33
+ #
34
+ # # Legacy methods for backward compatibility
35
+ # def create(model_name, data, metadata = {})
36
+ # model_class = get_model_class(model_name)
37
+ # clean_data = data.except('created_at', 'updated_at', :created_at, :updated_at)
38
+ #
39
+ # with_nats_publishing_disabled do
40
+ # record = model_class.create!(clean_data)
41
+ # NatsWave.logger.debug("Created #{model_name} with ID: #{record.id}")
42
+ # record
43
+ # end
44
+ # rescue => e
45
+ # raise DatabaseError, "Failed to create #{model_name}: #{e.message}"
46
+ # end
47
+ #
48
+ # def connected?
49
+ # return false unless defined?(::ActiveRecord)
50
+ # ::ActiveRecord::Base.connected?
51
+ # rescue
52
+ # false
53
+ # end
54
+ #
55
+ # private
56
+ #
57
+ # def create_or_update_local_record(local_model_name, external_model, external_data, config, action)
58
+ # model_class = get_model_class(local_model_name)
59
+ #
60
+ # # Transform the data using field mappings and transformations
61
+ # transformed_data = transform_external_data(external_data, config)
62
+ #
63
+ # # Find existing record based on unique fields
64
+ # existing_record = find_existing_record(model_class, transformed_data, config[:unique_fields])
65
+ #
66
+ # with_nats_publishing_disabled do
67
+ # case config[:sync_strategy]
68
+ # when :create_only
69
+ # create_record_if_not_exists(model_class, existing_record, transformed_data)
70
+ # when :update_only
71
+ # update_record_if_exists(existing_record, transformed_data) if existing_record
72
+ # when :upsert, nil
73
+ # upsert_record(model_class, existing_record, transformed_data, config)
74
+ # end
75
+ # end
76
+ #
77
+ # # Track the sync operation
78
+ # NatsWave::Metrics.track_sync_operation(
79
+ # local_model_name,
80
+ # action,
81
+ # 0, # duration - you could track this
82
+ # success: true
83
+ # )
84
+ #
85
+ # rescue => e
86
+ # NatsWave.logger.error("Failed to sync #{external_model} to #{local_model_name}: #{e.message}")
87
+ #
88
+ # NatsWave::Metrics.track_sync_operation(
89
+ # local_model_name,
90
+ # action,
91
+ # 0,
92
+ # success: false
93
+ # )
94
+ #
95
+ # raise DatabaseError, "Sync failed for #{local_model_name}: #{e.message}"
96
+ # end
97
+ #
98
+ # def transform_external_data(external_data, config)
99
+ # field_mappings = config[:field_mappings] || {}
100
+ # transformations = config[:transformations] || {}
101
+ # skip_fields = config[:skip_fields] || []
102
+ #
103
+ # transformed = {}
104
+ #
105
+ # external_data.each do |key, value|
106
+ # next if skip_fields.include?(key.to_s) || skip_fields.include?(key.to_sym)
107
+ #
108
+ # # Map field name
109
+ # local_field = field_mappings[key] || field_mappings[key.to_sym] || key
110
+ #
111
+ # # Apply transformation
112
+ # if transformations[local_field]
113
+ # transformation = transformations[local_field]
114
+ # value = case transformation
115
+ # when Proc
116
+ # transformation.arity == 1 ? transformation.call(value) : transformation.call(value, external_data)
117
+ # when Symbol
118
+ # # Would need to be called on the model instance
119
+ # value
120
+ # else
121
+ # value
122
+ # end
123
+ # end
124
+ #
125
+ # transformed[local_field] = value
126
+ # end
127
+ #
128
+ # # Remove Rails timestamp fields that might conflict
129
+ # transformed.except('created_at', 'updated_at', :created_at, :updated_at)
130
+ # end
131
+ #
132
+ # def find_existing_record(model_class, data, unique_fields)
133
+ # unique_fields ||= [:id]
134
+ #
135
+ # unique_fields.each do |field|
136
+ # value = data[field] || data[field.to_sym]
137
+ # next unless value
138
+ #
139
+ # record = model_class.find_by(field => value)
140
+ # return record if record
141
+ # end
142
+ #
143
+ # nil
144
+ # end
145
+ #
146
+ # def create_record_if_not_exists(model_class, existing_record, data)
147
+ # return existing_record if existing_record
148
+ #
149
+ # record = model_class.create!(data)
150
+ # NatsWave.logger.info("Created #{model_class.name} with ID: #{record.id}")
151
+ # record
152
+ # end
153
+ #
154
+ # def update_record_if_exists(existing_record, data)
155
+ # return unless existing_record
156
+ #
157
+ # existing_record.update!(data)
158
+ # NatsWave.logger.info("Updated #{existing_record.class.name} ID: #{existing_record.id}")
159
+ # existing_record
160
+ # end
161
+ #
162
+ # def upsert_record(model_class, existing_record, data, config)
163
+ # if existing_record
164
+ # existing_record.update!(data)
165
+ # NatsWave.logger.info("Updated #{model_class.name} ID: #{existing_record.id}")
166
+ # existing_record
167
+ # else
168
+ # record = model_class.create!(data)
169
+ # NatsWave.logger.info("Created #{model_class.name} with ID: #{record.id}")
170
+ # record
171
+ # end
172
+ # end
173
+ #
174
+ # def destroy_local_record(local_model_name, external_model, external_data, config)
175
+ # model_class = get_model_class(local_model_name)
176
+ # transformed_data = transform_external_data(external_data, config)
177
+ #
178
+ # existing_record = find_existing_record(model_class, transformed_data, config[:unique_fields])
179
+ # return unless existing_record
180
+ #
181
+ # with_nats_publishing_disabled do
182
+ # if existing_record.respond_to?(:soft_delete)
183
+ # existing_record.soft_delete
184
+ # else
185
+ # existing_record.destroy!
186
+ # end
187
+ #
188
+ # NatsWave.logger.info("Destroyed #{local_model_name} ID: #{existing_record.id}")
189
+ # end
190
+ # end
191
+ #
192
+ # def get_model_class(model_name)
193
+ # model_name.constantize
194
+ # rescue NameError
195
+ # raise DatabaseError, "Model class '#{model_name}' not found"
196
+ # end
197
+ #
198
+ # def with_nats_publishing_disabled(&block)
199
+ # Thread.current[:skip_nats_wave_publishing] = true
200
+ # yield
201
+ # ensure
202
+ # Thread.current[:skip_nats_wave_publishing] = false
203
+ # end
204
+ # end
205
+ # end
206
+ # end
207
+
1
208
  # frozen_string_literal: true
2
209
 
3
210
  module NatsWave
@@ -77,7 +77,7 @@ module NatsWave
77
77
  {
78
78
  service: @service_name,
79
79
  environment: ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'unknown',
80
- version: ENV['APP_VERSION'] || '1.1.8'
80
+ version: ENV['APP_VERSION'] || '1.1.9'
81
81
  }
82
82
  end
83
83