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