hairtrigger 0.2.21 → 0.2.25
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 +5 -5
- data/LICENSE.txt +1 -1
- data/README.md +47 -9
- data/lib/hair_trigger/builder.rb +15 -10
- data/lib/hair_trigger/schema_dumper.rb +38 -6
- data/lib/hair_trigger/version.rb +1 -1
- data/lib/hair_trigger.rb +31 -32
- metadata +13 -24
- data/spec/adapter_spec.rb +0 -94
- data/spec/builder_spec.rb +0 -433
- data/spec/migrations/20110331212003_initial_tables.rb +0 -18
- data/spec/migrations/20110331212631_user_trigger.rb +0 -18
- data/spec/migrations/20110417185102_manual_user_trigger.rb +0 -10
- data/spec/migrations-3.2/20110331212003_initial_tables.rb +0 -18
- data/spec/migrations-3.2/20110331212631_user_trigger.rb +0 -18
- data/spec/migrations-3.2/20110417185102_manual_user_trigger.rb +0 -10
- data/spec/migrations-pre-3.1/20110331212003_initial_tables.rb +0 -18
- data/spec/migrations-pre-3.1/20110331212631_user_trigger.rb +0 -18
- data/spec/migrations-pre-3.1/20110417185102_manual_user_trigger.rb +0 -10
- data/spec/migrations_spec.rb +0 -60
- data/spec/models/user.rb +0 -6
- data/spec/models/user_group.rb +0 -3
- data/spec/schema_dumper_spec.rb +0 -124
- data/spec/spec_helper.rb +0 -104
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 0537ad012af8bad34a62b81241c8517ac38fabeae279954e3e27ecc86e89cc9a
|
|
4
|
+
data.tar.gz: fe75190bc7fb5b8d2956929f5e72d2b48b0dbc81b5bdd27eac6fd48ccedf81cb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f8df4419131442b648d2ed1a8315281e5d289638a228cf112882faacbcc27ebf5ca136474d1bf34d2b84e920187b23ec42365de67d1856112aa2449413571b52
|
|
7
|
+
data.tar.gz: 97193387286d4ed659612eca3d4d4a3266965f259a95c15494a627e494904d7a48c3b40a705455400aa5fb65bb90219a1485fd5494d8f006b472e8ac690eca6e
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# HairTrigger
|
|
2
|
-
[<img src="https://
|
|
2
|
+
[<img src="https://github.com/jenseng/hair_trigger/workflows/CI/badge.svg" />](https://github.com/jenseng/hair_trigger/actions?query=workflow%3ACI)
|
|
3
3
|
|
|
4
4
|
HairTrigger lets you create and manage database triggers in a concise,
|
|
5
5
|
db-agnostic, Rails-y way. You declare triggers right in your models in Ruby,
|
|
@@ -7,8 +7,15 @@ and a simple rake task does all the dirty work for you.
|
|
|
7
7
|
|
|
8
8
|
## Installation
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
HairTrigger works with Rails 5.0 onwards. Add the following line to your Gemfile:
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
gem 'hairtrigger'
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Then run `bundle install`
|
|
17
|
+
|
|
18
|
+
For older versions of Rails check the last [0.2 release](https://github.com/jenseng/hair_trigger/tree/v0.2.21)
|
|
12
19
|
|
|
13
20
|
## Usage
|
|
14
21
|
|
|
@@ -94,10 +101,10 @@ Only fire the update trigger if at least one of the columns is specified in the
|
|
|
94
101
|
Permissions/role to check when calling trigger. PostgreSQL supports `:invoker` (default) and `:definer`, MySQL supports `:definer` (default) and arbitrary users (syntax: `'user'@'host'`).
|
|
95
102
|
|
|
96
103
|
#### timing(timing)
|
|
97
|
-
Required (but may be
|
|
104
|
+
Required (but may be satisfied by `before`/`after`). Possible values are `:before`/`:after`.
|
|
98
105
|
|
|
99
106
|
#### events(*events)
|
|
100
|
-
Required (but may be
|
|
107
|
+
Required (but may be satisfied by `before`/`after`). Possible values are `:insert`/`:update`/`:delete`/`:truncate`. MySQL/SQLite only support one action per trigger, and don't support `:truncate`.
|
|
101
108
|
|
|
102
109
|
#### nowrap(flag = true)
|
|
103
110
|
PostgreSQL-specific option to prevent the trigger action from being wrapped in a `CREATE FUNCTION`. This is useful for executing existing triggers/functions directly, but is not compatible with the `security` setting nor can it be used with pre-9.0 PostgreSQL when supplying a `where` condition.
|
|
@@ -233,6 +240,21 @@ As long as you don't delete old migrations and schema.rb prior to running
|
|
|
233
240
|
If you have deleted all trigger migrations, you can regenerate a new
|
|
234
241
|
baseline for model triggers via `rake db:generate_trigger_migration`.
|
|
235
242
|
|
|
243
|
+
### Filtering
|
|
244
|
+
|
|
245
|
+
It is possible to filter which triggers are dumped by setting any of these
|
|
246
|
+
configuration values:
|
|
247
|
+
|
|
248
|
+
```ruby
|
|
249
|
+
HairTrigger::SchemaDumper::Configuration.ignore_triggers = 'exact_trigger_name'
|
|
250
|
+
HairTrigger::SchemaDumper::Configuration.ignore_tables = [/partial_/, 'exact_table_name']
|
|
251
|
+
HairTrigger::SchemaDumper::Configuration.allow_triggers = [/partial_/, 'exact_trigger_name']
|
|
252
|
+
HairTrigger::SchemaDumper::Configuration.allow_tables = 'exact_table_name'
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Each option can accept a single String or Regexp, or a mixed array of both.
|
|
256
|
+
|
|
257
|
+
|
|
236
258
|
## Testing
|
|
237
259
|
|
|
238
260
|
To stay on top of things, it's strongly recommended that you add a test or
|
|
@@ -248,7 +270,7 @@ create.
|
|
|
248
270
|
|
|
249
271
|
## Warnings and Errors
|
|
250
272
|
|
|
251
|
-
There are a couple classes of errors: declaration errors and generation
|
|
273
|
+
There are a couple classes of errors: declaration errors and generation
|
|
252
274
|
errors/warnings.
|
|
253
275
|
|
|
254
276
|
Declaration errors happen if your trigger declaration is obviously wrong, and
|
|
@@ -313,10 +335,26 @@ existing trigger if you wish to redefine it.
|
|
|
313
335
|
* Manual `create_trigger` statements have some gotchas. See the section
|
|
314
336
|
"Manual triggers and :compatibility"
|
|
315
337
|
|
|
338
|
+
## Contributing
|
|
339
|
+
|
|
340
|
+
Contributions welcome! I don't write much Ruby these days 😢 (and haven't used this
|
|
341
|
+
gem in years 😬) but am happy to take contributions. If I'm slow to respond, don't
|
|
342
|
+
hesitate to @ me repeatedly, sometimes those github notifications slip through
|
|
343
|
+
the cracks. 😆.
|
|
344
|
+
|
|
345
|
+
If you want to add a feature/bugfix, you can rely on Github Actions to run the
|
|
346
|
+
tests, but do also run them locally (especially if you are changing supported
|
|
347
|
+
railses/etc). HairTrigger uses [appraisal](https://github.com/thoughtbot/appraisal)
|
|
348
|
+
to manage all that w/ automagical gemfiles. So the tl;dr when testing locally is:
|
|
349
|
+
|
|
350
|
+
1. make sure you have mysql and postgres installed (homebrew or whatever)
|
|
351
|
+
2. `bundle exec appraisal install` -- get all the dependencies
|
|
352
|
+
3. `bundle exec appraisal rake` -- run the specs every which way
|
|
353
|
+
|
|
316
354
|
## Compatibility
|
|
317
355
|
|
|
318
|
-
* Ruby
|
|
319
|
-
* Rails
|
|
356
|
+
* Ruby 2.3.0+
|
|
357
|
+
* Rails 5.0+
|
|
320
358
|
* PostgreSQL 8.0+
|
|
321
359
|
* MySQL 5.0.10+
|
|
322
360
|
* SQLite 3.3.8+
|
|
@@ -325,4 +363,4 @@ existing trigger if you wish to redefine it.
|
|
|
325
363
|
|
|
326
364
|
## Copyright
|
|
327
365
|
|
|
328
|
-
Copyright (c) 2011-
|
|
366
|
+
Copyright (c) 2011-2022 Jon Jensen. See LICENSE.txt for further details.
|
data/lib/hair_trigger/builder.rb
CHANGED
|
@@ -137,22 +137,22 @@ module HairTrigger
|
|
|
137
137
|
methods.each do |method|
|
|
138
138
|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
|
139
139
|
alias #{method}_orig #{method}
|
|
140
|
-
def #{method}(*args)
|
|
140
|
+
def #{method}(*args, &block)
|
|
141
141
|
@chained_calls << :#{method}
|
|
142
142
|
if @triggers || @trigger_group
|
|
143
143
|
@errors << ["mysql doesn't support #{method} within a trigger group", :mysql] unless [:name, :where, :all, :of].include?(:#{method})
|
|
144
144
|
end
|
|
145
|
-
set_#{method}(*args, &(block_given? ?
|
|
145
|
+
set_#{method}(*args, &(block_given? ? block : nil))
|
|
146
146
|
end
|
|
147
|
-
def set_#{method}(*args)
|
|
147
|
+
def set_#{method}(*args, &block)
|
|
148
148
|
if @triggers # i.e. each time we say t.something within a trigger group block
|
|
149
149
|
@chained_calls.pop # the subtrigger will get this, we don't need it
|
|
150
150
|
@chained_calls = @chained_calls.uniq
|
|
151
151
|
@triggers << trigger = clone
|
|
152
|
-
trigger.#{method}(*args, &(block_given? ?
|
|
152
|
+
trigger.#{method}(*args, &(block_given? ? block : nil))
|
|
153
153
|
else
|
|
154
|
-
#{method}_orig(*args)
|
|
155
|
-
maybe_execute(&
|
|
154
|
+
#{method}_orig(*args, &block)
|
|
155
|
+
maybe_execute(&block) if block_given?
|
|
156
156
|
self
|
|
157
157
|
end
|
|
158
158
|
end
|
|
@@ -335,10 +335,11 @@ module HairTrigger
|
|
|
335
335
|
block.call(self)
|
|
336
336
|
raise DeclarationError, "trigger group did not define any triggers" if @triggers.empty?
|
|
337
337
|
else
|
|
338
|
-
@actions =
|
|
339
|
-
|
|
340
|
-
actions.
|
|
341
|
-
|
|
338
|
+
@actions =
|
|
339
|
+
case (actions = block.call)
|
|
340
|
+
when Hash then actions.map { |key, action| [key, ensure_semicolon(action)] }.to_h
|
|
341
|
+
else ensure_semicolon(actions)
|
|
342
|
+
end
|
|
342
343
|
end
|
|
343
344
|
# only the top-most block actually executes
|
|
344
345
|
if !@trigger_group
|
|
@@ -350,6 +351,10 @@ module HairTrigger
|
|
|
350
351
|
self
|
|
351
352
|
end
|
|
352
353
|
|
|
354
|
+
def ensure_semicolon(action)
|
|
355
|
+
action && action !~ /;\s*\z/ ? action.sub(/(\s*)\z/, ';\1') : action
|
|
356
|
+
end
|
|
357
|
+
|
|
353
358
|
def validate_names!
|
|
354
359
|
subtriggers = all_triggers(false)
|
|
355
360
|
named_subtriggers = subtriggers.select{ |t| t.options[:name] }
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
module HairTrigger
|
|
2
2
|
module SchemaDumper
|
|
3
|
+
module Configuration
|
|
4
|
+
mattr_accessor :allow_tables
|
|
5
|
+
mattr_accessor :allow_triggers
|
|
6
|
+
mattr_accessor :ignore_tables
|
|
7
|
+
mattr_accessor :ignore_triggers
|
|
8
|
+
end
|
|
9
|
+
|
|
3
10
|
module TrailerWithTriggersSupport
|
|
4
11
|
def trailer(stream)
|
|
5
12
|
orig_show_warnings = Builder.show_warnings
|
|
@@ -55,7 +62,7 @@ module HairTrigger
|
|
|
55
62
|
stream.puts " # no candidate create_trigger statement could be found, creating an adapter-specific one"
|
|
56
63
|
end
|
|
57
64
|
if definition =~ /\n/
|
|
58
|
-
stream.print " execute(<<-
|
|
65
|
+
stream.print " execute(<<-SQL)\n#{definition.rstrip}\n SQL\n\n"
|
|
59
66
|
else
|
|
60
67
|
stream.print " execute(#{definition.inspect})\n\n"
|
|
61
68
|
end
|
|
@@ -67,7 +74,7 @@ module HairTrigger
|
|
|
67
74
|
def normalize_trigger(name, definition, type)
|
|
68
75
|
@adapter_name = @connection.adapter_name.downcase.to_sym
|
|
69
76
|
|
|
70
|
-
return definition unless @adapter_name == :postgresql
|
|
77
|
+
return definition unless @adapter_name == :postgresql || @adapter_name == :postgis
|
|
71
78
|
# because postgres does not preserve the original CREATE TRIGGER/
|
|
72
79
|
# FUNCTION statements, its decompiled reconstruction will not match
|
|
73
80
|
# ours. we work around it by creating our generated trigger/function,
|
|
@@ -91,17 +98,42 @@ module HairTrigger
|
|
|
91
98
|
end
|
|
92
99
|
|
|
93
100
|
def whitelist_triggers(triggers)
|
|
94
|
-
triggers.reject do |name, source|
|
|
101
|
+
triggers = triggers.reject do |name, source|
|
|
95
102
|
ActiveRecord::SchemaDumper.ignore_tables.any? { |ignored_table_name| source =~ /ON\s+#{@connection.quote_table_name(ignored_table_name)}\s/ }
|
|
96
103
|
end
|
|
104
|
+
|
|
105
|
+
if Configuration.allow_tables.present?
|
|
106
|
+
triggers = triggers.select do |name, source|
|
|
107
|
+
Array(Configuration.allow_tables).any? { |allowed_table_name| source =~ /ON\s+#{@connection.quote_table_name(allowed_table_name)}\s/ }
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
if Configuration.allow_triggers.present?
|
|
112
|
+
triggers = triggers.select do |name, source|
|
|
113
|
+
Array(Configuration.allow_triggers).any? { |allowed_trigger_name| allowed_trigger_name === name } # Triple equals to allow regexps or strings as allowed_trigger_name
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
if Configuration.ignore_tables.present?
|
|
118
|
+
triggers = triggers.reject do |name, source|
|
|
119
|
+
Array(Configuration.ignore_tables).any? { |allowed_table_name| source =~ /ON\s+#{@connection.quote_table_name(allowed_table_name)}\s/ }
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
if Configuration.ignore_triggers.present?
|
|
124
|
+
triggers = triggers.reject do |name, source|
|
|
125
|
+
Array(Configuration.ignore_triggers).any? { |allowed_trigger_name| allowed_trigger_name === name } # Triple equals to allow regexps or strings as allowed_trigger_name
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
triggers
|
|
97
130
|
end
|
|
98
131
|
|
|
99
132
|
def self.included(base)
|
|
100
133
|
base.class_eval do
|
|
101
134
|
prepend TrailerWithTriggersSupport
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
end
|
|
135
|
+
|
|
136
|
+
class_attribute :previous_schema
|
|
105
137
|
end
|
|
106
138
|
end
|
|
107
139
|
end
|
data/lib/hair_trigger/version.rb
CHANGED
data/lib/hair_trigger.rb
CHANGED
|
@@ -22,7 +22,7 @@ module HairTrigger
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def models
|
|
25
|
-
if defined?(Rails)
|
|
25
|
+
if defined?(Rails)
|
|
26
26
|
Rails.application.eager_load!
|
|
27
27
|
else
|
|
28
28
|
Dir[model_path + '/*rb'].each do |model|
|
|
@@ -35,22 +35,24 @@ module HairTrigger
|
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
|
-
ActiveRecord::
|
|
39
|
-
ActiveRecord::Base.send(:subclasses) :
|
|
40
|
-
ActiveRecord::Base.descendants
|
|
38
|
+
ActiveRecord::Base.descendants
|
|
41
39
|
end
|
|
42
40
|
|
|
43
41
|
def migrator
|
|
44
42
|
version = ActiveRecord::VERSION::STRING
|
|
45
|
-
if version >= "
|
|
43
|
+
if version >= "6.0."
|
|
44
|
+
migrations = ActiveRecord::MigrationContext.new(migration_path, ActiveRecord::SchemaMigration).migrations
|
|
45
|
+
elsif version >= "5.2."
|
|
46
46
|
migrations = ActiveRecord::MigrationContext.new(migration_path).migrations
|
|
47
|
-
elsif version < "4.0."
|
|
48
|
-
migrations = migration_path
|
|
49
47
|
else # version >= "4.0."
|
|
50
48
|
migrations = ActiveRecord::Migrator.migrations(migration_path)
|
|
51
49
|
end
|
|
52
50
|
|
|
53
|
-
|
|
51
|
+
if version >= "6.0."
|
|
52
|
+
ActiveRecord::Migrator.new(:up, migrations, ActiveRecord::SchemaMigration)
|
|
53
|
+
else
|
|
54
|
+
ActiveRecord::Migrator.new(:up, migrations)
|
|
55
|
+
end
|
|
54
56
|
end
|
|
55
57
|
|
|
56
58
|
def current_migrations(options = {})
|
|
@@ -59,8 +61,8 @@ module HairTrigger
|
|
|
59
61
|
options[:schema_rb_first] = true
|
|
60
62
|
options[:skip_pending_migrations] = true
|
|
61
63
|
end
|
|
62
|
-
|
|
63
|
-
# if we're in a db:schema:dump task (
|
|
64
|
+
|
|
65
|
+
# if we're in a db:schema:dump task (explicit or kicked off by db:migrate),
|
|
64
66
|
# we evaluate the previous schema.rb (if it exists), and then all applied
|
|
65
67
|
# migrations in order (even ones older than schema.rb). this ensures we
|
|
66
68
|
# handle db:migrate:down scenarios correctly
|
|
@@ -76,7 +78,7 @@ module HairTrigger
|
|
|
76
78
|
triggers = MigrationReader.get_triggers(migration, options)
|
|
77
79
|
migrations << [migration, triggers] unless triggers.empty?
|
|
78
80
|
end
|
|
79
|
-
|
|
81
|
+
|
|
80
82
|
if previous_schema = (options.has_key?(:previous_schema) ? options[:previous_schema] : File.exist?(schema_rb_path) && File.read(schema_rb_path))
|
|
81
83
|
base_triggers = MigrationReader.get_triggers(previous_schema, options)
|
|
82
84
|
unless base_triggers.empty?
|
|
@@ -84,9 +86,9 @@ module HairTrigger
|
|
|
84
86
|
migrations.unshift [OpenStruct.new({:version => version}), base_triggers]
|
|
85
87
|
end
|
|
86
88
|
end
|
|
87
|
-
|
|
89
|
+
|
|
88
90
|
migrations = migrations.sort_by{|(migration, triggers)| migration.version} unless options[:schema_rb_first]
|
|
89
|
-
|
|
91
|
+
|
|
90
92
|
all_builders = []
|
|
91
93
|
migrations.each do |(migration, triggers)|
|
|
92
94
|
triggers.each do |new_trigger|
|
|
@@ -97,7 +99,7 @@ module HairTrigger
|
|
|
97
99
|
all_builders << [migration.name, new_trigger] unless new_trigger.options[:drop]
|
|
98
100
|
end
|
|
99
101
|
end
|
|
100
|
-
|
|
102
|
+
|
|
101
103
|
all_builders
|
|
102
104
|
end
|
|
103
105
|
|
|
@@ -108,27 +110,27 @@ module HairTrigger
|
|
|
108
110
|
def generate_migration(silent = false)
|
|
109
111
|
begin
|
|
110
112
|
canonical_triggers = current_triggers
|
|
111
|
-
rescue
|
|
113
|
+
rescue
|
|
112
114
|
$stderr.puts $!
|
|
113
115
|
exit 1
|
|
114
116
|
end
|
|
115
|
-
|
|
117
|
+
|
|
116
118
|
migrations = current_migrations
|
|
117
119
|
migration_names = migrations.map(&:first)
|
|
118
120
|
existing_triggers = migrations.map(&:last)
|
|
119
|
-
|
|
121
|
+
|
|
120
122
|
up_drop_triggers = []
|
|
121
123
|
up_create_triggers = []
|
|
122
124
|
down_drop_triggers = []
|
|
123
125
|
down_create_triggers = []
|
|
124
|
-
|
|
126
|
+
|
|
125
127
|
# see which triggers need to be dropped
|
|
126
128
|
existing_triggers.each do |existing|
|
|
127
129
|
next if canonical_triggers.any?{ |t| t.prepared_name == existing.prepared_name }
|
|
128
130
|
up_drop_triggers.concat existing.drop_triggers
|
|
129
131
|
down_create_triggers << existing
|
|
130
132
|
end
|
|
131
|
-
|
|
133
|
+
|
|
132
134
|
# see which triggers need to be added/replaced
|
|
133
135
|
(canonical_triggers - existing_triggers).each do |new_trigger|
|
|
134
136
|
up_create_triggers << new_trigger
|
|
@@ -141,29 +143,28 @@ module HairTrigger
|
|
|
141
143
|
down_create_triggers << existing
|
|
142
144
|
end
|
|
143
145
|
end
|
|
144
|
-
|
|
146
|
+
|
|
145
147
|
return if up_drop_triggers.empty? && up_create_triggers.empty?
|
|
146
148
|
|
|
147
149
|
migration_name = infer_migration_name(migration_names, up_create_triggers, up_drop_triggers)
|
|
148
150
|
migration_version = infer_migration_version(migration_name)
|
|
149
151
|
file_name = migration_path + '/' + migration_version + "_" + migration_name.underscore + ".rb"
|
|
150
152
|
FileUtils.mkdir_p migration_path
|
|
151
|
-
|
|
152
|
-
File.open(file_name, "w"){ |f| f.write <<-MIGRATION }
|
|
153
|
+
File.open(file_name, "w") { |f| f.write <<-RUBY }
|
|
153
154
|
# This migration was auto-generated via `rake db:generate_trigger_migration'.
|
|
154
155
|
# While you can edit this file, any changes you make to the definitions here
|
|
155
156
|
# will be undone by the next auto-generated trigger migration.
|
|
156
157
|
|
|
157
|
-
class #{migration_name} < ActiveRecord::Migration
|
|
158
|
-
def
|
|
158
|
+
class #{migration_name} < ActiveRecord::Migration[#{ActiveRecord::Migration.current_version}]
|
|
159
|
+
def up
|
|
159
160
|
#{(up_drop_triggers + up_create_triggers).map{ |t| t.to_ruby(' ') }.join("\n\n").lstrip}
|
|
160
161
|
end
|
|
161
162
|
|
|
162
|
-
def
|
|
163
|
+
def down
|
|
163
164
|
#{(down_drop_triggers + down_create_triggers).map{ |t| t.to_ruby(' ') }.join("\n\n").lstrip}
|
|
164
165
|
end
|
|
165
166
|
end
|
|
166
|
-
|
|
167
|
+
RUBY
|
|
167
168
|
file_name
|
|
168
169
|
end
|
|
169
170
|
|
|
@@ -225,11 +226,9 @@ end
|
|
|
225
226
|
end
|
|
226
227
|
end
|
|
227
228
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
ActiveRecord::Migrator.send :extend, HairTrigger::Migrator
|
|
231
|
-
else
|
|
229
|
+
ActiveSupport.on_load(:active_record) do
|
|
230
|
+
ActiveRecord::Base.send :extend, HairTrigger::Base
|
|
232
231
|
ActiveRecord::Migration.send :include, HairTrigger::Migrator
|
|
232
|
+
ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval { include HairTrigger::Adapter }
|
|
233
|
+
ActiveRecord::SchemaDumper.class_eval { include HairTrigger::SchemaDumper }
|
|
233
234
|
end
|
|
234
|
-
ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval { include HairTrigger::Adapter }
|
|
235
|
-
ActiveRecord::SchemaDumper.class_eval { include HairTrigger::SchemaDumper }
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hairtrigger
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.25
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jon Jensen
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2022-01-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -16,14 +16,20 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '5.0'
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '8'
|
|
20
23
|
type: :runtime
|
|
21
24
|
prerelease: false
|
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
26
|
requirements:
|
|
24
27
|
- - ">="
|
|
25
28
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
29
|
+
version: '5.0'
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '8'
|
|
27
33
|
- !ruby/object:Gem::Dependency
|
|
28
34
|
name: ruby_parser
|
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -73,22 +79,6 @@ files:
|
|
|
73
79
|
- lib/hair_trigger/version.rb
|
|
74
80
|
- lib/hairtrigger.rb
|
|
75
81
|
- lib/tasks/hair_trigger.rake
|
|
76
|
-
- spec/adapter_spec.rb
|
|
77
|
-
- spec/builder_spec.rb
|
|
78
|
-
- spec/migrations-3.2/20110331212003_initial_tables.rb
|
|
79
|
-
- spec/migrations-3.2/20110331212631_user_trigger.rb
|
|
80
|
-
- spec/migrations-3.2/20110417185102_manual_user_trigger.rb
|
|
81
|
-
- spec/migrations-pre-3.1/20110331212003_initial_tables.rb
|
|
82
|
-
- spec/migrations-pre-3.1/20110331212631_user_trigger.rb
|
|
83
|
-
- spec/migrations-pre-3.1/20110417185102_manual_user_trigger.rb
|
|
84
|
-
- spec/migrations/20110331212003_initial_tables.rb
|
|
85
|
-
- spec/migrations/20110331212631_user_trigger.rb
|
|
86
|
-
- spec/migrations/20110417185102_manual_user_trigger.rb
|
|
87
|
-
- spec/migrations_spec.rb
|
|
88
|
-
- spec/models/user.rb
|
|
89
|
-
- spec/models/user_group.rb
|
|
90
|
-
- spec/schema_dumper_spec.rb
|
|
91
|
-
- spec/spec_helper.rb
|
|
92
82
|
homepage: http://github.com/jenseng/hair_trigger
|
|
93
83
|
licenses:
|
|
94
84
|
- MIT
|
|
@@ -101,15 +91,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
101
91
|
requirements:
|
|
102
92
|
- - ">="
|
|
103
93
|
- !ruby/object:Gem::Version
|
|
104
|
-
version:
|
|
94
|
+
version: 2.3.0
|
|
105
95
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
96
|
requirements:
|
|
107
97
|
- - ">="
|
|
108
98
|
- !ruby/object:Gem::Version
|
|
109
|
-
version:
|
|
99
|
+
version: '0'
|
|
110
100
|
requirements: []
|
|
111
|
-
|
|
112
|
-
rubygems_version: 2.6.13
|
|
101
|
+
rubygems_version: 3.0.3
|
|
113
102
|
signing_key:
|
|
114
103
|
specification_version: 4
|
|
115
104
|
summary: easy database triggers for active record
|
data/spec/adapter_spec.rb
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
# for this spec to work, you need to have postgres and mysql installed (in
|
|
4
|
-
# addition to the gems), and you should make sure that you have set up
|
|
5
|
-
# appropriate users and permissions. see database.yml for more info
|
|
6
|
-
|
|
7
|
-
describe "adapter" do
|
|
8
|
-
include_context "hairtrigger utils"
|
|
9
|
-
|
|
10
|
-
describe ".triggers" do
|
|
11
|
-
before do
|
|
12
|
-
reset_tmp(:migration_glob => "*initial_tables*")
|
|
13
|
-
initialize_db
|
|
14
|
-
migrate_db
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
shared_examples_for "mysql" do
|
|
18
|
-
# have to stub SHOW TRIGGERS to get back a '%' host, since GRANTs
|
|
19
|
-
# and such get a little dicey for testing (local vs travis, etc.)
|
|
20
|
-
it "matches the generated trigger with a '%' grant" do
|
|
21
|
-
conn.instance_variable_get(:@config)[:host] = "somehost" # wheeeee!
|
|
22
|
-
implicit_definer = "'root'@'somehost'"
|
|
23
|
-
show_triggers_definer = "root@%"
|
|
24
|
-
|
|
25
|
-
builder = trigger.on(:users).before(:insert){ "UPDATE foos SET bar = 1" }
|
|
26
|
-
triggers = builder.generate.select{|t|t !~ /\ADROP/}
|
|
27
|
-
expect(conn).to receive(:implicit_mysql_definer).and_return(implicit_definer)
|
|
28
|
-
expect(conn).to receive(:select_rows).with("SHOW TRIGGERS").and_return([
|
|
29
|
-
['users_before_insert_row_tr', 'INSERT', 'users', "BEGIN\n UPDATE foos SET bar = 1;\nEND", 'BEFORE', 'NULL', 'STRICT_ALL_TABLES', show_triggers_definer]
|
|
30
|
-
])
|
|
31
|
-
|
|
32
|
-
expect(db_triggers).to eq(triggers)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it "quotes table names" do
|
|
36
|
-
conn.execute <<-SQL
|
|
37
|
-
CREATE TRIGGER foos_tr AFTER DELETE ON users
|
|
38
|
-
FOR EACH ROW
|
|
39
|
-
BEGIN
|
|
40
|
-
UPDATE user_groups SET bob_count = bob_count - 1;
|
|
41
|
-
END
|
|
42
|
-
SQL
|
|
43
|
-
|
|
44
|
-
expect(conn.triggers["foos_tr"]).to match(/CREATE TRIGGER foos_tr AFTER DELETE ON `users`/)
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
context "mysql" do
|
|
49
|
-
let(:adapter) { :mysql }
|
|
50
|
-
it_behaves_like "mysql"
|
|
51
|
-
end if ADAPTERS.include? :mysql
|
|
52
|
-
|
|
53
|
-
context "mysql2" do
|
|
54
|
-
let(:adapter) { :mysql2 }
|
|
55
|
-
it_behaves_like "mysql"
|
|
56
|
-
end if ADAPTERS.include? :mysql2
|
|
57
|
-
|
|
58
|
-
context "postgresql" do
|
|
59
|
-
let(:adapter) { :postgresql }
|
|
60
|
-
|
|
61
|
-
it "quotes table names" do
|
|
62
|
-
conn.execute <<-SQL
|
|
63
|
-
CREATE FUNCTION foos_tr()
|
|
64
|
-
RETURNS TRIGGER AS $$
|
|
65
|
-
BEGIN
|
|
66
|
-
UPDATE user_groups SET bob_count = bob_count - 1;
|
|
67
|
-
END;
|
|
68
|
-
$$ LANGUAGE plpgsql;
|
|
69
|
-
|
|
70
|
-
CREATE TRIGGER foos_tr AFTER DELETE ON users
|
|
71
|
-
FOR EACH ROW EXECUTE PROCEDURE foos_tr();
|
|
72
|
-
SQL
|
|
73
|
-
|
|
74
|
-
expect(conn.triggers["foos_tr"]).to match(/CREATE TRIGGER foos_tr AFTER DELETE ON "users"/)
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
context "sqlite3" do
|
|
79
|
-
let(:adapter) { :sqlite3 }
|
|
80
|
-
|
|
81
|
-
it "quotes table names" do
|
|
82
|
-
conn.execute <<-SQL
|
|
83
|
-
CREATE TRIGGER foos_tr AFTER DELETE ON users
|
|
84
|
-
FOR EACH ROW
|
|
85
|
-
BEGIN
|
|
86
|
-
UPDATE user_groups SET bob_count = bob_count - 1;
|
|
87
|
-
END;
|
|
88
|
-
SQL
|
|
89
|
-
|
|
90
|
-
expect(conn.triggers["foos_tr"]).to match(/CREATE TRIGGER foos_tr AFTER DELETE ON "users"/)
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|