ibm_db 2.5.26-universal-darwin-14

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 (49) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +233 -0
  3. data/LICENSE +18 -0
  4. data/MANIFEST +14 -0
  5. data/ParameterizedQueries README +39 -0
  6. data/README +225 -0
  7. data/ext/Makefile.nt32 +181 -0
  8. data/ext/Makefile.nt32.191 +212 -0
  9. data/ext/extconf.rb +261 -0
  10. data/ext/ibm_db.c +11793 -0
  11. data/ext/ruby_ibm_db.h +240 -0
  12. data/ext/ruby_ibm_db_cli.c +845 -0
  13. data/ext/ruby_ibm_db_cli.h +489 -0
  14. data/init.rb +42 -0
  15. data/lib/IBM_DB.rb +19 -0
  16. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +3289 -0
  17. data/lib/active_record/connection_adapters/ibm_db_pstmt.rb +1965 -0
  18. data/lib/active_record/connection_adapters/ibmdb_adapter.rb +2 -0
  19. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -0
  20. data/lib/linux/rb18x/ibm_db.bundle +0 -0
  21. data/lib/linux/rb19x/ibm_db.bundle +0 -0
  22. data/lib/linux/rb20x/ibm_db.bundle +0 -0
  23. data/lib/linux/rb21x/ibm_db.bundle +0 -0
  24. data/test/cases/adapter_test.rb +207 -0
  25. data/test/cases/associations/belongs_to_associations_test.rb +711 -0
  26. data/test/cases/associations/cascaded_eager_loading_test.rb +181 -0
  27. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +851 -0
  28. data/test/cases/associations/join_model_test.rb +743 -0
  29. data/test/cases/attribute_methods_test.rb +822 -0
  30. data/test/cases/base_test.rb +2133 -0
  31. data/test/cases/calculations_test.rb +482 -0
  32. data/test/cases/migration_test.rb +2408 -0
  33. data/test/cases/persistence_test.rb +642 -0
  34. data/test/cases/query_cache_test.rb +257 -0
  35. data/test/cases/relations_test.rb +1182 -0
  36. data/test/cases/schema_dumper_test.rb +256 -0
  37. data/test/cases/transaction_callbacks_test.rb +300 -0
  38. data/test/cases/validations/uniqueness_validation_test.rb +299 -0
  39. data/test/cases/xml_serialization_test.rb +408 -0
  40. data/test/config.yml +154 -0
  41. data/test/connections/native_ibm_db/connection.rb +44 -0
  42. data/test/ibm_db_test.rb +25 -0
  43. data/test/models/warehouse_thing.rb +5 -0
  44. data/test/schema/i5/ibm_db_specific_schema.rb +137 -0
  45. data/test/schema/ids/ibm_db_specific_schema.rb +140 -0
  46. data/test/schema/luw/ibm_db_specific_schema.rb +137 -0
  47. data/test/schema/schema.rb +751 -0
  48. data/test/schema/zOS/ibm_db_specific_schema.rb +208 -0
  49. metadata +114 -0
@@ -0,0 +1,2133 @@
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/person'
22
+ require 'models/edge'
23
+ require 'models/joke'
24
+ require 'models/bulb'
25
+ require 'models/bird'
26
+ require 'rexml/document'
27
+ require 'active_support/core_ext/exception'
28
+ require 'bcrypt'
29
+
30
+ class FirstAbstractClass < ActiveRecord::Base
31
+ self.abstract_class = true
32
+ end
33
+ class SecondAbstractClass < FirstAbstractClass
34
+ self.abstract_class = true
35
+ end
36
+ class Photo < SecondAbstractClass; end
37
+ class Category < ActiveRecord::Base; end
38
+ class Categorization < ActiveRecord::Base; end
39
+ class Smarts < ActiveRecord::Base; end
40
+ class CreditCard < ActiveRecord::Base
41
+ class PinNumber < ActiveRecord::Base
42
+ class CvvCode < ActiveRecord::Base; end
43
+ class SubCvvCode < CvvCode; end
44
+ end
45
+ class SubPinNumber < PinNumber; end
46
+ class Brand < Category; end
47
+ end
48
+ class MasterCreditCard < ActiveRecord::Base; end
49
+ class Post < ActiveRecord::Base; end
50
+ class Computer < ActiveRecord::Base; end
51
+ class NonExistentTable < ActiveRecord::Base; end
52
+ class TestOracleDefault < ActiveRecord::Base; end
53
+
54
+ class ReadonlyTitlePost < Post
55
+ attr_readonly :title
56
+ end
57
+
58
+ class ProtectedTitlePost < Post
59
+ attr_protected :title
60
+ end
61
+
62
+ class Weird < ActiveRecord::Base; end
63
+
64
+ class Boolean < ActiveRecord::Base; end
65
+
66
+ class LintTest < ActiveRecord::TestCase
67
+ include ActiveModel::Lint::Tests
68
+
69
+ class LintModel < ActiveRecord::Base; end
70
+
71
+ def setup
72
+ @model = LintModel.new
73
+ end
74
+ end
75
+
76
+ class BasicsTest < ActiveRecord::TestCase
77
+ fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse_things', :authors, :categorizations, :categories, :posts
78
+
79
+ def test_generated_methods_modules
80
+ modules = Computer.ancestors
81
+ assert modules.include?(Computer::GeneratedFeatureMethods)
82
+ assert_equal(Computer::GeneratedFeatureMethods, Computer.generated_feature_methods)
83
+ assert(modules.index(Computer.generated_attribute_methods) > modules.index(Computer.generated_feature_methods),
84
+ "generated_attribute_methods must be higher in inheritance hierarchy than generated_feature_methods")
85
+ assert_not_equal Computer.generated_feature_methods, Post.generated_feature_methods
86
+ end
87
+
88
+ def test_column_names_are_escaped
89
+ conn = ActiveRecord::Base.connection
90
+ classname = conn.class.name[/[^:]*$/]
91
+ badchar = {
92
+ 'SQLite3Adapter' => '"',
93
+ 'MysqlAdapter' => '`',
94
+ 'Mysql2Adapter' => '`',
95
+ 'PostgreSQLAdapter' => '"',
96
+ 'OracleAdapter' => '"',
97
+ 'IBM_DBAdapter' => '"'
98
+ }.fetch(classname) {
99
+ raise "need a bad char for #{classname}"
100
+ }
101
+
102
+ quoted = conn.quote_column_name "foo#{badchar}bar"
103
+ if current_adapter?(:OracleAdapter)
104
+ # Oracle does not allow double quotes in table and column names at all
105
+ # therefore quoting removes them
106
+ assert_equal("#{badchar}foobar#{badchar}", quoted)
107
+ elsif current_adapter?(:IBM_DBAdapter)
108
+ assert_equal("foo#{badchar}bar", quoted)
109
+ else
110
+ assert_equal("#{badchar}foo#{badchar * 2}bar#{badchar}", quoted)
111
+ end
112
+ end
113
+
114
+ def test_columns_should_obey_set_primary_key
115
+ pk = Subscriber.columns.find { |x| x.name == 'nick' }
116
+ assert pk.primary, 'nick should be primary key'
117
+ end
118
+
119
+ def test_primary_key_with_no_id
120
+ assert_nil Edge.primary_key
121
+ end
122
+
123
+ unless current_adapter?(:PostgreSQLAdapter,:OracleAdapter,:SQLServerAdapter, :IBM_DBAdapter)
124
+ def test_limit_with_comma
125
+ assert_nothing_raised do
126
+ Topic.limit("1,2").all
127
+ end
128
+ end
129
+ end
130
+
131
+ def test_limit_without_comma
132
+ assert_nothing_raised do
133
+ assert_equal 1, Topic.limit("1").all.length
134
+ end
135
+
136
+ assert_nothing_raised do
137
+ assert_equal 1, Topic.limit(1).all.length
138
+ end
139
+ end
140
+
141
+ def test_invalid_limit
142
+ assert_raises(ArgumentError) do
143
+ Topic.limit("asdfadf").all
144
+ end
145
+ end
146
+
147
+ def test_limit_should_sanitize_sql_injection_for_limit_without_comas
148
+ assert_raises(ArgumentError) do
149
+ Topic.limit("1 select * from schema").all
150
+ end
151
+ end
152
+
153
+ def test_limit_should_sanitize_sql_injection_for_limit_with_comas
154
+ assert_raises(ArgumentError) do
155
+ Topic.limit("1, 7 procedure help()").all
156
+ end
157
+ end
158
+
159
+ unless current_adapter?(:MysqlAdapter) || current_adapter?(:Mysql2Adapter) || current_adapter?(:IBM_DBAdapter)
160
+ def test_limit_should_allow_sql_literal
161
+ assert_equal 1, Topic.limit(Arel.sql('2-1')).all.length
162
+ end
163
+ end
164
+
165
+ def test_select_symbol
166
+ topic_ids = Topic.select(:id).map(&:id).sort
167
+ assert_equal Topic.all.map(&:id).sort, topic_ids
168
+ end
169
+
170
+ def test_table_exists
171
+ assert !NonExistentTable.table_exists?
172
+ assert Topic.table_exists?
173
+ end
174
+
175
+ def test_preserving_date_objects
176
+ if current_adapter?(:SybaseAdapter)
177
+ # Sybase ctlib does not (yet?) support the date type; use datetime instead.
178
+ assert_kind_of(
179
+ Time, Topic.find(1).last_read,
180
+ "The last_read attribute should be of the Time class"
181
+ )
182
+ else
183
+ # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb)
184
+ assert_kind_of(
185
+ Date, Topic.find(1).last_read,
186
+ "The last_read attribute should be of the Date class"
187
+ )
188
+ end
189
+ end
190
+
191
+ def test_preserving_time_objects
192
+ assert_kind_of(
193
+ Time, Topic.find(1).bonus_time,
194
+ "The bonus_time attribute should be of the Time class"
195
+ )
196
+
197
+ assert_kind_of(
198
+ Time, Topic.find(1).written_on,
199
+ "The written_on attribute should be of the Time class"
200
+ )
201
+
202
+ # For adapters which support microsecond resolution.
203
+ if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:SQLiteAdapter)
204
+ assert_equal 11, Topic.find(1).written_on.sec
205
+ assert_equal 223300, Topic.find(1).written_on.usec
206
+ assert_equal 9900, Topic.find(2).written_on.usec
207
+ end
208
+ end
209
+
210
+ def test_preserving_time_objects_with_local_time_conversion_to_default_timezone_utc
211
+ with_env_tz 'America/New_York' do
212
+ with_active_record_default_timezone :utc do
213
+ time = Time.local(2000)
214
+ topic = Topic.create('written_on' => time)
215
+ saved_time = Topic.find(topic.id).reload.written_on
216
+ assert_equal time, saved_time
217
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "EST"], time.to_a
218
+ assert_equal [0, 0, 5, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a
219
+ end
220
+ end
221
+ end
222
+
223
+ def test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_utc
224
+ with_env_tz 'America/New_York' do
225
+ with_active_record_default_timezone :utc do
226
+ Time.use_zone 'Central Time (US & Canada)' do
227
+ time = Time.zone.local(2000)
228
+ topic = Topic.create('written_on' => time)
229
+ saved_time = Topic.find(topic.id).reload.written_on
230
+ assert_equal time, saved_time
231
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a
232
+ assert_equal [0, 0, 6, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a
233
+ end
234
+ end
235
+ end
236
+ end
237
+
238
+ def test_preserving_time_objects_with_utc_time_conversion_to_default_timezone_local
239
+ with_env_tz 'America/New_York' do
240
+ time = Time.utc(2000)
241
+ topic = Topic.create('written_on' => time)
242
+ saved_time = Topic.find(topic.id).reload.written_on
243
+ assert_equal time, saved_time
244
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], time.to_a
245
+ assert_equal [0, 0, 19, 31, 12, 1999, 5, 365, false, "EST"], saved_time.to_a
246
+ end
247
+ end
248
+
249
+ def test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_local
250
+ with_env_tz 'America/New_York' do
251
+ with_active_record_default_timezone :local do
252
+ Time.use_zone 'Central Time (US & Canada)' do
253
+ time = Time.zone.local(2000)
254
+ topic = Topic.create('written_on' => time)
255
+ saved_time = Topic.find(topic.id).reload.written_on
256
+ assert_equal time, saved_time
257
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a
258
+ assert_equal [0, 0, 1, 1, 1, 2000, 6, 1, false, "EST"], saved_time.to_a
259
+ end
260
+ end
261
+ end
262
+ end
263
+
264
+ def test_custom_mutator
265
+ topic = Topic.find(1)
266
+ # This mutator is protected in the class definition
267
+ topic.send(:approved=, true)
268
+ assert topic.instance_variable_get("@custom_approved")
269
+ end
270
+
271
+ def test_initialize_with_attributes
272
+ topic = Topic.new({
273
+ "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23"
274
+ })
275
+
276
+ assert_equal("initialized from attributes", topic.title)
277
+ end
278
+
279
+ def test_initialize_with_invalid_attribute
280
+ begin
281
+ Topic.new({ "title" => "test",
282
+ "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"})
283
+ rescue ActiveRecord::MultiparameterAssignmentErrors => ex
284
+ assert_equal(1, ex.errors.size)
285
+ assert_equal("last_read", ex.errors[0].attribute)
286
+ end
287
+ end
288
+
289
+ def test_create_after_initialize_without_block
290
+ cb = CustomBulb.create(:name => 'Dude')
291
+ assert_equal('Dude', cb.name)
292
+ assert_equal(true, cb.frickinawesome)
293
+ end
294
+
295
+ def test_create_after_initialize_with_block
296
+ cb = CustomBulb.create {|c| c.name = 'Dude' }
297
+ assert_equal('Dude', cb.name)
298
+ assert_equal(true, cb.frickinawesome)
299
+ end
300
+
301
+ def test_first_or_create
302
+ parrot = Bird.first_or_create(:color => 'green', :name => 'parrot')
303
+ assert parrot.persisted?
304
+ the_same_parrot = Bird.first_or_create(:color => 'yellow', :name => 'macaw')
305
+ assert_equal parrot, the_same_parrot
306
+ end
307
+
308
+ def test_first_or_create_bang
309
+ assert_raises(ActiveRecord::RecordInvalid) { Bird.first_or_create! }
310
+ parrot = Bird.first_or_create!(:color => 'green', :name => 'parrot')
311
+ assert parrot.persisted?
312
+ the_same_parrot = Bird.first_or_create!(:color => 'yellow', :name => 'macaw')
313
+ assert_equal parrot, the_same_parrot
314
+ end
315
+
316
+ def test_first_or_initialize
317
+ parrot = Bird.first_or_initialize(:color => 'green', :name => 'parrot')
318
+ assert_kind_of Bird, parrot
319
+ assert !parrot.persisted?
320
+ assert parrot.new_record?
321
+ assert parrot.valid?
322
+ end
323
+
324
+ def test_load
325
+ topics = Topic.find(:all, :order => 'id')
326
+ assert_equal(4, topics.size)
327
+ assert_equal(topics(:first).title, topics.first.title)
328
+ end
329
+
330
+ def test_load_with_condition
331
+ topics = Topic.find(:all, :conditions => "author_name = 'Mary'")
332
+
333
+ assert_equal(1, topics.size)
334
+ assert_equal(topics(:second).title, topics.first.title)
335
+ end
336
+
337
+ GUESSED_CLASSES = [Category, Smarts, CreditCard, CreditCard::PinNumber, CreditCard::PinNumber::CvvCode, CreditCard::SubPinNumber, CreditCard::Brand, MasterCreditCard]
338
+
339
+ def test_table_name_guesses
340
+ assert_equal "topics", Topic.table_name
341
+
342
+ assert_equal "categories", Category.table_name
343
+ assert_equal "smarts", Smarts.table_name
344
+ assert_equal "credit_cards", CreditCard.table_name
345
+ assert_equal "credit_card_pin_numbers", CreditCard::PinNumber.table_name
346
+ assert_equal "credit_card_pin_number_cvv_codes", CreditCard::PinNumber::CvvCode.table_name
347
+ assert_equal "credit_card_pin_numbers", CreditCard::SubPinNumber.table_name
348
+ assert_equal "categories", CreditCard::Brand.table_name
349
+ assert_equal "master_credit_cards", MasterCreditCard.table_name
350
+ ensure
351
+ GUESSED_CLASSES.each(&:reset_table_name)
352
+ end
353
+
354
+ def test_singular_table_name_guesses
355
+ ActiveRecord::Base.pluralize_table_names = false
356
+ GUESSED_CLASSES.each(&:reset_table_name)
357
+
358
+ assert_equal "category", Category.table_name
359
+ assert_equal "smarts", Smarts.table_name
360
+ assert_equal "credit_card", CreditCard.table_name
361
+ assert_equal "credit_card_pin_number", CreditCard::PinNumber.table_name
362
+ assert_equal "credit_card_pin_number_cvv_code", CreditCard::PinNumber::CvvCode.table_name
363
+ assert_equal "credit_card_pin_number", CreditCard::SubPinNumber.table_name
364
+ assert_equal "category", CreditCard::Brand.table_name
365
+ assert_equal "master_credit_card", MasterCreditCard.table_name
366
+ ensure
367
+ ActiveRecord::Base.pluralize_table_names = true
368
+ GUESSED_CLASSES.each(&:reset_table_name)
369
+ end
370
+
371
+ def test_table_name_guesses_with_prefixes_and_suffixes
372
+ ActiveRecord::Base.table_name_prefix = "test_"
373
+ Category.reset_table_name
374
+ assert_equal "test_categories", Category.table_name
375
+ ActiveRecord::Base.table_name_suffix = "_test"
376
+ Category.reset_table_name
377
+ assert_equal "test_categories_test", Category.table_name
378
+ ActiveRecord::Base.table_name_prefix = ""
379
+ Category.reset_table_name
380
+ assert_equal "categories_test", Category.table_name
381
+ ActiveRecord::Base.table_name_suffix = ""
382
+ Category.reset_table_name
383
+ assert_equal "categories", Category.table_name
384
+ ensure
385
+ ActiveRecord::Base.table_name_prefix = ""
386
+ ActiveRecord::Base.table_name_suffix = ""
387
+ GUESSED_CLASSES.each(&:reset_table_name)
388
+ end
389
+
390
+ def test_singular_table_name_guesses_with_prefixes_and_suffixes
391
+ ActiveRecord::Base.pluralize_table_names = false
392
+
393
+ ActiveRecord::Base.table_name_prefix = "test_"
394
+ Category.reset_table_name
395
+ assert_equal "test_category", Category.table_name
396
+ ActiveRecord::Base.table_name_suffix = "_test"
397
+ Category.reset_table_name
398
+ assert_equal "test_category_test", Category.table_name
399
+ ActiveRecord::Base.table_name_prefix = ""
400
+ Category.reset_table_name
401
+ assert_equal "category_test", Category.table_name
402
+ ActiveRecord::Base.table_name_suffix = ""
403
+ Category.reset_table_name
404
+ assert_equal "category", Category.table_name
405
+ ensure
406
+ ActiveRecord::Base.pluralize_table_names = true
407
+ ActiveRecord::Base.table_name_prefix = ""
408
+ ActiveRecord::Base.table_name_suffix = ""
409
+ GUESSED_CLASSES.each(&:reset_table_name)
410
+ end
411
+
412
+ def test_table_name_guesses_with_inherited_prefixes_and_suffixes
413
+ GUESSED_CLASSES.each(&:reset_table_name)
414
+
415
+ CreditCard.table_name_prefix = "test_"
416
+ CreditCard.reset_table_name
417
+ Category.reset_table_name
418
+ assert_equal "test_credit_cards", CreditCard.table_name
419
+ assert_equal "categories", Category.table_name
420
+ CreditCard.table_name_suffix = "_test"
421
+ CreditCard.reset_table_name
422
+ Category.reset_table_name
423
+ assert_equal "test_credit_cards_test", CreditCard.table_name
424
+ assert_equal "categories", Category.table_name
425
+ CreditCard.table_name_prefix = ""
426
+ CreditCard.reset_table_name
427
+ Category.reset_table_name
428
+ assert_equal "credit_cards_test", CreditCard.table_name
429
+ assert_equal "categories", Category.table_name
430
+ CreditCard.table_name_suffix = ""
431
+ CreditCard.reset_table_name
432
+ Category.reset_table_name
433
+ assert_equal "credit_cards", CreditCard.table_name
434
+ assert_equal "categories", Category.table_name
435
+ ensure
436
+ CreditCard.table_name_prefix = ""
437
+ CreditCard.table_name_suffix = ""
438
+ GUESSED_CLASSES.each(&:reset_table_name)
439
+ end
440
+
441
+ def test_singular_table_name_guesses_for_individual_table
442
+ CreditCard.pluralize_table_names = false
443
+ CreditCard.reset_table_name
444
+ assert_equal "credit_card", CreditCard.table_name
445
+ assert_equal "categories", Category.table_name
446
+ ensure
447
+ CreditCard.pluralize_table_names = true
448
+ CreditCard.reset_table_name
449
+ end
450
+
451
+ if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
452
+ def test_update_all_with_order_and_limit
453
+ assert_equal 1, Topic.update_all("content = 'bulk updated!'", nil, :limit => 1, :order => 'id DESC')
454
+ end
455
+ end
456
+
457
+ def test_null_fields
458
+ assert_nil Topic.find(1).parent_id
459
+ assert_nil Topic.create("title" => "Hey you").parent_id
460
+ end
461
+
462
+ def test_default_values
463
+ topic = Topic.new
464
+ assert topic.approved?
465
+ assert_nil topic.written_on
466
+ assert_nil topic.bonus_time
467
+ assert_nil topic.last_read
468
+
469
+ topic.save
470
+
471
+ topic = Topic.find(topic.id)
472
+ assert topic.approved?
473
+ assert_nil topic.last_read
474
+
475
+ # Oracle has some funky default handling, so it requires a bit of
476
+ # extra testing. See ticket #2788.
477
+ if current_adapter?(:OracleAdapter)
478
+ test = TestOracleDefault.new
479
+ assert_equal "X", test.test_char
480
+ assert_equal "hello", test.test_string
481
+ assert_equal 3, test.test_int
482
+ end
483
+ end
484
+
485
+ # Oracle, and Sybase do not have a TIME datatype.
486
+ unless current_adapter?(:OracleAdapter, :SybaseAdapter)
487
+ def test_utc_as_time_zone
488
+ Topic.default_timezone = :utc
489
+ attributes = { "bonus_time" => "5:42:00AM" }
490
+ topic = Topic.find(1)
491
+ topic.attributes = attributes
492
+ assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time
493
+ Topic.default_timezone = :local
494
+ end
495
+
496
+ def test_utc_as_time_zone_and_new
497
+ Topic.default_timezone = :utc
498
+ attributes = { "bonus_time(1i)"=>"2000",
499
+ "bonus_time(2i)"=>"1",
500
+ "bonus_time(3i)"=>"1",
501
+ "bonus_time(4i)"=>"10",
502
+ "bonus_time(5i)"=>"35",
503
+ "bonus_time(6i)"=>"50" }
504
+ topic = Topic.new(attributes)
505
+ assert_equal Time.utc(2000, 1, 1, 10, 35, 50), topic.bonus_time
506
+ Topic.default_timezone = :local
507
+ end
508
+ end
509
+
510
+ def test_default_values_on_empty_strings
511
+ topic = Topic.new
512
+ topic.approved = nil
513
+ topic.last_read = nil
514
+
515
+ topic.save
516
+
517
+ topic = Topic.find(topic.id)
518
+ assert_nil topic.last_read
519
+
520
+ # Sybase adapter does not allow nulls in boolean columns
521
+ if current_adapter?(:SybaseAdapter)
522
+ assert topic.approved == false
523
+ else
524
+ assert_nil topic.approved
525
+ end
526
+ end
527
+
528
+ def test_equality
529
+ assert_equal Topic.find(1), Topic.find(2).topic
530
+ end
531
+
532
+ def test_find_by_slug
533
+ assert_equal Topic.find('1-meowmeow'), Topic.find(1)
534
+ end
535
+
536
+ def test_equality_of_new_records
537
+ assert_not_equal Topic.new, Topic.new
538
+ end
539
+
540
+ def test_equality_of_destroyed_records
541
+ topic_1 = Topic.new(:title => 'test_1')
542
+ topic_1.save
543
+ topic_2 = Topic.find(topic_1.id)
544
+ topic_1.destroy
545
+ assert_equal topic_1, topic_2
546
+ assert_equal topic_2, topic_1
547
+ end
548
+
549
+ def test_hashing
550
+ assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
551
+ end
552
+
553
+ def test_comparison
554
+ topic_1 = Topic.create!
555
+ topic_2 = Topic.create!
556
+
557
+ assert_equal [topic_2, topic_1].sort, [topic_1, topic_2]
558
+ end
559
+
560
+ def test_comparison_with_different_objects
561
+ topic = Topic.create
562
+ category = Category.create(:name => "comparison")
563
+ assert_nil topic <=> category
564
+ end
565
+
566
+ def test_readonly_attributes
567
+ assert_equal Set.new([ 'title' , 'comments_count' ]), ReadonlyTitlePost.readonly_attributes
568
+
569
+ post = ReadonlyTitlePost.create(:title => "cannot change this", :body => "changeable")
570
+ post.reload
571
+ assert_equal "cannot change this", post.title
572
+
573
+ post.update_attributes(:title => "try to change", :body => "changed")
574
+ post.reload
575
+ assert_equal "cannot change this", post.title
576
+ assert_equal "changed", post.body
577
+ end
578
+
579
+ def test_non_valid_identifier_column_name
580
+ weird = Weird.create('a$b' => 'value')
581
+ weird.reload
582
+ assert_equal 'value', weird.send('a$b')
583
+ assert_equal 'value', weird.read_attribute('a$b')
584
+
585
+ weird.update_column('a$b', 'value2')
586
+ weird.reload
587
+ assert_equal 'value2', weird.send('a$b')
588
+ assert_equal 'value2', weird.read_attribute('a$b')
589
+ end
590
+
591
+ def test_multiparameter_attributes_on_date
592
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
593
+ topic = Topic.find(1)
594
+ topic.attributes = attributes
595
+ # note that extra #to_date call allows test to pass for Oracle, which
596
+ # treats dates/times the same
597
+ assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date
598
+ end
599
+
600
+ def test_multiparameter_attributes_on_date_with_empty_year
601
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "24" }
602
+ topic = Topic.find(1)
603
+ topic.attributes = attributes
604
+ # note that extra #to_date call allows test to pass for Oracle, which
605
+ # treats dates/times the same
606
+ assert_nil topic.last_read
607
+ end
608
+
609
+ def test_multiparameter_attributes_on_date_with_empty_month
610
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "24" }
611
+ topic = Topic.find(1)
612
+ topic.attributes = attributes
613
+ # note that extra #to_date call allows test to pass for Oracle, which
614
+ # treats dates/times the same
615
+ assert_nil topic.last_read
616
+ end
617
+
618
+ def test_multiparameter_attributes_on_date_with_empty_day
619
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
620
+ topic = Topic.find(1)
621
+ topic.attributes = attributes
622
+ # note that extra #to_date call allows test to pass for Oracle, which
623
+ # treats dates/times the same
624
+ assert_nil topic.last_read
625
+ end
626
+
627
+ def test_multiparameter_attributes_on_date_with_empty_day_and_year
628
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "" }
629
+ topic = Topic.find(1)
630
+ topic.attributes = attributes
631
+ # note that extra #to_date call allows test to pass for Oracle, which
632
+ # treats dates/times the same
633
+ assert_nil topic.last_read
634
+ end
635
+
636
+ def test_multiparameter_attributes_on_date_with_empty_day_and_month
637
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "" }
638
+ topic = Topic.find(1)
639
+ topic.attributes = attributes
640
+ # note that extra #to_date call allows test to pass for Oracle, which
641
+ # treats dates/times the same
642
+ assert_nil topic.last_read
643
+ end
644
+
645
+ def test_multiparameter_attributes_on_date_with_empty_year_and_month
646
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "24" }
647
+ topic = Topic.find(1)
648
+ topic.attributes = attributes
649
+ # note that extra #to_date call allows test to pass for Oracle, which
650
+ # treats dates/times the same
651
+ assert_nil topic.last_read
652
+ end
653
+
654
+ def test_multiparameter_attributes_on_date_with_all_empty
655
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" }
656
+ topic = Topic.find(1)
657
+ topic.attributes = attributes
658
+ assert_nil topic.last_read
659
+ end
660
+
661
+ def test_multiparameter_attributes_on_time
662
+ attributes = {
663
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
664
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
665
+ }
666
+ topic = Topic.find(1)
667
+ topic.attributes = attributes
668
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
669
+ end
670
+
671
+ def test_multiparameter_attributes_on_time_with_no_date
672
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
673
+ attributes = {
674
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
675
+ }
676
+ topic = Topic.find(1)
677
+ topic.attributes = attributes
678
+ end
679
+ assert_equal("written_on", ex.errors[0].attribute)
680
+ end
681
+
682
+ def test_multiparameter_attributes_on_time_with_invalid_time_params
683
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
684
+ attributes = {
685
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
686
+ "written_on(4i)" => "2004", "written_on(5i)" => "36", "written_on(6i)" => "64",
687
+ }
688
+ topic = Topic.find(1)
689
+ topic.attributes = attributes
690
+ end
691
+ assert_equal("written_on", ex.errors[0].attribute)
692
+ end
693
+
694
+ def test_multiparameter_attributes_on_time_with_old_date
695
+ attributes = {
696
+ "written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24",
697
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
698
+ }
699
+ topic = Topic.find(1)
700
+ topic.attributes = attributes
701
+ # testing against to_s(:db) representation because either a Time or a DateTime might be returned, depending on platform
702
+ assert_equal "1850-06-24 16:24:00", topic.written_on.to_s(:db)
703
+ end
704
+
705
+ def test_multiparameter_attributes_on_time_will_raise_on_big_time_if_missing_date_parts
706
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
707
+ attributes = {
708
+ "written_on(4i)" => "16", "written_on(5i)" => "24"
709
+ }
710
+ topic = Topic.find(1)
711
+ topic.attributes = attributes
712
+ end
713
+ assert_equal("written_on", ex.errors[0].attribute)
714
+ end
715
+
716
+ def test_multiparameter_attributes_on_time_with_raise_on_small_time_if_missing_date_parts
717
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
718
+ attributes = {
719
+ "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02"
720
+ }
721
+ topic = Topic.find(1)
722
+ topic.attributes = attributes
723
+ end
724
+ assert_equal("written_on", ex.errors[0].attribute)
725
+ end
726
+
727
+ def test_multiparameter_attributes_on_time_will_ignore_hour_if_missing
728
+ attributes = {
729
+ "written_on(1i)" => "2004", "written_on(2i)" => "12", "written_on(3i)" => "12",
730
+ "written_on(5i)" => "12", "written_on(6i)" => "02"
731
+ }
732
+ topic = Topic.find(1)
733
+ topic.attributes = attributes
734
+ assert_equal Time.local(2004, 12, 12, 0, 12, 2), topic.written_on
735
+ end
736
+
737
+ def test_multiparameter_attributes_on_time_will_ignore_hour_if_blank
738
+ attributes = {
739
+ "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
740
+ "written_on(4i)" => "", "written_on(5i)" => "12", "written_on(6i)" => "02"
741
+ }
742
+ topic = Topic.find(1)
743
+ topic.attributes = attributes
744
+ assert_nil topic.written_on
745
+ end
746
+
747
+ def test_multiparameter_attributes_on_time_will_ignore_date_if_empty
748
+ attributes = {
749
+ "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
750
+ "written_on(4i)" => "16", "written_on(5i)" => "24"
751
+ }
752
+ topic = Topic.find(1)
753
+ topic.attributes = attributes
754
+ assert_nil topic.written_on
755
+ end
756
+ def test_multiparameter_attributes_on_time_with_seconds_will_ignore_date_if_empty
757
+ attributes = {
758
+ "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
759
+ "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02"
760
+ }
761
+ topic = Topic.find(1)
762
+ topic.attributes = attributes
763
+ assert_nil topic.written_on
764
+ end
765
+
766
+ def test_multiparameter_attributes_on_time_with_utc
767
+ ActiveRecord::Base.default_timezone = :utc
768
+ attributes = {
769
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
770
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
771
+ }
772
+ topic = Topic.find(1)
773
+ topic.attributes = attributes
774
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
775
+ ensure
776
+ ActiveRecord::Base.default_timezone = :local
777
+ end
778
+
779
+ def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes
780
+ ActiveRecord::Base.time_zone_aware_attributes = true
781
+ ActiveRecord::Base.default_timezone = :utc
782
+ Time.zone = ActiveSupport::TimeZone[-28800]
783
+ attributes = {
784
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
785
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
786
+ }
787
+ topic = Topic.find(1)
788
+ topic.attributes = attributes
789
+ assert_equal Time.utc(2004, 6, 24, 23, 24, 0), topic.written_on
790
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on.time
791
+ assert_equal Time.zone, topic.written_on.time_zone
792
+ ensure
793
+ ActiveRecord::Base.time_zone_aware_attributes = false
794
+ ActiveRecord::Base.default_timezone = :local
795
+ Time.zone = nil
796
+ end
797
+
798
+ def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false
799
+ ActiveRecord::Base.time_zone_aware_attributes = false
800
+ Time.zone = ActiveSupport::TimeZone[-28800]
801
+ attributes = {
802
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
803
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
804
+ }
805
+ topic = Topic.find(1)
806
+ topic.attributes = attributes
807
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
808
+ assert_equal false, topic.written_on.respond_to?(:time_zone)
809
+ ensure
810
+ Time.zone = nil
811
+ end
812
+
813
+ def test_multiparameter_attributes_on_time_with_skip_time_zone_conversion_for_attributes
814
+ ActiveRecord::Base.time_zone_aware_attributes = true
815
+ ActiveRecord::Base.default_timezone = :utc
816
+ Time.zone = ActiveSupport::TimeZone[-28800]
817
+ Topic.skip_time_zone_conversion_for_attributes = [:written_on]
818
+ attributes = {
819
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
820
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
821
+ }
822
+ topic = Topic.find(1)
823
+ topic.attributes = attributes
824
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
825
+ assert_equal false, topic.written_on.respond_to?(:time_zone)
826
+ ensure
827
+ ActiveRecord::Base.time_zone_aware_attributes = false
828
+ ActiveRecord::Base.default_timezone = :local
829
+ Time.zone = nil
830
+ Topic.skip_time_zone_conversion_for_attributes = []
831
+ end
832
+
833
+ # Oracle, and Sybase do not have a TIME datatype.
834
+ unless current_adapter?(:OracleAdapter, :SybaseAdapter)
835
+ def test_multiparameter_attributes_on_time_only_column_with_time_zone_aware_attributes_does_not_do_time_zone_conversion
836
+ ActiveRecord::Base.time_zone_aware_attributes = true
837
+ ActiveRecord::Base.default_timezone = :utc
838
+ Time.zone = ActiveSupport::TimeZone[-28800]
839
+ attributes = {
840
+ "bonus_time(1i)" => "2000", "bonus_time(2i)" => "1", "bonus_time(3i)" => "1",
841
+ "bonus_time(4i)" => "16", "bonus_time(5i)" => "24"
842
+ }
843
+ topic = Topic.find(1)
844
+ topic.attributes = attributes
845
+ assert_equal Time.utc(2000, 1, 1, 16, 24, 0), topic.bonus_time
846
+ assert topic.bonus_time.utc?
847
+ ensure
848
+ ActiveRecord::Base.time_zone_aware_attributes = false
849
+ ActiveRecord::Base.default_timezone = :local
850
+ Time.zone = nil
851
+ end
852
+ end
853
+
854
+ def test_multiparameter_attributes_on_time_with_empty_seconds
855
+ attributes = {
856
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
857
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => ""
858
+ }
859
+ topic = Topic.find(1)
860
+ topic.attributes = attributes
861
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
862
+ end
863
+
864
+ def test_multiparameter_assignment_of_aggregation
865
+ customer = Customer.new
866
+ address = Address.new("The Street", "The City", "The Country")
867
+ attributes = { "address(1)" => address.street, "address(2)" => address.city, "address(3)" => address.country }
868
+ customer.attributes = attributes
869
+ assert_equal address, customer.address
870
+ end
871
+
872
+ def test_multiparameter_assignment_of_aggregation_out_of_order
873
+ customer = Customer.new
874
+ address = Address.new("The Street", "The City", "The Country")
875
+ attributes = { "address(3)" => address.country, "address(2)" => address.city, "address(1)" => address.street }
876
+ customer.attributes = attributes
877
+ assert_equal address, customer.address
878
+ end
879
+
880
+ def test_multiparameter_assignment_of_aggregation_with_missing_values
881
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
882
+ customer = Customer.new
883
+ address = Address.new("The Street", "The City", "The Country")
884
+ attributes = { "address(2)" => address.city, "address(3)" => address.country }
885
+ customer.attributes = attributes
886
+ end
887
+ assert_equal("address", ex.errors[0].attribute)
888
+ end
889
+
890
+ def test_multiparameter_assignment_of_aggregation_with_blank_values
891
+ customer = Customer.new
892
+ address = Address.new("The Street", "The City", "The Country")
893
+ attributes = { "address(1)" => "", "address(2)" => address.city, "address(3)" => address.country }
894
+ customer.attributes = attributes
895
+ assert_equal Address.new(nil, "The City", "The Country"), customer.address
896
+ end
897
+
898
+ def test_multiparameter_assignment_of_aggregation_with_large_index
899
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
900
+ customer = Customer.new
901
+ address = Address.new("The Street", "The City", "The Country")
902
+ attributes = { "address(1)" => "The Street", "address(2)" => address.city, "address(3000)" => address.country }
903
+ customer.attributes = attributes
904
+ end
905
+ assert_equal("address", ex.errors[0].attribute)
906
+ end
907
+
908
+ def test_attributes_on_dummy_time
909
+ # Oracle, and Sybase do not have a TIME datatype.
910
+ return true if current_adapter?(:OracleAdapter, :SybaseAdapter)
911
+
912
+ attributes = {
913
+ "bonus_time" => "5:42:00AM"
914
+ }
915
+ topic = Topic.find(1)
916
+ topic.attributes = attributes
917
+ assert_equal Time.local(2000, 1, 1, 5, 42, 0), topic.bonus_time
918
+ end
919
+
920
+ def test_boolean
921
+ b_nil = Boolean.create({ "value" => nil })
922
+ nil_id = b_nil.id
923
+ b_false = Boolean.create({ "value" => false })
924
+ false_id = b_false.id
925
+ b_true = Boolean.create({ "value" => true })
926
+ true_id = b_true.id
927
+
928
+ b_nil = Boolean.find(nil_id)
929
+ assert_nil b_nil.value
930
+ b_false = Boolean.find(false_id)
931
+ assert !b_false.value?
932
+ b_true = Boolean.find(true_id)
933
+ assert b_true.value?
934
+ end
935
+
936
+ def test_boolean_cast_from_string
937
+ b_blank = Boolean.create({ "value" => "" })
938
+ blank_id = b_blank.id
939
+ b_false = Boolean.create({ "value" => "0" })
940
+ false_id = b_false.id
941
+ b_true = Boolean.create({ "value" => "1" })
942
+ true_id = b_true.id
943
+
944
+ b_blank = Boolean.find(blank_id)
945
+ assert_nil b_blank.value
946
+ b_false = Boolean.find(false_id)
947
+ assert !b_false.value?
948
+ b_true = Boolean.find(true_id)
949
+ assert b_true.value?
950
+ end
951
+
952
+ def test_new_record_returns_boolean
953
+ assert_equal false, Topic.new.persisted?
954
+ assert_equal true, Topic.find(1).persisted?
955
+ end
956
+
957
+ def test_dup
958
+ topic = Topic.find(1)
959
+ duped_topic = nil
960
+ assert_nothing_raised { duped_topic = topic.dup }
961
+ assert_equal topic.title, duped_topic.title
962
+ assert !duped_topic.persisted?
963
+
964
+ # test if the attributes have been duped
965
+ topic.title = "a"
966
+ duped_topic.title = "b"
967
+ assert_equal "a", topic.title
968
+ assert_equal "b", duped_topic.title
969
+
970
+ # test if the attribute values have been duped
971
+ topic.title = {"a" => "b"}
972
+ duped_topic = topic.dup
973
+ duped_topic.title["a"] = "c"
974
+ assert_equal "b", topic.title["a"]
975
+
976
+ # test if attributes set as part of after_initialize are duped correctly
977
+ assert_equal topic.author_email_address, duped_topic.author_email_address
978
+
979
+ # test if saved clone object differs from original
980
+ duped_topic.save
981
+ assert duped_topic.persisted?
982
+ assert_not_equal duped_topic.id, topic.id
983
+
984
+ duped_topic.reload
985
+ # FIXME: I think this is poor behavior, and will fix it with #5686
986
+ assert_equal({'a' => 'c'}.to_yaml, duped_topic.title)
987
+ end
988
+
989
+ def test_dup_with_aggregate_of_same_name_as_attribute
990
+ dev = DeveloperWithAggregate.find(1)
991
+ assert_kind_of DeveloperSalary, dev.salary
992
+
993
+ dup = nil
994
+ assert_nothing_raised { dup = dev.dup }
995
+ assert_kind_of DeveloperSalary, dup.salary
996
+ assert_equal dev.salary.amount, dup.salary.amount
997
+ assert !dup.persisted?
998
+
999
+ # test if the attributes have been dupd
1000
+ original_amount = dup.salary.amount
1001
+ dev.salary.amount = 1
1002
+ assert_equal original_amount, dup.salary.amount
1003
+
1004
+ assert dup.save
1005
+ assert dup.persisted?
1006
+ assert_not_equal dup.id, dev.id
1007
+ end
1008
+
1009
+ def test_dup_does_not_copy_associations
1010
+ author = authors(:david)
1011
+ assert_not_equal [], author.posts
1012
+ author.send(:clear_association_cache)
1013
+
1014
+ author_dup = author.dup
1015
+ assert_equal [], author_dup.posts
1016
+ end
1017
+
1018
+ def test_clone_preserves_subtype
1019
+ clone = nil
1020
+ assert_nothing_raised { clone = Company.find(3).clone }
1021
+ assert_kind_of Client, clone
1022
+ end
1023
+
1024
+ def test_clone_of_new_object_with_defaults
1025
+ developer = Developer.new
1026
+ assert !developer.name_changed?
1027
+ assert !developer.salary_changed?
1028
+
1029
+ cloned_developer = developer.clone
1030
+ assert !cloned_developer.name_changed?
1031
+ assert !cloned_developer.salary_changed?
1032
+ end
1033
+
1034
+ def test_clone_of_new_object_marks_attributes_as_dirty
1035
+ developer = Developer.new :name => 'Bjorn', :salary => 100000
1036
+ assert developer.name_changed?
1037
+ assert developer.salary_changed?
1038
+
1039
+ cloned_developer = developer.clone
1040
+ assert cloned_developer.name_changed?
1041
+ assert cloned_developer.salary_changed?
1042
+ end
1043
+
1044
+ def test_clone_of_new_object_marks_as_dirty_only_changed_attributes
1045
+ developer = Developer.new :name => 'Bjorn'
1046
+ assert developer.name_changed? # obviously
1047
+ assert !developer.salary_changed? # attribute has non-nil default value, so treated as not changed
1048
+
1049
+ cloned_developer = developer.clone
1050
+ assert cloned_developer.name_changed?
1051
+ assert !cloned_developer.salary_changed? # ... and cloned instance should behave same
1052
+ end
1053
+
1054
+ def test_dup_of_saved_object_marks_attributes_as_dirty
1055
+ developer = Developer.create! :name => 'Bjorn', :salary => 100000
1056
+ assert !developer.name_changed?
1057
+ assert !developer.salary_changed?
1058
+
1059
+ cloned_developer = developer.dup
1060
+ assert cloned_developer.name_changed? # both attributes differ from defaults
1061
+ assert cloned_developer.salary_changed?
1062
+ end
1063
+
1064
+ def test_dup_of_saved_object_marks_as_dirty_only_changed_attributes
1065
+ developer = Developer.create! :name => 'Bjorn'
1066
+ assert !developer.name_changed? # both attributes of saved object should be treated as not changed
1067
+ assert !developer.salary_changed?
1068
+
1069
+ cloned_developer = developer.dup
1070
+ assert cloned_developer.name_changed? # ... but on cloned object should be
1071
+ assert !cloned_developer.salary_changed? # ... BUT salary has non-nil default which should be treated as not changed on cloned instance
1072
+ end
1073
+
1074
+ def test_bignum
1075
+ company = Company.find(1)
1076
+ company.rating = 2147483647
1077
+ company.save
1078
+ company = Company.find(1)
1079
+ assert_equal 2147483647, company.rating
1080
+ end
1081
+
1082
+ # TODO: extend defaults tests to other databases!
1083
+ if current_adapter?(:PostgreSQLAdapter)
1084
+ def test_default
1085
+ default = Default.new
1086
+
1087
+ # fixed dates / times
1088
+ assert_equal Date.new(2004, 1, 1), default.fixed_date
1089
+ assert_equal Time.local(2004, 1,1,0,0,0,0), default.fixed_time
1090
+
1091
+ # char types
1092
+ assert_equal 'Y', default.char1
1093
+ assert_equal 'a varchar field', default.char2
1094
+ assert_equal 'a text field', default.char3
1095
+ end
1096
+
1097
+ class Geometric < ActiveRecord::Base; end
1098
+ def test_geometric_content
1099
+
1100
+ # accepted format notes:
1101
+ # ()'s aren't required
1102
+ # values can be a mix of float or integer
1103
+
1104
+ g = Geometric.new(
1105
+ :a_point => '(5.0, 6.1)',
1106
+ #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
1107
+ :a_line_segment => '(2.0, 3), (5.5, 7.0)',
1108
+ :a_box => '2.0, 3, 5.5, 7.0',
1109
+ :a_path => '[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]', # [ ] is an open path
1110
+ :a_polygon => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))',
1111
+ :a_circle => '<(5.3, 10.4), 2>'
1112
+ )
1113
+
1114
+ assert g.save
1115
+
1116
+ # Reload and check that we have all the geometric attributes.
1117
+ h = ActiveRecord::IdentityMap.without { Geometric.find(g.id) }
1118
+
1119
+ assert_equal '(5,6.1)', h.a_point
1120
+ assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
1121
+ assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
1122
+ assert_equal '[(2,3),(5.5,7),(8.5,11)]', h.a_path
1123
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
1124
+ assert_equal '<(5.3,10.4),2>', h.a_circle
1125
+
1126
+ # use a geometric function to test for an open path
1127
+ objs = Geometric.find_by_sql ["select isopen(a_path) from geometrics where id = ?", g.id]
1128
+ assert_equal objs[0].isopen, 't'
1129
+
1130
+ # test alternate formats when defining the geometric types
1131
+
1132
+ g = Geometric.new(
1133
+ :a_point => '5.0, 6.1',
1134
+ #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
1135
+ :a_line_segment => '((2.0, 3), (5.5, 7.0))',
1136
+ :a_box => '(2.0, 3), (5.5, 7.0)',
1137
+ :a_path => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))', # ( ) is a closed path
1138
+ :a_polygon => '2.0, 3, 5.5, 7.0, 8.5, 11.0',
1139
+ :a_circle => '((5.3, 10.4), 2)'
1140
+ )
1141
+
1142
+ assert g.save
1143
+
1144
+ # Reload and check that we have all the geometric attributes.
1145
+ h = ActiveRecord::IdentityMap.without { Geometric.find(g.id) }
1146
+
1147
+ assert_equal '(5,6.1)', h.a_point
1148
+ assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
1149
+ assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
1150
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path
1151
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
1152
+ assert_equal '<(5.3,10.4),2>', h.a_circle
1153
+
1154
+ # use a geometric function to test for an closed path
1155
+ objs = Geometric.find_by_sql ["select isclosed(a_path) from geometrics where id = ?", g.id]
1156
+ assert_equal objs[0].isclosed, 't'
1157
+ end
1158
+ end
1159
+
1160
+ class NumericData < ActiveRecord::Base
1161
+ self.table_name = 'numeric_data'
1162
+ end
1163
+
1164
+ def test_big_decimal_conditions
1165
+ m = NumericData.new(
1166
+ :bank_balance => 1586.43,
1167
+ :big_bank_balance => BigDecimal("1000234000567.95"),
1168
+ :world_population => 6000000000,
1169
+ :my_house_population => 3
1170
+ )
1171
+ assert m.save
1172
+ assert_equal 0, NumericData.where("bank_balance > ?", 2000.0).count
1173
+ end
1174
+
1175
+ def test_numeric_fields
1176
+ m = NumericData.new(
1177
+ :bank_balance => 1586.43,
1178
+ :big_bank_balance => BigDecimal("1000234000567.95"),
1179
+ :world_population => 6000000000,
1180
+ :my_house_population => 3
1181
+ )
1182
+ assert m.save
1183
+
1184
+ m1 = NumericData.find(m.id)
1185
+ assert_not_nil m1
1186
+
1187
+ # As with migration_test.rb, we should make world_population >= 2**62
1188
+ # to cover 64-bit platforms and test it is a Bignum, but the main thing
1189
+ # is that it's an Integer.
1190
+ unless current_adapter?(:IBM_DBAdapter)
1191
+ assert_kind_of Integer, m1.world_population
1192
+ else
1193
+ assert_kind_of BigDecimal, m1.world_population
1194
+ end
1195
+ assert_equal 6000000000, m1.world_population
1196
+
1197
+ unless current_adapter?(:IBM_DBAdapter)
1198
+ assert_kind_of Fixnum, m1.my_house_population
1199
+ else
1200
+ assert_kind_of BigDecimal, m1.my_house_population
1201
+ end
1202
+ assert_equal 3, m1.my_house_population
1203
+
1204
+ assert_kind_of BigDecimal, m1.bank_balance
1205
+ assert_equal BigDecimal("1586.43"), m1.bank_balance
1206
+
1207
+ assert_kind_of BigDecimal, m1.big_bank_balance
1208
+ assert_equal BigDecimal("1000234000567.95"), m1.big_bank_balance
1209
+ end
1210
+
1211
+ def test_auto_id
1212
+ auto = AutoId.new
1213
+ auto.save
1214
+ assert(auto.id > 0)
1215
+ end
1216
+
1217
+ def test_sql_injection_via_find
1218
+ assert_raise(ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid) do
1219
+ Topic.find("123456 OR id > 0")
1220
+ end
1221
+ end
1222
+
1223
+ def test_column_name_properly_quoted
1224
+ col_record = ColumnName.new
1225
+ col_record.references = 40
1226
+ assert col_record.save
1227
+ col_record.references = 41
1228
+ assert col_record.save
1229
+ assert_not_nil c2 = ColumnName.find(col_record.id)
1230
+ assert_equal(41, c2.references)
1231
+ end
1232
+
1233
+ def test_quoting_arrays
1234
+ replies = Reply.find(:all, :conditions => [ "id IN (?)", topics(:first).replies.collect(&:id) ])
1235
+ assert_equal topics(:first).replies.size, replies.size
1236
+
1237
+ replies = Reply.find(:all, :conditions => [ "id IN (?)", [] ])
1238
+ assert_equal 0, replies.size
1239
+ end
1240
+
1241
+ MyObject = Struct.new :attribute1, :attribute2
1242
+
1243
+ def test_serialized_attribute
1244
+ Topic.serialize("content", MyObject)
1245
+
1246
+ myobj = MyObject.new('value1', 'value2')
1247
+ topic = Topic.create("content" => myobj)
1248
+ assert_equal(myobj, topic.content)
1249
+
1250
+ topic.reload
1251
+ assert_equal(myobj, topic.content)
1252
+ end
1253
+
1254
+ def test_serialized_attribute_in_base_class
1255
+ Topic.serialize("content", Hash)
1256
+
1257
+ hash = { 'content1' => 'value1', 'content2' => 'value2' }
1258
+ important_topic = ImportantTopic.create("content" => hash)
1259
+ assert_equal(hash, important_topic.content)
1260
+
1261
+ important_topic.reload
1262
+ assert_equal(hash, important_topic.content)
1263
+ end
1264
+
1265
+ # This test was added to fix GH #4004. Obviously the value returned
1266
+ # is not really the value 'before type cast' so we should maybe think
1267
+ # about changing that in the future.
1268
+ def test_serialized_attribute_before_type_cast_returns_unserialized_value
1269
+ klass = Class.new(ActiveRecord::Base)
1270
+ klass.table_name = "topics"
1271
+ klass.serialize :content, Hash
1272
+
1273
+ t = klass.new(:content => { :foo => :bar })
1274
+ assert_equal({ :foo => :bar }, t.content_before_type_cast)
1275
+ t.save!
1276
+ t.reload
1277
+ assert_equal({ :foo => :bar }, t.content_before_type_cast)
1278
+ end
1279
+
1280
+ def test_serialized_attribute_declared_in_subclass
1281
+ hash = { 'important1' => 'value1', 'important2' => 'value2' }
1282
+ important_topic = ImportantTopic.create("important" => hash)
1283
+ assert_equal(hash, important_topic.important)
1284
+
1285
+ important_topic.reload
1286
+ assert_equal(hash, important_topic.important)
1287
+ assert_equal(hash, important_topic.read_attribute(:important))
1288
+ end
1289
+
1290
+ def test_serialized_time_attribute
1291
+ myobj = Time.local(2008,1,1,1,0)
1292
+ topic = Topic.create("content" => myobj).reload
1293
+ assert_equal(myobj, topic.content)
1294
+ end
1295
+
1296
+ def test_serialized_string_attribute
1297
+ myobj = "Yes"
1298
+ topic = Topic.create("content" => myobj).reload
1299
+ assert_equal(myobj, topic.content)
1300
+ end
1301
+
1302
+ def test_nil_serialized_attribute_with_class_constraint
1303
+ topic = Topic.new
1304
+ assert_nil topic.content
1305
+ end
1306
+
1307
+ def test_should_raise_exception_on_serialized_attribute_with_type_mismatch
1308
+ myobj = MyObject.new('value1', 'value2')
1309
+ topic = Topic.new(:content => myobj)
1310
+ assert topic.save
1311
+ Topic.serialize(:content, Hash)
1312
+ assert_raise(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).reload.content }
1313
+ ensure
1314
+ Topic.serialize(:content)
1315
+ end
1316
+
1317
+ def test_serialized_attribute_with_class_constraint
1318
+ settings = { "color" => "blue" }
1319
+ Topic.serialize(:content, Hash)
1320
+ topic = Topic.new(:content => settings)
1321
+ assert topic.save
1322
+ assert_equal(settings, Topic.find(topic.id).content)
1323
+ ensure
1324
+ Topic.serialize(:content)
1325
+ end
1326
+
1327
+ def test_serialized_default_class
1328
+ Topic.serialize(:content, Hash)
1329
+ topic = Topic.new
1330
+ assert_equal Hash, topic.content.class
1331
+ assert_equal Hash, topic.read_attribute(:content).class
1332
+ topic.content["beer"] = "MadridRb"
1333
+ assert topic.save
1334
+ topic.reload
1335
+ assert_equal Hash, topic.content.class
1336
+ assert_equal "MadridRb", topic.content["beer"]
1337
+ ensure
1338
+ Topic.serialize(:content)
1339
+ end
1340
+
1341
+ def test_serialized_no_default_class_for_object
1342
+ topic = Topic.new
1343
+ assert_nil topic.content
1344
+ end
1345
+
1346
+ def test_serialized_boolean_value_true
1347
+ Topic.serialize(:content)
1348
+ topic = Topic.new(:content => true)
1349
+ assert topic.save
1350
+ topic = topic.reload
1351
+ assert_equal topic.content, true
1352
+ end
1353
+
1354
+ def test_serialized_boolean_value_false
1355
+ Topic.serialize(:content)
1356
+ topic = Topic.new(:content => false)
1357
+ assert topic.save
1358
+ topic = topic.reload
1359
+ assert_equal topic.content, false
1360
+ end
1361
+
1362
+ def test_serialize_with_coder
1363
+ coder = Class.new {
1364
+ # Identity
1365
+ def load(thing)
1366
+ thing
1367
+ end
1368
+
1369
+ # base 64
1370
+ def dump(thing)
1371
+ [thing].pack('m')
1372
+ end
1373
+ }.new
1374
+
1375
+ Topic.serialize(:content, coder)
1376
+ s = 'hello world'
1377
+ topic = Topic.new(:content => s)
1378
+ assert topic.save
1379
+ topic = topic.reload
1380
+ assert_equal [s].pack('m'), topic.content
1381
+ ensure
1382
+ Topic.serialize(:content)
1383
+ end
1384
+
1385
+ def test_serialize_with_bcrypt_coder
1386
+ crypt_coder = Class.new {
1387
+ def load(thing)
1388
+ return unless thing
1389
+ BCrypt::Password.new thing
1390
+ end
1391
+
1392
+ def dump(thing)
1393
+ BCrypt::Password.create(thing).to_s
1394
+ end
1395
+ }.new
1396
+
1397
+ Topic.serialize(:content, crypt_coder)
1398
+ password = 'password'
1399
+ topic = Topic.new(:content => password)
1400
+ assert topic.save
1401
+ topic = topic.reload
1402
+ assert_kind_of BCrypt::Password, topic.content
1403
+ assert_equal(true, topic.content == password, 'password should equal')
1404
+ ensure
1405
+ Topic.serialize(:content)
1406
+ end
1407
+
1408
+ def test_quote
1409
+ author_name = "\\ \001 ' \n \\n \""
1410
+ topic = Topic.create('author_name' => author_name)
1411
+ assert_equal author_name, Topic.find(topic.id).author_name
1412
+ end
1413
+
1414
+ if RUBY_VERSION < '1.9'
1415
+ def test_quote_chars
1416
+ with_kcode('UTF8') do
1417
+ str = 'The Narrator'
1418
+ topic = Topic.create(:author_name => str)
1419
+ assert_equal str, topic.author_name
1420
+
1421
+ assert_kind_of ActiveSupport::Multibyte.proxy_class, str.mb_chars
1422
+ topic = Topic.find_by_author_name(str.mb_chars)
1423
+
1424
+ assert_kind_of Topic, topic
1425
+ assert_equal str, topic.author_name, "The right topic should have been found by name even with name passed as Chars"
1426
+ end
1427
+ end
1428
+ end
1429
+
1430
+ def test_toggle_attribute
1431
+ assert !topics(:first).approved?
1432
+ topics(:first).toggle!(:approved)
1433
+ assert topics(:first).approved?
1434
+ topic = topics(:first)
1435
+ topic.toggle(:approved)
1436
+ assert !topic.approved?
1437
+ topic.reload
1438
+ assert topic.approved?
1439
+ end
1440
+
1441
+ def test_reload
1442
+ t1 = Topic.find(1)
1443
+ t2 = Topic.find(1)
1444
+ t1.title = "something else"
1445
+ t1.save
1446
+ t2.reload
1447
+ assert_equal t1.title, t2.title
1448
+ end
1449
+
1450
+ def test_reload_with_exclusive_scope
1451
+ dev = DeveloperCalledDavid.first
1452
+ dev.update_attributes!( :name => "NotDavid" )
1453
+ assert_equal dev, dev.reload
1454
+ end
1455
+
1456
+ def test_set_table_name_with_value
1457
+ k = Class.new( ActiveRecord::Base )
1458
+ k.table_name = "foo"
1459
+ assert_equal "foo", k.table_name
1460
+
1461
+ assert_deprecated do
1462
+ k.set_table_name "bar"
1463
+ end
1464
+ assert_equal "bar", k.table_name
1465
+ end
1466
+
1467
+ def test_switching_between_table_name
1468
+ assert_difference("GoodJoke.count") do
1469
+ Joke.table_name = "cold_jokes"
1470
+ Joke.create
1471
+
1472
+ Joke.table_name = "funny_jokes"
1473
+ Joke.create
1474
+ end
1475
+ end
1476
+
1477
+ def test_set_table_name_symbol_converted_to_string
1478
+ Joke.table_name = :cold_jokes
1479
+ assert_equal 'cold_jokes', Joke.table_name
1480
+ end
1481
+
1482
+ def test_quoted_table_name_after_set_table_name
1483
+ klass = Class.new(ActiveRecord::Base)
1484
+
1485
+ klass.table_name = "foo"
1486
+ assert_equal "foo", klass.table_name
1487
+ assert_equal klass.connection.quote_table_name("foo"), klass.quoted_table_name
1488
+
1489
+ klass.table_name = "bar"
1490
+ assert_equal "bar", klass.table_name
1491
+ assert_equal klass.connection.quote_table_name("bar"), klass.quoted_table_name
1492
+ end
1493
+
1494
+ def test_set_table_name_with_block
1495
+ k = Class.new( ActiveRecord::Base )
1496
+ assert_deprecated do
1497
+ k.set_table_name "foo"
1498
+ k.set_table_name do
1499
+ ActiveSupport::Deprecation.silence { original_table_name } + "ks"
1500
+ end
1501
+ end
1502
+ assert_equal "fooks", k.table_name
1503
+ end
1504
+
1505
+ def test_set_table_name_with_inheritance
1506
+ k = Class.new( ActiveRecord::Base )
1507
+ def k.name; "Foo"; end
1508
+ def k.table_name; super + "ks"; end
1509
+ assert_equal "foosks", k.table_name
1510
+ end
1511
+
1512
+ def test_original_table_name
1513
+ k = Class.new(ActiveRecord::Base)
1514
+ def k.name; "Foo"; end
1515
+ k.table_name = "bar"
1516
+
1517
+ assert_deprecated do
1518
+ assert_equal "foos", k.original_table_name
1519
+ end
1520
+
1521
+ k = Class.new(ActiveRecord::Base)
1522
+ k.table_name = "omg"
1523
+ k.table_name = "wtf"
1524
+
1525
+ assert_deprecated do
1526
+ assert_equal "omg", k.original_table_name
1527
+ end
1528
+ end
1529
+
1530
+ def test_set_primary_key_with_value
1531
+ k = Class.new( ActiveRecord::Base )
1532
+ k.primary_key = "foo"
1533
+ assert_equal "foo", k.primary_key
1534
+
1535
+ assert_deprecated do
1536
+ k.set_primary_key "bar"
1537
+ end
1538
+ assert_equal "bar", k.primary_key
1539
+ end
1540
+
1541
+ def test_set_primary_key_with_block
1542
+ k = Class.new( ActiveRecord::Base )
1543
+ k.primary_key = 'id'
1544
+
1545
+ assert_deprecated do
1546
+ k.set_primary_key do
1547
+ "sys_" + ActiveSupport::Deprecation.silence { original_primary_key }
1548
+ end
1549
+ end
1550
+ assert_equal "sys_id", k.primary_key
1551
+ end
1552
+
1553
+ def test_original_primary_key
1554
+ k = Class.new(ActiveRecord::Base)
1555
+ def k.name; "Foo"; end
1556
+ k.table_name = "posts"
1557
+ k.primary_key = "bar"
1558
+
1559
+ assert_deprecated do
1560
+ assert_equal "id", k.original_primary_key
1561
+ end
1562
+
1563
+ k = Class.new(ActiveRecord::Base)
1564
+ k.primary_key = "omg"
1565
+ k.primary_key = "wtf"
1566
+
1567
+ assert_deprecated do
1568
+ assert_equal "omg", k.original_primary_key
1569
+ end
1570
+ end
1571
+
1572
+ def test_set_inheritance_column_with_value
1573
+ k = Class.new( ActiveRecord::Base )
1574
+ k.inheritance_column = "foo"
1575
+ assert_equal "foo", k.inheritance_column
1576
+
1577
+ assert_deprecated do
1578
+ k.set_inheritance_column "bar"
1579
+ end
1580
+ assert_equal "bar", k.inheritance_column
1581
+ end
1582
+
1583
+ def test_set_inheritance_column_with_block
1584
+ k = Class.new( ActiveRecord::Base )
1585
+ assert_deprecated do
1586
+ k.set_inheritance_column do
1587
+ ActiveSupport::Deprecation.silence { original_inheritance_column } + "_id"
1588
+ end
1589
+ end
1590
+ assert_equal "type_id", k.inheritance_column
1591
+ end
1592
+
1593
+ def test_original_inheritance_column
1594
+ k = Class.new(ActiveRecord::Base)
1595
+ def k.name; "Foo"; end
1596
+ k.inheritance_column = "omg"
1597
+
1598
+ assert_deprecated do
1599
+ assert_equal "type", k.original_inheritance_column
1600
+ end
1601
+ end
1602
+
1603
+ def test_set_sequence_name_with_value
1604
+ k = Class.new( ActiveRecord::Base )
1605
+ k.sequence_name = "foo"
1606
+ assert_equal "foo", k.sequence_name
1607
+
1608
+ assert_deprecated do
1609
+ k.set_sequence_name "bar"
1610
+ end
1611
+ assert_equal "bar", k.sequence_name
1612
+ end
1613
+
1614
+ def test_set_sequence_name_with_block
1615
+ k = Class.new( ActiveRecord::Base )
1616
+ k.table_name = "projects"
1617
+ orig_name = k.sequence_name
1618
+ return skip "sequences not supported by db" unless orig_name
1619
+
1620
+ assert_deprecated do
1621
+ k.set_sequence_name do
1622
+ ActiveSupport::Deprecation.silence { original_sequence_name } + "_lol"
1623
+ end
1624
+ end
1625
+ assert_equal orig_name + "_lol", k.sequence_name
1626
+ end
1627
+
1628
+ def test_original_sequence_name
1629
+ k = Class.new(ActiveRecord::Base)
1630
+ k.table_name = "projects"
1631
+ orig_name = k.sequence_name
1632
+ return skip "sequences not supported by db" unless orig_name
1633
+
1634
+ k = Class.new(ActiveRecord::Base)
1635
+ k.table_name = "projects"
1636
+ k.sequence_name = "omg"
1637
+
1638
+ assert_deprecated do
1639
+ assert_equal orig_name, k.original_sequence_name
1640
+ end
1641
+
1642
+ k = Class.new(ActiveRecord::Base)
1643
+ k.table_name = "projects"
1644
+ k.sequence_name = "omg"
1645
+ k.sequence_name = "wtf"
1646
+ assert_deprecated do
1647
+ assert_equal "omg", k.original_sequence_name
1648
+ end
1649
+ end
1650
+
1651
+ def test_sequence_name_with_abstract_class
1652
+ ak = Class.new(ActiveRecord::Base)
1653
+ ak.abstract_class = true
1654
+ k = Class.new(ak)
1655
+ k.table_name = "projects"
1656
+ orig_name = k.sequence_name
1657
+ return skip "sequences not supported by db" unless orig_name
1658
+ assert_equal k.reset_sequence_name, orig_name
1659
+ end
1660
+
1661
+ def test_count_with_join
1662
+ res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
1663
+
1664
+ res2 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'", :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1665
+ assert_equal res, res2
1666
+
1667
+ res3 = nil
1668
+ assert_nothing_raised do
1669
+ res3 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'",
1670
+ :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1671
+ end
1672
+ assert_equal res, res3
1673
+
1674
+ 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"
1675
+ res5 = nil
1676
+ assert_nothing_raised do
1677
+ res5 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
1678
+ :joins => "p, comments co",
1679
+ :select => "p.id")
1680
+ end
1681
+
1682
+ assert_equal res4, res5
1683
+
1684
+ 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"
1685
+ res7 = nil
1686
+ assert_nothing_raised do
1687
+ res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
1688
+ :joins => "p, comments co",
1689
+ :select => "p.id",
1690
+ :distinct => true)
1691
+ end
1692
+ assert_equal res6, res7
1693
+ end
1694
+
1695
+ def test_scoped_find_conditions
1696
+ scoped_developers = Developer.send(:with_scope, :find => { :conditions => 'salary > 90000' }) do
1697
+ Developer.find(:all, :conditions => 'id < 5')
1698
+ end
1699
+ assert !scoped_developers.include?(developers(:david)) # David's salary is less than 90,000
1700
+ assert_equal 3, scoped_developers.size
1701
+ end
1702
+
1703
+ def test_no_limit_offset
1704
+ assert_nothing_raised do
1705
+ Developer.find(:all, :offset => 2)
1706
+ end
1707
+ end
1708
+
1709
+ def test_scoped_find_limit_offset
1710
+ scoped_developers = Developer.send(:with_scope, :find => { :limit => 3, :offset => 2 }) do
1711
+ Developer.find(:all, :order => 'id')
1712
+ end
1713
+ assert !scoped_developers.include?(developers(:david))
1714
+ assert !scoped_developers.include?(developers(:jamis))
1715
+ assert_equal 3, scoped_developers.size
1716
+
1717
+ # Test without scoped find conditions to ensure we get the whole thing
1718
+ developers = Developer.find(:all, :order => 'id')
1719
+ assert_equal Developer.count, developers.size
1720
+ end
1721
+
1722
+ def test_scoped_find_order
1723
+ # Test order in scope
1724
+ scoped_developers = Developer.send(:with_scope, :find => { :limit => 1, :order => 'salary DESC' }) do
1725
+ Developer.find(:all)
1726
+ end
1727
+ assert_equal 'Jamis', scoped_developers.first.name
1728
+ assert scoped_developers.include?(developers(:jamis))
1729
+ # Test scope without order and order in find
1730
+ scoped_developers = Developer.send(:with_scope, :find => { :limit => 1 }) do
1731
+ Developer.find(:all, :order => 'salary DESC')
1732
+ end
1733
+ # Test scope order + find order, order has priority
1734
+ scoped_developers = Developer.send(:with_scope, :find => { :limit => 3, :order => 'id DESC' }) do
1735
+ Developer.find(:all, :order => 'salary ASC')
1736
+ end
1737
+ assert scoped_developers.include?(developers(:poor_jamis))
1738
+ assert ! scoped_developers.include?(developers(:david))
1739
+ assert ! scoped_developers.include?(developers(:jamis))
1740
+ assert_equal 3, scoped_developers.size
1741
+
1742
+ # Test without scoped find conditions to ensure we get the right thing
1743
+ assert ! scoped_developers.include?(Developer.find(1))
1744
+ assert scoped_developers.include?(Developer.find(11))
1745
+ end
1746
+
1747
+ def test_scoped_find_limit_offset_including_has_many_association
1748
+ topics = Topic.send(:with_scope, :find => {:limit => 1, :offset => 1, :include => :replies}) do
1749
+ Topic.find(:all, :order => "topics.id")
1750
+ end
1751
+ assert_equal 1, topics.size
1752
+ assert_equal 2, topics.first.id
1753
+ end
1754
+
1755
+ def test_scoped_find_order_including_has_many_association
1756
+ developers = Developer.send(:with_scope, :find => { :order => 'developers.salary DESC', :include => :projects }) do
1757
+ Developer.find(:all)
1758
+ end
1759
+ assert developers.size >= 2
1760
+ for i in 1...developers.size
1761
+ assert developers[i-1].salary >= developers[i].salary
1762
+ end
1763
+ end
1764
+
1765
+ def test_scoped_find_with_group_and_having
1766
+ developers = Developer.send(:with_scope, :find => { :group => 'developers.salary', :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" }) do
1767
+ Developer.find(:all)
1768
+ end
1769
+ assert_equal 3, developers.size
1770
+ end
1771
+
1772
+ def test_find_last
1773
+ last = Developer.find :last
1774
+ assert_equal last, Developer.find(:first, :order => 'id desc')
1775
+ end
1776
+
1777
+ def test_last
1778
+ assert_equal Developer.find(:first, :order => 'id desc'), Developer.last
1779
+ end
1780
+
1781
+ def test_all
1782
+ developers = Developer.all
1783
+ assert_kind_of Array, developers
1784
+ assert_equal Developer.find(:all), developers
1785
+ end
1786
+
1787
+ def test_all_with_conditions
1788
+ assert_equal Developer.find(:all, :order => 'id desc'), Developer.order('id desc').all
1789
+ end
1790
+
1791
+ def test_find_ordered_last
1792
+ last = Developer.find :last, :order => 'developers.salary ASC'
1793
+ assert_equal last, Developer.find(:all, :order => 'developers.salary ASC').last
1794
+ end
1795
+
1796
+ def test_find_reverse_ordered_last
1797
+ last = Developer.find :last, :order => 'developers.salary DESC'
1798
+ assert_equal last, Developer.find(:all, :order => 'developers.salary DESC').last
1799
+ end
1800
+
1801
+ def test_find_multiple_ordered_last
1802
+ last = Developer.find :last, :order => 'developers.name, developers.salary DESC'
1803
+ assert_equal last, Developer.find(:all, :order => 'developers.name, developers.salary DESC').last
1804
+ end
1805
+
1806
+ def test_find_keeps_multiple_order_values
1807
+ combined = Developer.find(:all, :order => 'developers.name, developers.salary')
1808
+ assert_equal combined, Developer.find(:all, :order => ['developers.name', 'developers.salary'])
1809
+ end
1810
+
1811
+ def test_find_keeps_multiple_group_values
1812
+ combined = Developer.find(:all, :group => 'developers.name, developers.salary, developers.id, developers.created_at, developers.updated_at')
1813
+ assert_equal combined, Developer.find(:all, :group => ['developers.name', 'developers.salary', 'developers.id', 'developers.created_at', 'developers.updated_at'])
1814
+ end
1815
+
1816
+ def test_find_symbol_ordered_last
1817
+ last = Developer.find :last, :order => :salary
1818
+ assert_equal last, Developer.find(:all, :order => :salary).last
1819
+ end
1820
+
1821
+ def test_find_scoped_ordered_last
1822
+ last_developer = Developer.send(:with_scope, :find => { :order => 'developers.salary ASC' }) do
1823
+ Developer.find(:last)
1824
+ end
1825
+ assert_equal last_developer, Developer.find(:all, :order => 'developers.salary ASC').last
1826
+ end
1827
+
1828
+ def test_abstract_class
1829
+ assert !ActiveRecord::Base.abstract_class?
1830
+ assert LoosePerson.abstract_class?
1831
+ assert !LooseDescendant.abstract_class?
1832
+ end
1833
+
1834
+ def test_abstract_class_table_name
1835
+ assert_nil AbstractCompany.table_name
1836
+ end
1837
+
1838
+ def test_base_class
1839
+ assert_equal LoosePerson, LoosePerson.base_class
1840
+ assert_equal LooseDescendant, LooseDescendant.base_class
1841
+ assert_equal TightPerson, TightPerson.base_class
1842
+ assert_equal TightPerson, TightDescendant.base_class
1843
+
1844
+ assert_equal Post, Post.base_class
1845
+ assert_equal Post, SpecialPost.base_class
1846
+ assert_equal Post, StiPost.base_class
1847
+ assert_equal SubStiPost, SubStiPost.base_class
1848
+ end
1849
+
1850
+ def test_descends_from_active_record
1851
+ # Tries to call Object.abstract_class?
1852
+ assert_raise(NoMethodError) do
1853
+ ActiveRecord::Base.descends_from_active_record?
1854
+ end
1855
+
1856
+ # Abstract subclass of AR::Base.
1857
+ assert LoosePerson.descends_from_active_record?
1858
+
1859
+ # Concrete subclass of an abstract class.
1860
+ assert LooseDescendant.descends_from_active_record?
1861
+
1862
+ # Concrete subclass of AR::Base.
1863
+ assert TightPerson.descends_from_active_record?
1864
+
1865
+ # Concrete subclass of a concrete class but has no type column.
1866
+ assert TightDescendant.descends_from_active_record?
1867
+
1868
+ # Concrete subclass of AR::Base.
1869
+ assert Post.descends_from_active_record?
1870
+
1871
+ # Abstract subclass of a concrete class which has a type column.
1872
+ # This is pathological, as you'll never have Sub < Abstract < Concrete.
1873
+ assert !StiPost.descends_from_active_record?
1874
+
1875
+ # Concrete subclasses an abstract class which has a type column.
1876
+ assert !SubStiPost.descends_from_active_record?
1877
+ end
1878
+
1879
+ def test_find_on_abstract_base_class_doesnt_use_type_condition
1880
+ old_class = LooseDescendant
1881
+ Object.send :remove_const, :LooseDescendant
1882
+
1883
+ descendant = old_class.create! :first_name => 'bob'
1884
+ assert_not_nil LoosePerson.find(descendant.id), "Should have found instance of LooseDescendant when finding abstract LoosePerson: #{descendant.inspect}"
1885
+ ensure
1886
+ unless Object.const_defined?(:LooseDescendant)
1887
+ Object.const_set :LooseDescendant, old_class
1888
+ end
1889
+ end
1890
+
1891
+ def test_assert_queries
1892
+ query = lambda { ActiveRecord::Base.connection.execute 'select count(*) from developers' }
1893
+ assert_queries(2) { 2.times { query.call } }
1894
+ assert_queries 1, &query
1895
+ assert_no_queries { assert true }
1896
+ end
1897
+
1898
+ def test_to_param_should_return_string
1899
+ assert_kind_of String, Client.find(:first).to_param
1900
+ end
1901
+
1902
+ def test_inspect_class
1903
+ assert_equal 'ActiveRecord::Base', ActiveRecord::Base.inspect
1904
+ assert_equal 'LoosePerson(abstract)', LoosePerson.inspect
1905
+ assert_match(/^Topic\(id: integer, title: string/, Topic.inspect)
1906
+ end
1907
+
1908
+ def test_inspect_instance
1909
+ topic = topics(:first)
1910
+ 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", important: nil, approved: false, replies_count: 1, parent_id: nil, parent_title: nil, type: nil, group: nil, created_at: "#{topic.created_at.to_s(:db)}", updated_at: "#{topic.updated_at.to_s(:db)}">), topic.inspect
1911
+ end
1912
+
1913
+ def test_inspect_new_instance
1914
+ assert_match(/Topic id: nil/, Topic.new.inspect)
1915
+ end
1916
+
1917
+ def test_inspect_limited_select_instance
1918
+ assert_equal %(#<Topic id: 1>), Topic.find(:first, :select => 'id', :conditions => 'id = 1').inspect
1919
+ assert_equal %(#<Topic id: 1, title: "The First Topic">), Topic.find(:first, :select => 'id, title', :conditions => 'id = 1').inspect
1920
+ end
1921
+
1922
+ def test_inspect_class_without_table
1923
+ assert_equal "NonExistentTable(Table doesn't exist)", NonExistentTable.inspect
1924
+ end
1925
+
1926
+ def test_attribute_for_inspect
1927
+ t = topics(:first)
1928
+ t.title = "The First Topic Now Has A Title With\nNewlines And More Than 50 Characters"
1929
+
1930
+ assert_equal %("#{t.written_on.to_s(:db)}"), t.attribute_for_inspect(:written_on)
1931
+ assert_equal '"The First Topic Now Has A Title With\nNewlines And M..."', t.attribute_for_inspect(:title)
1932
+ end
1933
+
1934
+ def test_becomes
1935
+ assert_kind_of Reply, topics(:first).becomes(Reply)
1936
+ assert_equal "The First Topic", topics(:first).becomes(Reply).title
1937
+ end
1938
+
1939
+ def test_becomes_includes_errors
1940
+ company = Company.new(:name => nil)
1941
+ assert !company.valid?
1942
+ original_errors = company.errors
1943
+ client = company.becomes(Client)
1944
+ assert_equal original_errors, client.errors
1945
+ end
1946
+
1947
+ def test_silence_sets_log_level_to_error_in_block
1948
+ original_logger = ActiveRecord::Base.logger
1949
+ log = StringIO.new
1950
+ ActiveRecord::Base.logger = Logger.new(log)
1951
+ ActiveRecord::Base.logger.level = Logger::DEBUG
1952
+ ActiveRecord::Base.silence do
1953
+ ActiveRecord::Base.logger.warn "warn"
1954
+ ActiveRecord::Base.logger.error "error"
1955
+ end
1956
+ assert_equal "error\n", log.string
1957
+ ensure
1958
+ ActiveRecord::Base.logger = original_logger
1959
+ end
1960
+
1961
+ def test_silence_sets_log_level_back_to_level_before_yield
1962
+ original_logger = ActiveRecord::Base.logger
1963
+ log = StringIO.new
1964
+ ActiveRecord::Base.logger = Logger.new(log)
1965
+ ActiveRecord::Base.logger.level = Logger::WARN
1966
+ ActiveRecord::Base.silence do
1967
+ end
1968
+ assert_equal Logger::WARN, ActiveRecord::Base.logger.level
1969
+ ensure
1970
+ ActiveRecord::Base.logger = original_logger
1971
+ end
1972
+
1973
+ def test_benchmark_with_log_level
1974
+ original_logger = ActiveRecord::Base.logger
1975
+ log = StringIO.new
1976
+ ActiveRecord::Base.logger = Logger.new(log)
1977
+ ActiveRecord::Base.logger.level = Logger::WARN
1978
+ ActiveRecord::Base.benchmark("Debug Topic Count", :level => :debug) { Topic.count }
1979
+ ActiveRecord::Base.benchmark("Warn Topic Count", :level => :warn) { Topic.count }
1980
+ ActiveRecord::Base.benchmark("Error Topic Count", :level => :error) { Topic.count }
1981
+ assert_no_match(/Debug Topic Count/, log.string)
1982
+ assert_match(/Warn Topic Count/, log.string)
1983
+ assert_match(/Error Topic Count/, log.string)
1984
+ ensure
1985
+ ActiveRecord::Base.logger = original_logger
1986
+ end
1987
+
1988
+ def test_benchmark_with_use_silence
1989
+ original_logger = ActiveRecord::Base.logger
1990
+ log = StringIO.new
1991
+ ActiveRecord::Base.logger = Logger.new(log)
1992
+ assert_deprecated do
1993
+ ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => true) { ActiveRecord::Base.logger.debug "Loud" }
1994
+ end
1995
+ ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => false) { ActiveRecord::Base.logger.debug "Quiet" }
1996
+ assert_no_match(/Loud/, log.string)
1997
+ assert_match(/Quiet/, log.string)
1998
+ ensure
1999
+ ActiveRecord::Base.logger = original_logger
2000
+ end
2001
+
2002
+ def test_compute_type_success
2003
+ assert_equal Author, ActiveRecord::Base.send(:compute_type, 'Author')
2004
+ end
2005
+
2006
+ def test_compute_type_nonexistent_constant
2007
+ assert_raises NameError do
2008
+ ActiveRecord::Base.send :compute_type, 'NonexistentModel'
2009
+ end
2010
+ end
2011
+
2012
+ def test_compute_type_no_method_error
2013
+ ActiveSupport::Dependencies.stubs(:constantize).raises(NoMethodError)
2014
+ assert_raises NoMethodError do
2015
+ ActiveRecord::Base.send :compute_type, 'InvalidModel'
2016
+ end
2017
+ end
2018
+
2019
+ def test_compute_type_argument_error
2020
+ ActiveSupport::Dependencies.stubs(:constantize).raises(ArgumentError)
2021
+ assert_raises ArgumentError do
2022
+ ActiveRecord::Base.send :compute_type, 'InvalidModel'
2023
+ end
2024
+ end
2025
+
2026
+ def test_clear_cache!
2027
+ # preheat cache
2028
+ c1 = Post.connection.schema_cache.columns['posts']
2029
+ ActiveRecord::Base.clear_cache!
2030
+ c2 = Post.connection.schema_cache.columns['posts']
2031
+ assert_not_equal c1, c2
2032
+ end
2033
+
2034
+ def test_current_scope_is_reset
2035
+ Object.const_set :UnloadablePost, Class.new(ActiveRecord::Base)
2036
+ UnloadablePost.send(:current_scope=, UnloadablePost.scoped)
2037
+
2038
+ UnloadablePost.unloadable
2039
+ assert_not_nil Thread.current[:UnloadablePost_current_scope]
2040
+ ActiveSupport::Dependencies.remove_unloadable_constants!
2041
+ assert_nil Thread.current[:UnloadablePost_current_scope]
2042
+ ensure
2043
+ Object.class_eval{ remove_const :UnloadablePost } if defined?(UnloadablePost)
2044
+ end
2045
+
2046
+ def test_marshal_round_trip
2047
+ if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
2048
+ return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
2049
+ "to be a Ruby bug.")
2050
+ end
2051
+
2052
+ expected = posts(:welcome)
2053
+ marshalled = Marshal.dump(expected)
2054
+ actual = Marshal.load(marshalled)
2055
+
2056
+ assert_equal expected.attributes, actual.attributes
2057
+ end
2058
+
2059
+ def test_marshal_new_record_round_trip
2060
+ if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
2061
+ return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
2062
+ "to be a Ruby bug.")
2063
+ end
2064
+
2065
+ marshalled = Marshal.dump(Post.new)
2066
+ post = Marshal.load(marshalled)
2067
+
2068
+ assert post.new_record?, "should be a new record"
2069
+ end
2070
+
2071
+ def test_marshalling_with_associations
2072
+ if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
2073
+ return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
2074
+ "to be a Ruby bug.")
2075
+ end
2076
+
2077
+ post = Post.new
2078
+ post.comments.build
2079
+
2080
+ marshalled = Marshal.dump(post)
2081
+ post = Marshal.load(marshalled)
2082
+
2083
+ assert_equal 1, post.comments.length
2084
+ end
2085
+
2086
+ def test_attribute_names
2087
+ assert_equal ["id", "type", "ruby_type", "firm_id", "firm_name", "name", "client_of", "rating", "account_id"],
2088
+ Company.attribute_names
2089
+ end
2090
+
2091
+ def test_attribute_names_on_table_not_exists
2092
+ assert_equal [], NonExistentTable.attribute_names
2093
+ end
2094
+
2095
+ def test_attribtue_names_on_abstract_class
2096
+ assert_equal [], AbstractCompany.attribute_names
2097
+ end
2098
+
2099
+ def test_cache_key_for_existing_record_is_not_timezone_dependent
2100
+ ActiveRecord::Base.time_zone_aware_attributes = true
2101
+
2102
+ Time.zone = "UTC"
2103
+ utc_key = Developer.first.cache_key
2104
+
2105
+ Time.zone = "EST"
2106
+ est_key = Developer.first.cache_key
2107
+
2108
+ assert_equal utc_key, est_key
2109
+ ensure
2110
+ ActiveRecord::Base.time_zone_aware_attributes = false
2111
+ end
2112
+
2113
+ def test_cache_key_format_for_existing_record_with_updated_at
2114
+ dev = Developer.first
2115
+ assert_equal "developers/#{dev.id}-#{dev.updated_at.utc.to_s(:number)}", dev.cache_key
2116
+ end
2117
+
2118
+ def test_cache_key_format_for_existing_record_with_nil_updated_at
2119
+ dev = Developer.first
2120
+ dev.update_attribute(:updated_at, nil)
2121
+ assert_match(/\/#{dev.id}$/, dev.cache_key)
2122
+ end
2123
+
2124
+ def test_uniq_delegates_to_scoped
2125
+ scope = stub
2126
+ Bird.stubs(:scoped).returns(mock(:uniq => scope))
2127
+ assert_equal scope, Bird.uniq
2128
+ end
2129
+
2130
+ def test_table_name_with_2_abstract_subclasses
2131
+ assert_equal "photos", Photo.table_name
2132
+ end
2133
+ end