hairtrigger 0.1.6 → 0.1.7

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/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