fx 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +27 -0
- data/.yardopts +4 -0
- data/Appraisals +21 -0
- data/CONTRIBUTING.md +15 -0
- data/Gemfile +4 -0
- data/README.md +81 -0
- data/Rakefile +23 -0
- data/bin/appraisal +17 -0
- data/bin/console +14 -0
- data/bin/rake +17 -0
- data/bin/rspec +17 -0
- data/bin/setup +12 -0
- data/bin/yard +17 -0
- data/fx.gemspec +38 -0
- data/gemfiles/rails40.gemfile +8 -0
- data/gemfiles/rails40.gemfile.lock +111 -0
- data/gemfiles/rails41.gemfile +8 -0
- data/gemfiles/rails41.gemfile.lock +113 -0
- data/gemfiles/rails42.gemfile +8 -0
- data/gemfiles/rails42.gemfile.lock +130 -0
- data/gemfiles/rails50.gemfile +8 -0
- data/gemfiles/rails50.gemfile.lock +126 -0
- data/lib/fx.rb +21 -0
- data/lib/fx/adapters/postgres.rb +142 -0
- data/lib/fx/adapters/postgres/connection.rb +16 -0
- data/lib/fx/adapters/postgres/functions.rb +55 -0
- data/lib/fx/adapters/postgres/triggers.rb +56 -0
- data/lib/fx/command_recorder.rb +29 -0
- data/lib/fx/command_recorder/arguments.rb +43 -0
- data/lib/fx/command_recorder/function.rb +30 -0
- data/lib/fx/command_recorder/trigger.rb +30 -0
- data/lib/fx/configuration.rb +38 -0
- data/lib/fx/definition.rb +36 -0
- data/lib/fx/function.rb +24 -0
- data/lib/fx/schema_dumper.rb +15 -0
- data/lib/fx/schema_dumper/function.rb +29 -0
- data/lib/fx/schema_dumper/trigger.rb +29 -0
- data/lib/fx/statements.rb +16 -0
- data/lib/fx/statements/function.rb +105 -0
- data/lib/fx/statements/trigger.rb +133 -0
- data/lib/fx/trigger.rb +24 -0
- data/lib/fx/version.rb +4 -0
- data/lib/generators.rb +11 -0
- data/lib/generators/fx/function/USAGE +9 -0
- data/lib/generators/fx/function/function_generator.rb +98 -0
- data/lib/generators/fx/function/templates/db/migrate/create_function.erb +5 -0
- data/lib/generators/fx/function/templates/db/migrate/update_function.erb +5 -0
- data/lib/generators/fx/trigger/USAGE +18 -0
- data/lib/generators/fx/trigger/templates/db/migrate/create_trigger.erb +5 -0
- data/lib/generators/fx/trigger/templates/db/migrate/update_trigger.erb +5 -0
- data/lib/generators/fx/trigger/trigger_generator.rb +108 -0
- data/spec/acceptance/user_manages_functions_spec.rb +37 -0
- data/spec/acceptance/user_manages_triggers_spec.rb +51 -0
- data/spec/acceptance_helper.rb +61 -0
- data/spec/dummy/.gitignore +16 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +15 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +9 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/db/migrate/.keep +0 -0
- data/spec/features/functions/migrations_spec.rb +65 -0
- data/spec/features/functions/revert_spec.rb +75 -0
- data/spec/features/triggers/migrations_spec.rb +56 -0
- data/spec/features/triggers/revert_spec.rb +95 -0
- data/spec/fx/adapters/postgres_spec.rb +149 -0
- data/spec/fx/command_recorder/arguments_spec.rb +41 -0
- data/spec/fx/command_recorder_spec.rb +171 -0
- data/spec/fx/configuration_spec.rb +21 -0
- data/spec/fx/definition_spec.rb +111 -0
- data/spec/fx/schema_dumper/function_spec.rb +22 -0
- data/spec/fx/schema_dumper/trigger_spec.rb +40 -0
- data/spec/fx/statements/function_spec.rb +103 -0
- data/spec/fx/statements/trigger_spec.rb +132 -0
- data/spec/generators/fx/function/function_generator_spec.rb +34 -0
- data/spec/generators/fx/trigger/trigger_generator_spec.rb +47 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/definition_helpers.rb +37 -0
- data/spec/support/generator_setup.rb +11 -0
- data/spec/support/migration_helpers.rb +17 -0
- metadata +334 -0
@@ -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,111 @@
|
|
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
|
+
end
|
32
|
+
|
33
|
+
context "representing a trigger definition" do
|
34
|
+
it "returns the content of a trigger definition" do
|
35
|
+
sql_definition = <<~EOS
|
36
|
+
CREATE TRIGGER check_update
|
37
|
+
BEFORE UPDATE ON accounts
|
38
|
+
FOR EACH ROW
|
39
|
+
EXECUTE PROCEDURE check_account_update();
|
40
|
+
EOS
|
41
|
+
allow(File).to receive(:read).and_return(sql_definition)
|
42
|
+
|
43
|
+
definition = Fx::Definition.new(
|
44
|
+
name: "test",
|
45
|
+
version: 1,
|
46
|
+
type: "trigger",
|
47
|
+
)
|
48
|
+
|
49
|
+
expect(definition.to_sql).to eq sql_definition
|
50
|
+
end
|
51
|
+
|
52
|
+
it "raises an error if the file is empty" do
|
53
|
+
allow(File).to receive(:read).and_return("")
|
54
|
+
definition = Fx::Definition.new(
|
55
|
+
name: "test",
|
56
|
+
version: 1,
|
57
|
+
type: "trigger",
|
58
|
+
)
|
59
|
+
|
60
|
+
expect { definition.to_sql }.to raise_error(
|
61
|
+
RuntimeError,
|
62
|
+
%r(Define trigger in db/triggers/test_v01.sql before migrating),
|
63
|
+
)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#path" do
|
69
|
+
context "representing a function definition" do
|
70
|
+
it "returns a sql file with padded version and function name" do
|
71
|
+
definition = Fx::Definition.new(name: "test", version: 1)
|
72
|
+
|
73
|
+
expect(definition.path).to eq "db/functions/test_v01.sql"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "representing a trigger definition" do
|
78
|
+
it "returns a sql file with padded version and trigger name" do
|
79
|
+
definition = Fx::Definition.new(
|
80
|
+
name: "test",
|
81
|
+
version: 1,
|
82
|
+
type: "trigger",
|
83
|
+
)
|
84
|
+
|
85
|
+
expect(definition.path).to eq "db/triggers/test_v01.sql"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "#full_path" do
|
91
|
+
it "joins the path with Rails.root" do
|
92
|
+
definition = Fx::Definition.new(name: "test", version: 15)
|
93
|
+
|
94
|
+
expect(definition.full_path).to eq Rails.root.join(definition.path)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "#version" do
|
99
|
+
it "pads the version number with 0" do
|
100
|
+
definition = Fx::Definition.new(name: :_, version: 1)
|
101
|
+
|
102
|
+
expect(definition.version).to eq "01"
|
103
|
+
end
|
104
|
+
|
105
|
+
it "does not pad more than 2 characters" do
|
106
|
+
definition = Fx::Definition.new(name: :_, version: 15)
|
107
|
+
|
108
|
+
expect(definition.version).to eq "15"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,22 @@
|
|
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 test()
|
7
|
+
RETURNS text AS $$
|
8
|
+
BEGIN
|
9
|
+
RETURN 'test';
|
10
|
+
END;
|
11
|
+
$$ LANGUAGE plpgsql;
|
12
|
+
EOS
|
13
|
+
connection.create_function :test, sql_definition: sql_definition
|
14
|
+
stream = StringIO.new
|
15
|
+
|
16
|
+
ActiveRecord::SchemaDumper.dump(connection, stream)
|
17
|
+
|
18
|
+
output = stream.string
|
19
|
+
expect(output).to include "create_function :test, sql_definition: <<-SQL"
|
20
|
+
expect(output).to include "RETURN 'test';"
|
21
|
+
end
|
22
|
+
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
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "fx/statements/trigger"
|
3
|
+
|
4
|
+
describe Fx::Statements::Trigger, :db do
|
5
|
+
describe "#create_trigger" do
|
6
|
+
it "creates a trigger from a file" do
|
7
|
+
database = stubbed_database
|
8
|
+
definition = stubbed_definition
|
9
|
+
|
10
|
+
connection.create_trigger(:test)
|
11
|
+
|
12
|
+
expect(database).to have_received(:create_trigger).
|
13
|
+
with(definition.to_sql)
|
14
|
+
expect(Fx::Definition).to have_received(:new).
|
15
|
+
with(name: :test, version: 1, type: "trigger")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "allows creating a trigger with a specific version" do
|
19
|
+
database = stubbed_database
|
20
|
+
definition = stubbed_definition
|
21
|
+
|
22
|
+
connection.create_trigger(:test, version: 2)
|
23
|
+
|
24
|
+
expect(database).to have_received(:create_trigger).
|
25
|
+
with(definition.to_sql)
|
26
|
+
expect(Fx::Definition).to have_received(:new).
|
27
|
+
with(name: :test, version: 2, type: "trigger")
|
28
|
+
end
|
29
|
+
|
30
|
+
it "raises an error if both arguments are set" do
|
31
|
+
stubbed_database
|
32
|
+
|
33
|
+
expect {
|
34
|
+
connection.create_trigger(
|
35
|
+
:whatever,
|
36
|
+
version: 1,
|
37
|
+
sql_definition: "a definition",
|
38
|
+
)
|
39
|
+
}.to raise_error(
|
40
|
+
ArgumentError,
|
41
|
+
/cannot both be set/,
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#drop_trigger" do
|
47
|
+
it "drops the trigger" do
|
48
|
+
database = stubbed_database
|
49
|
+
|
50
|
+
connection.drop_trigger(:test, on: :users)
|
51
|
+
|
52
|
+
expect(database).to have_received(:drop_trigger).
|
53
|
+
with(:test, on: :users)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#update_trigger" do
|
58
|
+
it "updates the trigger" do
|
59
|
+
database = stubbed_database
|
60
|
+
definition = stubbed_definition
|
61
|
+
|
62
|
+
connection.update_trigger(:test, on: :users, version: 3)
|
63
|
+
|
64
|
+
expect(database).to have_received(:update_trigger).with(
|
65
|
+
:test,
|
66
|
+
on: :users,
|
67
|
+
sql_definition: definition.to_sql,
|
68
|
+
)
|
69
|
+
expect(Fx::Definition).to have_received(:new).with(
|
70
|
+
name: :test,
|
71
|
+
version: 3,
|
72
|
+
type: "trigger",
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "updates a trigger from a text definition" do
|
77
|
+
database = stubbed_database
|
78
|
+
|
79
|
+
connection.update_trigger(
|
80
|
+
:test,
|
81
|
+
on: :users,
|
82
|
+
sql_definition: "a definition",
|
83
|
+
)
|
84
|
+
|
85
|
+
expect(database).to have_received(:update_trigger).with(
|
86
|
+
:test,
|
87
|
+
on: :users,
|
88
|
+
sql_definition: "a definition",
|
89
|
+
)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "raises an error if not supplied a version" do
|
93
|
+
expect {
|
94
|
+
connection.update_trigger(
|
95
|
+
:whatever,
|
96
|
+
version: nil,
|
97
|
+
sql_definition: nil,
|
98
|
+
)
|
99
|
+
}.to raise_error(
|
100
|
+
ArgumentError,
|
101
|
+
/version or sql_definition must be specified/,
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "raises an error if both arguments are set" do
|
106
|
+
stubbed_database
|
107
|
+
|
108
|
+
expect {
|
109
|
+
connection.update_trigger(
|
110
|
+
:whatever,
|
111
|
+
version: 1,
|
112
|
+
sql_definition: "a definition",
|
113
|
+
)
|
114
|
+
}.to raise_error(
|
115
|
+
ArgumentError,
|
116
|
+
/cannot both be set/,
|
117
|
+
)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def stubbed_database
|
122
|
+
instance_spy("StubbedDatabase").tap do |stubbed_database|
|
123
|
+
allow(Fx).to receive(:database).and_return(stubbed_database)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def stubbed_definition
|
128
|
+
instance_double("Fx::Definition", to_sql: nil).tap do |stubbed_definition|
|
129
|
+
allow(Fx::Definition).to receive(:new).and_return(stubbed_definition)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "generators/fx/function/function_generator"
|
3
|
+
|
4
|
+
describe Fx::Generators::FunctionGenerator, :generator do
|
5
|
+
it "creates a function definition file, and a migration" do
|
6
|
+
migration = file("db/migrate/create_function_test.rb")
|
7
|
+
function_definition = file("db/functions/test_v01.sql")
|
8
|
+
|
9
|
+
run_generator ["test"]
|
10
|
+
|
11
|
+
expect(function_definition).to exist
|
12
|
+
expect(migration).to be_a_migration
|
13
|
+
expect(migration_file(migration)).to contain "CreateFunctionTest"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "updates an existing function" do
|
17
|
+
with_function_definition(
|
18
|
+
name: "test",
|
19
|
+
version: 1,
|
20
|
+
sql_definition: "hello",
|
21
|
+
) do
|
22
|
+
allow(Dir).to receive(:entries).and_return(["test_v01.sql"])
|
23
|
+
migration = file("db/migrate/update_function_test_to_version_2.rb")
|
24
|
+
function_definition = file("db/functions/test_v02.sql")
|
25
|
+
|
26
|
+
run_generator ["test"]
|
27
|
+
|
28
|
+
expect(function_definition).to exist
|
29
|
+
expect(migration).to be_a_migration
|
30
|
+
expect(migration_file(migration)).
|
31
|
+
to contain("UpdateFunctionTestToVersion2")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|