fx 0.8.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 -7
- data/CHANGELOG.md +150 -0
- data/CONTRIBUTING.md +3 -3
- data/Gemfile +11 -1
- data/README.md +2 -0
- data/bin/rake +2 -3
- data/bin/rspec +13 -3
- data/bin/standardrb +27 -0
- data/bin/yard +13 -3
- data/fx.gemspec +10 -15
- data/lib/fx/adapters/postgres/connection.rb +20 -0
- 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 +16 -24
- data/lib/fx/command_recorder.rb +87 -6
- data/lib/fx/configuration.rb +2 -27
- data/lib/fx/definition.rb +16 -6
- data/lib/fx/function.rb +3 -3
- data/lib/fx/schema_dumper.rb +37 -5
- data/lib/fx/statements.rb +231 -6
- data/lib/fx/trigger.rb +3 -3
- data/lib/fx/version.rb +1 -1
- data/lib/fx.rb +30 -12
- 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 +44 -67
- data/lib/generators/fx/version_helper.rb +55 -0
- data/spec/acceptance/user_manages_functions_spec.rb +7 -7
- data/spec/acceptance/user_manages_triggers_spec.rb +11 -11
- data/spec/acceptance_helper.rb +4 -4
- data/spec/dummy/config/application.rb +5 -1
- data/spec/features/functions/migrations_spec.rb +5 -5
- data/spec/features/functions/revert_spec.rb +5 -5
- data/spec/features/triggers/migrations_spec.rb +7 -7
- data/spec/features/triggers/revert_spec.rb +9 -9
- data/spec/fx/adapters/postgres/functions_spec.rb +33 -30
- data/spec/fx/adapters/postgres/query_executor_spec.rb +75 -0
- data/spec/fx/adapters/postgres/triggers_spec.rb +41 -38
- data/spec/fx/adapters/postgres_spec.rb +155 -115
- data/spec/fx/command_recorder_spec.rb +27 -25
- data/spec/fx/configuration_spec.rb +20 -9
- data/spec/fx/definition_spec.rb +31 -39
- data/spec/fx/function_spec.rb +45 -48
- data/spec/fx/schema_dumper_spec.rb +169 -0
- data/spec/fx/statements_spec.rb +217 -0
- data/spec/fx/trigger_spec.rb +37 -40
- data/spec/fx_spec.rb +28 -0
- data/spec/generators/fx/function/function_generator_spec.rb +11 -11
- 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 +45 -22
- data/spec/generators/fx/version_helper_spec.rb +157 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/support/definition_helpers.rb +2 -6
- data/spec/support/generator_setup.rb +46 -5
- data/spec/support/warning_helper.rb +5 -0
- metadata +40 -165
- 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 -38
- data/lib/fx/schema_dumper/trigger.rb +0 -29
- data/lib/fx/statements/function.rb +0 -113
- data/lib/fx/statements/trigger.rb +0 -144
- data/spec/fx/command_recorder/arguments_spec.rb +0 -41
- data/spec/fx/schema_dumper/function_spec.rb +0 -78
- 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,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
|
|
@@ -1,17 +1,20 @@
|
|
|
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")
|
|
15
18
|
end
|
|
16
19
|
|
|
17
20
|
context "when passed --no-migration" do
|
|
@@ -19,10 +22,14 @@ describe Fx::Generators::TriggerGenerator, :generator do
|
|
|
19
22
|
migration = file("db/migrate/create_trigger_test.rb")
|
|
20
23
|
trigger_definition = file("db/triggers/test_v01.sql")
|
|
21
24
|
|
|
22
|
-
run_generator
|
|
25
|
+
run_generator(
|
|
26
|
+
described_class,
|
|
27
|
+
["test", {"table_name" => "some_table"}],
|
|
28
|
+
{migration: false}
|
|
29
|
+
)
|
|
23
30
|
|
|
24
31
|
expect(trigger_definition).to exist
|
|
25
|
-
expect(
|
|
32
|
+
expect(migration).not_to exist
|
|
26
33
|
end
|
|
27
34
|
end
|
|
28
35
|
|
|
@@ -30,30 +37,46 @@ describe Fx::Generators::TriggerGenerator, :generator do
|
|
|
30
37
|
migration = file("db/migrate/create_trigger_test.rb")
|
|
31
38
|
trigger_definition = file("db/triggers/test_v01.sql")
|
|
32
39
|
|
|
33
|
-
run_generator
|
|
40
|
+
run_generator(
|
|
41
|
+
described_class,
|
|
42
|
+
["test", {"on" => "some_table"}]
|
|
43
|
+
)
|
|
34
44
|
|
|
35
45
|
expect(trigger_definition).to exist
|
|
36
|
-
|
|
37
|
-
expect(
|
|
38
|
-
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")
|
|
39
49
|
end
|
|
40
50
|
|
|
41
51
|
it "requires `table_name` or `on` to be specified" do
|
|
42
|
-
expect
|
|
43
|
-
run_generator
|
|
44
|
-
|
|
52
|
+
expect do
|
|
53
|
+
run_generator(
|
|
54
|
+
described_class,
|
|
55
|
+
["test", {"foo" => "some_table"}]
|
|
56
|
+
)
|
|
57
|
+
end.to raise_error(ArgumentError)
|
|
45
58
|
end
|
|
46
59
|
|
|
47
60
|
it "updates an existing trigger" do
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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")
|
|
51
68
|
|
|
52
|
-
|
|
69
|
+
run_generator(
|
|
70
|
+
described_class,
|
|
71
|
+
["test", {"table_name" => "some_table"}]
|
|
72
|
+
)
|
|
53
73
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
|
58
81
|
end
|
|
59
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
|
@@ -4,11 +4,18 @@ require "database_cleaner"
|
|
|
4
4
|
require File.expand_path("../dummy/config/environment", __FILE__)
|
|
5
5
|
Dir["spec/support/**/*.rb"].sort.each { |file| load file }
|
|
6
6
|
|
|
7
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
8
|
+
require "fx"
|
|
9
|
+
|
|
7
10
|
RSpec.configure do |config|
|
|
8
11
|
config.order = "random"
|
|
12
|
+
config.disable_monkey_patching!
|
|
13
|
+
|
|
9
14
|
DatabaseCleaner.strategy = :transaction
|
|
10
15
|
|
|
11
16
|
config.around(:each, db: true) do |example|
|
|
17
|
+
ActiveRecord::Base.connection.execute("SET search_path TO DEFAULT;")
|
|
18
|
+
|
|
12
19
|
DatabaseCleaner.start
|
|
13
20
|
example.run
|
|
14
21
|
DatabaseCleaner.clean
|
|
@@ -1,6 +1,6 @@
|
|
|
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,
|
|
@@ -10,11 +10,7 @@ module DefinitionHelpers
|
|
|
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,
|
|
@@ -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
|