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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -2
  3. data/CHANGELOG.md +27 -1
  4. data/Gemfile +0 -1
  5. data/bin/rake +2 -3
  6. data/bin/rspec +13 -3
  7. data/bin/yard +13 -3
  8. data/fx.gemspec +3 -3
  9. data/lib/fx/adapters/postgres/connection.rb +12 -4
  10. data/lib/fx/adapters/postgres/functions.rb +11 -28
  11. data/lib/fx/adapters/postgres/query_executor.rb +34 -0
  12. data/lib/fx/adapters/postgres/triggers.rb +14 -29
  13. data/lib/fx/adapters/postgres.rb +10 -10
  14. data/lib/fx/configuration.rb +2 -2
  15. data/lib/fx/schema_dumper.rb +6 -14
  16. data/lib/fx/statements.rb +61 -58
  17. data/lib/fx/version.rb +1 -1
  18. data/lib/generators/fx/function/function_generator.rb +50 -53
  19. data/lib/generators/fx/function/templates/db/migrate/create_function.erb +1 -1
  20. data/lib/generators/fx/function/templates/db/migrate/update_function.erb +1 -1
  21. data/lib/generators/fx/migration_helper.rb +53 -0
  22. data/lib/generators/fx/name_helper.rb +33 -0
  23. data/lib/generators/fx/trigger/templates/db/migrate/create_trigger.erb +1 -1
  24. data/lib/generators/fx/trigger/templates/db/migrate/update_trigger.erb +1 -1
  25. data/lib/generators/fx/trigger/trigger_generator.rb +45 -64
  26. data/lib/generators/fx/version_helper.rb +55 -0
  27. data/spec/acceptance/user_manages_functions_spec.rb +6 -6
  28. data/spec/acceptance/user_manages_triggers_spec.rb +10 -10
  29. data/spec/acceptance_helper.rb +2 -2
  30. data/spec/features/functions/migrations_spec.rb +4 -4
  31. data/spec/features/functions/revert_spec.rb +4 -4
  32. data/spec/features/triggers/migrations_spec.rb +6 -6
  33. data/spec/features/triggers/revert_spec.rb +8 -8
  34. data/spec/fx/adapters/postgres/functions_spec.rb +12 -5
  35. data/spec/fx/adapters/postgres/query_executor_spec.rb +75 -0
  36. data/spec/fx/adapters/postgres/triggers_spec.rb +14 -7
  37. data/spec/fx/adapters/postgres_spec.rb +62 -20
  38. data/spec/fx/definition_spec.rb +6 -6
  39. data/spec/fx/schema_dumper_spec.rb +60 -14
  40. data/spec/fx_spec.rb +1 -1
  41. data/spec/generators/fx/function/function_generator_spec.rb +10 -10
  42. data/spec/generators/fx/migration_helper_spec.rb +133 -0
  43. data/spec/generators/fx/name_helper_spec.rb +114 -0
  44. data/spec/generators/fx/trigger/trigger_generator_spec.rb +42 -19
  45. data/spec/generators/fx/version_helper_spec.rb +157 -0
  46. data/spec/spec_helper.rb +2 -0
  47. data/spec/support/generator_setup.rb +46 -5
  48. 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
- <<~EOS
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
- EOS
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 <<~EOS
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
- EOS
30
+ SQL
31
31
  adapter = Fx::Adapters::Postgres.new
32
- adapter.create_function <<~EOS
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
- EOS
40
+ SQL
41
41
  adapter.create_trigger(
42
- <<~EOS
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
- EOS
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
- <<~EOS
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
- EOS
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
- <<~EOS
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
- EOS
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
- <<~EOS
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
- EOS
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 <<~EOS
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
- EOS
122
+ SQL
123
123
  adapter = Fx::Adapters::Postgres.new
124
- adapter.create_function <<~EOS
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
- EOS
133
- sql_definition = <<~EOS
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
- EOS
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
@@ -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 = <<-EOS
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
- EOS
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 = <<~EOS
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
- EOS
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 = <<~EOS
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
- EOS
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 = <<~EOS
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
- EOS
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 = <<~EOS
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
- EOS
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 = <<~EOS
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
- EOS
57
+ SQL
58
58
 
59
- aggregate_sql_definition = <<~EOS
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
- EOS
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 <<~EOS
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
- EOS
87
- Fx.database.create_function <<~EOS
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
- EOS
96
- sql_definition = <<~EOS
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
- EOS
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
- expect(migration).to be_a_migration
13
- expect(migration_file(migration)).to contain("CreateFunctionTest")
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", "--no-migration"]
21
+ run_generator(described_class, ["test"], {migration: false})
22
22
 
23
23
  expect(function_definition).to exist
24
- expect(Pathname.new(migration_file(migration))).not_to exist
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
- expect(migration).to be_a_migration
42
- expect(migration_file(migration))
43
- .to contain("UpdateFunctionTestToVersion2")
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