db2 2.5.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.gitignore +1 -0
  2. data/CHANGES +181 -0
  3. data/LICENSE +18 -0
  4. data/MANIFEST +14 -0
  5. data/ParameterizedQueries README +39 -0
  6. data/README +282 -0
  7. data/ext/Makefile.nt32 +181 -0
  8. data/ext/extconf.rb +66 -0
  9. data/ext/ibm_db.c +11166 -0
  10. data/ext/ruby_ibm_db.h +236 -0
  11. data/ext/ruby_ibm_db_cli.c +738 -0
  12. data/ext/ruby_ibm_db_cli.h +431 -0
  13. data/init.rb +42 -0
  14. data/lib/IBM_DB.rb +2 -0
  15. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +2558 -0
  16. data/lib/active_record/connection_adapters/ibm_db_pstmt.rb +1965 -0
  17. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -0
  18. data/test/cases/adapter_test.rb +202 -0
  19. data/test/cases/associations/belongs_to_associations_test.rb +486 -0
  20. data/test/cases/associations/cascaded_eager_loading_test.rb +183 -0
  21. data/test/cases/associations/eager_test.rb +862 -0
  22. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +917 -0
  23. data/test/cases/associations/has_many_through_associations_test.rb +461 -0
  24. data/test/cases/associations/join_model_test.rb +793 -0
  25. data/test/cases/attribute_methods_test.rb +621 -0
  26. data/test/cases/base_test.rb +1486 -0
  27. data/test/cases/calculations_test.rb +362 -0
  28. data/test/cases/finder_test.rb +1088 -0
  29. data/test/cases/fixtures_test.rb +684 -0
  30. data/test/cases/migration_test.rb +2014 -0
  31. data/test/cases/schema_dumper_test.rb +232 -0
  32. data/test/cases/validations/uniqueness_validation_test.rb +283 -0
  33. data/test/connections/native_ibm_db/connection.rb +42 -0
  34. data/test/ibm_db_test.rb +25 -0
  35. data/test/models/warehouse_thing.rb +5 -0
  36. data/test/schema/i5/ibm_db_specific_schema.rb +135 -0
  37. data/test/schema/ids/ibm_db_specific_schema.rb +138 -0
  38. data/test/schema/luw/ibm_db_specific_schema.rb +135 -0
  39. data/test/schema/schema.rb +647 -0
  40. data/test/schema/zOS/ibm_db_specific_schema.rb +206 -0
  41. metadata +105 -0
@@ -0,0 +1,1486 @@
1
+ require "cases/helper"
2
+ require 'models/post'
3
+ require 'models/author'
4
+ require 'models/topic'
5
+ require 'models/reply'
6
+ require 'models/category'
7
+ require 'models/company'
8
+ require 'models/customer'
9
+ require 'models/developer'
10
+ require 'models/project'
11
+ require 'models/default'
12
+ require 'models/auto_id'
13
+ require 'models/boolean'
14
+ require 'models/column_name'
15
+ require 'models/subscriber'
16
+ require 'models/keyboard'
17
+ require 'models/comment'
18
+ require 'models/minimalistic'
19
+ require 'models/warehouse_thing'
20
+ require 'models/parrot'
21
+ require 'models/loose_person'
22
+ require 'rexml/document'
23
+ require 'active_support/core_ext/exception'
24
+
25
+ class Category < ActiveRecord::Base; end
26
+ class Categorization < ActiveRecord::Base; end
27
+ class Smarts < ActiveRecord::Base; end
28
+ class CreditCard < ActiveRecord::Base
29
+ class PinNumber < ActiveRecord::Base
30
+ class CvvCode < ActiveRecord::Base; end
31
+ class SubCvvCode < CvvCode; end
32
+ end
33
+ class SubPinNumber < PinNumber; end
34
+ class Brand < Category; end
35
+ end
36
+ class MasterCreditCard < ActiveRecord::Base; end
37
+ class Post < ActiveRecord::Base; end
38
+ class Computer < ActiveRecord::Base; end
39
+ class NonExistentTable < ActiveRecord::Base; end
40
+ class TestOracleDefault < ActiveRecord::Base; end
41
+
42
+ class ReadonlyTitlePost < Post
43
+ attr_readonly :title
44
+ end
45
+
46
+ class Boolean < ActiveRecord::Base; end
47
+
48
+ class BasicsTest < ActiveRecord::TestCase
49
+ fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, :warehouse_things, :authors, :categorizations, :categories, :posts
50
+
51
+ def test_table_exists
52
+ assert !NonExistentTable.table_exists?
53
+ assert Topic.table_exists?
54
+ end
55
+
56
+ def test_preserving_date_objects
57
+ if current_adapter?(:SybaseAdapter)
58
+ # Sybase ctlib does not (yet?) support the date type; use datetime instead.
59
+ assert_kind_of(
60
+ Time, Topic.find(1).last_read,
61
+ "The last_read attribute should be of the Time class"
62
+ )
63
+ else
64
+ # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb)
65
+ assert_kind_of(
66
+ Date, Topic.find(1).last_read,
67
+ "The last_read attribute should be of the Date class"
68
+ )
69
+ end
70
+ end
71
+
72
+ def test_use_table_engine_for_quoting_where
73
+ relation = Topic.where(Topic.arel_table[:id].eq(1))
74
+ engine = relation.table.engine
75
+
76
+ fakepool = Class.new(Struct.new(:spec)) {
77
+ def with_connection; yield self; end
78
+ def connection_pool; self; end
79
+ def quote_table_name(*args); raise "lol quote_table_name"; end
80
+ }
81
+
82
+ relation.table.engine = fakepool.new(engine.connection_pool.spec)
83
+
84
+ error = assert_raises(RuntimeError) { relation.to_a }
85
+ assert_match('lol', error.message)
86
+ ensure
87
+ relation.table.engine = engine
88
+ end
89
+
90
+ def test_preserving_time_objects
91
+ assert_kind_of(
92
+ Time, Topic.find(1).bonus_time,
93
+ "The bonus_time attribute should be of the Time class"
94
+ )
95
+
96
+ assert_kind_of(
97
+ Time, Topic.find(1).written_on,
98
+ "The written_on attribute should be of the Time class"
99
+ )
100
+
101
+ # For adapters which support microsecond resolution.
102
+ if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:SQLiteAdapter)
103
+ assert_equal 11, Topic.find(1).written_on.sec
104
+ assert_equal 223300, Topic.find(1).written_on.usec
105
+ assert_equal 9900, Topic.find(2).written_on.usec
106
+ end
107
+ end
108
+
109
+ def test_preserving_time_objects_with_local_time_conversion_to_default_timezone_utc
110
+ with_env_tz 'America/New_York' do
111
+ with_active_record_default_timezone :utc do
112
+ time = Time.local(2000)
113
+ topic = Topic.create('written_on' => time)
114
+ saved_time = Topic.find(topic.id).written_on
115
+ assert_equal time, saved_time
116
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "EST"], time.to_a
117
+ assert_equal [0, 0, 5, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a
118
+ end
119
+ end
120
+ end
121
+
122
+ def test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_utc
123
+ with_env_tz 'America/New_York' do
124
+ with_active_record_default_timezone :utc do
125
+ Time.use_zone 'Central Time (US & Canada)' do
126
+ time = Time.zone.local(2000)
127
+ topic = Topic.create('written_on' => time)
128
+ saved_time = Topic.find(topic.id).written_on
129
+ assert_equal time, saved_time
130
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a
131
+ assert_equal [0, 0, 6, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ def test_preserving_time_objects_with_utc_time_conversion_to_default_timezone_local
138
+ with_env_tz 'America/New_York' do
139
+ time = Time.utc(2000)
140
+ topic = Topic.create('written_on' => time)
141
+ saved_time = Topic.find(topic.id).written_on
142
+ assert_equal time, saved_time
143
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], time.to_a
144
+ assert_equal [0, 0, 19, 31, 12, 1999, 5, 365, false, "EST"], saved_time.to_a
145
+ end
146
+ end
147
+
148
+ def test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_local
149
+ with_env_tz 'America/New_York' do
150
+ with_active_record_default_timezone :local do
151
+ Time.use_zone 'Central Time (US & Canada)' do
152
+ time = Time.zone.local(2000)
153
+ topic = Topic.create('written_on' => time)
154
+ saved_time = Topic.find(topic.id).written_on
155
+ assert_equal time, saved_time
156
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a
157
+ assert_equal [0, 0, 1, 1, 1, 2000, 6, 1, false, "EST"], saved_time.to_a
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ def test_custom_mutator
164
+ topic = Topic.find(1)
165
+ # This mutator is protected in the class definition
166
+ topic.send(:approved=, true)
167
+ assert topic.instance_variable_get("@custom_approved")
168
+ end
169
+
170
+ def test_initialize_with_attributes
171
+ topic = Topic.new({
172
+ "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23"
173
+ })
174
+
175
+ assert_equal("initialized from attributes", topic.title)
176
+ end
177
+
178
+ def test_initialize_with_invalid_attribute
179
+ begin
180
+ topic = Topic.new({ "title" => "test",
181
+ "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"})
182
+ rescue ActiveRecord::MultiparameterAssignmentErrors => ex
183
+ assert_equal(1, ex.errors.size)
184
+ assert_equal("last_read", ex.errors[0].attribute)
185
+ end
186
+ end
187
+
188
+ def test_load
189
+ topics = Topic.find(:all, :order => 'id')
190
+ assert_equal(4, topics.size)
191
+ assert_equal(topics(:first).title, topics.first.title)
192
+ end
193
+
194
+ def test_load_with_condition
195
+ topics = Topic.find(:all, :conditions => "author_name = 'Mary'")
196
+
197
+ assert_equal(1, topics.size)
198
+ assert_equal(topics(:second).title, topics.first.title)
199
+ end
200
+
201
+ GUESSED_CLASSES = [Category, Smarts, CreditCard, CreditCard::PinNumber, CreditCard::PinNumber::CvvCode, CreditCard::SubPinNumber, CreditCard::Brand, MasterCreditCard]
202
+
203
+ def test_table_name_guesses
204
+ assert_equal "topics", Topic.table_name
205
+
206
+ assert_equal "categories", Category.table_name
207
+ assert_equal "smarts", Smarts.table_name
208
+ assert_equal "credit_cards", CreditCard.table_name
209
+ assert_equal "credit_card_pin_numbers", CreditCard::PinNumber.table_name
210
+ assert_equal "credit_card_pin_number_cvv_codes", CreditCard::PinNumber::CvvCode.table_name
211
+ assert_equal "credit_card_pin_numbers", CreditCard::SubPinNumber.table_name
212
+ assert_equal "categories", CreditCard::Brand.table_name
213
+ assert_equal "master_credit_cards", MasterCreditCard.table_name
214
+ ensure
215
+ GUESSED_CLASSES.each(&:reset_table_name)
216
+ end
217
+
218
+ def test_singular_table_name_guesses
219
+ ActiveRecord::Base.pluralize_table_names = false
220
+ GUESSED_CLASSES.each(&:reset_table_name)
221
+
222
+ assert_equal "category", Category.table_name
223
+ assert_equal "smarts", Smarts.table_name
224
+ assert_equal "credit_card", CreditCard.table_name
225
+ assert_equal "credit_card_pin_number", CreditCard::PinNumber.table_name
226
+ assert_equal "credit_card_pin_number_cvv_code", CreditCard::PinNumber::CvvCode.table_name
227
+ assert_equal "credit_card_pin_number", CreditCard::SubPinNumber.table_name
228
+ assert_equal "category", CreditCard::Brand.table_name
229
+ assert_equal "master_credit_card", MasterCreditCard.table_name
230
+ ensure
231
+ ActiveRecord::Base.pluralize_table_names = true
232
+ GUESSED_CLASSES.each(&:reset_table_name)
233
+ end
234
+
235
+ def test_table_name_guesses_with_prefixes_and_suffixes
236
+ ActiveRecord::Base.table_name_prefix = "test_"
237
+ Category.reset_table_name
238
+ assert_equal "test_categories", Category.table_name
239
+ ActiveRecord::Base.table_name_suffix = "_test"
240
+ Category.reset_table_name
241
+ assert_equal "test_categories_test", Category.table_name
242
+ ActiveRecord::Base.table_name_prefix = ""
243
+ Category.reset_table_name
244
+ assert_equal "categories_test", Category.table_name
245
+ ActiveRecord::Base.table_name_suffix = ""
246
+ Category.reset_table_name
247
+ assert_equal "categories", Category.table_name
248
+ ensure
249
+ ActiveRecord::Base.table_name_prefix = ""
250
+ ActiveRecord::Base.table_name_suffix = ""
251
+ GUESSED_CLASSES.each(&:reset_table_name)
252
+ end
253
+
254
+ def test_singular_table_name_guesses_with_prefixes_and_suffixes
255
+ ActiveRecord::Base.pluralize_table_names = false
256
+
257
+ ActiveRecord::Base.table_name_prefix = "test_"
258
+ Category.reset_table_name
259
+ assert_equal "test_category", Category.table_name
260
+ ActiveRecord::Base.table_name_suffix = "_test"
261
+ Category.reset_table_name
262
+ assert_equal "test_category_test", Category.table_name
263
+ ActiveRecord::Base.table_name_prefix = ""
264
+ Category.reset_table_name
265
+ assert_equal "category_test", Category.table_name
266
+ ActiveRecord::Base.table_name_suffix = ""
267
+ Category.reset_table_name
268
+ assert_equal "category", Category.table_name
269
+ ensure
270
+ ActiveRecord::Base.pluralize_table_names = true
271
+ ActiveRecord::Base.table_name_prefix = ""
272
+ ActiveRecord::Base.table_name_suffix = ""
273
+ GUESSED_CLASSES.each(&:reset_table_name)
274
+ end
275
+
276
+ def test_table_name_guesses_with_inherited_prefixes_and_suffixes
277
+ GUESSED_CLASSES.each(&:reset_table_name)
278
+
279
+ CreditCard.table_name_prefix = "test_"
280
+ CreditCard.reset_table_name
281
+ Category.reset_table_name
282
+ assert_equal "test_credit_cards", CreditCard.table_name
283
+ assert_equal "categories", Category.table_name
284
+ CreditCard.table_name_suffix = "_test"
285
+ CreditCard.reset_table_name
286
+ Category.reset_table_name
287
+ assert_equal "test_credit_cards_test", CreditCard.table_name
288
+ assert_equal "categories", Category.table_name
289
+ CreditCard.table_name_prefix = ""
290
+ CreditCard.reset_table_name
291
+ Category.reset_table_name
292
+ assert_equal "credit_cards_test", CreditCard.table_name
293
+ assert_equal "categories", Category.table_name
294
+ CreditCard.table_name_suffix = ""
295
+ CreditCard.reset_table_name
296
+ Category.reset_table_name
297
+ assert_equal "credit_cards", CreditCard.table_name
298
+ assert_equal "categories", Category.table_name
299
+ ensure
300
+ CreditCard.table_name_prefix = ""
301
+ CreditCard.table_name_suffix = ""
302
+ GUESSED_CLASSES.each(&:reset_table_name)
303
+ end
304
+
305
+
306
+ if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
307
+ def test_update_all_with_order_and_limit
308
+ assert_equal 1, Topic.update_all("content = 'bulk updated!'", nil, :limit => 1, :order => 'id DESC')
309
+ end
310
+ end
311
+
312
+ def test_null_fields
313
+ assert_nil Topic.find(1).parent_id
314
+ assert_nil Topic.create("title" => "Hey you").parent_id
315
+ end
316
+
317
+ def test_default_values
318
+ topic = Topic.new
319
+ assert topic.approved?
320
+ assert_nil topic.written_on
321
+ assert_nil topic.bonus_time
322
+ assert_nil topic.last_read
323
+
324
+ topic.save
325
+
326
+ topic = Topic.find(topic.id)
327
+ assert topic.approved?
328
+ assert_nil topic.last_read
329
+
330
+ # Oracle has some funky default handling, so it requires a bit of
331
+ # extra testing. See ticket #2788.
332
+ if current_adapter?(:OracleAdapter)
333
+ test = TestOracleDefault.new
334
+ assert_equal "X", test.test_char
335
+ assert_equal "hello", test.test_string
336
+ assert_equal 3, test.test_int
337
+ end
338
+ end
339
+
340
+ # Oracle, and Sybase do not have a TIME datatype.
341
+ unless current_adapter?(:OracleAdapter, :SybaseAdapter)
342
+ def test_utc_as_time_zone
343
+ Topic.default_timezone = :utc
344
+ attributes = { "bonus_time" => "5:42:00AM" }
345
+ topic = Topic.find(1)
346
+ topic.attributes = attributes
347
+ assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time
348
+ Topic.default_timezone = :local
349
+ end
350
+
351
+ def test_utc_as_time_zone_and_new
352
+ Topic.default_timezone = :utc
353
+ attributes = { "bonus_time(1i)"=>"2000",
354
+ "bonus_time(2i)"=>"1",
355
+ "bonus_time(3i)"=>"1",
356
+ "bonus_time(4i)"=>"10",
357
+ "bonus_time(5i)"=>"35",
358
+ "bonus_time(6i)"=>"50" }
359
+ topic = Topic.new(attributes)
360
+ assert_equal Time.utc(2000, 1, 1, 10, 35, 50), topic.bonus_time
361
+ Topic.default_timezone = :local
362
+ end
363
+ end
364
+
365
+ def test_default_values_on_empty_strings
366
+ topic = Topic.new
367
+ topic.approved = nil
368
+ topic.last_read = nil
369
+
370
+ topic.save
371
+
372
+ topic = Topic.find(topic.id)
373
+ assert_nil topic.last_read
374
+
375
+ # Sybase adapter does not allow nulls in boolean columns
376
+ if current_adapter?(:SybaseAdapter)
377
+ assert topic.approved == false
378
+ else
379
+ assert_nil topic.approved
380
+ end
381
+ end
382
+
383
+ def test_equality
384
+ assert_equal Topic.find(1), Topic.find(2).topic
385
+ end
386
+
387
+ def test_equality_of_new_records
388
+ assert_not_equal Topic.new, Topic.new
389
+ end
390
+
391
+ def test_equality_of_destroyed_records
392
+ topic_1 = Topic.new(:title => 'test_1')
393
+ topic_1.save
394
+ topic_2 = Topic.find(topic_1.id)
395
+ topic_1.destroy
396
+ assert_equal topic_1, topic_2
397
+ assert_equal topic_2, topic_1
398
+ end
399
+
400
+ def test_hashing
401
+ assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
402
+ end
403
+
404
+ def test_readonly_attributes
405
+ assert_equal Set.new([ 'title' , 'comments_count' ]), ReadonlyTitlePost.readonly_attributes
406
+
407
+ post = ReadonlyTitlePost.create(:title => "cannot change this", :body => "changeable")
408
+ post.reload
409
+ assert_equal "cannot change this", post.title
410
+
411
+ post.update_attributes(:title => "try to change", :body => "changed")
412
+ post.reload
413
+ assert_equal "cannot change this", post.title
414
+ assert_equal "changed", post.body
415
+ end
416
+
417
+ def test_multiparameter_attributes_on_date
418
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
419
+ topic = Topic.find(1)
420
+ topic.attributes = attributes
421
+ # note that extra #to_date call allows test to pass for Oracle, which
422
+ # treats dates/times the same
423
+ assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date
424
+ end
425
+
426
+ def test_multiparameter_attributes_on_date_with_empty_year
427
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "24" }
428
+ topic = Topic.find(1)
429
+ topic.attributes = attributes
430
+ # note that extra #to_date call allows test to pass for Oracle, which
431
+ # treats dates/times the same
432
+ assert_date_from_db Date.new(1, 6, 24), topic.last_read.to_date
433
+ end
434
+
435
+ def test_multiparameter_attributes_on_date_with_empty_month
436
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "24" }
437
+ topic = Topic.find(1)
438
+ topic.attributes = attributes
439
+ # note that extra #to_date call allows test to pass for Oracle, which
440
+ # treats dates/times the same
441
+ assert_date_from_db Date.new(2004, 1, 24), topic.last_read.to_date
442
+ end
443
+
444
+ def test_multiparameter_attributes_on_date_with_empty_day
445
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
446
+ topic = Topic.find(1)
447
+ topic.attributes = attributes
448
+ # note that extra #to_date call allows test to pass for Oracle, which
449
+ # treats dates/times the same
450
+ assert_date_from_db Date.new(2004, 6, 1), topic.last_read.to_date
451
+ end
452
+
453
+ def test_multiparameter_attributes_on_date_with_empty_day_and_year
454
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "" }
455
+ topic = Topic.find(1)
456
+ topic.attributes = attributes
457
+ # note that extra #to_date call allows test to pass for Oracle, which
458
+ # treats dates/times the same
459
+ assert_date_from_db Date.new(1, 6, 1), topic.last_read.to_date
460
+ end
461
+
462
+ def test_multiparameter_attributes_on_date_with_empty_day_and_month
463
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "" }
464
+ topic = Topic.find(1)
465
+ topic.attributes = attributes
466
+ # note that extra #to_date call allows test to pass for Oracle, which
467
+ # treats dates/times the same
468
+ assert_date_from_db Date.new(2004, 1, 1), topic.last_read.to_date
469
+ end
470
+
471
+ def test_multiparameter_attributes_on_date_with_empty_year_and_month
472
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "24" }
473
+ topic = Topic.find(1)
474
+ topic.attributes = attributes
475
+ # note that extra #to_date call allows test to pass for Oracle, which
476
+ # treats dates/times the same
477
+ assert_date_from_db Date.new(1, 1, 24), topic.last_read.to_date
478
+ end
479
+
480
+ def test_multiparameter_attributes_on_date_with_all_empty
481
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" }
482
+ topic = Topic.find(1)
483
+ topic.attributes = attributes
484
+ assert_nil topic.last_read
485
+ end
486
+
487
+ def test_multiparameter_attributes_on_time
488
+ attributes = {
489
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
490
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
491
+ }
492
+ topic = Topic.find(1)
493
+ topic.attributes = attributes
494
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
495
+ end
496
+
497
+ def test_multiparameter_attributes_on_time_with_old_date
498
+ attributes = {
499
+ "written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24",
500
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
501
+ }
502
+ topic = Topic.find(1)
503
+ topic.attributes = attributes
504
+ # testing against to_s(:db) representation because either a Time or a DateTime might be returned, depending on platform
505
+ assert_equal "1850-06-24 16:24:00", topic.written_on.to_s(:db)
506
+ end
507
+
508
+ def test_multiparameter_attributes_on_time_with_utc
509
+ ActiveRecord::Base.default_timezone = :utc
510
+ attributes = {
511
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
512
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
513
+ }
514
+ topic = Topic.find(1)
515
+ topic.attributes = attributes
516
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
517
+ ensure
518
+ ActiveRecord::Base.default_timezone = :local
519
+ end
520
+
521
+ def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes
522
+ ActiveRecord::Base.time_zone_aware_attributes = true
523
+ ActiveRecord::Base.default_timezone = :utc
524
+ Time.zone = ActiveSupport::TimeZone[-28800]
525
+ attributes = {
526
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
527
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
528
+ }
529
+ topic = Topic.find(1)
530
+ topic.attributes = attributes
531
+ assert_equal Time.utc(2004, 6, 24, 23, 24, 0), topic.written_on
532
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on.time
533
+ assert_equal Time.zone, topic.written_on.time_zone
534
+ ensure
535
+ ActiveRecord::Base.time_zone_aware_attributes = false
536
+ ActiveRecord::Base.default_timezone = :local
537
+ Time.zone = nil
538
+ end
539
+
540
+ def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false
541
+ ActiveRecord::Base.time_zone_aware_attributes = false
542
+ Time.zone = ActiveSupport::TimeZone[-28800]
543
+ attributes = {
544
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
545
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
546
+ }
547
+ topic = Topic.find(1)
548
+ topic.attributes = attributes
549
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
550
+ assert_equal false, topic.written_on.respond_to?(:time_zone)
551
+ ensure
552
+ Time.zone = nil
553
+ end
554
+
555
+ def test_multiparameter_attributes_on_time_with_skip_time_zone_conversion_for_attributes
556
+ ActiveRecord::Base.time_zone_aware_attributes = true
557
+ ActiveRecord::Base.default_timezone = :utc
558
+ Time.zone = ActiveSupport::TimeZone[-28800]
559
+ Topic.skip_time_zone_conversion_for_attributes = [:written_on]
560
+ attributes = {
561
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
562
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
563
+ }
564
+ topic = Topic.find(1)
565
+ topic.attributes = attributes
566
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
567
+ assert_equal false, topic.written_on.respond_to?(:time_zone)
568
+ ensure
569
+ ActiveRecord::Base.time_zone_aware_attributes = false
570
+ ActiveRecord::Base.default_timezone = :local
571
+ Time.zone = nil
572
+ Topic.skip_time_zone_conversion_for_attributes = []
573
+ end
574
+
575
+ # Oracle, and Sybase do not have a TIME datatype.
576
+ unless current_adapter?(:OracleAdapter, :SybaseAdapter)
577
+ def test_multiparameter_attributes_on_time_only_column_with_time_zone_aware_attributes_does_not_do_time_zone_conversion
578
+ ActiveRecord::Base.time_zone_aware_attributes = true
579
+ ActiveRecord::Base.default_timezone = :utc
580
+ Time.zone = ActiveSupport::TimeZone[-28800]
581
+ attributes = {
582
+ "bonus_time(1i)" => "2000", "bonus_time(2i)" => "1", "bonus_time(3i)" => "1",
583
+ "bonus_time(4i)" => "16", "bonus_time(5i)" => "24"
584
+ }
585
+ topic = Topic.find(1)
586
+ topic.attributes = attributes
587
+ assert_equal Time.utc(2000, 1, 1, 16, 24, 0), topic.bonus_time
588
+ assert topic.bonus_time.utc?
589
+ ensure
590
+ ActiveRecord::Base.time_zone_aware_attributes = false
591
+ ActiveRecord::Base.default_timezone = :local
592
+ Time.zone = nil
593
+ end
594
+ end
595
+
596
+ def test_multiparameter_attributes_on_time_with_empty_seconds
597
+ attributes = {
598
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
599
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => ""
600
+ }
601
+ topic = Topic.find(1)
602
+ topic.attributes = attributes
603
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
604
+ end
605
+
606
+ def test_multiparameter_assignment_of_aggregation
607
+ customer = Customer.new
608
+ address = Address.new("The Street", "The City", "The Country")
609
+ attributes = { "address(1)" => address.street, "address(2)" => address.city, "address(3)" => address.country }
610
+ customer.attributes = attributes
611
+ assert_equal address, customer.address
612
+ end
613
+
614
+ def test_attributes_on_dummy_time
615
+ # Oracle, and Sybase do not have a TIME datatype.
616
+ return true if current_adapter?(:OracleAdapter, :SybaseAdapter)
617
+
618
+ attributes = {
619
+ "bonus_time" => "5:42:00AM"
620
+ }
621
+ topic = Topic.find(1)
622
+ topic.attributes = attributes
623
+ assert_equal Time.local(2000, 1, 1, 5, 42, 0), topic.bonus_time
624
+ end
625
+
626
+ def test_boolean
627
+ b_nil = Boolean.create({ "value" => nil })
628
+ nil_id = b_nil.id
629
+ b_false = Boolean.create({ "value" => false })
630
+ false_id = b_false.id
631
+ b_true = Boolean.create({ "value" => true })
632
+ true_id = b_true.id
633
+
634
+ b_nil = Boolean.find(nil_id)
635
+ assert_nil b_nil.value
636
+ b_false = Boolean.find(false_id)
637
+ assert !b_false.value?
638
+ b_true = Boolean.find(true_id)
639
+ assert b_true.value?
640
+ end
641
+
642
+ def test_boolean_cast_from_string
643
+ b_blank = Boolean.create({ "value" => "" })
644
+ blank_id = b_blank.id
645
+ b_false = Boolean.create({ "value" => "0" })
646
+ false_id = b_false.id
647
+ b_true = Boolean.create({ "value" => "1" })
648
+ true_id = b_true.id
649
+
650
+ b_blank = Boolean.find(blank_id)
651
+ assert_nil b_blank.value
652
+ b_false = Boolean.find(false_id)
653
+ assert !b_false.value?
654
+ b_true = Boolean.find(true_id)
655
+ assert b_true.value?
656
+ end
657
+
658
+ def test_new_record_returns_boolean
659
+ assert_equal false, Topic.new.persisted?
660
+ assert_equal true, Topic.find(1).persisted?
661
+ end
662
+
663
+ def test_clone
664
+ topic = Topic.find(1)
665
+ cloned_topic = nil
666
+ assert_nothing_raised { cloned_topic = topic.clone }
667
+ assert_equal topic.title, cloned_topic.title
668
+ assert !cloned_topic.persisted?
669
+
670
+ # test if the attributes have been cloned
671
+ topic.title = "a"
672
+ cloned_topic.title = "b"
673
+ assert_equal "a", topic.title
674
+ assert_equal "b", cloned_topic.title
675
+
676
+ # test if the attribute values have been cloned
677
+ topic.title = {"a" => "b"}
678
+ cloned_topic = topic.clone
679
+ cloned_topic.title["a"] = "c"
680
+ assert_equal "b", topic.title["a"]
681
+
682
+ # test if attributes set as part of after_initialize are cloned correctly
683
+ assert_equal topic.author_email_address, cloned_topic.author_email_address
684
+
685
+ # test if saved clone object differs from original
686
+ cloned_topic.save
687
+ assert cloned_topic.persisted?
688
+ assert_not_equal cloned_topic.id, topic.id
689
+ end
690
+
691
+ def test_clone_with_aggregate_of_same_name_as_attribute
692
+ dev = DeveloperWithAggregate.find(1)
693
+ assert_kind_of DeveloperSalary, dev.salary
694
+
695
+ clone = nil
696
+ assert_nothing_raised { clone = dev.clone }
697
+ assert_kind_of DeveloperSalary, clone.salary
698
+ assert_equal dev.salary.amount, clone.salary.amount
699
+ assert !clone.persisted?
700
+
701
+ # test if the attributes have been cloned
702
+ original_amount = clone.salary.amount
703
+ dev.salary.amount = 1
704
+ assert_equal original_amount, clone.salary.amount
705
+
706
+ assert clone.save
707
+ assert clone.persisted?
708
+ assert_not_equal clone.id, dev.id
709
+ end
710
+
711
+ def test_clone_does_not_clone_associations
712
+ author = authors(:david)
713
+ assert_not_equal [], author.posts
714
+
715
+ author_clone = author.clone
716
+ assert_equal [], author_clone.posts
717
+ end
718
+
719
+ def test_clone_preserves_subtype
720
+ clone = nil
721
+ assert_nothing_raised { clone = Company.find(3).clone }
722
+ assert_kind_of Client, clone
723
+ end
724
+
725
+ def test_clone_of_new_object_with_defaults
726
+ developer = Developer.new
727
+ assert !developer.name_changed?
728
+ assert !developer.salary_changed?
729
+
730
+ cloned_developer = developer.clone
731
+ assert !cloned_developer.name_changed?
732
+ assert !cloned_developer.salary_changed?
733
+ end
734
+
735
+ def test_clone_of_new_object_marks_attributes_as_dirty
736
+ developer = Developer.new :name => 'Bjorn', :salary => 100000
737
+ assert developer.name_changed?
738
+ assert developer.salary_changed?
739
+
740
+ cloned_developer = developer.clone
741
+ assert cloned_developer.name_changed?
742
+ assert cloned_developer.salary_changed?
743
+ end
744
+
745
+ def test_clone_of_new_object_marks_as_dirty_only_changed_attributes
746
+ developer = Developer.new :name => 'Bjorn'
747
+ assert developer.name_changed? # obviously
748
+ assert !developer.salary_changed? # attribute has non-nil default value, so treated as not changed
749
+
750
+ cloned_developer = developer.clone
751
+ assert cloned_developer.name_changed?
752
+ assert !cloned_developer.salary_changed? # ... and cloned instance should behave same
753
+ end
754
+
755
+ def test_clone_of_saved_object_marks_attributes_as_dirty
756
+ developer = Developer.create! :name => 'Bjorn', :salary => 100000
757
+ assert !developer.name_changed?
758
+ assert !developer.salary_changed?
759
+
760
+ cloned_developer = developer.clone
761
+ assert cloned_developer.name_changed? # both attributes differ from defaults
762
+ assert cloned_developer.salary_changed?
763
+ end
764
+
765
+ def test_clone_of_saved_object_marks_as_dirty_only_changed_attributes
766
+ developer = Developer.create! :name => 'Bjorn'
767
+ assert !developer.name_changed? # both attributes of saved object should be threated as not changed
768
+ assert !developer.salary_changed?
769
+
770
+ cloned_developer = developer.clone
771
+ assert cloned_developer.name_changed? # ... but on cloned object should be
772
+ assert !cloned_developer.salary_changed? # ... BUT salary has non-nil default which should be threated as not changed on cloned instance
773
+ end
774
+
775
+ def test_bignum
776
+ company = Company.find(1)
777
+ company.rating = 2147483647
778
+ company.save
779
+ company = Company.find(1)
780
+ assert_equal 2147483647, company.rating
781
+ end
782
+
783
+ # TODO: extend defaults tests to other databases!
784
+ if current_adapter?(:PostgreSQLAdapter)
785
+ def test_default
786
+ default = Default.new
787
+
788
+ # fixed dates / times
789
+ assert_equal Date.new(2004, 1, 1), default.fixed_date
790
+ assert_equal Time.local(2004, 1,1,0,0,0,0), default.fixed_time
791
+
792
+ # char types
793
+ assert_equal 'Y', default.char1
794
+ assert_equal 'a varchar field', default.char2
795
+ assert_equal 'a text field', default.char3
796
+ end
797
+
798
+ class Geometric < ActiveRecord::Base; end
799
+ def test_geometric_content
800
+
801
+ # accepted format notes:
802
+ # ()'s aren't required
803
+ # values can be a mix of float or integer
804
+
805
+ g = Geometric.new(
806
+ :a_point => '(5.0, 6.1)',
807
+ #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
808
+ :a_line_segment => '(2.0, 3), (5.5, 7.0)',
809
+ :a_box => '2.0, 3, 5.5, 7.0',
810
+ :a_path => '[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]', # [ ] is an open path
811
+ :a_polygon => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))',
812
+ :a_circle => '<(5.3, 10.4), 2>'
813
+ )
814
+
815
+ assert g.save
816
+
817
+ # Reload and check that we have all the geometric attributes.
818
+ h = Geometric.find(g.id)
819
+
820
+ assert_equal '(5,6.1)', h.a_point
821
+ assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
822
+ assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
823
+ assert_equal '[(2,3),(5.5,7),(8.5,11)]', h.a_path
824
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
825
+ assert_equal '<(5.3,10.4),2>', h.a_circle
826
+
827
+ # use a geometric function to test for an open path
828
+ objs = Geometric.find_by_sql ["select isopen(a_path) from geometrics where id = ?", g.id]
829
+ assert_equal objs[0].isopen, 't'
830
+
831
+ # test alternate formats when defining the geometric types
832
+
833
+ g = Geometric.new(
834
+ :a_point => '5.0, 6.1',
835
+ #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
836
+ :a_line_segment => '((2.0, 3), (5.5, 7.0))',
837
+ :a_box => '(2.0, 3), (5.5, 7.0)',
838
+ :a_path => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))', # ( ) is a closed path
839
+ :a_polygon => '2.0, 3, 5.5, 7.0, 8.5, 11.0',
840
+ :a_circle => '((5.3, 10.4), 2)'
841
+ )
842
+
843
+ assert g.save
844
+
845
+ # Reload and check that we have all the geometric attributes.
846
+ h = Geometric.find(g.id)
847
+
848
+ assert_equal '(5,6.1)', h.a_point
849
+ assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
850
+ assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
851
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path
852
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
853
+ assert_equal '<(5.3,10.4),2>', h.a_circle
854
+
855
+ # use a geometric function to test for an closed path
856
+ objs = Geometric.find_by_sql ["select isclosed(a_path) from geometrics where id = ?", g.id]
857
+ assert_equal objs[0].isclosed, 't'
858
+ end
859
+ end
860
+
861
+ class NumericData < ActiveRecord::Base
862
+ self.table_name = 'numeric_data'
863
+ end
864
+
865
+ def test_numeric_fields
866
+ m = NumericData.new(
867
+ :bank_balance => 1586.43,
868
+ :big_bank_balance => BigDecimal("1000234000567.95"),
869
+ :world_population => 6000000000,
870
+ :my_house_population => 3
871
+ )
872
+ assert m.save
873
+
874
+ m1 = NumericData.find(m.id)
875
+ assert_not_nil m1
876
+
877
+ # As with migration_test.rb, we should make world_population >= 2**62
878
+ # to cover 64-bit platforms and test it is a Bignum, but the main thing
879
+ # is that it's an Integer.
880
+ unless current_adapter?(:IBM_DBAdapter)
881
+ assert_kind_of Integer, m1.world_population
882
+ else
883
+ assert_kind_of BigDecimal, m1.world_population
884
+ end
885
+ assert_equal 6000000000, m1.world_population
886
+ unless current_adapter?(:IBM_DBAdapter)
887
+ assert_kind_of Fixnum, m1.my_house_population
888
+ else
889
+ assert_kind_of BigDecimal, m1.my_house_population
890
+ end
891
+ assert_equal 3, m1.my_house_population
892
+
893
+ assert_kind_of BigDecimal, m1.bank_balance
894
+ assert_equal BigDecimal("1586.43"), m1.bank_balance
895
+
896
+ assert_kind_of BigDecimal, m1.big_bank_balance
897
+ assert_equal BigDecimal("1000234000567.95"), m1.big_bank_balance
898
+ end
899
+
900
+ def test_auto_id
901
+ auto = AutoId.new
902
+ auto.save
903
+ assert(auto.id > 0)
904
+ end
905
+
906
+ def quote_column_name(name)
907
+ "<#{name}>"
908
+ end
909
+
910
+ def test_quote_keys
911
+ ar = AutoId.new
912
+ source = {"foo" => "bar", "baz" => "quux"}
913
+ actual = ar.send(:quote_columns, self, source)
914
+ inverted = actual.invert
915
+ assert_equal("<foo>", inverted["bar"])
916
+ assert_equal("<baz>", inverted["quux"])
917
+ end
918
+
919
+ def test_sql_injection_via_find
920
+ assert_raise(ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid) do
921
+ Topic.find("123456 OR id > 0")
922
+ end
923
+ end
924
+
925
+ def test_column_name_properly_quoted
926
+ col_record = ColumnName.new
927
+ col_record.references = 40
928
+ assert col_record.save
929
+ col_record.references = 41
930
+ assert col_record.save
931
+ assert_not_nil c2 = ColumnName.find(col_record.id)
932
+ assert_equal(41, c2.references)
933
+ end
934
+
935
+ def test_quoting_arrays
936
+ replies = Reply.find(:all, :conditions => [ "id IN (?)", topics(:first).replies.collect(&:id) ])
937
+ assert_equal topics(:first).replies.size, replies.size
938
+
939
+ replies = Reply.find(:all, :conditions => [ "id IN (?)", [] ])
940
+ assert_equal 0, replies.size
941
+ end
942
+
943
+ MyObject = Struct.new :attribute1, :attribute2
944
+
945
+ def test_serialized_attribute
946
+ Topic.serialize("content", MyObject)
947
+
948
+ myobj = MyObject.new('value1', 'value2')
949
+ topic = Topic.create("content" => myobj)
950
+ assert_equal(myobj, topic.content)
951
+
952
+ topic.reload
953
+ assert_equal(myobj, topic.content)
954
+ end
955
+
956
+ def test_serialized_time_attribute
957
+ myobj = Time.local(2008,1,1,1,0)
958
+ topic = Topic.create("content" => myobj).reload
959
+ assert_equal(myobj, topic.content)
960
+ end
961
+
962
+ def test_serialized_string_attribute
963
+ myobj = "Yes"
964
+ topic = Topic.create("content" => myobj).reload
965
+ assert_equal(myobj, topic.content)
966
+ end
967
+
968
+ def test_nil_serialized_attribute_with_class_constraint
969
+ myobj = MyObject.new('value1', 'value2')
970
+ topic = Topic.new
971
+ assert_nil topic.content
972
+ end
973
+
974
+ def test_should_raise_exception_on_serialized_attribute_with_type_mismatch
975
+ myobj = MyObject.new('value1', 'value2')
976
+ topic = Topic.new(:content => myobj)
977
+ assert topic.save
978
+ Topic.serialize(:content, Hash)
979
+ assert_raise(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).content }
980
+ ensure
981
+ Topic.serialize(:content)
982
+ end
983
+
984
+ def test_serialized_attribute_with_class_constraint
985
+ settings = { "color" => "blue" }
986
+ Topic.serialize(:content, Hash)
987
+ topic = Topic.new(:content => settings)
988
+ assert topic.save
989
+ assert_equal(settings, Topic.find(topic.id).content)
990
+ ensure
991
+ Topic.serialize(:content)
992
+ end
993
+
994
+ def test_quote
995
+ author_name = "\\ \001 ' \n \\n \""
996
+ topic = Topic.create('author_name' => author_name)
997
+ assert_equal author_name, Topic.find(topic.id).author_name
998
+ end
999
+
1000
+ if RUBY_VERSION < '1.9'
1001
+ def test_quote_chars
1002
+ with_kcode('UTF8') do
1003
+ str = 'The Narrator'
1004
+ topic = Topic.create(:author_name => str)
1005
+ assert_equal str, topic.author_name
1006
+
1007
+ assert_kind_of ActiveSupport::Multibyte.proxy_class, str.mb_chars
1008
+ topic = Topic.find_by_author_name(str.mb_chars)
1009
+
1010
+ assert_kind_of Topic, topic
1011
+ assert_equal str, topic.author_name, "The right topic should have been found by name even with name passed as Chars"
1012
+ end
1013
+ end
1014
+ end
1015
+
1016
+ def test_toggle_attribute
1017
+ assert !topics(:first).approved?
1018
+ topics(:first).toggle!(:approved)
1019
+ assert topics(:first).approved?
1020
+ topic = topics(:first)
1021
+ topic.toggle(:approved)
1022
+ assert !topic.approved?
1023
+ topic.reload
1024
+ assert topic.approved?
1025
+ end
1026
+
1027
+ def test_reload
1028
+ t1 = Topic.find(1)
1029
+ t2 = Topic.find(1)
1030
+ t1.title = "something else"
1031
+ t1.save
1032
+ t2.reload
1033
+ assert_equal t1.title, t2.title
1034
+ end
1035
+
1036
+ def test_reload_with_exclusive_scope
1037
+ dev = DeveloperCalledDavid.first
1038
+ dev.update_attributes!( :name => "NotDavid" )
1039
+ assert_equal dev, dev.reload
1040
+ end
1041
+
1042
+ def test_define_attr_method_with_value
1043
+ k = Class.new( ActiveRecord::Base )
1044
+ k.send(:define_attr_method, :table_name, "foo")
1045
+ assert_equal "foo", k.table_name
1046
+ end
1047
+
1048
+ def test_define_attr_method_with_block
1049
+ k = Class.new( ActiveRecord::Base )
1050
+ k.send(:define_attr_method, :primary_key) { "sys_" + original_primary_key }
1051
+ assert_equal "sys_id", k.primary_key
1052
+ end
1053
+
1054
+ def test_set_table_name_with_value
1055
+ k = Class.new( ActiveRecord::Base )
1056
+ k.table_name = "foo"
1057
+ assert_equal "foo", k.table_name
1058
+ k.set_table_name "bar"
1059
+ assert_equal "bar", k.table_name
1060
+ end
1061
+
1062
+ def test_quoted_table_name_after_set_table_name
1063
+ klass = Class.new(ActiveRecord::Base)
1064
+
1065
+ klass.set_table_name "foo"
1066
+ assert_equal "foo", klass.table_name
1067
+ assert_equal klass.connection.quote_table_name("foo"), klass.quoted_table_name
1068
+
1069
+ klass.set_table_name "bar"
1070
+ assert_equal "bar", klass.table_name
1071
+ assert_equal klass.connection.quote_table_name("bar"), klass.quoted_table_name
1072
+ end
1073
+
1074
+ def test_set_table_name_with_block
1075
+ k = Class.new( ActiveRecord::Base )
1076
+ k.set_table_name { "ks" }
1077
+ assert_equal "ks", k.table_name
1078
+ end
1079
+
1080
+ def test_set_primary_key_with_value
1081
+ k = Class.new( ActiveRecord::Base )
1082
+ k.primary_key = "foo"
1083
+ assert_equal "foo", k.primary_key
1084
+ k.set_primary_key "bar"
1085
+ assert_equal "bar", k.primary_key
1086
+ end
1087
+
1088
+ def test_set_primary_key_with_block
1089
+ k = Class.new( ActiveRecord::Base )
1090
+ k.set_primary_key { "sys_" + original_primary_key }
1091
+ assert_equal "sys_id", k.primary_key
1092
+ end
1093
+
1094
+ def test_set_inheritance_column_with_value
1095
+ k = Class.new( ActiveRecord::Base )
1096
+ k.inheritance_column = "foo"
1097
+ assert_equal "foo", k.inheritance_column
1098
+ k.set_inheritance_column "bar"
1099
+ assert_equal "bar", k.inheritance_column
1100
+ end
1101
+
1102
+ def test_set_inheritance_column_with_block
1103
+ k = Class.new( ActiveRecord::Base )
1104
+ k.set_inheritance_column { original_inheritance_column + "_id" }
1105
+ assert_equal "type_id", k.inheritance_column
1106
+ end
1107
+
1108
+ def test_count_with_join
1109
+ res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
1110
+
1111
+ res2 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'", :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1112
+ assert_equal res, res2
1113
+
1114
+ res3 = nil
1115
+ assert_nothing_raised do
1116
+ res3 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'",
1117
+ :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1118
+ end
1119
+ assert_equal res, res3
1120
+
1121
+ 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"
1122
+ res5 = nil
1123
+ assert_nothing_raised do
1124
+ res5 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
1125
+ :joins => "p, comments co",
1126
+ :select => "p.id")
1127
+ end
1128
+
1129
+ assert_equal res4, res5
1130
+
1131
+ 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"
1132
+ res7 = nil
1133
+ assert_nothing_raised do
1134
+ res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
1135
+ :joins => "p, comments co",
1136
+ :select => "p.id",
1137
+ :distinct => true)
1138
+ end
1139
+ assert_equal res6, res7
1140
+ end
1141
+
1142
+ def test_interpolate_sql
1143
+ assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo@bar') }
1144
+ assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar) baz') }
1145
+ assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar} baz') }
1146
+ end
1147
+
1148
+ def test_scoped_find_conditions
1149
+ scoped_developers = Developer.send(:with_scope, :find => { :conditions => 'salary > 90000' }) do
1150
+ Developer.find(:all, :conditions => 'id < 5')
1151
+ end
1152
+ assert !scoped_developers.include?(developers(:david)) # David's salary is less than 90,000
1153
+ assert_equal 3, scoped_developers.size
1154
+ end
1155
+
1156
+ def test_scoped_find_limit_offset
1157
+ scoped_developers = Developer.send(:with_scope, :find => { :limit => 3, :offset => 2 }) do
1158
+ Developer.find(:all, :order => 'id')
1159
+ end
1160
+ assert !scoped_developers.include?(developers(:david))
1161
+ assert !scoped_developers.include?(developers(:jamis))
1162
+ assert_equal 3, scoped_developers.size
1163
+
1164
+ # Test without scoped find conditions to ensure we get the whole thing
1165
+ developers = Developer.find(:all, :order => 'id')
1166
+ assert_equal Developer.count, developers.size
1167
+ end
1168
+
1169
+ def test_scoped_find_order
1170
+ # Test order in scope
1171
+ scoped_developers = Developer.send(:with_scope, :find => { :limit => 1, :order => 'salary DESC' }) do
1172
+ Developer.find(:all)
1173
+ end
1174
+ assert_equal 'Jamis', scoped_developers.first.name
1175
+ assert scoped_developers.include?(developers(:jamis))
1176
+ # Test scope without order and order in find
1177
+ scoped_developers = Developer.send(:with_scope, :find => { :limit => 1 }) do
1178
+ Developer.find(:all, :order => 'salary DESC')
1179
+ end
1180
+ # Test scope order + find order, order has priority
1181
+ scoped_developers = Developer.send(:with_scope, :find => { :limit => 3, :order => 'id DESC' }) do
1182
+ Developer.find(:all, :order => 'salary ASC')
1183
+ end
1184
+ assert scoped_developers.include?(developers(:poor_jamis))
1185
+ assert ! scoped_developers.include?(developers(:david))
1186
+ assert ! scoped_developers.include?(developers(:jamis))
1187
+ assert_equal 3, scoped_developers.size
1188
+
1189
+ # Test without scoped find conditions to ensure we get the right thing
1190
+ assert ! scoped_developers.include?(Developer.find(1))
1191
+ assert scoped_developers.include?(Developer.find(11))
1192
+ end
1193
+
1194
+ def test_scoped_find_limit_offset_including_has_many_association
1195
+ topics = Topic.send(:with_scope, :find => {:limit => 1, :offset => 1, :include => :replies}) do
1196
+ Topic.find(:all, :order => "topics.id")
1197
+ end
1198
+ assert_equal 1, topics.size
1199
+ assert_equal 2, topics.first.id
1200
+ end
1201
+
1202
+ def test_scoped_find_order_including_has_many_association
1203
+ developers = Developer.send(:with_scope, :find => { :order => 'developers.salary DESC', :include => :projects }) do
1204
+ Developer.find(:all)
1205
+ end
1206
+ assert developers.size >= 2
1207
+ for i in 1...developers.size
1208
+ assert developers[i-1].salary >= developers[i].salary
1209
+ end
1210
+ end
1211
+
1212
+ def test_scoped_find_with_group_and_having
1213
+ developers = Developer.send(:with_scope, :find => { :group => 'developers.salary', :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" }) do
1214
+ Developer.find(:all)
1215
+ end
1216
+ assert_equal 3, developers.size
1217
+ end
1218
+
1219
+ def test_find_last
1220
+ last = Developer.find :last
1221
+ assert_equal last, Developer.find(:first, :order => 'id desc')
1222
+ end
1223
+
1224
+ def test_last
1225
+ assert_equal Developer.find(:first, :order => 'id desc'), Developer.last
1226
+ end
1227
+
1228
+ def test_all
1229
+ developers = Developer.all
1230
+ assert_kind_of Array, developers
1231
+ assert_equal Developer.find(:all), developers
1232
+ end
1233
+
1234
+ def test_all_with_conditions
1235
+ assert_equal Developer.find(:all, :order => 'id desc'), Developer.order('id desc').all
1236
+ end
1237
+
1238
+ def test_find_ordered_last
1239
+ last = Developer.find :last, :order => 'developers.salary ASC'
1240
+ assert_equal last, Developer.find(:all, :order => 'developers.salary ASC').last
1241
+ end
1242
+
1243
+ def test_find_reverse_ordered_last
1244
+ last = Developer.find :last, :order => 'developers.salary DESC'
1245
+ assert_equal last, Developer.find(:all, :order => 'developers.salary DESC').last
1246
+ end
1247
+
1248
+ def test_find_multiple_ordered_last
1249
+ last = Developer.find :last, :order => 'developers.name, developers.salary DESC'
1250
+ assert_equal last, Developer.find(:all, :order => 'developers.name, developers.salary DESC').last
1251
+ end
1252
+
1253
+ def test_find_keeps_multiple_order_values
1254
+ combined = Developer.find(:all, :order => 'developers.name, developers.salary')
1255
+ assert_equal combined, Developer.find(:all, :order => ['developers.name', 'developers.salary'])
1256
+ end
1257
+
1258
+ def test_find_keeps_multiple_group_values
1259
+ combined = Developer.find(:all, :group => 'developers.name, developers.salary, developers.id, developers.created_at, developers.updated_at')
1260
+ assert_equal combined, Developer.find(:all, :group => ['developers.name', 'developers.salary', 'developers.id', 'developers.created_at', 'developers.updated_at'])
1261
+ end
1262
+
1263
+ def test_find_symbol_ordered_last
1264
+ last = Developer.find :last, :order => :salary
1265
+ assert_equal last, Developer.find(:all, :order => :salary).last
1266
+ end
1267
+
1268
+ def test_find_scoped_ordered_last
1269
+ last_developer = Developer.send(:with_scope, :find => { :order => 'developers.salary ASC' }) do
1270
+ Developer.find(:last)
1271
+ end
1272
+ assert_equal last_developer, Developer.find(:all, :order => 'developers.salary ASC').last
1273
+ end
1274
+
1275
+ def test_abstract_class
1276
+ assert !ActiveRecord::Base.abstract_class?
1277
+ assert LoosePerson.abstract_class?
1278
+ assert !LooseDescendant.abstract_class?
1279
+ end
1280
+
1281
+ def test_base_class
1282
+ assert_equal LoosePerson, LoosePerson.base_class
1283
+ assert_equal LooseDescendant, LooseDescendant.base_class
1284
+ assert_equal TightPerson, TightPerson.base_class
1285
+ assert_equal TightPerson, TightDescendant.base_class
1286
+
1287
+ assert_equal Post, Post.base_class
1288
+ assert_equal Post, SpecialPost.base_class
1289
+ assert_equal Post, StiPost.base_class
1290
+ assert_equal SubStiPost, SubStiPost.base_class
1291
+ end
1292
+
1293
+ def test_descends_from_active_record
1294
+ # Tries to call Object.abstract_class?
1295
+ assert_raise(NoMethodError) do
1296
+ ActiveRecord::Base.descends_from_active_record?
1297
+ end
1298
+
1299
+ # Abstract subclass of AR::Base.
1300
+ assert LoosePerson.descends_from_active_record?
1301
+
1302
+ # Concrete subclass of an abstract class.
1303
+ assert LooseDescendant.descends_from_active_record?
1304
+
1305
+ # Concrete subclass of AR::Base.
1306
+ assert TightPerson.descends_from_active_record?
1307
+
1308
+ # Concrete subclass of a concrete class but has no type column.
1309
+ assert TightDescendant.descends_from_active_record?
1310
+
1311
+ # Concrete subclass of AR::Base.
1312
+ assert Post.descends_from_active_record?
1313
+
1314
+ # Abstract subclass of a concrete class which has a type column.
1315
+ # This is pathological, as you'll never have Sub < Abstract < Concrete.
1316
+ assert !StiPost.descends_from_active_record?
1317
+
1318
+ # Concrete subclasses an abstract class which has a type column.
1319
+ assert !SubStiPost.descends_from_active_record?
1320
+ end
1321
+
1322
+ def test_find_on_abstract_base_class_doesnt_use_type_condition
1323
+ old_class = LooseDescendant
1324
+ Object.send :remove_const, :LooseDescendant
1325
+
1326
+ descendant = old_class.create! :first_name => 'bob'
1327
+ assert_not_nil LoosePerson.find(descendant.id), "Should have found instance of LooseDescendant when finding abstract LoosePerson: #{descendant.inspect}"
1328
+ ensure
1329
+ unless Object.const_defined?(:LooseDescendant)
1330
+ Object.const_set :LooseDescendant, old_class
1331
+ end
1332
+ end
1333
+
1334
+ def test_assert_queries
1335
+ query = lambda { ActiveRecord::Base.connection.execute 'select count(*) from developers' }
1336
+ assert_queries(2) { 2.times { query.call } }
1337
+ assert_queries 1, &query
1338
+ assert_no_queries { assert true }
1339
+ end
1340
+
1341
+ def test_to_param_should_return_string
1342
+ assert_kind_of String, Client.find(:first).to_param
1343
+ end
1344
+
1345
+ def test_inspect_class
1346
+ assert_equal 'ActiveRecord::Base', ActiveRecord::Base.inspect
1347
+ assert_equal 'LoosePerson(abstract)', LoosePerson.inspect
1348
+ assert_match(/^Topic\(id: integer, title: string/, Topic.inspect)
1349
+ end
1350
+
1351
+ def test_inspect_instance
1352
+ topic = topics(:first)
1353
+ 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, group: nil>), topic.inspect
1354
+ end
1355
+
1356
+ def test_inspect_new_instance
1357
+ assert_match(/Topic id: nil/, Topic.new.inspect)
1358
+ end
1359
+
1360
+ def test_inspect_limited_select_instance
1361
+ assert_equal %(#<Topic id: 1>), Topic.find(:first, :select => 'id', :conditions => 'id = 1').inspect
1362
+ assert_equal %(#<Topic id: 1, title: "The First Topic">), Topic.find(:first, :select => 'id, title', :conditions => 'id = 1').inspect
1363
+ end
1364
+
1365
+ def test_inspect_class_without_table
1366
+ assert_equal "NonExistentTable(Table doesn't exist)", NonExistentTable.inspect
1367
+ end
1368
+
1369
+ def test_attribute_for_inspect
1370
+ t = topics(:first)
1371
+ t.title = "The First Topic Now Has A Title With\nNewlines And More Than 50 Characters"
1372
+
1373
+ assert_equal %("#{t.written_on.to_s(:db)}"), t.attribute_for_inspect(:written_on)
1374
+ assert_equal '"The First Topic Now Has A Title With\nNewlines And M..."', t.attribute_for_inspect(:title)
1375
+ end
1376
+
1377
+ def test_becomes
1378
+ assert_kind_of Reply, topics(:first).becomes(Reply)
1379
+ assert_equal "The First Topic", topics(:first).becomes(Reply).title
1380
+ end
1381
+
1382
+ def test_silence_sets_log_level_to_error_in_block
1383
+ original_logger = ActiveRecord::Base.logger
1384
+ log = StringIO.new
1385
+ ActiveRecord::Base.logger = Logger.new(log)
1386
+ ActiveRecord::Base.logger.level = Logger::DEBUG
1387
+ ActiveRecord::Base.silence do
1388
+ ActiveRecord::Base.logger.warn "warn"
1389
+ ActiveRecord::Base.logger.error "error"
1390
+ end
1391
+ assert_equal "error\n", log.string
1392
+ ensure
1393
+ ActiveRecord::Base.logger = original_logger
1394
+ end
1395
+
1396
+ def test_silence_sets_log_level_back_to_level_before_yield
1397
+ original_logger = ActiveRecord::Base.logger
1398
+ log = StringIO.new
1399
+ ActiveRecord::Base.logger = Logger.new(log)
1400
+ ActiveRecord::Base.logger.level = Logger::WARN
1401
+ ActiveRecord::Base.silence do
1402
+ end
1403
+ assert_equal Logger::WARN, ActiveRecord::Base.logger.level
1404
+ ensure
1405
+ ActiveRecord::Base.logger = original_logger
1406
+ end
1407
+
1408
+ def test_benchmark_with_log_level
1409
+ original_logger = ActiveRecord::Base.logger
1410
+ log = StringIO.new
1411
+ ActiveRecord::Base.logger = Logger.new(log)
1412
+ ActiveRecord::Base.logger.level = Logger::WARN
1413
+ ActiveRecord::Base.benchmark("Debug Topic Count", :level => :debug) { Topic.count }
1414
+ ActiveRecord::Base.benchmark("Warn Topic Count", :level => :warn) { Topic.count }
1415
+ ActiveRecord::Base.benchmark("Error Topic Count", :level => :error) { Topic.count }
1416
+ assert_no_match(/Debug Topic Count/, log.string)
1417
+ assert_match(/Warn Topic Count/, log.string)
1418
+ assert_match(/Error Topic Count/, log.string)
1419
+ ensure
1420
+ ActiveRecord::Base.logger = original_logger
1421
+ end
1422
+
1423
+ def test_benchmark_with_use_silence
1424
+ original_logger = ActiveRecord::Base.logger
1425
+ log = StringIO.new
1426
+ ActiveRecord::Base.logger = Logger.new(log)
1427
+ ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => true) { ActiveRecord::Base.logger.debug "Loud" }
1428
+ ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => false) { ActiveRecord::Base.logger.debug "Quiet" }
1429
+ assert_no_match(/Loud/, log.string)
1430
+ assert_match(/Quiet/, log.string)
1431
+ ensure
1432
+ ActiveRecord::Base.logger = original_logger
1433
+ end
1434
+
1435
+ def test_dup
1436
+ assert !Minimalistic.new.freeze.dup.frozen?
1437
+ end
1438
+
1439
+ def test_compute_type_success
1440
+ assert_equal Author, ActiveRecord::Base.send(:compute_type, 'Author')
1441
+ end
1442
+
1443
+ def test_compute_type_nonexistent_constant
1444
+ assert_raises NameError do
1445
+ ActiveRecord::Base.send :compute_type, 'NonexistentModel'
1446
+ end
1447
+ end
1448
+
1449
+ def test_compute_type_no_method_error
1450
+ ActiveSupport::Dependencies.stubs(:constantize).raises(NoMethodError)
1451
+ assert_raises NoMethodError do
1452
+ ActiveRecord::Base.send :compute_type, 'InvalidModel'
1453
+ end
1454
+ end
1455
+
1456
+ def test_default_scope_is_reset
1457
+ Object.const_set :UnloadablePost, Class.new(ActiveRecord::Base)
1458
+ UnloadablePost.table_name = 'posts'
1459
+ UnloadablePost.class_eval do
1460
+ default_scope order('posts.comments_count ASC')
1461
+ end
1462
+ UnloadablePost.scoped_methods # make Thread.current[:UnloadablePost_scoped_methods] not nil
1463
+
1464
+ UnloadablePost.unloadable
1465
+ assert_not_nil Thread.current[:UnloadablePost_scoped_methods]
1466
+ ActiveSupport::Dependencies.remove_unloadable_constants!
1467
+ assert_nil Thread.current[:UnloadablePost_scoped_methods]
1468
+ ensure
1469
+ Object.class_eval{ remove_const :UnloadablePost } if defined?(UnloadablePost)
1470
+ end
1471
+
1472
+ protected
1473
+ def with_env_tz(new_tz = 'US/Eastern')
1474
+ old_tz, ENV['TZ'] = ENV['TZ'], new_tz
1475
+ yield
1476
+ ensure
1477
+ old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
1478
+ end
1479
+
1480
+ def with_active_record_default_timezone(zone)
1481
+ old_zone, ActiveRecord::Base.default_timezone = ActiveRecord::Base.default_timezone, zone
1482
+ yield
1483
+ ensure
1484
+ ActiveRecord::Base.default_timezone = old_zone
1485
+ end
1486
+ end