fx-jets 0.6.3s

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