fx-jets 0.6.3s

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 (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