column_sync 0.1.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 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: []