massive_record 0.2.0 → 0.2.1.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/CHANGELOG.md +43 -4
  2. data/Gemfile.lock +3 -1
  3. data/README.md +5 -0
  4. data/lib/massive_record/adapters/thrift/connection.rb +23 -16
  5. data/lib/massive_record/adapters/thrift/row.rb +13 -33
  6. data/lib/massive_record/adapters/thrift/table.rb +24 -10
  7. data/lib/massive_record/orm/attribute_methods.rb +27 -1
  8. data/lib/massive_record/orm/attribute_methods/dirty.rb +2 -2
  9. data/lib/massive_record/orm/attribute_methods/read.rb +36 -1
  10. data/lib/massive_record/orm/attribute_methods/time_zone_conversion.rb +81 -0
  11. data/lib/massive_record/orm/attribute_methods/write.rb +18 -0
  12. data/lib/massive_record/orm/base.rb +52 -10
  13. data/lib/massive_record/orm/callbacks.rb +1 -1
  14. data/lib/massive_record/orm/default_id.rb +20 -0
  15. data/lib/massive_record/orm/errors.rb +4 -0
  16. data/lib/massive_record/orm/finders.rb +102 -57
  17. data/lib/massive_record/orm/finders/rescue_missing_table_on_find.rb +45 -0
  18. data/lib/massive_record/orm/id_factory.rb +1 -1
  19. data/lib/massive_record/orm/log_subscriber.rb +85 -0
  20. data/lib/massive_record/orm/persistence.rb +82 -37
  21. data/lib/massive_record/orm/query_instrumentation.rb +64 -0
  22. data/lib/massive_record/orm/relations/interface.rb +10 -0
  23. data/lib/massive_record/orm/relations/metadata.rb +2 -0
  24. data/lib/massive_record/orm/relations/proxy/references_one_polymorphic.rb +1 -1
  25. data/lib/massive_record/orm/schema/field.rb +33 -6
  26. data/lib/massive_record/orm/timestamps.rb +1 -1
  27. data/lib/massive_record/orm/validations.rb +2 -2
  28. data/lib/massive_record/rails/controller_runtime.rb +55 -0
  29. data/lib/massive_record/rails/railtie.rb +16 -0
  30. data/lib/massive_record/version.rb +1 -1
  31. data/lib/massive_record/wrapper/cell.rb +32 -3
  32. data/massive_record.gemspec +1 -0
  33. data/spec/{wrapper/cases → adapter/thrift}/adapter_spec.rb +0 -0
  34. data/spec/adapter/thrift/atomic_increment_spec.rb +55 -0
  35. data/spec/{wrapper/cases → adapter/thrift}/connection_spec.rb +0 -10
  36. data/spec/adapter/thrift/table_find_spec.rb +40 -0
  37. data/spec/{wrapper/cases → adapter/thrift}/table_spec.rb +55 -13
  38. data/spec/orm/cases/attribute_methods_spec.rb +6 -1
  39. data/spec/orm/cases/base_spec.rb +18 -4
  40. data/spec/orm/cases/callbacks_spec.rb +1 -1
  41. data/spec/orm/cases/default_id_spec.rb +38 -0
  42. data/spec/orm/cases/default_values_spec.rb +37 -0
  43. data/spec/orm/cases/dirty_spec.rb +25 -1
  44. data/spec/orm/cases/encoding_spec.rb +3 -3
  45. data/spec/orm/cases/finder_default_scope.rb +8 -1
  46. data/spec/orm/cases/finder_scope_spec.rb +2 -2
  47. data/spec/orm/cases/finders_spec.rb +8 -18
  48. data/spec/orm/cases/id_factory_spec.rb +38 -21
  49. data/spec/orm/cases/log_subscriber_spec.rb +133 -0
  50. data/spec/orm/cases/mass_assignment_security_spec.rb +97 -0
  51. data/spec/orm/cases/persistence_spec.rb +132 -27
  52. data/spec/orm/cases/single_table_inheritance_spec.rb +2 -2
  53. data/spec/orm/cases/time_zone_awareness_spec.rb +157 -0
  54. data/spec/orm/cases/timestamps_spec.rb +15 -0
  55. data/spec/orm/cases/validation_spec.rb +2 -2
  56. data/spec/orm/models/model_without_default_id.rb +5 -0
  57. data/spec/orm/models/person.rb +1 -0
  58. data/spec/orm/models/test_class.rb +1 -0
  59. data/spec/orm/relations/interface_spec.rb +2 -2
  60. data/spec/orm/relations/metadata_spec.rb +1 -1
  61. data/spec/orm/relations/proxy/references_many_spec.rb +21 -15
  62. data/spec/orm/relations/proxy/references_one_polymorphic_spec.rb +7 -1
  63. data/spec/orm/relations/proxy/references_one_spec.rb +7 -0
  64. data/spec/orm/schema/field_spec.rb +61 -5
  65. data/spec/support/connection_helpers.rb +2 -1
  66. data/spec/support/mock_massive_record_connection.rb +7 -0
  67. data/spec/support/time_zone_helper.rb +25 -0
  68. metadata +51 -14
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+ require 'orm/models/person'
3
+
4
+ describe "mass assignment security" do
5
+ def restore_mass_assignment_security_policy_after(klass)
6
+ accessible_attributes = klass._accessible_attributes
7
+ protected_attributes = klass._protected_attributes
8
+ active_authorizer = klass._active_authorizer
9
+
10
+ yield
11
+
12
+ klass._accessible_attributes = accessible_attributes
13
+ klass._protected_attributes = protected_attributes
14
+ klass._active_authorizer = active_authorizer
15
+ end
16
+
17
+
18
+
19
+ describe "settings and defaults" do
20
+ subject { Person }
21
+ its(:attributes_protected_by_default ) { should include 'id', Person.inheritance_attribute }
22
+ its(:protected_attributes) { should include 'id', Person.inheritance_attribute }
23
+
24
+
25
+ describe "#attr_accessible" do
26
+ it "sets attributes accessible" do
27
+ restore_mass_assignment_security_policy_after Person do
28
+ Person.class_eval do
29
+ attr_accessible :age
30
+ end
31
+
32
+ Person.accessible_attributes.should include 'age'
33
+ end
34
+ end
35
+ end
36
+
37
+ describe "#attr_protected" do
38
+ it "sets attributes protected" do
39
+ restore_mass_assignment_security_policy_after Person do
40
+ Person.class_eval do
41
+ attr_protected :age
42
+ end
43
+
44
+ Person.protected_attributes.should include 'age'
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ describe "access test on" do
51
+ describe Person do
52
+ context "when only age is accessible" do
53
+ it "sets age with mass assignment" do
54
+ restore_mass_assignment_security_policy_after Person do
55
+ Person.class_eval do
56
+ attr_accessible :age
57
+ end
58
+
59
+ Person.new(:age => 33).age.should eq 33
60
+ end
61
+ end
62
+
63
+ it "does not set name with mass assignment" do
64
+ restore_mass_assignment_security_policy_after Person do
65
+ Person.class_eval do
66
+ attr_accessible :age
67
+ end
68
+
69
+ Person.new(:name => 'Thorbjorn').name.should be_nil
70
+ end
71
+ end
72
+ end
73
+
74
+ context "when only age is protected" do
75
+ it "does not set age with mass assignment" do
76
+ restore_mass_assignment_security_policy_after Person do
77
+ Person.class_eval do
78
+ attr_protected :age
79
+ end
80
+
81
+ Person.new(:age => 33).age.should be_nil
82
+ end
83
+ end
84
+
85
+ it "sets name with mass assignment" do
86
+ restore_mass_assignment_security_policy_after Person do
87
+ Person.class_eval do
88
+ attr_protected :age
89
+ end
90
+
91
+ Person.new(:name => 'Thorbjorn').name.should eq "Thorbjorn"
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -15,27 +15,27 @@ describe "persistence" do
15
15
  end
16
16
 
17
17
  it "should be persisted if saved" do
18
- model = TestClass.new :id => "id1"
18
+ model = TestClass.new "id1"
19
19
  model.save
20
20
  model.should be_persisted
21
21
  end
22
22
 
23
23
  it "should be destroyed when destroyed" do
24
- model = TestClass.new :id => "id1"
24
+ model = TestClass.new "id1"
25
25
  model.save
26
26
  model.destroy
27
27
  model.should be_destroyed
28
28
  end
29
29
 
30
30
  it "should not be persisted if destroyed" do
31
- model = TestClass.new :id => "id1"
31
+ model = TestClass.new "id1"
32
32
  model.save
33
33
  model.destroy
34
34
  model.should_not be_persisted
35
35
  end
36
36
 
37
37
  it "should be possible to create new objects" do
38
- TestClass.create(:id => "id1").should be_persisted
38
+ TestClass.create("id1").should be_persisted
39
39
  end
40
40
 
41
41
  it "should raise an error if validation fails on save!" do
@@ -91,23 +91,28 @@ describe "persistence" do
91
91
  end
92
92
 
93
93
  it "should return a row with id set" do
94
- Person.new({:id => "foo"}).send(:row_for_record).id.should == "foo"
94
+ Person.new("foo").send(:row_for_record).id.should == "foo"
95
95
  end
96
96
 
97
97
  it "should return a row with table set" do
98
- Person.new({:id => "foo"}).send(:row_for_record).table.should == Person.table
98
+ Person.new("foo").send(:row_for_record).table.should == Person.table
99
99
  end
100
100
  end
101
101
 
102
102
  describe "#attributes_to_row_values_hash" do
103
103
  before do
104
- @person = Person.new({ :id => "new_id", :name => "Vincent", :points => 15 })
104
+ @person = Person.new("new_id", :name => "Vincent", :points => 15)
105
105
  end
106
106
 
107
107
  it "should include the 'pts' field in the database which has 'points' as an alias" do
108
108
  @person.send(:attributes_to_row_values_hash)["base"].keys.should include("pts")
109
109
  @person.send(:attributes_to_row_values_hash)["base"].keys.should_not include("points")
110
110
  end
111
+
112
+ it "should include integer value, even if it is set as string" do
113
+ @person.age = "20"
114
+ @person.send(:attributes_to_row_values_hash)["info"]["age"].should == 20
115
+ end
111
116
  end
112
117
 
113
118
 
@@ -116,7 +121,7 @@ describe "persistence" do
116
121
  include MockMassiveRecordConnection
117
122
 
118
123
  before do
119
- @person = Person.create! :id => "new_id", :name => "Thorbjorn", :age => "22"
124
+ @person = Person.create! "new_id", :name => "Thorbjorn", :age => "22"
120
125
  end
121
126
 
122
127
  it "should update given attriubte when valid" do
@@ -134,7 +139,7 @@ describe "persistence" do
134
139
  include MockMassiveRecordConnection
135
140
 
136
141
  before do
137
- @person = Person.create! :id => "new_id", :name => "Thorbjorn", :age => "22"
142
+ @person = Person.create! "new_id", :name => "Thorbjorn", :age => "22"
138
143
  end
139
144
 
140
145
  it "should update given attriubtes when valid" do
@@ -162,7 +167,7 @@ describe "persistence" do
162
167
  end
163
168
 
164
169
  it "should delegate save to update if its a persisted record" do
165
- person = Person.new :id => 14, :name => "Bob", :age => 33
170
+ person = Person.new '14', :name => "Bob", :age => 33
166
171
  person.should_receive(:new_record?).and_return(false)
167
172
  person.should_receive(:update)
168
173
  person.save
@@ -171,6 +176,7 @@ describe "persistence" do
171
176
 
172
177
  describe "database test" do
173
178
  include SetUpHbaseConnectionBeforeAll
179
+ include SetTableNamesToTestTable
174
180
 
175
181
  describe "create" do
176
182
  describe "when table does not exists" do
@@ -187,7 +193,7 @@ describe "persistence" do
187
193
  end
188
194
  end
189
195
 
190
- @new_instance = @new_class.new :id => "id_of_foo", :foo => "bar"
196
+ @new_instance = @new_class.new "id_of_foo", :foo => "bar"
191
197
  end
192
198
 
193
199
  after do
@@ -220,13 +226,38 @@ describe "persistence" do
220
226
  include CreatePersonBeforeEach
221
227
 
222
228
  it "should store (create) new objects" do
223
- person = Person.new :id => "new_id", :name => "Thorbjorn", :age => "22"
229
+ person = Person.new "new_id", :name => "Thorbjorn", :age => "22"
224
230
  person.save!
225
231
  person_from_db = Person.find(person.id)
226
232
  person_from_db.should == person
227
233
  person_from_db.name.should == "Thorbjorn"
228
234
  end
229
235
  end
236
+
237
+ it "raises an error if id already exists" do
238
+ check_was = Person.check_record_uniqueness_on_create
239
+ Person.check_record_uniqueness_on_create = true
240
+
241
+ Person.create! "foo", :name => "Thorbjorn", :age => "22"
242
+ expect {
243
+ Person.create! "foo", :name => "Anders", :age => "22"
244
+ }.to raise_error MassiveRecord::ORM::RecordNotUnique
245
+
246
+ Person.find("foo").name.should eq "Thorbjorn"
247
+
248
+ Person.check_record_uniqueness_on_create = check_was
249
+ end
250
+
251
+ it "raises no error if exist checking is turned off" do
252
+ check_was = Person.check_record_uniqueness_on_create
253
+ Person.check_record_uniqueness_on_create = false
254
+
255
+ Person.create! "foo", :name => "Thorbjorn", :age => "22"
256
+ Person.create! "foo", :name => "Anders", :age => "22" # This will result in an "update"
257
+ Person.find("foo").name.should eq "Anders"
258
+
259
+ Person.check_record_uniqueness_on_create = check_was
260
+ end
230
261
  end
231
262
 
232
263
  describe "update" do
@@ -292,7 +323,7 @@ describe "persistence" do
292
323
  include MockMassiveRecordConnection
293
324
 
294
325
  before do
295
- @person = Person.new :id => "id1"
326
+ @person = Person.new "id1"
296
327
  @person.stub!(:new_record?).and_return(false)
297
328
  @row = MassiveRecord::Wrapper::Row.new({:id => @person.id, :table => @person.class.table})
298
329
  @person.should_receive(:row_for_record).and_return(@row)
@@ -333,7 +364,7 @@ describe "persistence" do
333
364
  include SetTableNamesToTestTable
334
365
 
335
366
  before do
336
- @person = Person.create! :id => "id1", :name => "Thorbjorn", :age => 29
367
+ @person = Person.create! "id1", :name => "Thorbjorn", :age => 29
337
368
  end
338
369
 
339
370
  it "should be removed by destroy" do
@@ -355,7 +386,7 @@ describe "persistence" do
355
386
 
356
387
  describe "#destroy_all" do
357
388
  it "should remove all when calling remove_all" do
358
- Person.create! :id => "id2", :name => "Going to die :-(", :age => 99
389
+ Person.create! "id2", :name => "Going to die :-(", :age => 99
359
390
  Person.destroy_all
360
391
  Person.all.length.should == 0
361
392
  end
@@ -365,7 +396,7 @@ describe "persistence" do
365
396
  end
366
397
 
367
398
  it "should destroy all even if it is above 10 rows (obviously)" do
368
- 15.times { |i| Person.create! :id => "id-#{i}", :name => "Going to die :-(", :age => i + 20 }
399
+ 15.times { |i| Person.create! "id-#{i}", :name => "Going to die :-(", :age => i + 20 }
369
400
  Person.destroy_all
370
401
  Person.all.length.should == 0
371
402
  end
@@ -380,7 +411,7 @@ describe "persistence" do
380
411
  include MockMassiveRecordConnection
381
412
 
382
413
  before do
383
- @person = Person.create! :id => "id1", :name => "Thorbjorn", :age => 29
414
+ @person = Person.create! "id1", :name => "Thorbjorn", :age => 29
384
415
  end
385
416
 
386
417
  it "should being able to increment age" do
@@ -414,7 +445,7 @@ describe "persistence" do
414
445
  include SetTableNamesToTestTable
415
446
 
416
447
  before do
417
- @person = Person.create! :id => "id1", :name => "Thorbjorn", :age => 29
448
+ @person = Person.create! "id1", :name => "Thorbjorn", :age => 29
418
449
  end
419
450
 
420
451
  it "should delegate it's call to increment" do
@@ -428,11 +459,44 @@ describe "persistence" do
428
459
  @person.age.should == 30
429
460
  end
430
461
 
431
- it "should be able to do atomic increments" do
432
- @person.atomic_increment!(:age).should == 30
433
- @person.age.should == 30
434
- @person.reload
435
- @person.age.should == 30
462
+
463
+ describe "atomic increments" do
464
+ it "should be able to do atomic increments on existing objects" do
465
+ @person.atomic_increment!(:age).should == 30
466
+ @person.age.should == 30
467
+ @person.reload
468
+ @person.age.should == 30
469
+ end
470
+
471
+ it "is a persisted record after incrementation" do
472
+ person = Person.new('id2')
473
+ person.atomic_increment!(:age).should eq 1
474
+ person.should be_persisted
475
+ end
476
+
477
+ it "increments correctly when value is '1'" do
478
+ old_ensure = MassiveRecord::ORM::Base.backward_compatibility_integers_might_be_persisted_as_strings
479
+ MassiveRecord::ORM::Base.backward_compatibility_integers_might_be_persisted_as_strings = true
480
+
481
+ person = Person.new('id2')
482
+ person.atomic_increment!(:age).should eq 1
483
+
484
+ atomic_field = Person.attributes_schema['age']
485
+
486
+ # Enter incompatible data, number as string.
487
+ Person.table.find("id2").tap do |row|
488
+ row.update_column(
489
+ atomic_field.column_family.name,
490
+ atomic_field.name,
491
+ MassiveRecord::ORM::Base.coder.dump(1)
492
+ )
493
+ row.save
494
+ end
495
+
496
+ person.atomic_increment!(:age).should eq 2
497
+
498
+ MassiveRecord::ORM::Base.backward_compatibility_integers_might_be_persisted_as_strings = old_ensure
499
+ end
436
500
  end
437
501
  end
438
502
  end
@@ -442,7 +506,7 @@ describe "persistence" do
442
506
  include MockMassiveRecordConnection
443
507
 
444
508
  before do
445
- @person = Person.create! :id => "id1", :name => "Thorbjorn", :age => 29
509
+ @person = Person.create! "id1", :name => "Thorbjorn", :age => 29
446
510
  end
447
511
 
448
512
  it "should being able to decrement age" do
@@ -476,7 +540,7 @@ describe "persistence" do
476
540
  include SetTableNamesToTestTable
477
541
 
478
542
  before do
479
- @person = Person.create! :id => "id1", :name => "Thorbjorn", :age => 29
543
+ @person = Person.create! "id1", :name => "Thorbjorn", :age => 29
480
544
  end
481
545
 
482
546
  it "should delegate it's call to decrement" do
@@ -496,17 +560,58 @@ describe "persistence" do
496
560
  include MockMassiveRecordConnection
497
561
 
498
562
  it "should raise an error if new record is read only and you try to save it" do
499
- person = Person.new :id => "id1", :name => "Thorbjorn", :age => 29
563
+ person = Person.new "id1", :name => "Thorbjorn", :age => 29
500
564
  person.readonly!
501
565
  lambda { person.save }.should raise_error MassiveRecord::ORM::ReadOnlyRecord
502
566
  end
503
567
 
504
568
  it "should raise an error if record is read only and you try to save it" do
505
- person = Person.create :id => "id1", :name => "Thorbjorn", :age => 29
569
+ person = Person.create "id1", :name => "Thorbjorn", :age => 29
506
570
  person.should be_persisted
507
571
 
508
572
  person.readonly!
509
573
  lambda { person.save }.should raise_error MassiveRecord::ORM::ReadOnlyRecord
510
574
  end
511
575
  end
576
+
577
+ describe "id as int" do
578
+ include SetUpHbaseConnectionBeforeAll
579
+ include SetTableNamesToTestTable
580
+
581
+ it "saves id as string and reloads correctly" do
582
+ person = Person.new :name => "Thorbjorn", :age => 29
583
+ person.id = 1
584
+ person.save!
585
+
586
+ person.reload
587
+ person.id.should == "1"
588
+ end
589
+ end
590
+
591
+
592
+ describe "attributes with nil value" do
593
+ include SetUpHbaseConnectionBeforeAll
594
+ include SetTableNamesToTestTable
595
+
596
+
597
+ subject do
598
+ Person.create!("id", {
599
+ :name => "Thorbjorn",
600
+ :age => 22,
601
+ :points => 1,
602
+ :addresses => {'home' => 'Here'},
603
+ :status => true
604
+ })
605
+ end
606
+
607
+ %w(points addresses status).each do |attr|
608
+ it "removes the cell from hbase when #{attr} is set to nil" do
609
+ subject[attr] = nil
610
+ subject.save!
611
+
612
+ raw_values = Person.table.find(subject.id).values
613
+ raw_values[subject.attributes_schema[attr].unique_name].should be_nil
614
+ end
615
+ end
616
+ end
512
617
  end
@@ -8,7 +8,7 @@ describe "Single table inheritance" do
8
8
 
9
9
  [Friend, BestFriend, BestFriend::SuperBestFriend].each do |klass|
10
10
  describe klass do
11
- let(:subject) { klass.new(:id => "ID1", :name => "Person1", :email => "one@person.com", :age => 11, :points => 111, :status => true) }
11
+ let(:subject) { klass.new("ID1", :name => "Person1", :email => "one@person.com", :age => 11, :points => 111, :status => true) }
12
12
 
13
13
  its(:type) { should == klass.to_s }
14
14
 
@@ -20,7 +20,7 @@ describe "Single table inheritance" do
20
20
  end
21
21
 
22
22
  it "should not set type if record being saved is base class" do
23
- person = Person.new :id => "ID1", :name => "Person1", :email => "one@person.com", :age => 11, :points => 111, :status => true
23
+ person = Person.new "ID1", :name => "Person1", :email => "one@person.com", :age => 11, :points => 111, :status => true
24
24
  person.type.should be_nil
25
25
  end
26
26
  end
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+ require 'orm/models/test_class'
3
+ require 'orm/models/person'
4
+
5
+ describe "Time zone awareness" do
6
+ include TimeZoneHelper
7
+
8
+ describe "configuration" do
9
+ it "should have a default time zone configuration" do
10
+ TestClass.default_timezone.should eq :local
11
+ end
12
+
13
+ it "should have default time zone awareness" do
14
+ TestClass.time_zone_aware_attributes.should eq false
15
+ end
16
+
17
+ it "should by default skip no attributes when doing time zone conversions" do
18
+ TestClass.skip_time_zone_conversion_for_attributes.should eq []
19
+ end
20
+
21
+ it "should be possible to skip some attributes in TestClass while Person is untuched" do
22
+ TestClass.skip_time_zone_conversion_for_attributes = [:test]
23
+ TestClass.skip_time_zone_conversion_for_attributes.should include :test
24
+ Person.skip_time_zone_conversion_for_attributes.should be_empty
25
+ end
26
+ end
27
+
28
+
29
+ describe "when to do conversion" do
30
+ let(:field) { MassiveRecord::ORM::Schema::Field.new :name => 'tested_at' }
31
+
32
+ it "should do conversion when attribute is time" do
33
+ in_time_zone "utc" do
34
+ field.type = :time
35
+ TestClass.send(:time_zone_conversion_on_field?, field).should be_true
36
+ end
37
+ end
38
+
39
+ it "should not do conversion if time_zone_aware_attributes is false" do
40
+ field.type = :time
41
+ TestClass.time_zone_aware_attributes = false
42
+ TestClass.send(:time_zone_conversion_on_field?, field).should be_false
43
+ end
44
+
45
+ it "should not do conversion when attribute name is included in skip list" do
46
+ field.type = :time
47
+ TestClass.skip_time_zone_conversion_for_attributes = ['tested_at']
48
+ TestClass.send(:time_zone_conversion_on_field?, field).should be_false
49
+ TestClass.skip_time_zone_conversion_for_attributes = []
50
+ end
51
+
52
+ it "should not do conversion when attribute is string field" do
53
+ field.type = :string
54
+ TestClass.send(:time_zone_conversion_on_field?, field).should be_false
55
+ end
56
+ end
57
+
58
+
59
+
60
+ describe "conversion on attribute" do
61
+ include SetUpHbaseConnectionBeforeAll
62
+ include SetTableNamesToTestTable
63
+
64
+ subject { TestClass.new "test" }
65
+ let(:tz_europe) { "Europe/Stockholm" }
66
+ let(:tz_us) { "Pacific Time (US & Canada)" }
67
+
68
+ let(:time_as_string) { "2010-10-10 10:10:10" }
69
+
70
+ it "should be nil when set to nil" do
71
+ in_time_zone tz_europe do
72
+ subject.tested_at = nil
73
+ subject.tested_at.should be_nil
74
+ end
75
+ end
76
+
77
+ it "should return time as TimeWithZone when attribute accessed directly" do
78
+ in_time_zone tz_europe do
79
+ subject.tested_at = time_as_string
80
+ subject.tested_at.should be_instance_of ActiveSupport::TimeWithZone
81
+ end
82
+ end
83
+
84
+ it "should return time as TimeWithZone when attribute accessed through read_attribute" do
85
+ in_time_zone tz_europe do
86
+ subject.tested_at = time_as_string
87
+ subject.read_attribute(:tested_at).should be_instance_of ActiveSupport::TimeWithZone
88
+ end
89
+ end
90
+
91
+ it "should return time in local time" do
92
+ in_time_zone tz_europe do
93
+ subject.tested_at = time_as_string
94
+ subject.tested_at.time_zone.should eq ActiveSupport::TimeZone[tz_europe]
95
+
96
+ in_time_zone tz_us do
97
+ subject.tested_at.time_zone.should eq ActiveSupport::TimeZone[tz_us]
98
+ end
99
+ end
100
+ end
101
+
102
+ it "should return correct time in other time zones" do
103
+ utc_time = Time.utc(2010, 1, 1)
104
+ us_time = utc_time.in_time_zone(tz_us)
105
+
106
+ in_time_zone tz_europe do
107
+ subject.tested_at = us_time
108
+ subject.tested_at.should eq utc_time
109
+ end
110
+ end
111
+
112
+ it "should return correct times after save" do
113
+ utc_time = Time.now.utc
114
+ europe_time = utc_time.in_time_zone(tz_europe)
115
+ us_time = utc_time.in_time_zone(tz_us)
116
+
117
+ in_time_zone tz_europe do
118
+ subject.tested_at = europe_time
119
+ subject.save!
120
+ end
121
+
122
+ subject.reload
123
+
124
+ in_time_zone tz_us do
125
+ subject.tested_at.to_s.should eq us_time.to_s
126
+ end
127
+ end
128
+
129
+ it "should store time in DB format" do
130
+ utc_time = Time.now.utc
131
+ europe_time = utc_time.in_time_zone(tz_europe)
132
+
133
+ in_time_zone tz_europe do
134
+ subject.tested_at = europe_time
135
+ subject.save!
136
+ end
137
+
138
+ subject.reload
139
+ subject.tested_at.to_s.should eq utc_time.to_s
140
+
141
+ in_time_zone tz_europe do
142
+ subject.tested_at.to_s.should eq europe_time.to_s
143
+ end
144
+ end
145
+
146
+ it "should store time in DB format, raw check" do
147
+ in_time_zone tz_europe do
148
+ subject.tested_at = time_as_string
149
+ subject.save!
150
+
151
+ r = TestClass.table.find("test")
152
+ cell = r.columns["test_family:tested_at"]
153
+ cell.value.should eq MassiveRecord::ORM::Base.coder.dump(Time.zone.parse(time_as_string).utc)
154
+ end
155
+ end
156
+ end
157
+ end