hairtrigger 0.2.21 → 0.2.25
Sign up to get free protection for your applications and to get access to all the features.
- 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
|