ibm_db 2.5.14-x86-linux

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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +210 -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 +2 -0
  16. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +3020 -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/ibm_db.rb +8 -0
  21. data/lib/linux/rb18x/ibm_db.so +0 -0
  22. data/lib/linux/rb19x/ibm_db.so +0 -0
  23. data/lib/linux/rb2x/ibm_db.so +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