fx 0.9.0 → 0.10.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 +4 -4
- data/.github/workflows/ci.yml +2 -2
- data/CHANGELOG.md +27 -1
- data/Gemfile +0 -1
- data/bin/rake +2 -3
- data/bin/rspec +13 -3
- data/bin/yard +13 -3
- data/fx.gemspec +3 -3
- data/lib/fx/adapters/postgres/connection.rb +12 -4
- data/lib/fx/adapters/postgres/functions.rb +11 -28
- data/lib/fx/adapters/postgres/query_executor.rb +34 -0
- data/lib/fx/adapters/postgres/triggers.rb +14 -29
- data/lib/fx/adapters/postgres.rb +10 -10
- data/lib/fx/configuration.rb +2 -2
- data/lib/fx/schema_dumper.rb +6 -14
- data/lib/fx/statements.rb +61 -58
- data/lib/fx/version.rb +1 -1
- data/lib/generators/fx/function/function_generator.rb +50 -53
- data/lib/generators/fx/function/templates/db/migrate/create_function.erb +1 -1
- data/lib/generators/fx/function/templates/db/migrate/update_function.erb +1 -1
- data/lib/generators/fx/migration_helper.rb +53 -0
- data/lib/generators/fx/name_helper.rb +33 -0
- data/lib/generators/fx/trigger/templates/db/migrate/create_trigger.erb +1 -1
- data/lib/generators/fx/trigger/templates/db/migrate/update_trigger.erb +1 -1
- data/lib/generators/fx/trigger/trigger_generator.rb +45 -64
- data/lib/generators/fx/version_helper.rb +55 -0
- data/spec/acceptance/user_manages_functions_spec.rb +6 -6
- data/spec/acceptance/user_manages_triggers_spec.rb +10 -10
- data/spec/acceptance_helper.rb +2 -2
- data/spec/features/functions/migrations_spec.rb +4 -4
- data/spec/features/functions/revert_spec.rb +4 -4
- data/spec/features/triggers/migrations_spec.rb +6 -6
- data/spec/features/triggers/revert_spec.rb +8 -8
- data/spec/fx/adapters/postgres/functions_spec.rb +12 -5
- data/spec/fx/adapters/postgres/query_executor_spec.rb +75 -0
- data/spec/fx/adapters/postgres/triggers_spec.rb +14 -7
- data/spec/fx/adapters/postgres_spec.rb +62 -20
- data/spec/fx/definition_spec.rb +6 -6
- data/spec/fx/schema_dumper_spec.rb +60 -14
- data/spec/fx_spec.rb +1 -1
- data/spec/generators/fx/function/function_generator_spec.rb +10 -10
- data/spec/generators/fx/migration_helper_spec.rb +133 -0
- data/spec/generators/fx/name_helper_spec.rb +114 -0
- data/spec/generators/fx/trigger/trigger_generator_spec.rb +42 -19
- data/spec/generators/fx/version_helper_spec.rb +157 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/support/generator_setup.rb +46 -5
- metadata +28 -11
|
@@ -5,14 +5,14 @@ RSpec.describe Fx::Adapters::Postgres, :db do
|
|
|
5
5
|
it "successfully creates a function" do
|
|
6
6
|
adapter = Fx::Adapters::Postgres.new
|
|
7
7
|
adapter.create_function(
|
|
8
|
-
<<~
|
|
8
|
+
<<~SQL
|
|
9
9
|
CREATE OR REPLACE FUNCTION test()
|
|
10
10
|
RETURNS text AS $$
|
|
11
11
|
BEGIN
|
|
12
12
|
RETURN 'test';
|
|
13
13
|
END;
|
|
14
14
|
$$ LANGUAGE plpgsql;
|
|
15
|
-
|
|
15
|
+
SQL
|
|
16
16
|
)
|
|
17
17
|
|
|
18
18
|
expect(adapter.functions.map(&:name)).to include("test")
|
|
@@ -21,15 +21,15 @@ RSpec.describe Fx::Adapters::Postgres, :db do
|
|
|
21
21
|
|
|
22
22
|
describe "#create_trigger" do
|
|
23
23
|
it "successfully creates a trigger" do
|
|
24
|
-
connection.execute <<~
|
|
24
|
+
connection.execute <<~SQL
|
|
25
25
|
CREATE TABLE users (
|
|
26
26
|
id int PRIMARY KEY,
|
|
27
27
|
name varchar(256),
|
|
28
28
|
upper_name varchar(256)
|
|
29
29
|
);
|
|
30
|
-
|
|
30
|
+
SQL
|
|
31
31
|
adapter = Fx::Adapters::Postgres.new
|
|
32
|
-
adapter.create_function <<~
|
|
32
|
+
adapter.create_function <<~SQL
|
|
33
33
|
CREATE OR REPLACE FUNCTION uppercase_users_name()
|
|
34
34
|
RETURNS trigger AS $$
|
|
35
35
|
BEGIN
|
|
@@ -37,14 +37,14 @@ RSpec.describe Fx::Adapters::Postgres, :db do
|
|
|
37
37
|
RETURN NEW;
|
|
38
38
|
END;
|
|
39
39
|
$$ LANGUAGE plpgsql;
|
|
40
|
-
|
|
40
|
+
SQL
|
|
41
41
|
adapter.create_trigger(
|
|
42
|
-
<<~
|
|
42
|
+
<<~SQL
|
|
43
43
|
CREATE TRIGGER uppercase_users_name
|
|
44
44
|
BEFORE INSERT ON users
|
|
45
45
|
FOR EACH ROW
|
|
46
46
|
EXECUTE FUNCTION uppercase_users_name();
|
|
47
|
-
|
|
47
|
+
SQL
|
|
48
48
|
)
|
|
49
49
|
|
|
50
50
|
expect(adapter.triggers.map(&:name)).to include("uppercase_users_name")
|
|
@@ -56,14 +56,14 @@ RSpec.describe Fx::Adapters::Postgres, :db do
|
|
|
56
56
|
it "successfully drops a function with the entire function signature" do
|
|
57
57
|
adapter = Fx::Adapters::Postgres.new
|
|
58
58
|
adapter.create_function(
|
|
59
|
-
<<~
|
|
59
|
+
<<~SQL
|
|
60
60
|
CREATE FUNCTION adder(x int, y int)
|
|
61
61
|
RETURNS int AS $$
|
|
62
62
|
BEGIN
|
|
63
63
|
RETURN $1 + $2;
|
|
64
64
|
END;
|
|
65
65
|
$$ LANGUAGE plpgsql;
|
|
66
|
-
|
|
66
|
+
SQL
|
|
67
67
|
)
|
|
68
68
|
|
|
69
69
|
adapter.drop_function(:adder)
|
|
@@ -76,14 +76,14 @@ RSpec.describe Fx::Adapters::Postgres, :db do
|
|
|
76
76
|
it "successfully drops a function" do
|
|
77
77
|
adapter = Fx::Adapters::Postgres.new
|
|
78
78
|
adapter.create_function(
|
|
79
|
-
<<~
|
|
79
|
+
<<~SQL
|
|
80
80
|
CREATE OR REPLACE FUNCTION test()
|
|
81
81
|
RETURNS text AS $$
|
|
82
82
|
BEGIN
|
|
83
83
|
RETURN 'test';
|
|
84
84
|
END;
|
|
85
85
|
$$ LANGUAGE plpgsql;
|
|
86
|
-
|
|
86
|
+
SQL
|
|
87
87
|
)
|
|
88
88
|
|
|
89
89
|
adapter.drop_function(:test)
|
|
@@ -97,14 +97,14 @@ RSpec.describe Fx::Adapters::Postgres, :db do
|
|
|
97
97
|
it "finds functions and builds Fx::Function objects" do
|
|
98
98
|
adapter = Fx::Adapters::Postgres.new
|
|
99
99
|
adapter.create_function(
|
|
100
|
-
<<~
|
|
100
|
+
<<~SQL
|
|
101
101
|
CREATE OR REPLACE FUNCTION test()
|
|
102
102
|
RETURNS text AS $$
|
|
103
103
|
BEGIN
|
|
104
104
|
RETURN 'test';
|
|
105
105
|
END;
|
|
106
106
|
$$ LANGUAGE plpgsql;
|
|
107
|
-
|
|
107
|
+
SQL
|
|
108
108
|
)
|
|
109
109
|
|
|
110
110
|
expect(adapter.functions.map(&:name)).to eq ["test"]
|
|
@@ -113,15 +113,15 @@ RSpec.describe Fx::Adapters::Postgres, :db do
|
|
|
113
113
|
|
|
114
114
|
describe "#triggers" do
|
|
115
115
|
it "finds triggers and builds Fx::Trigger objects" do
|
|
116
|
-
connection.execute <<~
|
|
116
|
+
connection.execute <<~SQL
|
|
117
117
|
CREATE TABLE users (
|
|
118
118
|
id int PRIMARY KEY,
|
|
119
119
|
name varchar(256),
|
|
120
120
|
upper_name varchar(256)
|
|
121
121
|
);
|
|
122
|
-
|
|
122
|
+
SQL
|
|
123
123
|
adapter = Fx::Adapters::Postgres.new
|
|
124
|
-
adapter.create_function <<~
|
|
124
|
+
adapter.create_function <<~SQL
|
|
125
125
|
CREATE OR REPLACE FUNCTION uppercase_users_name()
|
|
126
126
|
RETURNS trigger AS $$
|
|
127
127
|
BEGIN
|
|
@@ -129,16 +129,58 @@ RSpec.describe Fx::Adapters::Postgres, :db do
|
|
|
129
129
|
RETURN NEW;
|
|
130
130
|
END;
|
|
131
131
|
$$ LANGUAGE plpgsql;
|
|
132
|
-
|
|
133
|
-
sql_definition = <<~
|
|
132
|
+
SQL
|
|
133
|
+
sql_definition = <<~SQL
|
|
134
134
|
CREATE TRIGGER uppercase_users_name
|
|
135
135
|
BEFORE INSERT ON users
|
|
136
136
|
FOR EACH ROW
|
|
137
137
|
EXECUTE FUNCTION uppercase_users_name()
|
|
138
|
-
|
|
138
|
+
SQL
|
|
139
139
|
adapter.create_trigger(sql_definition)
|
|
140
140
|
|
|
141
141
|
expect(adapter.triggers.map(&:name)).to eq ["uppercase_users_name"]
|
|
142
142
|
end
|
|
143
143
|
end
|
|
144
|
+
|
|
145
|
+
describe "#support_drop_function_without_args" do
|
|
146
|
+
it "returns true for PostgreSQL version 10.0" do
|
|
147
|
+
adapter = Fx::Adapters::Postgres.new
|
|
148
|
+
connection = adapter.send(:connection)
|
|
149
|
+
allow(connection).to receive(:server_version).and_return(10_00_00)
|
|
150
|
+
|
|
151
|
+
result = connection.support_drop_function_without_args
|
|
152
|
+
|
|
153
|
+
expect(result).to be(true)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it "returns true for PostgreSQL version 11.0" do
|
|
157
|
+
adapter = Fx::Adapters::Postgres.new
|
|
158
|
+
connection = adapter.send(:connection)
|
|
159
|
+
allow(connection).to receive(:server_version).and_return(11_00_00)
|
|
160
|
+
|
|
161
|
+
result = connection.support_drop_function_without_args
|
|
162
|
+
|
|
163
|
+
expect(result).to be(true)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "returns false for PostgreSQL version 9.6" do
|
|
167
|
+
adapter = Fx::Adapters::Postgres.new
|
|
168
|
+
connection = adapter.send(:connection)
|
|
169
|
+
allow(connection).to receive(:server_version).and_return(9_06_00)
|
|
170
|
+
|
|
171
|
+
result = connection.support_drop_function_without_args
|
|
172
|
+
|
|
173
|
+
expect(result).to be(false)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it "returns false for PostgreSQL version 9.5" do
|
|
177
|
+
adapter = Fx::Adapters::Postgres.new
|
|
178
|
+
connection = adapter.send(:connection)
|
|
179
|
+
allow(connection).to receive(:server_version).and_return(9_05_00)
|
|
180
|
+
|
|
181
|
+
result = connection.support_drop_function_without_args
|
|
182
|
+
|
|
183
|
+
expect(result).to be(false)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
144
186
|
end
|
data/spec/fx/definition_spec.rb
CHANGED
|
@@ -4,14 +4,14 @@ RSpec.describe Fx::Definition do
|
|
|
4
4
|
describe "#to_sql" do
|
|
5
5
|
context "representing a function definition" do
|
|
6
6
|
it "returns the content of a function definition" do
|
|
7
|
-
sql_definition =
|
|
7
|
+
sql_definition = <<~SQL
|
|
8
8
|
CREATE OR REPLACE FUNCTION test()
|
|
9
9
|
RETURNS text AS $$
|
|
10
10
|
BEGIN
|
|
11
11
|
RETURN 'test';
|
|
12
12
|
END;
|
|
13
13
|
$$ LANGUAGE plpgsql;
|
|
14
|
-
|
|
14
|
+
SQL
|
|
15
15
|
allow(File).to receive(:read).and_return(sql_definition)
|
|
16
16
|
|
|
17
17
|
definition = Fx::Definition.function(name: "test", version: 1)
|
|
@@ -33,14 +33,14 @@ RSpec.describe Fx::Definition do
|
|
|
33
33
|
|
|
34
34
|
context "when definition is at Rails engine" do
|
|
35
35
|
it "returns the content of a function definition" do
|
|
36
|
-
sql_definition = <<~
|
|
36
|
+
sql_definition = <<~SQL
|
|
37
37
|
CREATE OR REPLACE FUNCTION test()
|
|
38
38
|
RETURNS text AS $$
|
|
39
39
|
BEGIN
|
|
40
40
|
RETURN 'test';
|
|
41
41
|
END;
|
|
42
42
|
$$ LANGUAGE plpgsql;
|
|
43
|
-
|
|
43
|
+
SQL
|
|
44
44
|
engine_path = Rails.root.join("tmp", "engine")
|
|
45
45
|
FileUtils.mkdir_p(engine_path.join("db", "functions"))
|
|
46
46
|
File.write(engine_path.join("db", "functions", "custom_test_v01.sql"), sql_definition)
|
|
@@ -57,12 +57,12 @@ RSpec.describe Fx::Definition do
|
|
|
57
57
|
|
|
58
58
|
context "representing a trigger definition" do
|
|
59
59
|
it "returns the content of a trigger definition" do
|
|
60
|
-
sql_definition = <<~
|
|
60
|
+
sql_definition = <<~SQL
|
|
61
61
|
CREATE TRIGGER check_update
|
|
62
62
|
BEFORE UPDATE ON accounts
|
|
63
63
|
FOR EACH ROW
|
|
64
64
|
EXECUTE FUNCTION check_account_update();
|
|
65
|
-
|
|
65
|
+
SQL
|
|
66
66
|
allow(File).to receive(:read).and_return(sql_definition)
|
|
67
67
|
|
|
68
68
|
definition = Fx::Definition.trigger(name: "test", version: 1)
|
|
@@ -2,14 +2,14 @@ require "spec_helper"
|
|
|
2
2
|
|
|
3
3
|
RSpec.describe Fx::SchemaDumper, :db do
|
|
4
4
|
it "dumps a create_function for a function in the database" do
|
|
5
|
-
sql_definition = <<~
|
|
5
|
+
sql_definition = <<~SQL
|
|
6
6
|
CREATE OR REPLACE FUNCTION my_function()
|
|
7
7
|
RETURNS text AS $$
|
|
8
8
|
BEGIN
|
|
9
9
|
RETURN 'test';
|
|
10
10
|
END;
|
|
11
11
|
$$ LANGUAGE plpgsql;
|
|
12
|
-
|
|
12
|
+
SQL
|
|
13
13
|
connection.create_function :my_function, sql_definition: sql_definition
|
|
14
14
|
connection.create_table :my_table
|
|
15
15
|
stream = StringIO.new
|
|
@@ -24,14 +24,14 @@ RSpec.describe Fx::SchemaDumper, :db do
|
|
|
24
24
|
|
|
25
25
|
it "dumps a create_function for a function in the database" do
|
|
26
26
|
Fx.configuration.dump_functions_at_beginning_of_schema = true
|
|
27
|
-
sql_definition = <<~
|
|
27
|
+
sql_definition = <<~SQL
|
|
28
28
|
CREATE OR REPLACE FUNCTION my_function()
|
|
29
29
|
RETURNS text AS $$
|
|
30
30
|
BEGIN
|
|
31
31
|
RETURN 'test';
|
|
32
32
|
END;
|
|
33
33
|
$$ LANGUAGE plpgsql;
|
|
34
|
-
|
|
34
|
+
SQL
|
|
35
35
|
connection.create_function :my_function, sql_definition: sql_definition
|
|
36
36
|
connection.create_table :my_table
|
|
37
37
|
stream = StringIO.new
|
|
@@ -47,22 +47,22 @@ RSpec.describe Fx::SchemaDumper, :db do
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
it "does not dump a create_function for aggregates in the database" do
|
|
50
|
-
sql_definition = <<~
|
|
50
|
+
sql_definition = <<~SQL
|
|
51
51
|
CREATE OR REPLACE FUNCTION test(text, text)
|
|
52
52
|
RETURNS text AS $$
|
|
53
53
|
BEGIN
|
|
54
54
|
RETURN 'test';
|
|
55
55
|
END;
|
|
56
56
|
$$ LANGUAGE plpgsql;
|
|
57
|
-
|
|
57
|
+
SQL
|
|
58
58
|
|
|
59
|
-
aggregate_sql_definition = <<~
|
|
59
|
+
aggregate_sql_definition = <<~SQL
|
|
60
60
|
CREATE AGGREGATE aggregate_test(text)
|
|
61
61
|
(
|
|
62
62
|
sfunc = test,
|
|
63
63
|
stype = text
|
|
64
64
|
);
|
|
65
|
-
|
|
65
|
+
SQL
|
|
66
66
|
|
|
67
67
|
connection.create_function :test, sql_definition: sql_definition
|
|
68
68
|
connection.execute aggregate_sql_definition
|
|
@@ -77,14 +77,14 @@ RSpec.describe Fx::SchemaDumper, :db do
|
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
it "dumps a create_trigger for a trigger in the database" do
|
|
80
|
-
connection.execute <<~
|
|
80
|
+
connection.execute <<~SQL
|
|
81
81
|
CREATE TABLE users (
|
|
82
82
|
id int PRIMARY KEY,
|
|
83
83
|
name varchar(256),
|
|
84
84
|
upper_name varchar(256)
|
|
85
85
|
);
|
|
86
|
-
|
|
87
|
-
Fx.database.create_function <<~
|
|
86
|
+
SQL
|
|
87
|
+
Fx.database.create_function <<~SQL
|
|
88
88
|
CREATE OR REPLACE FUNCTION uppercase_users_name()
|
|
89
89
|
RETURNS trigger AS $$
|
|
90
90
|
BEGIN
|
|
@@ -92,13 +92,13 @@ RSpec.describe Fx::SchemaDumper, :db do
|
|
|
92
92
|
RETURN NEW;
|
|
93
93
|
END;
|
|
94
94
|
$$ LANGUAGE plpgsql;
|
|
95
|
-
|
|
96
|
-
sql_definition = <<~
|
|
95
|
+
SQL
|
|
96
|
+
sql_definition = <<~SQL
|
|
97
97
|
CREATE TRIGGER uppercase_users_name
|
|
98
98
|
BEFORE INSERT ON users
|
|
99
99
|
FOR EACH ROW
|
|
100
100
|
EXECUTE FUNCTION uppercase_users_name();
|
|
101
|
-
|
|
101
|
+
SQL
|
|
102
102
|
connection.create_trigger(
|
|
103
103
|
:uppercase_users_name,
|
|
104
104
|
sql_definition: sql_definition
|
|
@@ -113,6 +113,52 @@ RSpec.describe Fx::SchemaDumper, :db do
|
|
|
113
113
|
expect(output).to include("EXECUTE FUNCTION uppercase_users_name()")
|
|
114
114
|
end
|
|
115
115
|
|
|
116
|
+
it "dumps functions and triggers for multiple schemas" do
|
|
117
|
+
connection.schema_search_path = "public,test_schema"
|
|
118
|
+
connection.create_table :my_table
|
|
119
|
+
connection.create_function :test1, sql_definition: <<~SQL
|
|
120
|
+
CREATE OR REPLACE FUNCTION test_public_func()
|
|
121
|
+
RETURNS TRIGGER AS $$
|
|
122
|
+
BEGIN
|
|
123
|
+
RETURN 1;
|
|
124
|
+
END;
|
|
125
|
+
$$ LANGUAGE plpgsql;
|
|
126
|
+
SQL
|
|
127
|
+
connection.create_trigger :test1_trigger, sql_definition: <<~SQL
|
|
128
|
+
CREATE TRIGGER test_public_trigger
|
|
129
|
+
BEFORE INSERT ON my_table
|
|
130
|
+
FOR EACH ROW
|
|
131
|
+
EXECUTE FUNCTION test_public_func();
|
|
132
|
+
SQL
|
|
133
|
+
connection.execute("CREATE SCHEMA test_schema;")
|
|
134
|
+
connection.create_table "test_schema.my_table2"
|
|
135
|
+
connection.execute <<~SQL
|
|
136
|
+
CREATE OR REPLACE FUNCTION test_schema.test_schema_func()
|
|
137
|
+
RETURNS TRIGGER AS $$
|
|
138
|
+
BEGIN
|
|
139
|
+
RETURN 'test_schema';
|
|
140
|
+
END;
|
|
141
|
+
$$ LANGUAGE plpgsql;
|
|
142
|
+
SQL
|
|
143
|
+
connection.execute <<~SQL
|
|
144
|
+
CREATE TRIGGER test_schema_trigger
|
|
145
|
+
BEFORE INSERT ON test_schema.my_table2
|
|
146
|
+
FOR EACH ROW
|
|
147
|
+
EXECUTE FUNCTION test_schema.test_schema_func();
|
|
148
|
+
SQL
|
|
149
|
+
stream = StringIO.new
|
|
150
|
+
output = stream.string
|
|
151
|
+
|
|
152
|
+
dump(connection: connection, stream: stream)
|
|
153
|
+
|
|
154
|
+
expect(output.scan("create_function :test_public_func").size).to eq(1)
|
|
155
|
+
expect(output.scan("create_trigger :test_public_trigger").size).to eq(1)
|
|
156
|
+
expect(output.scan("create_function :test_schema_func").size).to eq(1)
|
|
157
|
+
expect(output.scan("create_trigger :test_schema_trigger").size).to eq(1)
|
|
158
|
+
|
|
159
|
+
connection.schema_search_path = "public"
|
|
160
|
+
end
|
|
161
|
+
|
|
116
162
|
def dump(connection:, stream:)
|
|
117
163
|
if Rails.version >= "7.2"
|
|
118
164
|
ActiveRecord::SchemaDumper.dump(connection.pool, stream)
|
data/spec/fx_spec.rb
CHANGED
|
@@ -6,10 +6,10 @@ RSpec.describe Fx do
|
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
it "loads fx into ActiveRecord" do
|
|
9
|
+
expect(Fx.load).to eq(true)
|
|
9
10
|
expect(ActiveRecord::Migration::CommandRecorder).to include(Fx::CommandRecorder)
|
|
10
11
|
expect(ActiveRecord::ConnectionAdapters::AbstractAdapter).to include(Fx::Statements)
|
|
11
12
|
expect(ActiveRecord::SchemaDumper).to include(Fx::SchemaDumper)
|
|
12
|
-
expect(Fx.load).to eq(true)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
it "allows configuration" do
|
|
@@ -6,11 +6,11 @@ RSpec.describe Fx::Generators::FunctionGenerator, :generator do
|
|
|
6
6
|
migration = file("db/migrate/create_function_test.rb")
|
|
7
7
|
function_definition = file("db/functions/test_v01.sql")
|
|
8
8
|
|
|
9
|
-
run_generator ["test"]
|
|
9
|
+
run_generator(described_class, ["test"])
|
|
10
10
|
|
|
11
11
|
expect(function_definition).to exist
|
|
12
|
-
|
|
13
|
-
expect(
|
|
12
|
+
expect_to_be_a_migration(migration)
|
|
13
|
+
expect(migration_content(migration)).to include("CreateFunctionTest")
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
context "when passed --no-migration" do
|
|
@@ -18,10 +18,10 @@ RSpec.describe Fx::Generators::FunctionGenerator, :generator do
|
|
|
18
18
|
migration = file("db/migrate/create_function_test.rb")
|
|
19
19
|
function_definition = file("db/functions/test_v01.sql")
|
|
20
20
|
|
|
21
|
-
run_generator ["test",
|
|
21
|
+
run_generator(described_class, ["test"], {migration: false})
|
|
22
22
|
|
|
23
23
|
expect(function_definition).to exist
|
|
24
|
-
expect(
|
|
24
|
+
expect(migration).not_to exist
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
|
|
@@ -31,16 +31,16 @@ RSpec.describe Fx::Generators::FunctionGenerator, :generator do
|
|
|
31
31
|
version: 1,
|
|
32
32
|
sql_definition: "hello"
|
|
33
33
|
) do
|
|
34
|
-
allow(Dir).to receive(:entries).and_return(["test_v01.sql"])
|
|
35
34
|
migration = file("db/migrate/update_function_test_to_version_2.rb")
|
|
36
35
|
function_definition = file("db/functions/test_v02.sql")
|
|
37
36
|
|
|
38
|
-
run_generator ["test"]
|
|
37
|
+
run_generator(described_class, ["test"])
|
|
39
38
|
|
|
40
39
|
expect(function_definition).to exist
|
|
41
|
-
|
|
42
|
-
expect(
|
|
43
|
-
|
|
40
|
+
expect_to_be_a_migration(migration)
|
|
41
|
+
expect(migration_content(migration)).to include(
|
|
42
|
+
"UpdateFunctionTestToVersion2"
|
|
43
|
+
)
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "generators/fx/migration_helper"
|
|
3
|
+
|
|
4
|
+
RSpec.describe Fx::Generators::MigrationHelper do
|
|
5
|
+
describe "#skip_creation?" do
|
|
6
|
+
it "returns false by default" do
|
|
7
|
+
helper = described_class.new({})
|
|
8
|
+
|
|
9
|
+
expect(helper.skip_creation?).to be(false)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "returns true when migration option is false" do
|
|
13
|
+
helper = described_class.new(migration: false)
|
|
14
|
+
|
|
15
|
+
expect(helper.skip_creation?).to be(true)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
describe "#active_record_migration_class" do
|
|
20
|
+
it "returns versioned migration class when current_version is available" do
|
|
21
|
+
allow(ActiveRecord::Migration).to receive(:current_version)
|
|
22
|
+
.and_return(7.0)
|
|
23
|
+
|
|
24
|
+
helper = described_class.new({})
|
|
25
|
+
|
|
26
|
+
expect(helper.active_record_migration_class).to eq(
|
|
27
|
+
"ActiveRecord::Migration[7.0]"
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "returns base migration class when current_version is not available" do
|
|
32
|
+
allow(ActiveRecord::Migration).to receive(:respond_to?)
|
|
33
|
+
.with(:current_version)
|
|
34
|
+
.and_return(false)
|
|
35
|
+
|
|
36
|
+
helper = described_class.new({})
|
|
37
|
+
|
|
38
|
+
expect(helper.active_record_migration_class).to eq(
|
|
39
|
+
"ActiveRecord::Migration"
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe "#update_migration_class_name" do
|
|
45
|
+
it "generates correct class name for functions" do
|
|
46
|
+
helper = described_class.new({})
|
|
47
|
+
|
|
48
|
+
result = helper.update_migration_class_name(
|
|
49
|
+
object_type: :function,
|
|
50
|
+
class_name: "TestFunction",
|
|
51
|
+
version: 3
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
expect(result).to eq("UpdateFunctionTestFunctionToVersion3")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "generates correct class name for triggers" do
|
|
58
|
+
helper = described_class.new({})
|
|
59
|
+
|
|
60
|
+
result = helper.update_migration_class_name(
|
|
61
|
+
object_type: :trigger,
|
|
62
|
+
class_name: "TestTrigger",
|
|
63
|
+
version: 2
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
expect(result).to eq("UpdateTriggerTestTriggerToVersion2")
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
describe "#migration_template_info" do
|
|
71
|
+
it "returns create template info for new objects" do
|
|
72
|
+
helper = described_class.new({})
|
|
73
|
+
|
|
74
|
+
result = helper.migration_template_info(
|
|
75
|
+
object_type: :function,
|
|
76
|
+
file_name: "test_func",
|
|
77
|
+
updating_existing: false,
|
|
78
|
+
version: 1
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
expect(result).to eq({
|
|
82
|
+
template: "db/migrate/create_function.erb",
|
|
83
|
+
filename: "db/migrate/create_function_test_func.rb"
|
|
84
|
+
})
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "returns update template info for existing objects" do
|
|
88
|
+
helper = described_class.new({})
|
|
89
|
+
|
|
90
|
+
result = helper.migration_template_info(
|
|
91
|
+
object_type: :trigger,
|
|
92
|
+
file_name: "test_trigger",
|
|
93
|
+
updating_existing: true,
|
|
94
|
+
version: 3
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
expect(result).to eq({
|
|
98
|
+
template: "db/migrate/update_trigger.erb",
|
|
99
|
+
filename: "db/migrate/update_trigger_test_trigger_to_version_3.rb"
|
|
100
|
+
})
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "handles different object types correctly" do
|
|
104
|
+
helper = described_class.new({})
|
|
105
|
+
|
|
106
|
+
function_result = helper.migration_template_info(
|
|
107
|
+
object_type: :function,
|
|
108
|
+
file_name: "my_function",
|
|
109
|
+
updating_existing: true,
|
|
110
|
+
version: 2
|
|
111
|
+
)
|
|
112
|
+
trigger_result = helper.migration_template_info(
|
|
113
|
+
object_type: :trigger,
|
|
114
|
+
file_name: "my_trigger",
|
|
115
|
+
updating_existing: true,
|
|
116
|
+
version: 2
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
expect(function_result.fetch(:template)).to eq(
|
|
120
|
+
"db/migrate/update_function.erb"
|
|
121
|
+
)
|
|
122
|
+
expect(trigger_result.fetch(:template)).to eq(
|
|
123
|
+
"db/migrate/update_trigger.erb"
|
|
124
|
+
)
|
|
125
|
+
expect(function_result.fetch(:filename)).to eq(
|
|
126
|
+
"db/migrate/update_function_my_function_to_version_2.rb"
|
|
127
|
+
)
|
|
128
|
+
expect(trigger_result.fetch(:filename)).to eq(
|
|
129
|
+
"db/migrate/update_trigger_my_trigger_to_version_2.rb"
|
|
130
|
+
)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "generators/fx/name_helper"
|
|
3
|
+
|
|
4
|
+
RSpec.describe Fx::Generators::NameHelper do
|
|
5
|
+
describe ".format_for_migration" do
|
|
6
|
+
it "returns symbol format for simple names" do
|
|
7
|
+
result = described_class.format_for_migration("simple_name")
|
|
8
|
+
|
|
9
|
+
expect(result).to eq(":simple_name")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "returns quoted string format for schema-qualified names" do
|
|
13
|
+
result = described_class.format_for_migration("schema.function_name")
|
|
14
|
+
|
|
15
|
+
expect(result).to eq("\"schema.function_name\"")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "handles names with multiple dots" do
|
|
19
|
+
result = described_class.format_for_migration("db.schema.function")
|
|
20
|
+
|
|
21
|
+
expect(result).to eq("\"db.schema.function\"")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "handles empty names" do
|
|
25
|
+
result = described_class.format_for_migration("")
|
|
26
|
+
|
|
27
|
+
expect(result).to eq(":")
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe ".format_table_name_from_hash" do
|
|
32
|
+
it "formats table_name key correctly" do
|
|
33
|
+
table_hash = {"table_name" => "users"}
|
|
34
|
+
|
|
35
|
+
result = described_class.format_table_name_from_hash(table_hash)
|
|
36
|
+
|
|
37
|
+
expect(result).to eq(":users")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "formats on key correctly" do
|
|
41
|
+
table_hash = {"on" => "posts"}
|
|
42
|
+
|
|
43
|
+
result = described_class.format_table_name_from_hash(table_hash)
|
|
44
|
+
|
|
45
|
+
expect(result).to eq(":posts")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "prefers table_name over on when both are present" do
|
|
49
|
+
table_hash = {"table_name" => "users", "on" => "posts"}
|
|
50
|
+
|
|
51
|
+
result = described_class.format_table_name_from_hash(table_hash)
|
|
52
|
+
|
|
53
|
+
expect(result).to eq(":users")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "handles schema-qualified table names" do
|
|
57
|
+
table_hash = {"table_name" => "public.users"}
|
|
58
|
+
|
|
59
|
+
result = described_class.format_table_name_from_hash(table_hash)
|
|
60
|
+
|
|
61
|
+
expect(result).to eq("\"public.users\"")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "raises error when neither key is present" do
|
|
65
|
+
table_hash = {"something_else" => "value"}
|
|
66
|
+
|
|
67
|
+
expect {
|
|
68
|
+
described_class.format_table_name_from_hash(table_hash)
|
|
69
|
+
}.to raise_error(
|
|
70
|
+
ArgumentError,
|
|
71
|
+
"Either `table_name:NAME` or `on:NAME` must be specified"
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "raises error when both keys have nil values" do
|
|
76
|
+
table_hash = {"table_name" => nil, "on" => nil}
|
|
77
|
+
|
|
78
|
+
expect {
|
|
79
|
+
described_class.format_table_name_from_hash(table_hash)
|
|
80
|
+
}.to raise_error(
|
|
81
|
+
ArgumentError,
|
|
82
|
+
"Either `table_name:NAME` or `on:NAME` must be specified"
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "uses on key when table_name is nil" do
|
|
87
|
+
table_hash = {"table_name" => nil, "on" => "comments"}
|
|
88
|
+
|
|
89
|
+
result = described_class.format_table_name_from_hash(table_hash)
|
|
90
|
+
|
|
91
|
+
expect(result).to eq(":comments")
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
describe ".validate_and_format" do
|
|
96
|
+
it "formats valid names correctly" do
|
|
97
|
+
result = described_class.validate_and_format("test_function")
|
|
98
|
+
|
|
99
|
+
expect(result).to eq(":test_function")
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "formats schema-qualified names correctly" do
|
|
103
|
+
result = described_class.validate_and_format("schema.test")
|
|
104
|
+
|
|
105
|
+
expect(result).to eq("\"schema.test\"")
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it "raises error for blank names" do
|
|
109
|
+
expect {
|
|
110
|
+
described_class.validate_and_format("")
|
|
111
|
+
}.to raise_error(ArgumentError, "Name cannot be blank")
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|