hairtrigger 0.2.12 → 0.2.13

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/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011 Jon Jensen
1
+ Copyright (c) 2011-2015 Jon Jensen
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -325,4 +325,4 @@ existing trigger if you wish to redefine it.
325
325
 
326
326
  ## Copyright
327
327
 
328
- Copyright (c) 2011-2014 Jon Jensen. See LICENSE.txt for further details.
328
+ Copyright (c) 2011-2015 Jon Jensen. See LICENSE.txt for further details.
data/lib/hair_trigger.rb CHANGED
@@ -157,21 +157,35 @@ end
157
157
  end
158
158
 
159
159
  def infer_migration_name(migration_names, create_triggers, drop_triggers)
160
- migration_base_name = if create_triggers.size > 0
161
- ("create trigger#{create_triggers.size > 1 ? 's' : ''} " +
162
- create_triggers.map{ |t| [t.options[:table], t.options[:events].join(" ")].join(" ") }.join(" and ")
163
- ).downcase.gsub(/[^a-z0-9_]/, '_').gsub(/_+/, '_').camelize
160
+ if create_triggers.size > 0
161
+ migration_base_name = "create trigger#{create_triggers.size > 1 ? 's' : ''} "
162
+ name_parts = create_triggers.map { |t| [t.options[:table], t.options[:events].join(" ")].join(" ") }.uniq
163
+ part_limit = 4
164
164
  else
165
- ("drop trigger#{drop_triggers.size > 1 ? 's' : ''} " +
166
- drop_triggers.map{ |t| t.options[:table] }.join(" and ")
167
- ).downcase.gsub(/[^a-z0-9_]/, '_').gsub(/_+/, '_').camelize
165
+ migration_base_name = "drop trigger#{drop_triggers.size > 1 ? 's' : ''} "
166
+ name_parts = drop_triggers.map { |t| t.options[:table] }
167
+ part_limit = 6
168
168
  end
169
169
 
170
+ # don't let migration names get too ridiculous
171
+ if name_parts.size > part_limit
172
+ migration_base_name << " multiple tables"
173
+ else
174
+ migration_base_name << name_parts.join(" OR ")
175
+ end
176
+
177
+ migration_base_name = migration_base_name.
178
+ downcase.
179
+ gsub(/[^a-z0-9_]/, '_').
180
+ gsub(/_+/, '_').
181
+ camelize
182
+
170
183
  name_version = nil
171
184
  while migration_names.include?("#{migration_base_name}#{name_version}")
172
185
  name_version = name_version.to_i + 1
173
186
  end
174
- migration_name = "#{migration_base_name}#{name_version}"
187
+
188
+ "#{migration_base_name}#{name_version}"
175
189
  end
176
190
 
177
191
  def infer_migration_version(migration_name)
@@ -29,14 +29,14 @@ module HairTrigger
29
29
  case adapter_name
30
30
  when :sqlite
31
31
  select_rows("SELECT name, sql FROM sqlite_master WHERE type = 'trigger' #{name_clause ? " AND name " + name_clause : ""}").each do |(name, definition)|
32
- triggers[name] = definition + ";\n"
32
+ triggers[name] = quote_table_name_in_trigger(definition) + ";\n"
33
33
  end
34
34
  when :mysql
35
35
  select_rows("SHOW TRIGGERS").each do |(name, event, table, actions, timing, created, sql_mode, definer)|
36
36
  definer = normalize_mysql_definer(definer)
37
37
  next if options[:only] && !options[:only].include?(name)
38
38
  triggers[name.strip] = <<-SQL
39
- CREATE #{definer != implicit_mysql_definer ? "DEFINER = #{definer} " : ""}TRIGGER #{name} #{timing} #{event} ON #{table}
39
+ CREATE #{definer != implicit_mysql_definer ? "DEFINER = #{definer} " : ""}TRIGGER #{name} #{timing} #{event} ON `#{table}`
40
40
  FOR EACH ROW
41
41
  #{actions}
42
42
  SQL
@@ -52,7 +52,7 @@ FOR EACH ROW
52
52
  )
53
53
  )
54
54
  SQL
55
- function_conditions =
55
+
56
56
  sql = <<-SQL
57
57
  SELECT tgname::varchar, pg_get_triggerdef(oid, true)
58
58
  FROM pg_trigger
@@ -68,12 +68,31 @@ FOR EACH ROW
68
68
  #{name_clause ? " AND (proname || '()')::varchar " + name_clause : ""}
69
69
  SQL
70
70
  select_rows(sql).each do |(name, definition)|
71
- triggers[name] = definition
71
+ triggers[name] = quote_table_name_in_trigger(definition)
72
72
  end
73
73
  else
74
74
  raise "don't know how to retrieve #{adapter_name} triggers yet"
75
75
  end
76
76
  triggers
77
77
  end
78
+
79
+ # a bit hacky, but we need to ensure the table name is always quoted
80
+ # on the way out, not just for reserved words. this is because we
81
+ # always quote on the way in, so we need them to match exactly
82
+ # when diffing
83
+ def quote_table_name_in_trigger(definition)
84
+ pattern = /
85
+ (
86
+ CREATE\sTRIGGER\s+
87
+ \S+\s+
88
+ (BEFORE|AFTER|INSTEAD\s+OF)\s+
89
+ (INSERT|UPDATE|DELETE|TRUNCATE)\s+
90
+ (OR\s+(INSERT|UPDATE|DELETE|TRUNCATE)\s+)*
91
+ (ON\s+)
92
+ )
93
+ (\w+) # quote if not already quoted
94
+ /ixm
95
+ definition.sub(pattern, '\\1"\\7"')
96
+ end
78
97
  end
79
98
  end
@@ -411,7 +411,7 @@ module HairTrigger
411
411
 
412
412
  def generate_trigger_sqlite
413
413
  <<-SQL
414
- CREATE TRIGGER #{prepared_name} #{options[:timing]} #{options[:events].first} #{of_clause}ON #{options[:table]}
414
+ CREATE TRIGGER #{prepared_name} #{options[:timing]} #{options[:events].first} #{of_clause}ON "#{options[:table]}"
415
415
  FOR EACH #{options[:for_each]}#{prepared_where ? " WHEN " + prepared_where : ''}
416
416
  BEGIN
417
417
  #{normalize(raw_actions, 1).rstrip}
@@ -464,7 +464,7 @@ $$ LANGUAGE plpgsql#{security ? " SECURITY #{security.to_s.upcase}" : ""};
464
464
  end
465
465
 
466
466
  [sql, <<-SQL]
467
- CREATE TRIGGER #{prepared_name} #{options[:timing]} #{options[:events].join(" OR ")} #{of_clause}ON #{options[:table]}
467
+ CREATE TRIGGER #{prepared_name} #{options[:timing]} #{options[:events].join(" OR ")} #{of_clause}ON "#{options[:table]}"
468
468
  FOR EACH #{options[:for_each]}#{prepared_where && db_version >= 90000 ? " WHEN (" + prepared_where + ')': ''} EXECUTE PROCEDURE #{trigger_action};
469
469
  SQL
470
470
  end
@@ -472,7 +472,7 @@ FOR EACH #{options[:for_each]}#{prepared_where && db_version >= 90000 ? " WHEN (
472
472
  def generate_trigger_mysql
473
473
  security = options[:security] if options[:security] && options[:security] != :definer
474
474
  sql = <<-SQL
475
- CREATE #{security ? "DEFINER = #{security} " : ""}TRIGGER #{prepared_name} #{options[:timing]} #{options[:events].first} ON #{options[:table]}
475
+ CREATE #{security ? "DEFINER = #{security} " : ""}TRIGGER #{prepared_name} #{options[:timing]} #{options[:events].first} ON `#{options[:table]}`
476
476
  FOR EACH #{options[:for_each]}
477
477
  BEGIN
478
478
  SQL
@@ -1,5 +1,5 @@
1
1
  module HairTrigger
2
- VERSION = "0.2.12"
2
+ VERSION = "0.2.13"
3
3
 
4
4
  def VERSION.<=>(other)
5
5
  split(/\./).map(&:to_i) <=> other.split(/\./).map(&:to_i)
data/spec/adapter_spec.rb CHANGED
@@ -31,6 +31,18 @@ describe "adapter" do
31
31
 
32
32
  expect(db_triggers).to eq(triggers)
33
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 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
34
46
  end
35
47
 
36
48
  context "mysql" do
@@ -42,6 +54,42 @@ describe "adapter" do
42
54
  let(:adapter) { :mysql2 }
43
55
  it_behaves_like "mysql"
44
56
  end
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 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 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
45
93
  end
46
94
  end
47
95
 
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.12
4
+ version: 0.2.13
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-10-14 00:00:00.000000000 Z
12
+ date: 2015-04-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -114,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
114
114
  version: 1.3.5
115
115
  requirements: []
116
116
  rubyforge_project:
117
- rubygems_version: 1.8.23
117
+ rubygems_version: 1.8.23.2
118
118
  signing_key:
119
119
  specification_version: 3
120
120
  summary: easy database triggers for active record