activerecord 1.15.6 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (185) hide show
  1. data/CHANGELOG +2454 -34
  2. data/README +1 -1
  3. data/RUNNING_UNIT_TESTS +3 -34
  4. data/Rakefile +98 -77
  5. data/install.rb +1 -1
  6. data/lib/active_record.rb +13 -22
  7. data/lib/active_record/aggregations.rb +38 -49
  8. data/lib/active_record/associations.rb +452 -333
  9. data/lib/active_record/associations/association_collection.rb +66 -20
  10. data/lib/active_record/associations/association_proxy.rb +9 -8
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +46 -51
  12. data/lib/active_record/associations/has_many_association.rb +21 -57
  13. data/lib/active_record/associations/has_many_through_association.rb +38 -18
  14. data/lib/active_record/associations/has_one_association.rb +30 -14
  15. data/lib/active_record/attribute_methods.rb +253 -0
  16. data/lib/active_record/base.rb +719 -494
  17. data/lib/active_record/calculations.rb +62 -63
  18. data/lib/active_record/callbacks.rb +57 -83
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +38 -9
  20. data/lib/active_record/connection_adapters/abstract/database_statements.rb +56 -15
  21. data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
  22. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -12
  23. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +191 -62
  24. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -34
  25. data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -17
  26. data/lib/active_record/connection_adapters/mysql_adapter.rb +119 -37
  27. data/lib/active_record/connection_adapters/postgresql_adapter.rb +473 -210
  28. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  29. data/lib/active_record/connection_adapters/sqlite_adapter.rb +91 -107
  30. data/lib/active_record/fixtures.rb +503 -113
  31. data/lib/active_record/locking/optimistic.rb +72 -34
  32. data/lib/active_record/migration.rb +80 -57
  33. data/lib/active_record/observer.rb +13 -10
  34. data/lib/active_record/query_cache.rb +16 -57
  35. data/lib/active_record/reflection.rb +35 -38
  36. data/lib/active_record/schema.rb +5 -5
  37. data/lib/active_record/schema_dumper.rb +35 -13
  38. data/lib/active_record/serialization.rb +98 -0
  39. data/lib/active_record/serializers/json_serializer.rb +71 -0
  40. data/lib/active_record/{xml_serialization.rb → serializers/xml_serializer.rb} +90 -83
  41. data/lib/active_record/timestamp.rb +20 -21
  42. data/lib/active_record/transactions.rb +39 -43
  43. data/lib/active_record/validations.rb +256 -107
  44. data/lib/active_record/version.rb +3 -3
  45. data/lib/activerecord.rb +1 -0
  46. data/test/aaa_create_tables_test.rb +15 -2
  47. data/test/abstract_unit.rb +24 -17
  48. data/test/active_schema_test_mysql.rb +20 -8
  49. data/test/adapter_test.rb +23 -5
  50. data/test/adapter_test_sqlserver.rb +15 -1
  51. data/test/aggregations_test.rb +16 -1
  52. data/test/all.sh +2 -2
  53. data/test/associations/ar_joins_test.rb +0 -0
  54. data/test/associations/callbacks_test.rb +51 -30
  55. data/test/associations/cascaded_eager_loading_test.rb +1 -29
  56. data/test/associations/eager_singularization_test.rb +145 -0
  57. data/test/associations/eager_test.rb +42 -6
  58. data/test/associations/extension_test.rb +6 -1
  59. data/test/associations/inner_join_association_test.rb +88 -0
  60. data/test/associations/join_model_test.rb +47 -16
  61. data/test/associations_test.rb +449 -226
  62. data/test/attribute_methods_test.rb +97 -0
  63. data/test/base_test.rb +251 -105
  64. data/test/binary_test.rb +22 -27
  65. data/test/calculations_test.rb +37 -5
  66. data/test/callbacks_test.rb +23 -0
  67. data/test/connection_test_firebird.rb +2 -2
  68. data/test/connection_test_mysql.rb +30 -0
  69. data/test/connections/native_mysql/connection.rb +3 -0
  70. data/test/connections/native_sqlite/connection.rb +5 -14
  71. data/test/connections/native_sqlite3/connection.rb +5 -14
  72. data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
  73. data/test/{copy_table_sqlite.rb → copy_table_test_sqlite.rb} +8 -3
  74. data/test/datatype_test_postgresql.rb +178 -27
  75. data/test/{empty_date_time_test.rb → date_time_test.rb} +13 -1
  76. data/test/defaults_test.rb +8 -1
  77. data/test/deprecated_finder_test.rb +7 -128
  78. data/test/finder_test.rb +192 -54
  79. data/test/fixtures/all/developers.yml +0 -0
  80. data/test/fixtures/all/people.csv +0 -0
  81. data/test/fixtures/all/tasks.yml +0 -0
  82. data/test/fixtures/author.rb +12 -5
  83. data/test/fixtures/binaries.yml +130 -435
  84. data/test/fixtures/category.rb +6 -0
  85. data/test/fixtures/company.rb +8 -1
  86. data/test/fixtures/computer.rb +1 -0
  87. data/test/fixtures/contact.rb +16 -0
  88. data/test/fixtures/customer.rb +2 -2
  89. data/test/fixtures/db_definitions/db2.drop.sql +1 -0
  90. data/test/fixtures/db_definitions/db2.sql +4 -0
  91. data/test/fixtures/db_definitions/firebird.drop.sql +3 -1
  92. data/test/fixtures/db_definitions/firebird.sql +6 -0
  93. data/test/fixtures/db_definitions/frontbase.drop.sql +1 -0
  94. data/test/fixtures/db_definitions/frontbase.sql +5 -0
  95. data/test/fixtures/db_definitions/openbase.sql +41 -25
  96. data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
  97. data/test/fixtures/db_definitions/oracle.sql +5 -0
  98. data/test/fixtures/db_definitions/postgresql.drop.sql +7 -0
  99. data/test/fixtures/db_definitions/postgresql.sql +87 -58
  100. data/test/fixtures/db_definitions/postgresql2.sql +1 -2
  101. data/test/fixtures/db_definitions/schema.rb +280 -0
  102. data/test/fixtures/db_definitions/schema2.rb +11 -0
  103. data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
  104. data/test/fixtures/db_definitions/sqlite.sql +4 -0
  105. data/test/fixtures/db_definitions/sybase.drop.sql +1 -0
  106. data/test/fixtures/db_definitions/sybase.sql +4 -0
  107. data/test/fixtures/developer.rb +10 -0
  108. data/test/fixtures/example.log +1 -0
  109. data/test/fixtures/flowers.jpg +0 -0
  110. data/test/fixtures/item.rb +7 -0
  111. data/test/fixtures/items.yml +4 -0
  112. data/test/fixtures/joke.rb +0 -3
  113. data/test/fixtures/matey.rb +4 -0
  114. data/test/fixtures/mateys.yml +4 -0
  115. data/test/fixtures/minimalistic.rb +2 -0
  116. data/test/fixtures/minimalistics.yml +2 -0
  117. data/test/fixtures/mixins.yml +2 -100
  118. data/test/fixtures/parrot.rb +13 -0
  119. data/test/fixtures/parrots.yml +27 -0
  120. data/test/fixtures/parrots_pirates.yml +7 -0
  121. data/test/fixtures/pirate.rb +5 -0
  122. data/test/fixtures/pirates.yml +9 -0
  123. data/test/fixtures/post.rb +1 -0
  124. data/test/fixtures/project.rb +3 -2
  125. data/test/fixtures/reserved_words/distinct.yml +5 -0
  126. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  127. data/test/fixtures/reserved_words/group.yml +14 -0
  128. data/test/fixtures/reserved_words/select.yml +8 -0
  129. data/test/fixtures/reserved_words/values.yml +7 -0
  130. data/test/fixtures/ship.rb +3 -0
  131. data/test/fixtures/ships.yml +5 -0
  132. data/test/fixtures/tagging.rb +4 -0
  133. data/test/fixtures/taggings.yml +8 -1
  134. data/test/fixtures/topic.rb +13 -1
  135. data/test/fixtures/treasure.rb +4 -0
  136. data/test/fixtures/treasures.yml +10 -0
  137. data/test/fixtures_test.rb +205 -24
  138. data/test/inheritance_test.rb +7 -1
  139. data/test/json_serialization_test.rb +180 -0
  140. data/test/lifecycle_test.rb +1 -1
  141. data/test/locking_test.rb +85 -2
  142. data/test/migration_test.rb +206 -40
  143. data/test/mixin_test.rb +13 -515
  144. data/test/pk_test.rb +3 -6
  145. data/test/query_cache_test.rb +104 -0
  146. data/test/reflection_test.rb +16 -0
  147. data/test/reserved_word_test_mysql.rb +177 -0
  148. data/test/schema_dumper_test.rb +38 -3
  149. data/test/serialization_test.rb +47 -0
  150. data/test/transactions_test.rb +74 -23
  151. data/test/unconnected_test.rb +1 -1
  152. data/test/validations_test.rb +322 -32
  153. data/test/xml_serialization_test.rb +121 -44
  154. metadata +48 -41
  155. data/examples/associations.rb +0 -87
  156. data/examples/shared_setup.rb +0 -15
  157. data/examples/validation.rb +0 -85
  158. data/lib/active_record/acts/list.rb +0 -256
  159. data/lib/active_record/acts/nested_set.rb +0 -211
  160. data/lib/active_record/acts/tree.rb +0 -96
  161. data/lib/active_record/connection_adapters/db2_adapter.rb +0 -228
  162. data/lib/active_record/connection_adapters/firebird_adapter.rb +0 -728
  163. data/lib/active_record/connection_adapters/frontbase_adapter.rb +0 -861
  164. data/lib/active_record/connection_adapters/openbase_adapter.rb +0 -350
  165. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -690
  166. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +0 -591
  167. data/lib/active_record/connection_adapters/sybase_adapter.rb +0 -662
  168. data/lib/active_record/deprecated_associations.rb +0 -104
  169. data/lib/active_record/deprecated_finders.rb +0 -44
  170. data/lib/active_record/vendor/simple.rb +0 -693
  171. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  172. data/lib/active_record/wrappings.rb +0 -58
  173. data/test/connections/native_sqlserver/connection.rb +0 -23
  174. data/test/connections/native_sqlserver_odbc/connection.rb +0 -25
  175. data/test/deprecated_associations_test.rb +0 -396
  176. data/test/fixtures/db_definitions/mysql.drop.sql +0 -32
  177. data/test/fixtures/db_definitions/mysql.sql +0 -234
  178. data/test/fixtures/db_definitions/mysql2.drop.sql +0 -2
  179. data/test/fixtures/db_definitions/mysql2.sql +0 -5
  180. data/test/fixtures/db_definitions/sqlserver.drop.sql +0 -34
  181. data/test/fixtures/db_definitions/sqlserver.sql +0 -243
  182. data/test/fixtures/db_definitions/sqlserver2.drop.sql +0 -2
  183. data/test/fixtures/db_definitions/sqlserver2.sql +0 -5
  184. data/test/fixtures/mixin.rb +0 -63
  185. data/test/mixin_nested_set_test.rb +0 -196
@@ -83,11 +83,8 @@ class PrimaryKeysTest < Test::Unit::TestCase
83
83
  def test_delete_should_quote_pkey
84
84
  assert_nothing_raised { MixedCaseMonkey.delete(1) }
85
85
  end
86
- def test_increment_counter_should_quote_pkey_and_quote_counter_columns
87
- assert_nothing_raised { MixedCaseMonkey.increment_counter(:fleaCount, 1) }
88
- end
89
- def test_decrement_counter_should_quote_pkey_and_quote_counter_columns
90
- assert_nothing_raised { MixedCaseMonkey.decrement_counter(:fleaCount, 1) }
86
+ def test_update_counters_should_quote_pkey_and_quote_counter_columns
87
+ assert_nothing_raised { MixedCaseMonkey.update_counters(1, :fleaCount => 99) }
91
88
  end
92
89
  def test_find_with_one_id_should_quote_pkey
93
90
  assert_nothing_raised { MixedCaseMonkey.find(1) }
@@ -98,7 +95,7 @@ class PrimaryKeysTest < Test::Unit::TestCase
98
95
  def test_instance_update_should_quote_pkey
99
96
  assert_nothing_raised { MixedCaseMonkey.find(1).save }
100
97
  end
101
- def test_instance_destry_should_quote_pkey
98
+ def test_instance_destroy_should_quote_pkey
102
99
  assert_nothing_raised { MixedCaseMonkey.find(1).destroy }
103
100
  end
104
101
  end
@@ -0,0 +1,104 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/topic'
3
+ require 'fixtures/reply'
4
+ require 'fixtures/task'
5
+ require 'fixtures/course'
6
+
7
+
8
+ class QueryCacheTest < Test::Unit::TestCase
9
+ fixtures :tasks, :topics
10
+
11
+ def test_find_queries
12
+ assert_queries(2) { Task.find(1); Task.find(1) }
13
+ end
14
+
15
+ def test_find_queries_with_cache
16
+ Task.cache do
17
+ assert_queries(1) { Task.find(1); Task.find(1) }
18
+ end
19
+ end
20
+
21
+ def test_count_queries_with_cache
22
+ Task.cache do
23
+ assert_queries(1) { Task.count; Task.count }
24
+ end
25
+ end
26
+
27
+ def test_query_cache_dups_results_correctly
28
+ Task.cache do
29
+ now = Time.now.utc
30
+ task = Task.find 1
31
+ assert_not_equal now, task.starting
32
+ task.starting = now
33
+ task.reload
34
+ assert_not_equal now, task.starting
35
+ end
36
+ end
37
+
38
+ def test_cache_is_flat
39
+ Task.cache do
40
+ Topic.columns # don't count this query
41
+ assert_queries(1) { Topic.find(1); Topic.find(1); }
42
+ end
43
+
44
+ ActiveRecord::Base.cache do
45
+ assert_queries(1) { Task.find(1); Task.find(1) }
46
+ end
47
+ end
48
+
49
+ def test_cache_does_not_wrap_string_results_in_arrays
50
+ Task.cache do
51
+ assert_instance_of String, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
52
+ end
53
+ end
54
+ end
55
+
56
+ uses_mocha 'QueryCacheExpiryTest' do
57
+
58
+ class QueryCacheExpiryTest < Test::Unit::TestCase
59
+ fixtures :tasks
60
+
61
+ def test_find
62
+ Task.connection.expects(:clear_query_cache).times(1)
63
+
64
+ assert !Task.connection.query_cache_enabled
65
+ Task.cache do
66
+ assert Task.connection.query_cache_enabled
67
+ Task.find(1)
68
+
69
+ Task.uncached do
70
+ assert !Task.connection.query_cache_enabled
71
+ Task.find(1)
72
+ end
73
+
74
+ assert Task.connection.query_cache_enabled
75
+ end
76
+ assert !Task.connection.query_cache_enabled
77
+ end
78
+
79
+ def test_update
80
+ Task.connection.expects(:clear_query_cache).times(2)
81
+
82
+ Task.cache do
83
+ Task.find(1).save!
84
+ end
85
+ end
86
+
87
+ def test_destroy
88
+ Task.connection.expects(:clear_query_cache).times(2)
89
+
90
+ Task.cache do
91
+ Task.find(1).destroy
92
+ end
93
+ end
94
+
95
+ def test_insert
96
+ ActiveRecord::Base.connection.expects(:clear_query_cache).times(2)
97
+
98
+ Task.cache do
99
+ Task.create!
100
+ end
101
+ end
102
+ end
103
+
104
+ end
@@ -60,6 +60,13 @@ class ReflectionTest < Test::Unit::TestCase
60
60
  assert_equal :integer, @first.column_for_attribute("id").type
61
61
  end
62
62
 
63
+ def test_reflection_klass_for_nested_class_name
64
+ reflection = ActiveRecord::Reflection::MacroReflection.new(nil, nil, { :class_name => 'MyApplication::Business::Company' }, nil)
65
+ assert_nothing_raised do
66
+ assert_equal MyApplication::Business::Company, reflection.klass
67
+ end
68
+ end
69
+
63
70
  def test_aggregation_reflection
64
71
  reflection_for_address = ActiveRecord::Reflection::AggregateReflection.new(
65
72
  :composed_of, :address, { :mapping => [ %w(address_street street), %w(address_city city), %w(address_country country) ] }, Customer
@@ -104,6 +111,15 @@ class ReflectionTest < Test::Unit::TestCase
104
111
  assert_equal 'accounts', Firm.reflect_on_association(:account).table_name
105
112
  end
106
113
 
114
+ def test_belongs_to_inferred_foreign_key_from_assoc_name
115
+ Company.belongs_to :foo
116
+ assert_equal "foo_id", Company.reflect_on_association(:foo).primary_key_name
117
+ Company.belongs_to :bar, :class_name => "Xyzzy"
118
+ assert_equal "bar_id", Company.reflect_on_association(:bar).primary_key_name
119
+ Company.belongs_to :baz, :class_name => "Xyzzy", :foreign_key => "xyzzy_id"
120
+ assert_equal "xyzzy_id", Company.reflect_on_association(:baz).primary_key_name
121
+ end
122
+
107
123
  def test_association_reflection_in_modules
108
124
  assert_reflection MyApplication::Business::Firm,
109
125
  :clients_of_firm,
@@ -0,0 +1,177 @@
1
+ require "#{File.dirname(__FILE__)}/abstract_unit"
2
+
3
+ class Group < ActiveRecord::Base
4
+ Group.table_name = 'group'
5
+ belongs_to :select, :class_name => 'Select'
6
+ has_one :values
7
+ end
8
+
9
+ class Select < ActiveRecord::Base
10
+ Select.table_name = 'select'
11
+ has_many :groups
12
+ end
13
+
14
+ class Values < ActiveRecord::Base
15
+ Values.table_name = 'values'
16
+ end
17
+
18
+ class Distinct < ActiveRecord::Base
19
+ Distinct.table_name = 'distinct'
20
+ has_and_belongs_to_many :selects
21
+ has_many :values, :through => :groups
22
+ end
23
+
24
+ # a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with
25
+ # reserved word names (ie: group, order, values, etc...)
26
+ class MysqlReservedWordTest < Test::Unit::TestCase
27
+ def setup
28
+ @connection = ActiveRecord::Base.connection
29
+
30
+ # we call execute directly here (and do similar below) because ActiveRecord::Base#create_table()
31
+ # will fail with these table names if these test cases fail
32
+
33
+ create_tables_directly 'group'=>'id int auto_increment primary key, `order` varchar(255), select_id int',
34
+ 'select'=>'id int auto_increment primary key',
35
+ 'values'=>'id int auto_increment primary key, group_id int',
36
+ 'distinct'=>'id int auto_increment primary key',
37
+ 'distincts_selects'=>'distinct_id int, select_id int'
38
+ end
39
+
40
+ def teardown
41
+ drop_tables_directly ['group', 'select', 'values', 'distinct', 'distincts_selects', 'order']
42
+ end
43
+
44
+ # create tables with reserved-word names and columns
45
+ def test_create_tables
46
+ assert_nothing_raised {
47
+ @connection.create_table :order do |t|
48
+ t.column :group, :string
49
+ end
50
+ }
51
+ end
52
+
53
+ # rename tables with reserved-word names
54
+ def test_rename_tables
55
+ assert_nothing_raised { @connection.rename_table(:group, :order) }
56
+ end
57
+
58
+ # alter column with a reserved-word name in a table with a reserved-word name
59
+ def test_change_columns
60
+ assert_nothing_raised { @connection.change_column_default(:group, :order, 'whatever') }
61
+ #the quoting here will reveal any double quoting issues in change_column's interaction with the column method in the adapter
62
+ assert_nothing_raised { @connection.change_column('group', 'order', :Int, :default => 0) }
63
+ assert_nothing_raised { @connection.rename_column(:group, :order, :values) }
64
+ end
65
+
66
+ # dump structure of table with reserved word name
67
+ def test_structure_dump
68
+ assert_nothing_raised { @connection.structure_dump }
69
+ end
70
+
71
+ # introspect table with reserved word name
72
+ def test_introspect
73
+ assert_nothing_raised { @connection.columns(:group) }
74
+ assert_nothing_raised { @connection.indexes(:group) }
75
+ end
76
+
77
+ #fixtures
78
+ self.use_instantiated_fixtures = true
79
+ self.use_transactional_fixtures = false
80
+
81
+ #fixtures :group
82
+
83
+ def test_fixtures
84
+ f = create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
85
+
86
+ assert_nothing_raised {
87
+ f.each do |x|
88
+ x.delete_existing_fixtures
89
+ end
90
+ }
91
+
92
+ assert_nothing_raised {
93
+ f.each do |x|
94
+ x.insert_fixtures
95
+ end
96
+ }
97
+ end
98
+
99
+ #activerecord model class with reserved-word table name
100
+ def test_activerecord_model
101
+ create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
102
+ x = nil
103
+ assert_nothing_raised { x = Group.new }
104
+ x.order = 'x'
105
+ assert_nothing_raised { x.save }
106
+ x.order = 'y'
107
+ assert_nothing_raised { x.save }
108
+ assert_nothing_raised { y = Group.find_by_order('y') }
109
+ assert_nothing_raised { y = Group.find(1) }
110
+ x = Group.find(1)
111
+ end
112
+
113
+ # has_one association with reserved-word table name
114
+ def test_has_one_associations
115
+ create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
116
+ v = nil
117
+ assert_nothing_raised { v = Group.find(1).values }
118
+ assert_equal v.id, 2
119
+ end
120
+
121
+ # belongs_to association with reserved-word table name
122
+ def test_belongs_to_associations
123
+ create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
124
+ gs = nil
125
+ assert_nothing_raised { gs = Select.find(2).groups }
126
+ assert_equal gs.length, 2
127
+ assert(gs.collect{|x| x.id}.sort == [2, 3])
128
+ end
129
+
130
+ # has_and_belongs_to_many with reserved-word table name
131
+ def test_has_and_belongs_to_many
132
+ create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
133
+ s = nil
134
+ assert_nothing_raised { s = Distinct.find(1).selects }
135
+ assert_equal s.length, 2
136
+ assert(s.collect{|x|x.id}.sort == [1, 2])
137
+ end
138
+
139
+ # activerecord model introspection with reserved-word table and column names
140
+ def test_activerecord_introspection
141
+ assert_nothing_raised { Group.table_exists? }
142
+ assert_nothing_raised { Group.columns }
143
+ end
144
+
145
+ # Calculations
146
+ def test_calculations_work_with_reserved_words
147
+ assert_nothing_raised { Group.count }
148
+ end
149
+
150
+ def test_associations_work_with_reserved_words
151
+ assert_nothing_raised { Select.find(:all, :include => [:groups]) }
152
+ end
153
+
154
+ #the following functions were added to DRY test cases
155
+
156
+ private
157
+ # custom fixture loader, uses Fixtures#create_fixtures and appends base_path to the current file's path
158
+ def create_test_fixtures(*fixture_names)
159
+ fixture_path = "./test/fixtures/reserved_words"
160
+ Fixtures.create_fixtures(fixture_path, fixture_names)
161
+ end
162
+
163
+ # custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name
164
+ def drop_tables_directly(table_names, connection = @connection)
165
+ table_names.each do |name|
166
+ connection.execute("DROP TABLE IF EXISTS `#{name}`")
167
+ end
168
+ end
169
+
170
+ # custom create table, uses execute on connection to create a table, note: escapes table_name, does NOT escape columns
171
+ def create_tables_directly (tables, connection = @connection)
172
+ tables.each do |table_name, column_properties|
173
+ connection.execute("CREATE TABLE `#{table_name}` ( #{column_properties} )")
174
+ end
175
+ end
176
+
177
+ end
@@ -19,6 +19,11 @@ if ActiveRecord::Base.connection.respond_to?(:tables)
19
19
  assert_no_match %r{create_table "schema_info"}, output
20
20
  end
21
21
 
22
+ def test_schema_dump_excludes_sqlite_sequence
23
+ output = standard_dump
24
+ assert_no_match %r{create_table "sqlite_sequence"}, output
25
+ end
26
+
22
27
  def assert_line_up(lines, pattern, required = false)
23
28
  return assert(true) if lines.empty?
24
29
  matches = lines.map { |line| line.match(pattern) }
@@ -27,11 +32,27 @@ if ActiveRecord::Base.connection.respond_to?(:tables)
27
32
  return assert(true) if matches.empty?
28
33
  assert_equal 1, matches.map{ |match| match.offset(0).first }.uniq.length
29
34
  end
35
+
36
+ def column_definition_lines(output = standard_dump)
37
+ output.scan(/^( *)create_table.*?\n(.*?)^\1end/m).map{ |m| m.last.split(/\n/) }
38
+ end
39
+
40
+ def test_types_line_up
41
+ column_definition_lines.each do |column_set|
42
+ next if column_set.empty?
43
+
44
+ lengths = column_set.map do |column|
45
+ if match = column.match(/t\.(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean)\s+"/)
46
+ match[0].length
47
+ end
48
+ end
49
+
50
+ assert_equal 1, lengths.uniq.length
51
+ end
52
+ end
30
53
 
31
54
  def test_arguments_line_up
32
- output = standard_dump
33
- output.scan(/^( *)create_table.*?\n(.*?)^\1end/m).map{ |m| m.last.split(/\n/) }.each do |column_set|
34
- assert_line_up(column_set, /:(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean)/, true)
55
+ column_definition_lines.each do |column_set|
35
56
  assert_line_up(column_set, /:default => /)
36
57
  assert_line_up(column_set, /:limit => /)
37
58
  assert_line_up(column_set, /:null => /)
@@ -84,6 +105,20 @@ if ActiveRecord::Base.connection.respond_to?(:tables)
84
105
  end
85
106
  end
86
107
 
108
+ if current_adapter?(:MysqlAdapter)
109
+ def test_schema_dump_should_not_add_default_value_for_mysql_text_field
110
+ output = standard_dump
111
+ assert_match %r{t.text\s+"body",\s+:default => "",\s+:null => false$}, output
112
+ end
113
+
114
+ def test_mysql_schema_dump_should_honor_nonstandard_primary_keys
115
+ output = standard_dump
116
+ match = output.match(%r{create_table "movies"(.*)do})
117
+ assert_not_nil(match, "nonstandardpk table not found")
118
+ assert_match %r(:primary_key => "movieid"), match[1], "non-standard primary key not preserved"
119
+ end
120
+ end
121
+
87
122
  def test_schema_dump_includes_decimal_options
88
123
  stream = StringIO.new
89
124
  ActiveRecord::SchemaDumper.ignore_tables = [/^[^n]/]
@@ -0,0 +1,47 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/contact'
3
+
4
+ class SerializationTest < Test::Unit::TestCase
5
+ FORMATS = [ :xml, :json ]
6
+
7
+ def setup
8
+ @contact_attributes = {
9
+ :name => 'aaron stack',
10
+ :age => 25,
11
+ :avatar => 'binarydata',
12
+ :created_at => Time.utc(2006, 8, 1),
13
+ :awesome => false,
14
+ :preferences => { :gem => '<strong>ruby</strong>' }
15
+ }
16
+
17
+ @contact = Contact.new(@contact_attributes)
18
+ end
19
+
20
+ def test_serialize_should_be_reversible
21
+ for format in FORMATS
22
+ @serialized = Contact.new.send("to_#{format}")
23
+ contact = Contact.new.send("from_#{format}", @serialized)
24
+
25
+ assert_equal @contact_attributes.keys.collect(&:to_s).sort, contact.attributes.keys.collect(&:to_s).sort, "For #{format}"
26
+ end
27
+ end
28
+
29
+ def test_serialize_should_allow_attribute_only_filtering
30
+ for format in FORMATS
31
+ @serialized = Contact.new(@contact_attributes).send("to_#{format}", :only => [ :age, :name ])
32
+ contact = Contact.new.send("from_#{format}", @serialized)
33
+ assert_equal @contact_attributes[:name], contact.name, "For #{format}"
34
+ assert_nil contact.avatar, "For #{format}"
35
+ end
36
+ end
37
+
38
+ def test_serialize_should_allow_attribute_except_filtering
39
+ for format in FORMATS
40
+ @serialized = Contact.new(@contact_attributes).send("to_#{format}", :except => [ :age, :name ])
41
+ contact = Contact.new.send("from_#{format}", @serialized)
42
+ assert_nil contact.name, "For #{format}"
43
+ assert_nil contact.age, "For #{format}"
44
+ assert_equal @contact_attributes[:awesome], contact.awesome, "For #{format}"
45
+ end
46
+ end
47
+ end
@@ -81,32 +81,12 @@ class TransactionTest < Test::Unit::TestCase
81
81
 
82
82
  assert @first.approved?, "First should still be changed in the objects"
83
83
  assert !@second.approved?, "Second should still be changed in the objects"
84
-
84
+
85
85
  assert !Topic.find(1).approved?, "First shouldn't have been approved"
86
86
  assert Topic.find(2).approved?, "Second should still be approved"
87
87
  end
88
-
89
- def test_failing_with_object_rollback
90
- assert !@first.approved?, "First should be unapproved initially"
91
88
 
92
- begin
93
- assert_deprecated /Object transactions/ do
94
- Topic.transaction(@first, @second) do
95
- @first.approved = true
96
- @second.approved = false
97
- @first.save
98
- @second.save
99
- raise "Bad things!"
100
- end
101
- end
102
- rescue
103
- # caught it
104
- end
105
-
106
- assert !@first.approved?, "First shouldn't have been approved"
107
- assert @second.approved?, "Second should still be approved"
108
- end
109
-
89
+
110
90
  def test_callback_rollback_in_save
111
91
  add_exception_raising_after_save_callback_to_topic
112
92
 
@@ -122,6 +102,38 @@ class TransactionTest < Test::Unit::TestCase
122
102
  end
123
103
  end
124
104
 
105
+ def test_callback_rollback_in_create
106
+ new_topic = Topic.new(
107
+ :title => "A new topic",
108
+ :author_name => "Ben",
109
+ :author_email_address => "ben@example.com",
110
+ :written_on => "2003-07-16t15:28:11.2233+01:00",
111
+ :last_read => "2004-04-15",
112
+ :bonus_time => "2005-01-30t15:28:00.00+01:00",
113
+ :content => "Have a nice day",
114
+ :approved => false)
115
+ new_record_snapshot = new_topic.new_record?
116
+ id_present = new_topic.has_attribute?(Topic.primary_key)
117
+ id_snapshot = new_topic.id
118
+
119
+ # Make sure the second save gets the after_create callback called.
120
+ 2.times do
121
+ begin
122
+ add_exception_raising_after_create_callback_to_topic
123
+ new_topic.approved = true
124
+ new_topic.save
125
+ flunk
126
+ rescue => e
127
+ assert_equal "Make the transaction rollback", e.message
128
+ assert_equal new_record_snapshot, new_topic.new_record?, "The topic should have its old new_record value"
129
+ assert_equal id_snapshot, new_topic.id, "The topic should have its old id"
130
+ assert_equal id_present, new_topic.has_attribute?(Topic.primary_key)
131
+ ensure
132
+ remove_exception_raising_after_create_callback_to_topic
133
+ end
134
+ end
135
+ end
136
+
125
137
  def test_nested_explicit_transactions
126
138
  Topic.transaction do
127
139
  Topic.transaction do
@@ -136,14 +148,53 @@ class TransactionTest < Test::Unit::TestCase
136
148
  assert !Topic.find(2).approved?, "Second should have been unapproved"
137
149
  end
138
150
 
151
+ def test_manually_rolling_back_a_transaction
152
+ Topic.transaction do
153
+ @first.approved = true
154
+ @second.approved = false
155
+ @first.save
156
+ @second.save
157
+
158
+ raise ActiveRecord::Rollback
159
+ end
160
+
161
+ assert @first.approved?, "First should still be changed in the objects"
162
+ assert !@second.approved?, "Second should still be changed in the objects"
163
+
164
+ assert !Topic.find(1).approved?, "First shouldn't have been approved"
165
+ assert Topic.find(2).approved?, "Second should still be approved"
166
+ end
167
+
168
+ uses_mocha 'mocking connection.commit_db_transaction' do
169
+ def test_rollback_when_commit_raises
170
+ Topic.connection.expects(:begin_db_transaction)
171
+ Topic.connection.expects(:commit_db_transaction).raises('OH NOES')
172
+ Topic.connection.expects(:rollback_db_transaction)
173
+
174
+ assert_raise RuntimeError do
175
+ Topic.transaction do
176
+ # do nothing
177
+ end
178
+ end
179
+ end
180
+ end
181
+
139
182
  private
140
183
  def add_exception_raising_after_save_callback_to_topic
141
184
  Topic.class_eval { def after_save() raise "Make the transaction rollback" end }
142
185
  end
143
-
186
+
144
187
  def remove_exception_raising_after_save_callback_to_topic
145
188
  Topic.class_eval { remove_method :after_save }
146
189
  end
190
+
191
+ def add_exception_raising_after_create_callback_to_topic
192
+ Topic.class_eval { def after_create() raise "Make the transaction rollback" end }
193
+ end
194
+
195
+ def remove_exception_raising_after_create_callback_to_topic
196
+ Topic.class_eval { remove_method :after_create }
197
+ end
147
198
  end
148
199
 
149
200
  if current_adapter?(:PostgreSQLAdapter)