fx 0.3.1 → 0.6.2

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 (44) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.travis.yml +15 -8
  4. data/Appraisals +16 -10
  5. data/LICENSE +18 -0
  6. data/README.md +42 -13
  7. data/fx.gemspec +1 -1
  8. data/gemfiles/rails42.gemfile +2 -1
  9. data/gemfiles/rails50.gemfile +1 -1
  10. data/gemfiles/rails51.gemfile +8 -0
  11. data/gemfiles/rails52.gemfile +8 -0
  12. data/gemfiles/rails60.gemfile +8 -0
  13. data/gemfiles/rails_edge.gemfile +3 -3
  14. data/lib/fx.rb +22 -0
  15. data/lib/fx/adapters/postgres.rb +13 -1
  16. data/lib/fx/adapters/postgres/functions.rb +3 -0
  17. data/lib/fx/command_recorder.rb +0 -5
  18. data/lib/fx/configuration.rb +10 -0
  19. data/lib/fx/definition.rb +13 -3
  20. data/lib/fx/railtie.rb +15 -0
  21. data/lib/fx/schema_dumper.rb +0 -5
  22. data/lib/fx/schema_dumper/function.rb +14 -5
  23. data/lib/fx/statements.rb +0 -5
  24. data/lib/fx/version.rb +1 -1
  25. data/lib/generators/fx/function/USAGE +2 -0
  26. data/lib/generators/fx/function/function_generator.rb +15 -1
  27. data/lib/generators/fx/trigger/USAGE +2 -0
  28. data/lib/generators/fx/trigger/trigger_generator.rb +15 -1
  29. data/spec/acceptance/user_manages_functions_spec.rb +20 -0
  30. data/spec/fx/adapters/postgres/triggers_spec.rb +4 -3
  31. data/spec/fx/adapters/postgres_spec.rb +36 -14
  32. data/spec/fx/definition_spec.rb +23 -0
  33. data/spec/fx/schema_dumper/function_spec.rb +57 -1
  34. data/spec/generators/fx/function/function_generator_spec.rb +12 -0
  35. data/spec/generators/fx/trigger/trigger_generator_spec.rb +12 -0
  36. metadata +12 -16
  37. data/.ruby-version +0 -1
  38. data/gemfiles/rails40.gemfile +0 -8
  39. data/gemfiles/rails40.gemfile.lock +0 -111
  40. data/gemfiles/rails41.gemfile +0 -8
  41. data/gemfiles/rails41.gemfile.lock +0 -113
  42. data/gemfiles/rails42.gemfile.lock +0 -130
  43. data/gemfiles/rails50.gemfile.lock +0 -126
  44. data/gemfiles/rails_edge.gemfile.lock +0 -179
@@ -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
- )
@@ -1,4 +1,4 @@
1
1
  module Fx
2
2
  # @api private
3
- VERSION = "0.3.1"
3
+ VERSION = "0.6.2"
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,17 @@ 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
+ # True unless explicitly false
115
+ def migration
116
+ options[:migration] != false
117
+ end
104
118
  end
105
119
  end
106
120
  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,17 @@ 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
+ # True unless explicitly false
125
+ def migration
126
+ options[:migration] != false
127
+ end
114
128
  end
115
129
  end
116
130
  end
@@ -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
@@ -34,9 +34,10 @@ module Fx
34
34
  first = triggers.first
35
35
  expect(triggers.size).to eq 1
36
36
  expect(first.name).to eq "uppercase_users_name"
37
- expect(first.definition).to eq <<-EOS.strip_heredoc.strip
38
- CREATE TRIGGER uppercase_users_name BEFORE INSERT ON users FOR EACH ROW EXECUTE PROCEDURE uppercase_users_name()
39
- EOS
37
+ expect(first.definition).to include("BEFORE INSERT")
38
+ expect(first.definition).to match(/ON [public\.users|users]/)
39
+ expect(first.definition).to include("FOR EACH ROW")
40
+ expect(first.definition).to include("EXECUTE PROCEDURE uppercase_users_name()")
40
41
  end
41
42
  end
42
43
  end
@@ -53,22 +53,44 @@ module Fx::Adapters
53
53
  end
54
54
 
55
55
  describe "#drop_function" do
56
- it "successfully drops a function" do
57
- adapter = Postgres.new
58
- adapter.create_function(
59
- <<-EOS
60
- CREATE OR REPLACE FUNCTION test()
61
- RETURNS text AS $$
62
- BEGIN
63
- RETURN 'test';
64
- END;
65
- $$ LANGUAGE plpgsql;
66
- EOS
67
- )
56
+ context "when the function has arguments" do
57
+ it "successfully drops a function with the entire function signature" do
58
+ adapter = Postgres.new
59
+ adapter.create_function(
60
+ <<-EOS
61
+ CREATE FUNCTION adder(x int, y int)
62
+ RETURNS int AS $$
63
+ BEGIN
64
+ RETURN $1 + $2;
65
+ END;
66
+ $$ LANGUAGE plpgsql;
67
+ EOS
68
+ )
69
+
70
+ adapter.drop_function(:adder)
71
+
72
+ expect(adapter.functions.map(&:name)).not_to include("adder")
73
+ end
74
+ end
75
+
76
+ context "when the function does not have arguments" do
77
+ it "successfully drops a function" do
78
+ adapter = Postgres.new
79
+ adapter.create_function(
80
+ <<-EOS
81
+ CREATE OR REPLACE FUNCTION test()
82
+ RETURNS text AS $$
83
+ BEGIN
84
+ RETURN 'test';
85
+ END;
86
+ $$ LANGUAGE plpgsql;
87
+ EOS
88
+ )
68
89
 
69
- adapter.drop_function(:test)
90
+ adapter.drop_function(:test)
70
91
 
71
- expect(adapter.functions.map(&:name)).not_to include("test")
92
+ expect(adapter.functions.map(&:name)).not_to include("test")
93
+ end
72
94
  end
73
95
  end
74
96
 
@@ -28,6 +28,29 @@ describe Fx::Definition do
28
28
  %r(Define function in db/functions/test_v01.sql before migrating),
29
29
  )
30
30
  end
31
+
32
+ context "when definition is at Rails engine" do
33
+ it "returns the content of a function definition" do
34
+ sql_definition = <<-EOS
35
+ CREATE OR REPLACE FUNCTION test()
36
+ RETURNS text AS $$
37
+ BEGIN
38
+ RETURN 'test';
39
+ END;
40
+ $$ LANGUAGE plpgsql;
41
+ EOS
42
+ engine_path = Rails.root.join("tmp", "engine")
43
+ FileUtils.mkdir_p(engine_path.join("db", "functions"))
44
+ File.write(engine_path.join("db", "functions", "custom_test_v01.sql"), sql_definition)
45
+ Rails.application.config.paths["db/migrate"].push(engine_path.join("db", "migrate"))
46
+
47
+ definition = Fx::Definition.new(name: "custom_test", version: 1)
48
+
49
+ expect(definition.to_sql).to eq sql_definition
50
+
51
+ FileUtils.rm_rf(engine_path)
52
+ end
53
+ end
31
54
  end
32
55
 
33
56
  context "representing a trigger definition" do
@@ -3,14 +3,69 @@ require "spec_helper"
3
3
  describe Fx::SchemaDumper::Function, :db do
4
4
  it "dumps a create_function for a function in the database" do
5
5
  sql_definition = <<-EOS
6
- CREATE OR REPLACE FUNCTION test()
6
+ CREATE OR REPLACE FUNCTION my_function()
7
7
  RETURNS text AS $$
8
8
  BEGIN
9
9
  RETURN 'test';
10
10
  END;
11
11
  $$ LANGUAGE plpgsql;
12
12
  EOS
13
+ connection.create_function :my_function, sql_definition: sql_definition
14
+ connection.create_table :my_table
15
+ stream = StringIO.new
16
+ output = stream.string
17
+
18
+ ActiveRecord::SchemaDumper.dump(connection, stream)
19
+
20
+ expect(output).to(
21
+ match(/table "my_table".*function :my_function.*RETURN 'test';/m),
22
+ )
23
+ end
24
+
25
+ it "dumps a create_function for a function in the database" do
26
+ Fx.configuration.dump_functions_at_beginning_of_schema = true
27
+ sql_definition = <<-EOS
28
+ CREATE OR REPLACE FUNCTION my_function()
29
+ RETURNS text AS $$
30
+ BEGIN
31
+ RETURN 'test';
32
+ END;
33
+ $$ LANGUAGE plpgsql;
34
+ EOS
35
+ connection.create_function :my_function, sql_definition: sql_definition
36
+ connection.create_table :my_table
37
+ stream = StringIO.new
38
+ output = stream.string
39
+
40
+ ActiveRecord::SchemaDumper.dump(connection, stream)
41
+
42
+ expect(output).to(
43
+ match(/function :my_function.*RETURN 'test';.*table "my_table"/m),
44
+ )
45
+ ensure
46
+ Fx.configuration.dump_functions_at_beginning_of_schema = false
47
+ end
48
+
49
+ it "does not dump a create_function for aggregates in the database" do
50
+ sql_definition = <<-EOS
51
+ CREATE OR REPLACE FUNCTION test(text, text)
52
+ RETURNS text AS $$
53
+ BEGIN
54
+ RETURN 'test';
55
+ END;
56
+ $$ LANGUAGE plpgsql;
57
+ EOS
58
+
59
+ aggregate_sql_definition = <<-EOS
60
+ CREATE AGGREGATE aggregate_test(text)
61
+ (
62
+ sfunc = test,
63
+ stype = text
64
+ );
65
+ EOS
66
+
13
67
  connection.create_function :test, sql_definition: sql_definition
68
+ connection.execute aggregate_sql_definition
14
69
  stream = StringIO.new
15
70
 
16
71
  ActiveRecord::SchemaDumper.dump(connection, stream)
@@ -18,5 +73,6 @@ describe Fx::SchemaDumper::Function, :db do
18
73
  output = stream.string
19
74
  expect(output).to include "create_function :test, sql_definition: <<-SQL"
20
75
  expect(output).to include "RETURN 'test';"
76
+ expect(output).not_to include "aggregate_test"
21
77
  end
22
78
  end
@@ -13,6 +13,18 @@ describe Fx::Generators::FunctionGenerator, :generator do
13
13
  expect(migration_file(migration)).to contain "CreateFunctionTest"
14
14
  end
15
15
 
16
+ context "when passed --no-migration" do
17
+ it "creates a only function definition file" do
18
+ migration = file("db/migrate/create_function_test.rb")
19
+ function_definition = file("db/functions/test_v01.sql")
20
+
21
+ run_generator ["test", "--no-migration"]
22
+
23
+ expect(function_definition).to exist
24
+ expect(migration_file(migration)).not_to exist
25
+ end
26
+ end
27
+
16
28
  it "updates an existing function" do
17
29
  with_function_definition(
18
30
  name: "test",
@@ -14,6 +14,18 @@ describe Fx::Generators::TriggerGenerator, :generator do
14
14
  expect(migration_file(migration)).to contain "on: :some_table"
15
15
  end
16
16
 
17
+ context "when passed --no-migration" do
18
+ it "creates a only trigger definition file" do
19
+ migration = file("db/migrate/create_trigger_test.rb")
20
+ trigger_definition = file("db/triggers/test_v01.sql")
21
+
22
+ run_generator ["test", {"table_name" => "some_table"}, "--no-migration"]
23
+
24
+ expect(trigger_definition).to exist
25
+ expect(migration_file(migration)).not_to exist
26
+ end
27
+ end
28
+
17
29
  it "supports naming the table as `on` aswell as `table_name`" do
18
30
  migration = file("db/migrate/create_trigger_test.rb")
19
31
  trigger_definition = file("db/triggers/test_v01.sql")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Teo Ljungberg
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-08 00:00:00.000000000 Z
11
+ date: 2020-12-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: appraisal
@@ -191,12 +191,12 @@ files:
191
191
  - ".hound.yml"
192
192
  - ".rspec"
193
193
  - ".rubocop.yml"
194
- - ".ruby-version"
195
194
  - ".travis.yml"
196
195
  - ".yardopts"
197
196
  - Appraisals
198
197
  - CONTRIBUTING.md
199
198
  - Gemfile
199
+ - LICENSE
200
200
  - README.md
201
201
  - Rakefile
202
202
  - bin/appraisal
@@ -206,16 +206,12 @@ files:
206
206
  - bin/setup
207
207
  - bin/yard
208
208
  - fx.gemspec
209
- - gemfiles/rails40.gemfile
210
- - gemfiles/rails40.gemfile.lock
211
- - gemfiles/rails41.gemfile
212
- - gemfiles/rails41.gemfile.lock
213
209
  - gemfiles/rails42.gemfile
214
- - gemfiles/rails42.gemfile.lock
215
210
  - gemfiles/rails50.gemfile
216
- - gemfiles/rails50.gemfile.lock
211
+ - gemfiles/rails51.gemfile
212
+ - gemfiles/rails52.gemfile
213
+ - gemfiles/rails60.gemfile
217
214
  - gemfiles/rails_edge.gemfile
218
- - gemfiles/rails_edge.gemfile.lock
219
215
  - lib/fx.rb
220
216
  - lib/fx/adapters/postgres.rb
221
217
  - lib/fx/adapters/postgres/connection.rb
@@ -228,6 +224,7 @@ files:
228
224
  - lib/fx/configuration.rb
229
225
  - lib/fx/definition.rb
230
226
  - lib/fx/function.rb
227
+ - lib/fx/railtie.rb
231
228
  - lib/fx/schema_dumper.rb
232
229
  - lib/fx/schema_dumper/function.rb
233
230
  - lib/fx/schema_dumper/trigger.rb
@@ -286,13 +283,13 @@ homepage: https://github.com/teoljungberg/fx
286
283
  licenses:
287
284
  - MIT
288
285
  metadata: {}
289
- post_install_message:
286
+ post_install_message:
290
287
  rdoc_options: []
291
288
  require_paths:
292
289
  - lib
293
290
  required_ruby_version: !ruby/object:Gem::Requirement
294
291
  requirements:
295
- - - "~>"
292
+ - - ">="
296
293
  - !ruby/object:Gem::Version
297
294
  version: '2.1'
298
295
  required_rubygems_version: !ruby/object:Gem::Requirement
@@ -301,9 +298,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
301
298
  - !ruby/object:Gem::Version
302
299
  version: '0'
303
300
  requirements: []
304
- rubyforge_project:
305
- rubygems_version: 2.5.2.1
306
- signing_key:
301
+ rubygems_version: 3.2.2
302
+ signing_key:
307
303
  specification_version: 4
308
304
  summary: Support for database functions and triggers in Rails migrations
309
305
  test_files: