ibm_db 2.5.26-universal-darwin-14

Sign up to get free protection for your applications and to get access to all the features.
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,822 @@
1
+ require "cases/helper"
2
+ require 'active_support/core_ext/object/inclusion'
3
+ require 'models/minimalistic'
4
+ require 'models/developer'
5
+ require 'models/auto_id'
6
+ require 'models/boolean'
7
+ require 'models/computer'
8
+ require 'models/topic'
9
+ require 'models/company'
10
+ require 'models/category'
11
+ require 'models/reply'
12
+ require 'models/contact'
13
+ require 'models/keyboard'
14
+
15
+ class AttributeMethodsTest < ActiveRecord::TestCase
16
+ fixtures :topics, :developers, :companies, :computers
17
+
18
+ def setup
19
+ @old_matchers = ActiveRecord::Base.send(:attribute_method_matchers).dup
20
+ @target = Class.new(ActiveRecord::Base)
21
+ @target.table_name = 'topics'
22
+ end
23
+
24
+ def teardown
25
+ ActiveRecord::Base.send(:attribute_method_matchers).clear
26
+ ActiveRecord::Base.send(:attribute_method_matchers).concat(@old_matchers)
27
+ end
28
+
29
+ def test_attribute_present
30
+ t = Topic.new
31
+ t.title = "hello there!"
32
+ t.written_on = Time.now
33
+ t.author_name = ""
34
+ assert t.attribute_present?("title")
35
+ assert t.attribute_present?("written_on")
36
+ assert !t.attribute_present?("content")
37
+ assert !t.attribute_present?("author_name")
38
+
39
+ end
40
+
41
+ def test_attribute_present_with_booleans
42
+ b1 = Boolean.new
43
+ b1.value = false
44
+ assert b1.attribute_present?(:value)
45
+
46
+ b2 = Boolean.new
47
+ b2.value = true
48
+ assert b2.attribute_present?(:value)
49
+
50
+ b3 = Boolean.new
51
+ assert !b3.attribute_present?(:value)
52
+
53
+ b4 = Boolean.new
54
+ b4.value = false
55
+ b4.save!
56
+ assert Boolean.find(b4.id).attribute_present?(:value)
57
+ end
58
+
59
+ def test_caching_nil_primary_key
60
+ klass = Class.new(Minimalistic)
61
+ klass.expects(:reset_primary_key).returns(nil).once
62
+ 2.times { klass.primary_key }
63
+ end
64
+
65
+ def test_attribute_keys_on_new_instance
66
+ t = Topic.new
67
+ assert_equal nil, t.title, "The topics table has a title column, so it should be nil"
68
+ assert_raise(NoMethodError) { t.title2 }
69
+ end
70
+
71
+ def test_boolean_attributes
72
+ assert ! Topic.find(1).approved?
73
+ assert Topic.find(2).approved?
74
+ end
75
+
76
+ def test_set_attributes
77
+ topic = Topic.find(1)
78
+ topic.attributes = { "title" => "Budget", "author_name" => "Jason" }
79
+ topic.save
80
+ assert_equal("Budget", topic.title)
81
+ assert_equal("Jason", topic.author_name)
82
+ assert_equal(topics(:first).author_email_address, Topic.find(1).author_email_address)
83
+ end
84
+
85
+ def test_set_attributes_without_hash
86
+ topic = Topic.new
87
+ assert_nothing_raised { topic.attributes = '' }
88
+ end
89
+
90
+ def test_integers_as_nil
91
+ test = AutoId.create('value' => '')
92
+ assert_nil AutoId.find(test.id).value
93
+ end
94
+
95
+ def test_set_attributes_with_block
96
+ topic = Topic.new do |t|
97
+ t.title = "Budget"
98
+ t.author_name = "Jason"
99
+ end
100
+
101
+ assert_equal("Budget", topic.title)
102
+ assert_equal("Jason", topic.author_name)
103
+ end
104
+
105
+ def test_respond_to?
106
+ topic = Topic.find(1)
107
+ assert_respond_to topic, "title"
108
+ assert_respond_to topic, "_title"
109
+ assert_respond_to topic, "title?"
110
+ assert_respond_to topic, "title="
111
+ assert_respond_to topic, :title
112
+ assert_respond_to topic, :title?
113
+ assert_respond_to topic, :title=
114
+ assert_respond_to topic, "author_name"
115
+ assert_respond_to topic, "attribute_names"
116
+ assert !topic.respond_to?("nothingness")
117
+ assert !topic.respond_to?(:nothingness)
118
+ end
119
+
120
+ def test_deprecated_underscore_method
121
+ topic = Topic.find(1)
122
+ assert_equal topic.title, assert_deprecated { topic._title }
123
+ end
124
+
125
+ def test_respond_to_with_custom_primary_key
126
+ keyboard = Keyboard.create
127
+ assert_not_nil keyboard.key_number
128
+ assert_equal keyboard.key_number, keyboard.id
129
+ assert keyboard.respond_to?('key_number')
130
+ assert keyboard.respond_to?('_key_number')
131
+ assert keyboard.respond_to?('id')
132
+ assert keyboard.respond_to?('_id')
133
+ end
134
+
135
+ # Syck calls respond_to? before actually calling initialize
136
+ def test_respond_to_with_allocated_object
137
+ topic = Topic.allocate
138
+ assert !topic.respond_to?("nothingness")
139
+ assert !topic.respond_to?(:nothingness)
140
+ assert_respond_to topic, "title"
141
+ assert_respond_to topic, :title
142
+ end
143
+
144
+ # IRB inspects the return value of "MyModel.allocate"
145
+ # by inspecting it.
146
+ def test_allocated_object_can_be_inspected
147
+ topic = Topic.allocate
148
+ topic.instance_eval { @attributes = nil }
149
+ assert_nothing_raised { topic.inspect }
150
+ assert topic.inspect, "#<Topic not initialized>"
151
+ end
152
+
153
+ def test_array_content
154
+ topic = Topic.new
155
+ topic.content = %w( one two three )
156
+ topic.save
157
+
158
+ assert_equal(%w( one two three ), Topic.find(topic.id).content)
159
+ end
160
+
161
+ def test_read_attributes_before_type_cast
162
+ category = Category.new({:name=>"Test categoty", :type => nil})
163
+ category_attrs = {"name"=>"Test categoty", "id" => nil, "type" => nil, "categorizations_count" => nil}
164
+ assert_equal category_attrs , category.attributes_before_type_cast
165
+ end
166
+
167
+ if current_adapter?(:MysqlAdapter)
168
+ def test_read_attributes_before_type_cast_on_boolean
169
+ bool = Boolean.create({ "value" => false })
170
+ if RUBY_PLATFORM =~ /java/
171
+ # JRuby will return the value before typecast as string
172
+ assert_equal "0", bool.reload.attributes_before_type_cast["value"]
173
+ else
174
+ assert_equal 0, bool.reload.attributes_before_type_cast["value"]
175
+ end
176
+ end
177
+ end
178
+
179
+ def test_read_attributes_before_type_cast_on_datetime
180
+ in_time_zone "Pacific Time (US & Canada)" do
181
+ record = @target.new
182
+
183
+ record.written_on = "345643456"
184
+ assert_equal "345643456", record.written_on_before_type_cast
185
+ assert_equal nil, record.written_on
186
+
187
+ record.written_on = "2009-10-11 12:13:14"
188
+ assert_equal "2009-10-11 12:13:14", record.written_on_before_type_cast
189
+ assert_equal Time.zone.parse("2009-10-11 12:13:14"), record.written_on
190
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
191
+ end
192
+ end
193
+
194
+ def test_read_attributes_after_type_cast_on_datetime
195
+ tz = "Pacific Time (US & Canada)"
196
+
197
+ in_time_zone tz do
198
+ record = @target.new
199
+
200
+ date_string = "2011-03-24"
201
+ time = Time.zone.parse date_string
202
+
203
+ record.written_on = date_string
204
+ assert_equal date_string, record.written_on_before_type_cast
205
+ assert_equal time, record.written_on
206
+ assert_equal ActiveSupport::TimeZone[tz], record.written_on.time_zone
207
+
208
+ record.save
209
+ record.reload
210
+
211
+ assert_equal time, record.written_on
212
+ end
213
+ end
214
+
215
+ def test_hash_content
216
+ topic = Topic.new
217
+ topic.content = { "one" => 1, "two" => 2 }
218
+ topic.save
219
+
220
+ assert_equal 2, Topic.find(topic.id).content["two"]
221
+
222
+ topic.content_will_change!
223
+ topic.content["three"] = 3
224
+ topic.save
225
+
226
+ assert_equal 3, Topic.find(topic.id).content["three"]
227
+ end
228
+
229
+ def test_update_array_content
230
+ topic = Topic.new
231
+ topic.content = %w( one two three )
232
+
233
+ topic.content.push "four"
234
+ assert_equal(%w( one two three four ), topic.content)
235
+
236
+ topic.save
237
+
238
+ topic = Topic.find(topic.id)
239
+ topic.content << "five"
240
+ assert_equal(%w( one two three four five ), topic.content)
241
+ end
242
+
243
+ def test_case_sensitive_attributes_hash
244
+ # DB2 is not case-sensitive
245
+ return true if current_adapter?(:DB2Adapter,:IBM_DBAdapter)
246
+
247
+ assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.find(:first).attributes
248
+ end
249
+
250
+ def test_hashes_not_mangled
251
+ new_topic = { :title => "New Topic" }
252
+ new_topic_values = { :title => "AnotherTopic" }
253
+
254
+ topic = Topic.new(new_topic)
255
+ assert_equal new_topic[:title], topic.title
256
+
257
+ topic.attributes= new_topic_values
258
+ assert_equal new_topic_values[:title], topic.title
259
+ end
260
+
261
+ def test_create_through_factory
262
+ topic = Topic.create("title" => "New Topic")
263
+ topicReloaded = Topic.find(topic.id)
264
+ assert_equal(topic, topicReloaded)
265
+ end
266
+
267
+ def test_write_attribute
268
+ topic = Topic.new
269
+ topic.send(:write_attribute, :title, "Still another topic")
270
+ assert_equal "Still another topic", topic.title
271
+
272
+ topic[:title] = "Still another topic: part 2"
273
+ assert_equal "Still another topic: part 2", topic.title
274
+
275
+ topic.send(:write_attribute, "title", "Still another topic: part 3")
276
+ assert_equal "Still another topic: part 3", topic.title
277
+
278
+ topic["title"] = "Still another topic: part 4"
279
+ assert_equal "Still another topic: part 4", topic.title
280
+ end
281
+
282
+ def test_read_attribute
283
+ topic = Topic.new
284
+ topic.title = "Don't change the topic"
285
+ assert_equal "Don't change the topic", topic.send(:read_attribute, "title")
286
+ assert_equal "Don't change the topic", topic["title"]
287
+
288
+ assert_equal "Don't change the topic", topic.send(:read_attribute, :title)
289
+ assert_equal "Don't change the topic", topic[:title]
290
+ end
291
+
292
+ def test_read_attribute_when_false
293
+ topic = topics(:first)
294
+ topic.approved = false
295
+ assert !topic.approved?, "approved should be false"
296
+ topic.approved = "false"
297
+ assert !topic.approved?, "approved should be false"
298
+ end
299
+
300
+ def test_read_attribute_when_true
301
+ topic = topics(:first)
302
+ topic.approved = true
303
+ assert topic.approved?, "approved should be true"
304
+ topic.approved = "true"
305
+ assert topic.approved?, "approved should be true"
306
+ end
307
+
308
+ def test_read_write_boolean_attribute
309
+ topic = Topic.new
310
+ # puts ""
311
+ # puts "New Topic"
312
+ # puts topic.inspect
313
+ topic.approved = "false"
314
+ # puts "Expecting false"
315
+ # puts topic.inspect
316
+ assert !topic.approved?, "approved should be false"
317
+ topic.approved = "false"
318
+ # puts "Expecting false"
319
+ # puts topic.inspect
320
+ assert !topic.approved?, "approved should be false"
321
+ topic.approved = "true"
322
+ # puts "Expecting true"
323
+ # puts topic.inspect
324
+ assert topic.approved?, "approved should be true"
325
+ topic.approved = "true"
326
+ # puts "Expecting true"
327
+ # puts topic.inspect
328
+ assert topic.approved?, "approved should be true"
329
+ # puts ""
330
+ end
331
+
332
+ def test_overridden_write_attribute
333
+ topic = Topic.new
334
+ def topic.write_attribute(attr_name, value)
335
+ super(attr_name, value.downcase)
336
+ end
337
+
338
+ topic.send(:write_attribute, :title, "Yet another topic")
339
+ assert_equal "yet another topic", topic.title
340
+
341
+ topic[:title] = "Yet another topic: part 2"
342
+ assert_equal "yet another topic: part 2", topic.title
343
+
344
+ topic.send(:write_attribute, "title", "Yet another topic: part 3")
345
+ assert_equal "yet another topic: part 3", topic.title
346
+
347
+ topic["title"] = "Yet another topic: part 4"
348
+ assert_equal "yet another topic: part 4", topic.title
349
+ end
350
+
351
+ def test_overridden_read_attribute
352
+ topic = Topic.new
353
+ topic.title = "Stop changing the topic"
354
+ def topic.read_attribute(attr_name)
355
+ super(attr_name).upcase
356
+ end
357
+
358
+ assert_equal "STOP CHANGING THE TOPIC", topic.send(:read_attribute, "title")
359
+ assert_equal "STOP CHANGING THE TOPIC", topic["title"]
360
+
361
+ assert_equal "STOP CHANGING THE TOPIC", topic.send(:read_attribute, :title)
362
+ assert_equal "STOP CHANGING THE TOPIC", topic[:title]
363
+ end
364
+
365
+ def test_read_overridden_attribute
366
+ topic = Topic.new(:title => 'a')
367
+ def topic.title() 'b' end
368
+ assert_equal 'a', topic[:title]
369
+ end
370
+
371
+ def test_query_attribute_string
372
+ [nil, "", " "].each do |value|
373
+ assert_equal false, Topic.new(:author_name => value).author_name?
374
+ end
375
+
376
+ assert_equal true, Topic.new(:author_name => "Name").author_name?
377
+ end
378
+
379
+ def test_query_attribute_number
380
+ [nil, 0, "0"].each do |value|
381
+ assert_equal false, Developer.new(:salary => value).salary?
382
+ end
383
+
384
+ assert_equal true, Developer.new(:salary => 1).salary?
385
+ assert_equal true, Developer.new(:salary => "1").salary?
386
+ end
387
+
388
+ def test_query_attribute_boolean
389
+ [nil, "", false, "false", "f", 0].each do |value|
390
+ assert_equal false, Topic.new(:approved => value).approved?
391
+ end
392
+
393
+ [true, "true", "1", 1].each do |value|
394
+ assert_equal true, Topic.new(:approved => value).approved?
395
+ end
396
+ end
397
+
398
+ def test_query_attribute_with_custom_fields
399
+ object = Company.find_by_sql(<<-SQL).first
400
+ SELECT c1.*, c2.ruby_type as string_value, c2.rating as int_value
401
+ FROM companies c1, companies c2
402
+ WHERE c1.firm_id = c2.id
403
+ AND c1.id = 2
404
+ SQL
405
+
406
+ assert_equal "Firm", object.string_value
407
+ assert object.string_value?
408
+
409
+ object.string_value = " "
410
+ assert !object.string_value?
411
+
412
+ assert_equal 1, object.int_value.to_i
413
+ assert object.int_value?
414
+
415
+ object.int_value = "0"
416
+ assert !object.int_value?
417
+ end
418
+
419
+ def test_non_attribute_access_and_assignment
420
+ topic = Topic.new
421
+ assert !topic.respond_to?("mumbo")
422
+ assert_raise(NoMethodError) { topic.mumbo }
423
+ assert_raise(NoMethodError) { topic.mumbo = 5 }
424
+ end
425
+
426
+ def test_undeclared_attribute_method_does_not_affect_respond_to_and_method_missing
427
+ topic = @target.new(:title => 'Budget')
428
+ assert topic.respond_to?('title')
429
+ assert_equal 'Budget', topic.title
430
+ assert !topic.respond_to?('title_hello_world')
431
+ assert_raise(NoMethodError) { topic.title_hello_world }
432
+ end
433
+
434
+ def test_declared_prefixed_attribute_method_affects_respond_to_and_method_missing
435
+ topic = @target.new(:title => 'Budget')
436
+ %w(default_ title_).each do |prefix|
437
+ @target.class_eval "def #{prefix}attribute(*args) args end"
438
+ @target.attribute_method_prefix prefix
439
+
440
+ meth = "#{prefix}title"
441
+ assert topic.respond_to?(meth)
442
+ assert_equal ['title'], topic.send(meth)
443
+ assert_equal ['title', 'a'], topic.send(meth, 'a')
444
+ assert_equal ['title', 1, 2, 3], topic.send(meth, 1, 2, 3)
445
+ end
446
+ end
447
+
448
+ def test_declared_suffixed_attribute_method_affects_respond_to_and_method_missing
449
+ topic = @target.new(:title => 'Budget')
450
+ %w(_default _title_default _it! _candidate= able?).each do |suffix|
451
+ @target.class_eval "def attribute#{suffix}(*args) args end"
452
+ @target.attribute_method_suffix suffix
453
+
454
+ meth = "title#{suffix}"
455
+ assert topic.respond_to?(meth)
456
+ assert_equal ['title'], topic.send(meth)
457
+ assert_equal ['title', 'a'], topic.send(meth, 'a')
458
+ assert_equal ['title', 1, 2, 3], topic.send(meth, 1, 2, 3)
459
+ end
460
+ end
461
+
462
+ def test_declared_affixed_attribute_method_affects_respond_to_and_method_missing
463
+ topic = @target.new(:title => 'Budget')
464
+ [['mark_', '_for_update'], ['reset_', '!'], ['default_', '_value?']].each do |prefix, suffix|
465
+ @target.class_eval "def #{prefix}attribute#{suffix}(*args) args end"
466
+ @target.attribute_method_affix({ :prefix => prefix, :suffix => suffix })
467
+
468
+ meth = "#{prefix}title#{suffix}"
469
+ assert topic.respond_to?(meth)
470
+ assert_equal ['title'], topic.send(meth)
471
+ assert_equal ['title', 'a'], topic.send(meth, 'a')
472
+ assert_equal ['title', 1, 2, 3], topic.send(meth, 1, 2, 3)
473
+ end
474
+ end
475
+
476
+ def test_should_unserialize_attributes_for_frozen_records
477
+ myobj = {:value1 => :value2}
478
+ topic = Topic.create("content" => myobj)
479
+ topic.freeze
480
+ assert_equal myobj, topic.content
481
+ end
482
+
483
+ def test_typecast_attribute_from_select_to_false
484
+ topic = Topic.create(:title => 'Budget')
485
+ # Oracle and DB2 does not support boolean expressions in SELECT
486
+ if current_adapter?(:OracleAdapter) || current_adapter?(:IBM_DBAdapter)
487
+ topic = Topic.find(:first, :select => "topics.*, 0 as is_test")
488
+ else
489
+ topic = Topic.find(:first, :select => "topics.*, 1=2 as is_test")
490
+ end
491
+ assert !topic.is_test?
492
+ end
493
+
494
+ def test_typecast_attribute_from_select_to_true
495
+ topic = Topic.create(:title => 'Budget')
496
+ # Oracle and DB2 does not support boolean expressions in SELECT
497
+ if current_adapter?(:OracleAdapter) || current_adapter?(:IBM_DBAdapter)
498
+ topic = Topic.find(:first, :select => "topics.*, 1 as is_test")
499
+ else
500
+ topic = Topic.find(:first, :select => "topics.*, 2=2 as is_test")
501
+ end
502
+ assert topic.is_test?
503
+ end
504
+
505
+ def test_raises_dangerous_attribute_error_when_defining_activerecord_method_in_model
506
+ %w(save create_or_update).each do |method|
507
+ klass = Class.new ActiveRecord::Base
508
+ klass.class_eval "def #{method}() 'defined #{method}' end"
509
+ assert_raise ActiveRecord::DangerousAttributeError do
510
+ klass.instance_method_already_implemented?(method)
511
+ end
512
+ end
513
+ end
514
+
515
+ def test_only_time_related_columns_are_meant_to_be_cached_by_default
516
+ expected = %w(datetime timestamp time date).sort
517
+ assert_equal expected, ActiveRecord::Base.attribute_types_cached_by_default.map(&:to_s).sort
518
+ end
519
+
520
+ def test_declaring_attributes_as_cached_adds_them_to_the_attributes_cached_by_default
521
+ default_attributes = Topic.cached_attributes
522
+ Topic.cache_attributes :replies_count
523
+ expected = default_attributes + ["replies_count"]
524
+ assert_equal expected.sort, Topic.cached_attributes.sort
525
+ Topic.instance_variable_set "@cached_attributes", nil
526
+ end
527
+
528
+ def test_cacheable_columns_are_actually_cached
529
+ assert_equal cached_columns.sort, Topic.cached_attributes.sort
530
+ end
531
+
532
+ def test_accessing_cached_attributes_caches_the_converted_values_and_nothing_else
533
+ t = topics(:first)
534
+ cache = t.instance_variable_get "@attributes_cache"
535
+
536
+ assert_not_nil cache
537
+ assert cache.empty?
538
+
539
+ all_columns = Topic.columns.map(&:name)
540
+ uncached_columns = all_columns - cached_columns
541
+
542
+ all_columns.each do |attr_name|
543
+ attribute_gets_cached = Topic.cache_attribute?(attr_name)
544
+ val = t.send attr_name unless attr_name == "type"
545
+ if attribute_gets_cached
546
+ assert cached_columns.include?(attr_name)
547
+ assert_equal val, cache[attr_name]
548
+ else
549
+ assert uncached_columns.include?(attr_name)
550
+ assert !cache.include?(attr_name)
551
+ end
552
+ end
553
+ end
554
+
555
+ def test_write_nil_to_time_attributes
556
+ in_time_zone "Pacific Time (US & Canada)" do
557
+ record = @target.new
558
+ record.written_on = nil
559
+ assert_nil record.written_on
560
+ end
561
+ end
562
+
563
+ def test_time_attributes_are_retrieved_in_current_time_zone
564
+ in_time_zone "Pacific Time (US & Canada)" do
565
+ utc_time = Time.utc(2008, 1, 1)
566
+ record = @target.new
567
+ record[:written_on] = utc_time
568
+ assert_equal utc_time, record.written_on # record.written on is equal to (i.e., simultaneous with) utc_time
569
+ assert_kind_of ActiveSupport::TimeWithZone, record.written_on # but is a TimeWithZone
570
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone # and is in the current Time.zone
571
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time # and represents time values adjusted accordingly
572
+ end
573
+ end
574
+
575
+ def test_setting_time_zone_aware_attribute_to_utc
576
+ in_time_zone "Pacific Time (US & Canada)" do
577
+ utc_time = Time.utc(2008, 1, 1)
578
+ record = @target.new
579
+ record.written_on = utc_time
580
+ assert_equal utc_time, record.written_on
581
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
582
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
583
+ end
584
+ end
585
+
586
+ def test_setting_time_zone_aware_attribute_in_other_time_zone
587
+ utc_time = Time.utc(2008, 1, 1)
588
+ cst_time = utc_time.in_time_zone("Central Time (US & Canada)")
589
+ in_time_zone "Pacific Time (US & Canada)" do
590
+ record = @target.new
591
+ record.written_on = cst_time
592
+ assert_equal utc_time, record.written_on
593
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
594
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
595
+ end
596
+ end
597
+
598
+ def test_setting_time_zone_aware_read_attribute
599
+ utc_time = Time.utc(2008, 1, 1)
600
+ cst_time = utc_time.in_time_zone("Central Time (US & Canada)")
601
+ in_time_zone "Pacific Time (US & Canada)" do
602
+ record = @target.create(:written_on => cst_time).reload
603
+ assert_equal utc_time, record[:written_on]
604
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record[:written_on].time_zone
605
+ assert_equal Time.utc(2007, 12, 31, 16), record[:written_on].time
606
+ end
607
+ end
608
+
609
+ def test_setting_time_zone_aware_attribute_with_string
610
+ utc_time = Time.utc(2008, 1, 1)
611
+ (-11..13).each do |timezone_offset|
612
+ time_string = utc_time.in_time_zone(timezone_offset).to_s
613
+ in_time_zone "Pacific Time (US & Canada)" do
614
+ record = @target.new
615
+ record.written_on = time_string
616
+ assert_equal Time.zone.parse(time_string), record.written_on
617
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
618
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
619
+ end
620
+ end
621
+ end
622
+
623
+ def test_setting_time_zone_aware_attribute_to_blank_string_returns_nil
624
+ in_time_zone "Pacific Time (US & Canada)" do
625
+ record = @target.new
626
+ record.written_on = ' '
627
+ assert_nil record.written_on
628
+ assert_nil record[:written_on]
629
+ end
630
+ end
631
+
632
+ def test_setting_time_zone_aware_attribute_interprets_time_zone_unaware_string_in_time_zone
633
+ time_string = 'Tue Jan 01 00:00:00 2008'
634
+ (-11..13).each do |timezone_offset|
635
+ in_time_zone timezone_offset do
636
+ record = @target.new
637
+ record.written_on = time_string
638
+ assert_equal Time.zone.parse(time_string), record.written_on
639
+ assert_equal ActiveSupport::TimeZone[timezone_offset], record.written_on.time_zone
640
+ assert_equal Time.utc(2008, 1, 1), record.written_on.time
641
+ end
642
+ end
643
+ end
644
+
645
+ def test_setting_time_zone_aware_attribute_in_current_time_zone
646
+ utc_time = Time.utc(2008, 1, 1)
647
+ in_time_zone "Pacific Time (US & Canada)" do
648
+ record = @target.new
649
+ record.written_on = utc_time.in_time_zone
650
+ assert_equal utc_time, record.written_on
651
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
652
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
653
+ end
654
+ end
655
+
656
+ def test_setting_time_zone_conversion_for_attributes_should_write_value_on_class_variable
657
+ Topic.skip_time_zone_conversion_for_attributes = [:field_a]
658
+ Minimalistic.skip_time_zone_conversion_for_attributes = [:field_b]
659
+
660
+ assert_equal [:field_a], Topic.skip_time_zone_conversion_for_attributes
661
+ assert_equal [:field_b], Minimalistic.skip_time_zone_conversion_for_attributes
662
+ end
663
+
664
+ def test_read_attributes_respect_access_control
665
+ privatize("title")
666
+
667
+ topic = @target.new(:title => "The pros and cons of programming naked.")
668
+ assert !topic.respond_to?(:title)
669
+ exception = assert_raise(NoMethodError) { topic.title }
670
+ assert exception.message.include?("private method")
671
+ assert_equal "I'm private", topic.send(:title)
672
+ end
673
+
674
+ def test_write_attributes_respect_access_control
675
+ privatize("title=(value)")
676
+
677
+ topic = @target.new
678
+ assert !topic.respond_to?(:title=)
679
+ exception = assert_raise(NoMethodError) { topic.title = "Pants"}
680
+ assert exception.message.include?("private method")
681
+ topic.send(:title=, "Very large pants")
682
+ end
683
+
684
+ def test_question_attributes_respect_access_control
685
+ privatize("title?")
686
+
687
+ topic = @target.new(:title => "Isaac Newton's pants")
688
+ assert !topic.respond_to?(:title?)
689
+ exception = assert_raise(NoMethodError) { topic.title? }
690
+ assert exception.message.include?("private method")
691
+ assert topic.send(:title?)
692
+ end
693
+
694
+ def test_bulk_update_respects_access_control
695
+ privatize("title=(value)")
696
+
697
+ assert_raise(ActiveRecord::UnknownAttributeError) { @target.new(:title => "Rants about pants") }
698
+ assert_raise(ActiveRecord::UnknownAttributeError) { @target.new.attributes = { :title => "Ants in pants" } }
699
+ end
700
+
701
+ def test_read_attribute_overwrites_private_method_not_considered_implemented
702
+ # simulate a model with a db column that shares its name an inherited
703
+ # private method (e.g. Object#system)
704
+ #
705
+ Object.class_eval do
706
+ private
707
+ def title; "private!"; end
708
+ end
709
+ assert !@target.instance_method_already_implemented?(:title)
710
+ topic = @target.new
711
+ assert_nil topic.title
712
+
713
+ Object.send(:undef_method, :title) # remove test method from object
714
+ end
715
+
716
+ def test_list_of_serialized_attributes
717
+ assert_equal %w(content), Topic.serialized_attributes.keys
718
+ assert_equal %w(preferences), Contact.serialized_attributes.keys
719
+ end
720
+
721
+ def test_instance_method_should_be_defined_on_the_base_class
722
+ subklass = Class.new(Topic)
723
+
724
+ Topic.define_attribute_methods
725
+
726
+ instance = subklass.new
727
+ instance.id = 5
728
+ assert_equal 5, instance.id
729
+ assert subklass.method_defined?(:id), "subklass is missing id method"
730
+
731
+ Topic.undefine_attribute_methods
732
+
733
+ assert_equal 5, instance.id
734
+ assert subklass.method_defined?(:id), "subklass is missing id method"
735
+ end
736
+
737
+ def test_dispatching_column_attributes_through_method_missing_deprecated
738
+ Topic.define_attribute_methods
739
+
740
+ topic = Topic.new(:id => 5)
741
+ topic.id = 5
742
+
743
+ topic.method(:id).owner.send(:undef_method, :id)
744
+
745
+ assert_deprecated do
746
+ assert_equal 5, topic.id
747
+ end
748
+ ensure
749
+ Topic.undefine_attribute_methods
750
+ end
751
+
752
+ def test_read_attribute_with_nil_should_not_asplode
753
+ assert_equal nil, Topic.new.read_attribute(nil)
754
+ end
755
+
756
+ # If B < A, and A defines an accessor for 'foo', we don't want to override
757
+ # that by defining a 'foo' method in the generated methods module for B.
758
+ # (That module will be inserted between the two, e.g. [B, <GeneratedAttributes>, A].)
759
+ def test_inherited_custom_accessors
760
+ klass = Class.new(ActiveRecord::Base) do
761
+ self.table_name = "topics"
762
+ self.abstract_class = true
763
+ def title; "omg"; end
764
+ def title=(val); self.author_name = val; end
765
+ end
766
+ subklass = Class.new(klass)
767
+ [klass, subklass].each(&:define_attribute_methods)
768
+
769
+ topic = subklass.find(1)
770
+ assert_equal "omg", topic.title
771
+
772
+ topic.title = "lol"
773
+ assert_equal "lol", topic.author_name
774
+ end
775
+
776
+ def test_inherited_hook_removed
777
+ parent = Class.new(ActiveRecord::Base)
778
+ parent.table_name = "posts"
779
+ def parent.inherited(k)
780
+ end
781
+
782
+ klass = Class.new(parent)
783
+ assert_deprecated { klass.define_attribute_methods }
784
+ end
785
+
786
+ def test_setting_new_attributes_deprecated
787
+ t = Topic.new
788
+ assert_deprecated { t[:foo] = "bar" }
789
+ assert_equal "bar", t.foo
790
+ assert_equal "bar", t[:foo]
791
+ end
792
+
793
+ private
794
+ def cached_columns
795
+ @cached_columns ||= time_related_columns_on_topic.map(&:name)
796
+ end
797
+
798
+ def time_related_columns_on_topic
799
+ Topic.columns.select { |c| c.type.in?([:time, :date, :datetime, :timestamp]) }
800
+ end
801
+
802
+ def in_time_zone(zone)
803
+ old_zone = Time.zone
804
+ old_tz = ActiveRecord::Base.time_zone_aware_attributes
805
+
806
+ Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil
807
+ ActiveRecord::Base.time_zone_aware_attributes = !zone.nil?
808
+ yield
809
+ ensure
810
+ Time.zone = old_zone
811
+ ActiveRecord::Base.time_zone_aware_attributes = old_tz
812
+ end
813
+
814
+ def privatize(method_signature)
815
+ @target.class_eval <<-private_method
816
+ private
817
+ def #{method_signature}
818
+ "I'm private"
819
+ end
820
+ private_method
821
+ end
822
+ end