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
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