massive_record 0.2.1 → 0.2.2.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 (135) hide show
  1. data/CHANGELOG.md +58 -2
  2. data/Gemfile.lock +17 -17
  3. data/README.md +98 -41
  4. data/lib/massive_record.rb +2 -1
  5. data/lib/massive_record/adapters/thrift/hbase/hbase.rb +2425 -2154
  6. data/lib/massive_record/adapters/thrift/hbase/hbase_constants.rb +3 -3
  7. data/lib/massive_record/adapters/thrift/hbase/hbase_types.rb +195 -195
  8. data/lib/massive_record/adapters/thrift/row.rb +35 -4
  9. data/lib/massive_record/adapters/thrift/table.rb +49 -12
  10. data/lib/massive_record/orm/attribute_methods.rb +77 -5
  11. data/lib/massive_record/orm/attribute_methods/cast_numbers_on_write.rb +24 -0
  12. data/lib/massive_record/orm/attribute_methods/dirty.rb +18 -0
  13. data/lib/massive_record/orm/attribute_methods/time_zone_conversion.rb +24 -3
  14. data/lib/massive_record/orm/attribute_methods/write.rb +8 -1
  15. data/lib/massive_record/orm/base.rb +62 -8
  16. data/lib/massive_record/orm/column.rb +7 -11
  17. data/lib/massive_record/orm/default_id.rb +1 -1
  18. data/lib/massive_record/orm/embedded.rb +66 -0
  19. data/lib/massive_record/orm/errors.rb +17 -0
  20. data/lib/massive_record/orm/finders.rb +124 -71
  21. data/lib/massive_record/orm/finders/rescue_missing_table_on_find.rb +1 -1
  22. data/lib/massive_record/orm/finders/scope.rb +58 -34
  23. data/lib/massive_record/orm/id_factory.rb +22 -105
  24. data/lib/massive_record/orm/id_factory/atomic_incrementation.rb +117 -0
  25. data/lib/massive_record/orm/id_factory/timestamp.rb +60 -0
  26. data/lib/massive_record/orm/identity_map.rb +256 -0
  27. data/lib/massive_record/orm/log_subscriber.rb +18 -0
  28. data/lib/massive_record/orm/observer.rb +69 -0
  29. data/lib/massive_record/orm/persistence.rb +47 -119
  30. data/lib/massive_record/orm/persistence/operations.rb +100 -0
  31. data/lib/massive_record/orm/persistence/operations/atomic_operation.rb +71 -0
  32. data/lib/massive_record/orm/persistence/operations/destroy.rb +17 -0
  33. data/lib/massive_record/orm/persistence/operations/embedded/destroy.rb +26 -0
  34. data/lib/massive_record/orm/persistence/operations/embedded/insert.rb +27 -0
  35. data/lib/massive_record/orm/persistence/operations/embedded/operation_helpers.rb +66 -0
  36. data/lib/massive_record/orm/persistence/operations/embedded/reload.rb +39 -0
  37. data/lib/massive_record/orm/persistence/operations/embedded/update.rb +29 -0
  38. data/lib/massive_record/orm/persistence/operations/insert.rb +19 -0
  39. data/lib/massive_record/orm/persistence/operations/reload.rb +26 -0
  40. data/lib/massive_record/orm/persistence/operations/suppress.rb +15 -0
  41. data/lib/massive_record/orm/persistence/operations/table_operation_helpers.rb +106 -0
  42. data/lib/massive_record/orm/persistence/operations/update.rb +25 -0
  43. data/lib/massive_record/orm/query_instrumentation.rb +26 -49
  44. data/lib/massive_record/orm/raw_data.rb +47 -0
  45. data/lib/massive_record/orm/relations.rb +4 -0
  46. data/lib/massive_record/orm/relations/interface.rb +134 -0
  47. data/lib/massive_record/orm/relations/metadata.rb +58 -12
  48. data/lib/massive_record/orm/relations/proxy.rb +17 -12
  49. data/lib/massive_record/orm/relations/proxy/embedded_in.rb +54 -0
  50. data/lib/massive_record/orm/relations/proxy/embedded_in_polymorphic.rb +15 -0
  51. data/lib/massive_record/orm/relations/proxy/embeds_many.rb +215 -0
  52. data/lib/massive_record/orm/relations/proxy/references_many.rb +112 -88
  53. data/lib/massive_record/orm/relations/proxy/references_one.rb +1 -1
  54. data/lib/massive_record/orm/relations/proxy/references_one_polymorphic.rb +1 -1
  55. data/lib/massive_record/orm/relations/proxy_collection.rb +84 -0
  56. data/lib/massive_record/orm/schema/column_family.rb +3 -2
  57. data/lib/massive_record/orm/schema/{column_interface.rb → embedded_interface.rb} +38 -4
  58. data/lib/massive_record/orm/schema/field.rb +2 -0
  59. data/lib/massive_record/orm/schema/table_interface.rb +19 -2
  60. data/lib/massive_record/orm/single_table_inheritance.rb +37 -2
  61. data/lib/massive_record/orm/timestamps.rb +17 -7
  62. data/lib/massive_record/orm/validations.rb +4 -0
  63. data/lib/massive_record/orm/validations/associated.rb +50 -0
  64. data/lib/massive_record/rails/railtie.rb +31 -0
  65. data/lib/massive_record/version.rb +1 -1
  66. data/lib/massive_record/wrapper/cell.rb +8 -1
  67. data/massive_record.gemspec +4 -4
  68. data/spec/adapter/thrift/atomic_increment_spec.rb +16 -0
  69. data/spec/adapter/thrift/table_find_spec.rb +14 -2
  70. data/spec/adapter/thrift/table_spec.rb +6 -6
  71. data/spec/adapter/thrift/utf8_encoding_of_id_spec.rb +71 -0
  72. data/spec/orm/cases/attribute_methods_spec.rb +215 -22
  73. data/spec/orm/cases/auto_generate_id_spec.rb +1 -1
  74. data/spec/orm/cases/change_id_spec.rb +62 -0
  75. data/spec/orm/cases/default_id_spec.rb +25 -6
  76. data/spec/orm/cases/default_values_spec.rb +6 -3
  77. data/spec/orm/cases/dirty_spec.rb +150 -102
  78. data/spec/orm/cases/embedded_spec.rb +250 -0
  79. data/spec/orm/cases/{finder_default_scope.rb → finder_default_scope_spec.rb} +4 -0
  80. data/spec/orm/cases/finder_scope_spec.rb +96 -29
  81. data/spec/orm/cases/finders_spec.rb +57 -10
  82. data/spec/orm/cases/id_factory/atomic_incrementation_spec.rb +72 -0
  83. data/spec/orm/cases/id_factory/timestamp_spec.rb +61 -0
  84. data/spec/orm/cases/identity_map/identity_map_spec.rb +357 -0
  85. data/spec/orm/cases/identity_map/middleware_spec.rb +74 -0
  86. data/spec/orm/cases/log_subscriber_spec.rb +15 -2
  87. data/spec/orm/cases/observing_spec.rb +61 -0
  88. data/spec/orm/cases/persistence_spec.rb +151 -60
  89. data/spec/orm/cases/raw_data_spec.rb +58 -0
  90. data/spec/orm/cases/single_table_inheritance_spec.rb +58 -2
  91. data/spec/orm/cases/table_spec.rb +3 -3
  92. data/spec/orm/cases/time_zone_awareness_spec.rb +27 -0
  93. data/spec/orm/cases/timestamps_spec.rb +23 -109
  94. data/spec/orm/cases/validation_spec.rb +9 -0
  95. data/spec/orm/models/address.rb +5 -1
  96. data/spec/orm/models/address_with_timestamp.rb +12 -0
  97. data/spec/orm/models/car.rb +5 -0
  98. data/spec/orm/models/person.rb +13 -1
  99. data/spec/orm/models/person_with_timestamp.rb +4 -2
  100. data/spec/orm/models/test_class.rb +1 -0
  101. data/spec/orm/persistence/operations/atomic_operation_spec.rb +58 -0
  102. data/spec/orm/persistence/operations/destroy_spec.rb +22 -0
  103. data/spec/orm/persistence/operations/embedded/destroy_spec.rb +71 -0
  104. data/spec/orm/persistence/operations/embedded/insert_spec.rb +59 -0
  105. data/spec/orm/persistence/operations/embedded/operation_helpers_spec.rb +92 -0
  106. data/spec/orm/persistence/operations/embedded/reload_spec.rb +67 -0
  107. data/spec/orm/persistence/operations/embedded/update_spec.rb +60 -0
  108. data/spec/orm/persistence/operations/insert_spec.rb +31 -0
  109. data/spec/orm/persistence/operations/reload_spec.rb +48 -0
  110. data/spec/orm/persistence/operations/suppress_spec.rb +17 -0
  111. data/spec/orm/persistence/operations/table_operation_helpers_spec.rb +98 -0
  112. data/spec/orm/persistence/operations/update_spec.rb +25 -0
  113. data/spec/orm/persistence/operations_spec.rb +58 -0
  114. data/spec/orm/relations/interface_spec.rb +188 -0
  115. data/spec/orm/relations/metadata_spec.rb +92 -15
  116. data/spec/orm/relations/proxy/embedded_in_polymorphic_spec.rb +37 -0
  117. data/spec/orm/relations/proxy/embedded_in_spec.rb +66 -0
  118. data/spec/orm/relations/proxy/embeds_many_spec.rb +651 -0
  119. data/spec/orm/relations/proxy/references_many_spec.rb +466 -2
  120. data/spec/orm/schema/column_family_spec.rb +21 -0
  121. data/spec/orm/schema/embedded_interface_spec.rb +181 -0
  122. data/spec/orm/schema/field_spec.rb +7 -0
  123. data/spec/orm/schema/table_interface_spec.rb +31 -1
  124. data/spec/shared/orm/id_factories.rb +44 -0
  125. data/spec/shared/orm/model_with_timestamps.rb +132 -0
  126. data/spec/shared/orm/persistence/a_persistence_embedded_operation_class.rb +3 -0
  127. data/spec/shared/orm/persistence/a_persistence_operation_class.rb +11 -0
  128. data/spec/shared/orm/persistence/a_persistence_table_operation_class.rb +11 -0
  129. data/spec/shared/orm/relations/proxy.rb +9 -2
  130. data/spec/spec_helper.rb +9 -0
  131. data/spec/support/mock_massive_record_connection.rb +2 -1
  132. metadata +106 -21
  133. data/spec/orm/cases/column_spec.rb +0 -49
  134. data/spec/orm/cases/id_factory_spec.rb +0 -92
  135. data/spec/orm/schema/column_interface_spec.rb +0 -136
@@ -1,3 +1,3 @@
1
1
  module MassiveRecord
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2.rc1"
3
3
  end
@@ -10,7 +10,7 @@ module MassiveRecord
10
10
  #
11
11
  # Packs an integer as a 64-bit signed integer, native endian (int64_t)
12
12
  # Reverse it as the byte order in hbase are reversed
13
- #
13
+ p #
14
14
  def self.integer_to_hex_string(int)
15
15
  [int].pack('q').reverse
16
16
  end
@@ -24,6 +24,13 @@ module MassiveRecord
24
24
  end
25
25
 
26
26
 
27
+ def self.populate_from_tcell(tcell)
28
+ new({
29
+ :value => tcell.value,
30
+ :created_at => Time.at(tcell.timestamp / 1000, (tcell.timestamp % 1000) * 1000)
31
+ })
32
+ end
33
+
27
34
 
28
35
  def initialize(opts = {})
29
36
  self.value = opts[:value]
@@ -14,12 +14,12 @@ Gem::Specification.new do |s|
14
14
  s.rubyforge_project = "massive_record"
15
15
 
16
16
 
17
- s.add_dependency "thrift", ">= 0.5.0"
18
- s.add_dependency "activesupport"
19
- s.add_dependency "activemodel"
17
+ s.add_dependency "thrift", "= 0.6.0"
18
+ s.add_dependency "activesupport", "~> 3.0.7"
19
+ s.add_dependency "activemodel", "~> 3.0.7"
20
20
  s.add_dependency "tzinfo"
21
21
 
22
- s.add_development_dependency "rspec", ">= 2.1.0"
22
+ s.add_development_dependency "rspec"
23
23
 
24
24
 
25
25
  s.files = `git ls-files`.split("\n")
@@ -51,5 +51,21 @@ describe MassiveRecord::Wrapper::Row do
51
51
  subject.atomic_increment(atomic_inc_attr_name)
52
52
  subject.read_atomic_integer_value(atomic_inc_attr_name).should eq 1
53
53
  end
54
+
55
+ it "returns -1 after one decrementation of 1" do
56
+ subject.atomic_decrement(atomic_inc_attr_name).should eq -1
57
+ subject.read_atomic_integer_value(atomic_inc_attr_name).should eq -1
58
+ end
59
+ end
60
+
61
+
62
+ describe "#atomic_decrement" do
63
+ it "decrements to -1 when called on a new value" do
64
+ subject.atomic_decrement(atomic_inc_attr_name).should eq -1
65
+ end
66
+
67
+ it "decrements by 2 when asked to do so" do
68
+ subject.atomic_decrement(atomic_inc_attr_name, 2).should eq -2
69
+ end
54
70
  end
55
71
  end
@@ -8,7 +8,7 @@ describe MassiveRecord::Adapters::Thrift::Table do
8
8
  end
9
9
 
10
10
  subject do
11
- MassiveRecord::Wrapper::Table.new(@connection, MR_CONFIG['table'])
11
+ MassiveRecord::Wrapper::Table.new(connection, MR_CONFIG['table'])
12
12
  end
13
13
 
14
14
 
@@ -26,7 +26,7 @@ describe MassiveRecord::Adapters::Thrift::Table do
26
26
  before do
27
27
  2.times do |index|
28
28
  MassiveRecord::Wrapper::Row.new.tap do |row|
29
- row.id = index.to_s
29
+ row.id = (index + 1).to_s
30
30
  row.values = {:base => {:first_name => "John-#{index}", :last_name => "Doe-#{index}" }}
31
31
  row.table = subject
32
32
  row.save
@@ -37,4 +37,16 @@ describe MassiveRecord::Adapters::Thrift::Table do
37
37
  after do
38
38
  subject.all.each &:destroy
39
39
  end
40
+
41
+ it "finds one id" do
42
+ subject.find("1").id.should eq "1"
43
+ end
44
+
45
+ it "finds one id given as array" do
46
+ subject.find(["1"]).first.id.should eq "1"
47
+ end
48
+
49
+ it "finds multiple ids" do
50
+ subject.find(["1", "2"]).collect(&:id).should eq ["1", "2"]
51
+ end
40
52
  end
@@ -297,20 +297,20 @@ describe "A table" do
297
297
  group_number.should == 5
298
298
  end
299
299
 
300
- it "should find 1 row using the :start option" do
301
- @table.all(:start => "A1").size.should == 1
300
+ it "should find 1 row using the :starts_with option" do
301
+ @table.all(:starts_with => "A1").size.should == 1
302
302
  end
303
303
 
304
- it "should find 5 rows using the :start option" do
305
- @table.all(:start => "A").size.should == 5
304
+ it "should find 5 rows using the :starts_with option" do
305
+ @table.all(:starts_with => "A").size.should == 5
306
306
  end
307
307
 
308
308
  it "should find 9 rows using the :offset option" do
309
309
  @table.all(:offset => "A2").size.should == 9
310
310
  end
311
311
 
312
- it "should find 4 rows using both :offset and :start options" do
313
- @table.all(:offset => "A2", :start => "A").size.should == 4
312
+ it "should find 4 rows using both :offset and :starts_with options" do
313
+ @table.all(:offset => "A2", :starts_with => "A").size.should == 4
314
314
  end
315
315
  end
316
316
  end
@@ -0,0 +1,71 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe MassiveRecord::Wrapper::Row do
5
+ let(:connection) do
6
+ MassiveRecord::Wrapper::Connection.new(:host => MR_CONFIG['host'], :port => MR_CONFIG['port']).tap do |connection|
7
+ connection.open
8
+ end
9
+ end
10
+
11
+ let(:table) do
12
+ MassiveRecord::Wrapper::Table.new(connection, MR_CONFIG['table']).tap do |table|
13
+ table.column_families.create(:misc)
14
+ table.save
15
+ end
16
+ end
17
+
18
+ let(:id) { 'thorbjørn' }
19
+ let(:name) { 'Thorbjorn' }
20
+
21
+ subject do
22
+ MassiveRecord::Wrapper::Row.new.tap do |row|
23
+ row.id = id
24
+ row.values = {:misc => {:name => name}}
25
+ row.table = table
26
+ end
27
+ end
28
+
29
+
30
+
31
+ after do
32
+ table.all.each &:destroy
33
+ end
34
+
35
+ after :all do
36
+ table.destroy
37
+ connection.close
38
+ end
39
+
40
+
41
+
42
+
43
+
44
+ describe "ids utf-8 encoded" do
45
+ context "new record" do
46
+ it "saves" do
47
+ subject.save.should be_true
48
+ end
49
+ end
50
+
51
+ context "persisted record" do
52
+ before { subject.save }
53
+
54
+ it "finds" do
55
+ table.find(id).values["misc:name"].should eq name
56
+ end
57
+
58
+ it "gets a cell" do
59
+ table.get(id, :misc, :name).should eq name
60
+ end
61
+
62
+ it "finds with starts_with option" do
63
+ table.all(:starts_with => id).first.values["misc:name"].should eq name
64
+ end
65
+
66
+ it "finds with offset option" do
67
+ table.all(:offset => id).first.values["misc:name"].should eq name
68
+ end
69
+ end
70
+ end
71
+ end
@@ -2,66 +2,259 @@ require 'spec_helper'
2
2
  require 'orm/models/person'
3
3
 
4
4
  describe "attribute methods" do
5
- before do
6
- @model = Person.new "5", :name => "John", :age => "15"
7
- end
5
+ include TimeZoneHelper
6
+
7
+ subject { Person.new "5", :name => "John", :age => "15" }
8
8
 
9
9
  it "should define reader method" do
10
- @model.name.should == "John"
10
+ subject.name.should == "John"
11
11
  end
12
12
 
13
13
  it "should define writer method" do
14
- @model.name = "Bar"
15
- @model.name.should == "Bar"
14
+ subject.name = "Bar"
15
+ subject.name.should == "Bar"
16
16
  end
17
17
 
18
18
  it "should be possible to write attributes" do
19
- @model.write_attribute :name, "baaaaar"
20
- @model.name.should == "baaaaar"
19
+ subject.write_attribute :name, "baaaaar"
20
+ subject.name.should == "baaaaar"
21
+ end
22
+
23
+
24
+ it "converts correcly written floats as string to float on write" do
25
+ subject.write_attribute(:carma, "1.5")
26
+ subject.carma.should eq 1.5
27
+ end
28
+
29
+ it "converts baldy written floats as string to float on write" do
30
+ subject.write_attribute(:carma, "1.5f")
31
+ subject.carma.should eq 1.5
32
+ end
33
+
34
+ it "keeps nil when assigned to float" do
35
+ subject.write_attribute(:carma, nil)
36
+ subject.carma.should eq nil
37
+ end
38
+
39
+ it "keeps empty string when assigned to float" do
40
+ subject.write_attribute(:carma, "")
41
+ subject.carma.should eq nil
21
42
  end
22
43
 
44
+ it "converts correcly written integers as string to integer on write" do
45
+ subject.write_attribute(:points, "1")
46
+ subject.points.should eq 1
47
+ end
48
+
49
+ it "converts baldy written integers as string to integer on write" do
50
+ subject.write_attribute(:points, "1f")
51
+ subject.points.should eq 1
52
+ end
53
+
54
+ it "keeps nil when assigned to integer" do
55
+ subject.write_attribute(:points, nil)
56
+ subject.points.should eq nil
57
+ end
58
+
59
+ it "keeps empty string when assigned to integer" do
60
+ subject.write_attribute(:points, "")
61
+ subject.points.should eq nil
62
+ end
63
+
64
+
65
+
66
+
67
+
23
68
  it "should be possible to read attributes" do
24
- @model.read_attribute(:name).should == "John"
69
+ subject.read_attribute(:name).should == "John"
25
70
  end
26
71
 
27
72
  it "should return casted value when read" do
28
- @model.read_attribute(:age).should == 15
73
+ subject.read_attribute(:age).should == 15
29
74
  end
30
75
 
31
76
  it "should read from a method if it has been defined" do
32
- @model.should_receive(:_name).and_return("my name is")
33
- @model.read_attribute(:name).should eq "my name is"
77
+ subject.should_receive(:_name).and_return("my name is")
78
+ subject.read_attribute(:name).should eq "my name is"
34
79
  end
35
80
 
36
81
  describe "#attributes" do
37
82
  it "should contain the id" do
38
- @model.attributes.should include("id")
83
+ subject.attributes.should include("id")
39
84
  end
40
85
 
41
86
  it "should not return @attributes directly" do
42
- @model.attributes.object_id.should_not == @model.instance_variable_get(:@attributes).object_id
87
+ subject.attributes.object_id.should_not == subject.instance_variable_get(:@attributes).object_id
43
88
  end
44
89
 
45
90
  it "should ask read_attribute for help" do
46
- @model.should_receive(:read_attribute).any_number_of_times.and_return("stub")
47
- @model.attributes['name'].should eq 'stub'
91
+ subject.should_receive(:read_attribute).any_number_of_times.and_return("stub")
92
+ subject.attributes['name'].should eq 'stub'
48
93
  end
49
94
  end
50
95
 
51
96
  describe "#attributes=" do
52
97
  it "should simply return if incomming value is not a hash" do
53
- @model.attributes = "FOO BAR"
54
- @model.attributes.keys.should include("name")
98
+ subject.attributes = "FOO BAR"
99
+ subject.attributes.keys.should include("name")
55
100
  end
56
101
 
57
102
  it "should mass assign attributes" do
58
- @model.attributes = {:name => "Foo", :age => 20}
59
- @model.name.should == "Foo"
60
- @model.age.should == 20
103
+ subject.attributes = {:name => "Foo", :age => 20}
104
+ subject.name.should == "Foo"
105
+ subject.age.should == 20
61
106
  end
62
107
 
63
108
  it "should raise an error if we encounter an unkown attribute" do
64
- lambda { @model.attributes = {:unkown => "foo"} }.should raise_error MassiveRecord::ORM::UnknownAttributeError
109
+ lambda { subject.attributes = {:unkown => "foo"} }.should raise_error MassiveRecord::ORM::UnknownAttributeError
110
+ end
111
+
112
+ describe "multiparameter" do
113
+ describe "date" do
114
+ let(:date) { Date.new 1981, 8, 20 }
115
+ let(:params) do
116
+ {
117
+ "date_of_birth(1i)" => date.year.to_s,
118
+ "date_of_birth(2i)" => date.month.to_s,
119
+ "date_of_birth(3i)" => date.day.to_s
120
+ }
121
+ end
122
+
123
+ it "parses a complete multiparameter" do
124
+ subject.attributes = params
125
+ subject.date_of_birth.should eq date
126
+ end
127
+
128
+ it "parses when year is missing" do
129
+ params["date_of_birth(1i)"] = ""
130
+ subject.attributes = params
131
+ subject.date_of_birth.should eq Date.new(1, date.month, date.day)
132
+ end
133
+
134
+ it "parses when month is missing" do
135
+ params["date_of_birth(2i)"] = ""
136
+ subject.attributes = params
137
+ subject.date_of_birth.should eq Date.new(date.year, 1, date.day)
138
+ end
139
+
140
+ it "parses when day is missing" do
141
+ params["date_of_birth(3i)"] = ""
142
+ subject.attributes = params
143
+ subject.date_of_birth.should eq Date.new(date.year, date.month, 1)
144
+ end
145
+
146
+ it "sets to nil if all are blank" do
147
+ params["date_of_birth(1i)"] = ""
148
+ params["date_of_birth(2i)"] = ""
149
+ params["date_of_birth(3i)"] = ""
150
+ subject.attributes = params
151
+ subject.date_of_birth.should be_nil
152
+ end
153
+
154
+ it "ignores the overflow of arguments" do
155
+ params["date_of_birth(4i)"] = "1"
156
+ params["date_of_birth(5i)"] = "2"
157
+ params["date_of_birth(6i)"] = "3"
158
+ subject.attributes = params
159
+ subject.date_of_birth.should eq date
160
+ end
161
+
162
+ it "sets to nil if any of the values are on wrong format" do
163
+ params["date_of_birth(3i)"] = "foobar"
164
+ subject.attributes = params
165
+ subject.date_of_birth.should be_nil
166
+ end
167
+ end
168
+
169
+ describe "time" do
170
+ let(:time) { Time.new 2011, 8, 20, 17, 30, 0 }
171
+ let(:tz_europe) { "Europe/Stockholm" }
172
+ let(:tz_us) { "Pacific Time (US & Canada)" }
173
+
174
+ let(:params) do
175
+ {
176
+ "last_signed_in_at(1i)" => time.year.to_s,
177
+ "last_signed_in_at(2i)" => time.month.to_s,
178
+ "last_signed_in_at(3i)" => time.day.to_s,
179
+ "last_signed_in_at(4i)" => time.hour.to_s,
180
+ "last_signed_in_at(5i)" => time.min.to_s,
181
+ "last_signed_in_at(6i)" => time.sec.to_s
182
+ }
183
+ end
184
+
185
+ it "parses complete multiparameter" do
186
+ subject.attributes = params
187
+ subject.last_signed_in_at.should eq time
188
+ end
189
+
190
+ it "parses complete multiparameter with time zone" do
191
+ in_time_zone tz_us do
192
+ subject.attributes = params
193
+ subject.last_signed_in_at.should eq time.in_time_zone(tz_us)
194
+ end
195
+ end
196
+
197
+ it "parses an old time" do
198
+ year = 1835
199
+ params["last_signed_in_at(1i)"] = year.to_s
200
+ subject.attributes = params
201
+ subject.last_signed_in_at.should eq Time.new(
202
+ year, time.month, time.day,
203
+ time.hour, time.min, time.sec
204
+ )
205
+ end
206
+
207
+ it "parses when year is missing" do
208
+ params["last_signed_in_at(1i)"] = ""
209
+ subject.attributes = params
210
+ subject.last_signed_in_at.should eq Time.new(
211
+ 0, time.month, time.day,
212
+ time.hour, time.min, time.sec
213
+ )
214
+ end
215
+
216
+ it "parses when hour is missing" do
217
+ params["last_signed_in_at(4i)"] = ""
218
+ subject.attributes = params
219
+ subject.last_signed_in_at.should eq Time.new(
220
+ time.year, time.month, time.day,
221
+ 0, time.min, time.sec
222
+ )
223
+ end
224
+
225
+ it "parses when seconds is missing" do
226
+ params["last_signed_in_at(6i)"] = ""
227
+ subject.attributes = params
228
+ subject.last_signed_in_at.should eq Time.new(
229
+ time.year, time.month, time.day,
230
+ time.hour, time.min, 0
231
+ )
232
+ end
233
+
234
+ it "sets to nil if all are blank" do
235
+ params["last_signed_in_at(1i)"] = ""
236
+ params["last_signed_in_at(2i)"] = ""
237
+ params["last_signed_in_at(3i)"] = ""
238
+ params["last_signed_in_at(4i)"] = ""
239
+ params["last_signed_in_at(5i)"] = ""
240
+ params["last_signed_in_at(6i)"] = ""
241
+ subject.attributes = params
242
+ subject.last_signed_in_at.should eq nil
243
+ end
244
+
245
+ it "sets to nil if any of the values are on wrong format" do
246
+ params["last_signed_in_at(3i)"] = "foobar"
247
+ subject.attributes = params
248
+ subject.last_signed_in_at.should be_nil
249
+ end
250
+
251
+ it "ignores the overflow of arguments" do
252
+ params["last_signed_in_at(7i)"] = "1"
253
+ params["last_signed_in_at(8i)"] = "2"
254
+ subject.attributes = params
255
+ subject.last_signed_in_at.should eq time
256
+ end
257
+ end
65
258
  end
66
259
  end
67
260
  end