active_nomad 0.2.2 → 0.2.3

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.
data/CHANGELOG CHANGED
@@ -1,3 +1,10 @@
1
+ == 0.2.3 2010-10-12
2
+
3
+ * #inspect no longer requires a database connection.
4
+ * ActiveRecord::Base.default_timezone support.
5
+ * Dirty attribute tracking works.
6
+ * Undeclared attributes are preserved when roundtripped through ActiveNomad.
7
+
1
8
  == 0.2.2 2010-10-07
2
9
 
3
10
  * Support custom destruction strategies.
@@ -1,5 +1,5 @@
1
1
  module ActiveNomad
2
- VERSION = [0, 2, 2]
2
+ VERSION = [0, 2, 3]
3
3
 
4
4
  class << VERSION
5
5
  include Comparable
data/lib/active_nomad.rb CHANGED
@@ -26,25 +26,23 @@ module ActiveNomad
26
26
  def to_serialized_attributes
27
27
  attributes = ActiveSupport::OrderedHash.new
28
28
  columns = self.class.columns_hash
29
- self.class.column_names.sort.each do |name|
30
- column = columns[name] or
31
- next
32
- attributes[name] = serialize_value(send(name), column.type)
29
+ @attributes.sort.each do |name, value|
30
+ column = columns[name]
31
+ attributes[name] = serialize_value(send(name), column ? column.type : :string)
33
32
  end
34
33
  attributes
35
34
  end
36
35
 
37
36
  #
38
- # Recreate an object from a serialized string.
37
+ # Recreate an object from the serialized attributes returned by
38
+ # #to_serialized_attributes.
39
39
  #
40
- def self.from_serialized_attributes(deserialized_attributes)
41
- instance = new
42
- deserialized_attributes.each do |name, serialized_value|
43
- column = columns_hash[name.to_s] or
44
- next
45
- instance.send "#{column.name}=", deserialize_value(serialized_value, column.type)
40
+ def self.from_serialized_attributes(serialized_attributes)
41
+ serialized_attributes = serialized_attributes.dup
42
+ columns_hash.each do |name, column|
43
+ serialized_attributes[name] ||= column.default
46
44
  end
47
- instance
45
+ instantiate(serialized_attributes)
48
46
  end
49
47
 
50
48
  #
@@ -65,9 +63,10 @@ module ActiveNomad
65
63
  #
66
64
  def self.from_query_string(string)
67
65
  return new if string.blank?
68
- serialized_attributes = string.strip.split(/&/).map do |pair|
66
+ serialized_attributes = {}
67
+ string.strip.split(/&/).map do |pair|
69
68
  name, value = pair.split(/=/, 2)
70
- [CGI.unescape(name), CGI.unescape(value)]
69
+ serialized_attributes[CGI.unescape(name)] = CGI.unescape(value)
71
70
  end
72
71
  from_serialized_attributes(serialized_attributes)
73
72
  end
@@ -170,6 +169,10 @@ module ActiveNomad
170
169
  def transaction
171
170
  yield
172
171
  end
172
+
173
+ def inspect
174
+ (n = name).blank? ? '(anonymous)' : n
175
+ end
173
176
  end
174
177
 
175
178
  @columns = []
@@ -185,6 +188,7 @@ module ActiveNomad
185
188
  case type
186
189
  when :datetime, :timestamp, :time
187
190
  # The day in RFC 2822 is optional - chop it.
191
+ value = ActiveRecord::Base.default_timezone == :utc ? value.utc : value.in_time_zone(Time.zone)
188
192
  value.rfc2822.sub(/\A[A-Za-z]{3}, /, '')
189
193
  when :date
190
194
  value.to_date.strftime(DATE_FORMAT)
@@ -197,7 +201,8 @@ module ActiveNomad
197
201
  return nil if string.nil?
198
202
  case type
199
203
  when :datetime, :timestamp, :time
200
- Time.parse(string)
204
+ value = Time.parse(string).in_time_zone
205
+ ActiveRecord::Base.default_timezone == :utc ? value.utc : value.in_time_zone(Time.zone)
201
206
  when :date
202
207
  Date.parse(string)
203
208
  else
@@ -1,6 +1,12 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe ActiveNomad::Base do
4
+ before do
5
+ Time.zone = -5
6
+ ActiveRecord::Base.default_timezone = :utc
7
+ ActiveRecord::Base.time_zone_aware_attributes = true
8
+ end
9
+
4
10
  describe ".attribute" do
5
11
  it "should create a column with the given name and type" do
6
12
  klass = Class.new(ActiveNomad::Base) do
@@ -148,7 +154,7 @@ describe ActiveNomad::Base do
148
154
  end
149
155
 
150
156
  describe "#to_serialized_attributes" do
151
- it "should return a list of attribute names with serialized attributes, sorted by name" do
157
+ it "should return an ordered hash of attribute names with serialized attributes, sorted by name" do
152
158
  klass = Class.new(ActiveNomad::Base) do
153
159
  attribute :integer_attribute, :integer
154
160
  attribute :string_attribute, :string
@@ -169,15 +175,15 @@ describe ActiveNomad::Base do
169
175
  :text_attribute => 'text',
170
176
  :float_attribute => 1.23,
171
177
  :decimal_attribute => BigDecimal.new('123.45'),
172
- :datetime_attribute => Time.parse('03 Feb 2001 12:34:56 -0000'),
173
- :timestamp_attribute => Time.parse('03 Feb 2001 12:34:56 -0000'),
174
- :time_attribute => Time.parse('03 Feb 2001 12:34:56 -0000'),
178
+ :datetime_attribute => Time.parse('03 Feb 2001 7:34:56 -500'),
179
+ :timestamp_attribute => Time.parse('03 Feb 2001 7:34:56 -500'),
180
+ :time_attribute => Time.parse('03 Feb 2001 7:34:56 -500'),
175
181
  :date_attribute => Date.parse('03 Feb 2001'),
176
182
  :binary_attribute => "\0\1",
177
183
  :boolean_attribute => true,
178
184
  :nil_attribute => nil
179
185
  )
180
- instance.to_serialized_attributes.to_a.should == [
186
+ instance.to_serialized_attributes.should == ActiveSupport::OrderedHash[[
181
187
  ['binary_attribute', "\0\1"],
182
188
  ['boolean_attribute', 'true'],
183
189
  ['date_attribute', '03 Feb 2001'],
@@ -190,7 +196,7 @@ describe ActiveNomad::Base do
190
196
  ['text_attribute', 'text'],
191
197
  ['time_attribute', '03 Feb 2001 12:34:56 -0000'],
192
198
  ['timestamp_attribute', '03 Feb 2001 12:34:56 -0000'],
193
- ]
199
+ ]]
194
200
  end
195
201
  end
196
202
 
@@ -210,49 +216,34 @@ describe ActiveNomad::Base do
210
216
  attribute :boolean_attribute, :boolean
211
217
  attribute :nil_attribute, :boolean
212
218
  end
213
- instance = klass.from_serialized_attributes([
214
- [:integer_attribute, '5'],
215
- [:string_attribute, 'string'],
216
- [:text_attribute, 'text'],
217
- [:float_attribute, '1.23'],
218
- [:decimal_attribute, '123.45'],
219
- [:datetime_attribute, '03 Feb 2001 12:34:56 -0000'],
220
- [:timestamp_attribute, '03 Feb 2001 12:34:56 -0000'],
221
- [:time_attribute, '03 Feb 2001 12:34:56 -0000'],
222
- [:date_attribute, '03 Feb 2001'],
223
- [:binary_attribute, "\0\1"],
224
- [:boolean_attribute, 'true'],
225
- [:nil_attribute, nil]
226
- ])
219
+ instance = klass.from_serialized_attributes(ActiveSupport::OrderedHash[[
220
+ ['integer_attribute', '5'],
221
+ ['string_attribute', 'string'],
222
+ ['text_attribute', 'text'],
223
+ ['float_attribute', '1.23'],
224
+ ['decimal_attribute', '123.45'],
225
+ ['datetime_attribute', '03 Feb 2001 12:34:56 -0000'],
226
+ ['timestamp_attribute', '03 Feb 2001 12:34:56 -0000'],
227
+ ['time_attribute', '03 Feb 2001 12:34:56 -0000'],
228
+ ['date_attribute', '03 Feb 2001'],
229
+ ['binary_attribute', "\0\1"],
230
+ ['boolean_attribute', 'true'],
231
+ ['nil_attribute', nil],
232
+ ]])
227
233
  instance.integer_attribute.should == 5
228
234
  instance.string_attribute.should == 'string'
229
235
  instance.text_attribute.should == 'text'
230
236
  instance.float_attribute.should == 1.23
231
237
  instance.decimal_attribute.should == BigDecimal.new('123.45')
232
- instance.datetime_attribute.should == Time.parse('03 Feb 2001 12:34:56 -0000')
233
- instance.timestamp_attribute.should == Time.parse('03 Feb 2001 12:34:56 -0000')
234
- instance.time_attribute.should == Time.parse('03 Feb 2001 12:34:56 -0000')
238
+ instance.datetime_attribute.should == Time.parse('03 Feb 2001 7:34:56 -500').in_time_zone
239
+ instance.timestamp_attribute.should == Time.parse('03 Feb 2001 7:34:56 -500').in_time_zone
240
+ instance.time_attribute.should == Time.parse('03 Feb 2001 12:34:56 -0000').in_time_zone
235
241
  instance.date_attribute.should == Date.parse('03 Feb 2001')
236
242
  instance.binary_attribute.should == "\0\1"
237
243
  instance.boolean_attribute.should be_true
238
244
  instance.nil_attribute.should be_nil
239
245
  end
240
246
 
241
- it "should work with any enumerable" do
242
- klass = Class.new(ActiveNomad::Base) do
243
- attribute :name, :string
244
- end
245
- params = Object.new
246
- class << params
247
- def each
248
- yield :name, 'joe'
249
- end
250
- end
251
- params.extend Enumerable
252
- instance = klass.from_serialized_attributes(params)
253
- instance.name.should == 'joe'
254
- end
255
-
256
247
  it "should leave defaults alone for attributes which are not set" do
257
248
  klass = Class.new(ActiveNomad::Base) do
258
249
  attribute :name, :string, :default => 'Joe'
@@ -317,6 +308,22 @@ describe ActiveNomad::Base do
317
308
  end
318
309
  end
319
310
 
311
+ it "should roundtrip undeclared attributes from the store through ActiveNomad" do
312
+ klass = Class.new(ActiveNomad::Base) do
313
+ attribute :declared, :string
314
+ end
315
+ stored_attributes = ActiveSupport::OrderedHash[[
316
+ ['declared', 'a'],
317
+ ['undeclared', 'b'],
318
+ ]]
319
+ instance = klass.from_serialized_attributes(stored_attributes)
320
+ roundtripped_attributes = instance.to_serialized_attributes
321
+ roundtripped_attributes.to_a.should == [
322
+ ['declared', 'a'],
323
+ ['undeclared', 'b'],
324
+ ]
325
+ end
326
+
320
327
  def self.it_should_roundtrip_through(serializer, deserializer, &block)
321
328
  describe "roundtripping through ##{serializer} and .#{deserializer}" do
322
329
  class_eval(&block) if block
@@ -356,15 +363,18 @@ describe ActiveNomad::Base do
356
363
  it_should_roundtrip :decimal, BigDecimal.new('123.45')
357
364
 
358
365
  it_should_roundtrip :datetime, nil
359
- it_should_roundtrip :datetime, Time.now.in_time_zone
366
+ it_should_roundtrip :datetime, Time.parse('2000-01-01 01:23:34 UTC')
367
+ it_should_roundtrip :datetime, Time.parse('2000-01-01 01:23:34 EST')
360
368
  # TODO: Support DateTime here, which is used when the value is
361
369
  # outside the range of a Time.
362
370
 
363
371
  it_should_roundtrip :timestamp, nil
364
- it_should_roundtrip :timestamp, Time.now.in_time_zone
372
+ it_should_roundtrip :timestamp, Time.parse('2000-01-01 01:23:34 UTC')
373
+ it_should_roundtrip :timestamp, Time.parse('2000-01-01 01:23:34 EST')
365
374
 
366
375
  it_should_roundtrip :time, nil
367
- it_should_roundtrip :time, Time.parse('2000-01-01 01:23:34').in_time_zone
376
+ it_should_roundtrip :time, Time.parse('2000-01-01 01:23:34 UTC')
377
+ it_should_roundtrip :time, Time.parse('2000-01-01 01:23:34 EST')
368
378
 
369
379
  it_should_roundtrip :date, nil
370
380
  it_should_roundtrip :date, Date.today
@@ -449,4 +459,39 @@ describe ActiveNomad::Base do
449
459
  instance.transaction_called.should be_true
450
460
  end
451
461
  end
462
+
463
+ describe "#ATTRIBUTE_changed?" do
464
+ describe "on a new record with no attributes" do
465
+ before do
466
+ @klass = Class.new(ActiveNomad::Base) do
467
+ attribute :i, :integer
468
+ end
469
+ @instance = @klass.new
470
+ end
471
+
472
+ it "should be false" do
473
+ @instance.i_changed?.should be_false
474
+ end
475
+
476
+ describe "when the attribute has been assigned" do
477
+ before do
478
+ @instance.i = 2
479
+ end
480
+
481
+ it "should be true" do
482
+ @instance.i_changed?.should be_true
483
+ end
484
+
485
+ describe "when the record is roundtripped" do
486
+ before do
487
+ @instance = @klass.from_serialized_attributes(@instance.to_serialized_attributes)
488
+ end
489
+
490
+ it "should be false" do
491
+ @instance.i_changed?.should be_false
492
+ end
493
+ end
494
+ end
495
+ end
496
+ end
452
497
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_nomad
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 17
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 2
10
- version: 0.2.2
9
+ - 3
10
+ version: 0.2.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - George Ogata
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-10-07 00:00:00 -04:00
18
+ date: 2010-10-12 00:00:00 -04:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency