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,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
|
|
@@ -1,47 +1,82 @@
|
|
|
1
1
|
require "spec_helper"
|
|
2
2
|
require "generators/fx/trigger/trigger_generator"
|
|
3
3
|
|
|
4
|
-
describe Fx::Generators::TriggerGenerator, :generator do
|
|
4
|
+
RSpec.describe Fx::Generators::TriggerGenerator, :generator do
|
|
5
5
|
it "creates a trigger definition file, and a migration" do
|
|
6
6
|
migration = file("db/migrate/create_trigger_test.rb")
|
|
7
7
|
trigger_definition = file("db/triggers/test_v01.sql")
|
|
8
8
|
|
|
9
|
-
run_generator
|
|
9
|
+
run_generator(
|
|
10
|
+
described_class,
|
|
11
|
+
["test", {"table_name" => "some_table"}]
|
|
12
|
+
)
|
|
10
13
|
|
|
11
14
|
expect(trigger_definition).to exist
|
|
12
|
-
|
|
13
|
-
expect(
|
|
14
|
-
expect(
|
|
15
|
+
expect_to_be_a_migration(migration)
|
|
16
|
+
expect(migration_content(migration)).to include("CreateTriggerTest")
|
|
17
|
+
expect(migration_content(migration)).to include("on: :some_table")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context "when passed --no-migration" do
|
|
21
|
+
it "creates a only trigger definition file" do
|
|
22
|
+
migration = file("db/migrate/create_trigger_test.rb")
|
|
23
|
+
trigger_definition = file("db/triggers/test_v01.sql")
|
|
24
|
+
|
|
25
|
+
run_generator(
|
|
26
|
+
described_class,
|
|
27
|
+
["test", {"table_name" => "some_table"}],
|
|
28
|
+
{migration: false}
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
expect(trigger_definition).to exist
|
|
32
|
+
expect(migration).not_to exist
|
|
33
|
+
end
|
|
15
34
|
end
|
|
16
35
|
|
|
17
36
|
it "supports naming the table as `on` aswell as `table_name`" do
|
|
18
37
|
migration = file("db/migrate/create_trigger_test.rb")
|
|
19
38
|
trigger_definition = file("db/triggers/test_v01.sql")
|
|
20
39
|
|
|
21
|
-
run_generator
|
|
40
|
+
run_generator(
|
|
41
|
+
described_class,
|
|
42
|
+
["test", {"on" => "some_table"}]
|
|
43
|
+
)
|
|
22
44
|
|
|
23
45
|
expect(trigger_definition).to exist
|
|
24
|
-
|
|
25
|
-
expect(
|
|
26
|
-
expect(
|
|
46
|
+
expect_to_be_a_migration(migration)
|
|
47
|
+
expect(migration_content(migration)).to include("CreateTriggerTest")
|
|
48
|
+
expect(migration_content(migration)).to include("on: :some_table")
|
|
27
49
|
end
|
|
28
50
|
|
|
29
51
|
it "requires `table_name` or `on` to be specified" do
|
|
30
|
-
expect
|
|
31
|
-
run_generator
|
|
32
|
-
|
|
52
|
+
expect do
|
|
53
|
+
run_generator(
|
|
54
|
+
described_class,
|
|
55
|
+
["test", {"foo" => "some_table"}]
|
|
56
|
+
)
|
|
57
|
+
end.to raise_error(ArgumentError)
|
|
33
58
|
end
|
|
34
59
|
|
|
35
60
|
it "updates an existing trigger" do
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
61
|
+
with_trigger_definition(
|
|
62
|
+
name: "test",
|
|
63
|
+
version: 1,
|
|
64
|
+
sql_definition: "hello"
|
|
65
|
+
) do
|
|
66
|
+
migration = file("db/migrate/update_trigger_test_to_version_2.rb")
|
|
67
|
+
trigger_definition = file("db/triggers/test_v02.sql")
|
|
39
68
|
|
|
40
|
-
|
|
69
|
+
run_generator(
|
|
70
|
+
described_class,
|
|
71
|
+
["test", {"table_name" => "some_table"}]
|
|
72
|
+
)
|
|
41
73
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
74
|
+
expect(trigger_definition).to exist
|
|
75
|
+
expect_to_be_a_migration(migration)
|
|
76
|
+
expect(migration_content(migration)).to include(
|
|
77
|
+
"UpdateTriggerTestToVersion2"
|
|
78
|
+
)
|
|
79
|
+
expect(migration_content(migration)).to include("on: :some_table")
|
|
80
|
+
end
|
|
46
81
|
end
|
|
47
82
|
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "generators/fx/version_helper"
|
|
3
|
+
|
|
4
|
+
RSpec.describe Fx::Generators::VersionHelper do
|
|
5
|
+
describe "#previous_version" do
|
|
6
|
+
it "returns 0 when no existing versions found" do
|
|
7
|
+
with_temp_directory do |temp_dir|
|
|
8
|
+
helper = described_class.new(file_name: "test_function", definition_path: temp_dir)
|
|
9
|
+
|
|
10
|
+
expect(helper.previous_version).to eq(0)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "returns highest version number from existing files" do
|
|
15
|
+
with_temp_directory do |temp_dir|
|
|
16
|
+
create_version_file(temp_dir, "test_function", 1)
|
|
17
|
+
create_version_file(temp_dir, "test_function", 3)
|
|
18
|
+
create_version_file(temp_dir, "test_function", 2)
|
|
19
|
+
|
|
20
|
+
helper = described_class.new(file_name: "test_function", definition_path: temp_dir)
|
|
21
|
+
|
|
22
|
+
expect(helper.previous_version).to eq(3)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "ignores files that don't match the pattern" do
|
|
27
|
+
with_temp_directory do |temp_dir|
|
|
28
|
+
create_version_file(temp_dir, "test_function", 2)
|
|
29
|
+
FileUtils.touch(temp_dir.join("other_function_v3.sql"))
|
|
30
|
+
FileUtils.touch(temp_dir.join("test_function.sql"))
|
|
31
|
+
|
|
32
|
+
helper = described_class.new(file_name: "test_function", definition_path: temp_dir)
|
|
33
|
+
|
|
34
|
+
expect(helper.previous_version).to eq(2)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe "#current_version" do
|
|
40
|
+
it "returns previous version + 1" do
|
|
41
|
+
with_temp_directory do |temp_dir|
|
|
42
|
+
create_version_file(temp_dir, "test_function", 5)
|
|
43
|
+
|
|
44
|
+
helper = described_class.new(file_name: "test_function", definition_path: temp_dir)
|
|
45
|
+
|
|
46
|
+
expect(helper.current_version).to eq(6)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "returns 1 when no previous versions exist" do
|
|
51
|
+
with_temp_directory do |temp_dir|
|
|
52
|
+
helper = described_class.new(file_name: "test_function", definition_path: temp_dir)
|
|
53
|
+
|
|
54
|
+
expect(helper.current_version).to eq(1)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
describe "#updating_existing?" do
|
|
60
|
+
it "returns false when no previous versions exist" do
|
|
61
|
+
with_temp_directory do |temp_dir|
|
|
62
|
+
helper = described_class.new(file_name: "test_function", definition_path: temp_dir)
|
|
63
|
+
|
|
64
|
+
expect(helper.updating_existing?).to be false
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "returns true when previous versions exist" do
|
|
69
|
+
with_temp_directory do |temp_dir|
|
|
70
|
+
create_version_file(temp_dir, "test_function", 1)
|
|
71
|
+
|
|
72
|
+
helper = described_class.new(file_name: "test_function", definition_path: temp_dir)
|
|
73
|
+
|
|
74
|
+
expect(helper.updating_existing?).to be true
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
describe "#creating_new?" do
|
|
80
|
+
it "returns true when no previous versions exist" do
|
|
81
|
+
with_temp_directory do |temp_dir|
|
|
82
|
+
helper = described_class.new(file_name: "test_function", definition_path: temp_dir)
|
|
83
|
+
|
|
84
|
+
expect(helper.creating_new?).to be true
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "returns false when previous versions exist" do
|
|
89
|
+
with_temp_directory do |temp_dir|
|
|
90
|
+
create_version_file(temp_dir, "test_function", 1)
|
|
91
|
+
|
|
92
|
+
helper = described_class.new(file_name: "test_function", definition_path: temp_dir)
|
|
93
|
+
|
|
94
|
+
expect(helper.creating_new?).to be false
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
describe "#definition_for_version" do
|
|
100
|
+
it "returns function definition for function type" do
|
|
101
|
+
with_temp_directory do |temp_dir|
|
|
102
|
+
helper = described_class.new(file_name: "test_function", definition_path: temp_dir)
|
|
103
|
+
allow(Fx::Definition).to receive(:function)
|
|
104
|
+
.and_return("function_definition")
|
|
105
|
+
|
|
106
|
+
result = helper.definition_for_version(version: 2, type: :function)
|
|
107
|
+
|
|
108
|
+
expect(result).to eq("function_definition")
|
|
109
|
+
expect(Fx::Definition).to have_received(:function).with(
|
|
110
|
+
name: "test_function",
|
|
111
|
+
version: 2
|
|
112
|
+
)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "returns trigger definition for trigger type" do
|
|
117
|
+
with_temp_directory do |temp_dir|
|
|
118
|
+
helper = described_class.new(file_name: "test_trigger", definition_path: temp_dir)
|
|
119
|
+
allow(Fx::Definition).to receive(:trigger)
|
|
120
|
+
.and_return("trigger_definition")
|
|
121
|
+
|
|
122
|
+
result = helper.definition_for_version(version: 3, type: :trigger)
|
|
123
|
+
|
|
124
|
+
expect(result).to eq("trigger_definition")
|
|
125
|
+
expect(Fx::Definition).to have_received(:trigger).with(
|
|
126
|
+
name: "test_trigger",
|
|
127
|
+
version: 3
|
|
128
|
+
)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it "raises error for unknown type" do
|
|
133
|
+
with_temp_directory do |temp_dir|
|
|
134
|
+
helper = described_class.new(file_name: "test_function", definition_path: temp_dir)
|
|
135
|
+
|
|
136
|
+
expect {
|
|
137
|
+
helper.definition_for_version(version: 1, type: :unknown)
|
|
138
|
+
}.to raise_error(
|
|
139
|
+
ArgumentError,
|
|
140
|
+
"Unknown type: unknown. Must be :function or :trigger"
|
|
141
|
+
)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
private
|
|
147
|
+
|
|
148
|
+
def with_temp_directory
|
|
149
|
+
Dir.mktmpdir do |path|
|
|
150
|
+
yield Pathname.new(path)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def create_version_file(dir, name, version)
|
|
155
|
+
FileUtils.touch(dir.join("#{name}_v#{version}.sql"))
|
|
156
|
+
end
|
|
157
|
+
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
ENV["RAILS_ENV"] = "test"
|
|
2
|
-
require "database_cleaner"
|
|
3
2
|
|
|
4
3
|
require File.expand_path("../dummy/config/environment", __FILE__)
|
|
5
4
|
Dir["spec/support/**/*.rb"].sort.each { |file| load file }
|
|
6
5
|
|
|
6
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
7
|
+
require "fx"
|
|
8
|
+
|
|
7
9
|
RSpec.configure do |config|
|
|
8
10
|
config.order = "random"
|
|
9
|
-
|
|
11
|
+
config.disable_monkey_patching!
|
|
12
|
+
|
|
13
|
+
config.define_derived_metadata(file_path: %r{spec/(fx|features)/}) do |metadata|
|
|
14
|
+
metadata[:db] = true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
config.before(:suite) do
|
|
18
|
+
DatabaseReset.call
|
|
19
|
+
end
|
|
10
20
|
|
|
11
21
|
config.around(:each, db: true) do |example|
|
|
12
|
-
|
|
22
|
+
DatabaseReset.call
|
|
23
|
+
|
|
13
24
|
example.run
|
|
14
|
-
|
|
25
|
+
|
|
26
|
+
DatabaseReset.call
|
|
15
27
|
end
|
|
16
28
|
|
|
17
29
|
unless defined?(silence_stream)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module DatabaseReset
|
|
2
|
+
def self.call
|
|
3
|
+
connection = ActiveRecord::Base.connection
|
|
4
|
+
connection.execute("SET search_path TO DEFAULT;")
|
|
5
|
+
|
|
6
|
+
connection.execute <<~SQL
|
|
7
|
+
DO $$
|
|
8
|
+
DECLARE
|
|
9
|
+
schema_name TEXT;
|
|
10
|
+
BEGIN
|
|
11
|
+
FOR schema_name IN
|
|
12
|
+
SELECT nspname FROM pg_namespace
|
|
13
|
+
WHERE nspname NOT LIKE 'pg_%'
|
|
14
|
+
AND nspname != 'information_schema'
|
|
15
|
+
LOOP
|
|
16
|
+
EXECUTE format('DROP SCHEMA IF EXISTS %I CASCADE', schema_name);
|
|
17
|
+
END LOOP;
|
|
18
|
+
END $$;
|
|
19
|
+
SQL
|
|
20
|
+
|
|
21
|
+
connection.execute("CREATE SCHEMA public;")
|
|
22
|
+
connection.schema_search_path = "public"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -1,31 +1,27 @@
|
|
|
1
1
|
module DefinitionHelpers
|
|
2
2
|
def with_function_definition(name:, sql_definition:, version: 1, &block)
|
|
3
|
-
definition = Fx::Definition.
|
|
3
|
+
definition = Fx::Definition.function(name: name, version: version)
|
|
4
4
|
|
|
5
5
|
with_definition(
|
|
6
6
|
definition: definition,
|
|
7
7
|
sql_definition: sql_definition,
|
|
8
|
-
block: block
|
|
8
|
+
block: block
|
|
9
9
|
)
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def with_trigger_definition(name:, sql_definition:, version: 1, &block)
|
|
13
|
-
definition = Fx::Definition.
|
|
14
|
-
name: name,
|
|
15
|
-
version: version,
|
|
16
|
-
type: "trigger",
|
|
17
|
-
)
|
|
13
|
+
definition = Fx::Definition.trigger(name: name, version: version)
|
|
18
14
|
|
|
19
15
|
with_definition(
|
|
20
16
|
definition: definition,
|
|
21
17
|
sql_definition: sql_definition,
|
|
22
|
-
block: block
|
|
18
|
+
block: block
|
|
23
19
|
)
|
|
24
20
|
end
|
|
25
21
|
|
|
26
22
|
def with_definition(definition:, sql_definition:, block:)
|
|
27
23
|
FileUtils.mkdir_p(File.dirname(definition.full_path))
|
|
28
|
-
File.
|
|
24
|
+
File.write(definition.full_path, sql_definition)
|
|
29
25
|
block.call
|
|
30
26
|
ensure
|
|
31
27
|
File.delete definition.full_path
|
|
@@ -1,11 +1,52 @@
|
|
|
1
|
-
|
|
1
|
+
module GeneratorSetup
|
|
2
|
+
RAILS_ROOT = Pathname.new(File.expand_path("../../../tmp/dummy", __dir__)).freeze
|
|
3
|
+
MIGRATION_TIMESTAMP_PATTERN = /\A\d{14}_/
|
|
4
|
+
|
|
5
|
+
def run_generator(generator_class, args = [], options = {})
|
|
6
|
+
allow(Rails).to receive(:root).and_return(RAILS_ROOT)
|
|
7
|
+
generator = generator_class.new(args, options, destination_root: RAILS_ROOT)
|
|
8
|
+
|
|
9
|
+
silence_stream($stdout) do
|
|
10
|
+
generator.invoke_all
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def file(relative_path)
|
|
15
|
+
RAILS_ROOT.join(relative_path)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def migration_content(file_path)
|
|
19
|
+
migration_path = find_migration_files(file_path).first
|
|
20
|
+
return if migration_path.nil?
|
|
21
|
+
|
|
22
|
+
Pathname.new(migration_path).read
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def find_migration_files(file_path)
|
|
26
|
+
directory = File.dirname(file_path)
|
|
27
|
+
basename = File.basename(file_path, ".rb")
|
|
28
|
+
Dir.glob(File.join(directory, "*#{basename}.rb"))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def expect_to_be_a_migration(pathname)
|
|
32
|
+
migration_files = find_migration_files(pathname)
|
|
33
|
+
|
|
34
|
+
expect(migration_files).to be_present,
|
|
35
|
+
"expected #{pathname} to be a migration file"
|
|
36
|
+
first_migration = migration_files.first
|
|
37
|
+
migration_basename = File.basename(first_migration)
|
|
38
|
+
expect(migration_basename).to match(MIGRATION_TIMESTAMP_PATTERN),
|
|
39
|
+
"expected #{migration_basename} to have timestamp prefix (format: YYYYMMDDHHMMSS_)"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
2
42
|
|
|
3
43
|
RSpec.configure do |config|
|
|
44
|
+
config.include GeneratorSetup, :generator
|
|
45
|
+
|
|
4
46
|
config.before(:each, :generator) do
|
|
5
|
-
|
|
6
|
-
|
|
47
|
+
FileUtils.rm_rf(GeneratorSetup::RAILS_ROOT) if File.exist?(GeneratorSetup::RAILS_ROOT)
|
|
48
|
+
FileUtils.mkdir_p(GeneratorSetup::RAILS_ROOT)
|
|
7
49
|
|
|
8
|
-
|
|
9
|
-
prepare_destination
|
|
50
|
+
allow(Rails).to receive(:root).and_return(Pathname.new(GeneratorSetup::RAILS_ROOT))
|
|
10
51
|
end
|
|
11
52
|
end
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
module MigrationsHelper
|
|
2
2
|
def run_migration(migration, directions)
|
|
3
|
-
silence_stream(
|
|
3
|
+
silence_stream($stdout) do
|
|
4
4
|
Array.wrap(directions).each do |direction|
|
|
5
5
|
migration.migrate(direction)
|
|
6
6
|
end
|
|
7
7
|
end
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
+
def migration_class
|
|
11
|
+
if Rails::VERSION::MAJOR >= 5
|
|
12
|
+
::ActiveRecord::Migration[5.0]
|
|
13
|
+
else
|
|
14
|
+
::ActiveRecord::Migration
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
10
18
|
def connection
|
|
11
19
|
@_connection ||= ActiveRecord::Base.connection
|
|
12
20
|
end
|