fx 0.1.0 → 0.11.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 +5 -5
- data/.github/scripts/check-eol +95 -0
- data/.github/workflows/ci.yml +61 -0
- data/.github/workflows/eol-check.yml +30 -0
- data/.rspec +1 -1
- data/.standard.yml +3 -0
- data/CHANGELOG.md +186 -0
- data/CONTRIBUTING.md +13 -4
- data/Gemfile +21 -2
- data/LICENSE +18 -0
- data/README.md +99 -14
- data/Rakefile +11 -1
- data/bin/rake +2 -3
- data/bin/rspec +13 -3
- data/bin/setup +1 -4
- data/bin/standardrb +27 -0
- data/bin/yard +13 -3
- data/fx.gemspec +20 -27
- data/lib/fx/adapters/postgres/connection.rb +6 -0
- data/lib/fx/adapters/postgres/functions.rb +16 -28
- data/lib/fx/adapters/postgres/query_executor.rb +34 -0
- data/lib/fx/adapters/postgres/triggers.rb +14 -28
- data/lib/fx/adapters/postgres.rb +30 -17
- data/lib/fx/command_recorder.rb +104 -13
- data/lib/fx/configuration.rb +10 -25
- data/lib/fx/definition.rb +32 -10
- data/lib/fx/function.rb +23 -9
- data/lib/fx/railtie.rb +15 -0
- data/lib/fx/schema_dumper.rb +33 -10
- data/lib/fx/statements.rb +213 -11
- data/lib/fx/trigger.rb +5 -3
- data/lib/fx/version.rb +1 -1
- data/lib/fx.rb +40 -0
- data/lib/generators/fx/function/USAGE +2 -0
- data/lib/generators/fx/function/function_generator.rb +54 -39
- 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 +45 -0
- data/lib/generators/fx/name_helper.rb +33 -0
- data/lib/generators/fx/trigger/USAGE +2 -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 +46 -51
- data/lib/generators/fx/version_helper.rb +55 -0
- data/spec/acceptance/user_manages_functions_spec.rb +26 -6
- data/spec/acceptance/user_manages_triggers_spec.rb +11 -11
- data/spec/acceptance_helper.rb +17 -11
- data/spec/dummy/Rakefile +8 -1
- data/spec/dummy/bin/bundle +2 -2
- data/spec/dummy/bin/rails +3 -3
- data/spec/dummy/bin/rake +2 -2
- data/spec/dummy/config/application.rb +2 -0
- data/spec/dummy/config/database.yml +2 -0
- data/spec/dummy/config.ru +1 -1
- data/spec/features/functions/migrations_spec.rb +9 -9
- data/spec/features/functions/revert_spec.rb +13 -13
- data/spec/features/triggers/migrations_spec.rb +11 -11
- data/spec/features/triggers/revert_spec.rb +21 -21
- data/spec/fx/adapters/postgres/functions_spec.rb +40 -0
- data/spec/fx/adapters/postgres/query_executor_spec.rb +75 -0
- data/spec/fx/adapters/postgres/triggers_spec.rb +48 -0
- data/spec/fx/adapters/postgres_spec.rb +115 -111
- data/spec/fx/command_recorder_spec.rb +39 -53
- data/spec/fx/configuration_spec.rb +20 -9
- data/spec/fx/definition_spec.rb +53 -38
- data/spec/fx/function_spec.rb +143 -0
- data/spec/fx/schema_dumper_spec.rb +241 -0
- data/spec/fx/statements_spec.rb +219 -0
- data/spec/fx/trigger_spec.rb +52 -0
- data/spec/fx_spec.rb +28 -0
- data/spec/generators/fx/function/function_generator_spec.rb +22 -10
- data/spec/generators/fx/migration_helper_spec.rb +108 -0
- data/spec/generators/fx/name_helper_spec.rb +114 -0
- data/spec/generators/fx/trigger/trigger_generator_spec.rb +55 -20
- data/spec/generators/fx/version_helper_spec.rb +157 -0
- data/spec/spec_helper.rb +16 -4
- data/spec/support/database_reset.rb +24 -0
- data/spec/support/definition_helpers.rb +5 -9
- data/spec/support/generator_setup.rb +46 -5
- data/spec/support/migration_helpers.rb +9 -1
- data/spec/support/warning_helper.rb +5 -0
- metadata +43 -215
- data/.ruby-version +0 -1
- data/.travis.yml +0 -27
- data/Appraisals +0 -21
- data/bin/appraisal +0 -17
- data/gemfiles/rails40.gemfile +0 -8
- data/gemfiles/rails40.gemfile.lock +0 -111
- data/gemfiles/rails41.gemfile +0 -8
- data/gemfiles/rails41.gemfile.lock +0 -113
- data/gemfiles/rails42.gemfile +0 -8
- data/gemfiles/rails42.gemfile.lock +0 -130
- data/gemfiles/rails50.gemfile +0 -8
- data/gemfiles/rails50.gemfile.lock +0 -126
- data/lib/fx/command_recorder/arguments.rb +0 -43
- data/lib/fx/command_recorder/function.rb +0 -30
- data/lib/fx/command_recorder/trigger.rb +0 -30
- data/lib/fx/schema_dumper/function.rb +0 -29
- data/lib/fx/schema_dumper/trigger.rb +0 -29
- data/lib/fx/statements/function.rb +0 -105
- data/lib/fx/statements/trigger.rb +0 -133
- data/spec/fx/command_recorder/arguments_spec.rb +0 -41
- data/spec/fx/schema_dumper/function_spec.rb +0 -22
- data/spec/fx/schema_dumper/trigger_spec.rb +0 -40
- data/spec/fx/statements/function_spec.rb +0 -103
- data/spec/fx/statements/trigger_spec.rb +0 -132
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
RSpec.describe Fx::SchemaDumper, :db do
|
|
4
|
+
it "dumps a create_function for a function in the database" do
|
|
5
|
+
sql_definition = <<~SQL
|
|
6
|
+
CREATE OR REPLACE FUNCTION my_function()
|
|
7
|
+
RETURNS text AS $$
|
|
8
|
+
BEGIN
|
|
9
|
+
RETURN 'test';
|
|
10
|
+
END;
|
|
11
|
+
$$ LANGUAGE plpgsql;
|
|
12
|
+
SQL
|
|
13
|
+
connection.create_function :my_function, sql_definition: sql_definition
|
|
14
|
+
connection.create_table :my_table
|
|
15
|
+
stream = StringIO.new
|
|
16
|
+
output = stream.string
|
|
17
|
+
|
|
18
|
+
dump(connection: connection, stream: stream)
|
|
19
|
+
|
|
20
|
+
expect(output).to match(
|
|
21
|
+
/table "my_table".*function :my_function.*RETURN 'test';/m
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "dumps a create_function for a function in the database" do
|
|
26
|
+
Fx.configuration.dump_functions_at_beginning_of_schema = true
|
|
27
|
+
sql_definition = <<~SQL
|
|
28
|
+
CREATE OR REPLACE FUNCTION my_function()
|
|
29
|
+
RETURNS text AS $$
|
|
30
|
+
BEGIN
|
|
31
|
+
RETURN 'test';
|
|
32
|
+
END;
|
|
33
|
+
$$ LANGUAGE plpgsql;
|
|
34
|
+
SQL
|
|
35
|
+
connection.create_function :my_function, sql_definition: sql_definition
|
|
36
|
+
connection.create_table :my_table
|
|
37
|
+
stream = StringIO.new
|
|
38
|
+
output = stream.string
|
|
39
|
+
|
|
40
|
+
dump(connection: connection, stream: stream)
|
|
41
|
+
|
|
42
|
+
expect(output).to(
|
|
43
|
+
match(/function :my_function.*RETURN 'test';.*table "my_table"/m)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
Fx.configuration.dump_functions_at_beginning_of_schema = false
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "does not dump a create_function for aggregates in the database" do
|
|
50
|
+
sql_definition = <<~SQL
|
|
51
|
+
CREATE OR REPLACE FUNCTION test(text, text)
|
|
52
|
+
RETURNS text AS $$
|
|
53
|
+
BEGIN
|
|
54
|
+
RETURN 'test';
|
|
55
|
+
END;
|
|
56
|
+
$$ LANGUAGE plpgsql;
|
|
57
|
+
SQL
|
|
58
|
+
|
|
59
|
+
aggregate_sql_definition = <<~SQL
|
|
60
|
+
CREATE AGGREGATE aggregate_test(text)
|
|
61
|
+
(
|
|
62
|
+
sfunc = test,
|
|
63
|
+
stype = text
|
|
64
|
+
);
|
|
65
|
+
SQL
|
|
66
|
+
|
|
67
|
+
connection.create_function :test, sql_definition: sql_definition
|
|
68
|
+
connection.execute aggregate_sql_definition
|
|
69
|
+
stream = StringIO.new
|
|
70
|
+
|
|
71
|
+
dump(connection: connection, stream: stream)
|
|
72
|
+
|
|
73
|
+
output = stream.string
|
|
74
|
+
expect(output).to include("create_function :test, sql_definition: <<-'SQL'")
|
|
75
|
+
expect(output).to include("RETURN 'test';")
|
|
76
|
+
expect(output).not_to include("aggregate_test")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "dumps a create_trigger for a trigger in the database" do
|
|
80
|
+
connection.execute <<~SQL
|
|
81
|
+
CREATE TABLE users (
|
|
82
|
+
id int PRIMARY KEY,
|
|
83
|
+
name varchar(256),
|
|
84
|
+
upper_name varchar(256)
|
|
85
|
+
);
|
|
86
|
+
SQL
|
|
87
|
+
Fx.database.create_function <<~SQL
|
|
88
|
+
CREATE OR REPLACE FUNCTION uppercase_users_name()
|
|
89
|
+
RETURNS trigger AS $$
|
|
90
|
+
BEGIN
|
|
91
|
+
NEW.upper_name = UPPER(NEW.name);
|
|
92
|
+
RETURN NEW;
|
|
93
|
+
END;
|
|
94
|
+
$$ LANGUAGE plpgsql;
|
|
95
|
+
SQL
|
|
96
|
+
sql_definition = <<~SQL
|
|
97
|
+
CREATE TRIGGER uppercase_users_name
|
|
98
|
+
BEFORE INSERT ON users
|
|
99
|
+
FOR EACH ROW
|
|
100
|
+
EXECUTE FUNCTION uppercase_users_name();
|
|
101
|
+
SQL
|
|
102
|
+
connection.create_trigger(
|
|
103
|
+
:uppercase_users_name,
|
|
104
|
+
sql_definition: sql_definition
|
|
105
|
+
)
|
|
106
|
+
stream = StringIO.new
|
|
107
|
+
|
|
108
|
+
dump(connection: connection, stream: stream)
|
|
109
|
+
|
|
110
|
+
output = stream.string
|
|
111
|
+
expect(output).to include("create_trigger :uppercase_users_name")
|
|
112
|
+
expect(output).to include("sql_definition: <<-SQL")
|
|
113
|
+
expect(output).to include("EXECUTE FUNCTION uppercase_users_name()")
|
|
114
|
+
end
|
|
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
|
+
ensure
|
|
159
|
+
connection.schema_search_path = "public"
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it "puts a blank line before each function and trigger" do
|
|
163
|
+
connection.create_table :users do |t|
|
|
164
|
+
t.string :name
|
|
165
|
+
t.string :upper_name
|
|
166
|
+
t.string :down_name
|
|
167
|
+
end
|
|
168
|
+
Fx.database.create_function <<~SQL
|
|
169
|
+
CREATE OR REPLACE FUNCTION uppercase_users_name()
|
|
170
|
+
RETURNS trigger AS $$
|
|
171
|
+
BEGIN
|
|
172
|
+
NEW.upper_name = UPPER(NEW.name);
|
|
173
|
+
RETURN NEW;
|
|
174
|
+
END;
|
|
175
|
+
$$ LANGUAGE plpgsql;
|
|
176
|
+
SQL
|
|
177
|
+
sql_definition = <<~SQL
|
|
178
|
+
CREATE TRIGGER uppercase_users_name
|
|
179
|
+
BEFORE INSERT ON users
|
|
180
|
+
FOR EACH ROW
|
|
181
|
+
EXECUTE FUNCTION uppercase_users_name();
|
|
182
|
+
SQL
|
|
183
|
+
connection.create_trigger(
|
|
184
|
+
:uppercase_users_name,
|
|
185
|
+
sql_definition: sql_definition
|
|
186
|
+
)
|
|
187
|
+
Fx.database.create_function <<~SQL
|
|
188
|
+
CREATE OR REPLACE FUNCTION downcase_users_name()
|
|
189
|
+
RETURNS trigger AS $$
|
|
190
|
+
BEGIN
|
|
191
|
+
NEW.down_name = LOWER(NEW.name);
|
|
192
|
+
RETURN NEW;
|
|
193
|
+
END;
|
|
194
|
+
$$ LANGUAGE plpgsql;
|
|
195
|
+
SQL
|
|
196
|
+
sql_definition = <<~SQL
|
|
197
|
+
CREATE TRIGGER downcase_users_name
|
|
198
|
+
BEFORE INSERT ON users
|
|
199
|
+
FOR EACH ROW
|
|
200
|
+
EXECUTE FUNCTION downcase_users_name();
|
|
201
|
+
SQL
|
|
202
|
+
connection.create_trigger(
|
|
203
|
+
:downcase_users_name,
|
|
204
|
+
sql_definition: sql_definition
|
|
205
|
+
)
|
|
206
|
+
stream = StringIO.new
|
|
207
|
+
output = stream.string
|
|
208
|
+
|
|
209
|
+
dump(connection: connection, stream: stream)
|
|
210
|
+
|
|
211
|
+
pattern = /(end|SQL)\n\n (create_function|create_trigger)/
|
|
212
|
+
# a blank line between:
|
|
213
|
+
# - the table and the uppercase_users_name function,
|
|
214
|
+
# - the uppercase_users_name function and the downcase_users_name function,
|
|
215
|
+
# - the downcase_users_name function and the uppercase_users_name trigger,
|
|
216
|
+
# - the uppercase_users_name trigger and the downcase_users_name trigger,
|
|
217
|
+
expect(output.scan(pattern).size).to eq(4)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
it "does not add blank lines when there are no functions or triggers" do
|
|
221
|
+
connection.create_table :users do |t|
|
|
222
|
+
t.string :name
|
|
223
|
+
end
|
|
224
|
+
stream = StringIO.new
|
|
225
|
+
output = stream.string
|
|
226
|
+
|
|
227
|
+
dump(connection: connection, stream: stream)
|
|
228
|
+
|
|
229
|
+
expect(output).not_to match(/create_function/)
|
|
230
|
+
expect(output).not_to match(/create_trigger/)
|
|
231
|
+
expect(output).not_to match(/end\n\n\nend/)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def dump(connection:, stream:)
|
|
235
|
+
if Rails.version >= "7.2"
|
|
236
|
+
ActiveRecord::SchemaDumper.dump(connection.pool, stream)
|
|
237
|
+
else
|
|
238
|
+
ActiveRecord::SchemaDumper.dump(connection, stream)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
RSpec.describe Fx::Statements, :db do
|
|
4
|
+
describe "#create_function" do
|
|
5
|
+
it "creates a function from a file" do
|
|
6
|
+
database = stubbed_database
|
|
7
|
+
definition = stubbed_definition
|
|
8
|
+
|
|
9
|
+
connection.create_function(:test)
|
|
10
|
+
|
|
11
|
+
expect(database).to have_received(:create_function)
|
|
12
|
+
.with(definition.to_sql)
|
|
13
|
+
expect(Fx::Definition).to have_received(:function)
|
|
14
|
+
.with(name: :test, version: 1)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "allows creating a function with a specific version" do
|
|
18
|
+
database = stubbed_database
|
|
19
|
+
definition = stubbed_definition
|
|
20
|
+
|
|
21
|
+
connection.create_function(:test, version: 2)
|
|
22
|
+
|
|
23
|
+
expect(database).to have_received(:create_function)
|
|
24
|
+
.with(definition.to_sql)
|
|
25
|
+
expect(Fx::Definition).to have_received(:function)
|
|
26
|
+
.with(name: :test, version: 2)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "raises an error if both arguments are nil" do
|
|
30
|
+
expect do
|
|
31
|
+
connection.create_function(
|
|
32
|
+
:whatever,
|
|
33
|
+
version: nil,
|
|
34
|
+
sql_definition: nil
|
|
35
|
+
)
|
|
36
|
+
end.to raise_error(
|
|
37
|
+
ArgumentError,
|
|
38
|
+
/version or sql_definition must be specified/
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe "#drop_function" do
|
|
44
|
+
it "drops the function" do
|
|
45
|
+
database = stubbed_database
|
|
46
|
+
|
|
47
|
+
connection.drop_function(:test)
|
|
48
|
+
|
|
49
|
+
expect(database).to have_received(:drop_function).with(:test)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe "#update_function" do
|
|
54
|
+
it "updates the function" do
|
|
55
|
+
database = stubbed_database
|
|
56
|
+
definition = stubbed_definition
|
|
57
|
+
|
|
58
|
+
connection.update_function(:test, version: 3)
|
|
59
|
+
|
|
60
|
+
expect(database).to have_received(:update_function)
|
|
61
|
+
.with(:test, definition.to_sql)
|
|
62
|
+
expect(Fx::Definition).to have_received(:function)
|
|
63
|
+
.with(name: :test, version: 3)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "updates a function from a text definition" do
|
|
67
|
+
database = stubbed_database
|
|
68
|
+
|
|
69
|
+
connection.update_function(:test, sql_definition: "a definition")
|
|
70
|
+
|
|
71
|
+
expect(database).to have_received(:update_function)
|
|
72
|
+
.with(:test, "a definition")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "raises an error if not supplied a version" do
|
|
76
|
+
expect do
|
|
77
|
+
connection.update_function(
|
|
78
|
+
:whatever,
|
|
79
|
+
version: nil,
|
|
80
|
+
sql_definition: nil
|
|
81
|
+
)
|
|
82
|
+
end.to raise_error(
|
|
83
|
+
ArgumentError,
|
|
84
|
+
/version or sql_definition must be specified/
|
|
85
|
+
)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe "#create_trigger" do
|
|
90
|
+
it "creates a trigger from a file" do
|
|
91
|
+
database = stubbed_database
|
|
92
|
+
definition = stubbed_definition
|
|
93
|
+
|
|
94
|
+
connection.create_trigger(:test)
|
|
95
|
+
|
|
96
|
+
expect(database).to have_received(:create_trigger)
|
|
97
|
+
.with(definition.to_sql)
|
|
98
|
+
expect(Fx::Definition).to have_received(:trigger)
|
|
99
|
+
.with(name: :test, version: 1)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "allows creating a trigger with a specific version" do
|
|
103
|
+
database = stubbed_database
|
|
104
|
+
definition = stubbed_definition
|
|
105
|
+
|
|
106
|
+
connection.create_trigger(:test, version: 2)
|
|
107
|
+
|
|
108
|
+
expect(database).to have_received(:create_trigger)
|
|
109
|
+
.with(definition.to_sql)
|
|
110
|
+
expect(Fx::Definition).to have_received(:trigger)
|
|
111
|
+
.with(name: :test, version: 2)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "raises an error if both arguments are set" do
|
|
115
|
+
stubbed_database
|
|
116
|
+
|
|
117
|
+
expect do
|
|
118
|
+
connection.create_trigger(
|
|
119
|
+
:whatever,
|
|
120
|
+
version: 1,
|
|
121
|
+
sql_definition: "a definition"
|
|
122
|
+
)
|
|
123
|
+
end.to raise_error(
|
|
124
|
+
ArgumentError,
|
|
125
|
+
/cannot both be set/
|
|
126
|
+
)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
describe "#drop_trigger" do
|
|
131
|
+
it "drops the trigger" do
|
|
132
|
+
database = stubbed_database
|
|
133
|
+
|
|
134
|
+
connection.drop_trigger(:test, on: :users)
|
|
135
|
+
|
|
136
|
+
expect(database).to have_received(:drop_trigger)
|
|
137
|
+
.with(:test, on: :users)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
describe "#update_trigger" do
|
|
142
|
+
it "updates the trigger" do
|
|
143
|
+
database = stubbed_database
|
|
144
|
+
definition = stubbed_definition
|
|
145
|
+
|
|
146
|
+
connection.update_trigger(:test, on: :users, version: 3)
|
|
147
|
+
|
|
148
|
+
expect(database).to have_received(:update_trigger).with(
|
|
149
|
+
:test,
|
|
150
|
+
on: :users,
|
|
151
|
+
sql_definition: definition.to_sql
|
|
152
|
+
)
|
|
153
|
+
expect(Fx::Definition).to have_received(:trigger).with(
|
|
154
|
+
name: :test,
|
|
155
|
+
version: 3
|
|
156
|
+
)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it "updates a trigger from a text definition" do
|
|
160
|
+
database = stubbed_database
|
|
161
|
+
|
|
162
|
+
connection.update_trigger(
|
|
163
|
+
:test,
|
|
164
|
+
on: :users,
|
|
165
|
+
sql_definition: "a definition"
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
expect(database).to have_received(:update_trigger).with(
|
|
169
|
+
:test,
|
|
170
|
+
on: :users,
|
|
171
|
+
sql_definition: "a definition"
|
|
172
|
+
)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
it "raises an error if not supplied a version" do
|
|
176
|
+
expect do
|
|
177
|
+
connection.update_trigger(
|
|
178
|
+
:whatever,
|
|
179
|
+
on: :users,
|
|
180
|
+
version: nil,
|
|
181
|
+
sql_definition: nil
|
|
182
|
+
)
|
|
183
|
+
end.to raise_error(
|
|
184
|
+
ArgumentError,
|
|
185
|
+
/version or sql_definition must be specified/
|
|
186
|
+
)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it "raises an error if both arguments are set" do
|
|
190
|
+
stubbed_database
|
|
191
|
+
|
|
192
|
+
expect do
|
|
193
|
+
connection.update_trigger(
|
|
194
|
+
:whatever,
|
|
195
|
+
on: :users,
|
|
196
|
+
version: 1,
|
|
197
|
+
sql_definition: "a definition"
|
|
198
|
+
)
|
|
199
|
+
end.to raise_error(
|
|
200
|
+
ArgumentError,
|
|
201
|
+
/cannot both be set/
|
|
202
|
+
)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def stubbed_definition
|
|
207
|
+
instance_double("Fx::Definition", to_sql: nil).tap do |stubbed_definition|
|
|
208
|
+
allow(Fx::Definition).to receive(:function).and_return(stubbed_definition)
|
|
209
|
+
allow(Fx::Definition).to receive(:trigger).and_return(stubbed_definition)
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def stubbed_database
|
|
214
|
+
database = instance_spy("StubbedDatabase")
|
|
215
|
+
allow(Fx).to receive(:database).and_return(database)
|
|
216
|
+
|
|
217
|
+
database
|
|
218
|
+
end
|
|
219
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
RSpec.describe Fx::Trigger do
|
|
4
|
+
describe "#<=>" do
|
|
5
|
+
it "delegates to `name`" do
|
|
6
|
+
trigger_a = Fx::Trigger.new(
|
|
7
|
+
"name" => "name_a",
|
|
8
|
+
"definition" => "some definition"
|
|
9
|
+
)
|
|
10
|
+
trigger_b = Fx::Trigger.new(
|
|
11
|
+
"name" => "name_b",
|
|
12
|
+
"definition" => "some definition"
|
|
13
|
+
)
|
|
14
|
+
trigger_c = Fx::Trigger.new(
|
|
15
|
+
"name" => "name_c",
|
|
16
|
+
"definition" => "some definition"
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
expect(trigger_b).to be_between(trigger_a, trigger_c)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe "#==" do
|
|
24
|
+
it "compares `name` and `definition`" do
|
|
25
|
+
trigger_a = Fx::Trigger.new(
|
|
26
|
+
"name" => "name_a",
|
|
27
|
+
"definition" => "some definition"
|
|
28
|
+
)
|
|
29
|
+
trigger_b = Fx::Trigger.new(
|
|
30
|
+
"name" => "name_b",
|
|
31
|
+
"definition" => "some other definition"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
expect(trigger_a).not_to eq(trigger_b)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe "#to_schema" do
|
|
39
|
+
it "returns a schema compatible version of the trigger" do
|
|
40
|
+
trigger = Fx::Trigger.new(
|
|
41
|
+
"name" => "uppercase_users_name",
|
|
42
|
+
"definition" => "CREATE TRIGGER uppercase_users_name ..."
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
expect(trigger.to_schema).to eq(<<-EOS)
|
|
46
|
+
create_trigger :uppercase_users_name, sql_definition: <<-\SQL
|
|
47
|
+
CREATE TRIGGER uppercase_users_name ...
|
|
48
|
+
SQL
|
|
49
|
+
EOS
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
data/spec/fx_spec.rb
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
RSpec.describe Fx do
|
|
4
|
+
it "has a version number" do
|
|
5
|
+
expect(Fx::VERSION).to be_present
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it "loads fx into ActiveRecord" do
|
|
9
|
+
expect(Fx.load).to eq(true)
|
|
10
|
+
expect(ActiveRecord::Migration::CommandRecorder).to include(Fx::CommandRecorder)
|
|
11
|
+
expect(ActiveRecord::ConnectionAdapters::AbstractAdapter).to include(Fx::Statements)
|
|
12
|
+
expect(ActiveRecord::SchemaDumper).to include(Fx::SchemaDumper)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "allows configuration" do
|
|
16
|
+
adapter = double("Fx Adapter")
|
|
17
|
+
|
|
18
|
+
Fx.configure do |config|
|
|
19
|
+
config.database = adapter
|
|
20
|
+
config.dump_functions_at_beginning_of_schema = true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
expect(Fx.configuration.database).to eq(adapter)
|
|
24
|
+
expect(Fx.configuration.dump_functions_at_beginning_of_schema).to eq(true)
|
|
25
|
+
|
|
26
|
+
Fx.configuration = nil
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -1,34 +1,46 @@
|
|
|
1
1
|
require "spec_helper"
|
|
2
2
|
require "generators/fx/function/function_generator"
|
|
3
3
|
|
|
4
|
-
describe Fx::Generators::FunctionGenerator, :generator do
|
|
4
|
+
RSpec.describe Fx::Generators::FunctionGenerator, :generator do
|
|
5
5
|
it "creates a function definition file, and a migration" 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
|
+
end
|
|
15
|
+
|
|
16
|
+
context "when passed --no-migration" do
|
|
17
|
+
it "creates a only function definition file" do
|
|
18
|
+
migration = file("db/migrate/create_function_test.rb")
|
|
19
|
+
function_definition = file("db/functions/test_v01.sql")
|
|
20
|
+
|
|
21
|
+
run_generator(described_class, ["test"], {migration: false})
|
|
22
|
+
|
|
23
|
+
expect(function_definition).to exist
|
|
24
|
+
expect(migration).not_to exist
|
|
25
|
+
end
|
|
14
26
|
end
|
|
15
27
|
|
|
16
28
|
it "updates an existing function" do
|
|
17
29
|
with_function_definition(
|
|
18
30
|
name: "test",
|
|
19
31
|
version: 1,
|
|
20
|
-
sql_definition: "hello"
|
|
32
|
+
sql_definition: "hello"
|
|
21
33
|
) do
|
|
22
|
-
allow(Dir).to receive(:entries).and_return(["test_v01.sql"])
|
|
23
34
|
migration = file("db/migrate/update_function_test_to_version_2.rb")
|
|
24
35
|
function_definition = file("db/functions/test_v02.sql")
|
|
25
36
|
|
|
26
|
-
run_generator ["test"]
|
|
37
|
+
run_generator(described_class, ["test"])
|
|
27
38
|
|
|
28
39
|
expect(function_definition).to exist
|
|
29
|
-
|
|
30
|
-
expect(
|
|
31
|
-
|
|
40
|
+
expect_to_be_a_migration(migration)
|
|
41
|
+
expect(migration_content(migration)).to include(
|
|
42
|
+
"UpdateFunctionTestToVersion2"
|
|
43
|
+
)
|
|
32
44
|
end
|
|
33
45
|
end
|
|
34
46
|
end
|
|
@@ -0,0 +1,108 @@
|
|
|
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 "#update_migration_class_name" do
|
|
20
|
+
it "generates correct class name for functions" do
|
|
21
|
+
helper = described_class.new({})
|
|
22
|
+
|
|
23
|
+
result = helper.update_migration_class_name(
|
|
24
|
+
object_type: :function,
|
|
25
|
+
class_name: "TestFunction",
|
|
26
|
+
version: 3
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
expect(result).to eq("UpdateFunctionTestFunctionToVersion3")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "generates correct class name for triggers" do
|
|
33
|
+
helper = described_class.new({})
|
|
34
|
+
|
|
35
|
+
result = helper.update_migration_class_name(
|
|
36
|
+
object_type: :trigger,
|
|
37
|
+
class_name: "TestTrigger",
|
|
38
|
+
version: 2
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
expect(result).to eq("UpdateTriggerTestTriggerToVersion2")
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe "#migration_template_info" do
|
|
46
|
+
it "returns create template info for new objects" do
|
|
47
|
+
helper = described_class.new({})
|
|
48
|
+
|
|
49
|
+
result = helper.migration_template_info(
|
|
50
|
+
object_type: :function,
|
|
51
|
+
file_name: "test_func",
|
|
52
|
+
updating_existing: false,
|
|
53
|
+
version: 1
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
expect(result).to eq({
|
|
57
|
+
template: "db/migrate/create_function.erb",
|
|
58
|
+
filename: "db/migrate/create_function_test_func.rb"
|
|
59
|
+
})
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "returns update template info for existing objects" do
|
|
63
|
+
helper = described_class.new({})
|
|
64
|
+
|
|
65
|
+
result = helper.migration_template_info(
|
|
66
|
+
object_type: :trigger,
|
|
67
|
+
file_name: "test_trigger",
|
|
68
|
+
updating_existing: true,
|
|
69
|
+
version: 3
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
expect(result).to eq({
|
|
73
|
+
template: "db/migrate/update_trigger.erb",
|
|
74
|
+
filename: "db/migrate/update_trigger_test_trigger_to_version_3.rb"
|
|
75
|
+
})
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "handles different object types correctly" do
|
|
79
|
+
helper = described_class.new({})
|
|
80
|
+
|
|
81
|
+
function_result = helper.migration_template_info(
|
|
82
|
+
object_type: :function,
|
|
83
|
+
file_name: "my_function",
|
|
84
|
+
updating_existing: true,
|
|
85
|
+
version: 2
|
|
86
|
+
)
|
|
87
|
+
trigger_result = helper.migration_template_info(
|
|
88
|
+
object_type: :trigger,
|
|
89
|
+
file_name: "my_trigger",
|
|
90
|
+
updating_existing: true,
|
|
91
|
+
version: 2
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
expect(function_result.fetch(:template)).to eq(
|
|
95
|
+
"db/migrate/update_function.erb"
|
|
96
|
+
)
|
|
97
|
+
expect(trigger_result.fetch(:template)).to eq(
|
|
98
|
+
"db/migrate/update_trigger.erb"
|
|
99
|
+
)
|
|
100
|
+
expect(function_result.fetch(:filename)).to eq(
|
|
101
|
+
"db/migrate/update_function_my_function_to_version_2.rb"
|
|
102
|
+
)
|
|
103
|
+
expect(trigger_result.fetch(:filename)).to eq(
|
|
104
|
+
"db/migrate/update_trigger_my_trigger_to_version_2.rb"
|
|
105
|
+
)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|