ibm_db 1.1.1-mswin32

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 (36) hide show
  1. data/CHANGES +155 -0
  2. data/LICENSE +18 -0
  3. data/MANIFEST +14 -0
  4. data/README +274 -0
  5. data/ext/Makefile.nt32 +181 -0
  6. data/ext/extconf.rb +58 -0
  7. data/ext/ibm_db.c +6553 -0
  8. data/ext/ruby_ibm_db.h +214 -0
  9. data/init.rb +42 -0
  10. data/lib/IBM_DB.rb +2 -0
  11. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +2218 -0
  12. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -0
  13. data/lib/mswin32/ibm_db.so +0 -0
  14. data/test/cases/adapter_test.rb +180 -0
  15. data/test/cases/associations/cascaded_eager_loading_test.rb +133 -0
  16. data/test/cases/associations/eager_test.rb +842 -0
  17. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +874 -0
  18. data/test/cases/associations/has_many_through_associations_test.rb +281 -0
  19. data/test/cases/associations/join_model_test.rb +801 -0
  20. data/test/cases/attribute_methods_test.rb +312 -0
  21. data/test/cases/base_test.rb +2114 -0
  22. data/test/cases/calculations_test.rb +346 -0
  23. data/test/cases/finder_test.rb +1092 -0
  24. data/test/cases/fixtures_test.rb +660 -0
  25. data/test/cases/migration_test.rb +1618 -0
  26. data/test/cases/schema_dumper_test.rb +197 -0
  27. data/test/cases/validations_test.rb +1604 -0
  28. data/test/connections/native_ibm_db/connection.rb +40 -0
  29. data/test/ibm_db_test.rb +25 -0
  30. data/test/models/warehouse_thing.rb +5 -0
  31. data/test/schema/i5/ibm_db_specific_schema.rb +134 -0
  32. data/test/schema/ids/ibm_db_specific_schema.rb +137 -0
  33. data/test/schema/luw/ibm_db_specific_schema.rb +134 -0
  34. data/test/schema/schema.rb +499 -0
  35. data/test/schema/zOS/ibm_db_specific_schema.rb +205 -0
  36. metadata +115 -0
@@ -0,0 +1,312 @@
1
+ require "cases/helper"
2
+ require 'models/topic'
3
+ require 'models/minimalistic'
4
+
5
+ class AttributeMethodsTest < ActiveRecord::TestCase
6
+ fixtures :topics
7
+ def setup
8
+ @old_suffixes = ActiveRecord::Base.send(:attribute_method_suffixes).dup
9
+ @target = Class.new(ActiveRecord::Base)
10
+ @target.table_name = 'topics'
11
+ end
12
+
13
+ def teardown
14
+ ActiveRecord::Base.send(:attribute_method_suffixes).clear
15
+ ActiveRecord::Base.attribute_method_suffix *@old_suffixes
16
+ end
17
+
18
+ def test_match_attribute_method_query_returns_match_data
19
+ assert_not_nil md = @target.match_attribute_method?('title=')
20
+ assert_equal 'title', md.pre_match
21
+ assert_equal ['='], md.captures
22
+
23
+ %w(_hello_world ist! _maybe?).each do |suffix|
24
+ @target.class_eval "def attribute#{suffix}(*args) args end"
25
+ @target.attribute_method_suffix suffix
26
+
27
+ assert_not_nil md = @target.match_attribute_method?("title#{suffix}")
28
+ assert_equal 'title', md.pre_match
29
+ assert_equal [suffix], md.captures
30
+ end
31
+ end
32
+
33
+ def test_declared_attribute_method_affects_respond_to_and_method_missing
34
+ topic = @target.new(:title => 'Budget')
35
+ assert topic.respond_to?('title')
36
+ assert_equal 'Budget', topic.title
37
+ assert !topic.respond_to?('title_hello_world')
38
+ assert_raise(NoMethodError) { topic.title_hello_world }
39
+
40
+ %w(_hello_world _it! _candidate= able?).each do |suffix|
41
+ @target.class_eval "def attribute#{suffix}(*args) args end"
42
+ @target.attribute_method_suffix suffix
43
+
44
+ meth = "title#{suffix}"
45
+ assert topic.respond_to?(meth)
46
+ assert_equal ['title'], topic.send(meth)
47
+ assert_equal ['title', 'a'], topic.send(meth, 'a')
48
+ assert_equal ['title', 1, 2, 3], topic.send(meth, 1, 2, 3)
49
+ end
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_typecast_attribute_from_select_to_false
60
+ topic = Topic.create(:title => 'Budget')
61
+ unless current_adapter?(:IBM_DBAdapter)
62
+ topic = Topic.find(:first, :select => "topics.*, 1=2 as is_test")
63
+ assert !topic.is_test?
64
+ else
65
+ topic = Topic.find(:first, :select => "topics.approved as is_test")
66
+ assert topic.is_test?
67
+ end
68
+ end
69
+
70
+ unless current_adapter?(:IBM_DBAdapter)
71
+ def test_typecast_attribute_from_select_to_true
72
+ topic = Topic.create(:title => 'Budget')
73
+ topic = Topic.find(:first, :select => "topics.*, 2=2 as is_test")
74
+ assert topic.is_test?
75
+ end
76
+ end
77
+
78
+ def test_kernel_methods_not_implemented_in_activerecord
79
+ %w(test name display y).each do |method|
80
+ assert !ActiveRecord::Base.instance_method_already_implemented?(method), "##{method} is defined"
81
+ end
82
+ end
83
+
84
+ def test_primary_key_implemented
85
+ assert Class.new(ActiveRecord::Base).instance_method_already_implemented?('id')
86
+ end
87
+
88
+ def test_defined_kernel_methods_implemented_in_model
89
+ %w(test name display y).each do |method|
90
+ klass = Class.new ActiveRecord::Base
91
+ klass.class_eval "def #{method}() 'defined #{method}' end"
92
+ assert klass.instance_method_already_implemented?(method), "##{method} is not defined"
93
+ end
94
+ end
95
+
96
+ def test_defined_kernel_methods_implemented_in_model_abstract_subclass
97
+ %w(test name display y).each do |method|
98
+ abstract = Class.new ActiveRecord::Base
99
+ abstract.class_eval "def #{method}() 'defined #{method}' end"
100
+ abstract.abstract_class = true
101
+ klass = Class.new abstract
102
+ assert klass.instance_method_already_implemented?(method), "##{method} is not defined"
103
+ end
104
+ end
105
+
106
+ def test_raises_dangerous_attribute_error_when_defining_activerecord_method_in_model
107
+ %w(save create_or_update).each do |method|
108
+ klass = Class.new ActiveRecord::Base
109
+ klass.class_eval "def #{method}() 'defined #{method}' end"
110
+ assert_raise ActiveRecord::DangerousAttributeError do
111
+ klass.instance_method_already_implemented?(method)
112
+ end
113
+ end
114
+ end
115
+
116
+ def test_only_time_related_columns_are_meant_to_be_cached_by_default
117
+ expected = %w(datetime timestamp time date).sort
118
+ assert_equal expected, ActiveRecord::Base.attribute_types_cached_by_default.map(&:to_s).sort
119
+ end
120
+
121
+ def test_declaring_attributes_as_cached_adds_them_to_the_attributes_cached_by_default
122
+ default_attributes = Topic.cached_attributes
123
+ Topic.cache_attributes :replies_count
124
+ expected = default_attributes + ["replies_count"]
125
+ assert_equal expected.sort, Topic.cached_attributes.sort
126
+ Topic.instance_variable_set "@cached_attributes", nil
127
+ end
128
+
129
+ def test_time_related_columns_are_actually_cached
130
+ column_types = %w(datetime timestamp time date).map(&:to_sym)
131
+ column_names = Topic.columns.select{|c| column_types.include?(c.type) }.map(&:name)
132
+
133
+ assert_equal column_names.sort, Topic.cached_attributes.sort
134
+ assert_equal time_related_columns_on_topic.sort, Topic.cached_attributes.sort
135
+ end
136
+
137
+ def test_accessing_cached_attributes_caches_the_converted_values_and_nothing_else
138
+ t = topics(:first)
139
+ cache = t.instance_variable_get "@attributes_cache"
140
+
141
+ assert_not_nil cache
142
+ assert cache.empty?
143
+
144
+ all_columns = Topic.columns.map(&:name)
145
+ cached_columns = time_related_columns_on_topic
146
+ uncached_columns = all_columns - cached_columns
147
+
148
+ all_columns.each do |attr_name|
149
+ attribute_gets_cached = Topic.cache_attribute?(attr_name)
150
+ val = t.send attr_name unless attr_name == "type"
151
+ if attribute_gets_cached
152
+ assert cached_columns.include?(attr_name)
153
+ assert_equal val, cache[attr_name]
154
+ else
155
+ assert uncached_columns.include?(attr_name)
156
+ assert !cache.include?(attr_name)
157
+ end
158
+ end
159
+ end
160
+
161
+ def test_time_attributes_are_retrieved_in_current_time_zone
162
+ in_time_zone "Pacific Time (US & Canada)" do
163
+ utc_time = Time.utc(2008, 1, 1)
164
+ record = @target.new
165
+ record[:written_on] = utc_time
166
+ assert_equal utc_time, record.written_on # record.written on is equal to (i.e., simultaneous with) utc_time
167
+ assert_kind_of ActiveSupport::TimeWithZone, record.written_on # but is a TimeWithZone
168
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone # and is in the current Time.zone
169
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time # and represents time values adjusted accordingly
170
+ end
171
+ end
172
+
173
+ def test_setting_time_zone_aware_attribute_to_utc
174
+ in_time_zone "Pacific Time (US & Canada)" do
175
+ utc_time = Time.utc(2008, 1, 1)
176
+ record = @target.new
177
+ record.written_on = utc_time
178
+ assert_equal utc_time, record.written_on
179
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
180
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
181
+ end
182
+ end
183
+
184
+ def test_setting_time_zone_aware_attribute_in_other_time_zone
185
+ utc_time = Time.utc(2008, 1, 1)
186
+ cst_time = utc_time.in_time_zone("Central Time (US & Canada)")
187
+ in_time_zone "Pacific Time (US & Canada)" do
188
+ record = @target.new
189
+ record.written_on = cst_time
190
+ assert_equal utc_time, record.written_on
191
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
192
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
193
+ end
194
+ end
195
+
196
+ def test_setting_time_zone_aware_attribute_with_string
197
+ utc_time = Time.utc(2008, 1, 1)
198
+ (-11..13).each do |timezone_offset|
199
+ time_string = utc_time.in_time_zone(timezone_offset).to_s
200
+ in_time_zone "Pacific Time (US & Canada)" do
201
+ record = @target.new
202
+ record.written_on = time_string
203
+ assert_equal Time.zone.parse(time_string), record.written_on
204
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
205
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
206
+ end
207
+ end
208
+ end
209
+
210
+ def test_setting_time_zone_aware_attribute_to_blank_string_returns_nil
211
+ in_time_zone "Pacific Time (US & Canada)" do
212
+ record = @target.new
213
+ record.written_on = ' '
214
+ assert_nil record.written_on
215
+ end
216
+ end
217
+
218
+ def test_setting_time_zone_aware_attribute_interprets_time_zone_unaware_string_in_time_zone
219
+ time_string = 'Tue Jan 01 00:00:00 2008'
220
+ (-11..13).each do |timezone_offset|
221
+ in_time_zone timezone_offset do
222
+ record = @target.new
223
+ record.written_on = time_string
224
+ assert_equal Time.zone.parse(time_string), record.written_on
225
+ assert_equal ActiveSupport::TimeZone[timezone_offset], record.written_on.time_zone
226
+ assert_equal Time.utc(2008, 1, 1), record.written_on.time
227
+ end
228
+ end
229
+ end
230
+
231
+ def test_setting_time_zone_aware_attribute_in_current_time_zone
232
+ utc_time = Time.utc(2008, 1, 1)
233
+ in_time_zone "Pacific Time (US & Canada)" do
234
+ record = @target.new
235
+ record.written_on = utc_time.in_time_zone
236
+ assert_equal utc_time, record.written_on
237
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
238
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
239
+ end
240
+ end
241
+
242
+ def test_setting_time_zone_conversion_for_attributes_should_write_value_on_class_variable
243
+ Topic.skip_time_zone_conversion_for_attributes = [:field_a]
244
+ Minimalistic.skip_time_zone_conversion_for_attributes = [:field_b]
245
+
246
+ assert_equal [:field_a], Topic.skip_time_zone_conversion_for_attributes
247
+ assert_equal [:field_b], Minimalistic.skip_time_zone_conversion_for_attributes
248
+ end
249
+
250
+ def test_read_attributes_respect_access_control
251
+ privatize("title")
252
+
253
+ topic = @target.new(:title => "The pros and cons of programming naked.")
254
+ assert !topic.respond_to?(:title)
255
+ exception = assert_raise(NoMethodError) { topic.title }
256
+ assert_equal "Attempt to call private method", exception.message
257
+ assert_equal "I'm private", topic.send(:title)
258
+ end
259
+
260
+ def test_write_attributes_respect_access_control
261
+ privatize("title=(value)")
262
+
263
+ topic = @target.new
264
+ assert !topic.respond_to?(:title=)
265
+ exception = assert_raise(NoMethodError) { topic.title = "Pants"}
266
+ assert_equal "Attempt to call private method", exception.message
267
+ topic.send(:title=, "Very large pants")
268
+ end
269
+
270
+ def test_question_attributes_respect_access_control
271
+ privatize("title?")
272
+
273
+ topic = @target.new(:title => "Isaac Newton's pants")
274
+ assert !topic.respond_to?(:title?)
275
+ exception = assert_raise(NoMethodError) { topic.title? }
276
+ assert_equal "Attempt to call private method", exception.message
277
+ assert topic.send(:title?)
278
+ end
279
+
280
+ def test_bulk_update_respects_access_control
281
+ privatize("title=(value)")
282
+
283
+ assert_raise(ActiveRecord::UnknownAttributeError) { topic = @target.new(:title => "Rants about pants") }
284
+ assert_raise(ActiveRecord::UnknownAttributeError) { @target.new.attributes = { :title => "Ants in pants" } }
285
+ end
286
+
287
+ private
288
+ def time_related_columns_on_topic
289
+ Topic.columns.select{|c| [:time, :date, :datetime, :timestamp].include?(c.type)}.map(&:name)
290
+ end
291
+
292
+ def in_time_zone(zone)
293
+ old_zone = Time.zone
294
+ old_tz = ActiveRecord::Base.time_zone_aware_attributes
295
+
296
+ Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil
297
+ ActiveRecord::Base.time_zone_aware_attributes = !zone.nil?
298
+ yield
299
+ ensure
300
+ Time.zone = old_zone
301
+ ActiveRecord::Base.time_zone_aware_attributes = old_tz
302
+ end
303
+
304
+ def privatize(method_signature)
305
+ @target.class_eval <<-private_method
306
+ private
307
+ def #{method_signature}
308
+ "I'm private"
309
+ end
310
+ private_method
311
+ end
312
+ end
@@ -0,0 +1,2114 @@
1
+ require "cases/helper"
2
+ require 'models/author'
3
+ require 'models/topic'
4
+ require 'models/reply'
5
+ require 'models/category'
6
+ require 'models/company'
7
+ require 'models/customer'
8
+ require 'models/developer'
9
+ require 'models/project'
10
+ require 'models/default'
11
+ require 'models/auto_id'
12
+ require 'models/column_name'
13
+ require 'models/subscriber'
14
+ require 'models/keyboard'
15
+ require 'models/post'
16
+ require 'models/comment'
17
+ require 'models/minimalistic'
18
+ require 'models/warehouse_thing'
19
+ require 'models/parrot'
20
+ require 'rexml/document'
21
+
22
+ class Category < ActiveRecord::Base; end
23
+ class Categorization < ActiveRecord::Base; end
24
+ class Smarts < ActiveRecord::Base; end
25
+ class CreditCard < ActiveRecord::Base
26
+ class PinNumber < ActiveRecord::Base
27
+ class CvvCode < ActiveRecord::Base; end
28
+ class SubCvvCode < CvvCode; end
29
+ end
30
+ class SubPinNumber < PinNumber; end
31
+ class Brand < Category; end
32
+ end
33
+ class MasterCreditCard < ActiveRecord::Base; end
34
+ class Post < ActiveRecord::Base; end
35
+ class Computer < ActiveRecord::Base; end
36
+ class NonExistentTable < ActiveRecord::Base; end
37
+ class TestOracleDefault < ActiveRecord::Base; end
38
+
39
+ class LoosePerson < ActiveRecord::Base
40
+ self.table_name = 'people'
41
+ self.abstract_class = true
42
+ attr_protected :credit_rating, :administrator
43
+ end
44
+
45
+ class LooseDescendant < LoosePerson
46
+ attr_protected :phone_number
47
+ end
48
+
49
+ class LooseDescendantSecond< LoosePerson
50
+ attr_protected :phone_number
51
+ attr_protected :name
52
+ end
53
+
54
+ class TightPerson < ActiveRecord::Base
55
+ self.table_name = 'people'
56
+ attr_accessible :name, :address
57
+ end
58
+
59
+ class TightDescendant < TightPerson
60
+ attr_accessible :phone_number
61
+ end
62
+
63
+ class ReadonlyTitlePost < Post
64
+ attr_readonly :title
65
+ end
66
+
67
+ class Booleantest < ActiveRecord::Base; end
68
+
69
+ class Task < ActiveRecord::Base
70
+ attr_protected :starting
71
+ end
72
+
73
+ class TopicWithProtectedContentAndAccessibleAuthorName < ActiveRecord::Base
74
+ self.table_name = 'topics'
75
+ attr_accessible :author_name
76
+ attr_protected :content
77
+ end
78
+
79
+ class BasicsTest < ActiveRecord::TestCase
80
+ fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, :warehouse_things, :authors, :categorizations, :categories, :posts
81
+
82
+ def test_table_exists
83
+ assert !NonExistentTable.table_exists?
84
+ assert Topic.table_exists?
85
+ end
86
+
87
+ def test_set_attributes
88
+ topic = Topic.find(1)
89
+ topic.attributes = { "title" => "Budget", "author_name" => "Jason" }
90
+ topic.save
91
+ assert_equal("Budget", topic.title)
92
+ assert_equal("Jason", topic.author_name)
93
+ assert_equal(topics(:first).author_email_address, Topic.find(1).author_email_address)
94
+ end
95
+
96
+ def test_integers_as_nil
97
+ test = AutoId.create('value' => '')
98
+ assert_nil AutoId.find(test.id).value
99
+ end
100
+
101
+ def test_set_attributes_with_block
102
+ topic = Topic.new do |t|
103
+ t.title = "Budget"
104
+ t.author_name = "Jason"
105
+ end
106
+
107
+ assert_equal("Budget", topic.title)
108
+ assert_equal("Jason", topic.author_name)
109
+ end
110
+
111
+ def test_respond_to?
112
+ topic = Topic.find(1)
113
+ assert topic.respond_to?("title")
114
+ assert topic.respond_to?("title?")
115
+ assert topic.respond_to?("title=")
116
+ assert topic.respond_to?(:title)
117
+ assert topic.respond_to?(:title?)
118
+ assert topic.respond_to?(:title=)
119
+ assert topic.respond_to?("author_name")
120
+ assert topic.respond_to?("attribute_names")
121
+ assert !topic.respond_to?("nothingness")
122
+ assert !topic.respond_to?(:nothingness)
123
+ end
124
+
125
+ def test_array_content
126
+ topic = Topic.new
127
+ topic.content = %w( one two three )
128
+ topic.save
129
+
130
+ assert_equal(%w( one two three ), Topic.find(topic.id).content)
131
+ end
132
+
133
+ def test_read_attributes_before_type_cast
134
+ category = Category.new({:name=>"Test categoty", :type => nil})
135
+ category_attrs = {"name"=>"Test categoty", "type" => nil, "categorizations_count" => nil}
136
+ assert_equal category_attrs , category.attributes_before_type_cast
137
+ end
138
+
139
+ if current_adapter?(:MysqlAdapter)
140
+ def test_read_attributes_before_type_cast_on_boolean
141
+ bool = Booleantest.create({ "value" => false })
142
+ assert_equal "0", bool.reload.attributes_before_type_cast["value"]
143
+ end
144
+ end
145
+
146
+ def test_read_attributes_before_type_cast_on_datetime
147
+ developer = Developer.find(:first)
148
+ assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"]
149
+ end
150
+
151
+ def test_hash_content
152
+ topic = Topic.new
153
+ topic.content = { "one" => 1, "two" => 2 }
154
+ topic.save
155
+
156
+ assert_equal 2, Topic.find(topic.id).content["two"]
157
+
158
+ topic.content_will_change!
159
+ topic.content["three"] = 3
160
+ topic.save
161
+
162
+ assert_equal 3, Topic.find(topic.id).content["three"]
163
+ end
164
+
165
+ def test_update_array_content
166
+ topic = Topic.new
167
+ topic.content = %w( one two three )
168
+
169
+ topic.content.push "four"
170
+ assert_equal(%w( one two three four ), topic.content)
171
+
172
+ topic.save
173
+
174
+ topic = Topic.find(topic.id)
175
+ topic.content << "five"
176
+ assert_equal(%w( one two three four five ), topic.content)
177
+ end
178
+
179
+ def test_case_sensitive_attributes_hash
180
+ # DB2 is not case-sensitive
181
+ return true if current_adapter?(:DB2Adapter, :IBM_DBAdapter)
182
+
183
+ assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.find(:first).attributes
184
+ end
185
+
186
+ def test_create
187
+ topic = Topic.new
188
+ topic.title = "New Topic"
189
+ topic.save
190
+ topic_reloaded = Topic.find(topic.id)
191
+ assert_equal("New Topic", topic_reloaded.title)
192
+ end
193
+
194
+ def test_save!
195
+ topic = Topic.new(:title => "New Topic")
196
+ assert topic.save!
197
+
198
+ reply = Reply.new
199
+ assert_raise(ActiveRecord::RecordInvalid) { reply.save! }
200
+ end
201
+
202
+ def test_save_null_string_attributes
203
+ topic = Topic.find(1)
204
+ topic.attributes = { "title" => "null", "author_name" => "null" }
205
+ topic.save!
206
+ topic.reload
207
+ assert_equal("null", topic.title)
208
+ assert_equal("null", topic.author_name)
209
+ end
210
+
211
+ def test_save_nil_string_attributes
212
+ topic = Topic.find(1)
213
+ topic.title = nil
214
+ topic.save!
215
+ topic.reload
216
+ assert_nil topic.title
217
+ end
218
+
219
+ def test_save_for_record_with_only_primary_key
220
+ minimalistic = Minimalistic.new
221
+ assert_nothing_raised { minimalistic.save }
222
+ end
223
+
224
+ def test_save_for_record_with_only_primary_key_that_is_provided
225
+ assert_nothing_raised { Minimalistic.create!(:id => 2) }
226
+ end
227
+
228
+ def test_hashes_not_mangled
229
+ new_topic = { :title => "New Topic" }
230
+ new_topic_values = { :title => "AnotherTopic" }
231
+
232
+ topic = Topic.new(new_topic)
233
+ assert_equal new_topic[:title], topic.title
234
+
235
+ topic.attributes= new_topic_values
236
+ assert_equal new_topic_values[:title], topic.title
237
+ end
238
+
239
+ def test_create_many
240
+ topics = Topic.create([ { "title" => "first" }, { "title" => "second" }])
241
+ assert_equal 2, topics.size
242
+ assert_equal "first", topics.first.title
243
+ end
244
+
245
+ def test_create_columns_not_equal_attributes
246
+ topic = Topic.new
247
+ topic.title = 'Another New Topic'
248
+ topic.send :write_attribute, 'does_not_exist', 'test'
249
+ assert_nothing_raised { topic.save }
250
+ end
251
+
252
+ def test_create_through_factory
253
+ topic = Topic.create("title" => "New Topic")
254
+ topicReloaded = Topic.find(topic.id)
255
+ assert_equal(topic, topicReloaded)
256
+ end
257
+
258
+ def test_create_through_factory_with_block
259
+ topic = Topic.create("title" => "New Topic") do |t|
260
+ t.author_name = "David"
261
+ end
262
+ topicReloaded = Topic.find(topic.id)
263
+ assert_equal("New Topic", topic.title)
264
+ assert_equal("David", topic.author_name)
265
+ end
266
+
267
+ def test_create_many_through_factory_with_block
268
+ topics = Topic.create([ { "title" => "first" }, { "title" => "second" }]) do |t|
269
+ t.author_name = "David"
270
+ end
271
+ assert_equal 2, topics.size
272
+ topic1, topic2 = Topic.find(topics[0].id), Topic.find(topics[1].id)
273
+ assert_equal "first", topic1.title
274
+ assert_equal "David", topic1.author_name
275
+ assert_equal "second", topic2.title
276
+ assert_equal "David", topic2.author_name
277
+ end
278
+
279
+ def test_update
280
+ topic = Topic.new
281
+ topic.title = "Another New Topic"
282
+ topic.written_on = "2003-12-12 23:23:00"
283
+ topic.save
284
+ topicReloaded = Topic.find(topic.id)
285
+ assert_equal("Another New Topic", topicReloaded.title)
286
+
287
+ topicReloaded.title = "Updated topic"
288
+ topicReloaded.save
289
+
290
+ topicReloadedAgain = Topic.find(topic.id)
291
+
292
+ assert_equal("Updated topic", topicReloadedAgain.title)
293
+ end
294
+
295
+ def test_update_columns_not_equal_attributes
296
+ topic = Topic.new
297
+ topic.title = "Still another topic"
298
+ topic.save
299
+
300
+ topicReloaded = Topic.find(topic.id)
301
+ topicReloaded.title = "A New Topic"
302
+ topicReloaded.send :write_attribute, 'does_not_exist', 'test'
303
+ assert_nothing_raised { topicReloaded.save }
304
+ end
305
+
306
+ def test_update_for_record_with_only_primary_key
307
+ minimalistic = minimalistics(:first)
308
+ assert_nothing_raised { minimalistic.save }
309
+ end
310
+
311
+ def test_write_attribute
312
+ topic = Topic.new
313
+ topic.send(:write_attribute, :title, "Still another topic")
314
+ assert_equal "Still another topic", topic.title
315
+
316
+ topic.send(:write_attribute, "title", "Still another topic: part 2")
317
+ assert_equal "Still another topic: part 2", topic.title
318
+ end
319
+
320
+ def test_read_attribute
321
+ topic = Topic.new
322
+ topic.title = "Don't change the topic"
323
+ assert_equal "Don't change the topic", topic.send(:read_attribute, "title")
324
+ assert_equal "Don't change the topic", topic["title"]
325
+
326
+ assert_equal "Don't change the topic", topic.send(:read_attribute, :title)
327
+ assert_equal "Don't change the topic", topic[:title]
328
+ end
329
+
330
+ def test_read_attribute_when_false
331
+ topic = topics(:first)
332
+ topic.approved = false
333
+ assert !topic.approved?, "approved should be false"
334
+ topic.approved = "false"
335
+ assert !topic.approved?, "approved should be false"
336
+ end
337
+
338
+ def test_read_attribute_when_true
339
+ topic = topics(:first)
340
+ topic.approved = true
341
+ assert topic.approved?, "approved should be true"
342
+ topic.approved = "true"
343
+ assert topic.approved?, "approved should be true"
344
+ end
345
+
346
+ def test_read_write_boolean_attribute
347
+ topic = Topic.new
348
+ # puts ""
349
+ # puts "New Topic"
350
+ # puts topic.inspect
351
+ topic.approved = "false"
352
+ # puts "Expecting false"
353
+ # puts topic.inspect
354
+ assert !topic.approved?, "approved should be false"
355
+ topic.approved = "false"
356
+ # puts "Expecting false"
357
+ # puts topic.inspect
358
+ assert !topic.approved?, "approved should be false"
359
+ topic.approved = "true"
360
+ # puts "Expecting true"
361
+ # puts topic.inspect
362
+ assert topic.approved?, "approved should be true"
363
+ topic.approved = "true"
364
+ # puts "Expecting true"
365
+ # puts topic.inspect
366
+ assert topic.approved?, "approved should be true"
367
+ # puts ""
368
+ end
369
+
370
+ def test_query_attribute_string
371
+ [nil, "", " "].each do |value|
372
+ assert_equal false, Topic.new(:author_name => value).author_name?
373
+ end
374
+
375
+ assert_equal true, Topic.new(:author_name => "Name").author_name?
376
+ end
377
+
378
+ def test_query_attribute_number
379
+ [nil, 0, "0"].each do |value|
380
+ assert_equal false, Developer.new(:salary => value).salary?
381
+ end
382
+
383
+ assert_equal true, Developer.new(:salary => 1).salary?
384
+ assert_equal true, Developer.new(:salary => "1").salary?
385
+ end
386
+
387
+ def test_query_attribute_boolean
388
+ [nil, "", false, "false", "f", 0].each do |value|
389
+ assert_equal false, Topic.new(:approved => value).approved?
390
+ end
391
+
392
+ [true, "true", "1", 1].each do |value|
393
+ assert_equal true, Topic.new(:approved => value).approved?
394
+ end
395
+ end
396
+
397
+ def test_query_attribute_with_custom_fields
398
+ object = Company.find_by_sql(<<-SQL).first
399
+ SELECT c1.*, c2.ruby_type as string_value, c2.rating as int_value
400
+ FROM companies c1, companies c2
401
+ WHERE c1.firm_id = c2.id
402
+ AND c1.id = 2
403
+ SQL
404
+
405
+ assert_equal "Firm", object.string_value
406
+ assert object.string_value?
407
+
408
+ object.string_value = " "
409
+ assert !object.string_value?
410
+
411
+ assert_equal 1, object.int_value.to_i
412
+ assert object.int_value?
413
+
414
+ object.int_value = "0"
415
+ assert !object.int_value?
416
+ end
417
+
418
+
419
+ def test_reader_for_invalid_column_names
420
+ Topic.send(:define_read_method, "mumub-jumbo".to_sym, "mumub-jumbo", nil)
421
+ assert !Topic.generated_methods.include?("mumub-jumbo")
422
+ end
423
+
424
+ def test_non_attribute_access_and_assignment
425
+ topic = Topic.new
426
+ assert !topic.respond_to?("mumbo")
427
+ assert_raise(NoMethodError) { topic.mumbo }
428
+ assert_raise(NoMethodError) { topic.mumbo = 5 }
429
+ end
430
+
431
+ def test_preserving_date_objects
432
+ if current_adapter?(:SybaseAdapter, :OracleAdapter)
433
+ # Sybase ctlib does not (yet?) support the date type; use datetime instead.
434
+ # Oracle treats all dates/times as Time.
435
+ assert_kind_of(
436
+ Time, Topic.find(1).last_read,
437
+ "The last_read attribute should be of the Time class"
438
+ )
439
+ else
440
+ assert_kind_of(
441
+ Date, Topic.find(1).last_read,
442
+ "The last_read attribute should be of the Date class"
443
+ )
444
+ end
445
+ end
446
+
447
+ def test_preserving_time_objects
448
+ assert_kind_of(
449
+ Time, Topic.find(1).bonus_time,
450
+ "The bonus_time attribute should be of the Time class"
451
+ )
452
+
453
+ assert_kind_of(
454
+ Time, Topic.find(1).written_on,
455
+ "The written_on attribute should be of the Time class"
456
+ )
457
+
458
+ # For adapters which support microsecond resolution.
459
+ if current_adapter?(:PostgreSQLAdapter)
460
+ assert_equal 11, Topic.find(1).written_on.sec
461
+ assert_equal 223300, Topic.find(1).written_on.usec
462
+ assert_equal 9900, Topic.find(2).written_on.usec
463
+ end
464
+ end
465
+
466
+ def test_custom_mutator
467
+ topic = Topic.find(1)
468
+ # This mutator is protected in the class definition
469
+ topic.send(:approved=, true)
470
+ assert topic.instance_variable_get("@custom_approved")
471
+ end
472
+
473
+ def test_delete
474
+ topic = Topic.find(1)
475
+ assert_equal topic, topic.delete, 'topic.delete did not return self'
476
+ assert topic.frozen?, 'topic not frozen after delete'
477
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
478
+ end
479
+
480
+ def test_delete_doesnt_run_callbacks
481
+ Topic.find(1).delete
482
+ assert_not_nil Topic.find(2)
483
+ end
484
+
485
+ def test_destroy
486
+ topic = Topic.find(1)
487
+ assert_equal topic, topic.destroy, 'topic.destroy did not return self'
488
+ assert topic.frozen?, 'topic not frozen after destroy'
489
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
490
+ end
491
+
492
+ def test_record_not_found_exception
493
+ assert_raise(ActiveRecord::RecordNotFound) { topicReloaded = Topic.find(99999) }
494
+ end
495
+
496
+ def test_initialize_with_attributes
497
+ topic = Topic.new({
498
+ "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23"
499
+ })
500
+
501
+ assert_equal("initialized from attributes", topic.title)
502
+ end
503
+
504
+ def test_initialize_with_invalid_attribute
505
+ begin
506
+ topic = Topic.new({ "title" => "test",
507
+ "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"})
508
+ rescue ActiveRecord::MultiparameterAssignmentErrors => ex
509
+ assert_equal(1, ex.errors.size)
510
+ assert_equal("last_read", ex.errors[0].attribute)
511
+ end
512
+ end
513
+
514
+ def test_load
515
+ topics = Topic.find(:all, :order => 'id')
516
+ assert_equal(4, topics.size)
517
+ assert_equal(topics(:first).title, topics.first.title)
518
+ end
519
+
520
+ def test_load_with_condition
521
+ topics = Topic.find(:all, :conditions => "author_name = 'Mary'")
522
+
523
+ assert_equal(1, topics.size)
524
+ assert_equal(topics(:second).title, topics.first.title)
525
+ end
526
+
527
+ def test_table_name_guesses
528
+ classes = [Category, Smarts, CreditCard, CreditCard::PinNumber, CreditCard::PinNumber::CvvCode, CreditCard::SubPinNumber, CreditCard::Brand, MasterCreditCard]
529
+
530
+ assert_equal "topics", Topic.table_name
531
+
532
+ assert_equal "categories", Category.table_name
533
+ assert_equal "smarts", Smarts.table_name
534
+ assert_equal "credit_cards", CreditCard.table_name
535
+ assert_equal "credit_card_pin_numbers", CreditCard::PinNumber.table_name
536
+ assert_equal "credit_card_pin_number_cvv_codes", CreditCard::PinNumber::CvvCode.table_name
537
+ assert_equal "credit_card_pin_numbers", CreditCard::SubPinNumber.table_name
538
+ assert_equal "categories", CreditCard::Brand.table_name
539
+ assert_equal "master_credit_cards", MasterCreditCard.table_name
540
+
541
+ ActiveRecord::Base.pluralize_table_names = false
542
+ classes.each(&:reset_table_name)
543
+
544
+ assert_equal "category", Category.table_name
545
+ assert_equal "smarts", Smarts.table_name
546
+ assert_equal "credit_card", CreditCard.table_name
547
+ assert_equal "credit_card_pin_number", CreditCard::PinNumber.table_name
548
+ assert_equal "credit_card_pin_number_cvv_code", CreditCard::PinNumber::CvvCode.table_name
549
+ assert_equal "credit_card_pin_number", CreditCard::SubPinNumber.table_name
550
+ assert_equal "category", CreditCard::Brand.table_name
551
+ assert_equal "master_credit_card", MasterCreditCard.table_name
552
+
553
+ ActiveRecord::Base.pluralize_table_names = true
554
+ classes.each(&:reset_table_name)
555
+
556
+ ActiveRecord::Base.table_name_prefix = "test_"
557
+ Category.reset_table_name
558
+ assert_equal "test_categories", Category.table_name
559
+ ActiveRecord::Base.table_name_suffix = "_test"
560
+ Category.reset_table_name
561
+ assert_equal "test_categories_test", Category.table_name
562
+ ActiveRecord::Base.table_name_prefix = ""
563
+ Category.reset_table_name
564
+ assert_equal "categories_test", Category.table_name
565
+ ActiveRecord::Base.table_name_suffix = ""
566
+ Category.reset_table_name
567
+ assert_equal "categories", Category.table_name
568
+
569
+ ActiveRecord::Base.pluralize_table_names = false
570
+ ActiveRecord::Base.table_name_prefix = "test_"
571
+ Category.reset_table_name
572
+ assert_equal "test_category", Category.table_name
573
+ ActiveRecord::Base.table_name_suffix = "_test"
574
+ Category.reset_table_name
575
+ assert_equal "test_category_test", Category.table_name
576
+ ActiveRecord::Base.table_name_prefix = ""
577
+ Category.reset_table_name
578
+ assert_equal "category_test", Category.table_name
579
+ ActiveRecord::Base.table_name_suffix = ""
580
+ Category.reset_table_name
581
+ assert_equal "category", Category.table_name
582
+
583
+ ActiveRecord::Base.pluralize_table_names = true
584
+ classes.each(&:reset_table_name)
585
+ end
586
+
587
+ def test_destroy_all
588
+ original_count = Topic.count
589
+ topics_by_mary = Topic.count(:conditions => mary = "author_name = 'Mary'")
590
+
591
+ Topic.destroy_all mary
592
+ assert_equal original_count - topics_by_mary, Topic.count
593
+ end
594
+
595
+ def test_destroy_many
596
+ assert_equal 3, Client.count
597
+ Client.destroy([2, 3])
598
+ assert_equal 1, Client.count
599
+ end
600
+
601
+ def test_delete_many
602
+ original_count = Topic.count
603
+ Topic.delete(deleting = [1, 2])
604
+ assert_equal original_count - deleting.size, Topic.count
605
+ end
606
+
607
+ def test_boolean_attributes
608
+ assert ! Topic.find(1).approved?
609
+ assert Topic.find(2).approved?
610
+ end
611
+
612
+ def test_increment_counter
613
+ Topic.increment_counter("replies_count", 1)
614
+ assert_equal 2, Topic.find(1).replies_count
615
+
616
+ Topic.increment_counter("replies_count", 1)
617
+ assert_equal 3, Topic.find(1).replies_count
618
+ end
619
+
620
+ def test_decrement_counter
621
+ Topic.decrement_counter("replies_count", 2)
622
+ assert_equal -1, Topic.find(2).replies_count
623
+
624
+ Topic.decrement_counter("replies_count", 2)
625
+ assert_equal -2, Topic.find(2).replies_count
626
+ end
627
+
628
+ def test_update_counter
629
+ category = categories(:general)
630
+ assert_nil category.categorizations_count
631
+ assert_equal 2, category.categorizations.count
632
+
633
+ Category.update_counters(category.id, "categorizations_count" => category.categorizations.count)
634
+ category.reload
635
+ assert_not_nil category.categorizations_count
636
+ assert_equal 2, category.categorizations_count
637
+
638
+ Category.update_counters(category.id, "categorizations_count" => category.categorizations.count)
639
+ category.reload
640
+ assert_not_nil category.categorizations_count
641
+ assert_equal 4, category.categorizations_count
642
+
643
+ category_2 = categories(:technology)
644
+ count_1, count_2 = (category.categorizations_count || 0), (category_2.categorizations_count || 0)
645
+ Category.update_counters([category.id, category_2.id], "categorizations_count" => 2)
646
+ category.reload; category_2.reload
647
+ assert_equal count_1 + 2, category.categorizations_count
648
+ assert_equal count_2 + 2, category_2.categorizations_count
649
+ end
650
+
651
+ def test_update_all
652
+ assert_equal Topic.count, Topic.update_all("content = 'bulk updated!'")
653
+ assert_equal "bulk updated!", Topic.find(1).content
654
+ assert_equal "bulk updated!", Topic.find(2).content
655
+
656
+ assert_equal Topic.count, Topic.update_all(['content = ?', 'bulk updated again!'])
657
+ assert_equal "bulk updated again!", Topic.find(1).content
658
+ assert_equal "bulk updated again!", Topic.find(2).content
659
+
660
+ assert_equal Topic.count, Topic.update_all(['content = ?', nil])
661
+ assert_nil Topic.find(1).content
662
+ end
663
+
664
+ def test_update_all_with_hash
665
+ assert_not_nil Topic.find(1).last_read
666
+ assert_equal Topic.count, Topic.update_all(:content => 'bulk updated with hash!', :last_read => nil)
667
+ assert_equal "bulk updated with hash!", Topic.find(1).content
668
+ assert_equal "bulk updated with hash!", Topic.find(2).content
669
+ assert_nil Topic.find(1).last_read
670
+ assert_nil Topic.find(2).last_read
671
+ end
672
+
673
+ def test_update_all_with_non_standard_table_name
674
+ assert_equal 1, WarehouseThing.update_all(['value = ?', 0], ['id = ?', 1])
675
+ assert_equal 0, WarehouseThing.find(1).value
676
+ end
677
+
678
+ if current_adapter?(:MysqlAdapter)
679
+ def test_update_all_with_order_and_limit
680
+ assert_equal 1, Topic.update_all("content = 'bulk updated!'", nil, :limit => 1, :order => 'id DESC')
681
+ end
682
+ end
683
+
684
+ def test_update_all_ignores_order_without_limit_from_association
685
+ author = authors(:david)
686
+ assert_nothing_raised do
687
+ assert_equal author.posts_with_comments_and_categories.length, author.posts_with_comments_and_categories.update_all([ "body = ?", "bulk update!" ])
688
+ end
689
+ end
690
+
691
+ def test_update_all_with_order_and_limit_updates_subset_only
692
+ author = authors(:david)
693
+ assert_nothing_raised do
694
+ assert_equal 1, author.posts_sorted_by_id_limited.size
695
+ assert_equal 2, author.posts_sorted_by_id_limited.find(:all, :limit => 2).size
696
+ assert_equal 1, author.posts_sorted_by_id_limited.update_all([ "body = ?", "bulk update!" ])
697
+ assert_equal "bulk update!", posts(:welcome).body
698
+ assert_not_equal "bulk update!", posts(:thinking).body
699
+ end
700
+ end
701
+
702
+ def test_update_many
703
+ topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
704
+ updated = Topic.update(topic_data.keys, topic_data.values)
705
+
706
+ assert_equal 2, updated.size
707
+ assert_equal "1 updated", Topic.find(1).content
708
+ assert_equal "2 updated", Topic.find(2).content
709
+ end
710
+
711
+ def test_delete_all
712
+ assert Topic.count > 0
713
+
714
+ assert_equal Topic.count, Topic.delete_all
715
+ end
716
+
717
+ def test_update_by_condition
718
+ Topic.update_all "content = 'bulk updated!'", ["approved = ?", true]
719
+ assert_equal "Have a nice day", Topic.find(1).content
720
+ assert_equal "bulk updated!", Topic.find(2).content
721
+ end
722
+
723
+ def test_attribute_present
724
+ t = Topic.new
725
+ t.title = "hello there!"
726
+ t.written_on = Time.now
727
+ assert t.attribute_present?("title")
728
+ assert t.attribute_present?("written_on")
729
+ assert !t.attribute_present?("content")
730
+ end
731
+
732
+ def test_attribute_keys_on_new_instance
733
+ t = Topic.new
734
+ assert_equal nil, t.title, "The topics table has a title column, so it should be nil"
735
+ assert_raise(NoMethodError) { t.title2 }
736
+ end
737
+
738
+ def test_class_name
739
+ assert_equal "Firm", ActiveRecord::Base.class_name("firms")
740
+ assert_equal "Category", ActiveRecord::Base.class_name("categories")
741
+ assert_equal "AccountHolder", ActiveRecord::Base.class_name("account_holder")
742
+
743
+ ActiveRecord::Base.pluralize_table_names = false
744
+ assert_equal "Firms", ActiveRecord::Base.class_name( "firms" )
745
+ ActiveRecord::Base.pluralize_table_names = true
746
+
747
+ ActiveRecord::Base.table_name_prefix = "test_"
748
+ assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms" )
749
+ ActiveRecord::Base.table_name_suffix = "_tests"
750
+ assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms_tests" )
751
+ ActiveRecord::Base.table_name_prefix = ""
752
+ assert_equal "Firm", ActiveRecord::Base.class_name( "firms_tests" )
753
+ ActiveRecord::Base.table_name_suffix = ""
754
+ assert_equal "Firm", ActiveRecord::Base.class_name( "firms" )
755
+ end
756
+
757
+ def test_null_fields
758
+ assert_nil Topic.find(1).parent_id
759
+ assert_nil Topic.create("title" => "Hey you").parent_id
760
+ end
761
+
762
+ def test_default_values
763
+ topic = Topic.new
764
+ assert topic.approved?
765
+ assert_nil topic.written_on
766
+ assert_nil topic.bonus_time
767
+ assert_nil topic.last_read
768
+
769
+ topic.save
770
+
771
+ topic = Topic.find(topic.id)
772
+ assert topic.approved?
773
+ assert_nil topic.last_read
774
+
775
+ # Oracle has some funky default handling, so it requires a bit of
776
+ # extra testing. See ticket #2788.
777
+ if current_adapter?(:OracleAdapter)
778
+ test = TestOracleDefault.new
779
+ assert_equal "X", test.test_char
780
+ assert_equal "hello", test.test_string
781
+ assert_equal 3, test.test_int
782
+ end
783
+ end
784
+
785
+ # Oracle, and Sybase do not have a TIME datatype.
786
+ unless current_adapter?(:OracleAdapter, :SybaseAdapter)
787
+ def test_utc_as_time_zone
788
+ Topic.default_timezone = :utc
789
+ attributes = { "bonus_time" => "5:42:00AM" }
790
+ topic = Topic.find(1)
791
+ topic.attributes = attributes
792
+ assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time
793
+ Topic.default_timezone = :local
794
+ end
795
+
796
+ def test_utc_as_time_zone_and_new
797
+ Topic.default_timezone = :utc
798
+ attributes = { "bonus_time(1i)"=>"2000",
799
+ "bonus_time(2i)"=>"1",
800
+ "bonus_time(3i)"=>"1",
801
+ "bonus_time(4i)"=>"10",
802
+ "bonus_time(5i)"=>"35",
803
+ "bonus_time(6i)"=>"50" }
804
+ topic = Topic.new(attributes)
805
+ assert_equal Time.utc(2000, 1, 1, 10, 35, 50), topic.bonus_time
806
+ Topic.default_timezone = :local
807
+ end
808
+ end
809
+
810
+ def test_default_values_on_empty_strings
811
+ topic = Topic.new
812
+ topic.approved = nil
813
+ topic.last_read = nil
814
+
815
+ topic.save
816
+
817
+ topic = Topic.find(topic.id)
818
+ assert_nil topic.last_read
819
+
820
+ # Sybase adapter does not allow nulls in boolean columns
821
+ if current_adapter?(:SybaseAdapter)
822
+ assert topic.approved == false
823
+ else
824
+ assert_nil topic.approved
825
+ end
826
+ end
827
+
828
+ def test_equality
829
+ assert_equal Topic.find(1), Topic.find(2).topic
830
+ end
831
+
832
+ def test_equality_of_new_records
833
+ assert_not_equal Topic.new, Topic.new
834
+ end
835
+
836
+ def test_hashing
837
+ assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
838
+ end
839
+
840
+ def test_delete_new_record
841
+ client = Client.new
842
+ client.delete
843
+ assert client.frozen?
844
+ end
845
+
846
+ def test_delete_record_with_associations
847
+ client = Client.find(3)
848
+ client.delete
849
+ assert client.frozen?
850
+ assert_kind_of Firm, client.firm
851
+ assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" }
852
+ end
853
+
854
+ def test_destroy_new_record
855
+ client = Client.new
856
+ client.destroy
857
+ assert client.frozen?
858
+ end
859
+
860
+ def test_destroy_record_with_associations
861
+ client = Client.find(3)
862
+ client.destroy
863
+ assert client.frozen?
864
+ assert_kind_of Firm, client.firm
865
+ assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" }
866
+ end
867
+
868
+ def test_update_attribute
869
+ assert !Topic.find(1).approved?
870
+ Topic.find(1).update_attribute("approved", true)
871
+ assert Topic.find(1).approved?
872
+
873
+ Topic.find(1).update_attribute(:approved, false)
874
+ assert !Topic.find(1).approved?
875
+ end
876
+
877
+ def test_update_attributes
878
+ topic = Topic.find(1)
879
+ assert !topic.approved?
880
+ assert_equal "The First Topic", topic.title
881
+
882
+ topic.update_attributes("approved" => true, "title" => "The First Topic Updated")
883
+ topic.reload
884
+ assert topic.approved?
885
+ assert_equal "The First Topic Updated", topic.title
886
+
887
+ topic.update_attributes(:approved => false, :title => "The First Topic")
888
+ topic.reload
889
+ assert !topic.approved?
890
+ assert_equal "The First Topic", topic.title
891
+ end
892
+
893
+ def test_update_attributes!
894
+ reply = Reply.find(2)
895
+ assert_equal "The Second Topic of the day", reply.title
896
+ assert_equal "Have a nice day", reply.content
897
+
898
+ reply.update_attributes!("title" => "The Second Topic of the day updated", "content" => "Have a nice evening")
899
+ reply.reload
900
+ assert_equal "The Second Topic of the day updated", reply.title
901
+ assert_equal "Have a nice evening", reply.content
902
+
903
+ reply.update_attributes!(:title => "The Second Topic of the day", :content => "Have a nice day")
904
+ reply.reload
905
+ assert_equal "The Second Topic of the day", reply.title
906
+ assert_equal "Have a nice day", reply.content
907
+
908
+ assert_raise(ActiveRecord::RecordInvalid) { reply.update_attributes!(:title => nil, :content => "Have a nice evening") }
909
+ end
910
+
911
+ def test_mass_assignment_should_raise_exception_if_accessible_and_protected_attribute_writers_are_both_used
912
+ topic = TopicWithProtectedContentAndAccessibleAuthorName.new
913
+ assert_raise(RuntimeError) { topic.attributes = { "author_name" => "me" } }
914
+ assert_raise(RuntimeError) { topic.attributes = { "content" => "stuff" } }
915
+ end
916
+
917
+ def test_mass_assignment_protection
918
+ firm = Firm.new
919
+ firm.attributes = { "name" => "Next Angle", "rating" => 5 }
920
+ assert_equal 1, firm.rating
921
+ end
922
+
923
+ def test_mass_assignment_protection_against_class_attribute_writers
924
+ [:logger, :configurations, :primary_key_prefix_type, :table_name_prefix, :table_name_suffix, :pluralize_table_names, :colorize_logging,
925
+ :default_timezone, :schema_format, :lock_optimistically, :record_timestamps].each do |method|
926
+ assert Task.respond_to?(method)
927
+ assert Task.respond_to?("#{method}=")
928
+ assert Task.new.respond_to?(method)
929
+ assert !Task.new.respond_to?("#{method}=")
930
+ end
931
+ end
932
+
933
+ def test_customized_primary_key_remains_protected
934
+ subscriber = Subscriber.new(:nick => 'webster123', :name => 'nice try')
935
+ assert_nil subscriber.id
936
+
937
+ keyboard = Keyboard.new(:key_number => 9, :name => 'nice try')
938
+ assert_nil keyboard.id
939
+ end
940
+
941
+ def test_customized_primary_key_remains_protected_when_referred_to_as_id
942
+ subscriber = Subscriber.new(:id => 'webster123', :name => 'nice try')
943
+ assert_nil subscriber.id
944
+
945
+ keyboard = Keyboard.new(:id => 9, :name => 'nice try')
946
+ assert_nil keyboard.id
947
+ end
948
+
949
+ def test_mass_assigning_invalid_attribute
950
+ firm = Firm.new
951
+
952
+ assert_raise(ActiveRecord::UnknownAttributeError) do
953
+ firm.attributes = { "id" => 5, "type" => "Client", "i_dont_even_exist" => 20 }
954
+ end
955
+ end
956
+
957
+ def test_mass_assignment_protection_on_defaults
958
+ firm = Firm.new
959
+ firm.attributes = { "id" => 5, "type" => "Client" }
960
+ assert_nil firm.id
961
+ assert_equal "Firm", firm[:type]
962
+ end
963
+
964
+ def test_mass_assignment_accessible
965
+ reply = Reply.new("title" => "hello", "content" => "world", "approved" => true)
966
+ reply.save
967
+
968
+ assert reply.approved?
969
+
970
+ reply.approved = false
971
+ reply.save
972
+
973
+ assert !reply.approved?
974
+ end
975
+
976
+ def test_mass_assignment_protection_inheritance
977
+ assert_nil LoosePerson.accessible_attributes
978
+ assert_equal Set.new([ 'credit_rating', 'administrator' ]), LoosePerson.protected_attributes
979
+
980
+ assert_nil LooseDescendant.accessible_attributes
981
+ assert_equal Set.new([ 'credit_rating', 'administrator', 'phone_number' ]), LooseDescendant.protected_attributes
982
+
983
+ assert_nil LooseDescendantSecond.accessible_attributes
984
+ assert_equal Set.new([ 'credit_rating', 'administrator', 'phone_number', 'name' ]), LooseDescendantSecond.protected_attributes, 'Running attr_protected twice in one class should merge the protections'
985
+
986
+ assert_nil TightPerson.protected_attributes
987
+ assert_equal Set.new([ 'name', 'address' ]), TightPerson.accessible_attributes
988
+
989
+ assert_nil TightDescendant.protected_attributes
990
+ assert_equal Set.new([ 'name', 'address', 'phone_number' ]), TightDescendant.accessible_attributes
991
+ end
992
+
993
+ def test_readonly_attributes
994
+ assert_equal Set.new([ 'title' , 'comments_count' ]), ReadonlyTitlePost.readonly_attributes
995
+
996
+ post = ReadonlyTitlePost.create(:title => "cannot change this", :body => "changeable")
997
+ post.reload
998
+ assert_equal "cannot change this", post.title
999
+
1000
+ post.update_attributes(:title => "try to change", :body => "changed")
1001
+ post.reload
1002
+ assert_equal "cannot change this", post.title
1003
+ assert_equal "changed", post.body
1004
+ end
1005
+
1006
+ def test_multiparameter_attributes_on_date
1007
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
1008
+ topic = Topic.find(1)
1009
+ topic.attributes = attributes
1010
+ # note that extra #to_date call allows test to pass for Oracle, which
1011
+ # treats dates/times the same
1012
+ assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date
1013
+ end
1014
+
1015
+ def test_multiparameter_attributes_on_date_with_empty_date
1016
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
1017
+ topic = Topic.find(1)
1018
+ topic.attributes = attributes
1019
+ # note that extra #to_date call allows test to pass for Oracle, which
1020
+ # treats dates/times the same
1021
+ assert_date_from_db Date.new(2004, 6, 1), topic.last_read.to_date
1022
+ end
1023
+
1024
+ def test_multiparameter_attributes_on_date_with_all_empty
1025
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" }
1026
+ topic = Topic.find(1)
1027
+ topic.attributes = attributes
1028
+ assert_nil topic.last_read
1029
+ end
1030
+
1031
+ def test_multiparameter_attributes_on_time
1032
+ attributes = {
1033
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1034
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1035
+ }
1036
+ topic = Topic.find(1)
1037
+ topic.attributes = attributes
1038
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
1039
+ end
1040
+
1041
+ def test_multiparameter_attributes_on_time_with_old_date
1042
+ attributes = {
1043
+ "written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24",
1044
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1045
+ }
1046
+ topic = Topic.find(1)
1047
+ topic.attributes = attributes
1048
+ # testing against to_s(:db) representation because either a Time or a DateTime might be returned, depending on platform
1049
+ assert_equal "1850-06-24 16:24:00", topic.written_on.to_s(:db)
1050
+ end
1051
+
1052
+ def test_multiparameter_attributes_on_time_with_utc
1053
+ ActiveRecord::Base.default_timezone = :utc
1054
+ attributes = {
1055
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1056
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1057
+ }
1058
+ topic = Topic.find(1)
1059
+ topic.attributes = attributes
1060
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
1061
+ ensure
1062
+ ActiveRecord::Base.default_timezone = :local
1063
+ end
1064
+
1065
+ def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes
1066
+ ActiveRecord::Base.time_zone_aware_attributes = true
1067
+ ActiveRecord::Base.default_timezone = :utc
1068
+ Time.zone = ActiveSupport::TimeZone[-28800]
1069
+ attributes = {
1070
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1071
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1072
+ }
1073
+ topic = Topic.find(1)
1074
+ topic.attributes = attributes
1075
+ assert_equal Time.utc(2004, 6, 24, 23, 24, 0), topic.written_on
1076
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on.time
1077
+ assert_equal Time.zone, topic.written_on.time_zone
1078
+ ensure
1079
+ ActiveRecord::Base.time_zone_aware_attributes = false
1080
+ ActiveRecord::Base.default_timezone = :local
1081
+ Time.zone = nil
1082
+ end
1083
+
1084
+ def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false
1085
+ ActiveRecord::Base.time_zone_aware_attributes = false
1086
+ Time.zone = ActiveSupport::TimeZone[-28800]
1087
+ attributes = {
1088
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1089
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1090
+ }
1091
+ topic = Topic.find(1)
1092
+ topic.attributes = attributes
1093
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
1094
+ assert_equal false, topic.written_on.respond_to?(:time_zone)
1095
+ ensure
1096
+ Time.zone = nil
1097
+ end
1098
+
1099
+ def test_multiparameter_attributes_on_time_with_skip_time_zone_conversion_for_attributes
1100
+ ActiveRecord::Base.time_zone_aware_attributes = true
1101
+ ActiveRecord::Base.default_timezone = :utc
1102
+ Time.zone = ActiveSupport::TimeZone[-28800]
1103
+ Topic.skip_time_zone_conversion_for_attributes = [:written_on]
1104
+ attributes = {
1105
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1106
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1107
+ }
1108
+ topic = Topic.find(1)
1109
+ topic.attributes = attributes
1110
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
1111
+ assert_equal false, topic.written_on.respond_to?(:time_zone)
1112
+ ensure
1113
+ ActiveRecord::Base.time_zone_aware_attributes = false
1114
+ ActiveRecord::Base.default_timezone = :local
1115
+ Time.zone = nil
1116
+ Topic.skip_time_zone_conversion_for_attributes = []
1117
+ end
1118
+
1119
+ def test_multiparameter_attributes_on_time_only_column_with_time_zone_aware_attributes_does_not_do_time_zone_conversion
1120
+ ActiveRecord::Base.time_zone_aware_attributes = true
1121
+ ActiveRecord::Base.default_timezone = :utc
1122
+ Time.zone = ActiveSupport::TimeZone[-28800]
1123
+ attributes = {
1124
+ "bonus_time(1i)" => "2000", "bonus_time(2i)" => "1", "bonus_time(3i)" => "1",
1125
+ "bonus_time(4i)" => "16", "bonus_time(5i)" => "24"
1126
+ }
1127
+ topic = Topic.find(1)
1128
+ topic.attributes = attributes
1129
+ assert_equal Time.utc(2000, 1, 1, 16, 24, 0), topic.bonus_time
1130
+ assert topic.bonus_time.utc?
1131
+ ensure
1132
+ ActiveRecord::Base.time_zone_aware_attributes = false
1133
+ ActiveRecord::Base.default_timezone = :local
1134
+ Time.zone = nil
1135
+ end
1136
+
1137
+ def test_multiparameter_attributes_on_time_with_empty_seconds
1138
+ attributes = {
1139
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1140
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => ""
1141
+ }
1142
+ topic = Topic.find(1)
1143
+ topic.attributes = attributes
1144
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
1145
+ end
1146
+
1147
+ def test_multiparameter_mass_assignment_protector
1148
+ task = Task.new
1149
+ time = Time.mktime(2000, 1, 1, 1)
1150
+ task.starting = time
1151
+ attributes = { "starting(1i)" => "2004", "starting(2i)" => "6", "starting(3i)" => "24" }
1152
+ task.attributes = attributes
1153
+ assert_equal time, task.starting
1154
+ end
1155
+
1156
+ def test_multiparameter_assignment_of_aggregation
1157
+ customer = Customer.new
1158
+ address = Address.new("The Street", "The City", "The Country")
1159
+ attributes = { "address(1)" => address.street, "address(2)" => address.city, "address(3)" => address.country }
1160
+ customer.attributes = attributes
1161
+ assert_equal address, customer.address
1162
+ end
1163
+
1164
+ def test_attributes_on_dummy_time
1165
+ # Oracle, and Sybase do not have a TIME datatype.
1166
+ return true if current_adapter?(:OracleAdapter, :SybaseAdapter)
1167
+
1168
+ attributes = {
1169
+ "bonus_time" => "5:42:00AM"
1170
+ }
1171
+ topic = Topic.find(1)
1172
+ topic.attributes = attributes
1173
+ assert_equal Time.local(2000, 1, 1, 5, 42, 0), topic.bonus_time
1174
+ end
1175
+
1176
+ def test_boolean
1177
+ b_nil = Booleantest.create({ "value" => nil })
1178
+ nil_id = b_nil.id
1179
+ b_false = Booleantest.create({ "value" => false })
1180
+ false_id = b_false.id
1181
+ b_true = Booleantest.create({ "value" => true })
1182
+ true_id = b_true.id
1183
+
1184
+ b_nil = Booleantest.find(nil_id)
1185
+ assert_nil b_nil.value
1186
+ b_false = Booleantest.find(false_id)
1187
+ assert !b_false.value?
1188
+ b_true = Booleantest.find(true_id)
1189
+ assert b_true.value?
1190
+ end
1191
+
1192
+ def test_boolean_cast_from_string
1193
+ b_blank = Booleantest.create({ "value" => "" })
1194
+ blank_id = b_blank.id
1195
+ b_false = Booleantest.create({ "value" => "0" })
1196
+ false_id = b_false.id
1197
+ b_true = Booleantest.create({ "value" => "1" })
1198
+ true_id = b_true.id
1199
+
1200
+ b_blank = Booleantest.find(blank_id)
1201
+ assert_nil b_blank.value
1202
+ b_false = Booleantest.find(false_id)
1203
+ assert !b_false.value?
1204
+ b_true = Booleantest.find(true_id)
1205
+ assert b_true.value?
1206
+ end
1207
+
1208
+ def test_new_record_returns_boolean
1209
+ assert_equal Topic.new.new_record?, true
1210
+ assert_equal Topic.find(1).new_record?, false
1211
+ end
1212
+
1213
+ def test_clone
1214
+ topic = Topic.find(1)
1215
+ cloned_topic = nil
1216
+ assert_nothing_raised { cloned_topic = topic.clone }
1217
+ assert_equal topic.title, cloned_topic.title
1218
+ assert cloned_topic.new_record?
1219
+
1220
+ # test if the attributes have been cloned
1221
+ topic.title = "a"
1222
+ cloned_topic.title = "b"
1223
+ assert_equal "a", topic.title
1224
+ assert_equal "b", cloned_topic.title
1225
+
1226
+ # test if the attribute values have been cloned
1227
+ topic.title = {"a" => "b"}
1228
+ cloned_topic = topic.clone
1229
+ cloned_topic.title["a"] = "c"
1230
+ assert_equal "b", topic.title["a"]
1231
+
1232
+ #test if attributes set as part of after_initialize are cloned correctly
1233
+ assert_equal topic.author_email_address, cloned_topic.author_email_address
1234
+
1235
+ # test if saved clone object differs from original
1236
+ cloned_topic.save
1237
+ assert !cloned_topic.new_record?
1238
+ assert cloned_topic.id != topic.id
1239
+ end
1240
+
1241
+ def test_clone_with_aggregate_of_same_name_as_attribute
1242
+ dev = DeveloperWithAggregate.find(1)
1243
+ assert_kind_of DeveloperSalary, dev.salary
1244
+
1245
+ clone = nil
1246
+ assert_nothing_raised { clone = dev.clone }
1247
+ assert_kind_of DeveloperSalary, clone.salary
1248
+ assert_equal dev.salary.amount, clone.salary.amount
1249
+ assert clone.new_record?
1250
+
1251
+ # test if the attributes have been cloned
1252
+ original_amount = clone.salary.amount
1253
+ dev.salary.amount = 1
1254
+ assert_equal original_amount, clone.salary.amount
1255
+
1256
+ assert clone.save
1257
+ assert !clone.new_record?
1258
+ assert clone.id != dev.id
1259
+ end
1260
+
1261
+ def test_clone_preserves_subtype
1262
+ clone = nil
1263
+ assert_nothing_raised { clone = Company.find(3).clone }
1264
+ assert_kind_of Client, clone
1265
+ end
1266
+
1267
+ def test_bignum
1268
+ company = Company.find(1)
1269
+ company.rating = 2147483647
1270
+ company.save
1271
+ company = Company.find(1)
1272
+ assert_equal 2147483647, company.rating
1273
+ end
1274
+
1275
+ # TODO: extend defaults tests to other databases!
1276
+ if current_adapter?(:PostgreSQLAdapter)
1277
+ def test_default
1278
+ default = Default.new
1279
+
1280
+ # fixed dates / times
1281
+ assert_equal Date.new(2004, 1, 1), default.fixed_date
1282
+ assert_equal Time.local(2004, 1,1,0,0,0,0), default.fixed_time
1283
+
1284
+ # char types
1285
+ assert_equal 'Y', default.char1
1286
+ assert_equal 'a varchar field', default.char2
1287
+ assert_equal 'a text field', default.char3
1288
+ end
1289
+
1290
+ class Geometric < ActiveRecord::Base; end
1291
+ def test_geometric_content
1292
+
1293
+ # accepted format notes:
1294
+ # ()'s aren't required
1295
+ # values can be a mix of float or integer
1296
+
1297
+ g = Geometric.new(
1298
+ :a_point => '(5.0, 6.1)',
1299
+ #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
1300
+ :a_line_segment => '(2.0, 3), (5.5, 7.0)',
1301
+ :a_box => '2.0, 3, 5.5, 7.0',
1302
+ :a_path => '[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]', # [ ] is an open path
1303
+ :a_polygon => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))',
1304
+ :a_circle => '<(5.3, 10.4), 2>'
1305
+ )
1306
+
1307
+ assert g.save
1308
+
1309
+ # Reload and check that we have all the geometric attributes.
1310
+ h = Geometric.find(g.id)
1311
+
1312
+ assert_equal '(5,6.1)', h.a_point
1313
+ assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
1314
+ assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
1315
+ assert_equal '[(2,3),(5.5,7),(8.5,11)]', h.a_path
1316
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
1317
+ assert_equal '<(5.3,10.4),2>', h.a_circle
1318
+
1319
+ # use a geometric function to test for an open path
1320
+ objs = Geometric.find_by_sql ["select isopen(a_path) from geometrics where id = ?", g.id]
1321
+ assert_equal objs[0].isopen, 't'
1322
+
1323
+ # test alternate formats when defining the geometric types
1324
+
1325
+ g = Geometric.new(
1326
+ :a_point => '5.0, 6.1',
1327
+ #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
1328
+ :a_line_segment => '((2.0, 3), (5.5, 7.0))',
1329
+ :a_box => '(2.0, 3), (5.5, 7.0)',
1330
+ :a_path => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))', # ( ) is a closed path
1331
+ :a_polygon => '2.0, 3, 5.5, 7.0, 8.5, 11.0',
1332
+ :a_circle => '((5.3, 10.4), 2)'
1333
+ )
1334
+
1335
+ assert g.save
1336
+
1337
+ # Reload and check that we have all the geometric attributes.
1338
+ h = Geometric.find(g.id)
1339
+
1340
+ assert_equal '(5,6.1)', h.a_point
1341
+ assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
1342
+ assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
1343
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path
1344
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
1345
+ assert_equal '<(5.3,10.4),2>', h.a_circle
1346
+
1347
+ # use a geometric function to test for an closed path
1348
+ objs = Geometric.find_by_sql ["select isclosed(a_path) from geometrics where id = ?", g.id]
1349
+ assert_equal objs[0].isclosed, 't'
1350
+ end
1351
+ end
1352
+
1353
+ class NumericData < ActiveRecord::Base
1354
+ self.table_name = 'numeric_data'
1355
+ end
1356
+
1357
+ def test_numeric_fields
1358
+ m = NumericData.new(
1359
+ :bank_balance => 1586.43,
1360
+ :big_bank_balance => BigDecimal("1000234000567.95"),
1361
+ :world_population => 6000000000,
1362
+ :my_house_population => 3
1363
+ )
1364
+ assert m.save
1365
+
1366
+ m1 = NumericData.find(m.id)
1367
+ assert_not_nil m1
1368
+
1369
+ # As with migration_test.rb, we should make world_population >= 2**62
1370
+ # to cover 64-bit platforms and test it is a Bignum, but the main thing
1371
+ # is that it's an Integer.
1372
+ unless current_adapter?(:IBM_DBAdapter)
1373
+ assert_kind_of Integer, m1.world_population
1374
+ else
1375
+ assert_kind_of BigDecimal, m1.world_population
1376
+ end
1377
+ assert_equal 6000000000, m1.world_population
1378
+ unless current_adapter?(:IBM_DBAdapter)
1379
+ assert_kind_of Fixnum, m1.my_house_population
1380
+ else
1381
+ assert_kind_of BigDecimal, m1.my_house_population
1382
+ end
1383
+ assert_equal 3, m1.my_house_population
1384
+
1385
+ assert_kind_of BigDecimal, m1.bank_balance
1386
+ assert_equal BigDecimal("1586.43"), m1.bank_balance
1387
+
1388
+ assert_kind_of BigDecimal, m1.big_bank_balance
1389
+ assert_equal BigDecimal("1000234000567.95"), m1.big_bank_balance
1390
+ end
1391
+
1392
+ def test_auto_id
1393
+ auto = AutoId.new
1394
+ auto.save
1395
+ assert (auto.id > 0)
1396
+ end
1397
+
1398
+ def quote_column_name(name)
1399
+ "<#{name}>"
1400
+ end
1401
+
1402
+ def test_quote_keys
1403
+ ar = AutoId.new
1404
+ source = {"foo" => "bar", "baz" => "quux"}
1405
+ actual = ar.send(:quote_columns, self, source)
1406
+ inverted = actual.invert
1407
+ assert_equal("<foo>", inverted["bar"])
1408
+ assert_equal("<baz>", inverted["quux"])
1409
+ end
1410
+
1411
+ def test_sql_injection_via_find
1412
+ assert_raise(ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid) do
1413
+ Topic.find("123456 OR id > 0")
1414
+ end
1415
+ end
1416
+
1417
+ def test_column_name_properly_quoted
1418
+ col_record = ColumnName.new
1419
+ col_record.references = 40
1420
+ assert col_record.save
1421
+ col_record.references = 41
1422
+ assert col_record.save
1423
+ assert_not_nil c2 = ColumnName.find(col_record.id)
1424
+ assert_equal(41, c2.references)
1425
+ end
1426
+
1427
+ def test_quoting_arrays
1428
+ replies = Reply.find(:all, :conditions => [ "id IN (?)", topics(:first).replies.collect(&:id) ])
1429
+ assert_equal topics(:first).replies.size, replies.size
1430
+
1431
+ replies = Reply.find(:all, :conditions => [ "id IN (?)", [] ])
1432
+ assert_equal 0, replies.size
1433
+ end
1434
+
1435
+ MyObject = Struct.new :attribute1, :attribute2
1436
+
1437
+ def test_serialized_attribute
1438
+ myobj = MyObject.new('value1', 'value2')
1439
+ topic = Topic.create("content" => myobj)
1440
+ Topic.serialize("content", MyObject)
1441
+ assert_equal(myobj, topic.content)
1442
+ end
1443
+
1444
+ def test_serialized_time_attribute
1445
+ myobj = Time.local(2008,1,1,1,0)
1446
+ topic = Topic.create("content" => myobj).reload
1447
+ assert_equal(myobj, topic.content)
1448
+ end
1449
+
1450
+ def test_serialized_string_attribute
1451
+ myobj = "Yes"
1452
+ topic = Topic.create("content" => myobj).reload
1453
+ assert_equal(myobj, topic.content)
1454
+ end
1455
+
1456
+ def test_nil_serialized_attribute_with_class_constraint
1457
+ myobj = MyObject.new('value1', 'value2')
1458
+ topic = Topic.new
1459
+ assert_nil topic.content
1460
+ end
1461
+
1462
+ def test_should_raise_exception_on_serialized_attribute_with_type_mismatch
1463
+ myobj = MyObject.new('value1', 'value2')
1464
+ topic = Topic.new(:content => myobj)
1465
+ assert topic.save
1466
+ Topic.serialize(:content, Hash)
1467
+ assert_raise(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).content }
1468
+ ensure
1469
+ Topic.serialize(:content)
1470
+ end
1471
+
1472
+ def test_serialized_attribute_with_class_constraint
1473
+ settings = { "color" => "blue" }
1474
+ Topic.serialize(:content, Hash)
1475
+ topic = Topic.new(:content => settings)
1476
+ assert topic.save
1477
+ assert_equal(settings, Topic.find(topic.id).content)
1478
+ ensure
1479
+ Topic.serialize(:content)
1480
+ end
1481
+
1482
+ def test_quote
1483
+ author_name = "\\ \001 ' \n \\n \""
1484
+ topic = Topic.create('author_name' => author_name)
1485
+ assert_equal author_name, Topic.find(topic.id).author_name
1486
+ end
1487
+
1488
+ if RUBY_VERSION < '1.9'
1489
+ def test_quote_chars
1490
+ with_kcode('UTF8') do
1491
+ str = 'The Narrator'
1492
+ topic = Topic.create(:author_name => str)
1493
+ assert_equal str, topic.author_name
1494
+
1495
+ assert_kind_of ActiveSupport::Multibyte.proxy_class, str.mb_chars
1496
+ topic = Topic.find_by_author_name(str.mb_chars)
1497
+
1498
+ assert_kind_of Topic, topic
1499
+ assert_equal str, topic.author_name, "The right topic should have been found by name even with name passed as Chars"
1500
+ end
1501
+ end
1502
+ end
1503
+
1504
+ def test_class_level_destroy
1505
+ should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
1506
+ Topic.find(1).replies << should_be_destroyed_reply
1507
+
1508
+ Topic.destroy(1)
1509
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
1510
+ assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) }
1511
+ end
1512
+
1513
+ def test_class_level_delete
1514
+ should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
1515
+ Topic.find(1).replies << should_be_destroyed_reply
1516
+
1517
+ Topic.delete(1)
1518
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
1519
+ assert_nothing_raised { Reply.find(should_be_destroyed_reply.id) }
1520
+ end
1521
+
1522
+ def test_increment_attribute
1523
+ assert_equal 50, accounts(:signals37).credit_limit
1524
+ accounts(:signals37).increment! :credit_limit
1525
+ assert_equal 51, accounts(:signals37, :reload).credit_limit
1526
+
1527
+ accounts(:signals37).increment(:credit_limit).increment!(:credit_limit)
1528
+ assert_equal 53, accounts(:signals37, :reload).credit_limit
1529
+ end
1530
+
1531
+ def test_increment_nil_attribute
1532
+ assert_nil topics(:first).parent_id
1533
+ topics(:first).increment! :parent_id
1534
+ assert_equal 1, topics(:first).parent_id
1535
+ end
1536
+
1537
+ def test_increment_attribute_by
1538
+ assert_equal 50, accounts(:signals37).credit_limit
1539
+ accounts(:signals37).increment! :credit_limit, 5
1540
+ assert_equal 55, accounts(:signals37, :reload).credit_limit
1541
+
1542
+ accounts(:signals37).increment(:credit_limit, 1).increment!(:credit_limit, 3)
1543
+ assert_equal 59, accounts(:signals37, :reload).credit_limit
1544
+ end
1545
+
1546
+ def test_decrement_attribute
1547
+ assert_equal 50, accounts(:signals37).credit_limit
1548
+
1549
+ accounts(:signals37).decrement!(:credit_limit)
1550
+ assert_equal 49, accounts(:signals37, :reload).credit_limit
1551
+
1552
+ accounts(:signals37).decrement(:credit_limit).decrement!(:credit_limit)
1553
+ assert_equal 47, accounts(:signals37, :reload).credit_limit
1554
+ end
1555
+
1556
+ def test_decrement_attribute_by
1557
+ assert_equal 50, accounts(:signals37).credit_limit
1558
+ accounts(:signals37).decrement! :credit_limit, 5
1559
+ assert_equal 45, accounts(:signals37, :reload).credit_limit
1560
+
1561
+ accounts(:signals37).decrement(:credit_limit, 1).decrement!(:credit_limit, 3)
1562
+ assert_equal 41, accounts(:signals37, :reload).credit_limit
1563
+ end
1564
+
1565
+ def test_toggle_attribute
1566
+ assert !topics(:first).approved?
1567
+ topics(:first).toggle!(:approved)
1568
+ assert topics(:first).approved?
1569
+ topic = topics(:first)
1570
+ topic.toggle(:approved)
1571
+ assert !topic.approved?
1572
+ topic.reload
1573
+ assert topic.approved?
1574
+ end
1575
+
1576
+ def test_reload
1577
+ t1 = Topic.find(1)
1578
+ t2 = Topic.find(1)
1579
+ t1.title = "something else"
1580
+ t1.save
1581
+ t2.reload
1582
+ assert_equal t1.title, t2.title
1583
+ end
1584
+
1585
+ def test_define_attr_method_with_value
1586
+ k = Class.new( ActiveRecord::Base )
1587
+ k.send(:define_attr_method, :table_name, "foo")
1588
+ assert_equal "foo", k.table_name
1589
+ end
1590
+
1591
+ def test_define_attr_method_with_block
1592
+ k = Class.new( ActiveRecord::Base )
1593
+ k.send(:define_attr_method, :primary_key) { "sys_" + original_primary_key }
1594
+ assert_equal "sys_id", k.primary_key
1595
+ end
1596
+
1597
+ def test_set_table_name_with_value
1598
+ k = Class.new( ActiveRecord::Base )
1599
+ k.table_name = "foo"
1600
+ assert_equal "foo", k.table_name
1601
+ k.set_table_name "bar"
1602
+ assert_equal "bar", k.table_name
1603
+ end
1604
+
1605
+ def test_set_table_name_with_block
1606
+ k = Class.new( ActiveRecord::Base )
1607
+ k.set_table_name { "ks" }
1608
+ assert_equal "ks", k.table_name
1609
+ end
1610
+
1611
+ def test_set_primary_key_with_value
1612
+ k = Class.new( ActiveRecord::Base )
1613
+ k.primary_key = "foo"
1614
+ assert_equal "foo", k.primary_key
1615
+ k.set_primary_key "bar"
1616
+ assert_equal "bar", k.primary_key
1617
+ end
1618
+
1619
+ def test_set_primary_key_with_block
1620
+ k = Class.new( ActiveRecord::Base )
1621
+ k.set_primary_key { "sys_" + original_primary_key }
1622
+ assert_equal "sys_id", k.primary_key
1623
+ end
1624
+
1625
+ def test_set_inheritance_column_with_value
1626
+ k = Class.new( ActiveRecord::Base )
1627
+ k.inheritance_column = "foo"
1628
+ assert_equal "foo", k.inheritance_column
1629
+ k.set_inheritance_column "bar"
1630
+ assert_equal "bar", k.inheritance_column
1631
+ end
1632
+
1633
+ def test_set_inheritance_column_with_block
1634
+ k = Class.new( ActiveRecord::Base )
1635
+ k.set_inheritance_column { original_inheritance_column + "_id" }
1636
+ assert_equal "type_id", k.inheritance_column
1637
+ end
1638
+
1639
+ def test_count_with_join
1640
+ res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
1641
+
1642
+ res2 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'", :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1643
+ assert_equal res, res2
1644
+
1645
+ res3 = nil
1646
+ assert_nothing_raised do
1647
+ res3 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'",
1648
+ :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1649
+ end
1650
+ assert_equal res, res3
1651
+
1652
+ res4 = Post.count_by_sql "SELECT COUNT(p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
1653
+ res5 = nil
1654
+ assert_nothing_raised do
1655
+ res5 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
1656
+ :joins => "p, comments co",
1657
+ :select => "p.id")
1658
+ end
1659
+
1660
+ assert_equal res4, res5
1661
+
1662
+ unless current_adapter?(:SQLite2Adapter, :DeprecatedSQLiteAdapter)
1663
+ 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"
1664
+ res7 = nil
1665
+ assert_nothing_raised do
1666
+ res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
1667
+ :joins => "p, comments co",
1668
+ :select => "p.id",
1669
+ :distinct => true)
1670
+ end
1671
+ assert_equal res6, res7
1672
+ end
1673
+ end
1674
+
1675
+ def test_clear_association_cache_stored
1676
+ firm = Firm.find(1)
1677
+ assert_kind_of Firm, firm
1678
+
1679
+ firm.clear_association_cache
1680
+ assert_equal Firm.find(1).clients.collect{ |x| x.name }.sort, firm.clients.collect{ |x| x.name }.sort
1681
+ end
1682
+
1683
+ def test_clear_association_cache_new_record
1684
+ firm = Firm.new
1685
+ client_stored = Client.find(3)
1686
+ client_new = Client.new
1687
+ client_new.name = "The Joneses"
1688
+ clients = [ client_stored, client_new ]
1689
+
1690
+ firm.clients << clients
1691
+ assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
1692
+
1693
+ firm.clear_association_cache
1694
+ assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
1695
+ end
1696
+
1697
+ def test_interpolate_sql
1698
+ assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo@bar') }
1699
+ assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar) baz') }
1700
+ assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar} baz') }
1701
+ end
1702
+
1703
+ def test_scoped_find_conditions
1704
+ scoped_developers = Developer.with_scope(:find => { :conditions => 'salary > 90000' }) do
1705
+ Developer.find(:all, :conditions => 'id < 5')
1706
+ end
1707
+ assert !scoped_developers.include?(developers(:david)) # David's salary is less than 90,000
1708
+ assert_equal 3, scoped_developers.size
1709
+ end
1710
+
1711
+ def test_scoped_find_limit_offset
1712
+ scoped_developers = Developer.with_scope(:find => { :limit => 3, :offset => 2 }) do
1713
+ Developer.find(:all, :order => 'id')
1714
+ end
1715
+ assert !scoped_developers.include?(developers(:david))
1716
+ assert !scoped_developers.include?(developers(:jamis))
1717
+ assert_equal 3, scoped_developers.size
1718
+
1719
+ # Test without scoped find conditions to ensure we get the whole thing
1720
+ developers = Developer.find(:all, :order => 'id')
1721
+ assert_equal Developer.count, developers.size
1722
+ end
1723
+
1724
+ def test_scoped_find_order
1725
+ # Test order in scope
1726
+ scoped_developers = Developer.with_scope(:find => { :limit => 1, :order => 'salary DESC' }) do
1727
+ Developer.find(:all)
1728
+ end
1729
+ assert_equal 'Jamis', scoped_developers.first.name
1730
+ assert scoped_developers.include?(developers(:jamis))
1731
+ # Test scope without order and order in find
1732
+ scoped_developers = Developer.with_scope(:find => { :limit => 1 }) do
1733
+ Developer.find(:all, :order => 'salary DESC')
1734
+ end
1735
+ # Test scope order + find order, find has priority
1736
+ scoped_developers = Developer.with_scope(:find => { :limit => 3, :order => 'id DESC' }) do
1737
+ Developer.find(:all, :order => 'salary ASC')
1738
+ end
1739
+ assert scoped_developers.include?(developers(:poor_jamis))
1740
+ assert scoped_developers.include?(developers(:david))
1741
+ assert scoped_developers.include?(developers(:dev_10))
1742
+ # Test without scoped find conditions to ensure we get the right thing
1743
+ developers = Developer.find(:all, :order => 'id', :limit => 1)
1744
+ assert scoped_developers.include?(developers(:david))
1745
+ end
1746
+
1747
+ def test_scoped_find_limit_offset_including_has_many_association
1748
+ topics = Topic.with_scope(:find => {:limit => 1, :offset => 1, :include => :replies}) do
1749
+ Topic.find(:all, :order => "topics.id")
1750
+ end
1751
+ assert_equal 1, topics.size
1752
+ assert_equal 2, topics.first.id
1753
+ end
1754
+
1755
+ def test_scoped_find_order_including_has_many_association
1756
+ developers = Developer.with_scope(:find => { :order => 'developers.salary DESC', :include => :projects }) do
1757
+ Developer.find(:all)
1758
+ end
1759
+ assert developers.size >= 2
1760
+ for i in 1...developers.size
1761
+ assert developers[i-1].salary >= developers[i].salary
1762
+ end
1763
+ end
1764
+
1765
+ def test_scoped_find_with_group_and_having
1766
+ developers = Developer.with_scope(:find => { :group => 'developers.salary', :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" }) do
1767
+ Developer.find(:all)
1768
+ end
1769
+ assert_equal 3, developers.size
1770
+ end
1771
+
1772
+ def test_find_last
1773
+ last = Developer.find :last
1774
+ assert_equal last, Developer.find(:first, :order => 'id desc')
1775
+ end
1776
+
1777
+ def test_last
1778
+ assert_equal Developer.find(:first, :order => 'id desc'), Developer.last
1779
+ end
1780
+
1781
+ def test_all_with_conditions
1782
+ assert_equal Developer.find(:all, :order => 'id desc'), Developer.all(:order => 'id desc')
1783
+ end
1784
+
1785
+ def test_find_ordered_last
1786
+ last = Developer.find :last, :order => 'developers.salary ASC'
1787
+ assert_equal last, Developer.find(:all, :order => 'developers.salary ASC').last
1788
+ end
1789
+
1790
+ def test_find_reverse_ordered_last
1791
+ last = Developer.find :last, :order => 'developers.salary DESC'
1792
+ assert_equal last, Developer.find(:all, :order => 'developers.salary DESC').last
1793
+ end
1794
+
1795
+ def test_find_multiple_ordered_last
1796
+ last = Developer.find :last, :order => 'developers.name, developers.salary DESC'
1797
+ assert_equal last, Developer.find(:all, :order => 'developers.name, developers.salary DESC').last
1798
+ end
1799
+
1800
+ def test_find_symbol_ordered_last
1801
+ last = Developer.find :last, :order => :salary
1802
+ assert_equal last, Developer.find(:all, :order => :salary).last
1803
+ end
1804
+
1805
+ def test_find_scoped_ordered_last
1806
+ last_developer = Developer.with_scope(:find => { :order => 'developers.salary ASC' }) do
1807
+ Developer.find(:last)
1808
+ end
1809
+ assert_equal last_developer, Developer.find(:all, :order => 'developers.salary ASC').last
1810
+ end
1811
+
1812
+ def test_abstract_class
1813
+ assert !ActiveRecord::Base.abstract_class?
1814
+ assert LoosePerson.abstract_class?
1815
+ assert !LooseDescendant.abstract_class?
1816
+ end
1817
+
1818
+ def test_base_class
1819
+ assert_equal LoosePerson, LoosePerson.base_class
1820
+ assert_equal LooseDescendant, LooseDescendant.base_class
1821
+ assert_equal TightPerson, TightPerson.base_class
1822
+ assert_equal TightPerson, TightDescendant.base_class
1823
+
1824
+ assert_equal Post, Post.base_class
1825
+ assert_equal Post, SpecialPost.base_class
1826
+ assert_equal Post, StiPost.base_class
1827
+ assert_equal SubStiPost, SubStiPost.base_class
1828
+ end
1829
+
1830
+ def test_descends_from_active_record
1831
+ # Tries to call Object.abstract_class?
1832
+ assert_raise(NoMethodError) do
1833
+ ActiveRecord::Base.descends_from_active_record?
1834
+ end
1835
+
1836
+ # Abstract subclass of AR::Base.
1837
+ assert LoosePerson.descends_from_active_record?
1838
+
1839
+ # Concrete subclass of an abstract class.
1840
+ assert LooseDescendant.descends_from_active_record?
1841
+
1842
+ # Concrete subclass of AR::Base.
1843
+ assert TightPerson.descends_from_active_record?
1844
+
1845
+ # Concrete subclass of a concrete class but has no type column.
1846
+ assert TightDescendant.descends_from_active_record?
1847
+
1848
+ # Concrete subclass of AR::Base.
1849
+ assert Post.descends_from_active_record?
1850
+
1851
+ # Abstract subclass of a concrete class which has a type column.
1852
+ # This is pathological, as you'll never have Sub < Abstract < Concrete.
1853
+ assert !StiPost.descends_from_active_record?
1854
+
1855
+ # Concrete subclasses an abstract class which has a type column.
1856
+ assert !SubStiPost.descends_from_active_record?
1857
+ end
1858
+
1859
+ def test_find_on_abstract_base_class_doesnt_use_type_condition
1860
+ old_class = LooseDescendant
1861
+ Object.send :remove_const, :LooseDescendant
1862
+
1863
+ descendant = old_class.create! :first_name => 'bob'
1864
+ assert_not_nil LoosePerson.find(descendant.id), "Should have found instance of LooseDescendant when finding abstract LoosePerson: #{descendant.inspect}"
1865
+ ensure
1866
+ unless Object.const_defined?(:LooseDescendant)
1867
+ Object.const_set :LooseDescendant, old_class
1868
+ end
1869
+ end
1870
+
1871
+ def test_assert_queries
1872
+ query = lambda { ActiveRecord::Base.connection.execute 'select count(*) from developers' }
1873
+ assert_queries(2) { 2.times { query.call } }
1874
+ assert_queries 1, &query
1875
+ assert_no_queries { assert true }
1876
+ end
1877
+
1878
+ def test_to_xml
1879
+ xml = REXML::Document.new(topics(:first).to_xml(:indent => 0))
1880
+ bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema
1881
+ written_on_in_current_timezone = topics(:first).written_on.xmlschema
1882
+ last_read_in_current_timezone = topics(:first).last_read.xmlschema
1883
+
1884
+ assert_equal "topic", xml.root.name
1885
+ assert_equal "The First Topic" , xml.elements["//title"].text
1886
+ assert_equal "David" , xml.elements["//author-name"].text
1887
+
1888
+ assert_equal "1", xml.elements["//id"].text
1889
+ assert_equal "integer" , xml.elements["//id"].attributes['type']
1890
+
1891
+ assert_equal "1", xml.elements["//replies-count"].text
1892
+ assert_equal "integer" , xml.elements["//replies-count"].attributes['type']
1893
+
1894
+ assert_equal written_on_in_current_timezone, xml.elements["//written-on"].text
1895
+ assert_equal "datetime" , xml.elements["//written-on"].attributes['type']
1896
+
1897
+ assert_equal "--- Have a nice day\n" , xml.elements["//content"].text
1898
+ assert_equal "yaml" , xml.elements["//content"].attributes['type']
1899
+
1900
+ assert_equal "david@loudthinking.com", xml.elements["//author-email-address"].text
1901
+
1902
+ assert_equal nil, xml.elements["//parent-id"].text
1903
+ assert_equal "integer", xml.elements["//parent-id"].attributes['type']
1904
+ assert_equal "true", xml.elements["//parent-id"].attributes['nil']
1905
+
1906
+ if current_adapter?(:SybaseAdapter, :OracleAdapter)
1907
+ assert_equal last_read_in_current_timezone, xml.elements["//last-read"].text
1908
+ assert_equal "datetime" , xml.elements["//last-read"].attributes['type']
1909
+ else
1910
+ assert_equal "2004-04-15", xml.elements["//last-read"].text
1911
+ assert_equal "date" , xml.elements["//last-read"].attributes['type']
1912
+ end
1913
+
1914
+ # Oracle and DB2 don't have true boolean or time-only fields
1915
+ unless current_adapter?(:OracleAdapter, :DB2Adapter)
1916
+ assert_equal "false", xml.elements["//approved"].text
1917
+ assert_equal "boolean" , xml.elements["//approved"].attributes['type']
1918
+
1919
+ assert_equal bonus_time_in_current_timezone, xml.elements["//bonus-time"].text
1920
+ assert_equal "datetime" , xml.elements["//bonus-time"].attributes['type']
1921
+ end
1922
+ end
1923
+
1924
+ def test_to_xml_skipping_attributes
1925
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :replies_count])
1926
+ assert_equal "<topic>", xml.first(7)
1927
+ assert !xml.include?(%(<title>The First Topic</title>))
1928
+ assert xml.include?(%(<author-name>David</author-name>))
1929
+
1930
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :author_name, :replies_count])
1931
+ assert !xml.include?(%(<title>The First Topic</title>))
1932
+ assert !xml.include?(%(<author-name>David</author-name>))
1933
+ end
1934
+
1935
+ def test_to_xml_including_has_many_association
1936
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count)
1937
+ assert_equal "<topic>", xml.first(7)
1938
+ assert xml.include?(%(<replies type="array"><reply>))
1939
+ assert xml.include?(%(<title>The Second Topic of the day</title>))
1940
+ end
1941
+
1942
+ def test_array_to_xml_including_has_many_association
1943
+ xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :include => :replies)
1944
+ assert xml.include?(%(<replies type="array"><reply>))
1945
+ end
1946
+
1947
+ def test_array_to_xml_including_methods
1948
+ xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :methods => [ :topic_id ])
1949
+ assert xml.include?(%(<topic-id type="integer">#{topics(:first).topic_id}</topic-id>)), xml
1950
+ assert xml.include?(%(<topic-id type="integer">#{topics(:second).topic_id}</topic-id>)), xml
1951
+ end
1952
+
1953
+ def test_array_to_xml_including_has_one_association
1954
+ xml = [ companies(:first_firm), companies(:rails_core) ].to_xml(:indent => 0, :skip_instruct => true, :include => :account)
1955
+ assert xml.include?(companies(:first_firm).account.to_xml(:indent => 0, :skip_instruct => true))
1956
+ assert xml.include?(companies(:rails_core).account.to_xml(:indent => 0, :skip_instruct => true))
1957
+ end
1958
+
1959
+ def test_array_to_xml_including_belongs_to_association
1960
+ xml = [ companies(:first_client), companies(:second_client), companies(:another_client) ].to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
1961
+ assert xml.include?(companies(:first_client).to_xml(:indent => 0, :skip_instruct => true))
1962
+ assert xml.include?(companies(:second_client).firm.to_xml(:indent => 0, :skip_instruct => true))
1963
+ assert xml.include?(companies(:another_client).firm.to_xml(:indent => 0, :skip_instruct => true))
1964
+ end
1965
+
1966
+ def test_to_xml_including_belongs_to_association
1967
+ xml = companies(:first_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
1968
+ assert !xml.include?("<firm>")
1969
+
1970
+ xml = companies(:second_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
1971
+ assert xml.include?("<firm>")
1972
+ end
1973
+
1974
+ def test_to_xml_including_multiple_associations
1975
+ xml = companies(:first_firm).to_xml(:indent => 0, :skip_instruct => true, :include => [ :clients, :account ])
1976
+ assert_equal "<firm>", xml.first(6)
1977
+ assert xml.include?(%(<account>))
1978
+ assert xml.include?(%(<clients type="array"><client>))
1979
+ end
1980
+
1981
+ def test_to_xml_including_multiple_associations_with_options
1982
+ xml = companies(:first_firm).to_xml(
1983
+ :indent => 0, :skip_instruct => true,
1984
+ :include => { :clients => { :only => :name } }
1985
+ )
1986
+
1987
+ assert_equal "<firm>", xml.first(6)
1988
+ assert xml.include?(%(<client><name>Summit</name></client>))
1989
+ assert xml.include?(%(<clients type="array"><client>))
1990
+ end
1991
+
1992
+ def test_to_xml_including_methods
1993
+ xml = Company.new.to_xml(:methods => :arbitrary_method, :skip_instruct => true)
1994
+ assert_equal "<company>", xml.first(9)
1995
+ assert xml.include?(%(<arbitrary-method>I am Jack's profound disappointment</arbitrary-method>))
1996
+ end
1997
+
1998
+ def test_to_xml_with_block
1999
+ value = "Rockin' the block"
2000
+ xml = Company.new.to_xml(:skip_instruct => true) do |xml|
2001
+ xml.tag! "arbitrary-element", value
2002
+ end
2003
+ assert_equal "<company>", xml.first(9)
2004
+ assert xml.include?(%(<arbitrary-element>#{value}</arbitrary-element>))
2005
+ end
2006
+
2007
+ def test_type_name_with_module_should_handle_beginning
2008
+ assert_equal 'ActiveRecord::Person', ActiveRecord::Base.send(:type_name_with_module, 'Person')
2009
+ assert_equal '::Person', ActiveRecord::Base.send(:type_name_with_module, '::Person')
2010
+ end
2011
+
2012
+ def test_to_param_should_return_string
2013
+ assert_kind_of String, Client.find(:first).to_param
2014
+ end
2015
+
2016
+ def test_inspect_class
2017
+ assert_equal 'ActiveRecord::Base', ActiveRecord::Base.inspect
2018
+ assert_equal 'LoosePerson(abstract)', LoosePerson.inspect
2019
+ assert_match(/^Topic\(id: integer, title: string/, Topic.inspect)
2020
+ end
2021
+
2022
+ def test_inspect_instance
2023
+ topic = topics(:first)
2024
+ 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, parent_title: nil, type: nil>), topic.inspect
2025
+ end
2026
+
2027
+ def test_inspect_new_instance
2028
+ assert_match /Topic id: nil/, Topic.new.inspect
2029
+ end
2030
+
2031
+ def test_inspect_limited_select_instance
2032
+ assert_equal %(#<Topic id: 1>), Topic.find(:first, :select => 'id', :conditions => 'id = 1').inspect
2033
+ assert_equal %(#<Topic id: 1, title: "The First Topic">), Topic.find(:first, :select => 'id, title', :conditions => 'id = 1').inspect
2034
+ end
2035
+
2036
+ def test_inspect_class_without_table
2037
+ assert_equal "NonExistentTable(Table doesn't exist)", NonExistentTable.inspect
2038
+ end
2039
+
2040
+ def test_attribute_for_inspect
2041
+ t = topics(:first)
2042
+ t.title = "The First Topic Now Has A Title With\nNewlines And More Than 50 Characters"
2043
+
2044
+ assert_equal %("#{t.written_on.to_s(:db)}"), t.attribute_for_inspect(:written_on)
2045
+ assert_equal '"The First Topic Now Has A Title With\nNewlines And M..."', t.attribute_for_inspect(:title)
2046
+ end
2047
+
2048
+ def test_becomes
2049
+ assert_kind_of Reply, topics(:first).becomes(Reply)
2050
+ assert_equal "The First Topic", topics(:first).becomes(Reply).title
2051
+ end
2052
+
2053
+ def test_silence_sets_log_level_to_error_in_block
2054
+ original_logger = ActiveRecord::Base.logger
2055
+ log = StringIO.new
2056
+ ActiveRecord::Base.logger = Logger.new(log)
2057
+ ActiveRecord::Base.logger.level = Logger::DEBUG
2058
+ ActiveRecord::Base.silence do
2059
+ ActiveRecord::Base.logger.warn "warn"
2060
+ ActiveRecord::Base.logger.error "error"
2061
+ end
2062
+ assert_equal "error\n", log.string
2063
+ ensure
2064
+ ActiveRecord::Base.logger = original_logger
2065
+ end
2066
+
2067
+ def test_silence_sets_log_level_back_to_level_before_yield
2068
+ original_logger = ActiveRecord::Base.logger
2069
+ log = StringIO.new
2070
+ ActiveRecord::Base.logger = Logger.new(log)
2071
+ ActiveRecord::Base.logger.level = Logger::WARN
2072
+ ActiveRecord::Base.silence do
2073
+ end
2074
+ assert_equal Logger::WARN, ActiveRecord::Base.logger.level
2075
+ ensure
2076
+ ActiveRecord::Base.logger = original_logger
2077
+ end
2078
+
2079
+ def test_benchmark_with_log_level
2080
+ original_logger = ActiveRecord::Base.logger
2081
+ log = StringIO.new
2082
+ ActiveRecord::Base.logger = Logger.new(log)
2083
+ ActiveRecord::Base.logger.level = Logger::WARN
2084
+ ActiveRecord::Base.benchmark("Debug Topic Count", Logger::DEBUG) { Topic.count }
2085
+ ActiveRecord::Base.benchmark("Warn Topic Count", Logger::WARN) { Topic.count }
2086
+ ActiveRecord::Base.benchmark("Error Topic Count", Logger::ERROR) { Topic.count }
2087
+ assert_no_match /Debug Topic Count/, log.string
2088
+ assert_match /Warn Topic Count/, log.string
2089
+ assert_match /Error Topic Count/, log.string
2090
+ ensure
2091
+ ActiveRecord::Base.logger = original_logger
2092
+ end
2093
+
2094
+ def test_benchmark_with_use_silence
2095
+ original_logger = ActiveRecord::Base.logger
2096
+ log = StringIO.new
2097
+ ActiveRecord::Base.logger = Logger.new(log)
2098
+ ActiveRecord::Base.benchmark("Logging", Logger::DEBUG, true) { ActiveRecord::Base.logger.debug "Loud" }
2099
+ ActiveRecord::Base.benchmark("Logging", Logger::DEBUG, false) { ActiveRecord::Base.logger.debug "Quiet" }
2100
+ assert_no_match /Loud/, log.string
2101
+ assert_match /Quiet/, log.string
2102
+ ensure
2103
+ ActiveRecord::Base.logger = original_logger
2104
+ end
2105
+
2106
+ def test_create_with_custom_timestamps
2107
+ custom_datetime = 1.hour.ago.beginning_of_day
2108
+
2109
+ %w(created_at created_on updated_at updated_on).each do |attribute|
2110
+ parrot = LiveParrot.create(:name => "colombian", attribute => custom_datetime)
2111
+ assert_equal custom_datetime, parrot[attribute]
2112
+ end
2113
+ end
2114
+ end