pg_trunk 0.1.0 → 0.2.0

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +4 -15
  3. data/CHANGELOG.md +21 -0
  4. data/README.md +3 -1
  5. data/lib/pg_trunk/core/operation/attributes.rb +1 -1
  6. data/lib/pg_trunk/core/railtie/custom_types.rb +5 -6
  7. data/lib/pg_trunk/operations/check_constraints/add_check_constraint.rb +42 -33
  8. data/lib/pg_trunk/operations/check_constraints/drop_check_constraint.rb +51 -40
  9. data/lib/pg_trunk/operations/check_constraints/rename_check_constraint.rb +39 -30
  10. data/lib/pg_trunk/operations/check_constraints/validate_check_constraint.rb +28 -21
  11. data/lib/pg_trunk/operations/composite_types/change_composite_type.rb +59 -50
  12. data/lib/pg_trunk/operations/composite_types/create_composite_type.rb +23 -19
  13. data/lib/pg_trunk/operations/composite_types/drop_composite_type.rb +48 -43
  14. data/lib/pg_trunk/operations/composite_types/rename_composite_type.rb +15 -12
  15. data/lib/pg_trunk/operations/domains/change_domain.rb +53 -47
  16. data/lib/pg_trunk/operations/domains/create_domain.rb +28 -25
  17. data/lib/pg_trunk/operations/domains/drop_domain.rb +50 -41
  18. data/lib/pg_trunk/operations/domains/rename_domain.rb +17 -12
  19. data/lib/pg_trunk/operations/enums/change_enum.rb +37 -32
  20. data/lib/pg_trunk/operations/enums/create_enum.rb +23 -20
  21. data/lib/pg_trunk/operations/enums/drop_enum.rb +50 -39
  22. data/lib/pg_trunk/operations/enums/rename_enum.rb +17 -12
  23. data/lib/pg_trunk/operations/foreign_keys/add_foreign_key.rb +58 -49
  24. data/lib/pg_trunk/operations/foreign_keys/drop_foreign_key.rb +57 -48
  25. data/lib/pg_trunk/operations/foreign_keys/rename_foreign_key.rb +38 -29
  26. data/lib/pg_trunk/operations/functions/change_function.rb +53 -47
  27. data/lib/pg_trunk/operations/functions/create_function.rb +75 -64
  28. data/lib/pg_trunk/operations/functions/drop_function.rb +78 -65
  29. data/lib/pg_trunk/operations/functions/rename_function.rb +29 -22
  30. data/lib/pg_trunk/operations/materialized_views/change_materialized_view.rb +65 -55
  31. data/lib/pg_trunk/operations/materialized_views/create_materialized_view.rb +82 -71
  32. data/lib/pg_trunk/operations/materialized_views/drop_materialized_view.rb +59 -46
  33. data/lib/pg_trunk/operations/materialized_views/refresh_materialized_view.rb +29 -24
  34. data/lib/pg_trunk/operations/materialized_views/rename_materialized_view.rb +29 -22
  35. data/lib/pg_trunk/operations/procedures/change_procedure.rb +53 -46
  36. data/lib/pg_trunk/operations/procedures/create_procedure.rb +63 -52
  37. data/lib/pg_trunk/operations/procedures/drop_procedure.rb +56 -45
  38. data/lib/pg_trunk/operations/procedures/rename_procedure.rb +29 -22
  39. data/lib/pg_trunk/operations/rules/base.rb +77 -0
  40. data/lib/pg_trunk/operations/rules/create_rule.rb +155 -0
  41. data/lib/pg_trunk/operations/rules/drop_rule.rb +94 -0
  42. data/lib/pg_trunk/operations/rules/rename_rule.rb +62 -0
  43. data/lib/pg_trunk/operations/rules.rb +13 -0
  44. data/lib/pg_trunk/operations/sequences/base.rb +79 -0
  45. data/lib/pg_trunk/operations/sequences/change_sequence.rb +142 -0
  46. data/lib/pg_trunk/operations/sequences/create_sequence.rb +180 -0
  47. data/lib/pg_trunk/operations/sequences/drop_sequence.rb +82 -0
  48. data/lib/pg_trunk/operations/sequences/rename_sequence.rb +64 -0
  49. data/lib/pg_trunk/operations/sequences.rb +14 -0
  50. data/lib/pg_trunk/operations/statistics/create_statistics.rb +67 -56
  51. data/lib/pg_trunk/operations/statistics/drop_statistics.rb +64 -53
  52. data/lib/pg_trunk/operations/statistics/rename_statistics.rb +18 -13
  53. data/lib/pg_trunk/operations/triggers/change_trigger.rb +23 -18
  54. data/lib/pg_trunk/operations/triggers/create_trigger.rb +63 -54
  55. data/lib/pg_trunk/operations/triggers/drop_trigger.rb +55 -46
  56. data/lib/pg_trunk/operations/triggers/rename_trigger.rb +51 -48
  57. data/lib/pg_trunk/operations/views/change_view.rb +47 -38
  58. data/lib/pg_trunk/operations/views/create_view.rb +56 -45
  59. data/lib/pg_trunk/operations/views/drop_view.rb +59 -46
  60. data/lib/pg_trunk/operations/views/rename_view.rb +27 -20
  61. data/lib/pg_trunk/operations.rb +2 -0
  62. data/lib/pg_trunk/version.rb +1 -1
  63. data/pg_trunk.gemspec +0 -1
  64. data/spec/operations/rules/create_rule_spec.rb +119 -0
  65. data/spec/operations/rules/drop_rule_spec.rb +117 -0
  66. data/spec/operations/rules/rename_rule_spec.rb +148 -0
  67. data/spec/operations/sequences/change_sequence_spec.rb +134 -0
  68. data/spec/operations/sequences/create_sequence_spec.rb +156 -0
  69. data/spec/operations/sequences/drop_sequence_spec.rb +102 -0
  70. data/spec/operations/sequences/rename_sequence_spec.rb +100 -0
  71. metadata +22 -68
@@ -1,44 +1,53 @@
1
1
  # frozen_string_literal: false
2
2
 
3
- # @!method ActiveRecord::Migration#change_view(name, **options, &block)
4
- # Modify a view
5
- #
6
- # @param [#to_s] name (nil) The qualified name of the view
7
- # @option [Boolean] :if_exists (false) Suppress the error when the view is absent
8
- # @yield [Proc] the block with the view's definition
9
- # @yieldparam The receiver of methods specifying the view
10
- #
11
- # The operation replaces the view with a new definition(s):
12
- #
13
- # change_view "admin_users" do |v|
14
- # v.sql_definition: <<~SQL, from: <<~SQL
15
- # SELECT id, name FROM users WHERE admin;
16
- # SQL
17
- # SELECT * FROM users WHERE admin;
18
- # SQL
3
+ # @!parse
4
+ # class ActiveRecord::Migration
5
+ # # Modify a view
6
+ # #
7
+ # # @param [#to_s] name (nil) The qualified name of the view
8
+ # # @option options [Boolean] :if_exists (false) Suppress the error when the view is absent
9
+ # # @yield [v] the block with the view's definition
10
+ # # @yieldparam Object receiver of methods specifying the view
11
+ # # @return [void]
12
+ # #
13
+ # # The operation replaces the view with a new definition(s):
14
+ # #
15
+ # # ```ruby
16
+ # # change_view "admin_users" do |v|
17
+ # # v.sql_definition: <<~SQL, from: <<~SQL
18
+ # # SELECT id, name FROM users WHERE admin;
19
+ # # SQL
20
+ # # SELECT * FROM users WHERE admin;
21
+ # # SQL
22
+ # # end
23
+ # # ```
24
+ # #
25
+ # # For some compatibility to the `scenic` gem, we also support
26
+ # # adding a definition via its version:
27
+ # #
28
+ # # ```ruby
29
+ # # change_view "admin_users" do |v|
30
+ # # v.version 2, from: 1
31
+ # # end
32
+ # # ```
33
+ # #
34
+ # # It is expected, that both `db/views/admin_users_v01.sql`
35
+ # # and `db/views/admin_users_v02.sql` to contain SQL snippets.
36
+ # #
37
+ # # Please, notice that neither deletion of columns,
38
+ # # nor changing their types is supported by the PostgreSQL.
39
+ # #
40
+ # # You can also (re)set a comment describing the view,
41
+ # # and the check option (either `:local` or `:cascaded`):
42
+ # #
43
+ # # ```ruby
44
+ # # change_view "admin_users" do |v|
45
+ # # v.check :local, from: :cascaded
46
+ # # v.comment "Admin users only", from: ""
47
+ # # end
48
+ # # ```
49
+ # def change_view(name, **options, &block); end
19
50
  # end
20
- #
21
- # For some compatibility to the `scenic` gem, we also support
22
- # adding a definition via its version:
23
- #
24
- # change_view "admin_users" do |v|
25
- # v.version 2, from: 1
26
- # end
27
- #
28
- # It is expected, that both `db/views/admin_users_v01.sql`
29
- # and `db/views/admin_users_v02.sql` to contain SQL snippets.
30
- #
31
- # Please, notice that neither deletion of columns,
32
- # nor changing their types is supported by the PostgreSQL.
33
- #
34
- # You can also (re)set a comment describing the view,
35
- # and the check option (either `:local` or `:cascaded`):
36
- #
37
- # change_view "admin_users" do |v|
38
- # v.check :local, from: :cascaded
39
- # v.comment "Admin users only", from: ""
40
- # end
41
-
42
51
  module PGTrunk::Operations::Views
43
52
  # @private
44
53
  class ChangeView < Base
@@ -1,51 +1,62 @@
1
1
  # frozen_string_literal: false
2
2
 
3
- # @!method ActiveRecord::Migration#create_view(name, **options, &block)
4
- # Create a view
5
- #
6
- # @param [#to_s] name (nil) The qualified name of the view
7
- # @option [Boolean] :replace_existing (false) If the view should overwrite an existing one
8
- # @option [#to_s] :sql_definition (nil) The snippet containing the query
9
- # @option [#to_i] :version (nil)
10
- # The alternative way to set sql_definition by referencing to a file containing the snippet
11
- # @option [#to_s] :check (nil) Controls the behavior of automatically updatable views
12
- # Supported values: :local, :cascaded
13
- # @option [#to_s] :comment (nil) The comment describing the view
14
- # @yield [Proc] the block with the view's definition
15
- # @yieldparam The receiver of methods specifying the view
16
- #
17
- # The operation creates the view using its `sql_definition`:
18
- #
19
- # create_view("views.admin_users", sql_definition: <<~SQL)
20
- # SELECT id, name FROM users WHERE admin;
21
- # SQL
22
- #
23
- # For compatibility to the `scenic` gem, we also support
24
- # adding a definition via its version:
25
- #
26
- # create_view "admin_users", version: 1
27
- #
28
- # It is expected, that a `db/views/admin_users_v01.sql`
29
- # to contain the SQL snippet.
30
- #
31
- # You can also set a comment describing the view, and the check option
32
- # (either `:local` or `:cascaded`):
33
- #
34
- # create_view "admin_users" do |v|
35
- # v.sql_definition "SELECT id, name FROM users WHERE admin;"
36
- # v.check :local
37
- # v.comment "Admin users only"
3
+ # @!parse
4
+ # class ActiveRecord::Migration
5
+ # # Create a view
6
+ # #
7
+ # # @param [#to_s] name (nil) The qualified name of the view
8
+ # # @option options [Boolean] :replace_existing (false) If the view should overwrite an existing one
9
+ # # @option options [#to_s] :sql_definition (nil) The snippet containing the query
10
+ # # @option options [#to_i] :version (nil)
11
+ # # The alternative way to set sql_definition by referencing to a file containing the snippet
12
+ # # @option options [#to_s] :check (nil) Controls the behavior of automatically updatable views
13
+ # # Supported values: :local, :cascaded
14
+ # # @option options [#to_s] :comment (nil) The comment describing the view
15
+ # # @yield [v] the block with the view's definition
16
+ # # @yieldparam Object receiver of methods specifying the view
17
+ # # @return [void]
18
+ # #
19
+ # # The operation creates the view using its `sql_definition`:
20
+ # #
21
+ # # ```ruby
22
+ # # create_view("views.admin_users", sql_definition: <<~SQL)
23
+ # # SELECT id, name FROM users WHERE admin;
24
+ # # SQL
25
+ # # ```
26
+ # #
27
+ # # For compatibility to the `scenic` gem, we also support
28
+ # # adding a definition via its version:
29
+ # #
30
+ # # ```ruby
31
+ # # create_view "admin_users", version: 1
32
+ # # ```
33
+ # #
34
+ # # It is expected, that a `db/views/admin_users_v01.sql`
35
+ # # to contain the SQL snippet.
36
+ # #
37
+ # # You can also set a comment describing the view, and the check option
38
+ # # (either `:local` or `:cascaded`):
39
+ # #
40
+ # # ```ruby
41
+ # # create_view "admin_users" do |v|
42
+ # # v.sql_definition "SELECT id, name FROM users WHERE admin;"
43
+ # # v.check :local
44
+ # # v.comment "Admin users only"
45
+ # # end
46
+ # # ```
47
+ # #
48
+ # # With the `replace_existing: true` option the operation
49
+ # # would use `CREATE OR REPLACE VIEW` command, so it
50
+ # # can be used to "update" (or reload) the existing view.
51
+ # #
52
+ # # ```ruby
53
+ # # create_view "admin_users", version: 1, replace_existing: true
54
+ # # ```
55
+ # #
56
+ # # This option makes an operation irreversible due to uncertainty
57
+ # # of the previous state of the database.
58
+ # def create_view(name, **options, &block); end
38
59
  # end
39
- #
40
- # With the `replace_existing: true` option the operation
41
- # would use `CREATE OR REPLACE VIEW` command, so it
42
- # can be used to "update" (or reload) the existing view.
43
- #
44
- # create_view "admin_users", version: 1, replace_existing: true
45
- #
46
- # This option makes an operation irreversible due to uncertainty
47
- # of the previous state of the database.
48
-
49
60
  module PGTrunk::Operations::Views
50
61
  # @private
51
62
  class CreateView < Base
@@ -1,52 +1,65 @@
1
1
  # frozen_string_literal: false
2
2
 
3
- # @!method ActiveRecord::Migration#drop_view(name, **options, &block)
4
- # Drop a view
5
- #
6
- # @param [#to_s] name (nil) The qualified name of the view
7
- # @option [Boolean] :replace_existing (false) If the view should overwrite an existing one
8
- # @option [Boolean] :if_exists (false) Suppress the error when the view is absent
9
- # @option [Symbol] :force (:restrict) How to process dependent objects (`:cascade` or `:restrict`)
10
- # @option [#to_s] :sql_definition (nil) The snippet containing the query
11
- # @option [#to_i] :revert_to_version (nil)
12
- # The alternative way to set sql_definition by referencing to a file containing the snippet
13
- # @option [#to_s] :check (nil) Controls the behavior of automatically updatable views
14
- # Supported values: :local, :cascaded
15
- # @option [#to_s] :comment (nil) The comment describing the view
16
- # @yield [Proc] the block with the view's definition
17
- # @yieldparam The receiver of methods specifying the view
18
- #
19
- # The operation drops the existing view identified by its
20
- # qualified name (it can include a schema).
21
- #
22
- # drop_view "views.admin_users"
23
- #
24
- # To make the operation invertible, use the same options
25
- # as in the `create_view` operation.
26
- #
27
- # drop_view "views.admin_users" do |v|
28
- # v.sql_definition "SELECT name, email FROM users WHERE admin;"
29
- # v.check :local
30
- # v.comment "Admin users only"
3
+ # @!parse
4
+ # class ActiveRecord::Migration
5
+ # # Drop a view
6
+ # #
7
+ # # @param [#to_s] name (nil) The qualified name of the view
8
+ # # @option options [Boolean] :replace_existing (false) If the view should overwrite an existing one
9
+ # # @option options [Boolean] :if_exists (false) Suppress the error when the view is absent
10
+ # # @option options [Symbol] :force (:restrict) How to process dependent objects (`:cascade` or `:restrict`)
11
+ # # @option options [#to_s] :sql_definition (nil) The snippet containing the query
12
+ # # @option options [#to_i] :revert_to_version (nil)
13
+ # # The alternative way to set sql_definition by referencing to a file containing the snippet
14
+ # # @option options [#to_s] :check (nil) Controls the behavior of automatically updatable views
15
+ # # Supported values: :local, :cascaded
16
+ # # @option options [#to_s] :comment (nil) The comment describing the view
17
+ # # @yield [v] the block with the view's definition
18
+ # # @yieldparam Object receiver of methods specifying the view
19
+ # # @return [void]
20
+ # #
21
+ # # The operation drops the existing view identified by its
22
+ # # qualified name (it can include a schema).
23
+ # #
24
+ # # ```ruby
25
+ # # drop_view "views.admin_users"
26
+ # # ```
27
+ # #
28
+ # # To make the operation invertible, use the same options
29
+ # # as in the `create_view` operation.
30
+ # #
31
+ # # ```ruby
32
+ # # drop_view "views.admin_users" do |v|
33
+ # # v.sql_definition "SELECT name, email FROM users WHERE admin;"
34
+ # # v.check :local
35
+ # # v.comment "Admin users only"
36
+ # # end
37
+ # # ```
38
+ # #
39
+ # # You can also use a version-base SQL definition like:
40
+ # #
41
+ # # ```ruby
42
+ # # drop_view "views.admin_users", revert_to_version: 1
43
+ # # ```
44
+ # #
45
+ # # With the `force: :cascade` option the operation would remove
46
+ # # all the objects which depend on the view.
47
+ # #
48
+ # # ```ruby
49
+ # # drop_view "views.admin_users", force: :cascade
50
+ # # ```
51
+ # #
52
+ # # With the `if_exists: true` option the operation won't fail
53
+ # # even when the view was absent in the database.
54
+ # #
55
+ # # ```ruby
56
+ # # drop_view "views.admin_users", if_exists: true
57
+ # # ```
58
+ # #
59
+ # # Both options make an operation irreversible due to uncertainty
60
+ # # of the previous state of the database.
61
+ # def drop_view(name, **options, &block); end
31
62
  # end
32
- #
33
- # You can also use a version-base SQL definition like:
34
- #
35
- # drop_view "views.admin_users", revert_to_version: 1
36
- #
37
- # With the `force: :cascade` option the operation would remove
38
- # all the objects which depend on the view.
39
- #
40
- # drop_view "views.admin_users", force: :cascade
41
- #
42
- # With the `if_exists: true` option the operation won't fail
43
- # even when the view was absent in the database.
44
- #
45
- # drop_view "views.admin_users", if_exists: true
46
- #
47
- # Both options make an operation irreversible due to uncertainty
48
- # of the previous state of the database.
49
-
50
63
  module PGTrunk::Operations::Views
51
64
  # @private
52
65
  class DropView < Base
@@ -1,25 +1,32 @@
1
1
  # frozen_string_literal: false
2
2
 
3
- # @!method ActiveRecord::Migration#rename_view(name, **options)
4
- # Change the name and/or schema of a view
5
- #
6
- # @param [#to_s] :name (nil) The qualified name of the view
7
- # @option [#to_s] :to (nil) The new qualified name for the view
8
- # @option [Boolean] :if_exists (false) Suppress the error when the view is absent
9
- #
10
- # A view can be renamed by changing both the name
11
- # and the schema (namespace) it belongs to.
12
- #
13
- # rename_view "views.admin_users", to: "admins"
14
- #
15
- # With the `if_exists: true` option, the operation won't fail
16
- # even when the view wasn't existed.
17
- #
18
- # rename_view "views.admin_users", to: "admins", if_exists: true
19
- #
20
- # At the same time, the option makes a view irreversible
21
- # due to uncertainty of the previous state of the database.
22
-
3
+ # @!parse
4
+ # class ActiveRecord::Migration
5
+ # # Change the name and/or schema of a view
6
+ # #
7
+ # # @param [#to_s] :name (nil) The qualified name of the view
8
+ # # @option options [#to_s] :to (nil) The new qualified name for the view
9
+ # # @option options [Boolean] :if_exists (false) Suppress the error when the view is absent
10
+ # # @return [void]
11
+ # #
12
+ # # A view can be renamed by changing both the name
13
+ # # and the schema (namespace) it belongs to.
14
+ # #
15
+ # # ```ruby
16
+ # # rename_view "views.admin_users", to: "admins"
17
+ # # ```
18
+ # #
19
+ # # With the `if_exists: true` option, the operation won't fail
20
+ # # even when the view wasn't existed.
21
+ # #
22
+ # # ```ruby
23
+ # # rename_view "views.admin_users", to: "admins", if_exists: true
24
+ # # ```
25
+ # #
26
+ # # At the same time, the option makes a view irreversible
27
+ # # due to uncertainty of the previous state of the database.
28
+ # def rename_view(name, **options); end
29
+ # end
23
30
  module PGTrunk::Operations::Views
24
31
  # @private
25
32
  class RenameView < Base
@@ -9,6 +9,7 @@ module PGTrunk
9
9
  require_relative "operations/enums"
10
10
  require_relative "operations/composite_types"
11
11
  require_relative "operations/domains"
12
+ require_relative "operations/sequences"
12
13
  require_relative "operations/tables"
13
14
  require_relative "operations/views"
14
15
  require_relative "operations/materialized_views"
@@ -18,6 +19,7 @@ module PGTrunk
18
19
  require_relative "operations/foreign_keys"
19
20
  require_relative "operations/procedures"
20
21
  require_relative "operations/triggers"
22
+ require_relative "operations/rules"
21
23
  require_relative "operations/statistics"
22
24
  end
23
25
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module PGTrunk
4
4
  # @private
5
- VERSION = "0.1.0"
5
+ VERSION = "0.2.0"
6
6
  end
data/pg_trunk.gemspec CHANGED
@@ -23,7 +23,6 @@ Gem::Specification.new do |spec|
23
23
  spec.metadata["rubygems_mfa_required"] = "true"
24
24
 
25
25
  spec.files = `git ls-files -z`.split("\x0")
26
- spec.test_files = spec.files.grep(%r{^spec/})
27
26
  spec.require_paths = ["lib"]
28
27
 
29
28
  spec.add_dependency "activerecord", ">= 4.0.0"
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#create_rule" do
4
+ before_all do
5
+ run_migration <<~RUBY
6
+ create_table :users do |t|
7
+ t.string :name
8
+ end
9
+
10
+ create_table :user_updates do |t|
11
+ t.timestamps
12
+ end
13
+ RUBY
14
+ end
15
+
16
+ context "with a minimal definition" do
17
+ let(:migration) do
18
+ <<~RUBY
19
+ create_rule "users", "prevent_insertion" do |r|
20
+ r.event :insert
21
+ r.kind :instead
22
+ r.comment "Prevent insertion to users"
23
+ end
24
+ RUBY
25
+ end
26
+
27
+ its(:execution) { is_expected.to insert(migration).into_schema }
28
+ its(:inversion) { is_expected.not_to change_schema }
29
+ end
30
+
31
+ context "with a commands definition" do
32
+ let(:migration) do
33
+ <<~RUBY
34
+ create_rule "users", "count_updates" do |r|
35
+ r.event :update
36
+ r.command <<~Q.chomp
37
+ INSERT INTO user_updates (created_at) VALUES (now())
38
+ Q
39
+ r.comment "Count updates of users"
40
+ end
41
+ RUBY
42
+ end
43
+
44
+ its(:execution) { is_expected.to insert(migration).into_schema }
45
+ its(:inversion) { is_expected.not_to change_schema }
46
+ end
47
+
48
+ context "without an explicit name of the rule" do
49
+ let(:migration) do
50
+ <<~RUBY
51
+ create_rule "users" do |r|
52
+ r.event :update
53
+ r.command <<~Q.chomp
54
+ INSERT INTO user_updates (created_at) VALUES (now())
55
+ Q
56
+ r.comment "Count updates of users"
57
+ end
58
+ RUBY
59
+ end
60
+
61
+ its(:execution) { is_expected.to insert(migration).into_schema }
62
+ its(:inversion) { is_expected.not_to change_schema }
63
+ end
64
+
65
+ context "with the `replace_existing: true` option" do
66
+ let(:migration) do
67
+ <<~RUBY
68
+ create_rule "users", replace_existing: true do |r|
69
+ r.event :update
70
+ r.command <<~Q.chomp
71
+ INSERT INTO user_updates (created_at) VALUES (now());
72
+ Q
73
+ r.comment "Count updates of users"
74
+ end
75
+ RUBY
76
+ end
77
+ let(:snippet) do
78
+ <<~RUBY
79
+ create_rule "users" do |r|
80
+ r.event :update
81
+ r.command <<~Q.chomp
82
+ INSERT INTO user_updates (created_at) VALUES (now())
83
+ Q
84
+ r.comment "Count updates of users"
85
+ end
86
+ RUBY
87
+ end
88
+
89
+ its(:execution) { is_expected.to insert(snippet).into_schema }
90
+ it { is_expected.to be_irreversible.because_of(/replace_existing: true/i) }
91
+ end
92
+
93
+ context "without an event" do
94
+ let(:migration) do
95
+ <<~RUBY
96
+ create_rule "users" do |r|
97
+ r.kind :instead
98
+ r.comment "Prevent insertion to users"
99
+ end
100
+ RUBY
101
+ end
102
+
103
+ it { is_expected.to fail_validation.because(/event can't be blank/i) }
104
+ end
105
+
106
+ context "without a table" do
107
+ let(:migration) do
108
+ <<~RUBY
109
+ create_rule do |r|
110
+ r.event :insert
111
+ r.kind :instead
112
+ r.comment "Prevent insertion to users"
113
+ end
114
+ RUBY
115
+ end
116
+
117
+ it { is_expected.to fail_validation.because(/table can't be blank/i) }
118
+ end
119
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#drop_rule" do
4
+ before_all do
5
+ run_migration <<~RUBY
6
+ create_table :users do |t|
7
+ t.string :name
8
+ end
9
+ RUBY
10
+ end
11
+ before { run_migration(snippet) }
12
+
13
+ context "when a rule was named" do
14
+ let(:snippet) do
15
+ <<~RUBY
16
+ create_rule "users", "prevent_insertion" do |r|
17
+ r.event :insert
18
+ r.kind :instead
19
+ r.comment "Prevent insertion to users"
20
+ end
21
+ RUBY
22
+ end
23
+
24
+ context "with a full definition" do
25
+ let(:migration) do
26
+ <<~RUBY
27
+ drop_rule "users", "prevent_insertion" do |r|
28
+ r.event :insert
29
+ r.kind :instead
30
+ r.comment "Prevent insertion to users"
31
+ end
32
+ RUBY
33
+ end
34
+
35
+ its(:execution) { is_expected.to remove(snippet).from_schema }
36
+ its(:inversion) { is_expected.not_to change_schema }
37
+ end
38
+
39
+ context "with a name only" do
40
+ let(:migration) do
41
+ <<~RUBY
42
+ drop_rule "users", "prevent_insertion"
43
+ RUBY
44
+ end
45
+
46
+ its(:execution) { is_expected.to remove(snippet).from_schema }
47
+ it { is_expected.to be_irreversible.because(/event can't be blank/i) }
48
+ end
49
+
50
+ context "with if_exists: true option" do
51
+ let(:migration) do
52
+ <<~RUBY
53
+ drop_rule "users", "prevent_insertion", if_exists: true do |r|
54
+ r.event :insert
55
+ r.kind :instead
56
+ r.comment "Prevent insertion to users"
57
+ end
58
+ RUBY
59
+ end
60
+
61
+ its(:execution) { is_expected.to remove(snippet).from_schema }
62
+ it { is_expected.to be_irreversible.because_of(/if_exists: true/i) }
63
+ end
64
+
65
+ context "with force: :cascade option" do
66
+ let(:migration) do
67
+ <<~RUBY
68
+ drop_rule "users", "prevent_insertion", force: :cascade do |r|
69
+ r.event :insert
70
+ r.kind :instead
71
+ r.comment "Prevent insertion to users"
72
+ end
73
+ RUBY
74
+ end
75
+
76
+ its(:execution) { is_expected.to remove(snippet).from_schema }
77
+ it { is_expected.to be_irreversible.because_of(/force: :cascade/i) }
78
+ end
79
+ end
80
+
81
+ context "when a rule was anonymous" do
82
+ let(:snippet) do
83
+ <<~RUBY
84
+ create_rule "users" do |r|
85
+ r.event :insert
86
+ r.kind :instead
87
+ r.comment "Prevent insertion to users"
88
+ end
89
+ RUBY
90
+ end
91
+
92
+ context "with a full definition" do
93
+ let(:migration) do
94
+ <<~RUBY
95
+ drop_rule "users" do |r|
96
+ r.event :insert
97
+ r.kind :instead
98
+ r.comment "Prevent insertion to users"
99
+ end
100
+ RUBY
101
+ end
102
+
103
+ its(:execution) { is_expected.to remove(snippet).from_schema }
104
+ its(:inversion) { is_expected.not_to change_schema }
105
+ end
106
+
107
+ context "with a table only" do
108
+ let(:migration) do
109
+ <<~RUBY
110
+ drop_rule "users"
111
+ RUBY
112
+ end
113
+
114
+ it { is_expected.to fail_validation.because(/name can't be blank/i) }
115
+ end
116
+ end
117
+ end