dm-core 0.9.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/.autotest +26 -0
  2. data/{CHANGELOG → History.txt} +78 -77
  3. data/Manifest.txt +123 -0
  4. data/{README → README.txt} +0 -0
  5. data/Rakefile +29 -0
  6. data/SPECS +63 -0
  7. data/TODO +1 -0
  8. data/lib/dm-core.rb +6 -1
  9. data/lib/dm-core/adapters/data_objects_adapter.rb +29 -32
  10. data/lib/dm-core/adapters/mysql_adapter.rb +1 -1
  11. data/lib/dm-core/adapters/postgres_adapter.rb +1 -1
  12. data/lib/dm-core/adapters/sqlite3_adapter.rb +2 -2
  13. data/lib/dm-core/associations.rb +26 -0
  14. data/lib/dm-core/associations/many_to_many.rb +34 -25
  15. data/lib/dm-core/associations/many_to_one.rb +4 -4
  16. data/lib/dm-core/associations/one_to_many.rb +48 -13
  17. data/lib/dm-core/associations/one_to_one.rb +4 -4
  18. data/lib/dm-core/associations/relationship.rb +144 -42
  19. data/lib/dm-core/associations/relationship_chain.rb +31 -24
  20. data/lib/dm-core/auto_migrations.rb +0 -4
  21. data/lib/dm-core/collection.rb +40 -7
  22. data/lib/dm-core/dependency_queue.rb +31 -0
  23. data/lib/dm-core/hook.rb +2 -2
  24. data/lib/dm-core/is.rb +2 -2
  25. data/lib/dm-core/logger.rb +10 -10
  26. data/lib/dm-core/model.rb +94 -41
  27. data/lib/dm-core/property.rb +72 -41
  28. data/lib/dm-core/property_set.rb +8 -14
  29. data/lib/dm-core/query.rb +34 -9
  30. data/lib/dm-core/repository.rb +0 -0
  31. data/lib/dm-core/resource.rb +13 -13
  32. data/lib/dm-core/scope.rb +25 -2
  33. data/lib/dm-core/type.rb +3 -3
  34. data/lib/dm-core/types/discriminator.rb +10 -8
  35. data/lib/dm-core/types/object.rb +4 -0
  36. data/lib/dm-core/types/paranoid_boolean.rb +15 -4
  37. data/lib/dm-core/types/paranoid_datetime.rb +15 -4
  38. data/lib/dm-core/version.rb +3 -0
  39. data/script/all +5 -0
  40. data/script/performance.rb +191 -0
  41. data/script/profile.rb +86 -0
  42. data/spec/integration/association_spec.rb +288 -204
  43. data/spec/integration/association_through_spec.rb +9 -3
  44. data/spec/integration/associations/many_to_many_spec.rb +97 -31
  45. data/spec/integration/associations/many_to_one_spec.rb +41 -6
  46. data/spec/integration/associations/one_to_many_spec.rb +18 -2
  47. data/spec/integration/auto_migrations_spec.rb +0 -0
  48. data/spec/integration/collection_spec.rb +89 -42
  49. data/spec/integration/dependency_queue_spec.rb +58 -0
  50. data/spec/integration/model_spec.rb +67 -8
  51. data/spec/integration/postgres_adapter_spec.rb +19 -20
  52. data/spec/integration/property_spec.rb +17 -8
  53. data/spec/integration/query_spec.rb +273 -191
  54. data/spec/integration/resource_spec.rb +108 -10
  55. data/spec/integration/strategic_eager_loading_spec.rb +138 -0
  56. data/spec/integration/transaction_spec.rb +3 -3
  57. data/spec/integration/type_spec.rb +121 -0
  58. data/spec/lib/logging_helper.rb +18 -0
  59. data/spec/lib/model_loader.rb +91 -0
  60. data/spec/lib/publicize_methods.rb +28 -0
  61. data/spec/models/vehicles.rb +34 -0
  62. data/spec/models/zoo.rb +48 -0
  63. data/spec/spec.opts +3 -0
  64. data/spec/spec_helper.rb +25 -62
  65. data/spec/unit/adapters/data_objects_adapter_spec.rb +1 -0
  66. data/spec/unit/associations/many_to_many_spec.rb +3 -0
  67. data/spec/unit/associations/many_to_one_spec.rb +9 -1
  68. data/spec/unit/associations/one_to_many_spec.rb +12 -4
  69. data/spec/unit/associations/relationship_spec.rb +19 -15
  70. data/spec/unit/associations_spec.rb +37 -0
  71. data/spec/unit/collection_spec.rb +8 -0
  72. data/spec/unit/data_mapper_spec.rb +14 -0
  73. data/spec/unit/model_spec.rb +2 -2
  74. data/spec/unit/property_set_spec.rb +0 -13
  75. data/spec/unit/property_spec.rb +92 -21
  76. data/spec/unit/query_spec.rb +49 -4
  77. data/spec/unit/resource_spec.rb +122 -60
  78. data/spec/unit/scope_spec.rb +11 -0
  79. data/tasks/ci.rb +68 -0
  80. data/tasks/dm.rb +63 -0
  81. data/tasks/doc.rb +20 -0
  82. data/tasks/hoe.rb +38 -0
  83. data/tasks/install.rb +20 -0
  84. metadata +63 -22
@@ -1,6 +1,9 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
2
 
3
3
  describe DataMapper::Associations::Relationship do
4
+
5
+ load_models_for_metaphor :vehicles
6
+
4
7
  it "should describe an association" do
5
8
  belongs_to = DataMapper::Associations::Relationship.new(
6
9
  :manufacturer,
@@ -11,50 +14,51 @@ describe DataMapper::Associations::Relationship do
11
14
  )
12
15
 
13
16
  belongs_to.should respond_to(:name)
14
- belongs_to.should respond_to(:repository_name)
17
+ belongs_to.should respond_to(:with_repository)
15
18
  belongs_to.should respond_to(:child_key)
16
19
  belongs_to.should respond_to(:parent_key)
17
20
  end
18
21
 
19
22
  it "should map properties explicitly when an association method passes them in its options" do
20
- repository_name = :mock
21
-
22
23
  belongs_to = DataMapper::Associations::Relationship.new(
23
24
  :manufacturer,
24
- repository_name,
25
+ :mock,
25
26
  'Vehicle',
26
27
  'Manufacturer',
27
28
  { :child_key => [ :manufacturer_id ], :parent_key => [ :id ] }
28
29
  )
29
30
 
30
31
  belongs_to.name.should == :manufacturer
31
- belongs_to.repository_name.should == repository_name
32
+ belongs_to.with_repository do |r|
33
+ r.name.should == :mock
34
+ end
32
35
 
33
36
  belongs_to.child_key.should be_a_kind_of(DataMapper::PropertySet)
34
37
  belongs_to.parent_key.should be_a_kind_of(DataMapper::PropertySet)
35
38
 
36
- belongs_to.child_key.to_a.should == Vehicle.properties(repository_name).slice(:manufacturer_id)
37
- belongs_to.parent_key.to_a.should == Manufacturer.properties(repository_name).key
39
+ belongs_to.child_key.to_a.should == Vehicle.properties(:mock).slice(:manufacturer_id)
40
+ belongs_to.parent_key.to_a.should == Manufacturer.properties(:mock).key
38
41
  end
39
42
 
40
43
  it "should infer properties when options aren't passed" do
41
- repository_name = :mock
42
-
43
44
  has_many = DataMapper::Associations::Relationship.new(
44
45
  :models,
45
- repository_name,
46
+ :mock,
46
47
  'Vehicle',
47
- 'Manufacturer'
48
+ 'Manufacturer',
49
+ { :child_key => [:model_id] }
48
50
  )
49
51
 
50
52
  has_many.name.should == :models
51
- has_many.repository_name.should == repository_name
53
+ has_many.with_repository do |r|
54
+ r.name.should == :mock
55
+ end
52
56
 
53
57
  has_many.child_key.should be_a_kind_of(DataMapper::PropertySet)
54
58
  has_many.parent_key.should be_a_kind_of(DataMapper::PropertySet)
55
-
56
- has_many.child_key.to_a.should == Vehicle.properties(repository_name).slice(:models_id)
57
- has_many.parent_key.to_a.should == Manufacturer.properties(repository_name).key
59
+ # Vehicle.has n, :models, :class_name => 'Manufacturer', :child_key => "models_id"
60
+ has_many.child_key.to_a.should == Vehicle.properties(:mock).slice(:model_id)
61
+ has_many.parent_key.to_a.should == Manufacturer.properties(:mock).key
58
62
  end
59
63
 
60
64
  it "should generate child properties with a safe subset of the parent options" do
@@ -1,6 +1,9 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
2
 
3
3
  describe "DataMapper::Associations" do
4
+
5
+ load_models_for_metaphor :vehicles
6
+
4
7
  before do
5
8
  @relationship = mock(DataMapper::Associations::Relationship)
6
9
  @n = 1.0/0
@@ -8,6 +11,40 @@ describe "DataMapper::Associations" do
8
11
  Manufacturer.mock_relationship = Vehicle.mock_relationship = @relationship
9
12
  end
10
13
 
14
+ describe "#many_to_one_relationships" do
15
+ before :all do
16
+ module MTORelationships
17
+ class A
18
+ include DataMapper::Resource
19
+ def self.default_repository_name
20
+ :a_db
21
+ end
22
+ repository(:b_db) do
23
+ belongs_to :b
24
+ end
25
+ repository(:c_db) do
26
+ belongs_to :c
27
+ end
28
+ end
29
+ class B
30
+ include DataMapper::Resource
31
+ def self.default_repository_name
32
+ :b_db
33
+ end
34
+ end
35
+ class C
36
+ include DataMapper::Resource
37
+ def self.default_repository_name
38
+ :c_db
39
+ end
40
+ end
41
+ end
42
+ end
43
+ it "should list all relationships that are one-to-many" do
44
+ MTORelationships::A.many_to_one_relationships.sort_by { |r| r.name.to_s }.should == [MTORelationships::A.relationships(:b_db)[:b], MTORelationships::A.relationships(:c_db)[:c]]
45
+ end
46
+ end
47
+
11
48
  describe ".relationships" do
12
49
  class B
13
50
  include DataMapper::Resource
@@ -24,6 +24,10 @@ describe DataMapper::Collection do
24
24
  @collection.should respond_to(:at)
25
25
  end
26
26
 
27
+ it 'should provide #build' do
28
+ @collection.should respond_to(:build)
29
+ end
30
+
27
31
  it 'should provide #clear' do
28
32
  @collection.should respond_to(:clear)
29
33
  end
@@ -72,6 +76,10 @@ describe DataMapper::Collection do
72
76
  @collection.should respond_to(:first)
73
77
  end
74
78
 
79
+ it 'should provide #freeze' do
80
+ @collection.should respond_to(:freeze)
81
+ end
82
+
75
83
  it 'should provide #get' do
76
84
  @collection.should respond_to(:get)
77
85
  end
@@ -1,6 +1,20 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
2
 
3
3
  describe DataMapper do
4
+ describe ".dependency_queue" do
5
+ before(:all) do
6
+ @q = DataMapper.dependency_queue
7
+ end
8
+
9
+ it "should return a dependency queue" do
10
+ @q.should be_a_kind_of(DataMapper::DependencyQueue)
11
+ end
12
+
13
+ it "should only create one dependency queue" do
14
+ @q.should == DataMapper.dependency_queue
15
+ end
16
+ end
17
+
4
18
  describe ".prepare" do
5
19
  it "should pass the default repository to the block if no argument is given" do
6
20
  DataMapper.should_receive(:repository).with(no_args).and_return :default_repo
@@ -205,11 +205,11 @@ describe 'DataMapper::Model' do
205
205
  @standard_resource_inclusions = DataMapper::Resource.instance_variable_get('@extra_inclusions')
206
206
  @standard_model_extensions = DataMapper::Model.instance_variable_get('@extra_extensions')
207
207
  end
208
-
208
+
209
209
  before(:each) do
210
210
  DataMapper::Resource.instance_variable_set('@extra_inclusions', [])
211
211
  DataMapper::Model.instance_variable_set('@extra_extensions', [])
212
-
212
+
213
213
  @module = Module.new do
214
214
  def greet
215
215
  hi_mom!
@@ -79,18 +79,5 @@ describe DataMapper::PropertySet do
79
79
  @properties.instance_variable_get("@property_for").should_not be_empty
80
80
  @properties.dup.instance_variable_get("@property_for").should be_empty
81
81
  end
82
-
83
- it 'should be able to retarget a new model' do
84
- copy = Icon.properties.dup(Boat)
85
- copy.should have(4).entries
86
-
87
- copy.each do |property|
88
- property.model.should == Boat
89
- end
90
-
91
- copy << DataMapper::Property.new(Icon, :z_index, Integer, {})
92
- copy.should have(5).entries
93
- Icon.properties.should have(4).entries
94
- end
95
82
  end
96
83
  end
@@ -52,6 +52,25 @@ describe DataMapper::Property do
52
52
  end
53
53
  end
54
54
 
55
+ it 'should provide #field' do
56
+ @property.should respond_to(:field)
57
+ end
58
+
59
+ describe '#field' do
60
+ it 'should accept a custom field' do
61
+ property = DataMapper::Property.new(Zoo, :location, String, :field => 'City')
62
+ property.field.should == 'City'
63
+ end
64
+
65
+ it 'should use repository name if passed in' do
66
+ @property.field(:default).should == 'name'
67
+ end
68
+
69
+ it 'should return default field if no repository name passed in' do
70
+ @property.field.should == 'name'
71
+ end
72
+ end
73
+
55
74
  it 'should provide #get' do
56
75
  @property.should respond_to(:get)
57
76
  end
@@ -105,7 +124,7 @@ describe DataMapper::Property do
105
124
  describe '#set' do
106
125
  before do
107
126
  @original_values = {}
108
- @resource = mock('resource', :kind_of? => true, :original_values => @original_values)
127
+ @resource = mock('resource', :kind_of? => true, :original_values => @original_values, :new_record? => true)
109
128
  end
110
129
 
111
130
  it 'should typecast the value' do
@@ -199,18 +218,56 @@ describe DataMapper::Property do
199
218
  property.default_for(resource).should == 'Tomato'
200
219
  end
201
220
 
202
- it "should determine visibility of readers and writers" do
203
- name = DataMapper::Property.new(Tomato,:botanical_name,String,{})
204
- name.reader_visibility.should == :public
205
- name.writer_visibility.should == :public
206
-
207
- seeds = DataMapper::Property.new(Tomato,:seeds,TrueClass,{:accessor=>:private})
208
- seeds.reader_visibility.should == :private
209
- seeds.writer_visibility.should == :private
221
+ describe "reader and writer visibility" do
222
+ # parameter passed to Property.new # reader | writer visibility
223
+ {
224
+ {} => [:public, :public],
225
+ { :accessor => :public } => [:public, :public],
226
+ { :accessor => :protected } => [:protected, :protected],
227
+ { :accessor => :private } => [:private, :private],
228
+ { :reader => :public } => [:public, :public],
229
+ { :reader => :protected } => [:protected, :public],
230
+ { :reader => :private } => [:private, :public],
231
+ { :writer => :public } => [:public, :public],
232
+ { :writer => :protected } => [:public, :protected],
233
+ { :writer => :private } => [:public, :private],
234
+ { :reader => :public, :writer => :public } => [:public, :public],
235
+ { :reader => :public, :writer => :protected } => [:public, :protected],
236
+ { :reader => :public, :writer => :private } => [:public, :private],
237
+ { :reader => :protected, :writer => :public } => [:protected, :public],
238
+ { :reader => :protected, :writer => :protected } => [:protected, :protected],
239
+ { :reader => :protected, :writer => :private } => [:protected, :private],
240
+ { :reader => :private, :writer => :public } => [:private, :public],
241
+ { :reader => :private, :writer => :protected } => [:private, :protected],
242
+ { :reader => :private, :writer => :private } => [:private, :private],
243
+ }.each do |input, output|
244
+ it "#{input.inspect} should make reader #{output[0]} and writer #{output[1]}" do
245
+ property = DataMapper::Property.new(Tomato, :botanical_name, String, input)
246
+ property.reader_visibility.should == output[0]
247
+ property.writer_visibility.should == output[1]
248
+ end
249
+ end
210
250
 
211
- family = DataMapper::Property.new(Tomato,:family,String,{:reader => :public, :writer => :private })
212
- family.reader_visibility.should == :public
213
- family.writer_visibility.should == :private
251
+ [
252
+ { :accessor => :junk },
253
+ { :reader => :junk },
254
+ { :writer => :junk },
255
+ { :reader => :public, :writer => :junk },
256
+ { :reader => :protected, :writer => :junk },
257
+ { :reader => :private, :writer => :junk },
258
+ { :reader => :junk, :writer => :public },
259
+ { :reader => :junk, :writer => :protected },
260
+ { :reader => :junk, :writer => :private },
261
+ { :reader => :junk, :writer => :junk },
262
+ { :reader => :junk, :writer => :junk },
263
+ { :reader => :junk, :writer => :junk },
264
+ ].each do |input|
265
+ it "#{input.inspect} should raise ArgumentError" do
266
+ lambda {
267
+ property = DataMapper::Property.new(Tomato, :family, String, input)
268
+ }.should raise_error(ArgumentError)
269
+ end
270
+ end
214
271
  end
215
272
 
216
273
  it "should return an instance variable name" do
@@ -263,8 +320,8 @@ describe DataMapper::Property do
263
320
  end
264
321
 
265
322
  it "should set the field to the correct field_naming_convention" do
266
- DataMapper::Property.new(Zoo, :species, String, {}).field.should == 'species'
267
- DataMapper::Property.new(Tomato, :genetic_history, DataMapper::Types::Text, {}).field.should == "genetic_history"
323
+ DataMapper::Property.new(Zoo, :species, String, {}).field(:default).should == 'species'
324
+ DataMapper::Property.new(Tomato, :genetic_history, DataMapper::Types::Text, {}).field(:default).should == "genetic_history"
268
325
  end
269
326
 
270
327
  it "should provide the primitive mapping" do
@@ -340,6 +397,13 @@ describe DataMapper::Property do
340
397
  end
341
398
  end
342
399
 
400
+ { '-8' => -8, '-8.0' => -8, -8 => -8, -8.0 => -8, BigDecimal('8.0') => 8 }.each do |value,expected|
401
+ it "should typecast #{format(value)} to #{format(expected)} for an Integer property" do
402
+ property = DataMapper::Property.new(Zoo, :integer, Integer)
403
+ property.typecast(value).should == expected
404
+ end
405
+ end
406
+
343
407
  { '0' => 0, '0.0' => 0, 0 => 0, 0.0 => 0, BigDecimal('0.0') => 0 }.each do |value,expected|
344
408
  it "should typecast #{format(value)} to #{format(expected)} for an Integer property" do
345
409
  property = DataMapper::Property.new(Zoo, :integer, Integer)
@@ -347,6 +411,20 @@ describe DataMapper::Property do
347
411
  end
348
412
  end
349
413
 
414
+ { '5' => 5, '5.0' => 5, 5 => 5, 5.0 => 5, BigDecimal('5.0') => 5 }.each do |value,expected|
415
+ it "should typecast #{format(value)} to #{format(expected)} for an Integer property" do
416
+ property = DataMapper::Property.new(Zoo, :integer, Integer)
417
+ property.typecast(value).should == expected
418
+ end
419
+ end
420
+
421
+ { 'none' => nil, 'almost 5' => nil, '-3 change' => -3, '9 items' => 9 }.each do |value,expected|
422
+ it "should typecast #{format(value)} to #{format(expected)} for an Integer property" do
423
+ property = DataMapper::Property.new(Zoo, :integer, Integer)
424
+ property.typecast(value).should == expected
425
+ end
426
+ end
427
+
350
428
  { '0' => BigDecimal('0'), '0.0' => BigDecimal('0.0'), 0.0 => BigDecimal('0.0'), BigDecimal('0.0') => BigDecimal('0.0') }.each do |value,expected|
351
429
  it "should typecast #{format(value)} to #{format(expected)} for a BigDecimal property" do
352
430
  property = DataMapper::Property.new(Zoo, :big_decimal, BigDecimal)
@@ -434,13 +512,6 @@ describe DataMapper::Property do
434
512
  DataMapper::Property.new(Zoo, :name, String).should respond_to(:inspect)
435
513
  end
436
514
 
437
- it "should use the Repository of its @model" do
438
- p = DataMapper::Property.new(Zoo, :name, String)
439
- repo = mock("repository")
440
- Zoo.should_receive(:repository).and_return(repo)
441
- p.repository.should == repo
442
- end
443
-
444
515
  it 'should return an abbreviated representation of the property when inspected' do
445
516
  DataMapper::Property.new(Zoo, :name, String).inspect.should == '#<Property:Zoo:name>'
446
517
  end
@@ -17,8 +17,8 @@ BAD_OPTIONS = {
17
17
  :reload => 'true',
18
18
  :offset => -1,
19
19
  :limit => 0,
20
- :order => [],
21
- :fields => [],
20
+ # :order => [], # TODO: spec conditions where :order may be empty
21
+ # :fields => [], # TODO: spec conditions where :fields may be empty
22
22
  :links => [],
23
23
  :includes => [],
24
24
  :conditions => [],
@@ -110,7 +110,7 @@ describe DataMapper::Query do
110
110
  query.conditions.should == [ [ :eql, Article.properties[:author], 'dkubb' ] ]
111
111
  end
112
112
 
113
- it 'when a Symbol::Operator object is a key' do
113
+ it 'when a Query::Operator object is a key' do
114
114
  query = DataMapper::Query.new(@repository, Article, :author.like => /\Ad(?:an\.)kubb\z/)
115
115
  query.conditions.should == [ [ :like, Article.properties[:author], /\Ad(?:an\.)kubb\z/ ] ]
116
116
  end
@@ -161,7 +161,7 @@ describe DataMapper::Query do
161
161
  end
162
162
  end
163
163
 
164
- it 'when unknown options use something that is not a Symbol::Operator, Symbol or String is a key' do
164
+ it 'when unknown options use something that is not a Query::Operator, Symbol or String is a key' do
165
165
  lambda {
166
166
  DataMapper::Query.new(@repository, Article, nil => nil)
167
167
  }.should raise_error(ArgumentError)
@@ -483,3 +483,48 @@ describe DataMapper::Query do
483
483
  end
484
484
  end
485
485
  end
486
+
487
+ describe DataMapper::Query::Operator do
488
+ before do
489
+ @operator = :thing.gte
490
+ end
491
+
492
+ it 'should provide #==' do
493
+ @operator.should respond_to(:==)
494
+ end
495
+
496
+ describe '#==' do
497
+ describe 'should be equal' do
498
+ it 'when other is same object' do
499
+ @operator.should == @operator
500
+ end
501
+
502
+ it 'when other has the same target and operator' do
503
+ other = :thing.gte
504
+ @operator.target.should == other.target
505
+ @operator.operator.should == other.operator
506
+ @operator.should == other
507
+ end
508
+ end
509
+
510
+ describe 'should be different' do
511
+ it 'when other class is not a descendant of self.class' do
512
+ other = :thing
513
+ other.class.should_not be_kind_of(@operator.class)
514
+ @operator.should_not == other
515
+ end
516
+
517
+ it 'when other has a different target' do
518
+ other = :other.gte
519
+ @operator.target.should_not == other.target
520
+ @operator.should_not == other
521
+ end
522
+
523
+ it 'when other has a different operator' do
524
+ other = :thing.gt
525
+ @operator.operator.should_not == other.operator
526
+ @operator.should_not == other
527
+ end
528
+ end
529
+ end
530
+ end
@@ -1,5 +1,52 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
2
 
3
+ # rSpec completely FUBARs everything if you give it a Module here.
4
+ # So we give it a String of the module name instead.
5
+ # DO NOT CHANGE THIS!
6
+ describe "DataMapper::Resource" do
7
+
8
+ load_models_for_metaphor :zoo
9
+
10
+ describe '#attributes' do
11
+ it 'should return a hash of attribute-names and values' do
12
+ zoo = Zoo.new
13
+ zoo.name = "San Francisco"
14
+ zoo.description = "This is a pretty awesome zoo"
15
+ zoo.attributes.should == {
16
+ :name => "San Francisco", :description => "This is a pretty awesome zoo",
17
+ :id => nil, :inception => nil, :open => false, :size => nil
18
+ }
19
+ end
20
+
21
+ it "should return a hash with all nil values if the instance is new and has no default values" do
22
+ Species.new.attributes.should == { :id => nil, :name => nil }
23
+ end
24
+
25
+ it 'should not include private attributes' do
26
+ Species.new.attributes.should == { :id => nil, :name => nil }
27
+ end
28
+ end
29
+
30
+ # ---------- REPOSITORY WRITE METHODS ---------------
31
+
32
+ describe '#save' do
33
+
34
+ describe 'with a new resource' do
35
+ it 'should set defaults before create'
36
+ it 'should create when dirty'
37
+ it 'should create when non-dirty, and it has a serial key'
38
+ end
39
+
40
+ describe 'with an existing resource' do
41
+ it 'should update'
42
+ end
43
+
44
+ end
45
+ end
46
+
47
+
48
+ # ---------- Old specs... BOOOOOOOOOO ---------------
49
+
3
50
  class Planet
4
51
  include DataMapper::Resource
5
52
 
@@ -8,7 +55,7 @@ class Planet
8
55
  property :id, Integer, :key => true
9
56
  property :name, String
10
57
  property :age, Integer
11
- property :core, String, :private => true
58
+ property :core, String, :accessor => :private
12
59
  property :type, Discriminator
13
60
  property :data, Object
14
61
 
@@ -25,9 +72,6 @@ class Planet
25
72
  end
26
73
  end
27
74
 
28
- class Moon
29
- end
30
-
31
75
  class BlackHole
32
76
  include DataMapper::Resource
33
77
 
@@ -74,54 +118,16 @@ class Banana < Fruit
74
118
  property :type, Discriminator
75
119
  end
76
120
 
121
+ class Cyclist
122
+ include DataMapper::Resource
123
+ property :id, Integer, :serial => true
124
+ property :victories, Integer
125
+ end
126
+
77
127
  # rSpec completely FUBARs everything if you give it a Module here.
78
128
  # So we give it a String of the module name instead.
79
129
  # DO NOT CHANGE THIS!
80
130
  describe "DataMapper::Resource" do
81
- it 'should provide #attribute_get' do
82
- Planet.new.should respond_to(:attribute_get)
83
- end
84
-
85
- describe '#attribute_get' do
86
- it 'should delegate to Property#get' do
87
- planet = Planet.new
88
- Planet.properties[:age].should_receive(:get).with(planet).and_return(1)
89
- planet.age.should == 1
90
- end
91
- end
92
-
93
- it 'should provide #attribute_set' do
94
- Planet.new.should respond_to(:attribute_set)
95
- end
96
-
97
- describe '#attribute_set' do
98
- it 'should typecast the value' do
99
- Planet.properties[:age].should_receive(:typecast).with('1').and_return(1)
100
- planet = Planet.new
101
- planet.age = '1'
102
- planet.age.should == 1
103
- end
104
-
105
- it 'should delegate to Property#set' do
106
- planet = Planet.new
107
- Planet.properties[:age].should_receive(:set).with(planet, 1).and_return(1)
108
- planet.age = 1
109
- end
110
- end
111
-
112
- describe '#attributes' do
113
- it 'should return a hash of attribute-names and values' do
114
- vegetable = Vegetable.new
115
- vegetable.attributes.should == {:name => nil, :id => nil}
116
- vegetable.name = "carot"
117
- vegetable.attributes.should == {:name => "carot", :id => nil}
118
- end
119
-
120
- it 'should not include private attributes' do
121
- hole = BlackHole.new
122
- hole.attributes.should == {:id => nil}
123
- end
124
- end
125
131
 
126
132
  it 'should provide #save' do
127
133
  Planet.new.should respond_to(:save)
@@ -181,6 +187,46 @@ describe "DataMapper::Resource" do
181
187
 
182
188
  resource.save.should be_false
183
189
  end
190
+
191
+ describe 'for integer fields' do
192
+
193
+ it "should save strings without digits as nil" do
194
+ resource = Cyclist.new
195
+ resource.victories = "none"
196
+ resource.save.should be_true
197
+ resource.victories.should be_nil
198
+ end
199
+
200
+ it "should save strings beginning with non-digits as nil" do
201
+ resource = Cyclist.new
202
+ resource.victories = "almost 5"
203
+ resource.save.should be_true
204
+ resource.victories.should be_nil
205
+ end
206
+
207
+ it 'should save strings beginning with negative numbers as that number' do
208
+ resource = Cyclist.new
209
+ resource.victories = "-4 victories"
210
+ resource.save.should be_true
211
+ resource.victories.should == -4
212
+ end
213
+
214
+ it 'should save strings beginning with 0 as 0' do
215
+ resource = Cyclist.new
216
+ resource.victories = "0 victories"
217
+ resource.save.should be_true
218
+ resource.victories.should == 0
219
+ end
220
+
221
+ it 'should save strings beginning with positive numbers as that number' do
222
+ resource = Cyclist.new
223
+ resource.victories = "23 victories"
224
+ resource.save.should be_true
225
+ resource.victories.should == 23
226
+ end
227
+
228
+ end
229
+
184
230
  end
185
231
 
186
232
  describe 'with an existing resource' do
@@ -245,7 +291,7 @@ describe "DataMapper::Resource" do
245
291
  end
246
292
 
247
293
  it "should return an instance of the created object" do
248
- Planet.create!(:name => 'Venus', :age => 1_000_000, :core => nil, :id => 42).should be_a_kind_of(Planet)
294
+ Planet.create(:name => 'Venus', :age => 1_000_000, :id => 42).should be_a_kind_of(Planet)
249
295
  end
250
296
 
251
297
  it 'should provide persistance methods' do
@@ -256,31 +302,47 @@ describe "DataMapper::Resource" do
256
302
  end
257
303
 
258
304
  it "should have attributes" do
259
- attributes = { :name => 'Jupiter', :age => 1_000_000, :core => nil, :id => 42, :type => Planet, :data => nil }
305
+ attributes = { :name => 'Jupiter', :age => 1_000_000, :id => 42, :type => Planet, :data => nil }
260
306
  jupiter = Planet.new(attributes)
261
307
  jupiter.attributes.should == attributes
262
308
  end
263
309
 
264
310
  it "should be able to set attributes" do
265
- attributes = { :name => 'Jupiter', :age => 1_000_000, :core => nil, :id => 42, :type => Planet, :data => nil }
311
+ attributes = { :name => 'Jupiter', :age => 1_000_000, :id => 42, :type => Planet, :data => nil }
266
312
  jupiter = Planet.new(attributes)
267
313
  jupiter.attributes.should == attributes
268
- jupiter.attributes = attributes.merge(:core => 'Magma')
314
+
315
+ new_attributes = attributes.merge( :age => 2_500_000 )
316
+ jupiter.attributes = new_attributes
317
+ jupiter.attributes.should == new_attributes
318
+ end
319
+
320
+ it "should be able to set attributes using update_attributes" do
321
+ attributes = { :name => 'Jupiter', :age => 1_000_000, :id => 42, :type => Planet, :data => nil }
322
+ jupiter = Planet.new(attributes)
269
323
  jupiter.attributes.should == attributes
270
324
 
271
- jupiter.update_attributes({ :core => "Toast", :type => "Bob" }, :core).should be_true
272
- jupiter.core.should == "Toast"
273
- jupiter.type.should_not == "Bob"
325
+ new_age = { :age => 3_700_000 }
326
+ jupiter.update_attributes(new_age).should be_true
327
+ jupiter.age.should == 3_700_000
328
+ jupiter.attributes.should == attributes.merge(new_age)
329
+ end
330
+
331
+ # :core is a private accessor so Ruby should raise NameError
332
+ it "should not be able to set private attributes" do
333
+ lambda {
334
+ jupiter = Planet.new({ :core => "Molten Metal" })
335
+ }.should raise_error(NameError)
274
336
  end
275
337
 
276
- it "should not mark attributes dirty if there similar after update" do
277
- jupiter = Planet.new(:name => 'Jupiter', :age => 1_000_000, :core => nil, :id => 42, :data => { :a => "Yeah!" })
338
+ it "should not mark attributes dirty if they are similar after update" do
339
+ jupiter = Planet.new(:name => 'Jupiter', :age => 1_000_000, :id => 42, :data => { :a => "Yeah!" })
278
340
  jupiter.save.should be_true
279
341
 
280
342
  # discriminator will be set automatically
281
343
  jupiter.type.should == Planet
282
344
 
283
- jupiter.attributes = { :name => 'Jupiter', :age => 1_000_000, :core => nil, :data => { :a => "Yeah!" } }
345
+ jupiter.attributes = { :name => 'Jupiter', :age => 1_000_000, :data => { :a => "Yeah!" } }
284
346
 
285
347
  jupiter.attribute_dirty?(:name).should be_false
286
348
  jupiter.attribute_dirty?(:age).should be_false
@@ -291,7 +353,7 @@ describe "DataMapper::Resource" do
291
353
  end
292
354
 
293
355
  it "should not mark attributes dirty if they are similar after typecasting" do
294
- jupiter = Planet.new(:name => 'Jupiter', :age => 1_000_000, :core => nil, :id => 42, :type => nil)
356
+ jupiter = Planet.new(:name => 'Jupiter', :age => 1_000_000, :id => 42, :type => nil)
295
357
  jupiter.save.should be_true
296
358
  jupiter.dirty?.should be_false
297
359
 
@@ -378,7 +440,7 @@ describe "DataMapper::Resource" do
378
440
 
379
441
  it 'should store and retrieve default values' do
380
442
  Planet.property(:satellite_count, Integer, :default => 0)
381
- # stupid example but it's realiable and works
443
+ # stupid example but it's reliable and works
382
444
  Planet.property(:orbit_period, Float, :default => lambda { |r,p| p.name.to_s.length })
383
445
  earth = Planet.new(:name => 'Earth')
384
446
  earth.satellite_count.should == 0