fx 0.2.0 → 0.6.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 (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