fx 0.2.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.hound.yml +2 -0
  4. data/.rubocop.yml +648 -0
  5. data/.travis.yml +18 -6
  6. data/Appraisals +21 -10
  7. data/LICENSE +18 -0
  8. data/README.md +45 -13
  9. data/bin/setup +1 -0
  10. data/gemfiles/rails42.gemfile +2 -1
  11. data/gemfiles/rails50.gemfile +1 -1
  12. data/gemfiles/rails51.gemfile +8 -0
  13. data/gemfiles/rails52.gemfile +8 -0
  14. data/gemfiles/rails60.gemfile +8 -0
  15. data/gemfiles/rails_edge.gemfile +8 -0
  16. data/lib/fx.rb +22 -0
  17. data/lib/fx/adapters/postgres.rb +26 -1
  18. data/lib/fx/adapters/postgres/functions.rb +5 -2
  19. data/lib/fx/adapters/postgres/triggers.rb +2 -2
  20. data/lib/fx/command_recorder.rb +0 -5
  21. data/lib/fx/configuration.rb +10 -0
  22. data/lib/fx/definition.rb +13 -3
  23. data/lib/fx/function.rb +2 -0
  24. data/lib/fx/railtie.rb +15 -0
  25. data/lib/fx/schema_dumper.rb +0 -5
  26. data/lib/fx/schema_dumper/function.rb +14 -5
  27. data/lib/fx/statements.rb +0 -5
  28. data/lib/fx/statements/function.rb +4 -2
  29. data/lib/fx/statements/trigger.rb +2 -0
  30. data/lib/fx/trigger.rb +2 -0
  31. data/lib/fx/version.rb +1 -1
  32. data/lib/generators/fx/function/USAGE +2 -0
  33. data/lib/generators/fx/function/function_generator.rb +14 -1
  34. data/lib/generators/fx/trigger/USAGE +2 -0
  35. data/lib/generators/fx/trigger/trigger_generator.rb +14 -1
  36. data/spec/acceptance/user_manages_functions_spec.rb +22 -2
  37. data/spec/acceptance/user_manages_triggers_spec.rb +6 -6
  38. data/spec/acceptance_helper.rb +2 -1
  39. data/spec/dummy/Rakefile +7 -0
  40. data/spec/features/functions/migrations_spec.rb +2 -2
  41. data/spec/features/functions/revert_spec.rb +2 -2
  42. data/spec/features/triggers/migrations_spec.rb +3 -3
  43. data/spec/features/triggers/revert_spec.rb +4 -4
  44. data/spec/fx/adapters/postgres/functions_spec.rb +37 -0
  45. data/spec/fx/adapters/postgres/triggers_spec.rb +45 -0
  46. data/spec/fx/adapters/postgres_spec.rb +46 -49
  47. data/spec/fx/definition_spec.rb +25 -2
  48. data/spec/fx/function_spec.rb +55 -0
  49. data/spec/fx/schema_dumper/function_spec.rb +58 -2
  50. data/spec/fx/schema_dumper/trigger_spec.rb +3 -3
  51. data/spec/fx/trigger_spec.rb +55 -0
  52. data/spec/generators/fx/function/function_generator_spec.rb +12 -0
  53. data/spec/generators/fx/trigger/trigger_generator_spec.rb +12 -0
  54. metadata +19 -11
  55. data/.ruby-version +0 -1
  56. data/gemfiles/rails40.gemfile +0 -8
  57. data/gemfiles/rails40.gemfile.lock +0 -111
  58. data/gemfiles/rails41.gemfile +0 -8
  59. data/gemfiles/rails41.gemfile.lock +0 -113
  60. data/gemfiles/rails42.gemfile.lock +0 -130
  61. data/gemfiles/rails50.gemfile.lock +0 -126
@@ -0,0 +1,45 @@
1
+ require "spec_helper"
2
+
3
+ module Fx
4
+ module Adapters
5
+ describe Postgres::Triggers, :db do
6
+ describe ".all" do
7
+ it "returns `Trigger` objects" do
8
+ connection = ActiveRecord::Base.connection
9
+ connection.execute <<-EOS.strip_heredoc
10
+ CREATE TABLE users (
11
+ id int PRIMARY KEY,
12
+ name varchar(256),
13
+ upper_name varchar(256)
14
+ );
15
+ EOS
16
+ connection.execute <<-EOS.strip_heredoc
17
+ CREATE OR REPLACE FUNCTION uppercase_users_name()
18
+ RETURNS trigger AS $$
19
+ BEGIN
20
+ NEW.upper_name = UPPER(NEW.name);
21
+ RETURN NEW;
22
+ END;
23
+ $$ LANGUAGE plpgsql;
24
+ EOS
25
+ connection.execute <<-EOS.strip_heredoc
26
+ CREATE TRIGGER uppercase_users_name
27
+ BEFORE INSERT ON users
28
+ FOR EACH ROW
29
+ EXECUTE PROCEDURE uppercase_users_name();
30
+ EOS
31
+
32
+ triggers = Postgres::Triggers.new(connection).all
33
+
34
+ first = triggers.first
35
+ expect(triggers.size).to eq 1
36
+ expect(first.name).to eq "uppercase_users_name"
37
+ expect(first.definition).to include("BEFORE INSERT")
38
+ expect(first.definition).to match(/ON [public\.users|users]/)
39
+ expect(first.definition).to include("FOR EACH ROW")
40
+ expect(first.definition).to include("EXECUTE PROCEDURE uppercase_users_name()")
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -6,7 +6,7 @@ module Fx::Adapters
6
6
  it "successfully creates a function" do
7
7
  adapter = Postgres.new
8
8
  adapter.create_function(
9
- <<~EOS
9
+ <<-EOS
10
10
  CREATE OR REPLACE FUNCTION test()
11
11
  RETURNS text AS $$
12
12
  BEGIN
@@ -22,7 +22,7 @@ module Fx::Adapters
22
22
 
23
23
  describe "#create_trigger" do
24
24
  it "successfully creates a trigger" do
25
- connection.execute <<~EOS
25
+ connection.execute <<-EOS
26
26
  CREATE TABLE users (
27
27
  id int PRIMARY KEY,
28
28
  name varchar(256),
@@ -30,7 +30,7 @@ module Fx::Adapters
30
30
  );
31
31
  EOS
32
32
  adapter = Postgres.new
33
- adapter.create_function <<~EOS
33
+ adapter.create_function <<-EOS
34
34
  CREATE OR REPLACE FUNCTION uppercase_users_name()
35
35
  RETURNS trigger AS $$
36
36
  BEGIN
@@ -40,7 +40,7 @@ module Fx::Adapters
40
40
  $$ LANGUAGE plpgsql;
41
41
  EOS
42
42
  adapter.create_trigger(
43
- <<~EOS
43
+ <<-EOS
44
44
  CREATE TRIGGER uppercase_users_name
45
45
  BEFORE INSERT ON users
46
46
  FOR EACH ROW
@@ -53,22 +53,44 @@ module Fx::Adapters
53
53
  end
54
54
 
55
55
  describe "#drop_function" do
56
- it "successfully drops a function" do
57
- adapter = Postgres.new
58
- adapter.create_function(
59
- <<~EOS
60
- CREATE OR REPLACE FUNCTION test()
61
- RETURNS text AS $$
62
- BEGIN
63
- RETURN 'test';
64
- END;
65
- $$ LANGUAGE plpgsql;
66
- EOS
67
- )
56
+ context "when the function has arguments" do
57
+ it "successfully drops a function with the entire function signature" do
58
+ adapter = Postgres.new
59
+ adapter.create_function(
60
+ <<-EOS
61
+ CREATE FUNCTION adder(x int, y int)
62
+ RETURNS int AS $$
63
+ BEGIN
64
+ RETURN $1 + $2;
65
+ END;
66
+ $$ LANGUAGE plpgsql;
67
+ EOS
68
+ )
69
+
70
+ adapter.drop_function(:adder)
68
71
 
69
- adapter.drop_function(:test)
72
+ expect(adapter.functions.map(&:name)).not_to include("adder")
73
+ end
74
+ end
75
+
76
+ context "when the function does not have arguments" do
77
+ it "successfully drops a function" do
78
+ adapter = Postgres.new
79
+ adapter.create_function(
80
+ <<-EOS
81
+ CREATE OR REPLACE FUNCTION test()
82
+ RETURNS text AS $$
83
+ BEGIN
84
+ RETURN 'test';
85
+ END;
86
+ $$ LANGUAGE plpgsql;
87
+ EOS
88
+ )
89
+
90
+ adapter.drop_function(:test)
70
91
 
71
- expect(adapter.functions.map(&:name)).not_to include("test")
92
+ expect(adapter.functions.map(&:name)).not_to include("test")
93
+ end
72
94
  end
73
95
  end
74
96
 
@@ -76,7 +98,7 @@ module Fx::Adapters
76
98
  it "finds functions and builds Fx::Function objects" do
77
99
  adapter = Postgres.new
78
100
  adapter.create_function(
79
- <<~EOS
101
+ <<-EOS
80
102
  CREATE OR REPLACE FUNCTION test()
81
103
  RETURNS text AS $$
82
104
  BEGIN
@@ -86,27 +108,13 @@ module Fx::Adapters
86
108
  EOS
87
109
  )
88
110
 
89
- expect(adapter.functions).to eq([
90
- Fx::Function.new(
91
- "name" => "test",
92
- "definition" => <<~EOS
93
- CREATE OR REPLACE FUNCTION public.test()
94
- RETURNS text
95
- LANGUAGE plpgsql
96
- AS $function$
97
- BEGIN
98
- RETURN 'test';
99
- END;
100
- $function$
101
- EOS
102
- ),
103
- ])
111
+ expect(adapter.functions.map(&:name)).to eq ["test"]
104
112
  end
105
113
  end
106
114
 
107
115
  describe "#triggers" do
108
116
  it "finds triggers and builds Fx::Trigger objects" do
109
- connection.execute <<~EOS
117
+ connection.execute <<-EOS
110
118
  CREATE TABLE users (
111
119
  id int PRIMARY KEY,
112
120
  name varchar(256),
@@ -114,7 +122,7 @@ module Fx::Adapters
114
122
  );
115
123
  EOS
116
124
  adapter = Postgres.new
117
- adapter.create_function <<~EOS
125
+ adapter.create_function <<-EOS
118
126
  CREATE OR REPLACE FUNCTION uppercase_users_name()
119
127
  RETURNS trigger AS $$
120
128
  BEGIN
@@ -123,7 +131,7 @@ module Fx::Adapters
123
131
  END;
124
132
  $$ LANGUAGE plpgsql;
125
133
  EOS
126
- sql_definition = <<~EOS
134
+ sql_definition = <<-EOS
127
135
  CREATE TRIGGER uppercase_users_name
128
136
  BEFORE INSERT ON users
129
137
  FOR EACH ROW
@@ -131,19 +139,8 @@ module Fx::Adapters
131
139
  EOS
132
140
  adapter.create_trigger(sql_definition)
133
141
 
134
- expect(adapter.triggers).to eq(
135
- [
136
- Fx::Trigger.new(
137
- "name" => "uppercase_users_name",
138
- "definition" => format_trigger(sql_definition),
139
- ),
140
- ],
141
- )
142
+ expect(adapter.triggers.map(&:name)).to eq ["uppercase_users_name"]
142
143
  end
143
144
  end
144
-
145
- def format_trigger(trigger)
146
- trigger.gsub("\n", "").gsub(" ", " ")
147
- end
148
145
  end
149
146
  end
@@ -4,7 +4,7 @@ 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 = <<-EOS
8
8
  CREATE OR REPLACE FUNCTION test()
9
9
  RETURNS text AS $$
10
10
  BEGIN
@@ -28,11 +28,34 @@ describe Fx::Definition do
28
28
  %r(Define function in db/functions/test_v01.sql before migrating),
29
29
  )
30
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 = Rails.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
+ Rails.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
31
54
  end
32
55
 
33
56
  context "representing a trigger definition" do
34
57
  it "returns the content of a trigger definition" do
35
- sql_definition = <<~EOS
58
+ sql_definition = <<-EOS
36
59
  CREATE TRIGGER check_update
37
60
  BEFORE UPDATE ON accounts
38
61
  FOR EACH ROW
@@ -0,0 +1,55 @@
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
+ end
54
+ end
55
+ end
@@ -2,15 +2,70 @@ require "spec_helper"
2
2
 
3
3
  describe Fx::SchemaDumper::Function, :db do
4
4
  it "dumps a create_function for a function in the database" do
5
- sql_definition = <<~EOS
6
- CREATE OR REPLACE FUNCTION test()
5
+ sql_definition = <<-EOS
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
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
+ 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
+ ActiveRecord::SchemaDumper.dump(connection, stream)
41
+
42
+ expect(output).to(
43
+ match(/function :my_function.*RETURN 'test';.*table "my_table"/m),
44
+ )
45
+ ensure
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
+
13
67
  connection.create_function :test, sql_definition: sql_definition
68
+ connection.execute aggregate_sql_definition
14
69
  stream = StringIO.new
15
70
 
16
71
  ActiveRecord::SchemaDumper.dump(connection, stream)
@@ -18,5 +73,6 @@ describe Fx::SchemaDumper::Function, :db do
18
73
  output = stream.string
19
74
  expect(output).to include "create_function :test, sql_definition: <<-SQL"
20
75
  expect(output).to include "RETURN 'test';"
76
+ expect(output).not_to include "aggregate_test"
21
77
  end
22
78
  end
@@ -2,14 +2,14 @@ require "spec_helper"
2
2
 
3
3
  describe Fx::SchemaDumper::Trigger, :db do
4
4
  it "dumps a create_trigger for a trigger in the database" do
5
- connection.execute <<~EOS
5
+ connection.execute <<-EOS
6
6
  CREATE TABLE users (
7
7
  id int PRIMARY KEY,
8
8
  name varchar(256),
9
9
  upper_name varchar(256)
10
10
  );
11
11
  EOS
12
- Fx.database.create_function <<~EOS
12
+ Fx.database.create_function <<-EOS
13
13
  CREATE OR REPLACE FUNCTION uppercase_users_name()
14
14
  RETURNS trigger AS $$
15
15
  BEGIN
@@ -18,7 +18,7 @@ describe Fx::SchemaDumper::Trigger, :db do
18
18
  END;
19
19
  $$ LANGUAGE plpgsql;
20
20
  EOS
21
- sql_definition = <<~EOS
21
+ sql_definition = <<-EOS
22
22
  CREATE TRIGGER uppercase_users_name
23
23
  BEFORE INSERT ON users
24
24
  FOR EACH ROW
@@ -0,0 +1,55 @@
1
+ require "spec_helper"
2
+ require "fx/trigger"
3
+
4
+ module Fx
5
+ describe Trigger do
6
+ describe "#<=>" do
7
+ it "delegates to `name`" do
8
+ trigger_a = Trigger.new(
9
+ "name" => "name_a",
10
+ "definition" => "some defintion",
11
+ )
12
+ trigger_b = Trigger.new(
13
+ "name" => "name_b",
14
+ "definition" => "some defintion",
15
+ )
16
+ trigger_c = Trigger.new(
17
+ "name" => "name_c",
18
+ "definition" => "some defintion",
19
+ )
20
+
21
+ expect(trigger_b).to be_between(trigger_a, trigger_c)
22
+ end
23
+ end
24
+
25
+ describe "#==" do
26
+ it "compares `name` and `definition`" do
27
+ trigger_a = Trigger.new(
28
+ "name" => "name_a",
29
+ "definition" => "some defintion",
30
+ )
31
+ trigger_b = Trigger.new(
32
+ "name" => "name_b",
33
+ "definition" => "some other defintion",
34
+ )
35
+
36
+ expect(trigger_a).not_to eq(trigger_b)
37
+ end
38
+ end
39
+
40
+ describe "#to_schema" do
41
+ it "returns a schema compatible version of the trigger" do
42
+ trigger = Trigger.new(
43
+ "name" => "uppercase_users_name",
44
+ "definition" => "CREATE TRIGGER uppercase_users_name ...",
45
+ )
46
+
47
+ expect(trigger.to_schema).to eq <<-EOS
48
+ create_trigger :uppercase_users_name, sql_definition: <<-\SQL
49
+ CREATE TRIGGER uppercase_users_name ...
50
+ SQL
51
+ EOS
52
+ end
53
+ end
54
+ end
55
+ end