fx 0.7.0 → 0.9.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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +53 -0
  3. data/.gitignore +0 -1
  4. data/.rspec +1 -1
  5. data/.standard.yml +3 -0
  6. data/CHANGELOG.md +124 -0
  7. data/CONTRIBUTING.md +13 -4
  8. data/Gemfile +13 -2
  9. data/README.md +4 -2
  10. data/Rakefile +2 -1
  11. data/bin/setup +0 -4
  12. data/bin/standardrb +27 -0
  13. data/fx.gemspec +20 -27
  14. data/lib/fx/adapters/postgres/connection.rb +12 -0
  15. data/lib/fx/adapters/postgres/functions.rb +3 -3
  16. data/lib/fx/adapters/postgres/triggers.rb +3 -3
  17. data/lib/fx/adapters/postgres.rb +6 -14
  18. data/lib/fx/command_recorder.rb +87 -6
  19. data/lib/fx/configuration.rb +0 -25
  20. data/lib/fx/definition.rb +16 -6
  21. data/lib/fx/function.rb +7 -7
  22. data/lib/fx/schema_dumper.rb +45 -5
  23. data/lib/fx/statements.rb +228 -6
  24. data/lib/fx/trigger.rb +3 -3
  25. data/lib/fx/version.rb +1 -1
  26. data/lib/fx.rb +30 -12
  27. data/lib/generators/fx/function/function_generator.rb +8 -8
  28. data/lib/generators/fx/trigger/trigger_generator.rb +6 -10
  29. data/spec/acceptance/user_manages_functions_spec.rb +5 -5
  30. data/spec/acceptance/user_manages_triggers_spec.rb +8 -8
  31. data/spec/acceptance_helper.rb +6 -4
  32. data/spec/dummy/Rakefile +4 -4
  33. data/spec/dummy/bin/bundle +2 -2
  34. data/spec/dummy/bin/rails +3 -3
  35. data/spec/dummy/bin/rake +2 -2
  36. data/spec/dummy/config/application.rb +6 -0
  37. data/spec/dummy/config/database.yml +2 -0
  38. data/spec/dummy/config.ru +1 -1
  39. data/spec/features/functions/migrations_spec.rb +4 -4
  40. data/spec/features/functions/revert_spec.rb +7 -7
  41. data/spec/features/triggers/migrations_spec.rb +6 -6
  42. data/spec/features/triggers/revert_spec.rb +13 -13
  43. data/spec/fx/adapters/postgres/functions_spec.rb +26 -30
  44. data/spec/fx/adapters/postgres/triggers_spec.rb +34 -38
  45. data/spec/fx/adapters/postgres_spec.rb +107 -109
  46. data/spec/fx/command_recorder_spec.rb +41 -39
  47. data/spec/fx/configuration_spec.rb +20 -9
  48. data/spec/fx/definition_spec.rb +30 -38
  49. data/spec/fx/function_spec.rb +45 -48
  50. data/spec/fx/schema_dumper_spec.rb +123 -0
  51. data/spec/fx/statements_spec.rb +217 -0
  52. data/spec/fx/trigger_spec.rb +37 -40
  53. data/spec/fx_spec.rb +28 -0
  54. data/spec/generators/fx/function/function_generator_spec.rb +6 -6
  55. data/spec/generators/fx/trigger/trigger_generator_spec.rb +10 -10
  56. data/spec/spec_helper.rb +5 -0
  57. data/spec/support/definition_helpers.rb +5 -9
  58. data/spec/support/generator_setup.rb +1 -1
  59. data/spec/support/migration_helpers.rb +1 -1
  60. data/spec/support/warning_helper.rb +5 -0
  61. metadata +24 -213
  62. data/.hound.yml +0 -2
  63. data/.rubocop.yml +0 -648
  64. data/.travis.yml +0 -60
  65. data/Appraisals +0 -45
  66. data/bin/appraisal +0 -17
  67. data/gemfiles/rails42.gemfile +0 -10
  68. data/gemfiles/rails50.gemfile +0 -8
  69. data/gemfiles/rails51.gemfile +0 -8
  70. data/gemfiles/rails52.gemfile +0 -8
  71. data/gemfiles/rails60.gemfile +0 -8
  72. data/gemfiles/rails61.gemfile +0 -8
  73. data/gemfiles/rails_edge.gemfile +0 -8
  74. data/lib/fx/command_recorder/arguments.rb +0 -43
  75. data/lib/fx/command_recorder/function.rb +0 -30
  76. data/lib/fx/command_recorder/trigger.rb +0 -30
  77. data/lib/fx/schema_dumper/function.rb +0 -38
  78. data/lib/fx/schema_dumper/trigger.rb +0 -29
  79. data/lib/fx/statements/function.rb +0 -115
  80. data/lib/fx/statements/trigger.rb +0 -146
  81. data/spec/fx/command_recorder/arguments_spec.rb +0 -41
  82. data/spec/fx/schema_dumper/function_spec.rb +0 -80
  83. data/spec/fx/schema_dumper/trigger_spec.rb +0 -40
  84. data/spec/fx/statements/function_spec.rb +0 -103
  85. data/spec/fx/statements/trigger_spec.rb +0 -132
@@ -1,13 +1,13 @@
1
1
  require "spec_helper"
2
2
 
3
- describe Fx::CommandRecorder, :db do
3
+ RSpec.describe Fx::CommandRecorder, :db do
4
4
  describe "#create_function" do
5
5
  it "records the created function" do
6
6
  recorder = ActiveRecord::Migration::CommandRecorder.new
7
7
 
8
8
  recorder.create_function :test
9
9
 
10
- expect(recorder.commands).to eq [[:create_function, [:test], nil]]
10
+ expect(recorder.commands).to eq([[:create_function, [:test], nil]])
11
11
  end
12
12
 
13
13
  it "reverts to drop_function" do
@@ -15,7 +15,7 @@ describe Fx::CommandRecorder, :db do
15
15
 
16
16
  recorder.create_function :test
17
17
 
18
- expect(recorder.commands).to eq [[:create_function, [:test], nil]]
18
+ expect(recorder.commands).to eq([[:create_function, [:test], nil]])
19
19
  end
20
20
 
21
21
  it "reverts to drop_function" do
@@ -23,7 +23,7 @@ describe Fx::CommandRecorder, :db do
23
23
 
24
24
  recorder.revert { recorder.create_function :test }
25
25
 
26
- expect(recorder.commands).to eq [[:drop_function, [:test]]]
26
+ expect(recorder.commands).to eq([[:drop_function, [:test]]])
27
27
  end
28
28
  end
29
29
 
@@ -33,54 +33,56 @@ describe Fx::CommandRecorder, :db do
33
33
 
34
34
  recorder.drop_function :test
35
35
 
36
- expect(recorder.commands).to eq [[:drop_function, [:test], nil]]
36
+ expect(recorder.commands).to eq([[:drop_function, [:test], nil]])
37
37
  end
38
38
 
39
39
  it "reverts to create_function with specified revert_to_version" do
40
40
  recorder = ActiveRecord::Migration::CommandRecorder.new
41
- args = [:test, { revert_to_version: 3 }]
42
- revert_args = [:test, { version: 3 }]
41
+ args = [:test, {revert_to_version: 3}]
42
+ revert_args = [:test, {version: 3}]
43
43
 
44
44
  recorder.revert { recorder.drop_function(*args) }
45
45
 
46
- expect(recorder.commands).to eq [[:create_function, revert_args]]
46
+ expect(recorder.commands).to eq([[:create_function, revert_args]])
47
47
  end
48
48
 
49
49
  it "raises when reverting without revert_to_version set" do
50
50
  recorder = ActiveRecord::Migration::CommandRecorder.new
51
- args = [:test, { another_argument: 1 }]
51
+ args = [:test, {another_argument: 1}]
52
52
 
53
- expect { recorder.revert { recorder.drop_function(*args) } }.
54
- to raise_error(ActiveRecord::IrreversibleMigration)
53
+ expect do
54
+ recorder.revert { recorder.drop_function(*args) }
55
+ end.to raise_error(ActiveRecord::IrreversibleMigration)
55
56
  end
56
57
  end
57
58
 
58
59
  describe "#update_function" do
59
60
  it "records the updated function" do
60
61
  recorder = ActiveRecord::Migration::CommandRecorder.new
61
- args = [:test, { version: 2 }]
62
+ args = [:test, {version: 2}]
62
63
 
63
64
  recorder.update_function(*args)
64
65
 
65
- expect(recorder.commands).to eq [[:update_function, args, nil]]
66
+ expect(recorder.commands).to eq([[:update_function, args, nil]])
66
67
  end
67
68
 
68
69
  it "reverts to update_function with the specified revert_to_version" do
69
70
  recorder = ActiveRecord::Migration::CommandRecorder.new
70
- args = [:test, { version: 2, revert_to_version: 1 }]
71
- revert_args = [:test, { version: 1 }]
71
+ args = [:test, {version: 2, revert_to_version: 1}]
72
+ revert_args = [:test, {version: 1}]
72
73
 
73
74
  recorder.revert { recorder.update_function(*args) }
74
75
 
75
- expect(recorder.commands).to eq [[:update_function, revert_args]]
76
+ expect(recorder.commands).to eq([[:update_function, revert_args]])
76
77
  end
77
78
 
78
79
  it "raises when reverting without revert_to_version set" do
79
80
  recorder = ActiveRecord::Migration::CommandRecorder.new
80
- args = [:test, { version: 42, another_argument: 1 }]
81
+ args = [:test, {version: 42, another_argument: 1}]
81
82
 
82
- expect { recorder.revert { recorder.update_function(*args) } }.
83
- to raise_error(ActiveRecord::IrreversibleMigration)
83
+ expect do
84
+ recorder.revert { recorder.update_function(*args) }
85
+ end.to raise_error(ActiveRecord::IrreversibleMigration)
84
86
  end
85
87
  end
86
88
 
@@ -90,7 +92,7 @@ describe Fx::CommandRecorder, :db do
90
92
 
91
93
  recorder.create_trigger :greetings
92
94
 
93
- expect(recorder.commands).to eq [[:create_trigger, [:greetings], nil]]
95
+ expect(recorder.commands).to eq([[:create_trigger, [:greetings], nil]])
94
96
  end
95
97
 
96
98
  it "reverts to drop_trigger" do
@@ -98,9 +100,7 @@ describe Fx::CommandRecorder, :db do
98
100
 
99
101
  recorder.create_trigger :greetings
100
102
 
101
- expect(recorder.commands).to eq [
102
- [:create_trigger, [:greetings], nil],
103
- ]
103
+ expect(recorder.commands).to eq([[:create_trigger, [:greetings], nil]])
104
104
  end
105
105
 
106
106
  it "reverts to drop_trigger" do
@@ -108,7 +108,7 @@ describe Fx::CommandRecorder, :db do
108
108
 
109
109
  recorder.revert { recorder.create_trigger :greetings }
110
110
 
111
- expect(recorder.commands).to eq [[:drop_trigger, [:greetings]]]
111
+ expect(recorder.commands).to eq([[:drop_trigger, [:greetings]]])
112
112
  end
113
113
  end
114
114
 
@@ -118,54 +118,56 @@ describe Fx::CommandRecorder, :db do
118
118
 
119
119
  recorder.drop_trigger :users
120
120
 
121
- expect(recorder.commands).to eq [[:drop_trigger, [:users], nil]]
121
+ expect(recorder.commands).to eq([[:drop_trigger, [:users], nil]])
122
122
  end
123
123
 
124
124
  it "reverts to create_trigger with specified revert_to_version" do
125
125
  recorder = ActiveRecord::Migration::CommandRecorder.new
126
- args = [:users, { revert_to_version: 3 }]
127
- revert_args = [:users, { version: 3 }]
126
+ args = [:users, {revert_to_version: 3}]
127
+ revert_args = [:users, {version: 3}]
128
128
 
129
129
  recorder.revert { recorder.drop_trigger(*args) }
130
130
 
131
- expect(recorder.commands).to eq [[:create_trigger, revert_args]]
131
+ expect(recorder.commands).to eq([[:create_trigger, revert_args]])
132
132
  end
133
133
 
134
134
  it "raises when reverting without revert_to_version set" do
135
135
  recorder = ActiveRecord::Migration::CommandRecorder.new
136
- args = [:users, { another_argument: 1 }]
136
+ args = [:users, {another_argument: 1}]
137
137
 
138
- expect { recorder.revert { recorder.drop_trigger(*args) } }.
139
- to raise_error(ActiveRecord::IrreversibleMigration)
138
+ expect do
139
+ recorder.revert { recorder.drop_trigger(*args) }
140
+ end.to raise_error(ActiveRecord::IrreversibleMigration)
140
141
  end
141
142
  end
142
143
 
143
144
  describe "#update_trigger" do
144
145
  it "records the updated trigger" do
145
146
  recorder = ActiveRecord::Migration::CommandRecorder.new
146
- args = [:users, { version: 2 }]
147
+ args = [:users, {version: 2}]
147
148
 
148
149
  recorder.update_trigger(*args)
149
150
 
150
- expect(recorder.commands).to eq [[:update_trigger, args, nil]]
151
+ expect(recorder.commands).to eq([[:update_trigger, args, nil]])
151
152
  end
152
153
 
153
154
  it "reverts to update_trigger with the specified revert_to_version" do
154
155
  recorder = ActiveRecord::Migration::CommandRecorder.new
155
- args = [:users, { version: 2, revert_to_version: 1 }]
156
- revert_args = [:users, { version: 1 }]
156
+ args = [:users, {version: 2, revert_to_version: 1}]
157
+ revert_args = [:users, {version: 1}]
157
158
 
158
159
  recorder.revert { recorder.update_trigger(*args) }
159
160
 
160
- expect(recorder.commands).to eq [[:update_trigger, revert_args]]
161
+ expect(recorder.commands).to eq([[:update_trigger, revert_args]])
161
162
  end
162
163
 
163
164
  it "raises when reverting without revert_to_version set" do
164
165
  recorder = ActiveRecord::Migration::CommandRecorder.new
165
- args = [:users, { version: 42, another_argument: 1 }]
166
+ args = [:users, {version: 42, another_argument: 1}]
166
167
 
167
- expect { recorder.revert { recorder.update_trigger(*args) } }.
168
- to raise_error(ActiveRecord::IrreversibleMigration)
168
+ expect do
169
+ recorder.revert { recorder.update_trigger(*args) }
170
+ end.to raise_error(ActiveRecord::IrreversibleMigration)
169
171
  end
170
172
  end
171
173
  end
@@ -1,21 +1,32 @@
1
1
  require "spec_helper"
2
2
 
3
- describe Fx::Configuration do
3
+ RSpec.describe Fx::Configuration do
4
4
  it "defaults the database adapter to postgres" do
5
- expect(Fx.configuration.database).to be_a Fx::Adapters::Postgres
6
- expect(Fx.database).to be_a Fx::Adapters::Postgres
5
+ configuration = Fx::Configuration.new
6
+
7
+ expect(configuration.database).to be_a(Fx::Adapters::Postgres)
8
+ end
9
+
10
+ it "defaults `dump_functions_at_beginning_of_schema` to false" do
11
+ configuration = Fx::Configuration.new
12
+
13
+ expect(configuration.dump_functions_at_beginning_of_schema).to eq(false)
7
14
  end
8
15
 
9
16
  it "allows the database adapter to be set" do
17
+ configuration = Fx::Configuration.new
10
18
  adapter = double("Fx Adapter")
11
19
 
12
- Fx.configure do |config|
13
- config.database = adapter
14
- end
20
+ configuration.database = adapter
21
+
22
+ expect(configuration.database).to eq(adapter)
23
+ end
24
+
25
+ it "allows `dump_functions_at_beginning_of_schema` to be set" do
26
+ configuration = Fx::Configuration.new
15
27
 
16
- expect(Fx.configuration.database).to eq adapter
17
- expect(Fx.database).to eq adapter
28
+ configuration.dump_functions_at_beginning_of_schema = true
18
29
 
19
- Fx.configuration = Fx::Configuration.new
30
+ expect(configuration.dump_functions_at_beginning_of_schema).to eq(true)
20
31
  end
21
32
  end
@@ -1,6 +1,6 @@
1
1
  require "spec_helper"
2
2
 
3
- describe Fx::Definition do
3
+ 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
@@ -14,24 +14,26 @@ describe Fx::Definition do
14
14
  EOS
15
15
  allow(File).to receive(:read).and_return(sql_definition)
16
16
 
17
- definition = Fx::Definition.new(name: "test", version: 1)
17
+ definition = Fx::Definition.function(name: "test", version: 1)
18
18
 
19
- expect(definition.to_sql).to eq sql_definition
19
+ expect(definition.to_sql).to eq(sql_definition)
20
20
  end
21
21
 
22
22
  it "raises an error if the file is empty" do
23
23
  allow(File).to receive(:read).and_return("")
24
- definition = Fx::Definition.new(name: "test", version: 1)
24
+ definition = Fx::Definition.function(name: "test", version: 1)
25
25
 
26
- expect { definition.to_sql }.to raise_error(
26
+ expect do
27
+ definition.to_sql
28
+ end.to raise_error(
27
29
  RuntimeError,
28
- %r(Define function in db/functions/test_v01.sql before migrating),
30
+ %r{Define function in db/functions/test_v01.sql before migrating}
29
31
  )
30
32
  end
31
33
 
32
34
  context "when definition is at Rails engine" do
33
35
  it "returns the content of a function definition" do
34
- sql_definition = <<-EOS
36
+ sql_definition = <<~EOS
35
37
  CREATE OR REPLACE FUNCTION test()
36
38
  RETURNS text AS $$
37
39
  BEGIN
@@ -44,9 +46,9 @@ describe Fx::Definition do
44
46
  File.write(engine_path.join("db", "functions", "custom_test_v01.sql"), sql_definition)
45
47
  Rails.application.config.paths["db/migrate"].push(engine_path.join("db", "migrate"))
46
48
 
47
- definition = Fx::Definition.new(name: "custom_test", version: 1)
49
+ definition = Fx::Definition.function(name: "custom_test", version: 1)
48
50
 
49
- expect(definition.to_sql).to eq sql_definition
51
+ expect(definition.to_sql).to eq(sql_definition)
50
52
 
51
53
  FileUtils.rm_rf(engine_path)
52
54
  end
@@ -55,34 +57,28 @@ describe Fx::Definition do
55
57
 
56
58
  context "representing a trigger definition" do
57
59
  it "returns the content of a trigger definition" do
58
- sql_definition = <<-EOS
60
+ sql_definition = <<~EOS
59
61
  CREATE TRIGGER check_update
60
62
  BEFORE UPDATE ON accounts
61
63
  FOR EACH ROW
62
- EXECUTE PROCEDURE check_account_update();
64
+ EXECUTE FUNCTION check_account_update();
63
65
  EOS
64
66
  allow(File).to receive(:read).and_return(sql_definition)
65
67
 
66
- definition = Fx::Definition.new(
67
- name: "test",
68
- version: 1,
69
- type: "trigger",
70
- )
68
+ definition = Fx::Definition.trigger(name: "test", version: 1)
71
69
 
72
- expect(definition.to_sql).to eq sql_definition
70
+ expect(definition.to_sql).to eq(sql_definition)
73
71
  end
74
72
 
75
73
  it "raises an error if the file is empty" do
76
74
  allow(File).to receive(:read).and_return("")
77
- definition = Fx::Definition.new(
78
- name: "test",
79
- version: 1,
80
- type: "trigger",
81
- )
75
+ definition = Fx::Definition.trigger(name: "test", version: 1)
82
76
 
83
- expect { definition.to_sql }.to raise_error(
77
+ expect do
78
+ definition.to_sql
79
+ end.to raise_error(
84
80
  RuntimeError,
85
- %r(Define trigger in db/triggers/test_v01.sql before migrating),
81
+ %r{Define trigger in db/triggers/test_v01.sql before migrating}
86
82
  )
87
83
  end
88
84
  end
@@ -91,44 +87,40 @@ describe Fx::Definition do
91
87
  describe "#path" do
92
88
  context "representing a function definition" do
93
89
  it "returns a sql file with padded version and function name" do
94
- definition = Fx::Definition.new(name: "test", version: 1)
90
+ definition = Fx::Definition.function(name: "test", version: 1)
95
91
 
96
- expect(definition.path).to eq "db/functions/test_v01.sql"
92
+ expect(definition.path).to eq("db/functions/test_v01.sql")
97
93
  end
98
94
  end
99
95
 
100
96
  context "representing a trigger definition" do
101
97
  it "returns a sql file with padded version and trigger name" do
102
- definition = Fx::Definition.new(
103
- name: "test",
104
- version: 1,
105
- type: "trigger",
106
- )
98
+ definition = Fx::Definition.trigger(name: "test", version: 1)
107
99
 
108
- expect(definition.path).to eq "db/triggers/test_v01.sql"
100
+ expect(definition.path).to eq("db/triggers/test_v01.sql")
109
101
  end
110
102
  end
111
103
  end
112
104
 
113
105
  describe "#full_path" do
114
106
  it "joins the path with Rails.root" do
115
- definition = Fx::Definition.new(name: "test", version: 15)
107
+ definition = Fx::Definition.function(name: "test", version: 15)
116
108
 
117
- expect(definition.full_path).to eq Rails.root.join(definition.path)
109
+ expect(definition.full_path).to eq(Rails.root.join(definition.path))
118
110
  end
119
111
  end
120
112
 
121
113
  describe "#version" do
122
114
  it "pads the version number with 0" do
123
- definition = Fx::Definition.new(name: :_, version: 1)
115
+ definition = Fx::Definition.function(name: :_, version: 1)
124
116
 
125
- expect(definition.version).to eq "01"
117
+ expect(definition.version).to eq("01")
126
118
  end
127
119
 
128
120
  it "does not pad more than 2 characters" do
129
- definition = Fx::Definition.new(name: :_, version: 15)
121
+ definition = Fx::Definition.function(name: :_, version: 15)
130
122
 
131
- expect(definition.version).to eq "15"
123
+ expect(definition.version).to eq("15")
132
124
  end
133
125
  end
134
126
  end
@@ -1,68 +1,65 @@
1
1
  require "spec_helper"
2
- require "fx/function"
3
2
 
4
- module Fx
5
- describe Function do
6
- describe "#<=>" do
7
- it "delegates to `name`" do
8
- function_a = Function.new(
9
- "name" => "name_a",
10
- "definition" => "some definition",
11
- )
12
- function_b = Function.new(
13
- "name" => "name_b",
14
- "definition" => "some definition",
15
- )
16
- function_c = Function.new(
17
- "name" => "name_c",
18
- "definition" => "some definition",
19
- )
3
+ RSpec.describe Fx::Function do
4
+ describe "#<=>" do
5
+ it "delegates to `name`" do
6
+ function_a = Fx::Function.new(
7
+ "name" => "name_a",
8
+ "definition" => "some definition"
9
+ )
10
+ function_b = Fx::Function.new(
11
+ "name" => "name_b",
12
+ "definition" => "some definition"
13
+ )
14
+ function_c = Fx::Function.new(
15
+ "name" => "name_c",
16
+ "definition" => "some definition"
17
+ )
20
18
 
21
- expect(function_b).to be_between(function_a, function_c)
22
- end
19
+ expect(function_b).to be_between(function_a, function_c)
23
20
  end
21
+ end
24
22
 
25
- describe "#==" do
26
- it "compares `name` and `definition`" do
27
- function_a = Function.new(
28
- "name" => "name_a",
29
- "definition" => "some definition",
30
- )
31
- function_b = Function.new(
32
- "name" => "name_b",
33
- "definition" => "some other definition",
34
- )
23
+ describe "#==" do
24
+ it "compares `name` and `definition`" do
25
+ function_a = Fx::Function.new(
26
+ "name" => "name_a",
27
+ "definition" => "some definition"
28
+ )
29
+ function_b = Fx::Function.new(
30
+ "name" => "name_b",
31
+ "definition" => "some other definition"
32
+ )
35
33
 
36
- expect(function_a).not_to eq(function_b)
37
- end
34
+ expect(function_a).not_to eq(function_b)
38
35
  end
36
+ end
39
37
 
40
- describe "#to_schema" do
41
- it "returns a schema compatible version of the function" do
42
- function = Function.new(
43
- "name" => "uppercase_users_name",
44
- "definition" => "CREATE OR REPLACE TRIGGER uppercase_users_name ...",
45
- )
38
+ describe "#to_schema" do
39
+ it "returns a schema compatible version of the function" do
40
+ function = Fx::Function.new(
41
+ "name" => "uppercase_users_name",
42
+ "definition" => "CREATE OR REPLACE TRIGGER uppercase_users_name ..."
43
+ )
46
44
 
47
- expect(function.to_schema).to eq <<-'EOS'
45
+ expect(function.to_schema).to eq(<<-EOS)
48
46
  create_function :uppercase_users_name, sql_definition: <<-'SQL'
49
47
  CREATE OR REPLACE TRIGGER uppercase_users_name ...
50
48
  SQL
51
- EOS
52
- end
49
+ EOS
50
+ end
53
51
 
54
- it "maintains backslashes" do
55
- function = Function.new(
56
- "name" => "regex",
57
- "definition" => "CREATE OR REPLACE FUNCTION regex \\1",
58
- )
52
+ it "maintains backslashes" do
53
+ function = Fx::Function.new(
54
+ "name" => "regex",
55
+ "definition" => "CREATE OR REPLACE FUNCTION regex \\1"
56
+ )
59
57
 
60
- expect(function.to_schema).to eq <<-'EOS'
58
+ expect(function.to_schema).to eq(<<-'EOS')
61
59
  create_function :regex, sql_definition: <<-'SQL'
62
60
  CREATE OR REPLACE FUNCTION regex \1
63
61
  SQL
64
- EOS
65
- end
62
+ EOS
66
63
  end
67
64
  end
68
65
  end
@@ -0,0 +1,123 @@
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 = <<~EOS
6
+ CREATE OR REPLACE FUNCTION my_function()
7
+ RETURNS text AS $$
8
+ BEGIN
9
+ RETURN 'test';
10
+ END;
11
+ $$ LANGUAGE plpgsql;
12
+ EOS
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 = <<~EOS
28
+ CREATE OR REPLACE FUNCTION my_function()
29
+ RETURNS text AS $$
30
+ BEGIN
31
+ RETURN 'test';
32
+ END;
33
+ $$ LANGUAGE plpgsql;
34
+ EOS
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 = <<~EOS
51
+ CREATE OR REPLACE FUNCTION test(text, text)
52
+ RETURNS text AS $$
53
+ BEGIN
54
+ RETURN 'test';
55
+ END;
56
+ $$ LANGUAGE plpgsql;
57
+ EOS
58
+
59
+ aggregate_sql_definition = <<~EOS
60
+ CREATE AGGREGATE aggregate_test(text)
61
+ (
62
+ sfunc = test,
63
+ stype = text
64
+ );
65
+ EOS
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 <<~EOS
81
+ CREATE TABLE users (
82
+ id int PRIMARY KEY,
83
+ name varchar(256),
84
+ upper_name varchar(256)
85
+ );
86
+ EOS
87
+ Fx.database.create_function <<~EOS
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
+ EOS
96
+ sql_definition = <<~EOS
97
+ CREATE TRIGGER uppercase_users_name
98
+ BEFORE INSERT ON users
99
+ FOR EACH ROW
100
+ EXECUTE FUNCTION uppercase_users_name();
101
+ EOS
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
+ def dump(connection:, stream:)
117
+ if Rails.version >= "7.2"
118
+ ActiveRecord::SchemaDumper.dump(connection.pool, stream)
119
+ else
120
+ ActiveRecord::SchemaDumper.dump(connection, stream)
121
+ end
122
+ end
123
+ end