dm-core 1.0.0.rc2 → 1.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/Gemfile +1 -1
  2. data/LICENSE +1 -1
  3. data/README.rdoc +1 -1
  4. data/Rakefile +3 -4
  5. data/VERSION +1 -1
  6. data/dm-core.gemspec +7 -5
  7. data/lib/dm-core.rb +44 -0
  8. data/lib/dm-core/adapters.rb +1 -1
  9. data/lib/dm-core/adapters/abstract_adapter.rb +16 -0
  10. data/lib/dm-core/collection.rb +2 -2
  11. data/lib/dm-core/model.rb +64 -53
  12. data/lib/dm-core/model/property.rb +14 -6
  13. data/lib/dm-core/model/relationship.rb +10 -18
  14. data/lib/dm-core/property.rb +10 -10
  15. data/lib/dm-core/query.rb +8 -18
  16. data/lib/dm-core/resource.rb +3 -11
  17. data/lib/dm-core/resource/state.rb +13 -16
  18. data/lib/dm-core/resource/state/dirty.rb +11 -1
  19. data/lib/dm-core/resource/state/transient.rb +9 -1
  20. data/lib/dm-core/spec/lib/adapter_helpers.rb +5 -0
  21. data/lib/dm-core/spec/shared/adapter_spec.rb +2 -0
  22. data/lib/dm-core/spec/shared/resource_spec.rb +0 -31
  23. data/lib/dm-core/version.rb +1 -1
  24. data/spec/public/associations/many_to_many/read_multiple_join_spec.rb +2 -0
  25. data/spec/public/associations/many_to_many_spec.rb +2 -1
  26. data/spec/public/associations/many_to_one_spec.rb +1 -0
  27. data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +1 -0
  28. data/spec/public/associations/one_to_many_spec.rb +2 -0
  29. data/spec/public/associations/one_to_one_spec.rb +2 -0
  30. data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +1 -0
  31. data/spec/public/collection_spec.rb +2 -0
  32. data/spec/public/finalize_spec.rb +34 -0
  33. data/spec/public/model/hook_spec.rb +1 -0
  34. data/spec/public/model/property_spec.rb +1 -0
  35. data/spec/public/model/relationship_spec.rb +22 -0
  36. data/spec/public/model_spec.rb +138 -3
  37. data/spec/public/property/discriminator_spec.rb +1 -0
  38. data/spec/public/property/object_spec.rb +1 -0
  39. data/spec/public/property_spec.rb +13 -4
  40. data/spec/public/resource_spec.rb +1 -0
  41. data/spec/public/sel_spec.rb +2 -0
  42. data/spec/public/shared/collection_shared_spec.rb +0 -45
  43. data/spec/public/shared/finder_shared_spec.rb +110 -0
  44. data/spec/public/shared/property_shared_spec.rb +1 -1
  45. data/spec/rcov.opts +1 -1
  46. data/spec/semipublic/associations/many_to_many_spec.rb +3 -0
  47. data/spec/semipublic/associations/many_to_one_spec.rb +2 -0
  48. data/spec/semipublic/associations/one_to_many_spec.rb +2 -0
  49. data/spec/semipublic/associations/one_to_one_spec.rb +2 -0
  50. data/spec/semipublic/associations/relationship_spec.rb +6 -0
  51. data/spec/semipublic/query/conditions/comparison_spec.rb +3 -0
  52. data/spec/semipublic/query/conditions/operation_spec.rb +1 -0
  53. data/spec/semipublic/query/path_spec.rb +2 -0
  54. data/spec/semipublic/query_spec.rb +2 -3
  55. data/spec/semipublic/resource/state/clean_spec.rb +2 -1
  56. data/spec/semipublic/resource/state/deleted_spec.rb +2 -1
  57. data/spec/semipublic/resource/state/dirty_spec.rb +42 -20
  58. data/spec/semipublic/resource/state/immutable_spec.rb +7 -1
  59. data/spec/semipublic/resource/state/transient_spec.rb +69 -40
  60. data/spec/semipublic/resource/state_spec.rb +72 -66
  61. data/spec/semipublic/shared/property_shared_spec.rb +1 -0
  62. data/spec/semipublic/shared/resource_shared_spec.rb +1 -0
  63. data/spec/spec_helper.rb +0 -10
  64. data/tasks/spec.rake +3 -0
  65. metadata +9 -7
data/Gemfile CHANGED
@@ -71,7 +71,7 @@
71
71
  source 'http://rubygems.org'
72
72
 
73
73
  DATAMAPPER = 'git://github.com/datamapper'
74
- DM_VERSION = '~> 1.0.0.rc2'
74
+ DM_VERSION = '~> 1.0.0.rc3'
75
75
 
76
76
  group :runtime do # Runtime dependencies (as in the gemspec)
77
77
 
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 Dan Kubb
1
+ Copyright (c) 2010 Dan Kubb
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.rdoc CHANGED
@@ -201,4 +201,4 @@ nice syntax tweaks DataMapper delivers out of the box...
201
201
 
202
202
  == Copyright
203
203
 
204
- Copyright (c) 2009 Dan Kubb. See LICENSE for details.
204
+ Copyright (c) 2010 Dan Kubb. See LICENSE for details.
data/Rakefile CHANGED
@@ -10,17 +10,16 @@ begin
10
10
  gem.summary = 'An Object/Relational Mapper for Ruby'
11
11
  gem.description = 'Faster, Better, Simpler.'
12
12
  gem.email = 'dan.kubb@gmail.com'
13
- gem.homepage = 'http://github.com/datamapper/dm-core'
13
+ gem.homepage = 'http://github.com/datamapper/%s' % gem.name
14
14
  gem.authors = [ 'Dan Kubb' ]
15
15
 
16
16
  gem.rubyforge_project = 'datamapper'
17
17
 
18
- gem.add_dependency 'extlib', '~> 0.9.14'
19
- gem.add_dependency 'addressable', '~> 2.1'
18
+ gem.add_dependency 'extlib', '~> 0.9.15'
19
+ gem.add_dependency 'addressable', '~> 2.1'
20
20
 
21
21
  gem.add_development_dependency 'rspec', '~> 1.3'
22
22
  gem.add_development_dependency 'jeweler', '~> 1.4'
23
-
24
23
  end
25
24
 
26
25
  Jeweler::GemcutterTasks.new
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0.rc2
1
+ 1.0.0.rc3
data/dm-core.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{dm-core}
8
- s.version = "1.0.0.rc2"
8
+ s.version = "1.0.0.rc3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Dan Kubb"]
12
- s.date = %q{2010-05-19}
12
+ s.date = %q{2010-05-27}
13
13
  s.description = %q{Faster, Better, Simpler.}
14
14
  s.email = %q{dan.kubb@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -126,6 +126,7 @@ Gem::Specification.new do |s|
126
126
  "spec/public/associations/one_to_one_spec.rb",
127
127
  "spec/public/associations/one_to_one_with_boolean_cpk_spec.rb",
128
128
  "spec/public/collection_spec.rb",
129
+ "spec/public/finalize_spec.rb",
129
130
  "spec/public/model/hook_spec.rb",
130
131
  "spec/public/model/property_spec.rb",
131
132
  "spec/public/model/relationship_spec.rb",
@@ -227,6 +228,7 @@ Gem::Specification.new do |s|
227
228
  "spec/public/associations/one_to_one_spec.rb",
228
229
  "spec/public/associations/one_to_one_with_boolean_cpk_spec.rb",
229
230
  "spec/public/collection_spec.rb",
231
+ "spec/public/finalize_spec.rb",
230
232
  "spec/public/model/hook_spec.rb",
231
233
  "spec/public/model/property_spec.rb",
232
234
  "spec/public/model/relationship_spec.rb",
@@ -310,18 +312,18 @@ Gem::Specification.new do |s|
310
312
  s.specification_version = 3
311
313
 
312
314
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
313
- s.add_runtime_dependency(%q<extlib>, ["~> 0.9.14"])
315
+ s.add_runtime_dependency(%q<extlib>, ["~> 0.9.15"])
314
316
  s.add_runtime_dependency(%q<addressable>, ["~> 2.1"])
315
317
  s.add_development_dependency(%q<rspec>, ["~> 1.3"])
316
318
  s.add_development_dependency(%q<jeweler>, ["~> 1.4"])
317
319
  else
318
- s.add_dependency(%q<extlib>, ["~> 0.9.14"])
320
+ s.add_dependency(%q<extlib>, ["~> 0.9.15"])
319
321
  s.add_dependency(%q<addressable>, ["~> 2.1"])
320
322
  s.add_dependency(%q<rspec>, ["~> 1.3"])
321
323
  s.add_dependency(%q<jeweler>, ["~> 1.4"])
322
324
  end
323
325
  else
324
- s.add_dependency(%q<extlib>, ["~> 0.9.14"])
326
+ s.add_dependency(%q<extlib>, ["~> 0.9.15"])
325
327
  s.add_dependency(%q<addressable>, ["~> 2.1"])
326
328
  s.add_dependency(%q<rspec>, ["~> 1.3"])
327
329
  s.add_dependency(%q<jeweler>, ["~> 1.4"])
data/lib/dm-core.rb CHANGED
@@ -285,4 +285,48 @@ module DataMapper
285
285
  current_repository
286
286
  end
287
287
  end
288
+
289
+ # Perform necessary steps to finalize DataMapper for the current repository
290
+ #
291
+ # This method should be called after loading all models and plugins.
292
+ #
293
+ # It ensures foreign key properties and anonymous join models are created.
294
+ # These are otherwise lazily declared, which can lead to unexpected errors.
295
+ # It also performs basic validity checking of the DataMapper models.
296
+ #
297
+ # @return [DataMapper] The DataMapper module
298
+ #
299
+ # @api public
300
+ def self.finalize
301
+ Model.descendants.each do |model|
302
+ finalize_model(model)
303
+ end
304
+ self
305
+ end
306
+
307
+ private
308
+ # @api private
309
+ def self.finalize_model(model)
310
+ name = model.name
311
+ repository_name = model.repository_name
312
+ relationships = model.relationships(repository_name).values
313
+
314
+ if model.properties(repository_name).empty? &&
315
+ !relationships.any? { |relationship| relationship.kind_of?(Associations::ManyToOne::Relationship) }
316
+ raise IncompleteModelError, "#{name} must have at least one property or many to one relationship to be valid"
317
+ end
318
+
319
+ if model.key(repository_name).empty?
320
+ raise IncompleteModelError, "#{name} must have a key to be valid"
321
+ end
322
+
323
+
324
+ # initialize join models and target keys
325
+ relationships.each do |relationship|
326
+ relationship.child_key
327
+ relationship.through if relationship.respond_to?(:through)
328
+ relationship.via if relationship.respond_to?(:via)
329
+ end
330
+
331
+ end
288
332
  end
@@ -147,7 +147,7 @@ module DataMapper
147
147
  #
148
148
  # @api semipublic
149
149
  def adapter_name(const_name)
150
- const_name.to_s.sub('Adapter', '').downcase
150
+ const_name.to_s.chomp('Adapter').downcase
151
151
  end
152
152
 
153
153
  # Require the adapter library
@@ -18,6 +18,22 @@ module DataMapper
18
18
 
19
19
  equalize :name, :options, :resource_naming_convention, :field_naming_convention
20
20
 
21
+ # @api semipublic
22
+ def self.descendants
23
+ @descendants ||= Set.new
24
+ end
25
+
26
+ # @api private
27
+ def self.inherited(subclass)
28
+ add_descendant(subclass)
29
+ end
30
+
31
+ # @api private
32
+ def self.add_descendant(subclass)
33
+ descendants << subclass
34
+ superclass.add_descendant(subclass) if superclass.respond_to?(:add_descendant)
35
+ end
36
+
21
37
  # Adapter name
22
38
  #
23
39
  # @example
@@ -1441,7 +1441,7 @@ module DataMapper
1441
1441
  def method_missing(method, *args, &block)
1442
1442
  relationships = self.relationships
1443
1443
 
1444
- if model.model_method_defined?(method)
1444
+ if model.respond_to?(method)
1445
1445
  delegate_to_model(method, *args, &block)
1446
1446
  elsif relationship = relationships[method] || relationships[method.to_s.singularize.to_sym]
1447
1447
  delegate_to_relationship(relationship, *args)
@@ -1463,7 +1463,7 @@ module DataMapper
1463
1463
  # @api private
1464
1464
  def delegate_to_model(method, *args, &block)
1465
1465
  model = self.model
1466
- model.__send__(:with_scope, query) do
1466
+ model.send(:with_scope, query) do
1467
1467
  model.send(method, *args, &block)
1468
1468
  end
1469
1469
  end
data/lib/dm-core/model.rb CHANGED
@@ -7,6 +7,8 @@ module DataMapper
7
7
  module Model
8
8
  extend Chainable
9
9
 
10
+ include Enumerable
11
+
10
12
  # Creates a new Model class with default_storage_name +storage_name+
11
13
  #
12
14
  # If a block is passed, it will be eval'd in the context of the new Model
@@ -305,17 +307,23 @@ module DataMapper
305
307
  all.at(*args)
306
308
  end
307
309
 
310
+ def fetch(*args, &block)
311
+ all.fetch(*args, &block)
312
+ end
313
+
314
+ def values_at(*args)
315
+ all.values_at(*args)
316
+ end
317
+
308
318
  def reverse
309
319
  all.reverse
310
320
  end
311
321
 
312
- # TODO: spec this
313
- def entries
314
- all.entries
322
+ def each(&block)
323
+ all.each(&block)
324
+ self
315
325
  end
316
326
 
317
- alias to_a entries
318
-
319
327
  # Find a set of records matching an optional set of conditions. Additionally,
320
328
  # specify the order that the records are return.
321
329
  #
@@ -444,22 +452,6 @@ module DataMapper
444
452
  first(conditions) || create(conditions.merge(attributes))
445
453
  end
446
454
 
447
- # Initializes an instance of Resource with the given attributes
448
- #
449
- # @param [Hash(Symbol => Object)] attributes
450
- # hash of attributes to set
451
- #
452
- # @return [Resource]
453
- # the newly initialized Resource instance
454
- #
455
- # @api public
456
- chainable do
457
- def new(*args, &block)
458
- assert_valid
459
- super
460
- end
461
- end
462
-
463
455
  # Create a Resource
464
456
  #
465
457
  # @param [Hash(Symbol => Object)] attributes
@@ -486,6 +478,56 @@ module DataMapper
486
478
  _create(attributes, false)
487
479
  end
488
480
 
481
+ # Update every Resource
482
+ #
483
+ # Person.update(:allow_beer => true)
484
+ #
485
+ # @param [Hash] attributes
486
+ # attributes to update with
487
+ #
488
+ # @return [Boolean]
489
+ # true if the resources were successfully updated
490
+ #
491
+ # @api public
492
+ def update(attributes)
493
+ all.update(attributes)
494
+ end
495
+
496
+ # Update every Resource, bypassing validations
497
+ #
498
+ # Person.update!(:allow_beer => true)
499
+ #
500
+ # @param [Hash] attributes
501
+ # attributes to update with
502
+ #
503
+ # @return [Boolean]
504
+ # true if the resources were successfully updated
505
+ #
506
+ # @api public
507
+ def update!(attributes)
508
+ all.update!(attributes)
509
+ end
510
+
511
+ # Remove all Resources from the repository
512
+ #
513
+ # @return [Boolean]
514
+ # true if the resources were successfully destroyed
515
+ #
516
+ # @api public
517
+ def destroy
518
+ all.destroy
519
+ end
520
+
521
+ # Remove all Resources from the repository, bypassing validation
522
+ #
523
+ # @return [Boolean]
524
+ # true if the resources were successfully destroyed
525
+ #
526
+ # @api public
527
+ def destroy!
528
+ all.destroy!
529
+ end
530
+
489
531
  # Copy a set of records from one repository to another.
490
532
  #
491
533
  # @param [String] source_repository_name
@@ -668,16 +710,6 @@ module DataMapper
668
710
  [ repository ].to_set + @properties.keys.map { |repository_name| DataMapper.repository(repository_name) }
669
711
  end
670
712
 
671
- # @api private
672
- def model_method_defined?(method)
673
- model_methods.include?(method.to_s)
674
- end
675
-
676
- # @api private
677
- def resource_method_defined?(method)
678
- resource_methods.include?(method.to_s)
679
- end
680
-
681
713
  private
682
714
 
683
715
  # @api private
@@ -748,6 +780,7 @@ module DataMapper
748
780
  end
749
781
 
750
782
  # @api private
783
+ # TODO: Remove this once appropriate warnings can be added.
751
784
  def assert_valid(force = false) # :nodoc:
752
785
  return if @valid && !force
753
786
  @valid = true
@@ -774,28 +807,6 @@ module DataMapper
774
807
  end
775
808
  end
776
809
 
777
- # @api private
778
- def model_methods
779
- @model_methods ||= ancestor_instance_methods { |mod| mod.singleton_class }
780
- end
781
-
782
- # @api private
783
- def resource_methods
784
- @resource_methods ||= ancestor_instance_methods { |mod| mod }
785
- end
786
-
787
- # @api private
788
- def ancestor_instance_methods
789
- methods = Set.new
790
-
791
- ancestors.each do |mod|
792
- next unless mod <= DataMapper::Resource
793
- methods.merge(yield(mod).instance_methods(false).map { |method| method.to_s })
794
- end
795
-
796
- methods
797
- end
798
-
799
810
  # Raises an exception if #get receives the wrong number of arguments
800
811
  #
801
812
  # @param [Array] key
@@ -222,9 +222,8 @@ module DataMapper
222
222
  name = property.name.to_s
223
223
  reader_visibility = property.reader_visibility
224
224
  instance_variable_name = property.instance_variable_name
225
- primitive = property.primitive
226
225
 
227
- unless resource_method_defined?(name)
226
+ unless reserved_method?(name)
228
227
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
229
228
  chainable do
230
229
  #{reader_visibility}
@@ -239,10 +238,14 @@ module DataMapper
239
238
 
240
239
  boolean_reader_name = "#{name}?"
241
240
 
242
- if primitive == TrueClass && !resource_method_defined?(boolean_reader_name)
241
+ if property.kind_of?(DataMapper::Property::Boolean) && !reserved_method?(boolean_reader_name)
243
242
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
244
- #{reader_visibility}
245
- alias #{boolean_reader_name} #{name}
243
+ chainable do
244
+ #{reader_visibility}
245
+ def #{boolean_reader_name}
246
+ #{name}
247
+ end
248
+ end
246
249
  RUBY
247
250
  end
248
251
  end
@@ -256,7 +259,7 @@ module DataMapper
256
259
 
257
260
  writer_name = "#{name}="
258
261
 
259
- return if resource_method_defined?(writer_name)
262
+ return if reserved_method?(writer_name)
260
263
 
261
264
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
262
265
  chainable do
@@ -270,6 +273,11 @@ module DataMapper
270
273
  RUBY
271
274
  end
272
275
 
276
+ # @api private
277
+ def reserved_method?(name)
278
+ method_defined?(name) && !%w[ id type ].include?(name)
279
+ end
280
+
273
281
  chainable do
274
282
  # @api public
275
283
  def method_missing(method, *args, &block)
@@ -81,17 +81,13 @@ module DataMapper
81
81
  # cardinality that defines the association type and constraints
82
82
  # @param name [Symbol]
83
83
  # the name that the association will be referenced by
84
- # @param model [Model, #to_str]
85
- # the target model of the relationship
86
- # @param opts [Hash]
87
- # an options hash
84
+ # @param *args [Model, Hash] model and/or options hash
88
85
  #
89
- # @option :through[Symbol] A association that this join should go through to form
86
+ # @option *args :through[Symbol] A association that this join should go through to form
90
87
  # a many-to-many association
91
- # @option :model[Model, String] The name of the class to associate with, if omitted
88
+ # @option *args :model[Model, String] The name of the class to associate with, if omitted
92
89
  # then the association name is assumed to match the class name
93
- # @option :repository[Symbol]
94
- # name of child model repository
90
+ # @option *args :repository[Symbol] name of child model repository
95
91
  #
96
92
  # @return [Association::Relationship] the relationship that was
97
93
  # created to reflect either a one-to-one, one-to-many or many-to-many
@@ -148,15 +144,11 @@ module DataMapper
148
144
  #
149
145
  # @param name [Symbol]
150
146
  # the name that the association will be referenced by
151
- # @param model [Model, #to_str]
152
- # the target model of the relationship
153
- # @param opts [Hash]
154
- # an options hash
147
+ # @param *args [Model, Hash] model and/or options hash
155
148
  #
156
- # @option :model[Model, String] The name of the class to associate with, if omitted
149
+ # @option *args :model[Model, String] The name of the class to associate with, if omitted
157
150
  # then the association name is assumed to match the class name
158
- # @option :repository[Symbol]
159
- # name of child model repository
151
+ # @option *args :repository[Symbol] name of child model repository
160
152
  #
161
153
  # @return [Association::Relationship] The association created
162
154
  # should not be accessed directly
@@ -311,7 +303,7 @@ module DataMapper
311
303
  # :target_key (will mean something different for each relationship)
312
304
 
313
305
  [ :child_key, :parent_key ].each do |key|
314
- if options.key?(key) && !options[key].is_a?(Enumerable)
306
+ if options.key?(key)
315
307
  options[key] = Array(options[key])
316
308
  end
317
309
  end
@@ -328,7 +320,7 @@ module DataMapper
328
320
  name = relationship.name
329
321
  reader_name = name.to_s
330
322
 
331
- return if resource_method_defined?(reader_name)
323
+ return if method_defined?(reader_name)
332
324
 
333
325
  reader_visibility = relationship.reader_visibility
334
326
 
@@ -355,7 +347,7 @@ module DataMapper
355
347
  name = relationship.name
356
348
  writer_name = "#{name}="
357
349
 
358
- return if resource_method_defined?(writer_name)
350
+ return if method_defined?(writer_name)
359
351
 
360
352
  writer_visibility = relationship.writer_visibility
361
353