ibm_db 2.5.17-universal-darwin-13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +221 -0
  3. data/LICENSE +18 -0
  4. data/MANIFEST +14 -0
  5. data/ParameterizedQueries README +39 -0
  6. data/README +225 -0
  7. data/ext/Makefile.nt32 +181 -0
  8. data/ext/Makefile.nt32.191 +212 -0
  9. data/ext/extconf.rb +127 -0
  10. data/ext/ibm_db.c +11719 -0
  11. data/ext/ruby_ibm_db.h +240 -0
  12. data/ext/ruby_ibm_db_cli.c +845 -0
  13. data/ext/ruby_ibm_db_cli.h +489 -0
  14. data/init.rb +42 -0
  15. data/lib/IBM_DB.rb +16 -0
  16. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +3031 -0
  17. data/lib/active_record/connection_adapters/ibm_db_pstmt.rb +1965 -0
  18. data/lib/active_record/connection_adapters/ibmdb_adapter.rb +2 -0
  19. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -0
  20. data/lib/linux/rb18x/ibm_db.bundle +0 -0
  21. data/lib/linux/rb19x/ibm_db.bundle +0 -0
  22. data/lib/linux/rb20x/ibm_db.bundle +0 -0
  23. data/lib/linux/rb21x/ibm_db.bundle +0 -0
  24. data/test/cases/adapter_test.rb +207 -0
  25. data/test/cases/associations/belongs_to_associations_test.rb +711 -0
  26. data/test/cases/associations/cascaded_eager_loading_test.rb +181 -0
  27. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +851 -0
  28. data/test/cases/associations/join_model_test.rb +743 -0
  29. data/test/cases/attribute_methods_test.rb +822 -0
  30. data/test/cases/base_test.rb +2133 -0
  31. data/test/cases/calculations_test.rb +482 -0
  32. data/test/cases/migration_test.rb +2408 -0
  33. data/test/cases/persistence_test.rb +642 -0
  34. data/test/cases/query_cache_test.rb +257 -0
  35. data/test/cases/relations_test.rb +1182 -0
  36. data/test/cases/schema_dumper_test.rb +256 -0
  37. data/test/cases/transaction_callbacks_test.rb +300 -0
  38. data/test/cases/validations/uniqueness_validation_test.rb +299 -0
  39. data/test/cases/xml_serialization_test.rb +408 -0
  40. data/test/config.yml +154 -0
  41. data/test/connections/native_ibm_db/connection.rb +44 -0
  42. data/test/ibm_db_test.rb +25 -0
  43. data/test/models/warehouse_thing.rb +5 -0
  44. data/test/schema/i5/ibm_db_specific_schema.rb +137 -0
  45. data/test/schema/ids/ibm_db_specific_schema.rb +140 -0
  46. data/test/schema/luw/ibm_db_specific_schema.rb +137 -0
  47. data/test/schema/schema.rb +751 -0
  48. data/test/schema/zOS/ibm_db_specific_schema.rb +208 -0
  49. metadata +109 -0
@@ -0,0 +1,256 @@
1
+ require "cases/helper"
2
+
3
+
4
+ class SchemaDumperTest < ActiveRecord::TestCase
5
+ def setup
6
+ @stream = StringIO.new
7
+ end
8
+
9
+ def standard_dump
10
+ @stream = StringIO.new
11
+ ActiveRecord::SchemaDumper.ignore_tables = []
12
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, @stream)
13
+ @stream.string
14
+ end
15
+
16
+ if "string".encoding_aware?
17
+ def test_magic_comment
18
+ assert_match "# encoding: #{@stream.external_encoding.name}", standard_dump
19
+ end
20
+ end
21
+
22
+ def test_schema_dump
23
+ output = standard_dump
24
+ assert_match %r{create_table "accounts"}, output
25
+ assert_match %r{create_table "authors"}, output
26
+ assert_no_match %r{create_table "schema_migrations"}, output
27
+ end
28
+
29
+ def test_schema_dump_excludes_sqlite_sequence
30
+ output = standard_dump
31
+ assert_no_match %r{create_table "sqlite_sequence"}, output
32
+ end
33
+
34
+ def test_schema_dump_includes_camelcase_table_name
35
+ output = standard_dump
36
+ if current_adapter?(:IBM_DBAdapter)
37
+ #DB2 is case insensitive
38
+ assert_match %r{create_table "camelcase"}, output
39
+ else
40
+ assert_match %r{create_table "CamelCase"}, output
41
+ end
42
+ end
43
+
44
+ def assert_line_up(lines, pattern, required = false)
45
+ return assert(true) if lines.empty?
46
+ matches = lines.map { |line| line.match(pattern) }
47
+ assert matches.all? if required
48
+ matches.compact!
49
+ return assert(true) if matches.empty?
50
+ assert_equal 1, matches.map{ |match| match.offset(0).first }.uniq.length
51
+ end
52
+
53
+ def column_definition_lines(output = standard_dump)
54
+ output.scan(/^( *)create_table.*?\n(.*?)^\1end/m).map{ |m| m.last.split(/\n/) }
55
+ end
56
+
57
+ def test_types_line_up
58
+ column_definition_lines.each do |column_set|
59
+ next if column_set.empty?
60
+
61
+ lengths = column_set.map do |column|
62
+ if match = column.match(/t\.(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean)\s+"/)
63
+ match[0].length
64
+ end
65
+ end
66
+
67
+ assert_equal 1, lengths.uniq.length
68
+ end
69
+ end
70
+
71
+ def test_arguments_line_up
72
+ column_definition_lines.each do |column_set|
73
+ assert_line_up(column_set, /:default => /)
74
+ assert_line_up(column_set, /:limit => /)
75
+ assert_line_up(column_set, /:null => /)
76
+ end
77
+ end
78
+
79
+ def test_no_dump_errors
80
+ output = standard_dump
81
+ assert_no_match %r{\# Could not dump table}, output
82
+ end
83
+
84
+ def test_schema_dump_includes_not_null_columns
85
+ stream = StringIO.new
86
+
87
+ ActiveRecord::SchemaDumper.ignore_tables = [/^[^r]/]
88
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
89
+ output = stream.string
90
+ assert_match %r{:null => false}, output
91
+ end
92
+
93
+ def test_schema_dump_includes_limit_constraint_for_integer_columns
94
+ stream = StringIO.new
95
+
96
+ ActiveRecord::SchemaDumper.ignore_tables = [/^(?!integer_limits)/]
97
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
98
+ output = stream.string
99
+
100
+ if current_adapter?(:PostgreSQLAdapter)
101
+ assert_match %r{c_int_1.*:limit => 2}, output
102
+ assert_match %r{c_int_2.*:limit => 2}, output
103
+
104
+ # int 3 is 4 bytes in postgresql
105
+ assert_match %r{c_int_3.*}, output
106
+ assert_no_match %r{c_int_3.*:limit}, output
107
+
108
+ assert_match %r{c_int_4.*}, output
109
+ assert_no_match %r{c_int_4.*:limit}, output
110
+ elsif current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
111
+ assert_match %r{c_int_1.*:limit => 1}, output
112
+ assert_match %r{c_int_2.*:limit => 2}, output
113
+ assert_match %r{c_int_3.*:limit => 3}, output
114
+
115
+ assert_match %r{c_int_4.*}, output
116
+ assert_no_match %r{c_int_4.*:limit}, output
117
+ elsif current_adapter?(:SQLite3Adapter)
118
+ assert_match %r{c_int_1.*:limit => 1}, output
119
+ assert_match %r{c_int_2.*:limit => 2}, output
120
+ assert_match %r{c_int_3.*:limit => 3}, output
121
+ assert_match %r{c_int_4.*:limit => 4}, output
122
+ end
123
+ assert_match %r{c_int_without_limit.*}, output
124
+ assert_no_match %r{c_int_without_limit.*:limit}, output
125
+
126
+ if current_adapter?(:SQLite3Adapter)
127
+ assert_match %r{c_int_5.*:limit => 5}, output
128
+ assert_match %r{c_int_6.*:limit => 6}, output
129
+ assert_match %r{c_int_7.*:limit => 7}, output
130
+ assert_match %r{c_int_8.*:limit => 8}, output
131
+ elsif current_adapter?(:OracleAdapter)
132
+ assert_match %r{c_int_5.*:limit => 5}, output
133
+ assert_match %r{c_int_6.*:limit => 6}, output
134
+ assert_match %r{c_int_7.*:limit => 7}, output
135
+ assert_match %r{c_int_8.*:limit => 8}, output
136
+ elsif current_adapter?(:IBM_DBAdapter)
137
+ #Limits is not supported on integer column by DB2
138
+ else
139
+ assert_match %r{c_int_5.*:limit => 8}, output
140
+ assert_match %r{c_int_6.*:limit => 8}, output
141
+ assert_match %r{c_int_7.*:limit => 8}, output
142
+ assert_match %r{c_int_8.*:limit => 8}, output
143
+ end
144
+ end
145
+
146
+ def test_schema_dump_with_string_ignored_table
147
+ stream = StringIO.new
148
+
149
+ ActiveRecord::SchemaDumper.ignore_tables = ['accounts']
150
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
151
+ output = stream.string
152
+ assert_no_match %r{create_table "accounts"}, output
153
+ assert_match %r{create_table "authors"}, output
154
+ assert_no_match %r{create_table "schema_migrations"}, output
155
+ end
156
+
157
+ def test_schema_dump_with_regexp_ignored_table
158
+ stream = StringIO.new
159
+
160
+ ActiveRecord::SchemaDumper.ignore_tables = [/^account/]
161
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
162
+ output = stream.string
163
+ assert_no_match %r{create_table "accounts"}, output
164
+ assert_match %r{create_table "authors"}, output
165
+ assert_no_match %r{create_table "schema_migrations"}, output
166
+ end
167
+
168
+ def test_schema_dump_illegal_ignored_table_value
169
+ stream = StringIO.new
170
+ ActiveRecord::SchemaDumper.ignore_tables = [5]
171
+ assert_raise(StandardError) do
172
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
173
+ end
174
+ end
175
+
176
+ def test_schema_dumps_index_columns_in_right_order
177
+ index_definition = standard_dump.split(/\n/).grep(/add_index.*companies/).first.strip
178
+ assert_equal 'add_index "companies", ["firm_id", "type", "rating", "ruby_type"], :name => "company_index"', index_definition
179
+ end
180
+
181
+ def test_schema_dump_should_honor_nonstandard_primary_keys
182
+ output = standard_dump
183
+ match = output.match(%r{create_table "movies"(.*)do})
184
+ assert_not_nil(match, "nonstandardpk table not found")
185
+ assert_match %r(:primary_key => "movieid"), match[1], "non-standard primary key not preserved"
186
+ end
187
+
188
+ if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
189
+ def test_schema_dump_should_not_add_default_value_for_mysql_text_field
190
+ output = standard_dump
191
+ assert_match %r{t.text\s+"body",\s+:null => false$}, output
192
+ end
193
+
194
+ def test_schema_dump_includes_length_for_mysql_blob_and_text_fields
195
+ output = standard_dump
196
+ assert_match %r{t.binary\s+"tiny_blob",\s+:limit => 255$}, output
197
+ assert_match %r{t.binary\s+"normal_blob"$}, output
198
+ assert_match %r{t.binary\s+"medium_blob",\s+:limit => 16777215$}, output
199
+ assert_match %r{t.binary\s+"long_blob",\s+:limit => 2147483647$}, output
200
+ assert_match %r{t.text\s+"tiny_text",\s+:limit => 255$}, output
201
+ assert_match %r{t.text\s+"normal_text"$}, output
202
+ assert_match %r{t.text\s+"medium_text",\s+:limit => 16777215$}, output
203
+ assert_match %r{t.text\s+"long_text",\s+:limit => 2147483647$}, output
204
+ end
205
+ end
206
+
207
+ def test_schema_dump_includes_decimal_options
208
+ stream = StringIO.new
209
+ ActiveRecord::SchemaDumper.ignore_tables = [/^[^n]/]
210
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
211
+ output = stream.string
212
+ assert_match %r{:precision => 3,[[:space:]]+:scale => 2,[[:space:]]+:default => 2.78}, output
213
+ end
214
+
215
+ if current_adapter?(:PostgreSQLAdapter)
216
+ def test_schema_dump_includes_xml_shorthand_definition
217
+ output = standard_dump
218
+ if %r{create_table "postgresql_xml_data_type"} =~ output
219
+ assert_match %r{t.xml "data"}, output
220
+ end
221
+ end
222
+
223
+ def test_schema_dump_includes_tsvector_shorthand_definition
224
+ output = standard_dump
225
+ if %r{create_table "postgresql_tsvectors"} =~ output
226
+ assert_match %r{t.tsvector "text_vector"}, output
227
+ end
228
+ end
229
+ end
230
+
231
+ def test_schema_dump_keeps_large_precision_integer_columns_as_decimal
232
+ output = standard_dump
233
+ # Oracle supports precision up to 38 and it identifies decimals with scale 0 as integers
234
+ if current_adapter?(:OracleAdapter)
235
+ assert_match %r{t.integer\s+"atoms_in_universe",\s+:precision => 38,\s+:scale => 0}, output
236
+ elsif current_adapter?(:IBM_DBAdapter)
237
+ # DB2 supports precision up to 31
238
+ assert_match %r{t.decimal\s+"atoms_in_universe",\s+:precision => 31,\s+:scale => 0}, output
239
+ else
240
+ assert_match %r{t.decimal\s+"atoms_in_universe",\s+:precision => 55,\s+:scale => 0}, output
241
+ end
242
+ end
243
+
244
+ def test_schema_dump_keeps_id_column_when_id_is_false_and_id_column_added
245
+ output = standard_dump
246
+ match = output.match(%r{create_table "goofy_string_id"(.*)do.*\n(.*)\n})
247
+ assert_not_nil(match, "goofy_string_id table not found")
248
+ assert_match %r(:id => false), match[1], "no table id not preserved"
249
+ assert_match %r{t.string[[:space:]]+"id",[[:space:]]+:null => false$}, match[2], "non-primary key id column not preserved"
250
+ end
251
+
252
+ def test_schema_dump_keeps_id_false_when_id_is_false_and_unique_not_null_column_added
253
+ output = standard_dump
254
+ assert_match %r{create_table "subscribers", :id => false}, output
255
+ end
256
+ end
@@ -0,0 +1,300 @@
1
+ require "cases/helper"
2
+ require 'models/topic'
3
+
4
+ class TransactionCallbacksTest < ActiveRecord::TestCase
5
+ self.use_transactional_fixtures = false
6
+ fixtures :topics
7
+
8
+ class TopicWithCallbacks < ActiveRecord::Base
9
+ self.table_name = :topics
10
+
11
+ after_commit{|record| record.send(:do_after_commit, nil)}
12
+ after_commit(:on => :create){|record| record.send(:do_after_commit, :create)}
13
+ after_commit(:on => :update){|record| record.send(:do_after_commit, :update)}
14
+ after_commit(:on => :destroy){|record| record.send(:do_after_commit, :destroy)}
15
+ after_rollback{|record| record.send(:do_after_rollback, nil)}
16
+ after_rollback(:on => :create){|record| record.send(:do_after_rollback, :create)}
17
+ after_rollback(:on => :update){|record| record.send(:do_after_rollback, :update)}
18
+ after_rollback(:on => :destroy){|record| record.send(:do_after_rollback, :destroy)}
19
+
20
+ def history
21
+ @history ||= []
22
+ end
23
+
24
+ def after_commit_block(on = nil, &block)
25
+ @after_commit ||= {}
26
+ @after_commit[on] ||= []
27
+ @after_commit[on] << block
28
+ end
29
+
30
+ def after_rollback_block(on = nil, &block)
31
+ @after_rollback ||= {}
32
+ @after_rollback[on] ||= []
33
+ @after_rollback[on] << block
34
+ end
35
+
36
+ def do_after_commit(on)
37
+ blocks = @after_commit[on] if defined?(@after_commit)
38
+ blocks.each{|b| b.call(self)} if blocks
39
+ end
40
+
41
+ def do_after_rollback(on)
42
+ blocks = @after_rollback[on] if defined?(@after_rollback)
43
+ blocks.each{|b| b.call(self)} if blocks
44
+ end
45
+ end
46
+
47
+ def setup
48
+ @first, @second = TopicWithCallbacks.find(1, 3).sort_by { |t| t.id }
49
+ end
50
+
51
+ def test_call_after_commit_after_transaction_commits
52
+ @first.after_commit_block{|r| r.history << :after_commit}
53
+ @first.after_rollback_block{|r| r.history << :after_rollback}
54
+
55
+ @first.save!
56
+ assert_equal [:after_commit], @first.history
57
+ end
58
+
59
+ def test_only_call_after_commit_on_update_after_transaction_commits_for_existing_record
60
+ @first.after_commit_block(:create){|r| r.history << :commit_on_create}
61
+ @first.after_commit_block(:update){|r| r.history << :commit_on_update}
62
+ @first.after_commit_block(:destroy){|r| r.history << :commit_on_destroy}
63
+ @first.after_rollback_block(:create){|r| r.history << :rollback_on_create}
64
+ @first.after_rollback_block(:update){|r| r.history << :rollback_on_update}
65
+ @first.after_rollback_block(:destroy){|r| r.history << :rollback_on_destroy}
66
+
67
+ @first.save!
68
+ assert_equal [:commit_on_update], @first.history
69
+ end
70
+
71
+ def test_only_call_after_commit_on_destroy_after_transaction_commits_for_destroyed_record
72
+ @first.after_commit_block(:create){|r| r.history << :commit_on_create}
73
+ @first.after_commit_block(:update){|r| r.history << :commit_on_update}
74
+ @first.after_commit_block(:destroy){|r| r.history << :commit_on_destroy}
75
+ @first.after_rollback_block(:create){|r| r.history << :rollback_on_create}
76
+ @first.after_rollback_block(:update){|r| r.history << :rollback_on_update}
77
+ @first.after_rollback_block(:destroy){|r| r.history << :rollback_on_destroy}
78
+
79
+ @first.destroy
80
+ assert_equal [:commit_on_destroy], @first.history
81
+ end
82
+
83
+ def test_only_call_after_commit_on_create_after_transaction_commits_for_new_record
84
+ unless current_adapter?(:IBM_DBAdapter)
85
+ @new_record = TopicWithCallbacks.new(:title => "New topic", :written_on => Date.today)
86
+ else
87
+ @new_record = TopicWithCallbacks.new(:title => "New topic", :written_on => Time.now)
88
+ end
89
+ @new_record.after_commit_block(:create){|r| r.history << :commit_on_create}
90
+ @new_record.after_commit_block(:update){|r| r.history << :commit_on_update}
91
+ @new_record.after_commit_block(:destroy){|r| r.history << :commit_on_destroy}
92
+ @new_record.after_rollback_block(:create){|r| r.history << :rollback_on_create}
93
+ @new_record.after_rollback_block(:update){|r| r.history << :rollback_on_update}
94
+ @new_record.after_rollback_block(:destroy){|r| r.history << :rollback_on_destroy}
95
+
96
+ @new_record.save!
97
+ assert_equal [:commit_on_create], @new_record.history
98
+ end
99
+
100
+ def test_call_after_rollback_after_transaction_rollsback
101
+ @first.after_commit_block{|r| r.history << :after_commit}
102
+ @first.after_rollback_block{|r| r.history << :after_rollback}
103
+
104
+ Topic.transaction do
105
+ @first.save!
106
+ raise ActiveRecord::Rollback
107
+ end
108
+
109
+ assert_equal [:after_rollback], @first.history
110
+ end
111
+
112
+ def test_only_call_after_rollback_on_update_after_transaction_rollsback_for_existing_record
113
+ @first.after_commit_block(:create){|r| r.history << :commit_on_create}
114
+ @first.after_commit_block(:update){|r| r.history << :commit_on_update}
115
+ @first.after_commit_block(:destroy){|r| r.history << :commit_on_destroy}
116
+ @first.after_rollback_block(:create){|r| r.history << :rollback_on_create}
117
+ @first.after_rollback_block(:update){|r| r.history << :rollback_on_update}
118
+ @first.after_rollback_block(:destroy){|r| r.history << :rollback_on_destroy}
119
+
120
+ Topic.transaction do
121
+ @first.save!
122
+ raise ActiveRecord::Rollback
123
+ end
124
+
125
+ assert_equal [:rollback_on_update], @first.history
126
+ end
127
+
128
+ def test_only_call_after_rollback_on_destroy_after_transaction_rollsback_for_destroyed_record
129
+ @first.after_commit_block(:create){|r| r.history << :commit_on_create}
130
+ @first.after_commit_block(:update){|r| r.history << :commit_on_update}
131
+ @first.after_commit_block(:destroy){|r| r.history << :commit_on_update}
132
+ @first.after_rollback_block(:create){|r| r.history << :rollback_on_create}
133
+ @first.after_rollback_block(:update){|r| r.history << :rollback_on_update}
134
+ @first.after_rollback_block(:destroy){|r| r.history << :rollback_on_destroy}
135
+
136
+ Topic.transaction do
137
+ @first.destroy
138
+ raise ActiveRecord::Rollback
139
+ end
140
+
141
+ assert_equal [:rollback_on_destroy], @first.history
142
+ end
143
+
144
+ def test_only_call_after_rollback_on_create_after_transaction_rollsback_for_new_record
145
+ unless current_adapter?(:IBM_DBAdapter)
146
+ @new_record = TopicWithCallbacks.new(:title => "New topic", :written_on => Date.today)
147
+ else
148
+ @new_record = TopicWithCallbacks.new(:title => "New topic", :written_on => Time.now)
149
+ end
150
+ @new_record.after_commit_block(:create){|r| r.history << :commit_on_create}
151
+ @new_record.after_commit_block(:update){|r| r.history << :commit_on_update}
152
+ @new_record.after_commit_block(:destroy){|r| r.history << :commit_on_destroy}
153
+ @new_record.after_rollback_block(:create){|r| r.history << :rollback_on_create}
154
+ @new_record.after_rollback_block(:update){|r| r.history << :rollback_on_update}
155
+ @new_record.after_rollback_block(:destroy){|r| r.history << :rollback_on_destroy}
156
+
157
+ Topic.transaction do
158
+ @new_record.save!
159
+ raise ActiveRecord::Rollback
160
+ end
161
+
162
+ assert_equal [:rollback_on_create], @new_record.history
163
+ end
164
+
165
+ def test_call_after_rollback_when_commit_fails
166
+ @first.connection.class.send(:alias_method, :real_method_commit_db_transaction, :commit_db_transaction)
167
+ begin
168
+ @first.connection.class.class_eval do
169
+ def commit_db_transaction; raise "boom!"; end
170
+ end
171
+
172
+ @first.after_commit_block{|r| r.history << :after_commit}
173
+ @first.after_rollback_block{|r| r.history << :after_rollback}
174
+
175
+ assert !@first.save rescue nil
176
+ assert_equal [:after_rollback], @first.history
177
+ ensure
178
+ @first.connection.class.send(:remove_method, :commit_db_transaction)
179
+ @first.connection.class.send(:alias_method, :commit_db_transaction, :real_method_commit_db_transaction)
180
+ end
181
+ end
182
+
183
+ def test_only_call_after_rollback_on_records_rolled_back_to_a_savepoint
184
+ def @first.rollbacks(i=0); @rollbacks ||= 0; @rollbacks += i if i; end
185
+ def @first.commits(i=0); @commits ||= 0; @commits += i if i; end
186
+ @first.after_rollback_block{|r| r.rollbacks(1)}
187
+ @first.after_commit_block{|r| r.commits(1)}
188
+
189
+ def @second.rollbacks(i=0); @rollbacks ||= 0; @rollbacks += i if i; end
190
+ def @second.commits(i=0); @commits ||= 0; @commits += i if i; end
191
+ @second.after_rollback_block{|r| r.rollbacks(1)}
192
+ @second.after_commit_block{|r| r.commits(1)}
193
+
194
+ Topic.transaction do
195
+ @first.save!
196
+ Topic.transaction(:requires_new => true) do
197
+ @second.save!
198
+ raise ActiveRecord::Rollback
199
+ end
200
+ end
201
+
202
+ assert_equal 1, @first.commits
203
+ assert_equal 0, @first.rollbacks
204
+ assert_equal 0, @second.commits
205
+ assert_equal 1, @second.rollbacks
206
+ end
207
+
208
+ def test_only_call_after_rollback_on_records_rolled_back_to_a_savepoint_when_release_savepoint_fails
209
+ def @first.rollbacks(i=0); @rollbacks ||= 0; @rollbacks += i if i; end
210
+ def @first.commits(i=0); @commits ||= 0; @commits += i if i; end
211
+
212
+ @first.after_rollback_block{|r| r.rollbacks(1)}
213
+ @first.after_commit_block{|r| r.commits(1)}
214
+
215
+ Topic.transaction do
216
+ @first.save
217
+ Topic.transaction(:requires_new => true) do
218
+ @first.save!
219
+ raise ActiveRecord::Rollback
220
+ end
221
+ Topic.transaction(:requires_new => true) do
222
+ @first.save!
223
+ raise ActiveRecord::Rollback
224
+ end
225
+ end
226
+
227
+ assert_equal 1, @first.commits
228
+ assert_equal 2, @first.rollbacks
229
+ end
230
+
231
+ def test_after_transaction_callbacks_should_prevent_callbacks_from_being_called
232
+ def @first.last_after_transaction_error=(e); @last_transaction_error = e; end
233
+ def @first.last_after_transaction_error; @last_transaction_error; end
234
+ @first.after_commit_block{|r| r.last_after_transaction_error = :commit; raise "fail!";}
235
+ @first.after_rollback_block{|r| r.last_after_transaction_error = :rollback; raise "fail!";}
236
+ @second.after_commit_block{|r| r.history << :after_commit}
237
+ @second.after_rollback_block{|r| r.history << :after_rollback}
238
+
239
+ Topic.transaction do
240
+ @first.save!
241
+ @second.save!
242
+ end
243
+ assert_equal :commit, @first.last_after_transaction_error
244
+ assert_equal [:after_commit], @second.history
245
+
246
+ @second.history.clear
247
+ Topic.transaction do
248
+ @first.save!
249
+ @second.save!
250
+ raise ActiveRecord::Rollback
251
+ end
252
+ assert_equal :rollback, @first.last_after_transaction_error
253
+ assert_equal [:after_rollback], @second.history
254
+ end
255
+ end
256
+
257
+
258
+ class TransactionObserverCallbacksTest < ActiveRecord::TestCase
259
+ self.use_transactional_fixtures = false
260
+ fixtures :topics
261
+
262
+ class TopicWithObserverAttached < ActiveRecord::Base
263
+ self.table_name = :topics
264
+ def history
265
+ @history ||= []
266
+ end
267
+ end
268
+
269
+ class TopicWithObserverAttachedObserver < ActiveRecord::Observer
270
+ def after_commit(record)
271
+ record.history.push "after_commit"
272
+ end
273
+
274
+ def after_rollback(record)
275
+ record.history.push "after_rollback"
276
+ end
277
+ end
278
+
279
+ def test_after_commit_called
280
+ assert TopicWithObserverAttachedObserver.instance, 'should have observer'
281
+
282
+ topic = TopicWithObserverAttached.new
283
+ topic.save!
284
+
285
+ assert_equal %w{ after_commit }, topic.history
286
+ end
287
+
288
+ def test_after_rollback_called
289
+ assert TopicWithObserverAttachedObserver.instance, 'should have observer'
290
+
291
+ topic = TopicWithObserverAttached.new
292
+
293
+ Topic.transaction do
294
+ topic.save!
295
+ raise ActiveRecord::Rollback
296
+ end
297
+
298
+ assert_equal %w{ after_rollback }, topic.history
299
+ end
300
+ end