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,15 @@
1
+ require "rails/railtie"
2
+
3
+ module Fx
4
+ # Automatically initializes Fx in the context of a Rails application when
5
+ # ActiveRecord is loaded.
6
+ #
7
+ # @see Fx.load
8
+ class Railtie < Rails::Railtie
9
+ initializer "fx.load" do
10
+ ActiveSupport.on_load :active_record do
11
+ Fx.load
12
+ end
13
+ end
14
+ end
15
+ end
@@ -8,8 +8,3 @@ module Fx
8
8
  include Trigger
9
9
  end
10
10
  end
11
-
12
- ActiveRecord::SchemaDumper.send(
13
- :prepend,
14
- Fx::SchemaDumper,
15
- )
@@ -5,15 +5,24 @@ module Fx
5
5
  # @api private
6
6
  module Function
7
7
  def tables(stream)
8
+ if Fx.configuration.dump_functions_at_beginning_of_schema
9
+ functions(stream)
10
+ empty_line(stream)
11
+ end
12
+
8
13
  super
9
- functions(stream)
10
- end
11
14
 
12
- def functions(stream)
13
- if dumpable_functions_in_database.any?
14
- stream.puts
15
+ unless Fx.configuration.dump_functions_at_beginning_of_schema
16
+ functions(stream)
17
+ empty_line(stream)
15
18
  end
19
+ end
20
+
21
+ def empty_line(stream)
22
+ stream.puts if dumpable_functions_in_database.any?
23
+ end
16
24
 
25
+ def functions(stream)
17
26
  dumpable_functions_in_database.each do |function|
18
27
  stream.puts(function.to_schema)
19
28
  end
@@ -9,8 +9,3 @@ module Fx
9
9
  include Trigger
10
10
  end
11
11
  end
12
-
13
- ActiveRecord::ConnectionAdapters::AbstractAdapter.send(
14
- :include,
15
- Fx::Statements,
16
- )
@@ -36,6 +36,7 @@ module Fx
36
36
  "version or sql_definition must be specified",
37
37
  )
38
38
  end
39
+ sql_definition = sql_definition.strip_heredoc if sql_definition
39
40
  sql_definition ||= Fx::Definition.new(name: name, version: version).to_sql
40
41
 
41
42
  Fx.database.create_function(sql_definition)
@@ -49,8 +50,8 @@ module Fx
49
50
  # the `version` argument to {#create_function}.
50
51
  # @return The database response from executing the drop statement.
51
52
  #
52
- # @example Drop a function, rolling back to version 3 on rollback
53
- # drop_function(:uppercase_users_name, on: :users, revert_to_version: 3)
53
+ # @example Drop a function, rolling back to version 2 on rollback
54
+ # drop_function(:uppercase_users_name, revert_to_version: 2)
54
55
  #
55
56
  def drop_function(name, revert_to_version: nil)
56
57
  Fx.database.drop_function(name)
@@ -93,6 +94,7 @@ module Fx
93
94
  )
94
95
  end
95
96
 
97
+ sql_definition = sql_definition.strip_heredoc if sql_definition
96
98
  sql_definition ||= Fx::Definition.new(
97
99
  name: name,
98
100
  version: version,
@@ -39,6 +39,7 @@ module Fx
39
39
  version = 1
40
40
  end
41
41
 
42
+ sql_definition = sql_definition.strip_heredoc if sql_definition
42
43
  sql_definition ||= Fx::Definition.new(
43
44
  name: name,
44
45
  version: version,
@@ -116,6 +117,7 @@ module Fx
116
117
  raise ArgumentError, "on is required"
117
118
  end
118
119
 
120
+ sql_definition = sql_definition.strip_heredoc if sql_definition
119
121
  sql_definition ||= Fx::Definition.new(
120
122
  name: name,
121
123
  version: version,
@@ -1,6 +1,8 @@
1
1
  module Fx
2
2
  # @api private
3
3
  class Trigger
4
+ include Comparable
5
+
4
6
  attr_reader :name, :definition
5
7
  delegate :<=>, to: :name
6
8
 
@@ -1,4 +1,4 @@
1
1
  module Fx
2
2
  # @api private
3
- VERSION = "0.2.0"
3
+ VERSION = "0.6.0"
4
4
  end
@@ -2,6 +2,8 @@ Description:
2
2
  Create a new database function for your application. This will create a new
3
3
  function definition file and the accompanying migration.
4
4
 
5
+ When --no-migration is passed, skips generating a migration.
6
+
5
7
  Examples:
6
8
  rails generate fx:function test
7
9
 
@@ -8,6 +8,8 @@ module Fx
8
8
  include Rails::Generators::Migration
9
9
  source_root File.expand_path("../templates", __FILE__)
10
10
 
11
+ class_option :migration, type: :boolean
12
+
11
13
  def create_functions_directory
12
14
  unless function_definition_path.exist?
13
15
  empty_directory(function_definition_path)
@@ -23,6 +25,7 @@ module Fx
23
25
  end
24
26
 
25
27
  def create_migration_file
28
+ return if skip_migration_creation?
26
29
  if updating_existing_function?
27
30
  migration_template(
28
31
  "db/migrate/update_function.erb",
@@ -61,7 +64,7 @@ module Fx
61
64
 
62
65
  def activerecord_migration_class
63
66
  if ActiveRecord::Migration.respond_to?(:current_version)
64
- "ActiveRecord::Migration[5.0]"
67
+ "ActiveRecord::Migration[#{ActiveRecord::Migration.current_version}]"
65
68
  else
66
69
  "ActiveRecord::Migration"
67
70
  end
@@ -101,6 +104,16 @@ module Fx
101
104
  def previous_definition
102
105
  Fx::Definition.new(name: file_name, version: previous_version)
103
106
  end
107
+
108
+ # Skip creating migration file if:
109
+ # - migrations option is nil or false
110
+ def skip_migration_creation?
111
+ !migration
112
+ end
113
+
114
+ def migration
115
+ options[:migration]
116
+ end
104
117
  end
105
118
  end
106
119
  end
@@ -5,6 +5,8 @@ Description:
5
5
  If a trigger of the given name already exists, create a new version of the
6
6
  trigger and a migration to replace the old version with the new.
7
7
 
8
+ When --no-migration is passed, skips generating a migration.
9
+
8
10
  Examples:
9
11
 
10
12
  rails generate fx:trigger test
@@ -9,6 +9,8 @@ module Fx
9
9
  source_root File.expand_path("../templates", __FILE__)
10
10
  argument :table_name, type: :hash, required: true
11
11
 
12
+ class_option :migration, type: :boolean
13
+
12
14
  def create_triggers_directory
13
15
  unless trigger_definition_path.exist?
14
16
  empty_directory(trigger_definition_path)
@@ -20,6 +22,7 @@ module Fx
20
22
  end
21
23
 
22
24
  def create_migration_file
25
+ return if skip_migration_creation?
23
26
  if updating_existing_trigger?
24
27
  migration_template(
25
28
  "db/migrate/update_trigger.erb",
@@ -58,7 +61,7 @@ module Fx
58
61
 
59
62
  def activerecord_migration_class
60
63
  if ActiveRecord::Migration.respond_to?(:current_version)
61
- "ActiveRecord::Migration[5.0]"
64
+ "ActiveRecord::Migration[#{ActiveRecord::Migration.current_version}]"
62
65
  else
63
66
  "ActiveRecord::Migration"
64
67
  end
@@ -111,6 +114,16 @@ module Fx
111
114
  def trigger_definition_path
112
115
  @_trigger_definition_path ||= Rails.root.join(*["db", "triggers"])
113
116
  end
117
+
118
+ # Skip creating migration file if:
119
+ # - migrations option is nil or false
120
+ def skip_migration_creation?
121
+ !migration
122
+ end
123
+
124
+ def migration
125
+ options[:migration]
126
+ end
114
127
  end
115
128
  end
116
129
  end
@@ -3,7 +3,7 @@ require "acceptance_helper"
3
3
  describe "User manages functions" do
4
4
  it "handles simple functions" do
5
5
  successfully "rails generate fx:function test"
6
- write_function_definition "test_v01", <<~EOS
6
+ write_function_definition "test_v01", <<-EOS
7
7
  CREATE OR REPLACE FUNCTION test()
8
8
  RETURNS text AS $$
9
9
  BEGIN
@@ -21,7 +21,7 @@ describe "User manages functions" do
21
21
  "db/functions/test_v01.sql",
22
22
  "db/functions/test_v02.sql",
23
23
  )
24
- write_function_definition "test_v02", <<~EOS
24
+ write_function_definition "test_v02", <<-EOS
25
25
  CREATE OR REPLACE FUNCTION test()
26
26
  RETURNS text AS $$
27
27
  BEGIN
@@ -34,4 +34,24 @@ describe "User manages functions" do
34
34
  result = execute("SELECT * FROM test() AS result")
35
35
  expect(result).to eq("result" => "testest")
36
36
  end
37
+
38
+ it "handles functions with arguments" do
39
+ successfully "rails generate fx:function adder"
40
+ write_function_definition "adder_v01", <<-EOS
41
+ CREATE FUNCTION adder(x int, y int)
42
+ RETURNS int AS $$
43
+ BEGIN
44
+ RETURN $1 + $2;
45
+ END;
46
+ $$ LANGUAGE plpgsql;
47
+ EOS
48
+ successfully "rake db:migrate"
49
+
50
+ result = execute("SELECT * FROM adder(1, 2) AS result")
51
+ result["result"] = result["result"].to_i
52
+ expect(result).to eq("result" => 3)
53
+
54
+ successfully "rails destroy fx:function adder"
55
+ successfully "rake db:migrate"
56
+ end
37
57
  end
@@ -4,7 +4,7 @@ describe "User manages triggers" do
4
4
  it "handles simple triggers" do
5
5
  successfully "rails generate model user name:string upper_name:string"
6
6
  successfully "rails generate fx:function uppercase_users_name"
7
- write_function_definition "uppercase_users_name_v01", <<~EOS
7
+ write_function_definition "uppercase_users_name_v01", <<-EOS
8
8
  CREATE OR REPLACE FUNCTION uppercase_users_name()
9
9
  RETURNS trigger AS $$
10
10
  BEGIN
@@ -14,7 +14,7 @@ describe "User manages triggers" do
14
14
  $$ LANGUAGE plpgsql;
15
15
  EOS
16
16
  successfully "rails generate fx:trigger uppercase_users_name table_name:users"
17
- write_trigger_definition "uppercase_users_name_v01", <<~EOS
17
+ write_trigger_definition "uppercase_users_name_v01", <<-EOS
18
18
  CREATE TRIGGER uppercase_users_name
19
19
  BEFORE INSERT ON users
20
20
  FOR EACH ROW
@@ -22,24 +22,24 @@ describe "User manages triggers" do
22
22
  EOS
23
23
  successfully "rake db:migrate"
24
24
 
25
- execute <<~SQL
25
+ execute <<-EOS
26
26
  INSERT INTO users
27
27
  (name, created_at, updated_at)
28
28
  VALUES
29
29
  ('Bob', NOW(), NOW());
30
- SQL
30
+ EOS
31
31
  result = execute("SELECT upper_name FROM users WHERE name = 'Bob';")
32
32
  expect(result).to eq("upper_name" => "BOB")
33
33
 
34
34
  successfully "rails generate fx:trigger uppercase_users_name table_name:users"
35
- write_trigger_definition "uppercase_users_name_v02", <<~EOS
35
+ write_trigger_definition "uppercase_users_name_v02", <<-EOS
36
36
  CREATE TRIGGER uppercase_users_name
37
37
  BEFORE UPDATE ON users
38
38
  FOR EACH ROW
39
39
  EXECUTE PROCEDURE uppercase_users_name();
40
40
  EOS
41
41
  successfully "rake db:migrate"
42
- execute <<~EOS
42
+ execute <<-EOS
43
43
  UPDATE users
44
44
  SET name = 'Alice'
45
45
  WHERE id = 1;
@@ -23,7 +23,8 @@ RSpec.configure do |config|
23
23
  Dir.chdir("spec/dummy") do
24
24
  ActiveRecord::Base.connection.disconnect!
25
25
  system <<-CMD
26
- rake db:drop db:create &&
26
+ echo &&
27
+ rake db:environment:set db:drop db:create &&
27
28
  git add -A &&
28
29
  git reset --hard HEAD 1>/dev/null &&
29
30
  rm -rf .git/ 1>/dev/null
@@ -4,3 +4,10 @@
4
4
  require File.expand_path('../config/application', __FILE__)
5
5
 
6
6
  Rails.application.load_tasks
7
+
8
+ unless Rake::Task.task_defined?('db:environment:set')
9
+ desc 'dummy task for rails versions where this task does not exist'
10
+ task 'db:environment:set' do
11
+ # no-op
12
+ end
13
+ end
@@ -2,7 +2,7 @@ require "spec_helper"
2
2
 
3
3
  describe "Function migrations", :db do
4
4
  around do |example|
5
- sql_definition = <<~EOS
5
+ sql_definition = <<-EOS
6
6
  CREATE OR REPLACE FUNCTION test()
7
7
  RETURNS text AS $$
8
8
  BEGIN
@@ -40,7 +40,7 @@ describe "Function migrations", :db do
40
40
  it "can run migrations that updates functions" do
41
41
  connection.create_function(:test)
42
42
 
43
- sql_definition = <<~EOS
43
+ sql_definition = <<-EOS
44
44
  CREATE OR REPLACE FUNCTION test()
45
45
  RETURNS text AS $$
46
46
  BEGIN
@@ -2,7 +2,7 @@ require "spec_helper"
2
2
 
3
3
  describe "Reverting migrations", :db do
4
4
  around do |example|
5
- sql_definition = <<~EOS
5
+ sql_definition = <<-EOS
6
6
  CREATE OR REPLACE FUNCTION test()
7
7
  RETURNS text AS $$
8
8
  BEGIN
@@ -50,7 +50,7 @@ describe "Reverting migrations", :db do
50
50
  it "can run reversible migrations for updating functions" do
51
51
  connection.create_function(:test)
52
52
 
53
- sql_definition = <<~EOS
53
+ sql_definition = <<-EOS
54
54
  CREATE OR REPLACE FUNCTION test()
55
55
  RETURNS text AS $$
56
56
  BEGIN
@@ -2,14 +2,14 @@ require "spec_helper"
2
2
 
3
3
  describe "Trigger migrations", :db do
4
4
  around do |example|
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 "Trigger migrations", :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
@@ -2,14 +2,14 @@ require "spec_helper"
2
2
 
3
3
  describe "Reverting migrations", :db do
4
4
  around do |example|
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 "Reverting migrations", :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
@@ -67,7 +67,7 @@ describe "Reverting migrations", :db do
67
67
  it "can run reversible migrations for updating triggers" do
68
68
  connection.create_trigger(:uppercase_users_name)
69
69
 
70
- sql_definition = <<~EOS
70
+ sql_definition = <<-EOS
71
71
  CREATE TRIGGER uppercase_users_name
72
72
  BEFORE UPDATE ON users
73
73
  FOR EACH ROW
@@ -0,0 +1,37 @@
1
+ require "spec_helper"
2
+
3
+ module Fx
4
+ module Adapters
5
+ describe Postgres::Functions, :db do
6
+ describe ".all" do
7
+ it "returns `Function` objects" do
8
+ connection = ActiveRecord::Base.connection
9
+ connection.execute <<-EOS.strip_heredoc
10
+ CREATE OR REPLACE FUNCTION test()
11
+ RETURNS text AS $$
12
+ BEGIN
13
+ RETURN 'test';
14
+ END;
15
+ $$ LANGUAGE plpgsql;
16
+ EOS
17
+
18
+ functions = Postgres::Functions.new(connection).all
19
+
20
+ first = functions.first
21
+ expect(functions.size).to eq 1
22
+ expect(first.name).to eq "test"
23
+ expect(first.definition).to eq <<-EOS.strip_heredoc
24
+ CREATE OR REPLACE FUNCTION public.test()
25
+ RETURNS text
26
+ LANGUAGE plpgsql
27
+ AS $function$
28
+ BEGIN
29
+ RETURN 'test';
30
+ END;
31
+ $function$
32
+ EOS
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end