Skip to content

Commit 183fcc7

Browse files
committed
Add migration helpers
1 parent 6edef05 commit 183fcc7

File tree

5 files changed

+185
-0
lines changed

5 files changed

+185
-0
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# frozen_string_literal: true
2+
3+
module Code0
4+
module ZeroTrack
5+
module Database
6+
class Migration
7+
# rubocop:disable Naming/ClassAndModuleCamelCase
8+
class V1_0 < ::ActiveRecord::Migration[7.1]
9+
include Database::MigrationHelpers::AddColumnEnhancements
10+
include Database::MigrationHelpers::ConstraintHelpers
11+
include Database::MigrationHelpers::IndexHelpers
12+
include Database::MigrationHelpers::TableEnhancements
13+
end
14+
# rubocop:enable Naming/ClassAndModuleCamelCase
15+
16+
def self.[](version)
17+
version = version.to_s
18+
name = "V#{version.tr('.', '_')}"
19+
raise ArgumentError, "Invalid migration version: #{version}" unless const_defined?(name, false)
20+
21+
const_get(name, false)
22+
end
23+
end
24+
end
25+
end
26+
end
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# frozen_string_literal: true
2+
3+
module Code0
4+
module ZeroTrack
5+
module Database
6+
module MigrationHelpers
7+
module AddColumnEnhancements
8+
def add_column(table_name, column_name, type, *args, **kwargs, &block)
9+
helper_context = self
10+
11+
limit = kwargs.delete(:limit)
12+
unique = kwargs.delete(:unique)
13+
14+
super
15+
16+
return unless type == :text
17+
18+
quoted_column_name = helper_context.quote_column_name(column_name)
19+
20+
if limit
21+
name = helper_context.send(:text_limit_name, table_name, column_name)
22+
23+
definition = "char_length(#{quoted_column_name}) <= #{limit}"
24+
25+
add_check_constraint(table_name, definition, name: name)
26+
end
27+
28+
if unique.is_a?(Hash)
29+
unique[:where] = "#{column_name} IS NOT NULL" if unique.delete(:allow_nil_duplicate)
30+
column_name = "LOWER(#{quoted_column_name})" if unique.delete(:case_insensitive)
31+
32+
add_index table_name, column_name, unique: true, **unique
33+
elsif unique
34+
add_index table_name, column_name, unique: unique
35+
end
36+
end
37+
end
38+
end
39+
end
40+
end
41+
end
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# frozen_string_literal: true
2+
3+
module Code0
4+
module ZeroTrack
5+
module Database
6+
module MigrationHelpers
7+
module ConstraintHelpers
8+
def text_limit_name(table, column, name: nil)
9+
name.presence || check_constraint_name(table, column, 'max_length')
10+
end
11+
12+
def check_constraint_name(table, column, type)
13+
identifier = "#{table}_#{column}_check_#{type}"
14+
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
15+
16+
"check_#{hashed_identifier}"
17+
end
18+
end
19+
end
20+
end
21+
end
22+
end
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# frozen_string_literal: true
2+
3+
module Code0
4+
module ZeroTrack
5+
module Database
6+
module MigrationHelpers
7+
module IndexHelpers
8+
def index_name(table, column, type)
9+
identifier = "#{table}_#{column}_index_#{type}"
10+
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
11+
12+
"index_#{hashed_identifier}"
13+
end
14+
end
15+
end
16+
end
17+
end
18+
end
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# frozen_string_literal: true
2+
3+
module Code0
4+
module ZeroTrack
5+
module Database
6+
module MigrationHelpers
7+
module TableEnhancements
8+
def create_table(table_name, *args, **kwargs, &block)
9+
helper_context = self
10+
11+
super do |t|
12+
enhance(t, table_name, helper_context, &block)
13+
end
14+
end
15+
16+
def change_table(table_name, *args, **kwargs, &block)
17+
helper_context = self
18+
19+
super do |t|
20+
enhance(t, table_name, helper_context, &block)
21+
end
22+
end
23+
24+
private
25+
26+
def enhance(t, table_name, helper_context, &block)
27+
t.define_singleton_method(:text) do |column_name, **inner_kwargs|
28+
limit = inner_kwargs.delete(:limit)
29+
unique = inner_kwargs.delete(:unique)
30+
31+
super(column_name, **inner_kwargs)
32+
33+
quoted_column_name = helper_context.quote_column_name(column_name)
34+
35+
if limit
36+
name = helper_context.send(:text_limit_name, table_name, column_name)
37+
38+
definition = "char_length(#{quoted_column_name}) <= #{limit}"
39+
40+
t.check_constraint(definition, name: name)
41+
end
42+
43+
if unique.is_a?(Hash)
44+
index_definition = column_name
45+
unique[:where] = "#{column_name} IS NOT NULL" if unique.delete(:allow_nil_duplicate)
46+
index_definition = "LOWER(#{quoted_column_name})" if unique.delete(:case_insensitive)
47+
48+
t.index index_definition, unique: true, **unique
49+
elsif unique
50+
t.index column_name, unique: unique
51+
end
52+
end
53+
54+
t.define_singleton_method(:integer) do |column_name, **inner_kwargs|
55+
unique = inner_kwargs.delete(:unique)
56+
57+
super(column_name, **inner_kwargs)
58+
59+
t.index column_name, unique: unique unless unique.nil?
60+
end
61+
62+
return if block.nil?
63+
64+
t.instance_eval do |obj|
65+
if block.arity == 1
66+
block.call(obj)
67+
elsif block.arity == 2
68+
block.call(obj, helper_context)
69+
else
70+
raise ArgumentError, "Unsupported arity of #{block.arity}"
71+
end
72+
end
73+
end
74+
end
75+
end
76+
end
77+
end
78+
end

0 commit comments

Comments
 (0)