hairtrigger 0.2.8 → 0.2.9

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.
data/README.md CHANGED
@@ -83,6 +83,11 @@ Required (but may be satisified by `before`/`after`). Possible values are `:befo
83
83
  #### events(*events)
84
84
  Required (but may be satisified by `before`/`after`). Possible values are `:insert`/`:update`/`:delete`/`:truncate`. MySQL/SQLite only support one action per trigger, and don't support `:truncate`.
85
85
 
86
+ #### nowrap(flag = true)
87
+ 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.
88
+
89
+ Example: `trigger.after(:update).nowrap { "tsvector_update_trigger(...)" }`
90
+
86
91
  #### all
87
92
  Noop, useful for trigger groups (see below).
88
93
 
@@ -4,7 +4,7 @@ module HairTrigger
4
4
  class Builder
5
5
  class DeclarationError < StandardError; end
6
6
  class GenerationError < StandardError; end
7
-
7
+
8
8
  attr_accessor :options
9
9
  attr_reader :triggers # nil unless this is a trigger group
10
10
  attr_reader :prepared_actions, :prepared_where # after delayed interpolation
@@ -73,6 +73,10 @@ module HairTrigger
73
73
  options[:where] = where
74
74
  end
75
75
 
76
+ def nowrap(flag = true)
77
+ options[:nowrap] = flag
78
+ end
79
+
76
80
  # noop, just a way you can pass a block within a trigger group
77
81
  def all
78
82
  end
@@ -146,7 +150,7 @@ module HairTrigger
146
150
  METHOD
147
151
  end
148
152
  end
149
- chainable_methods :name, :on, :for_each, :before, :after, :where, :security, :timing, :events, :all
153
+ chainable_methods :name, :on, :for_each, :before, :after, :where, :security, :timing, :events, :all, :nowrap
150
154
 
151
155
  def create_grouped_trigger?
152
156
  adapter_name == :mysql
@@ -366,36 +370,48 @@ END;
366
370
  def generate_trigger_postgresql
367
371
  raise GenerationError, "truncate triggers are only supported on postgres 8.4 and greater" if db_version < 80400 && options[:events].include?('TRUNCATE')
368
372
  raise GenerationError, "FOR EACH ROW triggers may not be triggered by truncate events" if options[:for_each] == 'ROW' && options[:events].include?('TRUNCATE')
369
- security = options[:security] if options[:security] && options[:security] != :invoker
370
- sql = <<-SQL
373
+ raise GenerationError, "security cannot be used in conjunction with nowrap" if options[:nowrap] && options[:security]
374
+ raise GenerationError, "where can only be used in conjunction with nowrap on postgres 9.0 and greater" if options[:nowrap] && prepared_where && db_version < 90000
375
+
376
+ sql = ''
377
+
378
+ if options[:nowrap]
379
+ trigger_action = raw_actions
380
+ else
381
+ security = options[:security] if options[:security] && options[:security] != :invoker
382
+ sql << <<-SQL
371
383
  CREATE FUNCTION #{prepared_name}()
372
384
  RETURNS TRIGGER AS $$
373
385
  BEGIN
374
- SQL
375
- if prepared_where && db_version < 90000
376
- sql << normalize("IF #{prepared_where} THEN", 1)
377
- sql << normalize(raw_actions, 2)
378
- sql << normalize("END IF;", 1)
379
- else
380
- sql << normalize(raw_actions, 1)
381
- end
382
- # if no return is specified at the end, be sure we set a sane one
383
- unless raw_actions =~ /return [^;]+;\s*\z/i
384
- if options[:timing] == "AFTER" || options[:for_each] == 'STATEMENT'
385
- sql << normalize("RETURN NULL;", 1)
386
- elsif options[:events].include?('DELETE')
387
- sql << normalize("RETURN OLD;", 1)
386
+ SQL
387
+ if prepared_where && db_version < 90000
388
+ sql << normalize("IF #{prepared_where} THEN", 1)
389
+ sql << normalize(raw_actions, 2)
390
+ sql << normalize("END IF;", 1)
388
391
  else
389
- sql << normalize("RETURN NEW;", 1)
392
+ sql << normalize(raw_actions, 1)
390
393
  end
391
- end
392
- sql << <<-SQL
394
+ # if no return is specified at the end, be sure we set a sane one
395
+ unless raw_actions =~ /return [^;]+;\s*\z/i
396
+ if options[:timing] == "AFTER" || options[:for_each] == 'STATEMENT'
397
+ sql << normalize("RETURN NULL;", 1)
398
+ elsif options[:events].include?('DELETE')
399
+ sql << normalize("RETURN OLD;", 1)
400
+ else
401
+ sql << normalize("RETURN NEW;", 1)
402
+ end
403
+ end
404
+ sql << <<-SQL
393
405
  END;
394
406
  $$ LANGUAGE plpgsql#{security ? " SECURITY #{security.to_s.upcase}" : ""};
395
- SQL
407
+ SQL
408
+
409
+ trigger_action = "#{prepared_name}()"
410
+ end
411
+
396
412
  [sql, <<-SQL]
397
413
  CREATE TRIGGER #{prepared_name} #{options[:timing]} #{options[:events].join(" OR ")} ON #{options[:table]}
398
- FOR EACH #{options[:for_each]}#{prepared_where && db_version >= 90000 ? " WHEN (" + prepared_where + ')': ''} EXECUTE PROCEDURE #{prepared_name}();
414
+ FOR EACH #{options[:for_each]}#{prepared_where && db_version >= 90000 ? " WHEN (" + prepared_where + ')': ''} EXECUTE PROCEDURE #{trigger_action};
399
415
  SQL
400
416
  end
401
417
 
@@ -1,5 +1,5 @@
1
1
  module HairTrigger
2
- VERSION = "0.2.8"
2
+ VERSION = "0.2.9"
3
3
 
4
4
  def VERSION.<=>(other)
5
5
  split(/\./).map(&:to_i) <=> other.split(/\./).map(&:to_i)
data/spec/builder_spec.rb CHANGED
@@ -100,7 +100,7 @@ describe "builder" do
100
100
  }.should raise_error
101
101
  end
102
102
  end
103
-
103
+
104
104
  context "mysql" do
105
105
  before(:each) do
106
106
  @adapter = MockAdapter.new("mysql")
@@ -242,6 +242,17 @@ describe "builder" do
242
242
  grep(/RETURN NULL;/).size.should eql(1)
243
243
  end
244
244
 
245
+ it "should not wrap the action in a function" do
246
+ builder.on(:foos).after(:update).nowrap{ 'existing_procedure()' }.generate.
247
+ grep(/CREATE FUNCTION/).size.should eql(0)
248
+ end
249
+
250
+ it "should reject combined use of security and nowrap" do
251
+ lambda {
252
+ builder.on(:foos).after(:update).security("'user'@'host'").nowrap{ "FOO" }.generate
253
+ }.should raise_error
254
+ end
255
+
245
256
  context "legacy" do
246
257
  it "should reject truncate pre-8.4" do
247
258
  @adapter = MockAdapter.new("postgresql", :postgresql_version => 80300)
@@ -255,6 +266,13 @@ describe "builder" do
255
266
  builder.on(:foos).after(:insert).where("BAR"){ "FOO" }.generate.
256
267
  grep(/IF BAR/).size.should eql(1)
257
268
  end
269
+
270
+ it "should reject combined use of where and nowrap pre-9.0" do
271
+ @adapter = MockAdapter.new("postgresql", :postgresql_version => 80400)
272
+ lambda {
273
+ builder.on(:foos).after(:insert).where("BAR").nowrap{ "FOO" }.generate
274
+ }.should raise_error
275
+ end
258
276
  end
259
277
  end
260
278
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hairtrigger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8
4
+ version: 0.2.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-04-27 00:00:00.000000000 Z
12
+ date: 2014-05-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord