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,167 @@
1
+ require "fx/adapters/postgres/connection"
2
+ require "fx/adapters/postgres/functions"
3
+ require "fx/adapters/postgres/triggers"
4
+
5
+ module Fx
6
+ # F(x) database adapters.
7
+ #
8
+ # F(x) ships with a Postgres adapter only but can be extended with
9
+ # additional adapters. The {Fx::Adapters::Postgres} adapter provides the
10
+ # interface.
11
+ module Adapters
12
+ # Creates an instance of the F(x) Postgres adapter.
13
+ #
14
+ # This is the default adapter for F(x). Configuring it via
15
+ # {Fx.configure} is not required, but the example below shows how one
16
+ # would explicitly set it.
17
+ #
18
+ # @param [#connection] connectable An object that returns the connection
19
+ # for F(x) to use. Defaults to `ActiveRecord::Base`.
20
+ #
21
+ # @example
22
+ # Fx.configure do |config|
23
+ # config.adapter = Fx::Adapters::Postgres.new
24
+ # end
25
+ class Postgres
26
+ # Creates an instance of the F(x) Postgres adapter.
27
+ #
28
+ # This is the default adapter for F(x). Configuring it via
29
+ # {Fx.configure} is not required, but the example below shows how one
30
+ # would explicitly set it.
31
+ #
32
+ # @param [#connection] connectable An object that returns the connection
33
+ # for F(x) to use. Defaults to `ActiveRecord::Base`.
34
+ #
35
+ # @example
36
+ # Fx.configure do |config|
37
+ # config.adapter = Fx::Adapters::Postgres.new
38
+ # end
39
+ def initialize(connectable = ActiveRecord::Base)
40
+ @connectable = connectable
41
+ end
42
+
43
+ # Returns an array of functions in the database.
44
+ #
45
+ # This collection of functions is used by the [Fx::SchemaDumper] to
46
+ # populate the `schema.rb` file.
47
+ #
48
+ # @return [Array<Fx::Function>]
49
+ def functions
50
+ Functions.all(connection)
51
+ end
52
+
53
+ # Returns an array of triggers in the database.
54
+ #
55
+ # This collection of triggers is used by the [Fx::SchemaDumper] to
56
+ # populate the `schema.rb` file.
57
+ #
58
+ # @return [Array<Fx::Trigger>]
59
+ def triggers
60
+ Triggers.all(connection)
61
+ end
62
+
63
+ # Creates a function in the database.
64
+ #
65
+ # This is typically called in a migration via
66
+ # {Fx::Statements::Function#create_function}.
67
+ #
68
+ # @param sql_definition The SQL schema for the function.
69
+ #
70
+ # @return [void]
71
+ def create_function(sql_definition)
72
+ execute sql_definition
73
+ end
74
+
75
+ # Creates a trigger in the database.
76
+ #
77
+ # This is typically called in a migration via
78
+ # {Fx::Statements::Trigger#create_trigger}.
79
+ #
80
+ # @param sql_definition The SQL schema for the trigger.
81
+ #
82
+ # @return [void]
83
+ def create_trigger(sql_definition)
84
+ execute sql_definition
85
+ end
86
+
87
+ # Updates a function in the database.
88
+ #
89
+ # This is typically called in a migration via
90
+ # {Fx::Statements::Function#update_function}.
91
+ #
92
+ # @param name The name of the function.
93
+ # @param sql_definition The SQL schema for the function.
94
+ #
95
+ # @return [void]
96
+ def update_function(name, sql_definition)
97
+ drop_function(name)
98
+ create_function(sql_definition)
99
+ end
100
+
101
+ # Updates a trigger in the database.
102
+ #
103
+ # The existing trigger is dropped and recreated using the supplied `on`
104
+ # and `version` parameter.
105
+ #
106
+ # This is typically called in a migration via
107
+ # {Fx::Statements::Function#update_trigger}.
108
+ #
109
+ # @param name The name of the trigger.
110
+ # @param on The associated table for the trigger to drop
111
+ # @param sql_definition The SQL schema for the function.
112
+ #
113
+ # @return [void]
114
+ def update_trigger(name, on:, sql_definition:)
115
+ drop_trigger(name, on: on)
116
+ create_trigger(sql_definition)
117
+ end
118
+
119
+ # Drops the function from the database
120
+ #
121
+ # This is typically called in a migration via
122
+ # {Fx::Statements::Function#drop_function}.
123
+ #
124
+ # @param name The name of the function to drop
125
+ #
126
+ # @return [void]
127
+ def drop_function(name)
128
+ if support_drop_function_without_args
129
+ execute "DROP FUNCTION #{name};"
130
+ else
131
+ execute "DROP FUNCTION #{name}();"
132
+ end
133
+ end
134
+
135
+ # Drops the trigger from the database
136
+ #
137
+ # This is typically called in a migration via
138
+ # {Fx::Statements::Trigger#drop_trigger}.
139
+ #
140
+ # @param name The name of the trigger to drop
141
+ # @param on The associated table for the trigger to drop
142
+ #
143
+ # @return [void]
144
+ def drop_trigger(name, on:)
145
+ execute "DROP TRIGGER #{name} ON #{on};"
146
+ end
147
+
148
+ private
149
+
150
+ attr_reader :connectable
151
+
152
+ delegate :execute, to: :connection
153
+
154
+ def connection
155
+ Connection.new(connectable.connection)
156
+ end
157
+
158
+ def support_drop_function_without_args
159
+ # https://www.postgresql.org/docs/9.6/sql-dropfunction.html
160
+ # https://www.postgresql.org/docs/10/sql-dropfunction.html
161
+
162
+ pg_connection = connectable.connection.raw_connection
163
+ pg_connection.server_version >= 10_00_00
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,43 @@
1
+ module Fx
2
+ module CommandRecorder
3
+ # @api private
4
+ class Arguments
5
+ def initialize(args)
6
+ @args = args.freeze
7
+ end
8
+
9
+ def function
10
+ @args[0]
11
+ end
12
+
13
+ def version
14
+ options[:version]
15
+ end
16
+
17
+ def revert_to_version
18
+ options[:revert_to_version]
19
+ end
20
+
21
+ def invert_version
22
+ Arguments.new([function, options_for_revert])
23
+ end
24
+
25
+ def to_a
26
+ @args.to_a
27
+ end
28
+
29
+ private
30
+
31
+ def options
32
+ @options ||= @args[1] || {}
33
+ end
34
+
35
+ def options_for_revert
36
+ options.clone.tap do |revert_options|
37
+ revert_options[:version] = revert_to_version
38
+ revert_options.delete(:revert_to_version)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,30 @@
1
+ module Fx
2
+ module CommandRecorder
3
+ # @api private
4
+ module Function
5
+ def create_function(*args)
6
+ record(:create_function, args)
7
+ end
8
+
9
+ def drop_function(*args)
10
+ record(:drop_function, args)
11
+ end
12
+
13
+ def update_function(*args)
14
+ record(:update_function, args)
15
+ end
16
+
17
+ def invert_create_function(args)
18
+ [:drop_function, args]
19
+ end
20
+
21
+ def invert_drop_function(args)
22
+ perform_inversion(:create_function, args)
23
+ end
24
+
25
+ def invert_update_function(args)
26
+ perform_inversion(:update_function, args)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ module Fx
2
+ module CommandRecorder
3
+ # @api private
4
+ module Trigger
5
+ def create_trigger(*args)
6
+ record(:create_trigger, args)
7
+ end
8
+
9
+ def drop_trigger(*args)
10
+ record(:drop_trigger, args)
11
+ end
12
+
13
+ def update_trigger(*args)
14
+ record(:update_trigger, args)
15
+ end
16
+
17
+ def invert_create_trigger(args)
18
+ [:drop_trigger, args]
19
+ end
20
+
21
+ def invert_drop_trigger(args)
22
+ perform_inversion(:create_trigger, args)
23
+ end
24
+
25
+ def invert_update_trigger(args)
26
+ perform_inversion(:update_trigger, args)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ require "fx/command_recorder/arguments"
2
+ require "fx/command_recorder/function"
3
+ require "fx/command_recorder/trigger"
4
+
5
+ module Fx
6
+ # @api private
7
+ module CommandRecorder
8
+ include Function
9
+ include Trigger
10
+
11
+ private
12
+
13
+ def perform_inversion(method, args)
14
+ arguments = Arguments.new(args)
15
+
16
+ if arguments.revert_to_version.nil?
17
+ message = "`#{method}` is reversible only if given a `revert_to_version`"
18
+ raise ActiveRecord::IrreversibleMigration, message
19
+ end
20
+
21
+ [method, arguments.invert_version.to_a]
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,48 @@
1
+ module Fx
2
+ # @return [Fx::Configuration] F(x)'s current configuration
3
+ def self.configuration
4
+ @_configuration ||= Configuration.new
5
+ end
6
+
7
+ # Set F(x)'s configuration
8
+ #
9
+ # @param config [Fx::Configuration]
10
+ def self.configuration=(config)
11
+ @_configuration = config
12
+ end
13
+
14
+ # Modify F(x)'s current configuration
15
+ #
16
+ # @yieldparam [Fx::Configuration] config current F(x) config
17
+ # ```
18
+ # Fx.configure do |config|
19
+ # config.database = Fx::Adapters::Postgres
20
+ # config.dump_functions_at_beginning_of_schema = true
21
+ # end
22
+ # ```
23
+ def self.configure
24
+ yield configuration
25
+ end
26
+
27
+ # F(x)'s configuration object.
28
+ class Configuration
29
+ # The F(x) database adapter instance to use when executing SQL.
30
+ #
31
+ # Defaults to an instance of {Fx::Adapters::Postgres}
32
+ # @return Fx adapter
33
+ attr_accessor :database
34
+
35
+ # Prioritizes the order in the schema.rb of functions before other
36
+ # statements in order to make directly schema load work when using functions
37
+ # in statements below, i.e.: default column values.
38
+ #
39
+ # Defaults to false
40
+ # @return Boolean
41
+ attr_accessor :dump_functions_at_beginning_of_schema
42
+
43
+ def initialize
44
+ @database = Fx::Adapters::Postgres.new
45
+ @dump_functions_at_beginning_of_schema = false
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,46 @@
1
+ module Fx
2
+ # @api private
3
+ class Definition
4
+ def initialize(name:, version:, type: "function")
5
+ @name = name
6
+ @version = version.to_i
7
+ @type = type
8
+ end
9
+
10
+ def to_sql
11
+ File.read(find_file || full_path).tap do |content|
12
+ if content.empty?
13
+ raise "Define #{@type} in #{path} before migrating."
14
+ end
15
+ end
16
+ end
17
+
18
+ def full_path
19
+ Jets.root.join(path)
20
+ end
21
+
22
+ def path
23
+ @_path ||= File.join("db", @type.pluralize, filename)
24
+ end
25
+
26
+ def version
27
+ @version.to_s.rjust(2, "0")
28
+ end
29
+
30
+ private
31
+
32
+ def filename
33
+ @_filename ||= "#{@name}_v#{version}.sql"
34
+ end
35
+
36
+ def find_file
37
+ migration_paths.lazy
38
+ .map { |migration_path| File.expand_path(File.join("..", "..", path), migration_path) }
39
+ .find { |definition_path| File.exist?(definition_path) }
40
+ end
41
+
42
+ def migration_paths
43
+ [Jets.root.join('db', 'migrate').to_s]
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,26 @@
1
+ module Fx
2
+ # @api private
3
+ class Function
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.indent(2)
20
+ create_function :#{name}, sql_definition: <<-'SQL'
21
+ #{definition.indent(4).rstrip}
22
+ SQL
23
+ SCHEMA
24
+ end
25
+ end
26
+ end
data/lib/fx/railtie.rb ADDED
@@ -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
@@ -0,0 +1,38 @@
1
+ require "rails"
2
+
3
+ module Fx
4
+ module SchemaDumper
5
+ # @api private
6
+ module Function
7
+ def tables(stream)
8
+ if Fx.configuration.dump_functions_at_beginning_of_schema
9
+ functions(stream)
10
+ empty_line(stream)
11
+ end
12
+
13
+ super
14
+
15
+ unless Fx.configuration.dump_functions_at_beginning_of_schema
16
+ functions(stream)
17
+ empty_line(stream)
18
+ end
19
+ end
20
+
21
+ def empty_line(stream)
22
+ stream.puts if dumpable_functions_in_database.any?
23
+ end
24
+
25
+ def functions(stream)
26
+ dumpable_functions_in_database.each do |function|
27
+ stream.puts(function.to_schema)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def dumpable_functions_in_database
34
+ @_dumpable_functions_in_database ||= Fx.database.functions
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,29 @@
1
+ require "rails"
2
+
3
+ module Fx
4
+ module SchemaDumper
5
+ # @api private
6
+ module Trigger
7
+ def tables(stream)
8
+ super
9
+ triggers(stream)
10
+ end
11
+
12
+ def triggers(stream)
13
+ if dumpable_triggers_in_database.any?
14
+ stream.puts
15
+ end
16
+
17
+ dumpable_triggers_in_database.each do |trigger|
18
+ stream.puts(trigger.to_schema)
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def dumpable_triggers_in_database
25
+ @_dumpable_triggers_in_database ||= Fx.database.triggers
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,10 @@
1
+ require "fx/schema_dumper/function"
2
+ require "fx/schema_dumper/trigger"
3
+
4
+ module Fx
5
+ # @api private
6
+ module SchemaDumper
7
+ include Function
8
+ include Trigger
9
+ end
10
+ end
@@ -0,0 +1,115 @@
1
+ require "rails"
2
+
3
+ module Fx
4
+ module Statements
5
+ # Methods that are made available in migrations for managing Fx functions.
6
+ module Function
7
+ # Create a new database function.
8
+ #
9
+ # @param name [String, Symbol] The name of the database function.
10
+ # @param version [Fixnum] The version number of the function, used to
11
+ # find the definition file in `db/functions`. This defaults to `1` if
12
+ # not provided.
13
+ # @param sql_definition [String] The SQL query for the function schema.
14
+ # If both `sql_defintion` and `version` are provided,
15
+ # `sql_definition` takes prescedence.
16
+ # @return The database response from executing the create statement.
17
+ #
18
+ # @example Create from `db/functions/uppercase_users_name_v02.sql`
19
+ # create_function(:uppercase_users_name, version: 2)
20
+ #
21
+ # @example Create from provided SQL string
22
+ # create_function(:uppercase_users_name, sql_definition: <<-SQL)
23
+ # CREATE OR REPLACE FUNCTION uppercase_users_name()
24
+ # RETURNS trigger AS $$
25
+ # BEGIN
26
+ # NEW.upper_name = UPPER(NEW.name);
27
+ # RETURN NEW;
28
+ # END;
29
+ # $$ LANGUAGE plpgsql;
30
+ # SQL
31
+ #
32
+ def create_function(name, options = {})
33
+ version = options.fetch(:version, 1)
34
+ sql_definition = options[:sql_definition]
35
+
36
+ if version.nil? && sql_definition.nil?
37
+ raise(
38
+ ArgumentError,
39
+ "version or sql_definition must be specified",
40
+ )
41
+ end
42
+ sql_definition = sql_definition.strip_heredoc if sql_definition
43
+ sql_definition ||= Fx::Definition.new(name: name, version: version).to_sql
44
+
45
+ Fx.database.create_function(sql_definition)
46
+ end
47
+
48
+ # Drop a database function by name.
49
+ #
50
+ # @param name [String, Symbol] The name of the database function.
51
+ # @param revert_to_version [Fixnum] Used to reverse the `drop_function`
52
+ # command on `rake db:rollback`. The provided version will be passed as
53
+ # the `version` argument to {#create_function}.
54
+ # @return The database response from executing the drop statement.
55
+ #
56
+ # @example Drop a function, rolling back to version 2 on rollback
57
+ # drop_function(:uppercase_users_name, revert_to_version: 2)
58
+ #
59
+ def drop_function(name, options = {})
60
+ revert_to_version = options[:revert_to_version]
61
+ Fx.database.drop_function(name)
62
+ end
63
+
64
+ # Update a database function.
65
+ #
66
+ # @param name [String, Symbol] The name of the database function.
67
+ # @param version [Fixnum] The version number of the function, used to
68
+ # find the definition file in `db/functions`. This defaults to `1` if
69
+ # not provided.
70
+ # @param sql_definition [String] The SQL query for the function schema.
71
+ # If both `sql_defintion` and `version` are provided,
72
+ # `sql_definition` takes prescedence.
73
+ # @return The database response from executing the create statement.
74
+ #
75
+ # @example Update function to a given version
76
+ # update_function(
77
+ # :uppercase_users_name,
78
+ # version: 3,
79
+ # revert_to_version: 2,
80
+ # )
81
+ #
82
+ # @example Update function from provided SQL string
83
+ # update_function(:uppercase_users_name, sql_definition: <<-SQL)
84
+ # CREATE OR REPLACE FUNCTION uppercase_users_name()
85
+ # RETURNS trigger AS $$
86
+ # BEGIN
87
+ # NEW.upper_name = UPPER(NEW.name);
88
+ # RETURN NEW;
89
+ # END;
90
+ # $$ LANGUAGE plpgsql;
91
+ # SQL
92
+ #
93
+ def update_function(name, options = {})
94
+ version = options[:version]
95
+ sql_definition = options[:sql_definition]
96
+ revert_to_version = options[:revert_to_version]
97
+
98
+ if version.nil? && sql_definition.nil?
99
+ raise(
100
+ ArgumentError,
101
+ "version or sql_definition must be specified",
102
+ )
103
+ end
104
+
105
+ sql_definition = sql_definition.strip_heredoc if sql_definition
106
+ sql_definition ||= Fx::Definition.new(
107
+ name: name,
108
+ version: version,
109
+ ).to_sql
110
+
111
+ Fx.database.update_function(name, sql_definition)
112
+ end
113
+ end
114
+ end
115
+ end