dm-core 1.1.0 → 1.2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. data/Gemfile +13 -11
  2. data/README.rdoc +1 -1
  3. data/Rakefile +1 -2
  4. data/VERSION +1 -1
  5. data/dm-core.gemspec +30 -176
  6. data/lib/dm-core.rb +32 -67
  7. data/lib/dm-core/adapters/abstract_adapter.rb +1 -2
  8. data/lib/dm-core/associations/many_to_many.rb +11 -5
  9. data/lib/dm-core/associations/many_to_one.rb +17 -2
  10. data/lib/dm-core/associations/one_to_many.rb +16 -0
  11. data/lib/dm-core/backwards.rb +13 -0
  12. data/lib/dm-core/collection.rb +1 -1
  13. data/lib/dm-core/model.rb +99 -41
  14. data/lib/dm-core/model/property.rb +24 -27
  15. data/lib/dm-core/model/relationship.rb +22 -28
  16. data/lib/dm-core/property.rb +37 -50
  17. data/lib/dm-core/property/boolean.rb +6 -10
  18. data/lib/dm-core/property/date.rb +0 -2
  19. data/lib/dm-core/property/date_time.rb +0 -2
  20. data/lib/dm-core/property/decimal.rb +5 -1
  21. data/lib/dm-core/property/discriminator.rb +24 -26
  22. data/lib/dm-core/property/float.rb +5 -1
  23. data/lib/dm-core/property/numeric.rb +6 -9
  24. data/lib/dm-core/property/string.rb +2 -1
  25. data/lib/dm-core/property/time.rb +0 -2
  26. data/lib/dm-core/property/typecast/time.rb +7 -2
  27. data/lib/dm-core/property_set.rb +1 -3
  28. data/lib/dm-core/query.rb +3 -10
  29. data/lib/dm-core/query/conditions/comparison.rb +5 -1
  30. data/lib/dm-core/query/conditions/operation.rb +1 -1
  31. data/lib/dm-core/relationship_set.rb +0 -2
  32. data/lib/dm-core/resource.rb +27 -28
  33. data/lib/dm-core/resource/{state.rb → persistence_state.rb} +2 -2
  34. data/lib/dm-core/resource/{state → persistence_state}/clean.rb +4 -4
  35. data/lib/dm-core/resource/{state → persistence_state}/deleted.rb +2 -2
  36. data/lib/dm-core/resource/{state → persistence_state}/dirty.rb +2 -2
  37. data/lib/dm-core/resource/{state → persistence_state}/immutable.rb +3 -3
  38. data/lib/dm-core/resource/{state → persistence_state}/persisted.rb +3 -3
  39. data/lib/dm-core/resource/{state → persistence_state}/transient.rb +3 -3
  40. data/lib/dm-core/spec/lib/adapter_helpers.rb +2 -5
  41. data/lib/dm-core/spec/setup.rb +3 -2
  42. data/lib/dm-core/spec/shared/public/property_spec.rb +8 -0
  43. data/lib/dm-core/spec/shared/resource_spec.rb +14 -0
  44. data/lib/dm-core/spec/shared/semipublic/property_spec.rb +1 -1
  45. data/lib/dm-core/support/descendant_set.rb +0 -2
  46. data/lib/dm-core/support/ext/array.rb +0 -19
  47. data/lib/dm-core/support/ext/blank.rb +1 -0
  48. data/lib/dm-core/support/hook.rb +0 -3
  49. data/lib/dm-core/support/naming_conventions.rb +6 -0
  50. data/lib/dm-core/support/ordered_set.rb +0 -2
  51. data/lib/dm-core/support/subject_set.rb +0 -2
  52. data/lib/dm-core/version.rb +1 -1
  53. data/spec/public/associations/many_to_many_spec.rb +0 -1
  54. data/spec/public/model/property_spec.rb +55 -9
  55. data/spec/public/model/relationship_spec.rb +24 -2
  56. data/spec/public/model_spec.rb +32 -0
  57. data/spec/public/property/binary_spec.rb +14 -6
  58. data/spec/public/property/boolean_spec.rb +14 -6
  59. data/spec/public/property/class_spec.rb +14 -6
  60. data/spec/public/property/date_spec.rb +14 -6
  61. data/spec/public/property/date_time_spec.rb +14 -6
  62. data/spec/public/property/decimal_spec.rb +10 -2
  63. data/spec/public/property/discriminator_spec.rb +15 -1
  64. data/spec/public/property/float_spec.rb +14 -6
  65. data/spec/public/property/integer_spec.rb +14 -6
  66. data/spec/public/property/object_spec.rb +8 -0
  67. data/spec/public/property/serial_spec.rb +14 -6
  68. data/spec/public/property/string_spec.rb +14 -6
  69. data/spec/public/property/text_spec.rb +14 -6
  70. data/spec/public/property/time_spec.rb +14 -6
  71. data/spec/public/resource_spec.rb +58 -0
  72. data/spec/public/shared/finder_shared_spec.rb +8 -4
  73. data/spec/semipublic/associations/many_to_many_spec.rb +2 -0
  74. data/spec/semipublic/associations/many_to_one_spec.rb +2 -0
  75. data/spec/semipublic/associations/one_to_many_spec.rb +2 -0
  76. data/spec/semipublic/associations/one_to_one_spec.rb +2 -0
  77. data/spec/semipublic/property/binary_spec.rb +5 -5
  78. data/spec/semipublic/property/boolean_spec.rb +5 -5
  79. data/spec/semipublic/property/class_spec.rb +5 -5
  80. data/spec/semipublic/property/date_spec.rb +5 -5
  81. data/spec/semipublic/property/date_time_spec.rb +5 -5
  82. data/spec/semipublic/property/decimal_spec.rb +2 -2
  83. data/spec/semipublic/property/discriminator_spec.rb +5 -5
  84. data/spec/semipublic/property/float_spec.rb +5 -5
  85. data/spec/semipublic/property/integer_spec.rb +5 -5
  86. data/spec/semipublic/property/lookup_spec.rb +3 -3
  87. data/spec/semipublic/property/serial_spec.rb +5 -5
  88. data/spec/semipublic/property/string_spec.rb +5 -5
  89. data/spec/semipublic/property/text_spec.rb +5 -5
  90. data/spec/semipublic/property/time_spec.rb +5 -5
  91. data/spec/semipublic/query/conditions/comparison_spec.rb +44 -4
  92. data/spec/semipublic/query_spec.rb +2 -11
  93. data/spec/semipublic/resource/state/clean_spec.rb +6 -6
  94. data/spec/semipublic/resource/state/deleted_spec.rb +4 -4
  95. data/spec/semipublic/resource/state/dirty_spec.rb +8 -8
  96. data/spec/semipublic/resource/state/immutable_spec.rb +6 -6
  97. data/spec/semipublic/resource/state/transient_spec.rb +5 -5
  98. data/spec/semipublic/resource/state_spec.rb +15 -15
  99. data/spec/semipublic/shared/resource_shared_spec.rb +7 -1
  100. data/spec/semipublic/shared/resource_state_shared_spec.rb +8 -8
  101. data/spec/unit/array_spec.rb +0 -14
  102. data/spec/unit/blank_spec.rb +11 -0
  103. metadata +70 -188
  104. data/lib/dm-core/support/inflector.rb +0 -3
@@ -1,15 +1,12 @@
1
1
  # TODO: update Model#respond_to? to return true if method_method missing
2
2
  # would handle the message
3
3
 
4
- require 'dm-core/relationship_set'
5
-
6
4
  module DataMapper
7
5
  module Model
8
6
  module Relationship
9
7
  Model.append_extensions self
10
8
 
11
9
  include DataMapper::Assertions
12
- extend Chainable
13
10
 
14
11
  # Initializes relationships hash for extended model
15
12
  # class.
@@ -23,21 +20,19 @@ module DataMapper
23
20
  model.instance_variable_set(:@relationships, {})
24
21
  end
25
22
 
26
- chainable do
27
- # When DataMapper model is inherited, relationships
28
- # of parent are duplicated and copied to subclass model
29
- #
30
- # @api private
31
- def inherited(model)
32
- model.instance_variable_set(:@relationships, {})
33
-
34
- @relationships.each do |repository_name, relationships|
35
- model_relationships = model.relationships(repository_name)
36
- relationships.each { |relationship| model_relationships << relationship }
37
- end
23
+ # When DataMapper model is inherited, relationships
24
+ # of parent are duplicated and copied to subclass model
25
+ #
26
+ # @api private
27
+ def inherited(model)
28
+ model.instance_variable_set(:@relationships, {})
38
29
 
39
- super
30
+ @relationships.each do |repository_name, relationships|
31
+ model_relationships = model.relationships(repository_name)
32
+ relationships.each { |relationship| model_relationships << relationship }
40
33
  end
34
+
35
+ super
41
36
  end
42
37
 
43
38
  # Returns copy of relationships set in given repository.
@@ -194,7 +189,7 @@ module DataMapper
194
189
  relationship
195
190
  end
196
191
 
197
- private
192
+ private
198
193
 
199
194
  # Extract the model from an Array of arguments
200
195
  #
@@ -342,7 +337,7 @@ module DataMapper
342
337
  # of 1:1, the underlying collection is hidden in a
343
338
  # private ivar, and the resource is in a known ivar
344
339
 
345
- persisted_state.get(relationships[#{name.inspect}], query)
340
+ persistence_state.get(relationships[#{name.inspect}], query)
346
341
  end
347
342
  RUBY
348
343
  end
@@ -362,22 +357,21 @@ module DataMapper
362
357
  #{writer_visibility}
363
358
  def #{writer_name}(target)
364
359
  relationship = relationships[#{name.inspect}]
365
- self.persisted_state = persisted_state.set(relationship, target)
366
- persisted_state.get(relationship)
360
+ self.persistence_state = persistence_state.set(relationship, target)
361
+ persistence_state.get(relationship)
367
362
  end
368
363
  RUBY
369
364
  end
370
365
 
371
- chainable do
372
- # @api public
373
- def method_missing(method, *args, &block)
374
- if relationship = relationships(repository_name)[method]
375
- return Query::Path.new([ relationship ])
376
- end
377
-
378
- super
366
+ # @api public
367
+ def method_missing(method, *args, &block)
368
+ if relationship = relationships(repository_name)[method]
369
+ return Query::Path.new([ relationship ])
379
370
  end
371
+
372
+ super
380
373
  end
374
+
381
375
  end # module Relationship
382
376
  end # module Model
383
377
  end # module DataMapper
@@ -1,6 +1,3 @@
1
- require 'dm-core/resource'
2
- require 'dm-core/query'
3
-
4
1
  module DataMapper
5
2
  # = Properties
6
3
  # Properties for a model are not derived from a database structure, but
@@ -83,11 +80,13 @@ module DataMapper
83
80
  # property :title, String
84
81
  #
85
82
  # def title=(new_title)
86
- # raise ArgumentError if new_title != 'Luke is Awesome'
87
- # @title = new_title
83
+ # raise ArgumentError if new_title != 'Lee is l337'
84
+ # super(new_title)
88
85
  # end
89
86
  # end
90
87
  #
88
+ # Calling super ensures that any validators defined for the property are kept active.
89
+ #
91
90
  # == Lazy Loading
92
91
  # By default, some properties are not loaded when an object is fetched in
93
92
  # DataMapper. These lazily loaded properties are fetched on demand when their
@@ -107,7 +106,7 @@ module DataMapper
107
106
  #
108
107
  # If you want to over-ride the lazy loading on any field you can set it to a
109
108
  # context or false to disable it with the :lazy option. Contexts allow
110
- # multipule lazy properties to be loaded at one time. If you set :lazy to
109
+ # multiple lazy properties to be loaded at one time. If you set :lazy to
111
110
  # true, it is placed in the :default context
112
111
  #
113
112
  # class Post
@@ -179,44 +178,40 @@ module DataMapper
179
178
  #
180
179
  # == Inferred Validations
181
180
  # If you require the dm-validations plugin, auto-validations will
182
- # automatically be mixed-in in to your model classes:
183
- # validation rules that are inferred when properties are declared with
184
- # specific column restrictions.
181
+ # automatically be mixed-in in to your model classes: validation rules that
182
+ # are inferred when properties are declared with specific column restrictions.
185
183
  #
186
- # class Post
187
- # include DataMapper::Resource
188
- #
189
- # property :title, String, :length => 250
190
- # # => infers 'validates_length :title,
191
- # :minimum => 0, :maximum => 250'
184
+ # class Post
185
+ # include DataMapper::Resource
192
186
  #
193
- # property :title, String, :required => true
194
- # # => infers 'validates_present :title
187
+ # property :title, String, :length => 250, :min => 0, :max => 250
188
+ # # => infers 'validates_length :title'
195
189
  #
196
- # property :email, String, :format => :email_address
197
- # # => infers 'validates_format :email, :with => :email_address
190
+ # property :title, String, :required => true
191
+ # # => infers 'validates_present :title'
198
192
  #
199
- # property :title, String, :length => 255, :required => true
200
- # # => infers both 'validates_length' as well as
201
- # # 'validates_present'
202
- # # better: property :title, String, :length => 1..255
193
+ # property :email, String, :format => :email_address
194
+ # # => infers 'validates_format :email, :with => :email_address'
203
195
  #
204
- # end
196
+ # property :title, String, :length => 255, :required => true
197
+ # # => infers both 'validates_length' as well as 'validates_present'
198
+ # # better: property :title, String, :length => 1..255
199
+ # end
205
200
  #
206
201
  # This functionality is available with the dm-validations gem. For more information
207
202
  # about validations, check the documentation for dm-validations.
208
203
  #
209
204
  # == Default Values
210
- # To set a default for a property, use the <tt>:default</tt> key. The
205
+ # To set a default for a property, use the <tt>:default</tt> key. The
211
206
  # property will be set to the value associated with that key the first time
212
207
  # it is accessed, or when the resource is saved if it hasn't been set with
213
- # another value already. This value can be a static value, such as 'hello'
208
+ # another value already. This value can be a static value, such as 'hello'
214
209
  # but it can also be a proc that will be evaluated when the property is read
215
- # before its value has been set. The property is set to the return of the
216
- # proc. The proc is passed two values, the resource the property is being set
210
+ # before its value has been set. The property is set to the return of the
211
+ # proc. The proc is passed two values, the resource the property is being set
217
212
  # for and the property itself.
218
213
  #
219
- # property :display_name, String, :default => { |resource, property| resource.login }
214
+ # property :display_name, String, :default => lambda { |resource, property| resource.login }
220
215
  #
221
216
  # Word of warning. Don't try to read the value of the property you're setting
222
217
  # the default for in the proc. An infinite loop will ensue.
@@ -270,9 +265,6 @@ module DataMapper
270
265
  # Only makes sense for float type properties. Must be > 0.
271
266
  # Default is nil for Float type and 10 for BigDecimal
272
267
  #
273
- # All other keys you pass to +property+ method are stored and available
274
- # as options[:extra_keys].
275
- #
276
268
  # == Overriding default Property options
277
269
  #
278
270
  # There is the ability to reconfigure a Property and it's subclasses by explicitly
@@ -291,7 +283,7 @@ module DataMapper
291
283
  # DataMapper::Property.auto_validation(false)
292
284
  #
293
285
  # # set all mutator methods to be private by default
294
- # DataMapper::Property.writer(false)
286
+ # DataMapper::Property.writer(:private)
295
287
  #
296
288
  # Please note that this has no effect when a subclass has explicitly
297
289
  # defined it's own option. For example, setting the String length to
@@ -311,8 +303,7 @@ module DataMapper
311
303
  module PassThroughLoadDump
312
304
  # @api semipublic
313
305
  def load(value)
314
- return if value.nil?
315
- typecast(value)
306
+ typecast(value) unless value.nil?
316
307
  end
317
308
 
318
309
  # Stub instance method for dumping
@@ -329,10 +320,9 @@ module DataMapper
329
320
 
330
321
  include DataMapper::Assertions
331
322
  include Subject
332
- extend Chainable
333
323
  extend Equalizer
334
324
 
335
- equalize :model, :name
325
+ equalize :model, :name, :options
336
326
 
337
327
  PRIMITIVES = [
338
328
  TrueClass,
@@ -357,7 +347,10 @@ module DataMapper
357
347
  VISIBILITY_OPTIONS = [ :public, :protected, :private ].to_set.freeze
358
348
 
359
349
  # Invalid property names
360
- INVALID_NAMES = (Resource.instance_methods + Resource.private_instance_methods + Query::OPTIONS.to_a).map { |name| name.to_s }.freeze
350
+ INVALID_NAMES = (Resource.instance_methods +
351
+ Resource.private_instance_methods +
352
+ Query::OPTIONS.to_a
353
+ ).map { |name| name.to_s }
361
354
 
362
355
  attr_reader :primitive, :model, :name, :instance_variable_name,
363
356
  :reader_visibility, :writer_visibility, :options,
@@ -462,9 +455,8 @@ module DataMapper
462
455
  # @api public
463
456
  def options
464
457
  options = {}
465
- accepted_options.each do |method|
466
- value = send(method)
467
- options[method] = value unless value.nil?
458
+ accepted_options.each do |name|
459
+ options[name] = send(name) if instance_variable_defined?("@#{name}")
468
460
  end
469
461
  options
470
462
  end
@@ -737,20 +729,15 @@ module DataMapper
737
729
  value.kind_of?(primitive)
738
730
  end
739
731
 
740
- chainable do
741
- def self.new(model, name, options = {})
742
- super
743
- end
744
- end
745
-
746
- protected
732
+ protected
747
733
 
748
734
  # @api semipublic
749
735
  def initialize(model, name, options = {})
750
736
  options = options.to_hash.dup
751
737
 
752
- if INVALID_NAMES.include?(name.to_s)
753
- raise ArgumentError, "+name+ was #{name.inspect}, which cannot be used as a property name since it collides with an existing method or a query option"
738
+ if INVALID_NAMES.include?(name.to_s) || (kind_of?(Boolean) && INVALID_NAMES.include?("#{name}?"))
739
+ raise ArgumentError,
740
+ "+name+ was #{name.inspect}, which cannot be used as a property name since it collides with an existing method or a query option"
754
741
  end
755
742
 
756
743
  assert_valid_options(options)
@@ -5,6 +5,11 @@ module DataMapper
5
5
 
6
6
  primitive ::TrueClass
7
7
 
8
+ TRUE_VALUES = [ 1, '1', 't', 'T', 'true', 'TRUE' ].freeze
9
+ FALSE_VALUES = [ 0, '0', 'f', 'F', 'false', 'FALSE' ].freeze
10
+ BOOLEAN_MAP = Hash[
11
+ TRUE_VALUES.product([ true ]) + FALSE_VALUES.product([ false ]) ].freeze
12
+
8
13
  def primitive?(value)
9
14
  value == true || value == false
10
15
  end
@@ -19,16 +24,7 @@ module DataMapper
19
24
  #
20
25
  # @api private
21
26
  def typecast_to_primitive(value)
22
- if value.kind_of?(::Integer)
23
- return true if value == 1
24
- return false if value == 0
25
- elsif value.respond_to?(:to_str)
26
- string_value = value.to_str.downcase
27
- return true if %w[ true 1 t ].include?(string_value)
28
- return false if %w[ false 0 f ].include?(string_value)
29
- end
30
-
31
- value
27
+ BOOLEAN_MAP.fetch(value, value)
32
28
  end
33
29
  end # class Boolean
34
30
  end # class Property
@@ -1,5 +1,3 @@
1
- require 'dm-core/property/typecast/time'
2
-
3
1
  module DataMapper
4
2
  class Property
5
3
  class Date < Object
@@ -1,5 +1,3 @@
1
- require 'dm-core/property/typecast/time'
2
-
3
1
  module DataMapper
4
2
  class Property
5
3
  class DateTime < Object
@@ -3,7 +3,11 @@ module DataMapper
3
3
  class Decimal < Numeric
4
4
  primitive BigDecimal
5
5
 
6
- DEFAULT_SCALE = 0
6
+ DEFAULT_PRECISION = 10
7
+ DEFAULT_SCALE = 0
8
+
9
+ precision(DEFAULT_PRECISION)
10
+ scale(DEFAULT_SCALE)
7
11
 
8
12
  protected
9
13
 
@@ -8,40 +8,38 @@ module DataMapper
8
8
 
9
9
  # @api private
10
10
  def bind
11
- model.default_scope(repository_name).update(name => model.descendants.dup << model)
12
-
13
- model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
14
- extend Chainable
11
+ model.extend Model unless model < Model
12
+ end
15
13
 
16
- extendable do
17
- def inherited(model)
18
- super # setup self.descendants
19
- set_discriminator_scope_for(model)
20
- end
14
+ module Model
15
+ def inherited(model)
16
+ super # setup self.descendants
17
+ set_discriminator_scope_for(model)
18
+ end
21
19
 
22
- def new(*args, &block)
23
- if args.size == 1 && args.first.kind_of?(Hash)
24
- discriminator = properties(repository_name).discriminator
20
+ def new(*args, &block)
21
+ if args.size == 1 && args.first.kind_of?(Hash)
22
+ discriminator = properties(repository_name).discriminator
25
23
 
26
- if discriminator_value = args.first[discriminator.name]
27
- model = discriminator.typecast_to_primitive(discriminator_value)
24
+ if discriminator_value = args.first[discriminator.name]
25
+ model = discriminator.typecast_to_primitive(discriminator_value)
28
26
 
29
- if model.kind_of?(Model) && !model.equal?(self)
30
- return model.new(*args, &block)
31
- end
32
- end
27
+ if model.kind_of?(Model) && !model.equal?(self)
28
+ return model.new(*args, &block)
33
29
  end
34
-
35
- super
36
30
  end
31
+ end
37
32
 
38
- private
33
+ super
34
+ end
39
35
 
40
- def set_discriminator_scope_for(model)
41
- model.default_scope(#{repository_name.inspect}).update(#{name.inspect} => model.descendants.dup << model)
42
- end
43
- end
44
- RUBY
36
+ private
37
+
38
+ def set_discriminator_scope_for(model)
39
+ discriminator = self.properties.discriminator
40
+ default_scope = model.default_scope(discriminator.repository_name)
41
+ default_scope.update(discriminator.name => model.descendants.dup << model)
42
+ end
45
43
  end
46
44
  end # class Discriminator
47
45
  end # module Property
@@ -3,7 +3,11 @@ module DataMapper
3
3
  class Float < Numeric
4
4
  primitive ::Float
5
5
 
6
- DEFAULT_SCALE = nil
6
+ DEFAULT_PRECISION = 10
7
+ DEFAULT_SCALE = nil
8
+
9
+ precision(DEFAULT_PRECISION)
10
+ scale(DEFAULT_SCALE)
7
11
 
8
12
  protected
9
13
 
@@ -1,5 +1,3 @@
1
- require 'dm-core/property/typecast/numeric'
2
-
3
1
  module DataMapper
4
2
  class Property
5
3
  class Numeric < Object
@@ -9,9 +7,8 @@ module DataMapper
9
7
  accept_options :precision, :scale, :min, :max
10
8
  attr_reader :precision, :scale, :min, :max
11
9
 
12
- DEFAULT_PRECISION = 10
13
- DEFAULT_NUMERIC_MIN = 0
14
- DEFAULT_NUMERIC_MAX = 2**31-1
10
+ DEFAULT_NUMERIC_MIN = 0
11
+ DEFAULT_NUMERIC_MAX = 2**31-1
15
12
 
16
13
  protected
17
14
 
@@ -19,8 +16,8 @@ module DataMapper
19
16
  super
20
17
 
21
18
  if @primitive == BigDecimal || @primitive == ::Float
22
- @precision = @options.fetch(:precision, DEFAULT_PRECISION)
23
- @scale = @options.fetch(:scale, self.class::DEFAULT_SCALE)
19
+ @precision = @options.fetch(:precision)
20
+ @scale = @options.fetch(:scale)
24
21
 
25
22
  unless @precision > 0
26
23
  raise ArgumentError, "precision must be greater than 0, but was #{@precision.inspect}"
@@ -28,8 +25,8 @@ module DataMapper
28
25
  end
29
26
 
30
27
  if @options.key?(:min) || @options.key?(:max)
31
- @min = @options.fetch(:min, DEFAULT_NUMERIC_MIN)
32
- @max = @options.fetch(:max, DEFAULT_NUMERIC_MAX)
28
+ @min = @options.fetch(:min, self.class::DEFAULT_NUMERIC_MIN)
29
+ @max = @options.fetch(:max, self.class::DEFAULT_NUMERIC_MAX)
33
30
 
34
31
  if @max < DEFAULT_NUMERIC_MIN && !@options.key?(:min)
35
32
  raise ArgumentError, "min should be specified when the max is less than #{DEFAULT_NUMERIC_MIN}"