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
data/spec/builder_spec.rb
DELETED
|
@@ -1,433 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
HairTrigger::Builder.show_warnings = false
|
|
4
|
-
|
|
5
|
-
class MockAdapter
|
|
6
|
-
attr_reader :adapter_name
|
|
7
|
-
def initialize(type, methods = {})
|
|
8
|
-
@adapter_name = type
|
|
9
|
-
methods.each do |key, value|
|
|
10
|
-
instance_eval("def #{key}; #{value.inspect}; end")
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def quote_table_name(table)
|
|
15
|
-
table
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def builder(name = nil)
|
|
20
|
-
HairTrigger::Builder.new(name, :adapter => @adapter)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
describe "builder" do
|
|
24
|
-
context "chaining" do
|
|
25
|
-
it "should use the last redundant chained call" do
|
|
26
|
-
@adapter = MockAdapter.new("mysql")
|
|
27
|
-
builder.where(:foo).where(:bar).options[:where].should be(:bar)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
context "generation" do
|
|
32
|
-
it "should tack on a semicolon if none is provided" do
|
|
33
|
-
@adapter = MockAdapter.new("mysql")
|
|
34
|
-
builder.on(:foos).after(:update){ "FOO " }.generate.
|
|
35
|
-
grep(/FOO;/).size.should eql(1)
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
context "comparison" do
|
|
40
|
-
it "should view identical triggers as identical" do
|
|
41
|
-
@adapter = MockAdapter.new("mysql")
|
|
42
|
-
builder.on(:foos).after(:update){ "FOO" }.
|
|
43
|
-
should eql(builder.on(:foos).after(:update){ "FOO" })
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
it "should view incompatible triggers as different" do
|
|
47
|
-
@adapter = MockAdapter.new("mysql")
|
|
48
|
-
HairTrigger::Builder.new(nil, :adapter => @adapter, :compatibility => 0).on(:foos).after(:update){ "FOO" }.
|
|
49
|
-
should_not eql(builder.on(:foos).after(:update){ "FOO" })
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
describe "name" do
|
|
54
|
-
it "should be inferred if none is provided" do
|
|
55
|
-
builder.on(:foos).after(:update){ "foo" }.prepared_name.
|
|
56
|
-
should == "foos_after_update_row_tr"
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
it "should respect the last chained name" do
|
|
60
|
-
builder("lolwut").on(:foos).after(:update){ "foo" }.prepared_name.
|
|
61
|
-
should == "lolwut"
|
|
62
|
-
builder("lolwut").on(:foos).name("zomg").after(:update).name("yolo"){ "foo" }.prepared_name.
|
|
63
|
-
should == "yolo"
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
describe "`of' columns" do
|
|
68
|
-
it "should be disallowed for non-update triggers" do
|
|
69
|
-
lambda {
|
|
70
|
-
builder.on(:foos).after(:insert).of(:bar, :baz){ "BAR" }
|
|
71
|
-
}.should raise_error /of may only be specified on update triggers/
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
describe "groups" do
|
|
76
|
-
it "should allow chained methods" do
|
|
77
|
-
triggers = builder.on(:foos){ |t|
|
|
78
|
-
t.where('bar=1').name('bar'){ 'BAR;' }
|
|
79
|
-
t.where('baz=1').name('baz'){ 'BAZ;' }
|
|
80
|
-
}.triggers
|
|
81
|
-
triggers.map(&:prepare!)
|
|
82
|
-
triggers.map(&:prepared_name).should == ['bar', 'baz']
|
|
83
|
-
triggers.map(&:prepared_where).should == ['bar=1', 'baz=1']
|
|
84
|
-
triggers.map(&:prepared_actions).should == ['BAR;', 'BAZ;']
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
context "adapter-specific actions" do
|
|
89
|
-
before(:each) do
|
|
90
|
-
@adapter = MockAdapter.new("mysql")
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
it "should generate the appropriate trigger for the adapter" do
|
|
94
|
-
sql = builder.on(:foos).after(:update).where('BAR'){
|
|
95
|
-
{:default => "DEFAULT", :mysql => "MYSQL"}
|
|
96
|
-
}.generate
|
|
97
|
-
|
|
98
|
-
sql.grep(/DEFAULT/).size.should eql(0)
|
|
99
|
-
sql.grep(/MYSQL/).size.should eql(1)
|
|
100
|
-
|
|
101
|
-
sql = builder.on(:foos).after(:update).where('BAR'){
|
|
102
|
-
{:default => "DEFAULT", :postgres => "POSTGRES"}
|
|
103
|
-
}.generate
|
|
104
|
-
|
|
105
|
-
sql.grep(/POSTGRES/).size.should eql(0)
|
|
106
|
-
sql.grep(/DEFAULT/).size.should eql(1)
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
it "should complain if no actions are provided for this adapter" do
|
|
110
|
-
lambda {
|
|
111
|
-
builder.on(:foos).after(:update).where('BAR'){ {:postgres => "POSTGRES"} }.generate
|
|
112
|
-
}.should raise_error
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
context "mysql" do
|
|
117
|
-
before(:each) do
|
|
118
|
-
@adapter = MockAdapter.new("mysql")
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
it "should create a single trigger for a group" do
|
|
122
|
-
trigger = builder.on(:foos).after(:update){ |t|
|
|
123
|
-
t.where('BAR'){ 'BAR' }
|
|
124
|
-
t.where('BAZ'){ 'BAZ' }
|
|
125
|
-
}
|
|
126
|
-
trigger.generate.grep(/CREATE.*TRIGGER/).size.should eql(1)
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
it "should disallow nested groups" do
|
|
130
|
-
lambda {
|
|
131
|
-
builder.on(:foos){ |t|
|
|
132
|
-
t.after(:update){ |t|
|
|
133
|
-
t.where('BAR'){ 'BAR' }
|
|
134
|
-
t.where('BAZ'){ 'BAZ' }
|
|
135
|
-
}
|
|
136
|
-
}.generate
|
|
137
|
-
}.should raise_error
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
it "should warn on explicit subtrigger names and no group name" do
|
|
141
|
-
trigger = builder.on(:foos){ |t|
|
|
142
|
-
t.where('bar=1').name('bar'){ 'BAR;' }
|
|
143
|
-
t.where('baz=1').name('baz'){ 'BAZ;' }
|
|
144
|
-
}
|
|
145
|
-
trigger.warnings.size.should == 1
|
|
146
|
-
trigger.warnings.first.first.should =~ /nested triggers have explicit names/
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
it "should accept security" do
|
|
150
|
-
builder.on(:foos).after(:update).security(:definer){ "FOO" }.generate.
|
|
151
|
-
grep(/DEFINER/).size.should eql(0) # default, so we don't include it
|
|
152
|
-
builder.on(:foos).after(:update).security("CURRENT_USER"){ "FOO" }.generate.
|
|
153
|
-
grep(/DEFINER = CURRENT_USER/).size.should eql(1)
|
|
154
|
-
builder.on(:foos).after(:update).security("'user'@'host'"){ "FOO" }.generate.
|
|
155
|
-
grep(/DEFINER = 'user'@'host'/).size.should eql(1)
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
it "should infer `if' conditionals from `of' columns" do
|
|
159
|
-
builder.on(:foos).after(:update).of(:bar){ "BAZ" }.generate.join("\n").
|
|
160
|
-
should include("IF NEW.bar <> OLD.bar OR (NEW.bar IS NULL) <> (OLD.bar IS NULL) THEN")
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
it "should merge `where` and `of` into an `if` conditional" do
|
|
164
|
-
builder.on(:foos).after(:update).of(:bar).where("lol"){ "BAZ" }.generate.join("\n").
|
|
165
|
-
should include("IF (lol) AND (NEW.bar <> OLD.bar OR (NEW.bar IS NULL) <> (OLD.bar IS NULL)) THEN")
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
it "should reject :invoker security" do
|
|
169
|
-
lambda {
|
|
170
|
-
builder.on(:foos).after(:update).security(:invoker){ "FOO" }.generate
|
|
171
|
-
}.should raise_error
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
it "should reject for_each :statement" do
|
|
175
|
-
lambda {
|
|
176
|
-
builder.on(:foos).after(:update).for_each(:statement){ "FOO" }.generate
|
|
177
|
-
}.should raise_error
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
it "should reject multiple events" do
|
|
181
|
-
lambda {
|
|
182
|
-
builder.on(:foos).after(:update, :delete){ "FOO" }.generate
|
|
183
|
-
}.should raise_error
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
it "should reject truncate" do
|
|
187
|
-
lambda {
|
|
188
|
-
builder.on(:foos).after(:truncate){ "FOO" }.generate
|
|
189
|
-
}.should raise_error
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
describe "#to_ruby" do
|
|
193
|
-
it "should fully represent the builder" do
|
|
194
|
-
code = <<-CODE.strip.gsub(/^ +/, '')
|
|
195
|
-
on("foos").
|
|
196
|
-
security(:definer).
|
|
197
|
-
for_each(:row).
|
|
198
|
-
before(:update) do |t|
|
|
199
|
-
t.where("NEW.foo") do
|
|
200
|
-
"FOO;"
|
|
201
|
-
end
|
|
202
|
-
end
|
|
203
|
-
CODE
|
|
204
|
-
b = builder
|
|
205
|
-
b.instance_eval(code)
|
|
206
|
-
b.to_ruby.strip.gsub(/^ +/, '').should be_include(code)
|
|
207
|
-
end
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
context "postgresql" do
|
|
212
|
-
before(:each) do
|
|
213
|
-
@adapter = MockAdapter.new("postgresql", :postgresql_version => 94000)
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
it "should create multiple triggers for a group" do
|
|
217
|
-
trigger = builder.on(:foos).after(:update){ |t|
|
|
218
|
-
t.where('BAR'){ 'BAR' }
|
|
219
|
-
t.where('BAZ'){ 'BAZ' }
|
|
220
|
-
}
|
|
221
|
-
trigger.generate.grep(/CREATE.*TRIGGER/).size.should eql(2)
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
it "should allow nested groups" do
|
|
225
|
-
trigger = builder.on(:foos){ |t|
|
|
226
|
-
t.after(:update){ |t|
|
|
227
|
-
t.where('BAR'){ 'BAR' }
|
|
228
|
-
t.where('BAZ'){ 'BAZ' }
|
|
229
|
-
}
|
|
230
|
-
t.after(:insert){ 'BAZ' }
|
|
231
|
-
}
|
|
232
|
-
trigger.generate.grep(/CREATE.*TRIGGER/).size.should eql(3)
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
it "should warn on an explicit group names and no subtrigger names" do
|
|
236
|
-
trigger = builder.on(:foos).name('foos'){ |t|
|
|
237
|
-
t.where('bar=1'){ 'BAR;' }
|
|
238
|
-
t.where('baz=1'){ 'BAZ;' }
|
|
239
|
-
}
|
|
240
|
-
trigger.warnings.size.should == 1
|
|
241
|
-
trigger.warnings.first.first.should =~ /trigger group has an explicit name/
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
it "should accept `of' columns" do
|
|
245
|
-
trigger = builder.on(:foos).after(:update).of(:bar, :baz){ "BAR" }
|
|
246
|
-
trigger.generate.grep(/AFTER UPDATE OF bar, baz/).size.should eql(1)
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
it "should accept security" do
|
|
250
|
-
builder.on(:foos).after(:update).security(:invoker){ "FOO" }.generate.
|
|
251
|
-
grep(/SECURITY/).size.should eql(0) # default, so we don't include it
|
|
252
|
-
builder.on(:foos).after(:update).security(:definer){ "FOO" }.generate.
|
|
253
|
-
grep(/SECURITY DEFINER/).size.should eql(1)
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
it "should reject arbitrary user security" do
|
|
257
|
-
lambda {
|
|
258
|
-
builder.on(:foos).after(:update).security("'user'@'host'"){ "FOO" }.
|
|
259
|
-
generate
|
|
260
|
-
}.should raise_error
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
it "should accept multiple events" do
|
|
264
|
-
builder.on(:foos).after(:update, :delete){ "FOO" }.generate.
|
|
265
|
-
grep(/UPDATE OR DELETE/).size.should eql(1)
|
|
266
|
-
end
|
|
267
|
-
|
|
268
|
-
it "should reject long names" do
|
|
269
|
-
lambda {
|
|
270
|
-
builder.name('A'*65).on(:foos).after(:update){ "FOO" }.generate
|
|
271
|
-
}.should raise_error
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
it "should allow truncate with for_each statement" do
|
|
275
|
-
builder.on(:foos).after(:truncate).for_each(:statement){ "FOO" }.generate.
|
|
276
|
-
grep(/TRUNCATE.*FOR EACH STATEMENT/m).size.should eql(1)
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
it "should reject truncate with for_each row" do
|
|
280
|
-
lambda {
|
|
281
|
-
builder.on(:foos).after(:truncate){ "FOO" }.generate
|
|
282
|
-
}.should raise_error
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
it "should add a return statement if none is provided" do
|
|
286
|
-
builder.on(:foos).after(:update){ "FOO" }.generate.
|
|
287
|
-
grep(/RETURN NULL;/).size.should eql(1)
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
it "should not wrap the action in a function" do
|
|
291
|
-
builder.on(:foos).after(:update).nowrap{ 'existing_procedure()' }.generate.
|
|
292
|
-
grep(/CREATE FUNCTION/).size.should eql(0)
|
|
293
|
-
end
|
|
294
|
-
|
|
295
|
-
it "should reject combined use of security and nowrap" do
|
|
296
|
-
lambda {
|
|
297
|
-
builder.on(:foos).after(:update).security("'user'@'host'").nowrap{ "FOO" }.generate
|
|
298
|
-
}.should raise_error
|
|
299
|
-
end
|
|
300
|
-
|
|
301
|
-
it "should allow variable declarations" do
|
|
302
|
-
builder.on(:foos).after(:insert).declare("foo INT"){ "FOO" }.generate.join("\n").
|
|
303
|
-
should match(/DECLARE\s*foo INT;\s*BEGIN\s*FOO/)
|
|
304
|
-
end
|
|
305
|
-
|
|
306
|
-
context "legacy" do
|
|
307
|
-
it "should reject truncate pre-8.4" do
|
|
308
|
-
@adapter = MockAdapter.new("postgresql", :postgresql_version => 80300)
|
|
309
|
-
lambda {
|
|
310
|
-
builder.on(:foos).after(:truncate).for_each(:statement){ "FOO" }.generate
|
|
311
|
-
}.should raise_error
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
it "should use conditionals pre-9.0" do
|
|
315
|
-
@adapter = MockAdapter.new("postgresql", :postgresql_version => 80400)
|
|
316
|
-
builder.on(:foos).after(:insert).where("BAR"){ "FOO" }.generate.
|
|
317
|
-
grep(/IF BAR/).size.should eql(1)
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
it "should reject combined use of where and nowrap pre-9.0" do
|
|
321
|
-
@adapter = MockAdapter.new("postgresql", :postgresql_version => 80400)
|
|
322
|
-
lambda {
|
|
323
|
-
builder.on(:foos).after(:insert).where("BAR").nowrap{ "FOO" }.generate
|
|
324
|
-
}.should raise_error
|
|
325
|
-
end
|
|
326
|
-
|
|
327
|
-
it "should infer `if' conditionals from `of' columns on pre-9.0" do
|
|
328
|
-
@adapter = MockAdapter.new("postgresql", :postgresql_version => 80400)
|
|
329
|
-
builder.on(:foos).after(:update).of(:bar){ "BAZ" }.generate.join("\n").
|
|
330
|
-
should include("IF NEW.bar <> OLD.bar OR (NEW.bar IS NULL) <> (OLD.bar IS NULL) THEN")
|
|
331
|
-
end
|
|
332
|
-
end
|
|
333
|
-
|
|
334
|
-
describe "#to_ruby" do
|
|
335
|
-
it "should fully represent the builder" do
|
|
336
|
-
code = <<-CODE.strip.gsub(/^ +/, '')
|
|
337
|
-
on("foos").
|
|
338
|
-
of("bar").
|
|
339
|
-
security(:invoker).
|
|
340
|
-
for_each(:row).
|
|
341
|
-
before(:update) do |t|
|
|
342
|
-
t.where("NEW.foo").declare("row RECORD") do
|
|
343
|
-
"FOO;"
|
|
344
|
-
end
|
|
345
|
-
end
|
|
346
|
-
CODE
|
|
347
|
-
b = builder
|
|
348
|
-
b.instance_eval(code)
|
|
349
|
-
b.to_ruby.strip.gsub(/^ +/, '').should be_include(code)
|
|
350
|
-
end
|
|
351
|
-
end
|
|
352
|
-
end
|
|
353
|
-
|
|
354
|
-
context "sqlite" do
|
|
355
|
-
before(:each) do
|
|
356
|
-
@adapter = MockAdapter.new("sqlite")
|
|
357
|
-
end
|
|
358
|
-
|
|
359
|
-
it "should create multiple triggers for a group" do
|
|
360
|
-
trigger = builder.on(:foos).after(:update){ |t|
|
|
361
|
-
t.where('BAR'){ 'BAR' }
|
|
362
|
-
t.where('BAZ'){ 'BAZ' }
|
|
363
|
-
}
|
|
364
|
-
trigger.generate.grep(/CREATE.*TRIGGER/).size.should eql(2)
|
|
365
|
-
end
|
|
366
|
-
|
|
367
|
-
it "should allow nested groups" do
|
|
368
|
-
trigger = builder.on(:foos){ |t|
|
|
369
|
-
t.after(:update){ |t|
|
|
370
|
-
t.where('BAR'){ 'BAR' }
|
|
371
|
-
t.where('BAZ'){ 'BAZ' }
|
|
372
|
-
}
|
|
373
|
-
t.after(:insert){ 'BAZ' }
|
|
374
|
-
}
|
|
375
|
-
trigger.generate.grep(/CREATE.*TRIGGER/).size.should eql(3)
|
|
376
|
-
end
|
|
377
|
-
|
|
378
|
-
it "should warn on an explicit group names and no subtrigger names" do
|
|
379
|
-
trigger = builder.on(:foos).name('foos'){ |t|
|
|
380
|
-
t.where('bar=1'){ 'BAR;' }
|
|
381
|
-
t.where('baz=1'){ 'BAZ;' }
|
|
382
|
-
}
|
|
383
|
-
trigger.warnings.size.should == 1
|
|
384
|
-
trigger.warnings.first.first.should =~ /trigger group has an explicit name/
|
|
385
|
-
end
|
|
386
|
-
|
|
387
|
-
it "should accept `of' columns" do
|
|
388
|
-
trigger = builder.on(:foos).after(:update).of(:bar, :baz){ "BAR" }
|
|
389
|
-
trigger.generate.grep(/AFTER UPDATE OF bar, baz/).size.should eql(1)
|
|
390
|
-
end
|
|
391
|
-
|
|
392
|
-
it "should reject security" do
|
|
393
|
-
lambda {
|
|
394
|
-
builder.on(:foos).after(:update).security(:definer){ "FOO" }.generate
|
|
395
|
-
}.should raise_error
|
|
396
|
-
end
|
|
397
|
-
|
|
398
|
-
it "should reject for_each :statement" do
|
|
399
|
-
lambda {
|
|
400
|
-
builder.on(:foos).after(:update).for_each(:statement){ "FOO" }.generate
|
|
401
|
-
}.should raise_error
|
|
402
|
-
end
|
|
403
|
-
|
|
404
|
-
it "should reject multiple events" do
|
|
405
|
-
lambda {
|
|
406
|
-
builder.on(:foos).after(:update, :delete){ "FOO" }.generate
|
|
407
|
-
}.should raise_error
|
|
408
|
-
end
|
|
409
|
-
|
|
410
|
-
it "should reject truncate" do
|
|
411
|
-
lambda {
|
|
412
|
-
builder.on(:foos).after(:truncate){ "FOO" }.generate
|
|
413
|
-
}.should raise_error
|
|
414
|
-
end
|
|
415
|
-
|
|
416
|
-
describe "#to_ruby" do
|
|
417
|
-
it "should fully represent the builder" do
|
|
418
|
-
code = <<-CODE.strip.gsub(/^ +/, '')
|
|
419
|
-
on("foos").
|
|
420
|
-
of("bar").
|
|
421
|
-
before(:update) do |t|
|
|
422
|
-
t.where("NEW.foo") do
|
|
423
|
-
"FOO;"
|
|
424
|
-
end
|
|
425
|
-
end
|
|
426
|
-
CODE
|
|
427
|
-
b = builder
|
|
428
|
-
b.instance_eval(code)
|
|
429
|
-
b.to_ruby.strip.gsub(/^ +/, '').should be_include(code)
|
|
430
|
-
end
|
|
431
|
-
end
|
|
432
|
-
end
|
|
433
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
class InitialTables < ActiveRecord::Migration[5.0]
|
|
2
|
-
def up
|
|
3
|
-
create_table "users" do |t|
|
|
4
|
-
t.integer "user_group_id"
|
|
5
|
-
t.string "name"
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
create_table "user_groups" do |t|
|
|
9
|
-
t.integer "bob_count", :default => 0
|
|
10
|
-
t.integer "updated_joe_count", :default => 0
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def down
|
|
15
|
-
drop_table "users"
|
|
16
|
-
drop_table "user_groups"
|
|
17
|
-
end
|
|
18
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# This migration was auto-generated via `rake db:generate_trigger_migration'.
|
|
2
|
-
# While you can edit this file, any changes you make to the definitions here
|
|
3
|
-
# will be undone by the next auto-generated trigger migration.
|
|
4
|
-
|
|
5
|
-
class UserTrigger < ActiveRecord::Migration[5.0]
|
|
6
|
-
def up
|
|
7
|
-
create_trigger("users_after_insert_row_when_new_name_bob__tr", :generated => true, :compatibility => 1).
|
|
8
|
-
on("users").
|
|
9
|
-
after(:insert).
|
|
10
|
-
where("NEW.name = 'bob'") do
|
|
11
|
-
"UPDATE user_groups SET bob_count = bob_count + 1"
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def down
|
|
16
|
-
drop_trigger("users_after_insert_row_when_new_name_bob__tr", "users")
|
|
17
|
-
end
|
|
18
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
class InitialTables < ActiveRecord::Migration
|
|
2
|
-
def up
|
|
3
|
-
create_table "users" do |t|
|
|
4
|
-
t.integer "user_group_id"
|
|
5
|
-
t.string "name"
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
create_table "user_groups" do |t|
|
|
9
|
-
t.integer "bob_count", :default => 0
|
|
10
|
-
t.integer "updated_joe_count", :default => 0
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def down
|
|
15
|
-
drop_table "users"
|
|
16
|
-
drop_table "user_groups"
|
|
17
|
-
end
|
|
18
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# This migration was auto-generated via `rake db:generate_trigger_migration'.
|
|
2
|
-
# While you can edit this file, any changes you make to the definitions here
|
|
3
|
-
# will be undone by the next auto-generated trigger migration.
|
|
4
|
-
|
|
5
|
-
class UserTrigger < ActiveRecord::Migration
|
|
6
|
-
def up
|
|
7
|
-
create_trigger("users_after_insert_row_when_new_name_bob__tr", :generated => true, :compatibility => 1).
|
|
8
|
-
on("users").
|
|
9
|
-
after(:insert).
|
|
10
|
-
where("NEW.name = 'bob'") do
|
|
11
|
-
"UPDATE user_groups SET bob_count = bob_count + 1"
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def down
|
|
16
|
-
drop_trigger("users_after_insert_row_when_new_name_bob__tr", "users")
|
|
17
|
-
end
|
|
18
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
class InitialTables < ActiveRecord::Migration
|
|
2
|
-
def self.up
|
|
3
|
-
create_table "users" do |t|
|
|
4
|
-
t.integer "user_group_id"
|
|
5
|
-
t.string "name"
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
create_table "user_groups" do |t|
|
|
9
|
-
t.integer "bob_count", :default => 0
|
|
10
|
-
t.integer "updated_joe_count", :default => 0
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def self.down
|
|
15
|
-
drop_table "users"
|
|
16
|
-
drop_table "user_groups"
|
|
17
|
-
end
|
|
18
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# This migration was auto-generated via `rake db:generate_trigger_migration'.
|
|
2
|
-
# While you can edit this file, any changes you make to the definitions here
|
|
3
|
-
# will be undone by the next auto-generated trigger migration.
|
|
4
|
-
|
|
5
|
-
class UserTrigger < ActiveRecord::Migration
|
|
6
|
-
def self.up
|
|
7
|
-
create_trigger("users_after_insert_row_when_new_name_bob__tr", :generated => true, :compatibility => 1).
|
|
8
|
-
on("users").
|
|
9
|
-
after(:insert).
|
|
10
|
-
where("NEW.name = 'bob'") do
|
|
11
|
-
"UPDATE user_groups SET bob_count = bob_count + 1"
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def self.down
|
|
16
|
-
drop_trigger("users_after_insert_row_when_new_name_bob__tr", "users")
|
|
17
|
-
end
|
|
18
|
-
end
|
data/spec/migrations_spec.rb
DELETED
|
@@ -1,60 +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 "migrations" do
|
|
8
|
-
include_context "hairtrigger utils"
|
|
9
|
-
let(:adapter) { :sqlite3 }
|
|
10
|
-
|
|
11
|
-
describe "migrations_current?" do
|
|
12
|
-
|
|
13
|
-
it "should return false if there are pending model triggers" do
|
|
14
|
-
reset_tmp(:migration_glob => "*initial_tables*")
|
|
15
|
-
initialize_db
|
|
16
|
-
HairTrigger.should_not be_migrations_current
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
it "should return true if migrations are current" do
|
|
20
|
-
# just one trigger migration
|
|
21
|
-
reset_tmp(:migration_glob => "20110331212*")
|
|
22
|
-
initialize_db
|
|
23
|
-
migrate_db
|
|
24
|
-
HairTrigger.should be_migrations_current
|
|
25
|
-
|
|
26
|
-
# or multiple
|
|
27
|
-
reset_tmp
|
|
28
|
-
initialize_db
|
|
29
|
-
migrate_db
|
|
30
|
-
HairTrigger.should be_migrations_current
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
it "should return true even if migrations haven't run" do
|
|
34
|
-
reset_tmp
|
|
35
|
-
initialize_db
|
|
36
|
-
migrate_db
|
|
37
|
-
HairTrigger.should be_migrations_current
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
describe "current_triggers" do
|
|
42
|
-
it "should be inferred from self.up methods" do
|
|
43
|
-
reset_tmp(:migration_glob => "20110331212*")
|
|
44
|
-
initialize_db
|
|
45
|
-
|
|
46
|
-
migrations = HairTrigger.current_migrations
|
|
47
|
-
migrations.size.should == 1
|
|
48
|
-
migrations[0][1].prepared_name.should == "users_after_insert_row_when_new_name_bob__tr"
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
it "should not be inferred from change methods" do
|
|
52
|
-
reset_tmp(:migration_glob => "*manual*")
|
|
53
|
-
replace_file_contents("tmp/migrations/20110417185102_manual_user_trigger.rb", "def up", "def change")
|
|
54
|
-
initialize_db
|
|
55
|
-
|
|
56
|
-
migrations = HairTrigger.current_migrations
|
|
57
|
-
migrations.size.should == 0
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
data/spec/models/user.rb
DELETED
data/spec/models/user_group.rb
DELETED