column_sync 0.1.0 → 0.2.0

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: e1512d2bab77e8503dc4ab8733853e8ef1802c6f4c0a31488614b6c5a0c39ecc
4
- data.tar.gz: 4f5484e1809bac2bc7dc6262b29a7a38bbac93e42a04e680773d6bbb048f8cab
3
+ metadata.gz: b7f7eb5d8496290fa5def80e98ad2460c2c594138b0ee1742ec352cd26df5cab
4
+ data.tar.gz: 63d4bcc6b35668f914912912dcc1c46aff154aa8396d3a6297e01eb66c09da8b
5
5
  SHA512:
6
- metadata.gz: 16cb3a76f2c9754ff077efb0612d14c51700aa277b514d9197da1878bcea569b6994ec88048b70777f9ecb570d0b7472eb242f61eab4ca16aa602d7c8329272a
7
- data.tar.gz: 1bb08540d79e18d2c4d5b04371c7c3ab8b7d227cc32c3d29778e985a4b237ca8912d52f77ba664da23592b486abf13b992aaf4a51d26dd6c7b68c1cb63ebac92
6
+ metadata.gz: 8d1024c86f3847a7fd510abb2fb9cd9292204559ba7098c1ae04c1731e829f527b1a62104a9d13803cd954975ea3d2b993ee603494154d2f952404e91788ba8b
7
+ data.tar.gz: f7ca54ddd71c6e2e43dd1619e6530203f75eb2e597b1bb0a77f9d5df6434bc6d7167b07b3d86d699ef2ddf4dfa051c09068cf374943df18798956c2836e527aa
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.6
2
+ TargetRubyVersion: 3.0
3
3
 
4
4
  Style/StringLiterals:
5
5
  Enabled: true
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2023-12-23
4
+
5
+ - Added in-memory attribute sync
6
+
3
7
  ## [0.1.0] - 2023-12-10
4
8
 
5
9
  - Initial release
data/Gemfile.lock CHANGED
@@ -1,7 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- column_sync (0.1.0)
4
+ column_sync (0.2.0)
5
+ fx (~> 0.8.0)
6
+ pg (~> 1.5.4)
7
+ rails (>= 6.0.0)
5
8
 
6
9
  GEM
7
10
  remote: https://rubygems.org/
@@ -213,9 +216,6 @@ PLATFORMS
213
216
 
214
217
  DEPENDENCIES
215
218
  column_sync!
216
- fx (~> 0.8.0)
217
- pg (~> 1.5.4)
218
- rails (>= 6.0.0)
219
219
  rubocop
220
220
 
221
221
  BUNDLED WITH
data/README.md CHANGED
@@ -32,12 +32,67 @@ end
32
32
 
33
33
  The migration above generates the functions and triggers needed to keep `subscriptions.country_code` in sync with `companies.country`.
34
34
 
35
+ You also need to update your models to reflect the syncroniaztion between their columns:
36
+
37
+ ```ruby
38
+ class Company < ApplicationRecord
39
+ include ColumnSync::Model
40
+
41
+ has_one :subscription
42
+
43
+ sync_column :country, to: :subscription, column: :country_code
44
+ end
45
+
46
+ class Subscription < ApplicationRecord
47
+ include ColumnSync::Model
48
+
49
+ belongs_to :company
50
+
51
+ sync_column :country_code, to: :company, column: :country
52
+ end
53
+ ```
54
+
55
+ ## Example
56
+
57
+ Given the following scenario:
58
+
59
+ ```ruby
60
+ Company.create!(country: "es")
61
+ Subscription.create!(country_code: "es", company: Company.first)
62
+ ```
63
+
64
+ Changes are reflected in memory when the value is modified:
65
+
66
+ ```ruby
67
+ company = Company.first
68
+ company.country = "fr"
69
+ company.subscription.country_code
70
+ # => "fr"
71
+
72
+ company.subscription.country_code = "ca"
73
+ company.country
74
+ # => "ca"
75
+ ```
76
+
77
+ Changes are also reflected in the DB when the value is persisted:
78
+
79
+ ```ruby
80
+ company = Company.first
81
+ company.update(country: "it")
82
+ company.subscription.country_code
83
+ # => "it"
84
+
85
+ company.subscription.update(country_code: "ma")
86
+ company.country
87
+ # => "ma"
88
+ ```
89
+
35
90
  ## Limitations
36
91
 
37
92
  - Each `sync_columns` statement can only sync a pair of columns. If the same column needs to be synchronized across multiple
38
93
  tables, multiple such statements will be needed.
39
94
  - The gem expects a `has_one` - `belongs_to` association between the models involved. It uses Rails reflections to understand
40
95
  the table names and column names involved.
41
- - The columns involved in the migration will be kept in sync via database triggers executed on row update.
42
- It assumes the records are initially in sync, so it does **not** automatically sync values using any of the two columns involved.
96
+ - It is assumed that the records are initially in sync, so it does **not** automatically sync values using any of the two
97
+ columns involved.
43
98
  - It also does not sync values when a row is created, only when it is modified.
@@ -3,6 +3,9 @@
3
3
  module ColumnSync
4
4
  module Migration
5
5
  def sync_columns(columns)
6
+ FileUtils.mkdir_p("db/functions")
7
+ FileUtils.mkdir_p("db/triggers")
8
+
6
9
  service = Service.new(columns)
7
10
 
8
11
  each_function(service) { |name| create_function(name) }
@@ -0,0 +1,48 @@
1
+ require "active_support/concern"
2
+
3
+ module ColumnSync
4
+ module Model
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ scope :disabled, -> { where(disabled: true) }
9
+
10
+ before_update :propagate_changes
11
+
12
+ private
13
+
14
+ def propagate_changes
15
+ changes.each do |attribute, (_before, after)|
16
+ propagate_changes_in_memory(attribute, after)
17
+ end
18
+ end
19
+
20
+ def propagate_changes_in_memory(attribute, value)
21
+ self.class.columns_to_sync[attribute]&.each do |sync|
22
+ object = public_send(sync[:to])
23
+ next unless object
24
+
25
+ current_value = object.attributes[sync[:column].to_s]
26
+
27
+ object.public_send("#{sync[:column]}=", value) if current_value.to_s != value.to_s
28
+ end
29
+ end
30
+ end
31
+
32
+ class_methods do
33
+ attr_reader :columns_to_sync
34
+
35
+ def sync_column(column_name, to:, column:)
36
+ @columns_to_sync ||= {}
37
+ @columns_to_sync[column_name.to_s] ||= []
38
+ @columns_to_sync[column_name.to_s] << { to: to, column: column }
39
+
40
+ define_method("#{column_name}=") do |value|
41
+ super(value)
42
+
43
+ propagate_changes_in_memory(column_name.to_s, value)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ColumnSync
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/column_sync.rb CHANGED
@@ -6,6 +6,7 @@ require "fx"
6
6
  require "column_sync/version"
7
7
  require "column_sync/service"
8
8
  require "column_sync/migration"
9
+ require "column_sync/model"
9
10
 
10
11
  module ColumnSync
11
12
  class Error < StandardError; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: column_sync
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manuel Bustillo
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-12-12 00:00:00.000000000 Z
11
+ date: 2023-12-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fx
@@ -17,7 +17,7 @@ dependencies:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: 0.8.0
20
- type: :development
20
+ type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
@@ -31,7 +31,7 @@ dependencies:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: 1.5.4
34
- type: :development
34
+ type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
@@ -45,7 +45,7 @@ dependencies:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: 6.0.0
48
- type: :development
48
+ type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
@@ -82,6 +82,7 @@ files:
82
82
  - Rakefile
83
83
  - lib/column_sync.rb
84
84
  - lib/column_sync/migration.rb
85
+ - lib/column_sync/model.rb
85
86
  - lib/column_sync/service.rb
86
87
  - lib/column_sync/version.rb
87
88
  - sig/column_sync.rbs