datamapper 0.2.4 → 0.2.5

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 (65) hide show
  1. data/CHANGELOG +16 -1
  2. data/README +10 -8
  3. data/environment.rb +1 -1
  4. data/example.rb +13 -5
  5. data/lib/data_mapper.rb +2 -0
  6. data/lib/data_mapper/adapters/abstract_adapter.rb +61 -0
  7. data/lib/data_mapper/adapters/data_object_adapter.rb +33 -6
  8. data/lib/data_mapper/adapters/mysql_adapter.rb +5 -0
  9. data/lib/data_mapper/adapters/postgresql_adapter.rb +12 -0
  10. data/lib/data_mapper/adapters/sql/commands/load_command.rb +6 -14
  11. data/lib/data_mapper/adapters/sql/mappings/column.rb +37 -26
  12. data/lib/data_mapper/adapters/sql/mappings/table.rb +50 -8
  13. data/lib/data_mapper/associations.rb +4 -5
  14. data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +2 -2
  15. data/lib/data_mapper/associations/has_many_association.rb +40 -13
  16. data/lib/data_mapper/associations/has_n_association.rb +1 -1
  17. data/lib/data_mapper/base.rb +5 -448
  18. data/lib/data_mapper/callbacks.rb +12 -2
  19. data/lib/data_mapper/context.rb +4 -0
  20. data/lib/data_mapper/database.rb +1 -1
  21. data/lib/data_mapper/identity_map.rb +2 -2
  22. data/lib/data_mapper/persistence.rb +538 -0
  23. data/lib/data_mapper/support/active_record_impersonation.rb +21 -3
  24. data/lib/data_mapper/support/errors.rb +2 -0
  25. data/lib/data_mapper/support/serialization.rb +7 -10
  26. data/lib/data_mapper/support/struct.rb +7 -0
  27. data/lib/data_mapper/validatable_extensions/validations/validates_uniqueness_of.rb +11 -4
  28. data/performance.rb +23 -10
  29. data/rakefile.rb +1 -1
  30. data/spec/active_record_impersonation_spec.rb +2 -6
  31. data/spec/acts_as_tree_spec.rb +3 -1
  32. data/spec/associations_spec.rb +40 -160
  33. data/spec/attributes_spec.rb +1 -1
  34. data/spec/base_spec.rb +41 -13
  35. data/spec/callbacks_spec.rb +32 -0
  36. data/spec/coersion_spec.rb +1 -1
  37. data/spec/column_spec.rb +22 -12
  38. data/spec/dependency_spec.rb +5 -3
  39. data/spec/embedded_value_spec.rb +33 -17
  40. data/spec/has_many_association_spec.rb +173 -0
  41. data/spec/legacy_spec.rb +2 -2
  42. data/spec/load_command_spec.rb +59 -7
  43. data/spec/models/animal.rb +6 -2
  44. data/spec/models/animals_exhibit.rb +3 -1
  45. data/spec/models/career.rb +2 -1
  46. data/spec/models/comment.rb +3 -1
  47. data/spec/models/exhibit.rb +3 -1
  48. data/spec/models/fruit.rb +3 -1
  49. data/spec/models/person.rb +10 -1
  50. data/spec/models/post.rb +3 -1
  51. data/spec/models/project.rb +3 -1
  52. data/spec/models/section.rb +3 -1
  53. data/spec/models/serializer.rb +3 -1
  54. data/spec/models/user.rb +3 -1
  55. data/spec/models/zoo.rb +3 -1
  56. data/spec/paranoia_spec.rb +3 -1
  57. data/spec/postgres_spec.rb +54 -0
  58. data/spec/save_command_spec.rb +9 -5
  59. data/spec/schema_spec.rb +0 -91
  60. data/spec/single_table_inheritance_spec.rb +8 -0
  61. data/spec/table_spec.rb +46 -0
  62. data/spec/validates_uniqueness_of_spec.rb +19 -1
  63. metadata +8 -10
  64. data/lib/data_mapper/associations/has_one_association.rb +0 -77
  65. data/plugins/dataobjects/do_rb +0 -0
@@ -60,7 +60,7 @@ module DataMapper
60
60
  def initialize
61
61
  @callbacks = Hash.new do |h,k|
62
62
  raise 'Callback names must be Symbols' unless k.kind_of?(Symbol)
63
- h[k] = []
63
+ h[k] = Set.new
64
64
  end
65
65
  end
66
66
 
@@ -89,9 +89,19 @@ module DataMapper
89
89
  # the instance executed against (as a method call).
90
90
  def add(name, block)
91
91
  callback = @callbacks[name]
92
- raise ArgumentError.new("You didn't specify a callback in String, Symbol or Proc form.") if block.nil?
92
+ raise ArgumentError.new("You didn't specify a callback in String, Symbol or Proc form.") unless [String, Proc, Symbol].detect { |type| block.is_a?(type) }
93
93
  callback << block
94
94
  end
95
+
96
+ def dup
97
+ copy = self.class.new
98
+ @callbacks.each_pair do |name, callbacks|
99
+ callbacks.each do |callback|
100
+ copy.add(name, callback)
101
+ end
102
+ end
103
+ return copy
104
+ end
95
105
  end
96
106
 
97
107
  end
@@ -44,6 +44,10 @@ module DataMapper
44
44
  @adapter.load(self, klass, options).first
45
45
  end
46
46
 
47
+ def get(klass, keys)
48
+ @adapter.get(self, klass, keys)
49
+ end
50
+
47
51
  def all(klass, options = {})
48
52
  @adapter.load(self, klass, options)
49
53
  end
@@ -168,7 +168,7 @@ module DataMapper
168
168
  @schema_search_path = nil
169
169
  @username = 'root'
170
170
  @password = ''
171
- @index_path = (Dir::pwd + "/indexes")
171
+ @socket = nil
172
172
 
173
173
  @log_level = Logger::WARN
174
174
  @log_stream = nil
@@ -38,8 +38,8 @@ module DataMapper
38
38
  end
39
39
 
40
40
  private
41
- def mapped_class(klass)
42
- if klass.superclass == DataMapper::Base
41
+ def mapped_class(klass)
42
+ if ! klass.superclass.respond_to?(:persistent?)
43
43
  klass
44
44
  else
45
45
  mapped_class(klass.superclass)
@@ -0,0 +1,538 @@
1
+ require 'data_mapper/property'
2
+ require 'data_mapper/support/active_record_impersonation'
3
+ require 'data_mapper/support/serialization'
4
+ require 'data_mapper/validations'
5
+ require 'data_mapper/associations'
6
+ require 'data_mapper/callbacks'
7
+ require 'data_mapper/embedded_value'
8
+ require 'data_mapper/auto_migrations'
9
+ require 'data_mapper/dependency_queue'
10
+ require 'data_mapper/support/struct'
11
+
12
+ module DataMapper
13
+ # See DataMapper::Persistence::ClassMethods for DataMapper's DSL documentation.
14
+ module Persistence
15
+ # This probably needs to be protected
16
+ attr_accessor :loaded_set
17
+
18
+ def self.included(klass)
19
+ klass.extend(ClassMethods)
20
+
21
+ klass.send(:include, Associations)
22
+ klass.send(:include, Validations)
23
+ klass.send(:include, CallbacksHelper)
24
+ klass.send(:include, Support::ActiveRecordImpersonation)
25
+ klass.send(:include, Support::Serialization)
26
+
27
+ prepare_for_persistence(klass)
28
+ end
29
+
30
+ def self.prepare_for_persistence(klass)
31
+ klass.instance_variable_set('@properties', [])
32
+
33
+ klass.send :extend, AutoMigrations
34
+ klass.subclasses
35
+ DataMapper::Persistence::subclasses << klass unless klass == DataMapper::Base
36
+ klass.send(:undef_method, :id) if method_defined?(:id)
37
+
38
+ return if klass == DataMapper::Base
39
+
40
+ # When this class is sub-classed, copy the declared columns.
41
+ klass.class_eval do
42
+ def self.subclasses
43
+ @subclasses || (@subclasses = [])
44
+ end
45
+
46
+ def self.inherited(subclass)
47
+ super_table = database.table(self)
48
+
49
+ if super_table.type_column.nil?
50
+ super_table.add_column(:type, :class, {})
51
+ end
52
+
53
+ subclass.instance_variable_set("@callbacks", self.callbacks.dup)
54
+
55
+ self::subclasses << subclass
56
+ end
57
+
58
+ def self.persistent?
59
+ true
60
+ end
61
+ end
62
+ end
63
+
64
+ def self.auto_migrate!
65
+ subclasses.each do |subclass|
66
+ subclass.auto_migrate!
67
+ end
68
+ end
69
+
70
+ # Track classes that include this module.
71
+ def self.subclasses
72
+ @subclasses || (@subclasses = [])
73
+ end
74
+
75
+ def self.dependencies
76
+ @dependency_queue || (@dependency_queue = DependencyQueue.new)
77
+ end
78
+
79
+ def initialize(details = nil)
80
+ case details
81
+ when Hash then self.attributes = details
82
+ when details.respond_to?(:persistent?) then self.unsafe_attributes = details.attributes
83
+ when Struct then self.unsafe_attributes = details.attributes
84
+ when NilClass then nil
85
+ end
86
+ end
87
+
88
+ module ClassMethods
89
+
90
+ # Track classes that include this module.
91
+ def subclasses
92
+ @subclasses || (@subclasses = [])
93
+ end
94
+
95
+ def logger
96
+ database.logger
97
+ end
98
+
99
+ def transaction
100
+ yield
101
+ end
102
+
103
+ def foreign_key
104
+ Inflector.underscore(self.name) + "_id"
105
+ end
106
+
107
+ def extended(klass)
108
+ unless klass == DataMapper::Base
109
+ klass.class_eval do
110
+ def persistent?
111
+ true
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ def table
118
+ database.table(self)
119
+ end
120
+
121
+ # NOTE: check is only for psql, so maybe the postgres adapter should define
122
+ # its own property options. currently it will produce a warning tho since
123
+ # PROPERTY_OPTIONS is a constant
124
+ PROPERTY_OPTIONS = [
125
+ :public, :protected, :private, :accessor, :reader, :writer,
126
+ :lazy, :default, :nullable, :key, :serial, :column, :size, :length,
127
+ :index, :check
128
+ ]
129
+
130
+ # Adds property accessors for a field that you'd like to be able to modify. The DataMapper doesn't
131
+ # use the table schema to infer accessors, you must explicity call #property to add field accessors
132
+ # to your model.
133
+ #
134
+ # EXAMPLE:
135
+ # class CellProvider
136
+ # property :name, :string
137
+ # property :rating, :integer
138
+ # end
139
+ #
140
+ # att = CellProvider.new(:name => 'AT&T')
141
+ # att.rating = 3
142
+ # puts att.name, att.rating
143
+ #
144
+ # => AT&T
145
+ # => 3
146
+ #
147
+ # OPTIONS:
148
+ # * <tt>lazy</tt>: Lazy load the specified property (:lazy => true). False by default.
149
+ # * <tt>accessor</tt>: Set method visibility for the property accessors. Affects both
150
+ # reader and writer. Allowable values are :public, :protected, :private. Defaults to
151
+ # :public
152
+ # * <tt>reader</tt>: Like the accessor option but affects only the property reader.
153
+ # * <tt>writer</tt>: Like the accessor option but affects only the property writer.
154
+ # * <tt>protected</tt>: Alias for :reader => :public, :writer => :protected
155
+ # * <tt>private</tt>: Alias for :reader => :public, :writer => :private
156
+ def property(name, type, options = {})
157
+
158
+ options.each_pair do |k,v|
159
+ raise ArgumentError.new("#{k.inspect} is not a supported option in DataMapper::Base::PROPERTY_OPTIONS") unless PROPERTY_OPTIONS.include?(k)
160
+ end
161
+
162
+ visibility_options = [:public, :protected, :private]
163
+ reader_visibility = options[:reader] || options[:accessor] || :public
164
+ writer_visibility = options[:writer] || options[:accessor] || :public
165
+ writer_visibility = :protected if options[:protected]
166
+ writer_visibility = :private if options[:private]
167
+
168
+ raise(ArgumentError.new, "property visibility must be :public, :protected, or :private") unless visibility_options.include?(reader_visibility) && visibility_options.include?(writer_visibility)
169
+
170
+ mapping = database.schema[self].add_column(name.to_s.sub(/\?$/, '').to_sym, type, options)
171
+
172
+ property_getter(mapping, reader_visibility)
173
+ property_setter(mapping, writer_visibility)
174
+
175
+ if MAGIC_PROPERTIES.has_key?(name)
176
+ class_eval(&MAGIC_PROPERTIES[name])
177
+ end
178
+
179
+ return name
180
+ end
181
+
182
+ MAGIC_PROPERTIES = {
183
+ :updated_at => lambda { before_save { |x| x.updated_at = Time::now } },
184
+ :updated_on => lambda { before_save { |x| x.updated_on = Date::today } },
185
+ :created_at => lambda { before_create { |x| x.created_at = Time::now } },
186
+ :created_on => lambda { before_create { |x| x.created_on = Date::today } }
187
+ }
188
+
189
+ def property_getter(mapping, visibility = :public)
190
+ if mapping.lazy?
191
+ class_eval <<-EOS
192
+ #{visibility.to_s}
193
+ def #{mapping.name}
194
+ lazy_load!(#{mapping.name.inspect})
195
+ class << self;
196
+ attr_accessor #{mapping.name.inspect}
197
+ end
198
+ @#{mapping.name}
199
+ end
200
+ EOS
201
+ else
202
+ class_eval("#{visibility.to_s}; def #{mapping.name}; #{mapping.instance_variable_name} end") unless [ :public, :private, :protected ].include?(mapping.name)
203
+ end
204
+
205
+ if mapping.type == :boolean
206
+ class_eval("#{visibility.to_s}; def #{mapping.name.to_s.ensure_ends_with('?')}; #{mapping.instance_variable_name} end")
207
+ end
208
+
209
+ rescue SyntaxError
210
+ raise SyntaxError.new(mapping)
211
+ end
212
+
213
+ def property_setter(mapping, visibility = :public)
214
+ if mapping.lazy?
215
+ class_eval <<-EOS
216
+ #{visibility.to_s}
217
+ def #{mapping.name}=(value)
218
+ class << self;
219
+ attr_accessor #{mapping.name.inspect}
220
+ end
221
+ @#{mapping.name} = value
222
+ end
223
+ EOS
224
+ else
225
+ class_eval("#{visibility.to_s}; def #{mapping.name}=(value); #{mapping.instance_variable_name} = value end")
226
+ end
227
+ rescue SyntaxError
228
+ raise SyntaxError.new(mapping)
229
+ end
230
+
231
+ # Allows you to override the table name for a model.
232
+ # EXAMPLE:
233
+ # class WorkItem
234
+ # set_table_name 't_work_item_list'
235
+ # end
236
+ def set_table_name(value)
237
+ database.table(self).name = value
238
+ end
239
+ # An embedded value maps the values of an object to fields in the record of the object's owner.
240
+ # #embed takes a symbol to define the embedded class, options, and an optional block. See
241
+ # examples for use cases.
242
+ #
243
+ # EXAMPLE:
244
+ # class CellPhone < DataMapper::Base
245
+ # property :number, :string
246
+ #
247
+ # embed :owner, :prefix => true do
248
+ # property :name, :string
249
+ # property :address, :string
250
+ # end
251
+ # end
252
+ #
253
+ # my_phone = CellPhone.new
254
+ # my_phone.owner.name = "Nick"
255
+ # puts my_phone.owner.name
256
+ #
257
+ # => Nick
258
+ #
259
+ # OPTIONS:
260
+ # * <tt>prefix</tt>: define a column prefix, so instead of mapping :address to an 'address'
261
+ # column, it would map to 'owner_address' in the example above. If :prefix => true is
262
+ # specified, the prefix will be the name of the symbol given as the first parameter. If the
263
+ # prefix is a string the specified string will be used for the prefix.
264
+ # * <tt>lazy</tt>: lazy-load all embedded values at the same time. :lazy => true to enable.
265
+ # Disabled (false) by default.
266
+ # * <tt>accessor</tt>: Set method visibility for all embedded properties. Affects both
267
+ # reader and writer. Allowable values are :public, :protected, :private. Defaults to :public
268
+ # * <tt>reader</tt>: Like the accessor option but affects only embedded property readers.
269
+ # * <tt>writer</tt>: Like the accessor option but affects only embedded property writers.
270
+ # * <tt>protected</tt>: Alias for :reader => :public, :writer => :protected
271
+ # * <tt>private</tt>: Alias for :reader => :public, :writer => :private
272
+ #
273
+ def embed(name, options = {}, &block)
274
+ EmbeddedValue::define(self, name, options, &block)
275
+ end
276
+
277
+ def properties
278
+ @properties
279
+ end
280
+
281
+ # Creates a composite index for an arbitrary number of database columns. Note that
282
+ # it also is possible to specify single indexes directly for each property.
283
+ #
284
+ # === EXAMPLE WITH COMPOSITE INDEX:
285
+ # class Person < DataMapper::Base
286
+ # property :server_id, :integer
287
+ # property :name, :string
288
+ #
289
+ # index [:server_id, :name]
290
+ # end
291
+ #
292
+ # === EXAMPLE WITH COMPOSITE UNIQUE INDEX:
293
+ # class Person < DataMapper::Base
294
+ # property :server_id, :integer
295
+ # property :name, :string
296
+ #
297
+ # index [:server_id, :name], :unique => true
298
+ # end
299
+ #
300
+ # === SINGLE INDEX EXAMPLES:
301
+ # * property :name, :index => true
302
+ # * property :name, :index => :unique
303
+ def index(indexes, unique = false)
304
+ if indexes.kind_of?(Array) # if given an index of multiple columns
305
+ database.schema[self].add_composite_index(indexes, unique)
306
+ else
307
+ raise ArgumentError.new("You must supply an array for the composite index")
308
+ end
309
+ end
310
+
311
+ end
312
+
313
+ # Lazy-loads the attributes for a loaded_set, then overwrites the accessors
314
+ # for the named methods so that the lazy_loading is skipped the second time.
315
+ def lazy_load!(*names)
316
+
317
+ names = names.map { |name| name.to_sym }.reject { |name| lazy_loaded_attributes.include?(name) }
318
+
319
+ reset_attribute = lambda do |instance|
320
+ singleton_class = (class << instance; self end)
321
+ names.each do |name|
322
+ instance.lazy_loaded_attributes << name
323
+ singleton_class.send(:attr_accessor, name)
324
+ end
325
+ end
326
+
327
+ unless names.empty? || new_record? || loaded_set.nil?
328
+
329
+ key = database_context.table(self.class).key.to_sym
330
+ keys_to_select = loaded_set.map do |instance|
331
+ instance.send(key)
332
+ end
333
+
334
+ database_context.all(
335
+ self.class,
336
+ :select => ([key] + names),
337
+ :reload => true,
338
+ key => keys_to_select
339
+ ).each(&reset_attribute)
340
+ else
341
+ reset_attribute[self]
342
+ end
343
+ end
344
+
345
+ # Mass-assign mapped fields.
346
+ def attributes=(values_hash)
347
+ table = database_context.table(self.class)
348
+
349
+ values_hash.delete_if do |key, value|
350
+ !self.class.public_method_defined?("#{key}=")
351
+ end.each_pair do |key, value|
352
+ if respond_to?(key)
353
+ send("#{key}=", value)
354
+ elsif column = table[key]
355
+ instance_variable_set(column.instance_variable_name, value)
356
+ end
357
+ end
358
+ end
359
+
360
+ def database_context
361
+ @database_context || ( @database_context = database )
362
+ end
363
+
364
+ def database_context=(value)
365
+ @database_context = value
366
+ end
367
+
368
+ def logger
369
+ self.class.logger
370
+ end
371
+
372
+ def new_record?
373
+ @new_record.nil? || @new_record
374
+ end
375
+
376
+ def ==(other)
377
+ other.is_a?(self.class) && private_attributes == other.send(:private_attributes)
378
+ end
379
+
380
+ # Returns the difference between two objects, in terms of their attributes.
381
+ def ^(other)
382
+ results = {}
383
+
384
+ self_attributes, other_attributes = attributes, other.attributes
385
+
386
+ self_attributes.each_pair do |k,v|
387
+ other_value = other_attributes[k]
388
+ unless v == other_value
389
+ results[k] = [v, other_value]
390
+ end
391
+ end
392
+
393
+ results
394
+ end
395
+
396
+ def lazy_loaded_attributes
397
+ @lazy_loaded_attributes || @lazy_loaded_attributes = Set.new
398
+ end
399
+
400
+ def loaded_attributes
401
+ pairs = {}
402
+
403
+ database_context.table(self).columns.each do |column|
404
+ pairs[column.name] = instance_variable_get(column.instance_variable_name)
405
+ end
406
+
407
+ pairs
408
+ end
409
+
410
+ def update_attributes(update_hash)
411
+ self.attributes = update_hash
412
+ self.save
413
+ end
414
+
415
+ def attributes
416
+ pairs = {}
417
+
418
+ database_context.table(self).columns.each do |column|
419
+ if self.class.public_method_defined?(column.name)
420
+ lazy_load!(column.name) if column.lazy?
421
+ value = instance_variable_get(column.instance_variable_name)
422
+ pairs[column.name] = column.type == :class ? value.to_s : value
423
+ end
424
+ end
425
+
426
+ pairs
427
+ end
428
+
429
+ def unsafe_attributes=(values_hash)
430
+ table = database_context.table(self.class)
431
+
432
+ values_hash.each_pair do |key, value|
433
+ if respond_to?(key)
434
+ send("#{key}=", value)
435
+ elsif column = table[key]
436
+ instance_variable_set(column.instance_variable_name, value)
437
+ end
438
+ end
439
+ end
440
+
441
+ def dirty?
442
+ result = database_context.table(self).columns.any? do |column|
443
+ if column.type == :object
444
+ Marshal.dump(self.instance_variable_get(column.instance_variable_name)) != original_values[column.name]
445
+ else
446
+ self.instance_variable_get(column.instance_variable_name) != original_values[column.name]
447
+ end
448
+ end
449
+
450
+ return true if result
451
+
452
+ loaded_associations.any? do |loaded_association|
453
+ loaded_association.dirty?
454
+ end
455
+ end
456
+
457
+ def dirty_attributes
458
+ pairs = {}
459
+
460
+ database_context.table(self).columns.each do |column|
461
+ value = instance_variable_get(column.instance_variable_name)
462
+ if value != original_values[column.name] && (!new_record? || !column.serial?)
463
+ pairs[column.name] = column.type != :object ? value : YAML.dump(value)
464
+ end
465
+ end
466
+
467
+ pairs
468
+ end
469
+
470
+ def original_values=(values)
471
+ values.each_pair do |k,v|
472
+ original_values[k] = case v
473
+ when String, Date, Time then v.dup
474
+ # when column.type == :object then Marshal.dump(v)
475
+ else v
476
+ end
477
+ end
478
+ end
479
+
480
+ def original_values
481
+ class << self
482
+ attr_reader :original_values
483
+ end
484
+
485
+ @original_values = {}
486
+ end
487
+
488
+ def loaded_set=(value)
489
+ value << self
490
+ @loaded_set = value
491
+ end
492
+
493
+ def inspect
494
+ inspected_attributes = attributes.map { |k,v| "@#{k}=#{v.inspect}" }
495
+
496
+ instance_variables.each do |name|
497
+ if instance_variable_get(name).kind_of?(Associations::HasManyAssociation)
498
+ inspected_attributes << "#{name}=#{instance_variable_get(name).inspect}"
499
+ end
500
+ end
501
+
502
+ "#<%s:0x%x @new_record=%s, %s>" % [self.class.name, (object_id * 2), new_record?, inspected_attributes.join(', ')]
503
+ end
504
+
505
+ def loaded_associations
506
+ @loaded_associations || @loaded_associations = []
507
+ end
508
+
509
+ def key=(value)
510
+ key_column = database_context.table(self.class).key
511
+ @__key = key_column.type_cast_value(value)
512
+ instance_variable_set(key_column.instance_variable_name, @__key)
513
+ end
514
+
515
+ def key
516
+ @__key || @__key = begin
517
+ key_column = database_context.table(self.class).key
518
+ key_column.type_cast_value(instance_variable_get(key_column.instance_variable_name))
519
+ end
520
+ end
521
+
522
+ private
523
+
524
+ # return all attributes, regardless of their visibility
525
+ def private_attributes
526
+ pairs = {}
527
+
528
+ database_context.table(self).columns.each do |column|
529
+ lazy_load!(column.name) if column.lazy?
530
+ value = instance_variable_get(column.instance_variable_name)
531
+ pairs[column.name] = column.type == :class ? value.to_s : value
532
+ end
533
+
534
+ pairs
535
+ end
536
+
537
+ end
538
+ end