massive_record 0.2.0 → 0.2.1.rc1

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 (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