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.
- data/CHANGELOG +2454 -34
- data/README +1 -1
- data/RUNNING_UNIT_TESTS +3 -34
- data/Rakefile +98 -77
- data/install.rb +1 -1
- data/lib/active_record.rb +13 -22
- data/lib/active_record/aggregations.rb +38 -49
- data/lib/active_record/associations.rb +452 -333
- data/lib/active_record/associations/association_collection.rb +66 -20
- data/lib/active_record/associations/association_proxy.rb +9 -8
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +46 -51
- data/lib/active_record/associations/has_many_association.rb +21 -57
- data/lib/active_record/associations/has_many_through_association.rb +38 -18
- data/lib/active_record/associations/has_one_association.rb +30 -14
- data/lib/active_record/attribute_methods.rb +253 -0
- data/lib/active_record/base.rb +719 -494
- data/lib/active_record/calculations.rb +62 -63
- data/lib/active_record/callbacks.rb +57 -83
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +38 -9
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +56 -15
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +191 -62
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -17
- data/lib/active_record/connection_adapters/mysql_adapter.rb +119 -37
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +473 -210
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +91 -107
- data/lib/active_record/fixtures.rb +503 -113
- data/lib/active_record/locking/optimistic.rb +72 -34
- data/lib/active_record/migration.rb +80 -57
- data/lib/active_record/observer.rb +13 -10
- data/lib/active_record/query_cache.rb +16 -57
- data/lib/active_record/reflection.rb +35 -38
- data/lib/active_record/schema.rb +5 -5
- data/lib/active_record/schema_dumper.rb +35 -13
- data/lib/active_record/serialization.rb +98 -0
- data/lib/active_record/serializers/json_serializer.rb +71 -0
- data/lib/active_record/{xml_serialization.rb → serializers/xml_serializer.rb} +90 -83
- data/lib/active_record/timestamp.rb +20 -21
- data/lib/active_record/transactions.rb +39 -43
- data/lib/active_record/validations.rb +256 -107
- data/lib/active_record/version.rb +3 -3
- data/lib/activerecord.rb +1 -0
- data/test/aaa_create_tables_test.rb +15 -2
- data/test/abstract_unit.rb +24 -17
- data/test/active_schema_test_mysql.rb +20 -8
- data/test/adapter_test.rb +23 -5
- data/test/adapter_test_sqlserver.rb +15 -1
- data/test/aggregations_test.rb +16 -1
- data/test/all.sh +2 -2
- data/test/associations/ar_joins_test.rb +0 -0
- data/test/associations/callbacks_test.rb +51 -30
- data/test/associations/cascaded_eager_loading_test.rb +1 -29
- data/test/associations/eager_singularization_test.rb +145 -0
- data/test/associations/eager_test.rb +42 -6
- data/test/associations/extension_test.rb +6 -1
- data/test/associations/inner_join_association_test.rb +88 -0
- data/test/associations/join_model_test.rb +47 -16
- data/test/associations_test.rb +449 -226
- data/test/attribute_methods_test.rb +97 -0
- data/test/base_test.rb +251 -105
- data/test/binary_test.rb +22 -27
- data/test/calculations_test.rb +37 -5
- data/test/callbacks_test.rb +23 -0
- data/test/connection_test_firebird.rb +2 -2
- data/test/connection_test_mysql.rb +30 -0
- data/test/connections/native_mysql/connection.rb +3 -0
- data/test/connections/native_sqlite/connection.rb +5 -14
- data/test/connections/native_sqlite3/connection.rb +5 -14
- data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
- data/test/{copy_table_sqlite.rb → copy_table_test_sqlite.rb} +8 -3
- data/test/datatype_test_postgresql.rb +178 -27
- data/test/{empty_date_time_test.rb → date_time_test.rb} +13 -1
- data/test/defaults_test.rb +8 -1
- data/test/deprecated_finder_test.rb +7 -128
- data/test/finder_test.rb +192 -54
- data/test/fixtures/all/developers.yml +0 -0
- data/test/fixtures/all/people.csv +0 -0
- data/test/fixtures/all/tasks.yml +0 -0
- data/test/fixtures/author.rb +12 -5
- data/test/fixtures/binaries.yml +130 -435
- data/test/fixtures/category.rb +6 -0
- data/test/fixtures/company.rb +8 -1
- data/test/fixtures/computer.rb +1 -0
- data/test/fixtures/contact.rb +16 -0
- data/test/fixtures/customer.rb +2 -2
- data/test/fixtures/db_definitions/db2.drop.sql +1 -0
- data/test/fixtures/db_definitions/db2.sql +4 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +3 -1
- data/test/fixtures/db_definitions/firebird.sql +6 -0
- data/test/fixtures/db_definitions/frontbase.drop.sql +1 -0
- data/test/fixtures/db_definitions/frontbase.sql +5 -0
- data/test/fixtures/db_definitions/openbase.sql +41 -25
- data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle.sql +5 -0
- data/test/fixtures/db_definitions/postgresql.drop.sql +7 -0
- data/test/fixtures/db_definitions/postgresql.sql +87 -58
- data/test/fixtures/db_definitions/postgresql2.sql +1 -2
- data/test/fixtures/db_definitions/schema.rb +280 -0
- data/test/fixtures/db_definitions/schema2.rb +11 -0
- data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +4 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +1 -0
- data/test/fixtures/db_definitions/sybase.sql +4 -0
- data/test/fixtures/developer.rb +10 -0
- data/test/fixtures/example.log +1 -0
- data/test/fixtures/flowers.jpg +0 -0
- data/test/fixtures/item.rb +7 -0
- data/test/fixtures/items.yml +4 -0
- data/test/fixtures/joke.rb +0 -3
- data/test/fixtures/matey.rb +4 -0
- data/test/fixtures/mateys.yml +4 -0
- data/test/fixtures/minimalistic.rb +2 -0
- data/test/fixtures/minimalistics.yml +2 -0
- data/test/fixtures/mixins.yml +2 -100
- data/test/fixtures/parrot.rb +13 -0
- data/test/fixtures/parrots.yml +27 -0
- data/test/fixtures/parrots_pirates.yml +7 -0
- data/test/fixtures/pirate.rb +5 -0
- data/test/fixtures/pirates.yml +9 -0
- data/test/fixtures/post.rb +1 -0
- data/test/fixtures/project.rb +3 -2
- data/test/fixtures/reserved_words/distinct.yml +5 -0
- data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
- data/test/fixtures/reserved_words/group.yml +14 -0
- data/test/fixtures/reserved_words/select.yml +8 -0
- data/test/fixtures/reserved_words/values.yml +7 -0
- data/test/fixtures/ship.rb +3 -0
- data/test/fixtures/ships.yml +5 -0
- data/test/fixtures/tagging.rb +4 -0
- data/test/fixtures/taggings.yml +8 -1
- data/test/fixtures/topic.rb +13 -1
- data/test/fixtures/treasure.rb +4 -0
- data/test/fixtures/treasures.yml +10 -0
- data/test/fixtures_test.rb +205 -24
- data/test/inheritance_test.rb +7 -1
- data/test/json_serialization_test.rb +180 -0
- data/test/lifecycle_test.rb +1 -1
- data/test/locking_test.rb +85 -2
- data/test/migration_test.rb +206 -40
- data/test/mixin_test.rb +13 -515
- data/test/pk_test.rb +3 -6
- data/test/query_cache_test.rb +104 -0
- data/test/reflection_test.rb +16 -0
- data/test/reserved_word_test_mysql.rb +177 -0
- data/test/schema_dumper_test.rb +38 -3
- data/test/serialization_test.rb +47 -0
- data/test/transactions_test.rb +74 -23
- data/test/unconnected_test.rb +1 -1
- data/test/validations_test.rb +322 -32
- data/test/xml_serialization_test.rb +121 -44
- metadata +48 -41
- data/examples/associations.rb +0 -87
- data/examples/shared_setup.rb +0 -15
- data/examples/validation.rb +0 -85
- data/lib/active_record/acts/list.rb +0 -256
- data/lib/active_record/acts/nested_set.rb +0 -211
- data/lib/active_record/acts/tree.rb +0 -96
- data/lib/active_record/connection_adapters/db2_adapter.rb +0 -228
- data/lib/active_record/connection_adapters/firebird_adapter.rb +0 -728
- data/lib/active_record/connection_adapters/frontbase_adapter.rb +0 -861
- data/lib/active_record/connection_adapters/openbase_adapter.rb +0 -350
- data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -690
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +0 -591
- data/lib/active_record/connection_adapters/sybase_adapter.rb +0 -662
- data/lib/active_record/deprecated_associations.rb +0 -104
- data/lib/active_record/deprecated_finders.rb +0 -44
- data/lib/active_record/vendor/simple.rb +0 -693
- data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
- data/lib/active_record/wrappings.rb +0 -58
- data/test/connections/native_sqlserver/connection.rb +0 -23
- data/test/connections/native_sqlserver_odbc/connection.rb +0 -25
- data/test/deprecated_associations_test.rb +0 -396
- data/test/fixtures/db_definitions/mysql.drop.sql +0 -32
- data/test/fixtures/db_definitions/mysql.sql +0 -234
- data/test/fixtures/db_definitions/mysql2.drop.sql +0 -2
- data/test/fixtures/db_definitions/mysql2.sql +0 -5
- data/test/fixtures/db_definitions/sqlserver.drop.sql +0 -34
- data/test/fixtures/db_definitions/sqlserver.sql +0 -243
- data/test/fixtures/db_definitions/sqlserver2.drop.sql +0 -2
- data/test/fixtures/db_definitions/sqlserver2.sql +0 -5
- data/test/fixtures/mixin.rb +0 -63
- data/test/mixin_nested_set_test.rb +0 -196
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'abstract_unit'
|
2
|
+
require 'fixtures/topic'
|
2
3
|
|
3
4
|
class AttributeMethodsTest < Test::Unit::TestCase
|
5
|
+
fixtures :topics
|
4
6
|
def setup
|
5
7
|
@old_suffixes = ActiveRecord::Base.send(:attribute_method_suffixes).dup
|
6
8
|
@target = Class.new(ActiveRecord::Base)
|
@@ -46,4 +48,99 @@ class AttributeMethodsTest < Test::Unit::TestCase
|
|
46
48
|
assert_equal ['title', 1, 2, 3], topic.send(meth, 1, 2, 3)
|
47
49
|
end
|
48
50
|
end
|
51
|
+
|
52
|
+
def test_should_unserialize_attributes_for_frozen_records
|
53
|
+
myobj = {:value1 => :value2}
|
54
|
+
topic = Topic.create("content" => myobj)
|
55
|
+
topic.freeze
|
56
|
+
assert_equal myobj, topic.content
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_kernel_methods_not_implemented_in_activerecord
|
60
|
+
%w(test name display y).each do |method|
|
61
|
+
assert_equal false, ActiveRecord::Base.instance_method_already_implemented?(method), "##{method} is defined"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_primary_key_implemented
|
66
|
+
assert_equal true, Class.new(ActiveRecord::Base).instance_method_already_implemented?('id')
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_defined_kernel_methods_implemented_in_model
|
70
|
+
%w(test name display y).each do |method|
|
71
|
+
klass = Class.new ActiveRecord::Base
|
72
|
+
klass.class_eval "def #{method}() 'defined #{method}' end"
|
73
|
+
assert_equal true, klass.instance_method_already_implemented?(method), "##{method} is not defined"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_defined_kernel_methods_implemented_in_model_abstract_subclass
|
78
|
+
%w(test name display y).each do |method|
|
79
|
+
abstract = Class.new ActiveRecord::Base
|
80
|
+
abstract.class_eval "def #{method}() 'defined #{method}' end"
|
81
|
+
abstract.abstract_class = true
|
82
|
+
klass = Class.new abstract
|
83
|
+
assert_equal true, klass.instance_method_already_implemented?(method), "##{method} is not defined"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_raises_dangerous_attribute_error_when_defining_activerecord_method_in_model
|
88
|
+
%w(save create_or_update).each do |method|
|
89
|
+
klass = Class.new ActiveRecord::Base
|
90
|
+
klass.class_eval "def #{method}() 'defined #{method}' end"
|
91
|
+
assert_raises ActiveRecord::DangerousAttributeError do
|
92
|
+
klass.instance_method_already_implemented?(method)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_only_time_related_columns_are_meant_to_be_cached_by_default
|
98
|
+
expected = %w(datetime timestamp time date).sort
|
99
|
+
assert_equal expected, ActiveRecord::Base.attribute_types_cached_by_default.map(&:to_s).sort
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_declaring_attributes_as_cached_adds_them_to_the_attributes_cached_by_default
|
103
|
+
default_attributes = Topic.cached_attributes
|
104
|
+
Topic.cache_attributes :replies_count
|
105
|
+
expected = default_attributes + ["replies_count"]
|
106
|
+
assert_equal expected.sort, Topic.cached_attributes.sort
|
107
|
+
Topic.instance_variable_set "@cached_attributes", nil
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_time_related_columns_are_actually_cached
|
111
|
+
column_types = %w(datetime timestamp time date).map(&:to_sym)
|
112
|
+
column_names = Topic.columns.select{|c| column_types.include?(c.type) }.map(&:name)
|
113
|
+
|
114
|
+
assert_equal column_names.sort, Topic.cached_attributes.sort
|
115
|
+
assert_equal time_related_columns_on_topic.sort, Topic.cached_attributes.sort
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_accessing_cached_attributes_caches_the_converted_values_and_nothing_else
|
119
|
+
t = topics(:first)
|
120
|
+
cache = t.instance_variable_get "@attributes_cache"
|
121
|
+
|
122
|
+
assert_not_nil cache
|
123
|
+
assert cache.empty?
|
124
|
+
|
125
|
+
all_columns = Topic.columns.map(&:name)
|
126
|
+
cached_columns = time_related_columns_on_topic
|
127
|
+
uncached_columns = all_columns - cached_columns
|
128
|
+
|
129
|
+
all_columns.each do |attr_name|
|
130
|
+
attribute_gets_cached = Topic.cache_attribute?(attr_name)
|
131
|
+
val = t.send attr_name unless attr_name == "type"
|
132
|
+
if attribute_gets_cached
|
133
|
+
assert cached_columns.include?(attr_name)
|
134
|
+
assert_equal val, cache[attr_name]
|
135
|
+
else
|
136
|
+
assert uncached_columns.include?(attr_name)
|
137
|
+
assert !cache.include?(attr_name)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
def time_related_columns_on_topic
|
144
|
+
Topic.columns.select{|c| [:time, :date, :datetime, :timestamp].include?(c.type)}.map(&:name)
|
145
|
+
end
|
49
146
|
end
|
data/test/base_test.rb
CHANGED
@@ -11,6 +11,8 @@ require 'fixtures/column_name'
|
|
11
11
|
require 'fixtures/subscriber'
|
12
12
|
require 'fixtures/keyboard'
|
13
13
|
require 'fixtures/post'
|
14
|
+
require 'fixtures/minimalistic'
|
15
|
+
require 'rexml/document'
|
14
16
|
|
15
17
|
class Category < ActiveRecord::Base; end
|
16
18
|
class Smarts < ActiveRecord::Base; end
|
@@ -38,6 +40,11 @@ class LooseDescendant < LoosePerson
|
|
38
40
|
attr_protected :phone_number
|
39
41
|
end
|
40
42
|
|
43
|
+
class LooseDescendantSecond< LoosePerson
|
44
|
+
attr_protected :phone_number
|
45
|
+
attr_protected :name
|
46
|
+
end
|
47
|
+
|
41
48
|
class TightPerson < ActiveRecord::Base
|
42
49
|
self.table_name = 'people'
|
43
50
|
attr_accessible :name, :address
|
@@ -47,14 +54,24 @@ class TightDescendant < TightPerson
|
|
47
54
|
attr_accessible :phone_number
|
48
55
|
end
|
49
56
|
|
57
|
+
class ReadonlyTitlePost < Post
|
58
|
+
attr_readonly :title
|
59
|
+
end
|
60
|
+
|
50
61
|
class Booleantest < ActiveRecord::Base; end
|
51
62
|
|
52
63
|
class Task < ActiveRecord::Base
|
53
64
|
attr_protected :starting
|
54
65
|
end
|
55
66
|
|
67
|
+
class TopicWithProtectedContentAndAccessibleAuthorName < ActiveRecord::Base
|
68
|
+
self.table_name = 'topics'
|
69
|
+
attr_accessible :author_name
|
70
|
+
attr_protected :content
|
71
|
+
end
|
72
|
+
|
56
73
|
class BasicsTest < Test::Unit::TestCase
|
57
|
-
fixtures :topics, :companies, :developers, :projects, :computers, :accounts
|
74
|
+
fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics
|
58
75
|
|
59
76
|
def test_table_exists
|
60
77
|
assert !NonExistentTable.table_exists?
|
@@ -174,6 +191,15 @@ class BasicsTest < Test::Unit::TestCase
|
|
174
191
|
assert_nil topic.title
|
175
192
|
end
|
176
193
|
|
194
|
+
def test_save_for_record_with_only_primary_key
|
195
|
+
minimalistic = Minimalistic.new
|
196
|
+
assert_nothing_raised { minimalistic.save }
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_save_for_record_with_only_primary_key_that_is_provided
|
200
|
+
assert_nothing_raised { Minimalistic.create!(:id => 2) }
|
201
|
+
end
|
202
|
+
|
177
203
|
def test_hashes_not_mangled
|
178
204
|
new_topic = { :title => "New Topic" }
|
179
205
|
new_topic_values = { :title => "AnotherTopic" }
|
@@ -230,6 +256,11 @@ class BasicsTest < Test::Unit::TestCase
|
|
230
256
|
topicReloaded.send :write_attribute, 'does_not_exist', 'test'
|
231
257
|
assert_nothing_raised { topicReloaded.save }
|
232
258
|
end
|
259
|
+
|
260
|
+
def test_update_for_record_with_only_primary_key
|
261
|
+
minimalistic = minimalistics(:first)
|
262
|
+
assert_nothing_raised { minimalistic.save }
|
263
|
+
end
|
233
264
|
|
234
265
|
def test_write_attribute
|
235
266
|
topic = Topic.new
|
@@ -289,25 +320,59 @@ class BasicsTest < Test::Unit::TestCase
|
|
289
320
|
assert topic.approved?, "approved should be true"
|
290
321
|
# puts ""
|
291
322
|
end
|
292
|
-
|
293
|
-
def
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
323
|
+
|
324
|
+
def test_query_attribute_string
|
325
|
+
[nil, "", " "].each do |value|
|
326
|
+
assert_equal false, Topic.new(:author_name => value).author_name?
|
327
|
+
end
|
328
|
+
|
329
|
+
assert_equal true, Topic.new(:author_name => "Name").author_name?
|
330
|
+
end
|
331
|
+
|
332
|
+
def test_query_attribute_number
|
333
|
+
[nil, 0, "0"].each do |value|
|
334
|
+
assert_equal false, Developer.new(:salary => value).salary?
|
303
335
|
end
|
336
|
+
|
337
|
+
assert_equal true, Developer.new(:salary => 1).salary?
|
338
|
+
assert_equal true, Developer.new(:salary => "1").salary?
|
304
339
|
end
|
340
|
+
|
341
|
+
def test_query_attribute_boolean
|
342
|
+
[nil, "", false, "false", "f", 0].each do |value|
|
343
|
+
assert_equal false, Topic.new(:approved => value).approved?
|
344
|
+
end
|
345
|
+
|
346
|
+
[true, "true", "1", 1].each do |value|
|
347
|
+
assert_equal true, Topic.new(:approved => value).approved?
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
def test_query_attribute_with_custom_fields
|
352
|
+
object = Company.find_by_sql(<<-SQL).first
|
353
|
+
SELECT c1.*, c2.ruby_type as string_value, c2.rating as int_value
|
354
|
+
FROM companies c1, companies c2
|
355
|
+
WHERE c1.firm_id = c2.id
|
356
|
+
AND c1.id = 2
|
357
|
+
SQL
|
358
|
+
|
359
|
+
assert_equal "Firm", object.string_value
|
360
|
+
assert object.string_value?
|
361
|
+
|
362
|
+
object.string_value = " "
|
363
|
+
assert !object.string_value?
|
364
|
+
|
365
|
+
assert_equal 1, object.int_value.to_i
|
366
|
+
assert object.int_value?
|
367
|
+
|
368
|
+
object.int_value = "0"
|
369
|
+
assert !object.int_value?
|
370
|
+
end
|
371
|
+
|
305
372
|
|
306
373
|
def test_reader_for_invalid_column_names
|
307
|
-
|
308
|
-
|
309
|
-
topic.send(:define_read_method, "mumub-jumbo".to_sym, "mumub-jumbo", nil)
|
310
|
-
assert !Topic.read_methods.include?("mumub-jumbo")
|
374
|
+
Topic.send(:define_read_method, "mumub-jumbo".to_sym, "mumub-jumbo", nil)
|
375
|
+
assert !Topic.generated_methods.include?("mumub-jumbo")
|
311
376
|
end
|
312
377
|
|
313
378
|
def test_non_attribute_access_and_assignment
|
@@ -321,8 +386,9 @@ class BasicsTest < Test::Unit::TestCase
|
|
321
386
|
# SQL Server doesn't have a separate column type just for dates, so all are returned as time
|
322
387
|
return true if current_adapter?(:SQLServerAdapter)
|
323
388
|
|
324
|
-
if current_adapter?(:SybaseAdapter)
|
389
|
+
if current_adapter?(:SybaseAdapter, :OracleAdapter)
|
325
390
|
# Sybase ctlib does not (yet?) support the date type; use datetime instead.
|
391
|
+
# Oracle treats all dates/times as Time.
|
326
392
|
assert_kind_of(
|
327
393
|
Time, Topic.find(1).last_read,
|
328
394
|
"The last_read attribute should be of the Time class"
|
@@ -353,6 +419,13 @@ class BasicsTest < Test::Unit::TestCase
|
|
353
419
|
assert_equal 9900, Topic.find(2).written_on.usec
|
354
420
|
end
|
355
421
|
end
|
422
|
+
|
423
|
+
def test_custom_mutator
|
424
|
+
topic = Topic.find(1)
|
425
|
+
# This mutator is protected in the class definition
|
426
|
+
topic.send(:approved=, true)
|
427
|
+
assert topic.instance_variable_get("@custom_approved")
|
428
|
+
end
|
356
429
|
|
357
430
|
def test_destroy
|
358
431
|
topic = Topic.find(1)
|
@@ -494,17 +567,33 @@ class BasicsTest < Test::Unit::TestCase
|
|
494
567
|
Topic.decrement_counter("replies_count", 2)
|
495
568
|
assert_equal -2, Topic.find(2).replies_count
|
496
569
|
end
|
497
|
-
|
498
|
-
def test_update_all
|
499
|
-
# The ADO library doesn't support the number of affected rows
|
500
|
-
return true if current_adapter?(:SQLServerAdapter)
|
501
570
|
|
571
|
+
def test_update_all
|
502
572
|
assert_equal 2, Topic.update_all("content = 'bulk updated!'")
|
503
573
|
assert_equal "bulk updated!", Topic.find(1).content
|
504
574
|
assert_equal "bulk updated!", Topic.find(2).content
|
575
|
+
|
505
576
|
assert_equal 2, Topic.update_all(['content = ?', 'bulk updated again!'])
|
506
577
|
assert_equal "bulk updated again!", Topic.find(1).content
|
507
578
|
assert_equal "bulk updated again!", Topic.find(2).content
|
579
|
+
|
580
|
+
assert_equal 2, Topic.update_all(['content = ?', nil])
|
581
|
+
assert_nil Topic.find(1).content
|
582
|
+
end
|
583
|
+
|
584
|
+
def test_update_all_with_hash
|
585
|
+
assert_not_nil Topic.find(1).last_read
|
586
|
+
assert_equal 2, Topic.update_all(:content => 'bulk updated with hash!', :last_read => nil)
|
587
|
+
assert_equal "bulk updated with hash!", Topic.find(1).content
|
588
|
+
assert_equal "bulk updated with hash!", Topic.find(2).content
|
589
|
+
assert_nil Topic.find(1).last_read
|
590
|
+
assert_nil Topic.find(2).last_read
|
591
|
+
end
|
592
|
+
|
593
|
+
if current_adapter?(:MysqlAdapter)
|
594
|
+
def test_update_all_with_order_and_limit
|
595
|
+
assert_equal 1, Topic.update_all("content = 'bulk updated!'", nil, :limit => 1, :order => 'id DESC')
|
596
|
+
end
|
508
597
|
end
|
509
598
|
|
510
599
|
def test_update_many
|
@@ -517,9 +606,6 @@ class BasicsTest < Test::Unit::TestCase
|
|
517
606
|
end
|
518
607
|
|
519
608
|
def test_delete_all
|
520
|
-
# The ADO library doesn't support the number of affected rows
|
521
|
-
return true if current_adapter?(:SQLServerAdapter)
|
522
|
-
|
523
609
|
assert_equal 2, Topic.delete_all
|
524
610
|
end
|
525
611
|
|
@@ -703,6 +789,12 @@ class BasicsTest < Test::Unit::TestCase
|
|
703
789
|
assert_raise(ActiveRecord::RecordInvalid) { reply.update_attributes!(:title => nil, :content => "Have a nice evening") }
|
704
790
|
end
|
705
791
|
|
792
|
+
def test_mass_assignment_should_raise_exception_if_accessible_and_protected_attribute_writers_are_both_used
|
793
|
+
topic = TopicWithProtectedContentAndAccessibleAuthorName.new
|
794
|
+
assert_raises(RuntimeError) { topic.attributes = { "author_name" => "me" } }
|
795
|
+
assert_raises(RuntimeError) { topic.attributes = { "content" => "stuff" } }
|
796
|
+
end
|
797
|
+
|
706
798
|
def test_mass_assignment_protection
|
707
799
|
firm = Firm.new
|
708
800
|
firm.attributes = { "name" => "Next Angle", "rating" => 5 }
|
@@ -711,7 +803,7 @@ class BasicsTest < Test::Unit::TestCase
|
|
711
803
|
|
712
804
|
def test_mass_assignment_protection_against_class_attribute_writers
|
713
805
|
[:logger, :configurations, :primary_key_prefix_type, :table_name_prefix, :table_name_suffix, :pluralize_table_names, :colorize_logging,
|
714
|
-
:default_timezone, :allow_concurrency, :
|
806
|
+
:default_timezone, :allow_concurrency, :schema_format, :verification_timeout, :lock_optimistically, :record_timestamps].each do |method|
|
715
807
|
assert Task.respond_to?(method)
|
716
808
|
assert Task.respond_to?("#{method}=")
|
717
809
|
assert Task.new.respond_to?(method)
|
@@ -727,7 +819,7 @@ class BasicsTest < Test::Unit::TestCase
|
|
727
819
|
assert_nil keyboard.id
|
728
820
|
end
|
729
821
|
|
730
|
-
def
|
822
|
+
def test_customized_primary_key_remains_protected_when_referred_to_as_id
|
731
823
|
subscriber = Subscriber.new(:id => 'webster123', :name => 'nice try')
|
732
824
|
assert_nil subscriber.id
|
733
825
|
|
@@ -756,16 +848,32 @@ class BasicsTest < Test::Unit::TestCase
|
|
756
848
|
|
757
849
|
def test_mass_assignment_protection_inheritance
|
758
850
|
assert_nil LoosePerson.accessible_attributes
|
759
|
-
assert_equal [
|
851
|
+
assert_equal Set.new([ 'credit_rating', 'administrator' ]), LoosePerson.protected_attributes
|
760
852
|
|
761
853
|
assert_nil LooseDescendant.accessible_attributes
|
762
|
-
assert_equal [
|
854
|
+
assert_equal Set.new([ 'credit_rating', 'administrator', 'phone_number' ]), LooseDescendant.protected_attributes
|
855
|
+
|
856
|
+
assert_nil LooseDescendantSecond.accessible_attributes
|
857
|
+
assert_equal Set.new([ 'credit_rating', 'administrator', 'phone_number', 'name' ]), LooseDescendantSecond.protected_attributes, 'Running attr_protected twice in one class should merge the protections'
|
763
858
|
|
764
859
|
assert_nil TightPerson.protected_attributes
|
765
|
-
assert_equal [
|
860
|
+
assert_equal Set.new([ 'name', 'address' ]), TightPerson.accessible_attributes
|
766
861
|
|
767
862
|
assert_nil TightDescendant.protected_attributes
|
768
|
-
assert_equal [
|
863
|
+
assert_equal Set.new([ 'name', 'address', 'phone_number' ]), TightDescendant.accessible_attributes
|
864
|
+
end
|
865
|
+
|
866
|
+
def test_readonly_attributes
|
867
|
+
assert_equal Set.new([ 'title' ]), ReadonlyTitlePost.readonly_attributes
|
868
|
+
|
869
|
+
post = ReadonlyTitlePost.create(:title => "cannot change this", :body => "changeable")
|
870
|
+
post.reload
|
871
|
+
assert_equal "cannot change this", post.title
|
872
|
+
|
873
|
+
post.update_attributes(:title => "try to change", :body => "changed")
|
874
|
+
post.reload
|
875
|
+
assert_equal "cannot change this", post.title
|
876
|
+
assert_equal "changed", post.body
|
769
877
|
end
|
770
878
|
|
771
879
|
def test_multiparameter_attributes_on_date
|
@@ -885,6 +993,10 @@ class BasicsTest < Test::Unit::TestCase
|
|
885
993
|
cloned_topic.title["a"] = "c"
|
886
994
|
assert_equal "b", topic.title["a"]
|
887
995
|
|
996
|
+
#test if attributes set as part of after_initialize are cloned correctly
|
997
|
+
assert_equal topic.author_email_address, cloned_topic.author_email_address
|
998
|
+
|
999
|
+
# test if saved clone object differs from original
|
888
1000
|
cloned_topic.save
|
889
1001
|
assert !cloned_topic.new_record?
|
890
1002
|
assert cloned_topic.id != topic.id
|
@@ -1149,12 +1261,12 @@ class BasicsTest < Test::Unit::TestCase
|
|
1149
1261
|
end
|
1150
1262
|
|
1151
1263
|
def test_increment_attribute
|
1152
|
-
assert_equal
|
1153
|
-
|
1154
|
-
assert_equal
|
1155
|
-
|
1156
|
-
|
1157
|
-
assert_equal
|
1264
|
+
assert_equal 50, accounts(:signals37).credit_limit
|
1265
|
+
accounts(:signals37).increment! :credit_limit
|
1266
|
+
assert_equal 51, accounts(:signals37, :reload).credit_limit
|
1267
|
+
|
1268
|
+
accounts(:signals37).increment(:credit_limit).increment!(:credit_limit)
|
1269
|
+
assert_equal 53, accounts(:signals37, :reload).credit_limit
|
1158
1270
|
end
|
1159
1271
|
|
1160
1272
|
def test_increment_nil_attribute
|
@@ -1164,14 +1276,13 @@ class BasicsTest < Test::Unit::TestCase
|
|
1164
1276
|
end
|
1165
1277
|
|
1166
1278
|
def test_decrement_attribute
|
1167
|
-
|
1168
|
-
assert_equal 3, topics(:first).replies_count
|
1169
|
-
|
1170
|
-
topics(:first).decrement!(:replies_count)
|
1171
|
-
assert_equal 2, topics(:first, :reload).replies_count
|
1279
|
+
assert_equal 50, accounts(:signals37).credit_limit
|
1172
1280
|
|
1173
|
-
|
1174
|
-
assert_equal
|
1281
|
+
accounts(:signals37).decrement!(:credit_limit)
|
1282
|
+
assert_equal 49, accounts(:signals37, :reload).credit_limit
|
1283
|
+
|
1284
|
+
accounts(:signals37).decrement(:credit_limit).decrement!(:credit_limit)
|
1285
|
+
assert_equal 47, accounts(:signals37, :reload).credit_limit
|
1175
1286
|
end
|
1176
1287
|
|
1177
1288
|
def test_toggle_attribute
|
@@ -1250,11 +1361,8 @@ class BasicsTest < Test::Unit::TestCase
|
|
1250
1361
|
|
1251
1362
|
def test_count_with_join
|
1252
1363
|
res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
|
1253
|
-
|
1254
|
-
|
1255
|
-
res2 = Post.count("posts.#{QUOTED_TYPE} = 'Post'",
|
1256
|
-
"LEFT JOIN comments ON posts.id=comments.post_id")
|
1257
|
-
end
|
1364
|
+
|
1365
|
+
res2 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'", :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
|
1258
1366
|
assert_equal res, res2
|
1259
1367
|
|
1260
1368
|
res3 = nil
|
@@ -1274,15 +1382,17 @@ class BasicsTest < Test::Unit::TestCase
|
|
1274
1382
|
|
1275
1383
|
assert_equal res4, res5
|
1276
1384
|
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1385
|
+
unless current_adapter?(:SQLite2Adapter, :DeprecatedSQLiteAdapter)
|
1386
|
+
res6 = Post.count_by_sql "SELECT COUNT(DISTINCT p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
|
1387
|
+
res7 = nil
|
1388
|
+
assert_nothing_raised do
|
1389
|
+
res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
|
1390
|
+
:joins => "p, comments co",
|
1391
|
+
:select => "p.id",
|
1392
|
+
:distinct => true)
|
1393
|
+
end
|
1394
|
+
assert_equal res6, res7
|
1284
1395
|
end
|
1285
|
-
assert_equal res6, res7
|
1286
1396
|
end
|
1287
1397
|
|
1288
1398
|
def test_clear_association_cache_stored
|
@@ -1299,12 +1409,12 @@ class BasicsTest < Test::Unit::TestCase
|
|
1299
1409
|
client_new = Client.new
|
1300
1410
|
client_new.name = "The Joneses"
|
1301
1411
|
clients = [ client_stored, client_new ]
|
1302
|
-
|
1412
|
+
|
1303
1413
|
firm.clients << clients
|
1414
|
+
assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
|
1304
1415
|
|
1305
1416
|
firm.clear_association_cache
|
1306
|
-
|
1307
|
-
assert_equal firm.clients.collect{ |x| x.name }.sort, clients.collect{ |x| x.name }.sort
|
1417
|
+
assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
|
1308
1418
|
end
|
1309
1419
|
|
1310
1420
|
def test_interpolate_sql
|
@@ -1442,28 +1552,48 @@ class BasicsTest < Test::Unit::TestCase
|
|
1442
1552
|
end
|
1443
1553
|
|
1444
1554
|
def test_to_xml
|
1445
|
-
xml = topics(:first).to_xml(:indent => 0
|
1555
|
+
xml = REXML::Document.new(topics(:first).to_xml(:indent => 0))
|
1446
1556
|
bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema
|
1447
1557
|
written_on_in_current_timezone = topics(:first).written_on.xmlschema
|
1448
1558
|
last_read_in_current_timezone = topics(:first).last_read.xmlschema
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1559
|
+
|
1560
|
+
assert_equal "topic", xml.root.name
|
1561
|
+
assert_equal "The First Topic" , xml.elements["//title"].text
|
1562
|
+
assert_equal "David" , xml.elements["//author-name"].text
|
1563
|
+
|
1564
|
+
assert_equal "1", xml.elements["//id"].text
|
1565
|
+
assert_equal "integer" , xml.elements["//id"].attributes['type']
|
1566
|
+
|
1567
|
+
assert_equal "1", xml.elements["//replies-count"].text
|
1568
|
+
assert_equal "integer" , xml.elements["//replies-count"].attributes['type']
|
1569
|
+
|
1570
|
+
assert_equal written_on_in_current_timezone, xml.elements["//written-on"].text
|
1571
|
+
assert_equal "datetime" , xml.elements["//written-on"].attributes['type']
|
1572
|
+
|
1573
|
+
assert_equal "--- Have a nice day\n" , xml.elements["//content"].text
|
1574
|
+
assert_equal "yaml" , xml.elements["//content"].attributes['type']
|
1575
|
+
|
1576
|
+
assert_equal "david@loudthinking.com", xml.elements["//author-email-address"].text
|
1577
|
+
|
1578
|
+
assert_equal nil, xml.elements["//parent-id"].text
|
1579
|
+
assert_equal "integer", xml.elements["//parent-id"].attributes['type']
|
1580
|
+
assert_equal "true", xml.elements["//parent-id"].attributes['nil']
|
1581
|
+
|
1458
1582
|
if current_adapter?(:SybaseAdapter, :SQLServerAdapter, :OracleAdapter)
|
1459
|
-
|
1583
|
+
assert_equal last_read_in_current_timezone, xml.elements["//last-read"].text
|
1584
|
+
assert_equal "datetime" , xml.elements["//last-read"].attributes['type']
|
1460
1585
|
else
|
1461
|
-
|
1586
|
+
assert_equal "2004-04-15", xml.elements["//last-read"].text
|
1587
|
+
assert_equal "date" , xml.elements["//last-read"].attributes['type']
|
1462
1588
|
end
|
1589
|
+
|
1463
1590
|
# Oracle and DB2 don't have true boolean or time-only fields
|
1464
1591
|
unless current_adapter?(:OracleAdapter, :DB2Adapter)
|
1465
|
-
|
1466
|
-
|
1592
|
+
assert_equal "false", xml.elements["//approved"].text
|
1593
|
+
assert_equal "boolean" , xml.elements["//approved"].attributes['type']
|
1594
|
+
|
1595
|
+
assert_equal bonus_time_in_current_timezone, xml.elements["//bonus-time"].text
|
1596
|
+
assert_equal "datetime" , xml.elements["//bonus-time"].attributes['type']
|
1467
1597
|
end
|
1468
1598
|
end
|
1469
1599
|
|
@@ -1481,13 +1611,13 @@ class BasicsTest < Test::Unit::TestCase
|
|
1481
1611
|
def test_to_xml_including_has_many_association
|
1482
1612
|
xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count)
|
1483
1613
|
assert_equal "<topic>", xml.first(7)
|
1484
|
-
assert xml.include?(%(<replies><reply>))
|
1614
|
+
assert xml.include?(%(<replies type="array"><reply>))
|
1485
1615
|
assert xml.include?(%(<title>The Second Topic's of the day</title>))
|
1486
1616
|
end
|
1487
1617
|
|
1488
1618
|
def test_array_to_xml_including_has_many_association
|
1489
1619
|
xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :include => :replies)
|
1490
|
-
assert xml.include?(%(<replies><reply>))
|
1620
|
+
assert xml.include?(%(<replies type="array"><reply>))
|
1491
1621
|
end
|
1492
1622
|
|
1493
1623
|
def test_array_to_xml_including_methods
|
@@ -1521,7 +1651,7 @@ class BasicsTest < Test::Unit::TestCase
|
|
1521
1651
|
xml = companies(:first_firm).to_xml(:indent => 0, :skip_instruct => true, :include => [ :clients, :account ])
|
1522
1652
|
assert_equal "<firm>", xml.first(6)
|
1523
1653
|
assert xml.include?(%(<account>))
|
1524
|
-
assert xml.include?(%(<clients><client>))
|
1654
|
+
assert xml.include?(%(<clients type="array"><client>))
|
1525
1655
|
end
|
1526
1656
|
|
1527
1657
|
def test_to_xml_including_multiple_associations_with_options
|
@@ -1532,7 +1662,7 @@ class BasicsTest < Test::Unit::TestCase
|
|
1532
1662
|
|
1533
1663
|
assert_equal "<firm>", xml.first(6)
|
1534
1664
|
assert xml.include?(%(<client><name>Summit</name></client>))
|
1535
|
-
assert xml.include?(%(<clients><client>))
|
1665
|
+
assert xml.include?(%(<clients type="array"><client>))
|
1536
1666
|
end
|
1537
1667
|
|
1538
1668
|
def test_to_xml_including_methods
|
@@ -1541,6 +1671,15 @@ class BasicsTest < Test::Unit::TestCase
|
|
1541
1671
|
assert xml.include?(%(<arbitrary-method>I am Jack's profound disappointment</arbitrary-method>))
|
1542
1672
|
end
|
1543
1673
|
|
1674
|
+
def test_to_xml_with_block
|
1675
|
+
value = "Rockin' the block"
|
1676
|
+
xml = Company.new.to_xml(:skip_instruct => true) do |xml|
|
1677
|
+
xml.tag! "arbitrary-element", value
|
1678
|
+
end
|
1679
|
+
assert_equal "<company>", xml.first(9)
|
1680
|
+
assert xml.include?(%(<arbitrary-element>#{value}</arbitrary-element>))
|
1681
|
+
end
|
1682
|
+
|
1544
1683
|
def test_except_attributes
|
1545
1684
|
assert_equal(
|
1546
1685
|
%w( author_name type id approved replies_count bonus_time written_on content author_email_address parent_id last_read),
|
@@ -1566,34 +1705,41 @@ class BasicsTest < Test::Unit::TestCase
|
|
1566
1705
|
def test_to_param_should_return_string
|
1567
1706
|
assert_kind_of String, Client.find(:first).to_param
|
1568
1707
|
end
|
1708
|
+
|
1709
|
+
def test_inspect_class
|
1710
|
+
assert_equal 'ActiveRecord::Base', ActiveRecord::Base.inspect
|
1711
|
+
assert_equal 'LoosePerson(abstract)', LoosePerson.inspect
|
1712
|
+
assert_match(/^Topic\(id: integer, title: string/, Topic.inspect)
|
1713
|
+
end
|
1714
|
+
|
1715
|
+
def test_inspect_instance
|
1716
|
+
topic = topics(:first)
|
1717
|
+
assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david@loudthinking.com", written_on: "#{topic.written_on.to_s(:db)}", bonus_time: "#{topic.bonus_time.to_s(:db)}", last_read: "#{topic.last_read.to_s(:db)}", content: "Have a nice day", approved: false, replies_count: 1, parent_id: nil, type: nil>), topic.inspect
|
1718
|
+
end
|
1719
|
+
|
1720
|
+
def test_inspect_new_instance
|
1721
|
+
assert_match /Topic id: nil/, Topic.new.inspect
|
1722
|
+
end
|
1569
1723
|
|
1570
|
-
|
1571
|
-
|
1572
|
-
|
1573
|
-
|
1574
|
-
# counts = []
|
1575
|
-
# 2.times do
|
1576
|
-
# require_dependency 'fixtures/company'
|
1577
|
-
# Firm.find(:first)
|
1578
|
-
# Dependencies.clear
|
1579
|
-
# ActiveRecord::Base.reset_subclasses
|
1580
|
-
# Dependencies.remove_subclasses_for(ActiveRecord::Base)
|
1581
|
-
#
|
1582
|
-
# GC.start
|
1583
|
-
#
|
1584
|
-
# count = 0
|
1585
|
-
# ObjectSpace.each_object(Proc) { count += 1 }
|
1586
|
-
# counts << count
|
1587
|
-
# end
|
1588
|
-
# assert counts.last <= counts.first,
|
1589
|
-
# "expected last count (#{counts.last}) to be <= first count (#{counts.first})"
|
1590
|
-
#end
|
1724
|
+
def test_inspect_limited_select_instance
|
1725
|
+
assert_equal %(#<Topic id: 1>), Topic.find(:first, :select => 'id', :conditions => 'id = 1').inspect
|
1726
|
+
assert_equal %(#<Topic id: 1, title: "The First Topic">), Topic.find(:first, :select => 'id, title', :conditions => 'id = 1').inspect
|
1727
|
+
end
|
1591
1728
|
|
1592
|
-
|
1593
|
-
|
1594
|
-
|
1595
|
-
|
1596
|
-
|
1597
|
-
|
1598
|
-
|
1729
|
+
def test_inspect_class_without_table
|
1730
|
+
assert_equal "NonExistentTable(Table doesn't exist)", NonExistentTable.inspect
|
1731
|
+
end
|
1732
|
+
|
1733
|
+
def test_attribute_for_inspect
|
1734
|
+
t = topics(:first)
|
1735
|
+
t.title = "The First Topic Now Has A Title With\nNewlines And More Than 50 Characters"
|
1736
|
+
|
1737
|
+
assert_equal %("#{t.written_on.to_s(:db)}"), t.attribute_for_inspect(:written_on)
|
1738
|
+
assert_equal '"The First Topic Now Has A Title With\nNewlines And M..."', t.attribute_for_inspect(:title)
|
1739
|
+
end
|
1740
|
+
|
1741
|
+
def test_becomes
|
1742
|
+
assert_kind_of Reply, topics(:first).becomes(Reply)
|
1743
|
+
assert_equal "The First Topic", topics(:first).becomes(Reply).title
|
1744
|
+
end
|
1599
1745
|
end
|