fx-jets 0.6.3s

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 (95) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.hound.yml +2 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +648 -0
  6. data/.travis.yml +60 -0
  7. data/.yardopts +4 -0
  8. data/Appraisals +45 -0
  9. data/CONTRIBUTING.md +15 -0
  10. data/Gemfile +4 -0
  11. data/LICENSE +18 -0
  12. data/README.md +1 -0
  13. data/Rakefile +23 -0
  14. data/bin/appraisal +17 -0
  15. data/bin/console +14 -0
  16. data/bin/rake +17 -0
  17. data/bin/rspec +17 -0
  18. data/bin/setup +13 -0
  19. data/bin/yard +17 -0
  20. data/fx.gemspec +39 -0
  21. data/gemfiles/rails42.gemfile +10 -0
  22. data/gemfiles/rails50.gemfile +8 -0
  23. data/gemfiles/rails51.gemfile +8 -0
  24. data/gemfiles/rails52.gemfile +8 -0
  25. data/gemfiles/rails60.gemfile +8 -0
  26. data/gemfiles/rails61.gemfile +8 -0
  27. data/gemfiles/rails_edge.gemfile +8 -0
  28. data/lib/fx/adapters/postgres/connection.rb +16 -0
  29. data/lib/fx/adapters/postgres/functions.rb +59 -0
  30. data/lib/fx/adapters/postgres/triggers.rb +57 -0
  31. data/lib/fx/adapters/postgres.rb +167 -0
  32. data/lib/fx/command_recorder/arguments.rb +43 -0
  33. data/lib/fx/command_recorder/function.rb +30 -0
  34. data/lib/fx/command_recorder/trigger.rb +30 -0
  35. data/lib/fx/command_recorder.rb +24 -0
  36. data/lib/fx/configuration.rb +48 -0
  37. data/lib/fx/definition.rb +46 -0
  38. data/lib/fx/function.rb +26 -0
  39. data/lib/fx/railtie.rb +15 -0
  40. data/lib/fx/schema_dumper/function.rb +38 -0
  41. data/lib/fx/schema_dumper/trigger.rb +29 -0
  42. data/lib/fx/schema_dumper.rb +10 -0
  43. data/lib/fx/statements/function.rb +115 -0
  44. data/lib/fx/statements/trigger.rb +146 -0
  45. data/lib/fx/statements.rb +11 -0
  46. data/lib/fx/trigger.rb +26 -0
  47. data/lib/fx/version.rb +4 -0
  48. data/lib/fx.rb +43 -0
  49. data/lib/generators/fx/function/USAGE +11 -0
  50. data/lib/generators/fx/function/function_generator.rb +120 -0
  51. data/lib/generators/fx/function/templates/db/migrate/create_function.erb +5 -0
  52. data/lib/generators/fx/function/templates/db/migrate/update_function.erb +5 -0
  53. data/lib/generators/fx/trigger/USAGE +20 -0
  54. data/lib/generators/fx/trigger/templates/db/migrate/create_trigger.erb +5 -0
  55. data/lib/generators/fx/trigger/templates/db/migrate/update_trigger.erb +5 -0
  56. data/lib/generators/fx/trigger/trigger_generator.rb +130 -0
  57. data/lib/generators.rb +11 -0
  58. data/spec/acceptance/user_manages_functions_spec.rb +57 -0
  59. data/spec/acceptance/user_manages_triggers_spec.rb +51 -0
  60. data/spec/acceptance_helper.rb +62 -0
  61. data/spec/dummy/.gitignore +16 -0
  62. data/spec/dummy/Rakefile +13 -0
  63. data/spec/dummy/bin/bundle +3 -0
  64. data/spec/dummy/bin/rails +4 -0
  65. data/spec/dummy/bin/rake +4 -0
  66. data/spec/dummy/config/application.rb +15 -0
  67. data/spec/dummy/config/boot.rb +5 -0
  68. data/spec/dummy/config/database.yml +9 -0
  69. data/spec/dummy/config/environment.rb +5 -0
  70. data/spec/dummy/config.ru +4 -0
  71. data/spec/dummy/db/migrate/.keep +0 -0
  72. data/spec/features/functions/migrations_spec.rb +65 -0
  73. data/spec/features/functions/revert_spec.rb +75 -0
  74. data/spec/features/triggers/migrations_spec.rb +56 -0
  75. data/spec/features/triggers/revert_spec.rb +95 -0
  76. data/spec/fx/adapters/postgres/functions_spec.rb +37 -0
  77. data/spec/fx/adapters/postgres/triggers_spec.rb +45 -0
  78. data/spec/fx/adapters/postgres_spec.rb +146 -0
  79. data/spec/fx/command_recorder/arguments_spec.rb +41 -0
  80. data/spec/fx/command_recorder_spec.rb +171 -0
  81. data/spec/fx/configuration_spec.rb +21 -0
  82. data/spec/fx/definition_spec.rb +134 -0
  83. data/spec/fx/function_spec.rb +68 -0
  84. data/spec/fx/schema_dumper/function_spec.rb +80 -0
  85. data/spec/fx/schema_dumper/trigger_spec.rb +40 -0
  86. data/spec/fx/statements/function_spec.rb +103 -0
  87. data/spec/fx/statements/trigger_spec.rb +132 -0
  88. data/spec/fx/trigger_spec.rb +55 -0
  89. data/spec/generators/fx/function/function_generator_spec.rb +46 -0
  90. data/spec/generators/fx/trigger/trigger_generator_spec.rb +59 -0
  91. data/spec/spec_helper.rb +21 -0
  92. data/spec/support/definition_helpers.rb +37 -0
  93. data/spec/support/generator_setup.rb +11 -0
  94. data/spec/support/migration_helpers.rb +25 -0
  95. metadata +357 -0
@@ -0,0 +1,146 @@
1
+ module Fx
2
+ module Statements
3
+ # Methods that are made available in migrations for managing Fx triggers.
4
+ module Trigger
5
+ # @api private
6
+ DEFINTION_TYPE = "trigger".freeze
7
+
8
+ # Create a new database trigger.
9
+ #
10
+ # @param name [String, Symbol] The name of the database trigger.
11
+ # @param version [Fixnum] The version number of the trigger, used to
12
+ # find the definition file in `db/triggers`. This defaults to `1` if
13
+ # not provided.
14
+ # @param sql_definition [String] The SQL query for the function. An error
15
+ # will be raised if `sql_definition` and `version` are both set,
16
+ # as they are mutually exclusive.
17
+ # @return The database response from executing the create statement.
18
+ #
19
+ # @example Create trigger from `db/triggers/uppercase_users_name_v01.sql`
20
+ # create_trigger(:uppercase_users_name, version: 1)
21
+ #
22
+ # @example Create trigger from provided SQL string
23
+ # create_trigger(:uppercase_users_name, sql_definition: <<-SQL)
24
+ # CREATE TRIGGER uppercase_users_name
25
+ # BEFORE INSERT ON users
26
+ # FOR EACH ROW
27
+ # EXECUTE PROCEDURE uppercase_users_name();
28
+ # SQL
29
+ #
30
+ def create_trigger(name, options = {})
31
+ version = options[:version]
32
+ _on = options[:on]
33
+ sql_definition = options[:sql_definition]
34
+
35
+ if version.present? && sql_definition.present?
36
+ raise(
37
+ ArgumentError,
38
+ "sql_definition and version cannot both be set",
39
+ )
40
+ end
41
+
42
+ if version.nil?
43
+ version = 1
44
+ end
45
+
46
+ sql_definition = sql_definition.strip_heredoc if sql_definition
47
+ sql_definition ||= Fx::Definition.new(
48
+ name: name,
49
+ version: version,
50
+ type: DEFINTION_TYPE,
51
+ ).to_sql
52
+
53
+ Fx.database.create_trigger(sql_definition)
54
+ end
55
+
56
+ # Drop a database trigger by name.
57
+ #
58
+ # @param name [String, Symbol] The name of the database trigger.
59
+ # @param on [String, Symbol] The name of the table the database trigger
60
+ # is associated with.
61
+ # @param revert_to_version [Fixnum] Used to reverse the `drop_trigger`
62
+ # command on `rake db:rollback`. The provided version will be passed as
63
+ # the `version` argument to {#create_trigger}.
64
+ # @return The database response from executing the drop statement.
65
+ #
66
+ # @example Drop a trigger, rolling back to version 3 on rollback
67
+ # drop_trigger(:log_inserts, on: :users, revert_to_version: 3)
68
+ #
69
+ def drop_trigger(name, options = {})
70
+ on = options.fetch(:on)
71
+ revert_to_version = options[:revert_to_version]
72
+ Fx.database.drop_trigger(name, on: on)
73
+ end
74
+
75
+ # Update a database trigger to a new version.
76
+ #
77
+ # The existing trigger is dropped and recreated using the supplied `on`
78
+ # and `version` parameter.
79
+ #
80
+ # @param name [String, Symbol] The name of the database trigger.
81
+ # @param version [Fixnum] The version number of the trigger.
82
+ # @param on [String, Symbol] The name of the table the database trigger
83
+ # is associated with.
84
+ # @param sql_definition [String] The SQL query for the function. An error
85
+ # will be raised if `sql_definition` and `version` are both set,
86
+ # as they are mutually exclusive.
87
+ # @param revert_to_version [Fixnum] The version number to rollback to on
88
+ # `rake db rollback`
89
+ # @return The database response from executing the create statement.
90
+ #
91
+ # @example Update trigger to a given version
92
+ # update_trigger(
93
+ # :log_inserts,
94
+ # on: :users,
95
+ # version: 3,
96
+ # revert_to_version: 2,
97
+ # )
98
+ #
99
+ # @example Update trigger from provided SQL string
100
+ # update_trigger(:uppercase_users_name, sql_definition: <<-SQL)
101
+ # CREATE TRIGGER uppercase_users_name
102
+ # BEFORE INSERT ON users
103
+ # FOR EACH ROW
104
+ # EXECUTE PROCEDURE uppercase_users_name();
105
+ # SQL
106
+ #
107
+ def update_trigger(name, options = {})
108
+ version = options[:version]
109
+ on = options[:on]
110
+ sql_definition = options[:sql_definition]
111
+ revert_to_version = options[:revert_to_version]
112
+
113
+ if version.nil? && sql_definition.nil?
114
+ raise(
115
+ ArgumentError,
116
+ "version or sql_definition must be specified",
117
+ )
118
+ end
119
+
120
+ if version.present? && sql_definition.present?
121
+ raise(
122
+ ArgumentError,
123
+ "sql_definition and version cannot both be set",
124
+ )
125
+ end
126
+
127
+ if on.nil?
128
+ raise ArgumentError, "on is required"
129
+ end
130
+
131
+ sql_definition = sql_definition.strip_heredoc if sql_definition
132
+ sql_definition ||= Fx::Definition.new(
133
+ name: name,
134
+ version: version,
135
+ type: DEFINTION_TYPE,
136
+ ).to_sql
137
+
138
+ Fx.database.update_trigger(
139
+ name,
140
+ on: on,
141
+ sql_definition: sql_definition,
142
+ )
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,11 @@
1
+ require "rails"
2
+ require "fx/statements/function"
3
+ require "fx/statements/trigger"
4
+
5
+ module Fx
6
+ # @api private
7
+ module Statements
8
+ include Function
9
+ include Trigger
10
+ end
11
+ end
data/lib/fx/trigger.rb ADDED
@@ -0,0 +1,26 @@
1
+ module Fx
2
+ # @api private
3
+ class Trigger
4
+ include Comparable
5
+
6
+ attr_reader :name, :definition
7
+ delegate :<=>, to: :name
8
+
9
+ def initialize(function_row)
10
+ @name = function_row.fetch("name")
11
+ @definition = function_row.fetch("definition")
12
+ end
13
+
14
+ def ==(other)
15
+ name == other.name && definition == other.definition
16
+ end
17
+
18
+ def to_schema
19
+ <<-SCHEMA
20
+ create_trigger :#{name}, sql_definition: <<-\SQL
21
+ #{definition}
22
+ SQL
23
+ SCHEMA
24
+ end
25
+ end
26
+ end
data/lib/fx/version.rb ADDED
@@ -0,0 +1,4 @@
1
+ module Fx
2
+ # @api private
3
+ VERSION = "0.6.3s"
4
+ end
data/lib/fx.rb ADDED
@@ -0,0 +1,43 @@
1
+ require "fx/version"
2
+ require "fx/adapters/postgres"
3
+ require "fx/command_recorder"
4
+ require "fx/configuration"
5
+ require "fx/definition"
6
+ require "fx/function"
7
+ require "fx/statements"
8
+ require "fx/schema_dumper"
9
+ require "fx/trigger"
10
+ require "fx/railtie"
11
+
12
+ # F(x) adds methods `ActiveRecord::Migration` to create and manage database
13
+ # triggers and functions in Rails applications.
14
+ module Fx
15
+ # Hooks Fx into Rails.
16
+ #
17
+ # Enables fx migration methods, migration reversability, and `schema.rb`
18
+ # dumping.
19
+ def self.load
20
+ ActiveRecord::Migration::CommandRecorder.send(
21
+ :include,
22
+ Fx::CommandRecorder,
23
+ )
24
+
25
+ ActiveRecord::SchemaDumper.send(
26
+ :prepend,
27
+ Fx::SchemaDumper,
28
+ )
29
+
30
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.send(
31
+ :include,
32
+ Fx::Statements,
33
+ )
34
+ end
35
+
36
+ # The current database adapter used by F(x).
37
+ #
38
+ # This defaults to {Fx::Adapters::Postgres} but can be overridden
39
+ # via {Configuration}.
40
+ def self.database
41
+ configuration.database
42
+ end
43
+ end
@@ -0,0 +1,11 @@
1
+ Description:
2
+ Create a new database function for your application. This will create a new
3
+ function definition file and the accompanying migration.
4
+
5
+ When --no-migration is passed, skips generating a migration.
6
+
7
+ Examples:
8
+ rails generate fx:function test
9
+
10
+ create: db/functions/test_v01.sql
11
+ create: db/migrate/[TIMESTAMP]_create_test.rb
@@ -0,0 +1,120 @@
1
+ require "rails/generators"
2
+ require "rails/generators/active_record"
3
+
4
+ module Fx
5
+ module Generators
6
+ # @api private
7
+ class FunctionGenerator < Rails::Generators::NamedBase
8
+ include Rails::Generators::Migration
9
+ source_root File.expand_path("../templates", __FILE__)
10
+
11
+ class_option :migration, type: :boolean
12
+
13
+ def create_functions_directory
14
+ unless function_definition_path.exist?
15
+ empty_directory(function_definition_path)
16
+ end
17
+ end
18
+
19
+ def create_function_definition
20
+ if creating_new_function?
21
+ create_file definition.path
22
+ else
23
+ copy_file previous_definition.full_path, definition.full_path
24
+ end
25
+ end
26
+
27
+ def create_migration_file
28
+ return if skip_migration_creation?
29
+ if updating_existing_function?
30
+ migration_template(
31
+ "db/migrate/update_function.erb",
32
+ "db/migrate/update_function_#{file_name}_to_version_#{version}.rb",
33
+ )
34
+ else
35
+ migration_template(
36
+ "db/migrate/create_function.erb",
37
+ "db/migrate/create_function_#{file_name}.rb",
38
+ )
39
+ end
40
+ end
41
+
42
+ def self.next_migration_number(dir)
43
+ ::ActiveRecord::Generators::Base.next_migration_number(dir)
44
+ end
45
+
46
+ no_tasks do
47
+ def previous_version
48
+ @_previous_version ||= Dir.entries(function_definition_path).
49
+ map { |name| version_regex.match(name).try(:[], "version").to_i }.
50
+ max
51
+ end
52
+
53
+ def version
54
+ @_version ||= previous_version.next
55
+ end
56
+
57
+ def migration_class_name
58
+ if updating_existing_function?
59
+ "UpdateFunction#{class_name}ToVersion#{version}"
60
+ else
61
+ super
62
+ end
63
+ end
64
+
65
+ def activerecord_migration_class
66
+ if ActiveRecord::Migration.respond_to?(:current_version)
67
+ "ActiveRecord::Migration[#{ActiveRecord::Migration.current_version}]"
68
+ else
69
+ "ActiveRecord::Migration"
70
+ end
71
+ end
72
+
73
+ def formatted_name
74
+ if singular_name.include?(".")
75
+ "\"#{singular_name}\""
76
+ else
77
+ ":#{singular_name}"
78
+ end
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ def function_definition_path
85
+ @_function_definition_path ||= Jets.root.join(*%w(db functions))
86
+ end
87
+
88
+ def version_regex
89
+ /\A#{file_name}_v(?<version>\d+)\.sql\z/
90
+ end
91
+
92
+ def updating_existing_function?
93
+ previous_version > 0
94
+ end
95
+
96
+ def creating_new_function?
97
+ previous_version == 0
98
+ end
99
+
100
+ def definition
101
+ Fx::Definition.new(name: file_name, version: version)
102
+ end
103
+
104
+ def previous_definition
105
+ Fx::Definition.new(name: file_name, version: previous_version)
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
+ # True unless explicitly false
115
+ def migration
116
+ options[:migration] != false
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,5 @@
1
+ class <%= migration_class_name %> < <%= activerecord_migration_class %>
2
+ def change
3
+ create_function <%= formatted_name %>
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class <%= migration_class_name %> < <%= activerecord_migration_class %>
2
+ def change
3
+ update_function <%= formatted_name %>, version: <%= version %>, revert_to_version: <%= previous_version %>
4
+ end
5
+ end
@@ -0,0 +1,20 @@
1
+ Description:
2
+ Create a new database trigger for your application. This will create a new
3
+ trigger definition file and the accompanying migration.
4
+
5
+ If a trigger of the given name already exists, create a new version of the
6
+ trigger and a migration to replace the old version with the new.
7
+
8
+ When --no-migration is passed, skips generating a migration.
9
+
10
+ Examples:
11
+
12
+ rails generate fx:trigger test
13
+
14
+ create: db/triggers/test_v01.sql
15
+ create: db/migrate/[TIMESTAMP]_create_trigger_test.rb
16
+
17
+ rails generate fx:trigger test
18
+
19
+ create: db/triggers/test_v02.sql
20
+ create: db/migrate/[TIMESTAMP]_update_trigger_test_to_version_2.rb
@@ -0,0 +1,5 @@
1
+ class <%= migration_class_name %> < <%= activerecord_migration_class %>
2
+ def change
3
+ create_trigger <%= formatted_name %>, on: <%= formatted_table_name %>
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class <%= migration_class_name %> < <%= activerecord_migration_class %>
2
+ def change
3
+ update_trigger <%= formatted_name %>, on: <%= formatted_table_name %>, version: <%= version %>, revert_to_version: <%= previous_version %>
4
+ end
5
+ end
@@ -0,0 +1,130 @@
1
+ require "rails/generators"
2
+ require "rails/generators/active_record"
3
+
4
+ module Fx
5
+ module Generators
6
+ # @api private
7
+ class TriggerGenerator < Rails::Generators::NamedBase
8
+ include Rails::Generators::Migration
9
+ source_root File.expand_path("../templates", __FILE__)
10
+ argument :table_name, type: :hash, required: true
11
+
12
+ class_option :migration, type: :boolean
13
+
14
+ def create_triggers_directory
15
+ unless trigger_definition_path.exist?
16
+ empty_directory(trigger_definition_path)
17
+ end
18
+ end
19
+
20
+ def create_trigger_definition
21
+ create_file definition.path
22
+ end
23
+
24
+ def create_migration_file
25
+ return if skip_migration_creation?
26
+ if updating_existing_trigger?
27
+ migration_template(
28
+ "db/migrate/update_trigger.erb",
29
+ "db/migrate/update_trigger_#{file_name}_to_version_#{version}.rb"
30
+ )
31
+ else
32
+ migration_template(
33
+ "db/migrate/create_trigger.erb",
34
+ "db/migrate/create_trigger_#{file_name}.rb"
35
+ )
36
+ end
37
+ end
38
+
39
+ def self.next_migration_number(dir)
40
+ ::ActiveRecord::Generators::Base.next_migration_number(dir)
41
+ end
42
+
43
+ no_tasks do
44
+ def previous_version
45
+ @_previous_version ||= Dir.entries(trigger_definition_path).
46
+ map { |name| version_regex.match(name).try(:[], "version").to_i }.
47
+ max
48
+ end
49
+
50
+ def version
51
+ @_version ||= previous_version.next
52
+ end
53
+
54
+ def migration_class_name
55
+ if updating_existing_trigger?
56
+ "UpdateTrigger#{class_name}ToVersion#{version}"
57
+ else
58
+ super
59
+ end
60
+ end
61
+
62
+ def activerecord_migration_class
63
+ if ActiveRecord::Migration.respond_to?(:current_version)
64
+ "ActiveRecord::Migration[#{ActiveRecord::Migration.current_version}]"
65
+ else
66
+ "ActiveRecord::Migration"
67
+ end
68
+ end
69
+
70
+ def formatted_name
71
+ if singular_name.include?(".")
72
+ "\"#{singular_name}\""
73
+ else
74
+ ":#{singular_name}"
75
+ end
76
+ end
77
+
78
+ def formatted_table_name
79
+ name = table_name["table_name"] || table_name["on"]
80
+
81
+ if name.nil?
82
+ raise(
83
+ ArgumentError,
84
+ "Either `table_name:NAME` or `on:NAME` must be specified",
85
+ )
86
+ end
87
+
88
+ if name.include?(".")
89
+ "\"#{name}\""
90
+ else
91
+ ":#{name}"
92
+ end
93
+ end
94
+ end
95
+
96
+ private
97
+
98
+ def version_regex
99
+ /\A#{file_name}_v(?<version>\d+)\.sql\z/
100
+ end
101
+
102
+ def updating_existing_trigger?
103
+ previous_version > 0
104
+ end
105
+
106
+ def definition
107
+ Fx::Definition.new(
108
+ name: file_name,
109
+ version: version,
110
+ type: "trigger",
111
+ )
112
+ end
113
+
114
+ def trigger_definition_path
115
+ @_trigger_definition_path ||= Jets.root.join(*["db", "triggers"])
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
+ # True unless explicitly false
125
+ def migration
126
+ options[:migration] != false
127
+ end
128
+ end
129
+ end
130
+ end
data/lib/generators.rb ADDED
@@ -0,0 +1,11 @@
1
+ module Fx
2
+ # Fx provides generators for creating and updating functions and triggers.
3
+ #
4
+ # See:
5
+ #
6
+ # * {file:lib/generators/fx/function/USAGE Function Generator}
7
+ # * {file:lib/generators/fx/trigger/USAGE Trigger Generator}
8
+ # * {file:README.md README}
9
+ module Generators
10
+ end
11
+ end
@@ -0,0 +1,57 @@
1
+ require "acceptance_helper"
2
+
3
+ describe "User manages functions" do
4
+ it "handles simple functions" do
5
+ successfully "rails generate fx:function test"
6
+ write_function_definition "test_v01", <<-EOS
7
+ CREATE OR REPLACE FUNCTION test()
8
+ RETURNS text AS $$
9
+ BEGIN
10
+ RETURN 'test';
11
+ END;
12
+ $$ LANGUAGE plpgsql;
13
+ EOS
14
+ successfully "rake db:migrate"
15
+
16
+ result = execute("SELECT * FROM test() AS result")
17
+ expect(result).to eq("result" => "test")
18
+
19
+ successfully "rails generate fx:function test"
20
+ verify_identical_definitions(
21
+ "db/functions/test_v01.sql",
22
+ "db/functions/test_v02.sql",
23
+ )
24
+ write_function_definition "test_v02", <<-EOS
25
+ CREATE OR REPLACE FUNCTION test()
26
+ RETURNS text AS $$
27
+ BEGIN
28
+ RETURN 'testest';
29
+ END;
30
+ $$ LANGUAGE plpgsql;
31
+ EOS
32
+ successfully "rake db:migrate"
33
+
34
+ result = execute("SELECT * FROM test() AS result")
35
+ expect(result).to eq("result" => "testest")
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
57
+ end
@@ -0,0 +1,51 @@
1
+ require "acceptance_helper"
2
+
3
+ describe "User manages triggers" do
4
+ it "handles simple triggers" do
5
+ successfully "rails generate model user name:string upper_name:string"
6
+ successfully "rails generate fx:function uppercase_users_name"
7
+ write_function_definition "uppercase_users_name_v01", <<-EOS
8
+ CREATE OR REPLACE FUNCTION uppercase_users_name()
9
+ RETURNS trigger AS $$
10
+ BEGIN
11
+ NEW.upper_name = UPPER(NEW.name);
12
+ RETURN NEW;
13
+ END;
14
+ $$ LANGUAGE plpgsql;
15
+ EOS
16
+ successfully "rails generate fx:trigger uppercase_users_name table_name:users"
17
+ write_trigger_definition "uppercase_users_name_v01", <<-EOS
18
+ CREATE TRIGGER uppercase_users_name
19
+ BEFORE INSERT ON users
20
+ FOR EACH ROW
21
+ EXECUTE PROCEDURE uppercase_users_name();
22
+ EOS
23
+ successfully "rake db:migrate"
24
+
25
+ execute <<-EOS
26
+ INSERT INTO users
27
+ (name, created_at, updated_at)
28
+ VALUES
29
+ ('Bob', NOW(), NOW());
30
+ EOS
31
+ result = execute("SELECT upper_name FROM users WHERE name = 'Bob';")
32
+ expect(result).to eq("upper_name" => "BOB")
33
+
34
+ successfully "rails generate fx:trigger uppercase_users_name table_name:users"
35
+ write_trigger_definition "uppercase_users_name_v02", <<-EOS
36
+ CREATE TRIGGER uppercase_users_name
37
+ BEFORE UPDATE ON users
38
+ FOR EACH ROW
39
+ EXECUTE PROCEDURE uppercase_users_name();
40
+ EOS
41
+ successfully "rake db:migrate"
42
+ execute <<-EOS
43
+ UPDATE users
44
+ SET name = 'Alice'
45
+ WHERE id = 1;
46
+ EOS
47
+
48
+ result = execute("SELECT upper_name FROM users WHERE name = 'Alice';")
49
+ expect(result).to eq("upper_name" => "ALICE")
50
+ end
51
+ end