fx 0.3.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
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: