hairtrigger 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,12 +1,13 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gem "activerecord", ">=2.3.0", "<3.0"
3
+ gem "activerecord", ">=2.3.0"
4
4
  group :development do
5
5
  gem "rspec", "~> 2.3.0"
6
6
  gem "bundler", "~> 1.0.0"
7
7
  gem "jeweler", "~> 1.5.2"
8
8
  gem "rcov", ">= 0"
9
9
  gem 'mysql', '>= 2.8.1'
10
+ gem 'mysql2', '>= 0.2.7'
10
11
  gem 'pg', '>= 0.10.1'
11
12
  gem 'sqlite3-ruby', '>= 1.3.2'
12
13
  end
data/README.rdoc CHANGED
@@ -15,12 +15,14 @@ hairtrigger in environment.rb
15
15
 
16
16
  === Step 2.
17
17
 
18
- If you plan to use model triggers (which you should ;) you'll need to create
19
- lib/tasks/hair_trigger.rake with the following:
18
+ Create lib/tasks/hair_trigger.rake with the following:
20
19
 
21
20
  $VERBOSE = nil
22
21
  Dir["#{Gem.searcher.find('hair_trigger').full_gem_path}/lib/tasks/*.rake"].each { |ext| load ext }
23
22
 
23
+ This will give you the db:generate_trigger_migration task, and will ensure
24
+ that hairtrigger hooks into db:schema:dump.
25
+
24
26
  If you are unpacking the gem in vendor/plugins, this step is not needed
25
27
  (though you'll then want to delete its Gemfile to avoid possible conflicts).
26
28
 
@@ -65,10 +67,31 @@ drop_trigger. They are a little more verbose than model triggers, and they can
65
67
  be more work since you need to figure out the up/down create/drop logic when
66
68
  you change things. A sample trigger:
67
69
 
68
- create_trigger.on(:users).after(:insert) do
70
+ create_trigger(:compatibility => 1).on(:users).after(:insert) do
69
71
  "UPDATE accounts SET user_count = user_count + 1 WHERE id = NEW.account_id;"
70
72
  end
71
73
 
74
+ ==== Manual triggers and :compatibility
75
+
76
+ As bugs are fixed and features are implemented in hairtrigger, it's possible
77
+ that the generated trigger SQL will change (this has only happened once so
78
+ far). If you upgrade to a newer version of hairtrigger, it needs a way of
79
+ knowing which previous version generated the original trigger. You only need
80
+ to worry about this for manual trigger migrations, as the model ones
81
+ automatically take care of this. For your manual triggers you can either:
82
+
83
+ * pass ":compatibility => x" to your create_trigger statement, where x is
84
+ whatever HairTrigger::Builder.compatiblity is (1 for this version).
85
+ * set "HairTrigger::Builder.base_compatibility = x" in an initializer, where
86
+ x is whatever HairTrigger::Builder.compatiblity is. This is like doing the
87
+ first option on every create_trigger. Note that once the compatibility
88
+ changes, you'll need to set :compatibility on new triggers (unless you
89
+ just redo all your triggers and bump the base_compatibility).
90
+
91
+ If you upgrade to a newer version of hairtrigger and see that the SQL
92
+ compatibility has changed, you'll need to set the appropriate compatibility
93
+ on any new triggers that you create.
94
+
72
95
  === Chainable Methods
73
96
 
74
97
  Triggers are built by chaining several methods together, ending in a block
@@ -200,16 +223,20 @@ you want to support.
200
223
  limited to INSERT/UPDATE/DELETE/SELECT, and conditional logic should be
201
224
  handled through the :where option/method. Otherwise you'll likely run into
202
225
  trouble due to differences in syntax and supported features.
226
+ * Manual create_trigger statements have some gotchas. See the section
227
+ "Manual triggers and :compatibility"
203
228
 
204
229
  == Compatibility
205
230
 
206
- * Rails 2.3.x
231
+ * Rails 2.3+
207
232
  * Postgres 8.0+
208
233
  * MySQL 5.0.10+
209
234
  * SQLite 3.3.8+
210
235
 
211
236
  == Version History
212
237
 
238
+ * 0.1.7 Rails 3 support, fixed a couple manual create_trigger bugs
239
+ * 0.1.6 rake db:schema:dump support, respect non-timestamped migrations
213
240
  * 0.1.4 Compatibility tracking, fixed Postgres return bug, ensure last action
214
241
  has a semicolon
215
242
  * 0.1.3 Better error handling, Postgres 8.x support, updated docs
data/Rakefile CHANGED
@@ -19,7 +19,7 @@ Jeweler::Tasks.new do |gem|
19
19
  gem.description = %Q{allows you to declare database triggers in ruby in your models, and then generate appropriate migrations as they change}
20
20
  gem.email = "jenseng@gmail.com"
21
21
  gem.authors = ["Jon Jensen"]
22
- gem.add_dependency "activerecord", ">=2.3.0", "<3.0"
22
+ gem.add_dependency "activerecord", ">=2.3.0"
23
23
  gem.add_development_dependency "rspec", "~> 2.3.0"
24
24
  end
25
25
  Jeweler::RubygemsDotOrgTasks.new
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.6
1
+ 0.1.7
data/lib/hair_trigger.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'ostruct'
2
2
  require 'hair_trigger/base'
3
3
  require 'hair_trigger/builder'
4
+ require 'hair_trigger/migrator'
4
5
  require 'hair_trigger/migration'
5
6
  require 'hair_trigger/adapter'
6
7
  require 'hair_trigger/schema_dumper'
@@ -140,7 +141,7 @@ module HairTrigger
140
141
  migration_name = "#{migration_base_name}#{name_version}"
141
142
  migration_version = ActiveRecord::Base.timestamped_migrations ?
142
143
  Time.now.getutc.strftime("%Y%m%d%H%M%S") :
143
- Dir.glob(migration_path + '/*rb').map{ |f| f.gsub(/.*\/(\d+)_.*/, '\1').to_i}.inject(0){ |curr, i| i > curr ? i : curr }
144
+ Dir.glob(migration_path + '/*rb').map{ |f| f.gsub(/.*\/(\d+)_.*/, '\1').to_i}.inject(0){ |curr, i| i > curr ? i : curr } + 1
144
145
  file_name = migration_path + '/' + migration_version + "_" + migration_name.underscore + ".rb"
145
146
  File.open(file_name, "w"){ |f| f.write <<-MIGRATION }
146
147
  # This migration was auto-generated via `rake db:generate_trigger_migration'.
@@ -175,12 +176,17 @@ end
175
176
  def migration_path
176
177
  @migration_path ||= 'db/migrate'
177
178
  end
179
+
180
+ def adapter_name_for(adapter)
181
+ adapter.adapter_name.downcase.sub(/\d$/, '').to_sym
182
+ end
178
183
  end
179
184
  end
180
185
 
181
186
  ActiveRecord::Base.send :extend, HairTrigger::Base
182
187
  ActiveRecord::Migration.send :extend, HairTrigger::Migration
183
188
  ActiveRecord::MigrationProxy.send :delegate, :trigger_builders, :to=>:migration
189
+ ActiveRecord::Migrator.send :extend, HairTrigger::Migrator
184
190
  ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval { include HairTrigger::Adapter }
185
191
  ActiveRecord::SchemaDumper.class_eval { include HairTrigger::SchemaDumper }
186
192
  ActiveRecord::Schema.send :extend, HairTrigger::Schema
@@ -15,7 +15,8 @@ module HairTrigger
15
15
  def triggers(options = {})
16
16
  triggers = {}
17
17
  name_clause = options[:only] ? "IN ('" + options[:only].join("', '") + "')" : nil
18
- case self.adapter_name.downcase.to_sym
18
+ adapter_name = HairTrigger.adapter_name_for(self)
19
+ case adapter_name
19
20
  when :sqlite
20
21
  select_rows("SELECT name, sql FROM sqlite_master WHERE type = 'trigger' #{name_clause ? " AND name " + name_clause : ""}").each do |(name, definition)|
21
22
  triggers[name] = definition + ";\n"
@@ -195,7 +195,8 @@ module HairTrigger
195
195
  str << "#{indent}end"
196
196
  else
197
197
  str = "#{indent}create_trigger(#{prepared_name.inspect}"
198
- str << ", :generated => true, :compatibility => #{@compatibility}" if always_generated || options[:generated]
198
+ str << ", :generated => true" if always_generated || options[:generated]
199
+ str << ", :compatibility => #{@compatibility}"
199
200
  str << ").\n#{indent} " + chained_calls_to_ruby(".\n#{indent} ")
200
201
  if @triggers
201
202
  str << " do |t|\n"
@@ -270,7 +271,7 @@ module HairTrigger
270
271
  end
271
272
 
272
273
  def adapter_name
273
- @adapter_name ||= adapter.adapter_name.downcase.to_sym
274
+ @adapter_name ||= HairTrigger.adapter_name_for(adapter)
274
275
  end
275
276
 
276
277
  def adapter
@@ -387,8 +388,7 @@ BEGIN
387
388
  end
388
389
 
389
390
  class << self
390
- attr_writer :tab_spacing
391
- attr_writer :show_warnings
391
+ attr_writer :tab_spacing, :show_warnings, :base_compatibility
392
392
 
393
393
  def tab_spacing
394
394
  @tab_spacing ||= 4
@@ -399,6 +399,10 @@ BEGIN
399
399
  @show_warnings
400
400
  end
401
401
 
402
+ def base_compatibility
403
+ @base_compatibility ||= 0
404
+ end
405
+
402
406
  def compatibility
403
407
  @compatibility ||= begin
404
408
  gem_version = (File.read(File.dirname(__FILE__) + '/../../VERSION').chomp rescue '0.1.3').split(/\./).map(&:to_i)
@@ -4,17 +4,20 @@ module HairTrigger
4
4
 
5
5
  def method_missing_with_trigger_building(method, *arguments, &block)
6
6
  if extract_trigger_builders
7
- if method.to_sym == :create_trigger && (extract_all_triggers || arguments[1].delete(:generated))
7
+ extract_this_trigger = extract_all_triggers
8
+ trigger = if method.to_sym == :create_trigger
9
+ arguments.unshift({}) if arguments.empty?
8
10
  arguments.unshift(nil) if arguments.first.is_a?(Hash)
9
- arguments[1][:compatibility] ||= 0
10
- trigger = ::HairTrigger::Builder.new(*arguments)
11
- (@trigger_builders ||= []) << trigger
12
- trigger
13
- elsif method.to_sym == :drop_trigger && (extract_all_triggers || arguments[2] && arguments[2].delete(:generated))
14
- trigger = ::HairTrigger::Builder.new(arguments[0], {:table => arguments[1], :drop => true})
15
- (@trigger_builders ||= []) << trigger
16
- trigger
11
+ extract_this_trigger ||= arguments[1].delete(:generated)
12
+ arguments[1][:compatibility] ||= HairTrigger::Builder.base_compatibility
13
+ ::HairTrigger::Builder.new(*arguments)
14
+ elsif method.to_sym == :drop_trigger
15
+ extract_this_trigger ||= arguments[2].delete(:generated) if arguments[2]
16
+ ::HairTrigger::Builder.new(arguments[0], {:table => arguments[1], :drop => true})
17
17
  end
18
+ (@trigger_builders ||= []) << trigger if trigger && extract_this_trigger
19
+ trigger
20
+
18
21
  # normally we would fall through to the connection for everything
19
22
  # else, but we don't want to do that since we are not actually
20
23
  # running the migration
@@ -0,0 +1,14 @@
1
+ module HairTrigger
2
+ module Migrator
3
+ def proper_table_name_with_hash_awareness(name)
4
+ name.is_a?(Hash) ? name : proper_table_name_without_hash_awareness(name)
5
+ end
6
+ def self.extended(base)
7
+ base.class_eval do
8
+ class << self
9
+ alias_method_chain :proper_table_name, :hash_awareness
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -7,6 +7,7 @@ class InitialTables < ActiveRecord::Migration
7
7
 
8
8
  create_table "groups" do |t|
9
9
  t.integer "bob_count", :default => 0
10
+ t.integer "updated_joe_count", :default => 0
10
11
  end
11
12
  end
12
13
 
@@ -0,0 +1,10 @@
1
+ class ManualUserTrigger < ActiveRecord::Migration
2
+ def self.up
3
+ create_trigger(:compatibility => 1).
4
+ on("users").
5
+ after(:update).
6
+ where("NEW.name = 'joe'") do
7
+ "UPDATE groups SET updated_joe_count = updated_joe_count + 1"
8
+ end
9
+ end
10
+ end
@@ -1,7 +1,9 @@
1
1
  require 'active_record'
2
+ require 'logger'
2
3
  require 'active_record/connection_adapters/postgresql_adapter'
3
4
  require 'active_record/connection_adapters/mysql_adapter'
4
5
  require 'active_record/connection_adapters/sqlite3_adapter'
6
+ require 'mysql2'
5
7
  require 'rspec'
6
8
  require 'hair_trigger'
7
9
 
@@ -25,7 +27,7 @@ def initialize_db(adapter)
25
27
  reset_tmp
26
28
  config = {:database => 'hairtrigger_schema_test', :username => 'hairtrigger', :adapter => adapter.to_s, :host => 'localhost'}
27
29
  case adapter
28
- when :mysql
30
+ when :mysql, :mysql2
29
31
  ret = `echo "drop database if exists hairtrigger_schema_test; create database hairtrigger_schema_test;" | mysql hairtrigger_schema_test -u hairtrigger`
30
32
  raise "error creating database: #{ret}" unless $?.exitstatus == 0
31
33
  when :sqlite3
@@ -36,13 +38,16 @@ def initialize_db(adapter)
36
38
  raise "error creating database: #{ret}" unless $?.exitstatus == 0
37
39
  config[:min_messages] = :error
38
40
  end
41
+ # Arel has an issue in that it keeps using original connection for quoting,
42
+ # etc. (which breaks stuff) unless you do this:
43
+ Arel::Visitors::ENGINE_VISITORS.delete(ActiveRecord::Base) if defined?(Arel)
39
44
  ActiveRecord::Base.establish_connection(config)
40
45
  ActiveRecord::Base.logger = Logger.new('/dev/null')
41
46
  ActiveRecord::Migrator.migrate(HairTrigger.migration_path)
42
47
  end
43
48
 
44
49
  describe "schema" do
45
- [:mysql, :postgresql, :sqlite3].each do |adapter|
50
+ [:mysql, :mysql2, :postgresql, :sqlite3].each do |adapter|
46
51
  it "should correctly dump #{adapter}" do
47
52
  ActiveRecord::Migration.verbose = false
48
53
  initialize_db(adapter)
@@ -55,6 +60,7 @@ describe "schema" do
55
60
  io.rewind
56
61
  schema_rb = io.read
57
62
  schema_rb.should match(/create_trigger\("users_after_insert_row_when_new_name_bob__tr", :generated => true, :compatibility => 1\)/)
63
+ schema_rb.should match(/create_trigger\("users_after_update_row_when_new_name_joe__tr", :compatibility => 1\)/)
58
64
 
59
65
  # schema dump w/ schema.rb
60
66
  ActiveRecord::SchemaDumper.previous_schema = schema_rb
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hairtrigger
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 6
10
- version: 0.1.6
9
+ - 7
10
+ version: 0.1.7
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jon Jensen
@@ -15,20 +15,16 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-03-31 00:00:00 -06:00
18
+ date: 2011-04-17 00:00:00 -06:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- requirement: &id001 !ruby/object:Gem::Requirement
22
+ prerelease: false
23
+ name: activerecord
24
+ type: :runtime
25
+ version_requirements: &id001 !ruby/object:Gem::Requirement
23
26
  none: false
24
27
  requirements:
25
- - - <
26
- - !ruby/object:Gem::Version
27
- hash: 7
28
- segments:
29
- - 3
30
- - 0
31
- version: "3.0"
32
28
  - - ">="
33
29
  - !ruby/object:Gem::Version
34
30
  hash: 3
@@ -37,12 +33,12 @@ dependencies:
37
33
  - 3
38
34
  - 0
39
35
  version: 2.3.0
40
- version_requirements: *id001
41
- name: activerecord
42
- prerelease: false
43
- type: :runtime
36
+ requirement: *id001
44
37
  - !ruby/object:Gem::Dependency
45
- requirement: &id002 !ruby/object:Gem::Requirement
38
+ prerelease: false
39
+ name: rspec
40
+ type: :development
41
+ version_requirements: &id002 !ruby/object:Gem::Requirement
46
42
  none: false
47
43
  requirements:
48
44
  - - ~>
@@ -53,12 +49,12 @@ dependencies:
53
49
  - 3
54
50
  - 0
55
51
  version: 2.3.0
56
- version_requirements: *id002
57
- name: rspec
52
+ requirement: *id002
53
+ - !ruby/object:Gem::Dependency
58
54
  prerelease: false
55
+ name: bundler
59
56
  type: :development
60
- - !ruby/object:Gem::Dependency
61
- requirement: &id003 !ruby/object:Gem::Requirement
57
+ version_requirements: &id003 !ruby/object:Gem::Requirement
62
58
  none: false
63
59
  requirements:
64
60
  - - ~>
@@ -69,12 +65,12 @@ dependencies:
69
65
  - 0
70
66
  - 0
71
67
  version: 1.0.0
72
- version_requirements: *id003
73
- name: bundler
68
+ requirement: *id003
69
+ - !ruby/object:Gem::Dependency
74
70
  prerelease: false
71
+ name: jeweler
75
72
  type: :development
76
- - !ruby/object:Gem::Dependency
77
- requirement: &id004 !ruby/object:Gem::Requirement
73
+ version_requirements: &id004 !ruby/object:Gem::Requirement
78
74
  none: false
79
75
  requirements:
80
76
  - - ~>
@@ -85,12 +81,12 @@ dependencies:
85
81
  - 5
86
82
  - 2
87
83
  version: 1.5.2
88
- version_requirements: *id004
89
- name: jeweler
84
+ requirement: *id004
85
+ - !ruby/object:Gem::Dependency
90
86
  prerelease: false
87
+ name: rcov
91
88
  type: :development
92
- - !ruby/object:Gem::Dependency
93
- requirement: &id005 !ruby/object:Gem::Requirement
89
+ version_requirements: &id005 !ruby/object:Gem::Requirement
94
90
  none: false
95
91
  requirements:
96
92
  - - ">="
@@ -99,12 +95,12 @@ dependencies:
99
95
  segments:
100
96
  - 0
101
97
  version: "0"
102
- version_requirements: *id005
103
- name: rcov
98
+ requirement: *id005
99
+ - !ruby/object:Gem::Dependency
104
100
  prerelease: false
101
+ name: mysql
105
102
  type: :development
106
- - !ruby/object:Gem::Dependency
107
- requirement: &id006 !ruby/object:Gem::Requirement
103
+ version_requirements: &id006 !ruby/object:Gem::Requirement
108
104
  none: false
109
105
  requirements:
110
106
  - - ">="
@@ -115,12 +111,28 @@ dependencies:
115
111
  - 8
116
112
  - 1
117
113
  version: 2.8.1
118
- version_requirements: *id006
119
- name: mysql
114
+ requirement: *id006
115
+ - !ruby/object:Gem::Dependency
120
116
  prerelease: false
117
+ name: mysql2
121
118
  type: :development
119
+ version_requirements: &id007 !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ hash: 25
125
+ segments:
126
+ - 0
127
+ - 2
128
+ - 7
129
+ version: 0.2.7
130
+ requirement: *id007
122
131
  - !ruby/object:Gem::Dependency
123
- requirement: &id007 !ruby/object:Gem::Requirement
132
+ prerelease: false
133
+ name: pg
134
+ type: :development
135
+ version_requirements: &id008 !ruby/object:Gem::Requirement
124
136
  none: false
125
137
  requirements:
126
138
  - - ">="
@@ -131,12 +143,12 @@ dependencies:
131
143
  - 10
132
144
  - 1
133
145
  version: 0.10.1
134
- version_requirements: *id007
135
- name: pg
146
+ requirement: *id008
147
+ - !ruby/object:Gem::Dependency
136
148
  prerelease: false
149
+ name: sqlite3-ruby
137
150
  type: :development
138
- - !ruby/object:Gem::Dependency
139
- requirement: &id008 !ruby/object:Gem::Requirement
151
+ version_requirements: &id009 !ruby/object:Gem::Requirement
140
152
  none: false
141
153
  requirements:
142
154
  - - ">="
@@ -147,21 +159,14 @@ dependencies:
147
159
  - 3
148
160
  - 2
149
161
  version: 1.3.2
150
- version_requirements: *id008
151
- name: sqlite3-ruby
152
- prerelease: false
153
- type: :development
162
+ requirement: *id009
154
163
  - !ruby/object:Gem::Dependency
155
- requirement: &id009 !ruby/object:Gem::Requirement
164
+ prerelease: false
165
+ name: activerecord
166
+ type: :runtime
167
+ version_requirements: &id010 !ruby/object:Gem::Requirement
156
168
  none: false
157
169
  requirements:
158
- - - <
159
- - !ruby/object:Gem::Version
160
- hash: 7
161
- segments:
162
- - 3
163
- - 0
164
- version: "3.0"
165
170
  - - ">="
166
171
  - !ruby/object:Gem::Version
167
172
  hash: 3
@@ -170,12 +175,12 @@ dependencies:
170
175
  - 3
171
176
  - 0
172
177
  version: 2.3.0
173
- version_requirements: *id009
174
- name: activerecord
175
- prerelease: false
176
- type: :runtime
178
+ requirement: *id010
177
179
  - !ruby/object:Gem::Dependency
178
- requirement: &id010 !ruby/object:Gem::Requirement
180
+ prerelease: false
181
+ name: rspec
182
+ type: :development
183
+ version_requirements: &id011 !ruby/object:Gem::Requirement
179
184
  none: false
180
185
  requirements:
181
186
  - - ~>
@@ -186,10 +191,7 @@ dependencies:
186
191
  - 3
187
192
  - 0
188
193
  version: 2.3.0
189
- version_requirements: *id010
190
- name: rspec
191
- prerelease: false
192
- type: :development
194
+ requirement: *id011
193
195
  description: allows you to declare database triggers in ruby in your models, and then generate appropriate migrations as they change
194
196
  email: jenseng@gmail.com
195
197
  executables: []
@@ -213,6 +215,7 @@ files:
213
215
  - lib/hair_trigger/base.rb
214
216
  - lib/hair_trigger/builder.rb
215
217
  - lib/hair_trigger/migration.rb
218
+ - lib/hair_trigger/migrator.rb
216
219
  - lib/hair_trigger/schema.rb
217
220
  - lib/hair_trigger/schema_dumper.rb
218
221
  - lib/tasks/hair_trigger.rake
@@ -220,6 +223,7 @@ files:
220
223
  - spec/builder_spec.rb
221
224
  - spec/migrations/20110331212003_initial_tables.rb
222
225
  - spec/migrations/20110331212631_user_trigger.rb
226
+ - spec/migrations/20110417185102_manual_user_trigger.rb
223
227
  - spec/models/group.rb
224
228
  - spec/models/user.rb
225
229
  - spec/schema_dumper_spec.rb
@@ -253,7 +257,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
253
257
  requirements: []
254
258
 
255
259
  rubyforge_project:
256
- rubygems_version: 1.6.0
260
+ rubygems_version: 1.4.2
257
261
  signing_key:
258
262
  specification_version: 3
259
263
  summary: easy database triggers for active record
@@ -261,6 +265,7 @@ test_files:
261
265
  - spec/builder_spec.rb
262
266
  - spec/migrations/20110331212003_initial_tables.rb
263
267
  - spec/migrations/20110331212631_user_trigger.rb
268
+ - spec/migrations/20110417185102_manual_user_trigger.rb
264
269
  - spec/models/group.rb
265
270
  - spec/models/user.rb
266
271
  - spec/schema_dumper_spec.rb