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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c933b484808a6a2a99f4a0faf84a20ea685f17bf
4
- data.tar.gz: 6f38a705c1017b72f0930310f7a2dd9d3e273813
2
+ SHA256:
3
+ metadata.gz: 1e391283922484cf5a5b06c7de944c3162a85b7b49546f1fd1a017a6fc762898
4
+ data.tar.gz: 73b179c85ebc70977bf4d2afbe45088284a9730138c2a424d940d72fb2ab0a30
5
5
  SHA512:
6
- metadata.gz: '018b13261d9eabe7f6e79e36bcf4cf765956d409789d6d1c37255976c883a8f9fb4f4a2c5be960ebb283d5d436f9ea0e651309e635f531c0b74b9e88041cb6cd'
7
- data.tar.gz: 12a8a67b11f91f717c75d4fe7a6a98b400501bf5f3914dc8088ecc8abf07ab2a08f45c012eba6578cd9f97cf32cf106a0add7db2fdfcc8d816e705cfcd445187
6
+ metadata.gz: 144fcbf71aad79383ee1ae98047b6fc018212ecd3ea2d747f883d7def2c649590b1d04fc77b0a84113fa832a6a0377857cf8482d2290c0c40f6496be0e6db470
7
+ data.tar.gz: 057f585e7e83a89aebfda256605813a7db9e7d01b5841f436be0a9f7323540fd5a49999e4bf3bbc6db33ab0342e4ad0727b1b9b16690ed3a1aea00ea3011ea2d
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  /.bundle/
2
+ /gemfiles/*.gemfile.lock
2
3
  /.yardoc
3
4
  /Gemfile.lock
4
5
  /_yardoc/
@@ -1,5 +1,9 @@
1
1
  addons:
2
- postgresql: "9.4"
2
+ postgresql: "10"
3
+ apt:
4
+ packages:
5
+ - postgresql-10
6
+ - postgresql-client-10
3
7
  before_install:
4
8
  - "echo '--colour' > ~/.rspec"
5
9
  - "echo 'gem: --no-document' > ~/.gemrc"
@@ -15,18 +19,21 @@ language:
15
19
  notifications:
16
20
  email: false
17
21
  rvm:
18
- - 2.3.1
19
- - 2.2.6
20
- - 2.1.10
22
+ - 2.6
23
+ - 2.5
24
+ - 2.4
21
25
  gemfile:
22
- - gemfiles/rails40.gemfile
23
- - gemfiles/rails41.gemfile
24
26
  - gemfiles/rails42.gemfile
25
27
  - gemfiles/rails50.gemfile
28
+ - gemfiles/rails51.gemfile
29
+ - gemfiles/rails52.gemfile
30
+ - gemfiles/rails60.gemfile
26
31
  - gemfiles/rails_edge.gemfile
27
32
  matrix:
28
33
  allow_failures:
29
34
  - gemfile: gemfiles/rails_edge.gemfile
30
35
  exclude:
31
- - rvm: 2.1.10
32
- gemfile: gemfiles/rails50.gemfile
36
+ - rvm: 2.4
37
+ gemfile: gemfiles/rails_edge.gemfile
38
+ - rvm: 2.4
39
+ gemfile: gemfiles/rails60.gemfile
data/Appraisals CHANGED
@@ -1,16 +1,7 @@
1
- appraise "rails40" do
2
- gem "activerecord", "~> 4.0.0"
3
- gem "railties", "~> 4.0.0"
4
- end
5
-
6
- appraise "rails41" do
7
- gem "activerecord", "~> 4.1.0"
8
- gem "railties", "~> 4.1.0"
9
- end
10
-
11
1
  appraise "rails42" do
12
2
  gem "activerecord", "~> 4.2.0"
13
3
  gem "railties", "~> 4.2.0"
4
+ gem "pg", "~> 0.15"
14
5
  end
15
6
 
16
7
  if RUBY_VERSION > "2.2.0"
@@ -19,6 +10,21 @@ if RUBY_VERSION > "2.2.0"
19
10
  gem "railties", "~> 5.0"
20
11
  end
21
12
 
13
+ appraise "rails51" do
14
+ gem "activerecord", "~> 5.1"
15
+ gem "railties", "~> 5.1"
16
+ end
17
+
18
+ appraise "rails52" do
19
+ gem "activerecord", "~> 5.2"
20
+ gem "railties", "~> 5.2"
21
+ end
22
+
23
+ appraise "rails60" do
24
+ gem "activerecord", "~> 6.0"
25
+ gem "railties", "~> 6.0"
26
+ end
27
+
22
28
  appraise "rails-edge" do
23
29
  gem "rails", github: "rails/rails"
24
30
  gem "arel", :github => "rails/arel"
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright 2016 Teo Ljungberg
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -21,18 +21,29 @@ F(x) ships with support for PostgreSQL. The adapter is configurable (see
21
21
 
22
22
  ## Great, how do I create a trigger and a function?
23
23
 
24
- You've got this great idea for a trigger you'd like to call
24
+ You've got this great idea for a function you'd like to call
25
25
  `uppercase_users_name`. You can create the migration and the corresponding
26
26
  definition file with the following command:
27
27
 
28
28
  ```sh
29
- % rails generate fx:trigger uppercase_users_name
29
+ % rails generate fx:function uppercase_users_name
30
+ create db/functions/uppercase_users_name_v01.sql
31
+ create db/migrate/[TIMESTAMP]_create_function_uppercase_users_name.rb
32
+ ```
33
+
34
+ Edit the `db/functions/uppercase_users_name_v01.sql` file with the SQL statement
35
+ that defines your function.
36
+
37
+ Next, let's add a trigger called `uppercase_users_name` to call our new
38
+ function each time we `INSERT` on the `users` table.
39
+
40
+ ```sh
41
+ % rails generate fx:trigger uppercase_users_name table_name:users
30
42
  create db/triggers/uppercase_users_name_v01.sql
31
43
  create db/migrate/[TIMESTAMP]_create_trigger_uppercase_users_name.rb
32
44
  ```
33
45
 
34
- Edit the `db/triggers/uppercase_users_name_v01.sql` file with the SQL statement
35
- that defines your trigger. In our example, this might look something like this:
46
+ In our example, this might look something like this:
36
47
 
37
48
  ```sql
38
49
  CREATE TRIGGER uppercase_users_name
@@ -41,15 +52,6 @@ CREATE TRIGGER uppercase_users_name
41
52
  EXECUTE PROCEDURE uppercase_users_name();
42
53
  ```
43
54
 
44
- As you see, we execute a function called `uppercase_users_name` before each
45
- `INSERT` on the `users` table, which is a function we don't have yet.
46
-
47
- ```sh
48
- % rails generate fx:function uppercase_users_name
49
- create db/functions/uppercase_users_name_v01.sql
50
- create db/migrate/[TIMESTAMP]_create_function_uppercase_users_name.rb
51
- ```
52
-
53
55
  The generated migrations contains `create_function` and `create_trigger`
54
56
  statements. The migration is reversible and the schema will be dumped into your
55
57
  `schema.rb` file.
@@ -82,3 +84,30 @@ def change
82
84
  drop_function :uppercase_users_name, revert_to_version: 2
83
85
  end
84
86
  ```
87
+
88
+ ## What if I need to use a function as the default value of a column?
89
+
90
+ You need to set F(x) to dump the functions in the beginning of db/schema.rb in a
91
+ initializer:
92
+
93
+ ```ruby
94
+ # config/initializers/fx.rb
95
+ Fx.configure do |config|
96
+ config.dump_functions_at_beginning_of_schema = true
97
+ end
98
+ ```
99
+
100
+ And then you can use a lambda in your migration file:
101
+
102
+ ```ruby
103
+ create_table :my_table do |t|
104
+ t.string :my_column, default: -> { "my_function()" }
105
+ end
106
+ ```
107
+
108
+ That's how you tell Rails to use the default as a literal SQL for the default
109
+ column value instead of a plain string.
110
+
111
+ ## Contributing
112
+
113
+ See [contributing](CONTRIBUTING.md) for more details.
data/fx.gemspec CHANGED
@@ -34,5 +34,5 @@ Gem::Specification.new do |spec|
34
34
  spec.add_dependency "activerecord", '>= 4.0.0'
35
35
  spec.add_dependency "railties", '>= 4.0.0'
36
36
 
37
- spec.required_ruby_version = "~> 2.1"
37
+ spec.required_ruby_version = ">= 2.1"
38
38
  end
@@ -4,5 +4,6 @@ source "https://rubygems.org"
4
4
 
5
5
  gem "activerecord", "~> 4.2.0"
6
6
  gem "railties", "~> 4.2.0"
7
+ gem "pg", "~> 0.15"
7
8
 
8
- gemspec :path => "../"
9
+ gemspec path: "../"
@@ -5,4 +5,4 @@ source "https://rubygems.org"
5
5
  gem "activerecord", "~> 5.0"
6
6
  gem "railties", "~> 5.0"
7
7
 
8
- gemspec :path => "../"
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 5.1"
6
+ gem "railties", "~> 5.1"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 5.2"
6
+ gem "railties", "~> 5.2"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 6.0"
6
+ gem "railties", "~> 6.0"
7
+
8
+ gemspec path: "../"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", :github => "rails/rails"
6
- gem "arel", :github => "rails/arel"
5
+ gem "rails", github: "rails/rails"
6
+ gem "arel", github: "rails/arel"
7
7
 
8
- gemspec :path => "../"
8
+ gemspec path: "../"
data/lib/fx.rb CHANGED
@@ -7,10 +7,32 @@ require "fx/function"
7
7
  require "fx/statements"
8
8
  require "fx/schema_dumper"
9
9
  require "fx/trigger"
10
+ require "fx/railtie"
10
11
 
11
12
  # F(x) adds methods `ActiveRecord::Migration` to create and manage database
12
13
  # triggers and functions in Rails applications.
13
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
+
14
36
  # The current database adapter used by F(x).
15
37
  #
16
38
  # This defaults to {Fx::Adapters::Postgres} but can be overridden
@@ -125,7 +125,11 @@ module Fx
125
125
  #
126
126
  # @return [void]
127
127
  def drop_function(name)
128
- execute "DROP FUNCTION #{name}();"
128
+ if support_drop_function_without_args
129
+ execute "DROP FUNCTION #{name};"
130
+ else
131
+ execute "DROP FUNCTION #{name}();"
132
+ end
129
133
  end
130
134
 
131
135
  # Drops the trigger from the database
@@ -150,6 +154,14 @@ module Fx
150
154
  def connection
151
155
  Connection.new(connectable.connection)
152
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
153
165
  end
154
166
  end
155
167
  end
@@ -17,7 +17,10 @@ module Fx
17
17
  ON pn.oid = pp.pronamespace
18
18
  LEFT JOIN pg_depend pd
19
19
  ON pd.objid = pp.oid AND pd.deptype = 'e'
20
+ LEFT JOIN pg_aggregate pa
21
+ ON pa.aggfnoid = pp.oid
20
22
  WHERE pn.nspname = 'public' AND pd.objid IS NULL
23
+ AND pa.aggfnoid IS NULL
21
24
  ORDER BY pp.oid;
22
25
  EOS
23
26
 
@@ -22,8 +22,3 @@ module Fx
22
22
  end
23
23
  end
24
24
  end
25
-
26
- ActiveRecord::Migration::CommandRecorder.send(
27
- :include,
28
- Fx::CommandRecorder,
29
- )
@@ -17,6 +17,7 @@ module Fx
17
17
  # ```
18
18
  # Fx.configure do |config|
19
19
  # config.database = Fx::Adapters::Postgres
20
+ # config.dump_functions_at_beginning_of_schema = true
20
21
  # end
21
22
  # ```
22
23
  def self.configure
@@ -31,8 +32,17 @@ module Fx
31
32
  # @return Fx adapter
32
33
  attr_accessor :database
33
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
+
34
43
  def initialize
35
44
  @database = Fx::Adapters::Postgres.new
45
+ @dump_functions_at_beginning_of_schema = false
36
46
  end
37
47
  end
38
48
  end
@@ -8,7 +8,7 @@ module Fx
8
8
  end
9
9
 
10
10
  def to_sql
11
- File.read(full_path).tap do |content|
11
+ File.read(find_file || full_path).tap do |content|
12
12
  if content.empty?
13
13
  raise "Define #{@type} in #{path} before migrating."
14
14
  end
@@ -20,7 +20,7 @@ module Fx
20
20
  end
21
21
 
22
22
  def path
23
- File.join("db", @type.pluralize, filename)
23
+ @_path ||= File.join("db", @type.pluralize, filename)
24
24
  end
25
25
 
26
26
  def version
@@ -30,7 +30,17 @@ module Fx
30
30
  private
31
31
 
32
32
  def filename
33
- "#{@name}_v#{version}.sql"
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
+ Rails.application.config.paths["db/migrate"].expanded
34
44
  end
35
45
  end
36
46
  end
@@ -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