dm-core 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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