column_sync 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e1512d2bab77e8503dc4ab8733853e8ef1802c6f4c0a31488614b6c5a0c39ecc
4
+ data.tar.gz: 4f5484e1809bac2bc7dc6262b29a7a38bbac93e42a04e680773d6bbb048f8cab
5
+ SHA512:
6
+ metadata.gz: 16cb3a76f2c9754ff077efb0612d14c51700aa277b514d9197da1878bcea569b6994ec88048b70777f9ecb570d0b7472eb242f61eab4ca16aa602d7c8329272a
7
+ data.tar.gz: 1bb08540d79e18d2c4d5b04371c7c3ab8b7d227cc32c3d29778e985a4b237ca8912d52f77ba664da23592b486abf13b992aaf4a51d26dd6c7b68c1cb63ebac92
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+
4
+ Style/StringLiterals:
5
+ Enabled: true
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: true
10
+ EnforcedStyle: double_quotes
11
+
12
+ Layout/LineLength:
13
+ Max: 120
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2023-12-10
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,222 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ column_sync (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ actioncable (7.1.2)
10
+ actionpack (= 7.1.2)
11
+ activesupport (= 7.1.2)
12
+ nio4r (~> 2.0)
13
+ websocket-driver (>= 0.6.1)
14
+ zeitwerk (~> 2.6)
15
+ actionmailbox (7.1.2)
16
+ actionpack (= 7.1.2)
17
+ activejob (= 7.1.2)
18
+ activerecord (= 7.1.2)
19
+ activestorage (= 7.1.2)
20
+ activesupport (= 7.1.2)
21
+ mail (>= 2.7.1)
22
+ net-imap
23
+ net-pop
24
+ net-smtp
25
+ actionmailer (7.1.2)
26
+ actionpack (= 7.1.2)
27
+ actionview (= 7.1.2)
28
+ activejob (= 7.1.2)
29
+ activesupport (= 7.1.2)
30
+ mail (~> 2.5, >= 2.5.4)
31
+ net-imap
32
+ net-pop
33
+ net-smtp
34
+ rails-dom-testing (~> 2.2)
35
+ actionpack (7.1.2)
36
+ actionview (= 7.1.2)
37
+ activesupport (= 7.1.2)
38
+ nokogiri (>= 1.8.5)
39
+ racc
40
+ rack (>= 2.2.4)
41
+ rack-session (>= 1.0.1)
42
+ rack-test (>= 0.6.3)
43
+ rails-dom-testing (~> 2.2)
44
+ rails-html-sanitizer (~> 1.6)
45
+ actiontext (7.1.2)
46
+ actionpack (= 7.1.2)
47
+ activerecord (= 7.1.2)
48
+ activestorage (= 7.1.2)
49
+ activesupport (= 7.1.2)
50
+ globalid (>= 0.6.0)
51
+ nokogiri (>= 1.8.5)
52
+ actionview (7.1.2)
53
+ activesupport (= 7.1.2)
54
+ builder (~> 3.1)
55
+ erubi (~> 1.11)
56
+ rails-dom-testing (~> 2.2)
57
+ rails-html-sanitizer (~> 1.6)
58
+ activejob (7.1.2)
59
+ activesupport (= 7.1.2)
60
+ globalid (>= 0.3.6)
61
+ activemodel (7.1.2)
62
+ activesupport (= 7.1.2)
63
+ activerecord (7.1.2)
64
+ activemodel (= 7.1.2)
65
+ activesupport (= 7.1.2)
66
+ timeout (>= 0.4.0)
67
+ activestorage (7.1.2)
68
+ actionpack (= 7.1.2)
69
+ activejob (= 7.1.2)
70
+ activerecord (= 7.1.2)
71
+ activesupport (= 7.1.2)
72
+ marcel (~> 1.0)
73
+ activesupport (7.1.2)
74
+ base64
75
+ bigdecimal
76
+ concurrent-ruby (~> 1.0, >= 1.0.2)
77
+ connection_pool (>= 2.2.5)
78
+ drb
79
+ i18n (>= 1.6, < 2)
80
+ minitest (>= 5.1)
81
+ mutex_m
82
+ tzinfo (~> 2.0)
83
+ ast (2.4.2)
84
+ base64 (0.2.0)
85
+ bigdecimal (3.1.4)
86
+ builder (3.2.4)
87
+ concurrent-ruby (1.2.2)
88
+ connection_pool (2.4.1)
89
+ crass (1.0.6)
90
+ date (3.3.4)
91
+ drb (2.2.0)
92
+ ruby2_keywords
93
+ erubi (1.12.0)
94
+ fx (0.8.0)
95
+ activerecord (>= 6.0.0)
96
+ railties (>= 6.0.0)
97
+ globalid (1.2.1)
98
+ activesupport (>= 6.1)
99
+ i18n (1.14.1)
100
+ concurrent-ruby (~> 1.0)
101
+ io-console (0.6.0)
102
+ irb (1.10.1)
103
+ rdoc
104
+ reline (>= 0.3.8)
105
+ json (2.7.1)
106
+ language_server-protocol (3.17.0.3)
107
+ loofah (2.22.0)
108
+ crass (~> 1.0.2)
109
+ nokogiri (>= 1.12.0)
110
+ mail (2.8.1)
111
+ mini_mime (>= 0.1.1)
112
+ net-imap
113
+ net-pop
114
+ net-smtp
115
+ marcel (1.0.2)
116
+ mini_mime (1.1.5)
117
+ minitest (5.20.0)
118
+ mutex_m (0.2.0)
119
+ net-imap (0.4.8)
120
+ date
121
+ net-protocol
122
+ net-pop (0.1.2)
123
+ net-protocol
124
+ net-protocol (0.2.2)
125
+ timeout
126
+ net-smtp (0.4.0)
127
+ net-protocol
128
+ nio4r (2.7.0)
129
+ nokogiri (1.15.5-x86_64-linux)
130
+ racc (~> 1.4)
131
+ parallel (1.23.0)
132
+ parser (3.2.2.4)
133
+ ast (~> 2.4.1)
134
+ racc
135
+ pg (1.5.4)
136
+ psych (5.1.1.1)
137
+ stringio
138
+ racc (1.7.3)
139
+ rack (3.0.8)
140
+ rack-session (2.0.0)
141
+ rack (>= 3.0.0)
142
+ rack-test (2.1.0)
143
+ rack (>= 1.3)
144
+ rackup (2.1.0)
145
+ rack (>= 3)
146
+ webrick (~> 1.8)
147
+ rails (7.1.2)
148
+ actioncable (= 7.1.2)
149
+ actionmailbox (= 7.1.2)
150
+ actionmailer (= 7.1.2)
151
+ actionpack (= 7.1.2)
152
+ actiontext (= 7.1.2)
153
+ actionview (= 7.1.2)
154
+ activejob (= 7.1.2)
155
+ activemodel (= 7.1.2)
156
+ activerecord (= 7.1.2)
157
+ activestorage (= 7.1.2)
158
+ activesupport (= 7.1.2)
159
+ bundler (>= 1.15.0)
160
+ railties (= 7.1.2)
161
+ rails-dom-testing (2.2.0)
162
+ activesupport (>= 5.0.0)
163
+ minitest
164
+ nokogiri (>= 1.6)
165
+ rails-html-sanitizer (1.6.0)
166
+ loofah (~> 2.21)
167
+ nokogiri (~> 1.14)
168
+ railties (7.1.2)
169
+ actionpack (= 7.1.2)
170
+ activesupport (= 7.1.2)
171
+ irb
172
+ rackup (>= 1.0.0)
173
+ rake (>= 12.2)
174
+ thor (~> 1.0, >= 1.2.2)
175
+ zeitwerk (~> 2.6)
176
+ rainbow (3.1.1)
177
+ rake (13.1.0)
178
+ rdoc (6.6.1)
179
+ psych (>= 4.0.0)
180
+ regexp_parser (2.8.3)
181
+ reline (0.4.1)
182
+ io-console (~> 0.5)
183
+ rexml (3.2.6)
184
+ rubocop (1.59.0)
185
+ json (~> 2.3)
186
+ language_server-protocol (>= 3.17.0)
187
+ parallel (~> 1.10)
188
+ parser (>= 3.2.2.4)
189
+ rainbow (>= 2.2.2, < 4.0)
190
+ regexp_parser (>= 1.8, < 3.0)
191
+ rexml (>= 3.2.5, < 4.0)
192
+ rubocop-ast (>= 1.30.0, < 2.0)
193
+ ruby-progressbar (~> 1.7)
194
+ unicode-display_width (>= 2.4.0, < 3.0)
195
+ rubocop-ast (1.30.0)
196
+ parser (>= 3.2.1.0)
197
+ ruby-progressbar (1.13.0)
198
+ ruby2_keywords (0.0.5)
199
+ stringio (3.1.0)
200
+ thor (1.3.0)
201
+ timeout (0.4.1)
202
+ tzinfo (2.0.6)
203
+ concurrent-ruby (~> 1.0)
204
+ unicode-display_width (2.5.0)
205
+ webrick (1.8.1)
206
+ websocket-driver (0.7.6)
207
+ websocket-extensions (>= 0.1.0)
208
+ websocket-extensions (0.1.5)
209
+ zeitwerk (2.6.12)
210
+
211
+ PLATFORMS
212
+ x86_64-linux
213
+
214
+ DEPENDENCIES
215
+ column_sync!
216
+ fx (~> 0.8.0)
217
+ pg (~> 1.5.4)
218
+ rails (>= 6.0.0)
219
+ rubocop
220
+
221
+ BUNDLED WITH
222
+ 2.4.10
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Manuel Bustillo Alonso
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # ColumnSync
2
+
3
+ Keeping in sync columns in different tables is a pretty common step during database refactorings. The
4
+ `column_sync` gem provides ActiveRecord migration helpers to facilitate the sync of data between
5
+ columns.
6
+
7
+ ## Installation
8
+
9
+ Install the gem and add to the application's Gemfile by executing:
10
+
11
+ $ bundle add column_sync
12
+
13
+ If bundler is not being used to manage dependencies, install the gem by executing:
14
+
15
+ $ gem install column_sync
16
+
17
+ ## Usage
18
+
19
+ The following snippet should be used as a migration template:
20
+
21
+ ```ruby
22
+ class SyncColumns < ActiveRecord::Migration[7.1]
23
+ def up
24
+ sync_columns(Subscription => :country_code, Company => :country)
25
+ end
26
+
27
+ def down
28
+ unsync_columns(Subscription => :country_code, Company => :country)
29
+ end
30
+ end
31
+ ```
32
+
33
+ The migration above generates the functions and triggers needed to keep `subscriptions.country_code` in sync with `companies.country`.
34
+
35
+ ## Limitations
36
+
37
+ - Each `sync_columns` statement can only sync a pair of columns. If the same column needs to be synchronized across multiple
38
+ tables, multiple such statements will be needed.
39
+ - The gem expects a `has_one` - `belongs_to` association between the models involved. It uses Rails reflections to understand
40
+ 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.
43
+ - It also does not sync values when a row is created, only when it is modified.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rubocop/rake_task"
5
+
6
+ RuboCop::RakeTask.new
7
+
8
+ task default: %i[rubocop]
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ColumnSync
4
+ module Migration
5
+ def sync_columns(columns)
6
+ service = Service.new(columns)
7
+
8
+ each_function(service) { |name| create_function(name) }
9
+ each_trigger(service) { |name, table_name| create_trigger(name, on: table_name) }
10
+ end
11
+
12
+ def unsync_columns(columns)
13
+ service = Service.new(columns)
14
+
15
+ each_trigger(service) { |name, table_name| drop_trigger(name, on: table_name) }
16
+ each_function(service) { |name| drop_function(name) }
17
+ end
18
+
19
+ private
20
+
21
+ def each_function(service)
22
+ service.functions.each do |name, definition|
23
+ function_name = "#{name}_v01".to_sym
24
+ file_name = "db/functions/#{function_name}.sql"
25
+ File.open(file_name, "w") { |f| f.write(definition) }
26
+ yield name
27
+ ensure
28
+ FileUtils.rm_f(file_name)
29
+ end
30
+ end
31
+
32
+ def each_trigger(service)
33
+ service.triggers.each do |name, config|
34
+ trigger_name = "#{name}_v01".to_sym
35
+ file_name = "db/triggers/#{trigger_name}.sql"
36
+ File.open(file_name, "w") { |f| f.write(config[:definition]) }
37
+ yield name, config[:table_name]
38
+ ensure
39
+ FileUtils.rm_f(file_name)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ColumnSync
4
+ class Service
5
+ FUNCTION_PREFIX = "column_sync"
6
+ attr_reader :referenced_table_name,
7
+ :referenced_column_name,
8
+ :referenced_primary_key,
9
+ :referencer_table_name,
10
+ :referencer_column_name,
11
+ :referencer_fk_column_name
12
+
13
+ def initialize(columns)
14
+ raise ArgumentError, "columns must be a hash" unless columns.is_a?(Hash)
15
+ raise ArgumentError, "two tables and columns must be provided" unless columns.size == 2
16
+
17
+ referenced_model, referencer_model = columns.keys.permutation(2).find do |(referenced, referencer)|
18
+ referenced.reflect_on_all_associations(:has_one)
19
+ .find { |association| association.klass == referencer }
20
+ .tap { |association| @referencer_fk_column_name = association.foreign_key if association }
21
+ end
22
+
23
+ raise ArgumentError, "no valid column relation found" unless referenced_model && referencer_model
24
+
25
+ @referenced_column_name = columns[referenced_model].to_s
26
+ @referencer_column_name = columns[referencer_model].to_s
27
+
28
+ unless referenced_model.column_names.include?(referenced_column_name)
29
+ raise ArgumentError, "referenced column #{referenced_column_name} does not exist"
30
+ end
31
+
32
+ unless referencer_model.column_names.include?(referencer_column_name)
33
+ raise ArgumentError, "referencer column #{referencer_column_name} does not exist"
34
+ end
35
+
36
+ @referenced_table_name = referenced_model.table_name
37
+ @referenced_primary_key = referenced_model.primary_key
38
+ @referencer_table_name = referencer_model.table_name
39
+ end
40
+
41
+ def functions
42
+ {
43
+ referencer_sync_name => referencer_update_function,
44
+ referenced_sync_name => referenced_update_function
45
+ }
46
+ end
47
+
48
+ def triggers
49
+ {
50
+ referencer_sync_name => { definition: referencer_trigger, table_name: referenced_table_name },
51
+ referenced_sync_name => { definition: referenced_trigger, table_name: referencer_table_name }
52
+ }
53
+ end
54
+
55
+ private
56
+
57
+ def referencer_sync_name
58
+ "#{FUNCTION_PREFIX}_#{referencer_table_name}_#{referencer_column_name}_on_#{referenced_table_name}_update"
59
+ end
60
+
61
+ def referenced_sync_name
62
+ "#{FUNCTION_PREFIX}_#{referenced_table_name}_#{referenced_column_name}_on_#{referencer_table_name}_update"
63
+ end
64
+
65
+ def referencer_update_function
66
+ <<~SQL
67
+ CREATE OR REPLACE FUNCTION #{FUNCTION_PREFIX}_#{referencer_table_name}_#{referencer_column_name}_on_#{referenced_table_name}_update()
68
+ RETURNS TRIGGER AS $$
69
+ BEGIN
70
+ IF NEW.#{referenced_column_name} IS DISTINCT FROM OLD.#{referenced_column_name} THEN
71
+ UPDATE #{referencer_table_name}
72
+ SET #{referencer_column_name} = NEW.#{referenced_column_name}
73
+ WHERE #{referencer_fk_column_name} = NEW.#{referenced_primary_key};
74
+ END IF;
75
+ RETURN NEW;
76
+ END;
77
+ $$ LANGUAGE plpgsql;
78
+ SQL
79
+ end
80
+
81
+ def referenced_update_function
82
+ <<~SQL
83
+ CREATE OR REPLACE FUNCTION #{FUNCTION_PREFIX}_#{referenced_table_name}_#{referenced_column_name}_on_#{referencer_table_name}_update()
84
+ RETURNS TRIGGER AS $$
85
+ BEGIN
86
+ IF NEW.#{referencer_column_name} IS DISTINCT FROM OLD.#{referencer_column_name} THEN
87
+ UPDATE #{referenced_table_name}
88
+ SET #{referenced_column_name} = NEW.#{referencer_column_name}
89
+ WHERE #{referenced_primary_key} = NEW.#{referencer_fk_column_name};
90
+ END IF;
91
+ RETURN NEW;
92
+ END;
93
+ $$ LANGUAGE plpgsql;
94
+ SQL
95
+ end
96
+
97
+ def referencer_trigger
98
+ <<~SQL
99
+ CREATE TRIGGER #{FUNCTION_PREFIX}_#{referencer_table_name}_#{referencer_column_name}_on_#{referenced_table_name}_update
100
+ AFTER UPDATE ON #{referenced_table_name}
101
+ FOR EACH ROW EXECUTE PROCEDURE #{FUNCTION_PREFIX}_#{referencer_table_name}_#{referencer_column_name}_on_#{referenced_table_name}_update();
102
+ SQL
103
+ end
104
+
105
+ def referenced_trigger
106
+ <<~SQL
107
+ CREATE TRIGGER #{FUNCTION_PREFIX}_#{referenced_table_name}_#{referenced_column_name}_on_#{referencer_table_name}_update
108
+ AFTER UPDATE ON #{referencer_table_name}
109
+ FOR EACH ROW EXECUTE PROCEDURE #{FUNCTION_PREFIX}_#{referenced_table_name}_#{referenced_column_name}_on_#{referencer_table_name}_update();
110
+ SQL
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ColumnSync
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+ require "fx"
5
+
6
+ require "column_sync/version"
7
+ require "column_sync/service"
8
+ require "column_sync/migration"
9
+
10
+ module ColumnSync
11
+ class Error < StandardError; end
12
+
13
+ ::ActiveRecord::Migration.include(ColumnSync::Migration)
14
+ end
@@ -0,0 +1,4 @@
1
+ module ColumnSync
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: column_sync
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Manuel Bustillo
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-12-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: fx
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.8.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.8.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: pg
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.5.4
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.5.4
41
+ - !ruby/object:Gem::Dependency
42
+ name: rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 6.0.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 6.0.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ - bustikiller@bustikiller.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".rubocop.yml"
77
+ - CHANGELOG.md
78
+ - Gemfile
79
+ - Gemfile.lock
80
+ - LICENSE
81
+ - README.md
82
+ - Rakefile
83
+ - lib/column_sync.rb
84
+ - lib/column_sync/migration.rb
85
+ - lib/column_sync/service.rb
86
+ - lib/column_sync/version.rb
87
+ - sig/column_sync.rbs
88
+ homepage: https://github.com/bustikiller/column_sync
89
+ licenses: []
90
+ metadata:
91
+ homepage_uri: https://github.com/bustikiller/column_sync
92
+ source_code_uri: https://github.com/bustikiller/column_sync
93
+ changelog_uri: https://github.com/bustikiller/column_sync/blob/main/CHANGELOG.md
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 3.0.0
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubygems_version: 3.4.1
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: A ruby gem to sync values between columns using Postgres and Rails
113
+ test_files: []