schema_plus_triggers 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +16 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +127 -0
- data/Rakefile +11 -0
- data/gemfiles/Gemfile.base +4 -0
- data/gemfiles/activerecord-5.2/Gemfile.base +3 -0
- data/gemfiles/activerecord-5.2/Gemfile.postgresql +10 -0
- data/lib/schema_plus/triggers.rb +18 -0
- data/lib/schema_plus/triggers/active_record/connection_adapters/abstract_adapter.rb +62 -0
- data/lib/schema_plus/triggers/active_record/connection_adapters/postgresql_adapter.rb +51 -0
- data/lib/schema_plus/triggers/active_record/migration/command_recorder.rb +21 -0
- data/lib/schema_plus/triggers/middleware.rb +46 -0
- data/lib/schema_plus/triggers/version.rb +7 -0
- data/lib/schema_plus_triggers.rb +3 -0
- data/schema_dev.yml +6 -0
- data/schema_plus_triggers.gemspec +32 -0
- data/spec/migration_spec.rb +53 -0
- data/spec/schema_dumper_spec.rb +63 -0
- data/spec/spec_helper.rb +52 -0
- metadata +211 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6e78e81607d084ffbe2fca6564415bfa98e1044677be1869b0fb1707a2fd32d6
|
4
|
+
data.tar.gz: aaf94ee3cd40cb00d5360a1a98f702cad797f22b889d99c75a2dbf1c1184777b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0fd1787cae0ea894437358e3ee050763e493b3e2d8a716a76774948a6552c64cd6cdaea4faa74d21ab7629a3fed96500ae185cdb908d90e55f1954fecf05ecb6
|
7
|
+
data.tar.gz: d23458705d3ada0ad994adc492f439d75938d5bac8f82b34600cda0e054a1779a37df89d540a532bf51166c3a2e2ca71db12cc838247b122110e2490294c74c8
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# This file was auto-generated by the schema_dev tool, based on the data in
|
2
|
+
# ./schema_dev.yml
|
3
|
+
# Please do not edit this file; any changes will be overwritten next time
|
4
|
+
# schema_dev gets run.
|
5
|
+
---
|
6
|
+
sudo: false
|
7
|
+
rvm:
|
8
|
+
- 2.3.1
|
9
|
+
gemfile:
|
10
|
+
- gemfiles/activerecord-5.2/Gemfile.postgresql
|
11
|
+
env: POSTGRESQL_DB_USER=postgres
|
12
|
+
addons:
|
13
|
+
postgresql: '9.4'
|
14
|
+
before_script: bundle exec rake create_databases
|
15
|
+
after_script: bundle exec rake drop_databases
|
16
|
+
script: bundle exec rake travis
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2020 Edward Rudd
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
[](http://badge.fury.io/rb/schema_plus_triggers)
|
2
|
+
[](http://travis-ci.org/SchemaPlus/schema_plus_triggers)
|
3
|
+
[](https://coveralls.io/r/SchemaPlus/schema_plus_triggers)
|
4
|
+
[](https://gemnasium.com/SchemaPlus/schema_plus_triggers)
|
5
|
+
|
6
|
+
# SchemaPlus::Triggers
|
7
|
+
|
8
|
+
ScemaPlus::Triggers adds support for SQL triggers in ActiveRecord.
|
9
|
+
|
10
|
+
SchemaPlus::Triggers is part of the [SchemaPlus](https://github.com/SchemaPlus/) family of Ruby on Rails ActiveRecord extension gems.
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
<!-- SCHEMA_DEV: TEMPLATE INSTALLATION - begin -->
|
15
|
+
<!-- These lines are auto-inserted from a schema_dev template -->
|
16
|
+
As usual:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
gem "schema_plus_triggers" # in a Gemfile
|
20
|
+
gem.add_dependency "schema_plus_triggers" # in a .gemspec
|
21
|
+
```
|
22
|
+
|
23
|
+
<!-- SCHEMA_DEV: TEMPLATE INSTALLATION - end -->
|
24
|
+
|
25
|
+
## Compatibility
|
26
|
+
|
27
|
+
SchemaPlus::Triggers is tested on:
|
28
|
+
|
29
|
+
<!-- SCHEMA_DEV: MATRIX - begin -->
|
30
|
+
<!-- These lines are auto-generated by schema_dev based on schema_dev.yml -->
|
31
|
+
* ruby **2.3.1** with activerecord **5.2**, using **postgresql**
|
32
|
+
|
33
|
+
<!-- SCHEMA_DEV: MATRIX - end -->
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
### Note for PostgreSQL
|
38
|
+
|
39
|
+
You will need to also add the schema_plus_functions gem and define your trigger function.
|
40
|
+
|
41
|
+
create_function :my_trigger_func, '', <<-END
|
42
|
+
RETURNS trigger
|
43
|
+
LANGUAGE plpgsql
|
44
|
+
AS $$
|
45
|
+
BEGIN
|
46
|
+
BEGIN
|
47
|
+
IF (TG_OP = 'DELETE') THEN
|
48
|
+
INSERT INTO log (operation, log) VALUES ('DELETE', OLD.name);
|
49
|
+
ELSEIF (TG_OP = 'UPDATE') THEN
|
50
|
+
INSERT INTO log (operation, log) VALUES ('UPDATE', OLD.name || ' to ' || NEW.name);
|
51
|
+
ELSEIF (TG_OP = 'INSERT') THEN
|
52
|
+
INSERT INTO log (operation, log) VALUES ('INSERT', NEW.name);
|
53
|
+
END IF;
|
54
|
+
END;
|
55
|
+
END;
|
56
|
+
$$
|
57
|
+
END
|
58
|
+
|
59
|
+
### Migrations
|
60
|
+
|
61
|
+
To declare a trigger use `create_trigger`:
|
62
|
+
|
63
|
+
create_trigger :my_table, :my_trigger, 'after insert', 'for each row execute procedure my_trigger_func()'
|
64
|
+
|
65
|
+
To drop a trigger use `drop_trigger`:
|
66
|
+
|
67
|
+
drop_trigger :my_table, :my_trigger
|
68
|
+
|
69
|
+
### Introspection
|
70
|
+
|
71
|
+
You can query the list of triggers defined at the connection level (uncached):
|
72
|
+
|
73
|
+
connection.triggers
|
74
|
+
|
75
|
+
This will return an array of arrays. The inner array containing the table name and the trigger name.
|
76
|
+
|
77
|
+
## History
|
78
|
+
|
79
|
+
* 0.1.0 - Initial release
|
80
|
+
|
81
|
+
## Development & Testing
|
82
|
+
|
83
|
+
Are you interested in contributing to SchemaPlus::Triggers? Thanks! Please follow
|
84
|
+
the standard protocol: fork, feature branch, develop, push, and issue pull
|
85
|
+
request.
|
86
|
+
|
87
|
+
Some things to know about to help you develop and test:
|
88
|
+
|
89
|
+
<!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_DEV - begin -->
|
90
|
+
<!-- These lines are auto-inserted from a schema_dev template -->
|
91
|
+
* **schema_dev**: SchemaPlus::Triggers uses [schema_dev](https://github.com/SchemaPlus/schema_dev) to
|
92
|
+
facilitate running rspec tests on the matrix of ruby, activerecord, and database
|
93
|
+
versions that the gem supports, both locally and on
|
94
|
+
[travis-ci](http://travis-ci.org/SchemaPlus/schema_plus_triggers)
|
95
|
+
|
96
|
+
To to run rspec locally on the full matrix, do:
|
97
|
+
|
98
|
+
$ schema_dev bundle install
|
99
|
+
$ schema_dev rspec
|
100
|
+
|
101
|
+
You can also run on just one configuration at a time; For info, see `schema_dev --help` or the [schema_dev](https://github.com/SchemaPlus/schema_dev) README.
|
102
|
+
|
103
|
+
The matrix of configurations is specified in `schema_dev.yml` in
|
104
|
+
the project root.
|
105
|
+
|
106
|
+
|
107
|
+
<!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_DEV - end -->
|
108
|
+
|
109
|
+
<!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_PLUS_CORE - begin -->
|
110
|
+
<!-- These lines are auto-inserted from a schema_dev template -->
|
111
|
+
* **schema_plus_core**: SchemaPlus::Triggers uses the SchemaPlus::Core API that
|
112
|
+
provides middleware callback stacks to make it easy to extend
|
113
|
+
ActiveRecord's behavior. If that API is missing something you need for
|
114
|
+
your contribution, please head over to
|
115
|
+
[schema_plus_core](https://github.com/SchemaPlus/schema_plus_core) and open
|
116
|
+
an issue or pull request.
|
117
|
+
|
118
|
+
<!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_PLUS_CORE - end -->
|
119
|
+
|
120
|
+
<!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_MONKEY - begin -->
|
121
|
+
<!-- These lines are auto-inserted from a schema_dev template -->
|
122
|
+
* **schema_monkey**: SchemaPlus::Triggers is implemented as a
|
123
|
+
[schema_monkey](https://github.com/SchemaPlus/schema_monkey) client,
|
124
|
+
using [schema_monkey](https://github.com/SchemaPlus/schema_monkey)'s
|
125
|
+
convention-based protocols for extending ActiveRecord and using middleware stacks.
|
126
|
+
|
127
|
+
<!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_MONKEY - end -->
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'schema_plus/core'
|
4
|
+
|
5
|
+
require_relative 'triggers/version'
|
6
|
+
require_relative 'triggers/active_record/connection_adapters/abstract_adapter'
|
7
|
+
require_relative 'triggers/active_record/migration/command_recorder'
|
8
|
+
require_relative 'triggers/middleware'
|
9
|
+
|
10
|
+
module SchemaPlus::Triggers
|
11
|
+
module ActiveRecord
|
12
|
+
module ConnectionAdapters
|
13
|
+
autoload :PostgresqlAdapter, 'schema_plus/triggers/active_record/connection_adapters/postgresql_adapter'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
SchemaMonkey.register SchemaPlus::Triggers
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SchemaPlus::Triggers
|
4
|
+
module ActiveRecord
|
5
|
+
module ConnectionAdapters
|
6
|
+
module AbstractAdapter
|
7
|
+
# Create a trigger. Valid options are :force
|
8
|
+
def create_trigger(table_name, trigger_name, triggers, definition, options = {})
|
9
|
+
SchemaMonkey::Middleware::Migration::CreateTrigger.start(connection: self, table_name: table_name, trigger_name: trigger_name, triggers: triggers, definition: definition, options: options) do |env|
|
10
|
+
table_name = env.table_name
|
11
|
+
trigger_name = env.trigger_name
|
12
|
+
triggers = env.triggers
|
13
|
+
definition = env.definition
|
14
|
+
options = env.options
|
15
|
+
|
16
|
+
definition = definition.to_sql if definition.respond_to? :to_sql
|
17
|
+
if options[:force]
|
18
|
+
drop_trigger(table_name, trigger_name, if_exists: true)
|
19
|
+
end
|
20
|
+
|
21
|
+
execute "CREATE TRIGGER #{quote_table_name(trigger_name)} #{triggers} ON #{quote_table_name(table_name)} #{definition}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Remove a trigger. Valid options are :if_exists
|
26
|
+
# and :cascade
|
27
|
+
#
|
28
|
+
# drop_trigger 'my_table', 'trigger_name', if_exists: true
|
29
|
+
def drop_trigger(table_name, trigger_name, options = {})
|
30
|
+
SchemaMonkey::Middleware::Migration::CreateTrigger.start(connection: self, table_name: table_name, trigger_name: trigger_name, options: options) do |env|
|
31
|
+
table_name = env.table_name
|
32
|
+
trigger_name = env.trigger_name
|
33
|
+
options = env.options
|
34
|
+
|
35
|
+
sql = "DROP TRIGGER"
|
36
|
+
sql += " IF EXISTS" if options[:if_exists]
|
37
|
+
sql += " #{quote_table_name(trigger_name)} ON #{quote_table_name(table_name)}"
|
38
|
+
sql += " CASCADE" if options[:cascade]
|
39
|
+
|
40
|
+
execute sql
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
#####################################################################
|
45
|
+
#
|
46
|
+
# The functions below here are abstract; each subclass should
|
47
|
+
# define them all. Defining them here only for reference.
|
48
|
+
#
|
49
|
+
|
50
|
+
# (abstract) Return the Trigger objects for triggers
|
51
|
+
def triggers(name = nil)
|
52
|
+
raise "Internal Error: Connection adapter did not override abstract function"
|
53
|
+
end
|
54
|
+
|
55
|
+
# (abstract) Return the Trigger definition
|
56
|
+
def trigger_definition(table_name, trigger_name, name = nil)
|
57
|
+
raise "Internal Error: Connection adapter did not override abstract function"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SchemaPlus::Triggers
|
4
|
+
module ActiveRecord
|
5
|
+
module ConnectionAdapters
|
6
|
+
module PostgresqlAdapter
|
7
|
+
def triggers(name = nil) #:nodoc:
|
8
|
+
SchemaMonkey::Middleware::Schema::Triggers.start(connection: self, query_name: name, triggers: []) do |env|
|
9
|
+
sql = <<-SQL
|
10
|
+
SELECT relname as table_name, tgname as trigger_name
|
11
|
+
FROM pg_trigger T
|
12
|
+
JOIN pg_class C ON C.oid = T.tgrelid
|
13
|
+
WHERE tgisinternal = FALSE
|
14
|
+
AND relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)))
|
15
|
+
ORDER BY 1, 2
|
16
|
+
SQL
|
17
|
+
|
18
|
+
env.triggers += env.connection.query(sql, env.query_name).map { |row| [row[0], row[1]] }
|
19
|
+
end.triggers
|
20
|
+
end
|
21
|
+
|
22
|
+
def trigger_definition(table_name, trigger_name, name = nil) #:nodoc:
|
23
|
+
data = SchemaMonkey::Middleware::Schema::TriggerDefinition.start(connection: self, table_name: table_name, trigger_name: trigger_name, query_name: name) do |env|
|
24
|
+
result = env.connection.query(<<-SQL, env.query_name)
|
25
|
+
SELECT pg_get_triggerdef(T.oid)
|
26
|
+
FROM pg_trigger T
|
27
|
+
JOIN pg_class C ON C.oid = T.tgrelid
|
28
|
+
WHERE tgisinternal = FALSE
|
29
|
+
AND relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)))
|
30
|
+
AND tgname = '#{quote_string(trigger_name)}'
|
31
|
+
AND relname = '#{quote_string(table_name)}'
|
32
|
+
SQL
|
33
|
+
|
34
|
+
row = result.first
|
35
|
+
unless row.nil?
|
36
|
+
sql = row.first
|
37
|
+
|
38
|
+
m = sql.match(/CREATE.+?TRIGGER\s+"?\w+"?\s+(.+?) ON\s+[\w\."]+\s+(.+)/)
|
39
|
+
|
40
|
+
env.trigger = m[1]
|
41
|
+
env.definition = m[2]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
return data.trigger, data.definition
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SchemaPlus::Triggers
|
4
|
+
module ActiveRecord
|
5
|
+
module Migration
|
6
|
+
module CommandRecorder
|
7
|
+
def create_trigger(*args, &block)
|
8
|
+
record(:create_trigger, args, &block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def drop_trigger(*args, &block)
|
12
|
+
record(:drop_trigger, args, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def invert_create_trigger(args)
|
16
|
+
[:drop_trigger, [args.first, args.second]]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SchemaPlus::Triggers
|
4
|
+
module Middleware
|
5
|
+
module Dumper
|
6
|
+
module Tables
|
7
|
+
# Dump
|
8
|
+
def after(env)
|
9
|
+
env.connection.triggers.each do |table_name, trigger_name|
|
10
|
+
next if env.dumper.ignored?(table_name)
|
11
|
+
trigger, definition = env.connection.trigger_definition(table_name, trigger_name)
|
12
|
+
heredelim = "END_TRIGGER_#{table_name.upcase}_#{trigger_name.upcase}"
|
13
|
+
statement = <<~ENDTRIGGER
|
14
|
+
create_trigger "#{table_name}", "#{trigger_name}", "#{trigger}", <<-'#{heredelim}', :force => true
|
15
|
+
#{definition}
|
16
|
+
#{heredelim}
|
17
|
+
|
18
|
+
ENDTRIGGER
|
19
|
+
|
20
|
+
env.dump.final << statement
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module Schema
|
27
|
+
module Triggers
|
28
|
+
ENV = [:connection, :query_name, :triggers]
|
29
|
+
end
|
30
|
+
|
31
|
+
module TriggerDefinition
|
32
|
+
ENV = [:connection, :table_name, :trigger_name, :query_name, :trigger, :definition]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module Migration
|
37
|
+
module CreateTrigger
|
38
|
+
ENV = [:connection, :table_name, :trigger_name, :triggers, :definition, :options]
|
39
|
+
end
|
40
|
+
|
41
|
+
module DropTrigger
|
42
|
+
ENV = [:connection, :table_name, :trigger_name, :options]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/schema_dev.yml
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'schema_plus/triggers/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |gem|
|
8
|
+
gem.name = "schema_plus_triggers"
|
9
|
+
gem.version = SchemaPlus::Triggers::VERSION
|
10
|
+
gem.authors = ["Edward Rudd"]
|
11
|
+
gem.email = ["urkle@outoforder.cc"]
|
12
|
+
gem.summary = %q{Adds support for triggers in ActiveRecord}
|
13
|
+
gem.description = %q{Adds support for triggers in ActiveRecord}
|
14
|
+
gem.homepage = "https://github.com/SchemaPlus/schema_plus_triggers"
|
15
|
+
gem.license = "MIT"
|
16
|
+
|
17
|
+
gem.files = `git ls-files -z`.split("\x0")
|
18
|
+
gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
20
|
+
gem.require_paths = ["lib"]
|
21
|
+
|
22
|
+
gem.add_dependency "activerecord", ">= 5.2", '< 5.3'
|
23
|
+
gem.add_dependency "schema_plus_core", "~> 2.2", ">= 2.2.3"
|
24
|
+
|
25
|
+
gem.add_development_dependency "bundler"
|
26
|
+
gem.add_development_dependency "rake", "~> 10.0"
|
27
|
+
gem.add_development_dependency "rspec", "~> 3.0"
|
28
|
+
gem.add_development_dependency "schema_dev", "~> 3.11", ">= 3.11.1"
|
29
|
+
gem.add_development_dependency "schema_plus_compatibility", "~> 0.2"
|
30
|
+
gem.add_development_dependency "simplecov"
|
31
|
+
gem.add_development_dependency "simplecov-gem-profile"
|
32
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ActiveRecord::Migration do
|
6
|
+
before do
|
7
|
+
define_schema do
|
8
|
+
create_table :user do |t|
|
9
|
+
t.string :name, null: false
|
10
|
+
end
|
11
|
+
|
12
|
+
create_table :log do |t|
|
13
|
+
t.string :operation
|
14
|
+
t.string :log
|
15
|
+
end
|
16
|
+
|
17
|
+
# manual SQL so we do not need to depend on schema_plus_functions for testing
|
18
|
+
execute <<-SQL
|
19
|
+
CREATE OR REPLACE FUNCTION my_trigger_func() RETURNS trigger LANGUAGE plpgsql AS $$
|
20
|
+
BEGIN
|
21
|
+
IF (TG_OP = 'DELETE') THEN
|
22
|
+
INSERT INTO log (operation, log) VALUES ('DELETE', OLD.name);
|
23
|
+
ELSEIF (TG_OP = 'UPDATE') THEN
|
24
|
+
INSERT INTO log (operation, log) VALUES ('UPDATE', OLD.name || ' to ' || NEW.name);
|
25
|
+
ELSEIF (TG_OP = 'INSERT') THEN
|
26
|
+
INSERT INTO log (operation, log) VALUES ('INSERT', NEW.name);
|
27
|
+
END IF;
|
28
|
+
END;
|
29
|
+
$$
|
30
|
+
SQL
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
after do
|
35
|
+
apply_migration do
|
36
|
+
execute 'DROP FUNCTION my_trigger_func() CASCADE'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "creates the trigger on the table" do
|
41
|
+
apply_migration do
|
42
|
+
create_trigger :user, :log_trigger, 'after insert or update or delete', 'for each row execute procedure my_trigger_func()'
|
43
|
+
end
|
44
|
+
|
45
|
+
expect(triggers('user')).to include(['user', 'log_trigger'])
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def triggers(table)
|
51
|
+
ActiveRecord::Base.connection.triggers.select { |(t, _)| t == table }
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
describe "Schema dump" do
|
7
|
+
before do
|
8
|
+
define_schema do
|
9
|
+
create_table :user do |t|
|
10
|
+
t.string :name, null: false
|
11
|
+
end
|
12
|
+
|
13
|
+
create_table :log do |t|
|
14
|
+
t.string :operation
|
15
|
+
t.string :log
|
16
|
+
end
|
17
|
+
|
18
|
+
execute <<-SQL
|
19
|
+
CREATE OR REPLACE FUNCTION my_trigger_func() RETURNS trigger LANGUAGE plpgsql AS $$
|
20
|
+
BEGIN
|
21
|
+
IF (TG_OP = 'DELETE') THEN
|
22
|
+
INSERT INTO log (operation, log) VALUES ('DELETE', OLD.name);
|
23
|
+
ELSEIF (TG_OP = 'UPDATE') THEN
|
24
|
+
INSERT INTO log (operation, log) VALUES ('UPDATE', OLD.name || ' to ' || NEW.name);
|
25
|
+
ELSEIF (TG_OP = 'INSERT') THEN
|
26
|
+
INSERT INTO log (operation, log) VALUES ('INSERT', NEW.name);
|
27
|
+
END IF;
|
28
|
+
END;
|
29
|
+
$$
|
30
|
+
SQL
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
after do
|
35
|
+
apply_migration do
|
36
|
+
execute 'DROP FUNCTION my_trigger_func() CASCADE'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "includes the trigger definition" do
|
41
|
+
apply_migration do
|
42
|
+
create_trigger :user, :log_trigger, 'after insert or update or delete', 'for each row execute procedure my_trigger_func()'
|
43
|
+
end
|
44
|
+
|
45
|
+
dump_schema.tap do |dump|
|
46
|
+
expect(dump).to match(/create_trigger.+user.+log_trigger.+after insert/i)
|
47
|
+
|
48
|
+
expect(dump).to match(/for each row.+my_trigger_func/i)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
def dump_schema(opts = {})
|
56
|
+
stream = StringIO.new
|
57
|
+
|
58
|
+
ActiveRecord::SchemaDumper.ignore_tables = Array.wrap(opts[:ignore]) || []
|
59
|
+
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
60
|
+
|
61
|
+
stream.string
|
62
|
+
end
|
63
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'simplecov'
|
4
|
+
require 'simplecov-gem-profile'
|
5
|
+
SimpleCov.start "gem"
|
6
|
+
|
7
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
8
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
9
|
+
|
10
|
+
require 'rspec'
|
11
|
+
require 'active_record'
|
12
|
+
require 'schema_plus_triggers'
|
13
|
+
require 'schema_plus_compatibility'
|
14
|
+
require 'schema_dev/rspec'
|
15
|
+
|
16
|
+
SchemaDev::Rspec.setup
|
17
|
+
|
18
|
+
Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f}
|
19
|
+
|
20
|
+
RSpec.configure do |config|
|
21
|
+
config.warnings = true
|
22
|
+
config.around(:each) do |example|
|
23
|
+
ActiveRecord::Migration.suppress_messages do
|
24
|
+
begin
|
25
|
+
example.run
|
26
|
+
ensure
|
27
|
+
ActiveRecord::Base.connection.tables_only.each do |table|
|
28
|
+
ActiveRecord::Migration.drop_table table, force: :cascade
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def define_schema(config = {}, &block)
|
36
|
+
ActiveRecord::Migration.suppress_messages do
|
37
|
+
ActiveRecord::Schema.define do
|
38
|
+
ActiveRecord::Base.connection.tables_only.each do |table|
|
39
|
+
ActiveRecord::Migration.drop_table table, force: :cascade
|
40
|
+
end
|
41
|
+
instance_eval &block
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def apply_migration(config = {}, &block)
|
47
|
+
ActiveRecord::Schema.define do
|
48
|
+
instance_eval &block
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
SimpleCov.command_name "[ruby#{RUBY_VERSION}-activerecord#{::ActiveRecord.version}-#{ActiveRecord::Base.connection.adapter_name}]"
|
metadata
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: schema_plus_triggers
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Edward Rudd
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-08-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.2'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '5.3'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '5.2'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '5.3'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: schema_plus_core
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '2.2'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 2.2.3
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '2.2'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 2.2.3
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: bundler
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
type: :development
|
61
|
+
prerelease: false
|
62
|
+
version_requirements: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: rake
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - "~>"
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '10.0'
|
74
|
+
type: :development
|
75
|
+
prerelease: false
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - "~>"
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '10.0'
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: rspec
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - "~>"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '3.0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - "~>"
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '3.0'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: schema_dev
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - "~>"
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '3.11'
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: 3.11.1
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - "~>"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '3.11'
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: 3.11.1
|
115
|
+
- !ruby/object:Gem::Dependency
|
116
|
+
name: schema_plus_compatibility
|
117
|
+
requirement: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - "~>"
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0.2'
|
122
|
+
type: :development
|
123
|
+
prerelease: false
|
124
|
+
version_requirements: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - "~>"
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0.2'
|
129
|
+
- !ruby/object:Gem::Dependency
|
130
|
+
name: simplecov
|
131
|
+
requirement: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
type: :development
|
137
|
+
prerelease: false
|
138
|
+
version_requirements: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
- !ruby/object:Gem::Dependency
|
144
|
+
name: simplecov-gem-profile
|
145
|
+
requirement: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
requirements:
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: '0'
|
157
|
+
description: Adds support for triggers in ActiveRecord
|
158
|
+
email:
|
159
|
+
- urkle@outoforder.cc
|
160
|
+
executables: []
|
161
|
+
extensions: []
|
162
|
+
extra_rdoc_files: []
|
163
|
+
files:
|
164
|
+
- ".gitignore"
|
165
|
+
- ".travis.yml"
|
166
|
+
- Gemfile
|
167
|
+
- LICENSE.txt
|
168
|
+
- README.md
|
169
|
+
- Rakefile
|
170
|
+
- gemfiles/Gemfile.base
|
171
|
+
- gemfiles/activerecord-5.2/Gemfile.base
|
172
|
+
- gemfiles/activerecord-5.2/Gemfile.postgresql
|
173
|
+
- lib/schema_plus/triggers.rb
|
174
|
+
- lib/schema_plus/triggers/active_record/connection_adapters/abstract_adapter.rb
|
175
|
+
- lib/schema_plus/triggers/active_record/connection_adapters/postgresql_adapter.rb
|
176
|
+
- lib/schema_plus/triggers/active_record/migration/command_recorder.rb
|
177
|
+
- lib/schema_plus/triggers/middleware.rb
|
178
|
+
- lib/schema_plus/triggers/version.rb
|
179
|
+
- lib/schema_plus_triggers.rb
|
180
|
+
- schema_dev.yml
|
181
|
+
- schema_plus_triggers.gemspec
|
182
|
+
- spec/migration_spec.rb
|
183
|
+
- spec/schema_dumper_spec.rb
|
184
|
+
- spec/spec_helper.rb
|
185
|
+
homepage: https://github.com/SchemaPlus/schema_plus_triggers
|
186
|
+
licenses:
|
187
|
+
- MIT
|
188
|
+
metadata: {}
|
189
|
+
post_install_message:
|
190
|
+
rdoc_options: []
|
191
|
+
require_paths:
|
192
|
+
- lib
|
193
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
194
|
+
requirements:
|
195
|
+
- - ">="
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: '0'
|
198
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
199
|
+
requirements:
|
200
|
+
- - ">="
|
201
|
+
- !ruby/object:Gem::Version
|
202
|
+
version: '0'
|
203
|
+
requirements: []
|
204
|
+
rubygems_version: 3.0.6
|
205
|
+
signing_key:
|
206
|
+
specification_version: 4
|
207
|
+
summary: Adds support for triggers in ActiveRecord
|
208
|
+
test_files:
|
209
|
+
- spec/migration_spec.rb
|
210
|
+
- spec/schema_dumper_spec.rb
|
211
|
+
- spec/spec_helper.rb
|