ibm_db 2.5.6-x86-mingw32

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 (43) hide show
  1. data/CHANGES +181 -0
  2. data/LICENSE +18 -0
  3. data/MANIFEST +14 -0
  4. data/ParameterizedQueries README +39 -0
  5. data/README +282 -0
  6. data/ext/Makefile.nt32 +181 -0
  7. data/ext/extconf.rb +66 -0
  8. data/ext/ibm_db.c +11166 -0
  9. data/ext/ruby_ibm_db.h +236 -0
  10. data/ext/ruby_ibm_db_cli.c +738 -0
  11. data/ext/ruby_ibm_db_cli.h +431 -0
  12. data/init.rb +42 -0
  13. data/lib/IBM_DB.rb +2 -0
  14. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +2598 -0
  15. data/lib/active_record/connection_adapters/ibm_db_pstmt.rb +1965 -0
  16. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -0
  17. data/lib/mswin32/ibm_db.rb +1 -0
  18. data/lib/mswin32/rb18x/ibm_db.so +0 -0
  19. data/lib/mswin32/rb19x/ibm_db.so +0 -0
  20. data/test/cases/adapter_test.rb +202 -0
  21. data/test/cases/associations/belongs_to_associations_test.rb +486 -0
  22. data/test/cases/associations/cascaded_eager_loading_test.rb +183 -0
  23. data/test/cases/associations/eager_test.rb +862 -0
  24. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +917 -0
  25. data/test/cases/associations/has_many_through_associations_test.rb +461 -0
  26. data/test/cases/associations/join_model_test.rb +793 -0
  27. data/test/cases/attribute_methods_test.rb +621 -0
  28. data/test/cases/base_test.rb +1486 -0
  29. data/test/cases/calculations_test.rb +362 -0
  30. data/test/cases/finder_test.rb +1088 -0
  31. data/test/cases/fixtures_test.rb +684 -0
  32. data/test/cases/migration_test.rb +2014 -0
  33. data/test/cases/schema_dumper_test.rb +232 -0
  34. data/test/cases/validations/uniqueness_validation_test.rb +283 -0
  35. data/test/connections/native_ibm_db/connection.rb +42 -0
  36. data/test/ibm_db_test.rb +25 -0
  37. data/test/models/warehouse_thing.rb +5 -0
  38. data/test/schema/i5/ibm_db_specific_schema.rb +135 -0
  39. data/test/schema/ids/ibm_db_specific_schema.rb +138 -0
  40. data/test/schema/luw/ibm_db_specific_schema.rb +135 -0
  41. data/test/schema/schema.rb +647 -0
  42. data/test/schema/zOS/ibm_db_specific_schema.rb +206 -0
  43. metadata +123 -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