column_sync 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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