dm-core 0.9.2

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 (101) hide show
  1. data/CHANGELOG +144 -0
  2. data/FAQ +74 -0
  3. data/MIT-LICENSE +22 -0
  4. data/QUICKLINKS +12 -0
  5. data/README +143 -0
  6. data/lib/dm-core.rb +213 -0
  7. data/lib/dm-core/adapters.rb +4 -0
  8. data/lib/dm-core/adapters/abstract_adapter.rb +202 -0
  9. data/lib/dm-core/adapters/data_objects_adapter.rb +701 -0
  10. data/lib/dm-core/adapters/mysql_adapter.rb +132 -0
  11. data/lib/dm-core/adapters/postgres_adapter.rb +179 -0
  12. data/lib/dm-core/adapters/sqlite3_adapter.rb +105 -0
  13. data/lib/dm-core/associations.rb +172 -0
  14. data/lib/dm-core/associations/many_to_many.rb +138 -0
  15. data/lib/dm-core/associations/many_to_one.rb +101 -0
  16. data/lib/dm-core/associations/one_to_many.rb +275 -0
  17. data/lib/dm-core/associations/one_to_one.rb +61 -0
  18. data/lib/dm-core/associations/relationship.rb +116 -0
  19. data/lib/dm-core/associations/relationship_chain.rb +74 -0
  20. data/lib/dm-core/auto_migrations.rb +64 -0
  21. data/lib/dm-core/collection.rb +604 -0
  22. data/lib/dm-core/hook.rb +11 -0
  23. data/lib/dm-core/identity_map.rb +45 -0
  24. data/lib/dm-core/is.rb +16 -0
  25. data/lib/dm-core/logger.rb +233 -0
  26. data/lib/dm-core/migrations/destructive_migrations.rb +17 -0
  27. data/lib/dm-core/migrator.rb +29 -0
  28. data/lib/dm-core/model.rb +399 -0
  29. data/lib/dm-core/naming_conventions.rb +52 -0
  30. data/lib/dm-core/property.rb +611 -0
  31. data/lib/dm-core/property_set.rb +158 -0
  32. data/lib/dm-core/query.rb +590 -0
  33. data/lib/dm-core/repository.rb +159 -0
  34. data/lib/dm-core/resource.rb +618 -0
  35. data/lib/dm-core/scope.rb +35 -0
  36. data/lib/dm-core/support.rb +7 -0
  37. data/lib/dm-core/support/array.rb +13 -0
  38. data/lib/dm-core/support/assertions.rb +8 -0
  39. data/lib/dm-core/support/errors.rb +23 -0
  40. data/lib/dm-core/support/kernel.rb +7 -0
  41. data/lib/dm-core/support/symbol.rb +41 -0
  42. data/lib/dm-core/transaction.rb +267 -0
  43. data/lib/dm-core/type.rb +160 -0
  44. data/lib/dm-core/type_map.rb +80 -0
  45. data/lib/dm-core/types.rb +19 -0
  46. data/lib/dm-core/types/boolean.rb +7 -0
  47. data/lib/dm-core/types/discriminator.rb +32 -0
  48. data/lib/dm-core/types/object.rb +20 -0
  49. data/lib/dm-core/types/paranoid_boolean.rb +23 -0
  50. data/lib/dm-core/types/paranoid_datetime.rb +22 -0
  51. data/lib/dm-core/types/serial.rb +9 -0
  52. data/lib/dm-core/types/text.rb +10 -0
  53. data/spec/integration/association_spec.rb +1215 -0
  54. data/spec/integration/association_through_spec.rb +150 -0
  55. data/spec/integration/associations/many_to_many_spec.rb +171 -0
  56. data/spec/integration/associations/many_to_one_spec.rb +123 -0
  57. data/spec/integration/associations/one_to_many_spec.rb +66 -0
  58. data/spec/integration/auto_migrations_spec.rb +398 -0
  59. data/spec/integration/collection_spec.rb +1015 -0
  60. data/spec/integration/data_objects_adapter_spec.rb +32 -0
  61. data/spec/integration/model_spec.rb +68 -0
  62. data/spec/integration/mysql_adapter_spec.rb +85 -0
  63. data/spec/integration/postgres_adapter_spec.rb +732 -0
  64. data/spec/integration/property_spec.rb +224 -0
  65. data/spec/integration/query_spec.rb +376 -0
  66. data/spec/integration/repository_spec.rb +57 -0
  67. data/spec/integration/resource_spec.rb +324 -0
  68. data/spec/integration/sqlite3_adapter_spec.rb +352 -0
  69. data/spec/integration/sti_spec.rb +185 -0
  70. data/spec/integration/transaction_spec.rb +75 -0
  71. data/spec/integration/type_spec.rb +149 -0
  72. data/spec/lib/mock_adapter.rb +27 -0
  73. data/spec/spec_helper.rb +112 -0
  74. data/spec/unit/adapters/abstract_adapter_spec.rb +133 -0
  75. data/spec/unit/adapters/adapter_shared_spec.rb +15 -0
  76. data/spec/unit/adapters/data_objects_adapter_spec.rb +627 -0
  77. data/spec/unit/adapters/postgres_adapter_spec.rb +125 -0
  78. data/spec/unit/associations/many_to_many_spec.rb +14 -0
  79. data/spec/unit/associations/many_to_one_spec.rb +138 -0
  80. data/spec/unit/associations/one_to_many_spec.rb +385 -0
  81. data/spec/unit/associations/one_to_one_spec.rb +7 -0
  82. data/spec/unit/associations/relationship_spec.rb +67 -0
  83. data/spec/unit/associations_spec.rb +205 -0
  84. data/spec/unit/auto_migrations_spec.rb +110 -0
  85. data/spec/unit/collection_spec.rb +174 -0
  86. data/spec/unit/data_mapper_spec.rb +21 -0
  87. data/spec/unit/identity_map_spec.rb +126 -0
  88. data/spec/unit/is_spec.rb +80 -0
  89. data/spec/unit/migrator_spec.rb +33 -0
  90. data/spec/unit/model_spec.rb +339 -0
  91. data/spec/unit/naming_conventions_spec.rb +28 -0
  92. data/spec/unit/property_set_spec.rb +96 -0
  93. data/spec/unit/property_spec.rb +447 -0
  94. data/spec/unit/query_spec.rb +485 -0
  95. data/spec/unit/repository_spec.rb +93 -0
  96. data/spec/unit/resource_spec.rb +557 -0
  97. data/spec/unit/scope_spec.rb +131 -0
  98. data/spec/unit/transaction_spec.rb +493 -0
  99. data/spec/unit/type_map_spec.rb +114 -0
  100. data/spec/unit/type_spec.rb +119 -0
  101. metadata +187 -0
@@ -0,0 +1,224 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ gem 'fastercsv', '>=1.2.3'
4
+ require 'fastercsv'
5
+
6
+ if ADAPTER
7
+ describe DataMapper::Property, "with #{ADAPTER}" do
8
+ describe " tracking strategies" do
9
+ before :all do
10
+ class Actor
11
+ include DataMapper::Resource
12
+
13
+ property :id, Serial
14
+ property :name, String, :track => :set # :track default is :get for mutable types
15
+ property :notes, DataMapper::Types::Text
16
+ property :age, Integer # :track default is :set for mutable types
17
+ property :rating, Integer
18
+ property :location, String
19
+ property :lead, TrueClass, :track => :load
20
+ property :cv, Object # :track should be :hash
21
+ property :agent, String, :track => :hash # :track only Object#hash value on :load.
22
+ # Potentially faster, but less safe, so use judiciously, when the odds of a hash-collision are low.
23
+ end
24
+ end
25
+
26
+ before do
27
+ Actor.auto_migrate!(ADAPTER)
28
+ end
29
+
30
+ it "should set up tracking information" do
31
+ Actor.properties[:name].track.should == :set
32
+ Actor.properties[:location].track.should == :get
33
+ Actor.properties[:rating].track.should == :set
34
+ Actor.properties[:lead].track.should == :load
35
+ Actor.properties[:cv].track.should == :hash
36
+ Actor.properties[:agent].track.should == :hash
37
+ end
38
+
39
+ it "should track on :set" do
40
+ repository(ADAPTER) do
41
+ bob = Actor.new(:name => 'bob')
42
+ bob.save
43
+
44
+ bob.original_values.should_not have_key(:name)
45
+ bob.dirty?.should == false
46
+
47
+ bob.name = "Bob"
48
+ bob.original_values.should have_key(:name)
49
+ bob.original_values[:name].should == 'bob'
50
+ bob.dirty?.should == true
51
+ end
52
+ end
53
+
54
+ it "should track on :get" do
55
+ repository(ADAPTER) do
56
+ jon = Actor.new(:name => 'jon', :location => 'dallas')
57
+ jon.save
58
+
59
+ jon.location
60
+ jon.original_values.should have_key(:location)
61
+ jon.original_values[:location].should == 'dallas'
62
+
63
+ jon.dirty?.should be_false
64
+ jon.save.should be_false
65
+
66
+ jon.location.upcase!
67
+ jon.location.should == 'DALLAS'
68
+ jon.original_values[:location].should == 'dallas'
69
+
70
+ jon.dirty?.should be_true
71
+ jon.save.should be_true
72
+
73
+ jon.location << '!'
74
+ jon.original_values[:location].should == 'DALLAS'
75
+ jon.dirty?.should be_true
76
+ end
77
+ end
78
+
79
+ it "should track on :load" do
80
+ repository(ADAPTER) do
81
+ jan = Actor.create!(:name => 'jan', :lead => true)
82
+ jan.lead = false
83
+ jan.original_values[:lead].should be_true
84
+ jan.dirty?.should == true
85
+ end
86
+ repository(ADAPTER) do
87
+ jan = Actor.first
88
+ jan.original_values.should have_key(:lead)
89
+ jan.original_values[:lead].should be_true
90
+ jan.dirty?.should == false
91
+ end
92
+ end
93
+
94
+ it "should track on :hash" do
95
+ cv = { 2005 => "Othello" }
96
+ repository(ADAPTER) do
97
+ tom = Actor.create!(:name => 'tom', :cv => cv)
98
+ end
99
+ repository(ADAPTER) do
100
+ tom = Actor.first(:name => 'tom')
101
+ tom.cv.merge!({2006 => "Macbeth"})
102
+
103
+ tom.original_values.should have_key(:cv)
104
+ # tom.original_values[:cv].should == cv.hash
105
+ tom.cv.should == { 2005 => "Othello", 2006 => "Macbeth" }
106
+ tom.dirty?.should == true
107
+ end
108
+ end
109
+
110
+ it "should track with lazy text fields (#342)" do
111
+ repository(ADAPTER) do
112
+ tim = Actor.create!(:name => 'tim')
113
+ end
114
+ repository(ADAPTER) do
115
+ tim = Actor.first(:name => 'tim')
116
+ tim.notes # make sure they're loaded...
117
+ tim.dirty?.should be_false
118
+ tim.save.should be_false
119
+ tim.notes = "Testing"
120
+ tim.dirty?.should be_true
121
+ tim.save
122
+ end
123
+ repository(ADAPTER) do
124
+ tim = Actor.first(:name => 'tim')
125
+ tim.notes.should == "Testing"
126
+ end
127
+ end
128
+ end
129
+
130
+ describe "lazy loading" do
131
+ before :all do
132
+ class RowBoat
133
+ include DataMapper::Resource
134
+ property :id, Serial
135
+ property :notes, String, :lazy => [:notes]
136
+ property :trip_report, String, :lazy => [:notes,:trip]
137
+ property :miles, Integer, :lazy => [:trip]
138
+ end
139
+ end
140
+
141
+ before do
142
+ RowBoat.auto_migrate!(ADAPTER)
143
+
144
+ repository(ADAPTER) do
145
+ RowBoat.create!(:id => 1, :notes=>'Note',:trip_report=>'Report',:miles=>23)
146
+ RowBoat.create!(:id => 2, :notes=>'Note',:trip_report=>'Report',:miles=>23)
147
+ RowBoat.create!(:id => 3, :notes=>'Note',:trip_report=>'Report',:miles=>23)
148
+ end
149
+ end
150
+
151
+ it "should lazy load in context" do
152
+ result = repository(ADAPTER) { RowBoat.all.to_a }
153
+
154
+ result[0].attribute_loaded?(:notes).should be_false
155
+ result[0].attribute_loaded?(:trip_report).should be_false
156
+ result[1].attribute_loaded?(:notes).should be_false
157
+
158
+ result[0].notes.should_not be_nil
159
+
160
+ result[1].attribute_loaded?(:notes).should be_true
161
+ result[1].attribute_loaded?(:trip_report).should be_true
162
+ result[1].attribute_loaded?(:miles).should be_false
163
+
164
+ result = repository(ADAPTER) { RowBoat.all.to_a }
165
+
166
+ result[0].attribute_loaded?(:trip_report).should be_false
167
+ result[0].attribute_loaded?(:miles).should be_false
168
+
169
+ result[1].trip_report.should_not be_nil
170
+ result[2].attribute_loaded?(:miles).should be_true
171
+ end
172
+ end
173
+
174
+ describe 'defaults' do
175
+ before :all do
176
+ class Catamaran
177
+ include DataMapper::Resource
178
+ property :id, Serial
179
+ property :name, String
180
+
181
+ # Boolean
182
+ property :could_be_bool0, TrueClass, :default => true
183
+ property :could_be_bool1, TrueClass, :default => false
184
+ end
185
+
186
+ repository(ADAPTER){ Catamaran.auto_migrate!(ADAPTER) }
187
+ end
188
+
189
+ before :each do
190
+ @cat = Catamaran.new
191
+ end
192
+
193
+ it "should have defaults" do
194
+ @cat.could_be_bool0.should == true
195
+ @cat.could_be_bool1.should_not be_nil
196
+ @cat.could_be_bool1.should == false
197
+
198
+ @cat.name = 'Mary Mayweather'
199
+
200
+ repository(ADAPTER) do
201
+ @cat.save
202
+
203
+ cat = Catamaran.first
204
+ cat.could_be_bool0.should == true
205
+ cat.could_be_bool1.should_not be_nil
206
+ cat.could_be_bool1.should == false
207
+ cat.destroy
208
+ end
209
+
210
+ end
211
+
212
+ it "should have defaults even with creates" do
213
+ repository(ADAPTER) do
214
+ Catamaran.create!(:name => 'Jingle All The Way')
215
+ cat = Catamaran.first
216
+ cat.name.should == 'Jingle All The Way'
217
+ cat.could_be_bool0.should == true
218
+ cat.could_be_bool1.should_not be_nil
219
+ cat.could_be_bool1.should == false
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,376 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ if ADAPTER
4
+ describe DataMapper::Query, "with #{ADAPTER}" do
5
+ describe 'when ordering' do
6
+ before :all do
7
+ class SailBoat
8
+ include DataMapper::Resource
9
+ property :id, Serial
10
+ property :name, String
11
+ property :port, String
12
+ end
13
+ end
14
+
15
+ before do
16
+ SailBoat.auto_migrate!(ADAPTER)
17
+
18
+ repository(ADAPTER) do
19
+ SailBoat.create!(:name => 'A', :port => 'C')
20
+ SailBoat.create!(:name => 'B', :port => 'B')
21
+ SailBoat.create!(:name => 'C', :port => 'A')
22
+ end
23
+ end
24
+
25
+ it "should find by conditions" do
26
+ lambda do
27
+ repository(ADAPTER) do
28
+ SailBoat.first(:conditions => ['name = ?', 'B'])
29
+ end
30
+ end.should_not raise_error
31
+
32
+ lambda do
33
+ repository(ADAPTER) do
34
+ SailBoat.first(:conditions => ['name = ?', 'A'])
35
+ end
36
+ end.should_not raise_error
37
+ end
38
+
39
+ it "should find by conditions passed in as hash" do
40
+ repository(ADAPTER) do
41
+ SailBoat.create!(:name => "couldbe@email.com", :port => 'wee')
42
+
43
+ find = SailBoat.first(:name => 'couldbe@email.com')
44
+ find.name.should == 'couldbe@email.com'
45
+
46
+ find = SailBoat.first(:name => 'couldbe@email.com', :port.not => nil)
47
+ find.should_not be_nil
48
+ find.port.should_not be_nil
49
+ find.name.should == 'couldbe@email.com'
50
+ end
51
+ end
52
+
53
+ it "should find by conditions passed in a range" do
54
+ repository(ADAPTER) do
55
+ find = SailBoat.all(:id => 0..2)
56
+ find.should_not be_nil
57
+ find.should have(2).entries
58
+
59
+ find = SailBoat.all(:id.not => 0..2)
60
+ find.should have(1).entries
61
+ end
62
+ end
63
+
64
+ it "should order results" do
65
+ repository(ADAPTER) do
66
+ result = SailBoat.all(:order => [
67
+ DataMapper::Query::Direction.new(SailBoat.properties[:name], :asc)
68
+ ])
69
+ result[0].id.should == 1
70
+
71
+ result = SailBoat.all(:order => [
72
+ DataMapper::Query::Direction.new(SailBoat.properties[:port], :asc)
73
+ ])
74
+ result[0].id.should == 3
75
+
76
+ result = SailBoat.all(:order => [
77
+ DataMapper::Query::Direction.new(SailBoat.properties[:name], :asc),
78
+ DataMapper::Query::Direction.new(SailBoat.properties[:port], :asc)
79
+ ])
80
+ result[0].id.should == 1
81
+
82
+ result = SailBoat.all(:order => [
83
+ SailBoat.properties[:name],
84
+ DataMapper::Query::Direction.new(SailBoat.properties[:port], :asc)
85
+ ])
86
+ result[0].id.should == 1
87
+
88
+ result = SailBoat.all(:order => [:name])
89
+ result[0].id.should == 1
90
+
91
+ result = SailBoat.all(:order => [:name.desc])
92
+ result[0].id.should == 3
93
+ end
94
+ end
95
+ end
96
+
97
+ describe 'when sub-selecting' do
98
+ before :all do
99
+ class Permission
100
+ include DataMapper::Resource
101
+ property :id, Serial
102
+ property :user_id, Integer
103
+ property :resource_id, Integer
104
+ property :resource_type, String
105
+ property :token, String
106
+ end
107
+
108
+ class SailBoat
109
+ include DataMapper::Resource
110
+ property :id, Serial
111
+ property :name, String
112
+ property :port, String
113
+ property :captain, String
114
+ end
115
+ end
116
+
117
+ before do
118
+ Permission.auto_migrate!(ADAPTER)
119
+ SailBoat.auto_migrate!(ADAPTER)
120
+
121
+ repository(ADAPTER) do
122
+ SailBoat.create!(:id => 1, :name => "Fantasy I", :port => "Cape Town", :captain => 'Joe')
123
+ SailBoat.create!(:id => 2, :name => "Royal Flush II", :port => "Cape Town", :captain => 'James')
124
+ SailBoat.create!(:id => 3, :name => "Infringer III", :port => "Cape Town", :captain => 'Jason')
125
+
126
+ #User 1 permission -- read boat 1 & 2
127
+ Permission.create!(:id => 1, :user_id => 1, :resource_id => 1, :resource_type => 'SailBoat', :token => 'READ')
128
+ Permission.create!(:id => 2, :user_id => 1, :resource_id => 2, :resource_type => 'SailBoat', :token => 'READ')
129
+
130
+ #User 2 permission -- read boat 2 & 3
131
+ Permission.create!(:id => 3, :user_id => 2, :resource_id => 2, :resource_type => 'SailBoat', :token => 'READ')
132
+ Permission.create!(:id => 4, :user_id => 2, :resource_id => 3, :resource_type => 'SailBoat', :token => 'READ')
133
+ end
134
+ end
135
+
136
+ it 'should accept a DM::Query as a value of a condition' do
137
+ # User 1
138
+ acl = DataMapper::Query.new(repository(ADAPTER), Permission, :user_id => 1, :resource_type => 'SailBoat', :token => 'READ', :fields => [ :resource_id ])
139
+ query = { :port => 'Cape Town', :id => acl, :captain.like => 'J%', :order => [ :id ] }
140
+ boats = repository(ADAPTER) { SailBoat.all(query) }
141
+ boats.should have(2).entries
142
+ boats.entries[0].id.should == 1
143
+ boats.entries[1].id.should == 2
144
+
145
+ # User 2
146
+ acl = DataMapper::Query.new(repository(ADAPTER), Permission, :user_id => 2, :resource_type => 'SailBoat', :token => 'READ', :fields => [ :resource_id ])
147
+ query = { :port => 'Cape Town', :id => acl, :captain.like => 'J%', :order => [ :id ] }
148
+ boats = repository(ADAPTER) { SailBoat.all(query) }
149
+
150
+ boats.should have(2).entries
151
+ boats.entries[0].id.should == 2
152
+ boats.entries[1].id.should == 3
153
+ end
154
+
155
+ it 'when value is NOT IN another query' do
156
+ # Boats that User 1 Cannot see
157
+ acl = DataMapper::Query.new(repository(ADAPTER), Permission, :user_id => 1, :resource_type => 'SailBoat', :token => 'READ', :fields => [:resource_id])
158
+ query = { :port => 'Cape Town', :id.not => acl, :captain.like => 'J%' }
159
+ boats = repository(ADAPTER) { SailBoat.all(query) }
160
+ boats.should have(1).entries
161
+ boats.entries[0].id.should == 3
162
+ end
163
+ end # describe sub-selecting
164
+
165
+ describe 'when linking associated objects' do
166
+ before :all do
167
+ class Region
168
+ include DataMapper::Resource
169
+ property :id, Serial
170
+ property :name, String
171
+
172
+ def self.default_repository_name
173
+ ADAPTER
174
+ end
175
+ end
176
+
177
+ class Factory
178
+ include DataMapper::Resource
179
+ property :id, Serial
180
+ property :region_id, Integer
181
+ property :name, String
182
+
183
+ repository(:mock) do
184
+ property :land, String
185
+ end
186
+
187
+ belongs_to :region
188
+
189
+ def self.default_repository_name
190
+ ADAPTER
191
+ end
192
+ end
193
+
194
+ class Vehicle
195
+ include DataMapper::Resource
196
+ property :id, Serial
197
+ property :factory_id, Integer
198
+ property :name, String
199
+
200
+ belongs_to :factory
201
+
202
+ def self.default_repository_name
203
+ ADAPTER
204
+ end
205
+ end
206
+
207
+ module Namespace
208
+ class Region
209
+ include DataMapper::Resource
210
+ property :id, Serial
211
+ property :name, String
212
+
213
+ def self.default_repository_name
214
+ ADAPTER
215
+ end
216
+ end
217
+
218
+ class Factory
219
+ include DataMapper::Resource
220
+ property :id, Serial
221
+ property :region_id, Integer
222
+ property :name, String
223
+
224
+ repository(:mock) do
225
+ property :land, String
226
+ end
227
+
228
+ belongs_to :region
229
+
230
+ def self.default_repository_name
231
+ ADAPTER
232
+ end
233
+ end
234
+
235
+ class Vehicle
236
+ include DataMapper::Resource
237
+ property :id, Serial
238
+ property :factory_id, Integer
239
+ property :name, String
240
+
241
+ belongs_to :factory
242
+
243
+ def self.default_repository_name
244
+ ADAPTER
245
+ end
246
+ end
247
+ end
248
+ end
249
+
250
+ before do
251
+ Region.auto_migrate!
252
+ Factory.auto_migrate!
253
+ Vehicle.auto_migrate!
254
+
255
+ Region.new(:id=>1, :name=>'North West').save
256
+ Factory.new(:id=>2000, :region_id=>1, :name=>'North West Plant').save
257
+ Vehicle.new(:id=>1, :factory_id=>2000, :name=>'10 ton delivery truck').save
258
+
259
+ Namespace::Region.auto_migrate!
260
+ Namespace::Factory.auto_migrate!
261
+ Namespace::Vehicle.auto_migrate!
262
+
263
+ Namespace::Region.new(:id=>1, :name=>'North West').save
264
+ Namespace::Factory.new(:id=>2000, :region_id=>1, :name=>'North West Plant').save
265
+ Namespace::Vehicle.new(:id=>1, :factory_id=>2000, :name=>'10 ton delivery truck').save
266
+ end
267
+
268
+ it 'should require that all properties in :fields and all :links come from the same repository' #do
269
+ # land = Factory.properties(:mock)[:land]
270
+ # fields = []
271
+ # Vehicle.properties(ADAPTER).map do |property|
272
+ # fields << property
273
+ # end
274
+ # fields << land
275
+ #
276
+ # lambda{
277
+ # begin
278
+ # results = repository(ADAPTER) { Vehicle.all(:links => [:factory], :fields => fields) }
279
+ # rescue RuntimeError
280
+ # $!.message.should == "Property Factory.land not available in repository #{ADAPTER}"
281
+ # raise $!
282
+ # end
283
+ # }.should raise_error(RuntimeError)
284
+ #end
285
+
286
+ it 'should accept a DM::Assoc::Relationship as a link' do
287
+ factory = DataMapper::Associations::Relationship.new(
288
+ :factory,
289
+ ADAPTER,
290
+ 'Vehicle',
291
+ 'Factory',
292
+ { :child_key => [ :factory_id ], :parent_key => [ :id ] }
293
+ )
294
+ results = repository(ADAPTER) { Vehicle.all(:links => [factory]) }
295
+ results.should have(1).entries
296
+ end
297
+
298
+ it 'should accept a symbol of an association name as a link' do
299
+ results = repository(ADAPTER) { Vehicle.all(:links => [:factory]) }
300
+ results.should have(1).entries
301
+ end
302
+
303
+ it 'should accept a string of an association name as a link' do
304
+ results = repository(ADAPTER) { Vehicle.all(:links => ['factory']) }
305
+ results.should have(1).entries
306
+ end
307
+
308
+ it 'should accept a mixture of items as a set of links' do
309
+ region = DataMapper::Associations::Relationship.new(
310
+ :region,
311
+ ADAPTER,
312
+ 'Factory',
313
+ 'Region',
314
+ { :child_key => [ :region_id ], :parent_key => [ :id ] }
315
+ )
316
+ results = repository(ADAPTER) { Vehicle.all(:links => ['factory',region]) }
317
+ results.should have(1).entries
318
+ end
319
+
320
+ it 'should only accept a DM::Assoc::Relationship, String & Symbol as a link' do
321
+ lambda{
322
+ DataMapper::Query.new(repository(ADAPTER), Vehicle, :links => [1])
323
+ }.should raise_error(ArgumentError)
324
+ end
325
+
326
+ it 'should have a association by the name of the Symbol or String' do
327
+ lambda{
328
+ DataMapper::Query.new(repository(ADAPTER), Vehicle, :links=>['Sailing'])
329
+ }.should raise_error(ArgumentError)
330
+
331
+ lambda{
332
+ DataMapper::Query.new(repository(ADAPTER), Vehicle, :links=>[:sailing])
333
+ }.should raise_error(ArgumentError)
334
+ end
335
+
336
+ it 'should create an n-level query path' do
337
+ Vehicle.factory.region.model.should == Region
338
+ Vehicle.factory.region.name.property.should == Region.properties(Region.repository.name)[:name]
339
+ end
340
+
341
+ it 'should accept a DM::QueryPath as the key to a condition' do
342
+ vehicle = Vehicle.first(Vehicle.factory.region.name => 'North West')
343
+ vehicle.name.should == '10 ton delivery truck'
344
+
345
+ vehicle = Namespace::Vehicle.first(Namespace::Vehicle.factory.region.name => 'North West')
346
+ vehicle.name.should == '10 ton delivery truck'
347
+ end
348
+
349
+ it "should accept a string representing a DM::QueryPath as they key to a condition" do
350
+ vehicle = Vehicle.first("factory.region.name" => 'North West')
351
+ vehicle.name.should == '10 ton delivery truck'
352
+ end
353
+
354
+ it 'should auto generate the link if a DM::Property from a different resource is in the :fields option'
355
+
356
+ it 'should create links with composite keys'
357
+
358
+ it 'should eager load associations' do
359
+ repository(ADAPTER) do
360
+ vehicle = Vehicle.first(:includes => [Vehicle.factory])
361
+ end
362
+ end
363
+
364
+ it "should behave when using mocks" do
365
+ class Group
366
+ include DataMapper::Resource
367
+ property :id, Serial
368
+ property :name, String
369
+ end
370
+
371
+ Group.should_receive(:all).with(:order => [:id.asc])
372
+ Group.all(:order => [:id.asc])
373
+ end
374
+ end # describe links
375
+ end # DM::Query
376
+ end