dm-core 1.0.0 → 1.0.1

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 (42) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +19 -20
  3. data/Rakefile +1 -1
  4. data/VERSION +1 -1
  5. data/dm-core.gemspec +15 -12
  6. data/lib/dm-core.rb +23 -14
  7. data/lib/dm-core/adapters.rb +7 -3
  8. data/lib/dm-core/adapters/abstract_adapter.rb +3 -9
  9. data/lib/dm-core/associations/many_to_many.rb +1 -1
  10. data/lib/dm-core/associations/many_to_one.rb +3 -4
  11. data/lib/dm-core/associations/one_to_many.rb +2 -2
  12. data/lib/dm-core/associations/one_to_one.rb +1 -1
  13. data/lib/dm-core/collection.rb +4 -6
  14. data/lib/dm-core/model.rb +22 -32
  15. data/lib/dm-core/model/property.rb +15 -39
  16. data/lib/dm-core/property.rb +75 -87
  17. data/lib/dm-core/property/discriminator.rb +3 -3
  18. data/lib/dm-core/property/lookup.rb +42 -0
  19. data/lib/dm-core/property/object.rb +5 -0
  20. data/lib/dm-core/property/serial.rb +6 -1
  21. data/lib/dm-core/query/conditions/comparison.rb +3 -3
  22. data/lib/dm-core/query/conditions/operation.rb +3 -3
  23. data/lib/dm-core/resource.rb +2 -2
  24. data/lib/dm-core/resource/state/dirty.rb +2 -2
  25. data/lib/dm-core/spec/lib/spec_helper.rb +11 -3
  26. data/lib/dm-core/spec/setup.rb +2 -2
  27. data/{spec/public/shared/property_shared_spec.rb → lib/dm-core/spec/shared/public/property_spec.rb} +51 -20
  28. data/{spec/semipublic/shared/property_shared_spec.rb → lib/dm-core/spec/shared/semipublic/property_spec.rb} +22 -26
  29. data/lib/dm-core/support/descendant_set.rb +84 -0
  30. data/lib/dm-core/support/naming_conventions.rb +8 -8
  31. data/lib/dm-core/types/discriminator.rb +2 -2
  32. data/spec/public/associations/many_to_one_with_custom_fk_spec.rb +49 -0
  33. data/spec/public/finalize_spec.rb +42 -11
  34. data/spec/public/property/discriminator_spec.rb +6 -6
  35. data/spec/semipublic/adapters/abstract_adapter_spec.rb +1 -1
  36. data/spec/semipublic/property/lookup_spec.rb +26 -0
  37. data/spec/semipublic/property_spec.rb +43 -0
  38. data/spec/semipublic/resource/state/dirty_spec.rb +4 -2
  39. data/spec/support/{types → properties}/huge_integer.rb +5 -5
  40. data/tasks/local_gemfile.rake +5 -7
  41. metadata +15 -19
  42. data/lib/dm-core/model/descendant_set.rb +0 -81
@@ -65,9 +65,9 @@ module DataMapper
65
65
  # @return [self]
66
66
  #
67
67
  # @api public
68
- def reload(other_query = nil)
68
+ def reload(other_query = Undefined)
69
69
  query = self.query
70
- query = other_query.nil? ? query.dup : query.merge(other_query)
70
+ query = other_query.equal?(Undefined) ? query.dup : query.merge(other_query)
71
71
 
72
72
  # make sure the Identity Map contains all the existing resources
73
73
  identity_map = repository.identity_map(model)
@@ -214,10 +214,8 @@ module DataMapper
214
214
  # Collection scoped by +query+
215
215
  #
216
216
  # @api public
217
- def all(query = nil)
218
- # TODO: update this not to accept a nil value, and instead either
219
- # accept a Hash/Query and nothing else
220
- if query.nil? || (query.kind_of?(Hash) && query.empty?)
217
+ def all(query = Undefined)
218
+ if query.equal?(Undefined) || (query.kind_of?(Hash) && query.empty?)
221
219
  dup
222
220
  else
223
221
  # TODO: if there is no order parameter, and the Collection is not loaded
data/lib/dm-core/model.rb CHANGED
@@ -35,7 +35,7 @@ module DataMapper
35
35
  warn "Passing in +storage_name+ to #{name}.new is deprecated (#{caller[0]})"
36
36
  model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
37
37
  def self.default_storage_name
38
- #{ActiveSupport::Inflector.classify(storage_name).inspect}.freeze
38
+ #{DataMapper::Inflector.classify(storage_name).inspect}.freeze
39
39
  end
40
40
  RUBY
41
41
  end
@@ -75,7 +75,9 @@ module DataMapper
75
75
  # Set containing the descendant classes
76
76
  #
77
77
  # @api semipublic
78
- attr_reader :descendants
78
+ def descendants
79
+ @descendants ||= DescendantSet.new
80
+ end
79
81
 
80
82
  # Return if Resource#save should raise an exception on save failures (globally)
81
83
  #
@@ -203,35 +205,29 @@ module DataMapper
203
205
  end
204
206
 
205
207
  # @api private
206
- def self.extended(model)
207
- descendants = self.descendants
208
+ def self.extended(descendant)
209
+ descendants << descendant
208
210
 
209
- descendants << model
211
+ descendant.instance_variable_set(:@valid, false)
212
+ descendant.instance_variable_set(:@base_model, descendant)
213
+ descendant.instance_variable_set(:@storage_names, {})
214
+ descendant.instance_variable_set(:@default_order, {})
210
215
 
211
- model.instance_variable_set(:@valid, false)
212
- model.instance_variable_set(:@base_model, model)
213
- model.instance_variable_set(:@storage_names, {})
214
- model.instance_variable_set(:@default_order, {})
215
- model.instance_variable_set(:@descendants, descendants.class.new(model, descendants))
216
+ descendant.extend(Chainable)
216
217
 
217
- model.extend(Chainable)
218
-
219
- extra_extensions.each { |mod| model.extend(mod) }
220
- extra_inclusions.each { |mod| model.send(:include, mod) }
218
+ extra_extensions.each { |mod| descendant.extend(mod) }
219
+ extra_inclusions.each { |mod| descendant.send(:include, mod) }
221
220
  end
222
221
 
223
222
  # @api private
224
223
  chainable do
225
- def inherited(model)
226
- descendants = self.descendants
227
-
228
- descendants << model
224
+ def inherited(descendant)
225
+ descendants << descendant
229
226
 
230
- model.instance_variable_set(:@valid, false)
231
- model.instance_variable_set(:@base_model, base_model)
232
- model.instance_variable_set(:@storage_names, @storage_names.dup)
233
- model.instance_variable_set(:@default_order, @default_order.dup)
234
- model.instance_variable_set(:@descendants, descendants.class.new(model, descendants))
227
+ descendant.instance_variable_set(:@valid, false)
228
+ descendant.instance_variable_set(:@base_model, base_model)
229
+ descendant.instance_variable_set(:@storage_names, @storage_names.dup)
230
+ descendant.instance_variable_set(:@default_order, @default_order.dup)
235
231
  end
236
232
  end
237
233
 
@@ -250,7 +246,7 @@ module DataMapper
250
246
  # the names of the storage receptacles for this resource across all repositories
251
247
  #
252
248
  # @return [Hash(Symbol => String)]
253
- # All available names of storage recepticles
249
+ # All available names of storage receptacles
254
250
  #
255
251
  # @api public
256
252
  def storage_names
@@ -339,10 +335,8 @@ module DataMapper
339
335
  # @see Collection
340
336
  #
341
337
  # @api public
342
- def all(query = nil)
343
- # TODO: update this not to accept a nil value, and instead either
344
- # accept a Hash/Query and nothing else
345
- if query.nil? || (query.kind_of?(Hash) && query.empty?)
338
+ def all(query = Undefined)
339
+ if query.equal?(Undefined) || (query.kind_of?(Hash) && query.empty?)
346
340
  # TODO: after adding Enumerable methods to Model, try to return self here
347
341
  new_collection(self.query.dup)
348
342
  else
@@ -726,10 +720,6 @@ module DataMapper
726
720
  self
727
721
  elsif name == :Resource
728
722
  Resource
729
- elsif DataMapper::Property.const_defined?(name)
730
- DataMapper::Property.const_get(name)
731
- elsif DataMapper::Types.const_defined?(name)
732
- DataMapper::Types.const_get(name)
733
723
  else
734
724
  super
735
725
  end
@@ -7,7 +7,7 @@
7
7
  module DataMapper
8
8
  module Model
9
9
  module Property
10
- Model.append_extensions self
10
+ Model.append_extensions self, DataMapper::Property::Lookup
11
11
 
12
12
  extend Chainable
13
13
 
@@ -60,28 +60,13 @@ module DataMapper
60
60
 
61
61
  # if the type can be found within Property then
62
62
  # use that class rather than the primitive
63
- type_name = type.name
64
- unless type_name.blank?
65
- type_name = ActiveSupport::Inflector.demodulize(type_name)
66
-
67
- if DataMapper::Property.const_defined?(type_name)
68
- type = DataMapper::Property.find_const(type_name)
69
- elsif DataMapper::Types.const_defined?(type_name)
70
- type = DataMapper::Types.find_const(type_name)
71
- end
72
- end
63
+ klass = DataMapper::Property.determine_class(type)
73
64
 
74
- unless type < DataMapper::Property || type < DataMapper::Type
65
+ unless klass
75
66
  raise ArgumentError, "+type+ was #{type.inspect}, which is not a supported type"
76
67
  end
77
68
 
78
- klass = if type < DataMapper::Property
79
- type
80
- else
81
- DataMapper::Property.find_const(ActiveSupport::Inflector.demodulize(type.primitive.name))
82
- end
83
-
84
- property = klass.new(self, name, options, type)
69
+ property = klass.new(self, name, options, type < DataMapper::Type ? type : nil)
85
70
 
86
71
  repository_name = self.repository_name
87
72
  properties = properties(repository_name)
@@ -187,7 +172,7 @@ module DataMapper
187
172
 
188
173
  # @api private
189
174
  def properties_with_subclasses(repository_name = default_repository_name)
190
- properties = PropertySet.new
175
+ properties = properties(repository_name).dup
191
176
 
192
177
  descendants.each do |model|
193
178
  model.properties(repository_name).each do |property|
@@ -223,22 +208,20 @@ module DataMapper
223
208
  reader_visibility = property.reader_visibility
224
209
  instance_variable_name = property.instance_variable_name
225
210
 
226
- unless reserved_method?(name)
227
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
228
- chainable do
229
- #{reader_visibility}
230
- def #{name}
231
- return #{instance_variable_name} if defined?(#{instance_variable_name})
232
- property = properties[#{name.inspect}]
233
- #{instance_variable_name} = property ? persisted_state.get(property) : nil
234
- end
211
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
212
+ chainable do
213
+ #{reader_visibility}
214
+ def #{name}
215
+ return #{instance_variable_name} if defined?(#{instance_variable_name})
216
+ property = properties[#{name.inspect}]
217
+ #{instance_variable_name} = property ? persisted_state.get(property) : nil
235
218
  end
236
- RUBY
237
- end
219
+ end
220
+ RUBY
238
221
 
239
222
  boolean_reader_name = "#{name}?"
240
223
 
241
- if property.kind_of?(DataMapper::Property::Boolean) && !reserved_method?(boolean_reader_name)
224
+ if property.kind_of?(DataMapper::Property::Boolean)
242
225
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
243
226
  chainable do
244
227
  #{reader_visibility}
@@ -259,8 +242,6 @@ module DataMapper
259
242
 
260
243
  writer_name = "#{name}="
261
244
 
262
- return if reserved_method?(writer_name)
263
-
264
245
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
265
246
  chainable do
266
247
  #{writer_visibility}
@@ -273,11 +254,6 @@ module DataMapper
273
254
  RUBY
274
255
  end
275
256
 
276
- # @api private
277
- def reserved_method?(name)
278
- method_defined?(name) && !%w[ id type ].include?(name)
279
- end
280
-
281
257
  chainable do
282
258
  # @api public
283
259
  def method_missing(method, *args, &block)
@@ -292,7 +292,7 @@ module DataMapper
292
292
  # @api semipublic
293
293
  def load(value)
294
294
  unless value.nil?
295
- value = @type.load(value, self) if @type
295
+ value = type.load(value, self) if type
296
296
  typecast(value)
297
297
  else
298
298
  value
@@ -307,8 +307,8 @@ module DataMapper
307
307
  #
308
308
  # @api semipublic
309
309
  def dump(value)
310
- if @type
311
- @type.dump(value, self)
310
+ if type
311
+ type.dump(value, self)
312
312
  else
313
313
  value
314
314
  end
@@ -354,9 +354,53 @@ module DataMapper
354
354
  :default, :repository_name, :allow_nil, :allow_blank, :required
355
355
 
356
356
  class << self
357
+ extend Deprecate
358
+
359
+ deprecate :all_descendants, :descendants
360
+
361
+ # @api semipublic
362
+ def determine_class(type)
363
+ if type < DataMapper::Property::Object
364
+ return type
365
+ end
366
+
367
+ name = DataMapper::Inflector.demodulize(type.name)
368
+ klass = find_class(name)
369
+
370
+ if !klass && type < DataMapper::Type
371
+ klass = find_class(type.primitive.name)
372
+ end
373
+
374
+ klass
375
+ end
376
+
377
+ # @api semipublic
378
+ def find_class(name)
379
+ klass = descendants.detect do |descendant|
380
+ DataMapper::Inflector.demodulize(descendant.name) == name
381
+ end
382
+
383
+ if !klass && const_defined?(name)
384
+ klass = const_get(name)
385
+ end
386
+
387
+ klass
388
+ end
389
+
357
390
  # @api public
358
391
  def descendants
359
- @descendants ||= []
392
+ @descendants ||= DescendantSet.new
393
+ end
394
+
395
+ # @api private
396
+ def inherited(descendant)
397
+ descendants << descendant
398
+
399
+ # inherit accepted options
400
+ descendant.accepted_options.concat(accepted_options)
401
+
402
+ # inherit the option values
403
+ options.each { |key, value| descendant.send(key, value) }
360
404
  end
361
405
 
362
406
  # @api public
@@ -368,20 +412,27 @@ module DataMapper
368
412
  def accept_options(*args)
369
413
  accepted_options.concat(args)
370
414
 
371
- # Load Property options
372
- accepted_options.each do |property_option|
415
+ # create methods for each new option
416
+ args.each do |property_option|
373
417
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
374
- def self.#{property_option}(*args) # def unique(*args)
375
- if args.any? # if args.any?
376
- @#{property_option} = args.first # @unique = args.first
377
- else # else
378
- defined?(@#{property_option}) ? @#{property_option} : nil # defined?(@unique) ? @unique : nil
379
- end # end
380
- end # end
418
+ def self.#{property_option}(value = Undefined) # def self.unique(value = Undefined)
419
+ return @#{property_option} if value.equal?(Undefined) # return @unique if value.equal?(Undefined)
420
+ descendants.each do |descendant| # descendants.each do |descendant|
421
+ descendant.#{property_option}(value) # descendant.unique(value)
422
+ end # end
423
+ @#{property_option} = value # @unique = value
424
+ end # end
381
425
  RUBY
382
426
  end
383
427
 
384
- descendants.each {|descendant| descendant.accept_options(*args)}
428
+ descendants.each { |descendant| descendant.accepted_options.concat(args) }
429
+ end
430
+
431
+ # @api private
432
+ def nullable(*args)
433
+ # :required is preferable to :allow_nil, but :nullable maps precisely to :allow_nil
434
+ warn "#nullable is deprecated, use #required instead (#{caller[0]})"
435
+ allow_nil(*args)
385
436
  end
386
437
 
387
438
  # Gives all the options set on this type
@@ -392,49 +443,14 @@ module DataMapper
392
443
  def options
393
444
  options = {}
394
445
  accepted_options.each do |method|
395
- next if !respond_to?(method) || (value = send(method)).nil?
396
- options[method] = value
446
+ value = send(method)
447
+ options[method] = send(method) unless value.nil?
397
448
  end
398
449
  options
399
450
  end
400
-
401
- # Ruby primitive type to use as basis for this type. See
402
- # Property::PRIMITIVES for list of types.
403
- #
404
- # @param primitive [Class, nil]
405
- # The class for the primitive. If nil is passed in, it returns the
406
- # current primitive
407
- #
408
- # @return [Class] if the <primitive> param is nil, return the current primitive.
409
- #
410
- # @api public
411
- def primitive(primitive = nil)
412
- return @primitive if primitive.nil?
413
- @primitive = primitive
414
- end
415
-
416
- def nullable(value)
417
- # :required is preferable to :allow_nil, but :nullable maps precisely to :allow_nil
418
- warn "#nullable is deprecated, use #required instead (#{caller[0]})"
419
- allow_nil(value)
420
- end
421
-
422
- def inherited(base)
423
- descendants << base
424
-
425
- base.primitive primitive
426
- base.accept_options(*accepted_options)
427
-
428
- # inherit the options from the parent class
429
- base_options = base.options
430
-
431
- options.each do |key, value|
432
- base.send(key, value) unless base_options.key?(key)
433
- end
434
- end
435
451
  end
436
452
 
437
- accept_options *Property::OPTIONS
453
+ accept_options :primitive, *Property::OPTIONS
438
454
 
439
455
  # A hook to allow types to extend or modify property it's bound to.
440
456
  # Implementations are not supposed to modify the state of the type class, and
@@ -448,11 +464,11 @@ module DataMapper
448
464
  # @return [String] name of field in data-store
449
465
  #
450
466
  # @api semipublic
451
- def field(repository_name = nil)
467
+ def field(repository_name = Undefined)
452
468
  self_repository_name = self.repository_name
453
469
  klass = self.class
454
470
 
455
- if repository_name
471
+ unless repository_name.equal?(Undefined)
456
472
  warn "Passing in +repository_name+ to #{klass}#field is deprecated (#{caller[0]})"
457
473
 
458
474
  if repository_name != self_repository_name
@@ -516,16 +532,6 @@ module DataMapper
516
532
  @unique_index
517
533
  end
518
534
 
519
- # @api public
520
- def kind_of?(klass)
521
- super || klass == Property
522
- end
523
-
524
- # @api public
525
- def instance_of?(klass)
526
- super || klass == Property
527
- end
528
-
529
535
  # Returns whether or not the property is to be lazy-loaded
530
536
  #
531
537
  # @return [Boolean]
@@ -705,24 +711,6 @@ module DataMapper
705
711
  end
706
712
  end
707
713
 
708
- # Returns given value unchanged for core types and
709
- # uses +dump+ method of the property type for custom types.
710
- #
711
- # @param [Object] loaded_value
712
- # the value to be converted into a storeable (ie., primitive) value
713
- #
714
- # @return [Object]
715
- # the primitive value to be stored in the repository for +val+
716
- #
717
- # @api semipublic
718
- def dump(value)
719
- if custom?
720
- type.dump(loaded_value, self)
721
- else
722
- loaded_value
723
- end
724
- end
725
-
726
714
  # Test the value to see if it is a valid value for this Property
727
715
  #
728
716
  # @param [Object] loaded_value
@@ -775,9 +763,9 @@ module DataMapper
775
763
 
776
764
  # @api semipublic
777
765
  def initialize(model, name, options = {}, type = nil)
778
- options = options.to_hash.dup
766
+ options = options.to_hash.dup
779
767
 
780
- if type && !self.kind_of?(type)
768
+ if type && !kind_of?(type)
781
769
  warn "#{type} < DataMapper::Type is deprecated, use the new DataMapper::Property API instead (#{caller[2]})"
782
770
  @type = type
783
771
  end
@@ -879,10 +867,10 @@ module DataMapper
879
867
  #
880
868
  # @api private
881
869
  def determine_visibility
882
- default_accessor = @options[:accessor] || :public
870
+ default_accessor = @options.fetch(:accessor, :public)
883
871
 
884
- @reader_visibility = @options[:reader] || default_accessor
885
- @writer_visibility = @options[:writer] || default_accessor
872
+ @reader_visibility = @options.fetch(:reader, default_accessor)
873
+ @writer_visibility = @options.fetch(:writer, default_accessor)
886
874
  end
887
875
  end # class Property
888
876
  end