rpbertp13-dm-core 0.9.11.1

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 (131) hide show
  1. data/.autotest +26 -0
  2. data/.gitignore +18 -0
  3. data/CONTRIBUTING +51 -0
  4. data/FAQ +92 -0
  5. data/History.txt +52 -0
  6. data/MIT-LICENSE +22 -0
  7. data/Manifest.txt +130 -0
  8. data/QUICKLINKS +11 -0
  9. data/README.txt +143 -0
  10. data/Rakefile +32 -0
  11. data/SPECS +62 -0
  12. data/TODO +1 -0
  13. data/dm-core.gemspec +40 -0
  14. data/lib/dm-core.rb +217 -0
  15. data/lib/dm-core/adapters.rb +16 -0
  16. data/lib/dm-core/adapters/abstract_adapter.rb +209 -0
  17. data/lib/dm-core/adapters/data_objects_adapter.rb +716 -0
  18. data/lib/dm-core/adapters/in_memory_adapter.rb +87 -0
  19. data/lib/dm-core/adapters/mysql_adapter.rb +138 -0
  20. data/lib/dm-core/adapters/postgres_adapter.rb +189 -0
  21. data/lib/dm-core/adapters/sqlite3_adapter.rb +105 -0
  22. data/lib/dm-core/associations.rb +207 -0
  23. data/lib/dm-core/associations/many_to_many.rb +147 -0
  24. data/lib/dm-core/associations/many_to_one.rb +107 -0
  25. data/lib/dm-core/associations/one_to_many.rb +315 -0
  26. data/lib/dm-core/associations/one_to_one.rb +61 -0
  27. data/lib/dm-core/associations/relationship.rb +221 -0
  28. data/lib/dm-core/associations/relationship_chain.rb +81 -0
  29. data/lib/dm-core/auto_migrations.rb +105 -0
  30. data/lib/dm-core/collection.rb +670 -0
  31. data/lib/dm-core/dependency_queue.rb +32 -0
  32. data/lib/dm-core/hook.rb +11 -0
  33. data/lib/dm-core/identity_map.rb +42 -0
  34. data/lib/dm-core/is.rb +16 -0
  35. data/lib/dm-core/logger.rb +232 -0
  36. data/lib/dm-core/migrations/destructive_migrations.rb +17 -0
  37. data/lib/dm-core/migrator.rb +29 -0
  38. data/lib/dm-core/model.rb +526 -0
  39. data/lib/dm-core/naming_conventions.rb +84 -0
  40. data/lib/dm-core/property.rb +676 -0
  41. data/lib/dm-core/property_set.rb +169 -0
  42. data/lib/dm-core/query.rb +676 -0
  43. data/lib/dm-core/repository.rb +167 -0
  44. data/lib/dm-core/resource.rb +671 -0
  45. data/lib/dm-core/scope.rb +58 -0
  46. data/lib/dm-core/support.rb +7 -0
  47. data/lib/dm-core/support/array.rb +13 -0
  48. data/lib/dm-core/support/assertions.rb +8 -0
  49. data/lib/dm-core/support/errors.rb +23 -0
  50. data/lib/dm-core/support/kernel.rb +11 -0
  51. data/lib/dm-core/support/symbol.rb +41 -0
  52. data/lib/dm-core/transaction.rb +252 -0
  53. data/lib/dm-core/type.rb +160 -0
  54. data/lib/dm-core/type_map.rb +80 -0
  55. data/lib/dm-core/types.rb +19 -0
  56. data/lib/dm-core/types/boolean.rb +7 -0
  57. data/lib/dm-core/types/discriminator.rb +34 -0
  58. data/lib/dm-core/types/object.rb +24 -0
  59. data/lib/dm-core/types/paranoid_boolean.rb +34 -0
  60. data/lib/dm-core/types/paranoid_datetime.rb +33 -0
  61. data/lib/dm-core/types/serial.rb +9 -0
  62. data/lib/dm-core/types/text.rb +10 -0
  63. data/lib/dm-core/version.rb +3 -0
  64. data/script/all +4 -0
  65. data/script/performance.rb +282 -0
  66. data/script/profile.rb +87 -0
  67. data/spec/integration/association_spec.rb +1382 -0
  68. data/spec/integration/association_through_spec.rb +203 -0
  69. data/spec/integration/associations/many_to_many_spec.rb +449 -0
  70. data/spec/integration/associations/many_to_one_spec.rb +163 -0
  71. data/spec/integration/associations/one_to_many_spec.rb +188 -0
  72. data/spec/integration/auto_migrations_spec.rb +413 -0
  73. data/spec/integration/collection_spec.rb +1073 -0
  74. data/spec/integration/data_objects_adapter_spec.rb +32 -0
  75. data/spec/integration/dependency_queue_spec.rb +46 -0
  76. data/spec/integration/model_spec.rb +197 -0
  77. data/spec/integration/mysql_adapter_spec.rb +85 -0
  78. data/spec/integration/postgres_adapter_spec.rb +731 -0
  79. data/spec/integration/property_spec.rb +253 -0
  80. data/spec/integration/query_spec.rb +514 -0
  81. data/spec/integration/repository_spec.rb +61 -0
  82. data/spec/integration/resource_spec.rb +513 -0
  83. data/spec/integration/sqlite3_adapter_spec.rb +352 -0
  84. data/spec/integration/sti_spec.rb +273 -0
  85. data/spec/integration/strategic_eager_loading_spec.rb +156 -0
  86. data/spec/integration/transaction_spec.rb +60 -0
  87. data/spec/integration/type_spec.rb +275 -0
  88. data/spec/lib/logging_helper.rb +18 -0
  89. data/spec/lib/mock_adapter.rb +27 -0
  90. data/spec/lib/model_loader.rb +100 -0
  91. data/spec/lib/publicize_methods.rb +28 -0
  92. data/spec/models/content.rb +16 -0
  93. data/spec/models/vehicles.rb +34 -0
  94. data/spec/models/zoo.rb +48 -0
  95. data/spec/spec.opts +3 -0
  96. data/spec/spec_helper.rb +91 -0
  97. data/spec/unit/adapters/abstract_adapter_spec.rb +133 -0
  98. data/spec/unit/adapters/adapter_shared_spec.rb +15 -0
  99. data/spec/unit/adapters/data_objects_adapter_spec.rb +632 -0
  100. data/spec/unit/adapters/in_memory_adapter_spec.rb +98 -0
  101. data/spec/unit/adapters/postgres_adapter_spec.rb +133 -0
  102. data/spec/unit/associations/many_to_many_spec.rb +32 -0
  103. data/spec/unit/associations/many_to_one_spec.rb +159 -0
  104. data/spec/unit/associations/one_to_many_spec.rb +393 -0
  105. data/spec/unit/associations/one_to_one_spec.rb +7 -0
  106. data/spec/unit/associations/relationship_spec.rb +71 -0
  107. data/spec/unit/associations_spec.rb +242 -0
  108. data/spec/unit/auto_migrations_spec.rb +111 -0
  109. data/spec/unit/collection_spec.rb +182 -0
  110. data/spec/unit/data_mapper_spec.rb +35 -0
  111. data/spec/unit/identity_map_spec.rb +126 -0
  112. data/spec/unit/is_spec.rb +80 -0
  113. data/spec/unit/migrator_spec.rb +33 -0
  114. data/spec/unit/model_spec.rb +321 -0
  115. data/spec/unit/naming_conventions_spec.rb +36 -0
  116. data/spec/unit/property_set_spec.rb +90 -0
  117. data/spec/unit/property_spec.rb +753 -0
  118. data/spec/unit/query_spec.rb +571 -0
  119. data/spec/unit/repository_spec.rb +93 -0
  120. data/spec/unit/resource_spec.rb +649 -0
  121. data/spec/unit/scope_spec.rb +142 -0
  122. data/spec/unit/transaction_spec.rb +469 -0
  123. data/spec/unit/type_map_spec.rb +114 -0
  124. data/spec/unit/type_spec.rb +119 -0
  125. data/tasks/ci.rb +36 -0
  126. data/tasks/dm.rb +63 -0
  127. data/tasks/doc.rb +20 -0
  128. data/tasks/gemspec.rb +23 -0
  129. data/tasks/hoe.rb +46 -0
  130. data/tasks/install.rb +20 -0
  131. metadata +215 -0
@@ -0,0 +1,253 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ # if RUBY_VERSION >= '1.9.0'
4
+ # require 'csv'
5
+ # else
6
+ # gem 'fastercsv', '~>1.4.0'
7
+ # require 'fastercsv'
8
+ # end
9
+
10
+ describe DataMapper::Property do
11
+ before do
12
+ module ::PropertySpec
13
+ class Resource
14
+ include DataMapper::Resource
15
+ end
16
+ end
17
+
18
+ @property = PropertySpec::Resource.property :id, DM::Serial
19
+ end
20
+
21
+ it 'should be serializable with Marshal' do
22
+ Marshal.load(Marshal.dump(@property)).should == @property
23
+ end
24
+ end
25
+
26
+ if ADAPTER
27
+ describe DataMapper::Property, "with #{ADAPTER}" do
28
+ describe " tracking strategies" do
29
+ before :all do
30
+ class ::Actor
31
+ include DataMapper::Resource
32
+
33
+ property :id, Serial
34
+ property :name, String, :track => :set # :track default is :get for mutable types
35
+ property :notes, DataMapper::Types::Text
36
+ property :age, Integer # :track default is :set for immutable types
37
+ property :rating, Integer
38
+ property :location, String
39
+ property :lead, TrueClass, :track => :load
40
+ property :cv, Object # :track should be :hash
41
+ property :agent, String, :track => :hash # :track only Object#hash value on :load.
42
+ # Potentially faster, but less safe, so use judiciously, when the odds of a hash-collision are low.
43
+ end
44
+ end
45
+
46
+ before do
47
+ Actor.auto_migrate!(ADAPTER)
48
+ end
49
+
50
+ it "should set up tracking information" do
51
+ Actor.properties[:name].track.should == :set
52
+ Actor.properties[:location].track.should == :get
53
+ Actor.properties[:rating].track.should == :set
54
+ Actor.properties[:lead].track.should == :load
55
+ Actor.properties[:cv].track.should == :hash
56
+ Actor.properties[:agent].track.should == :hash
57
+ end
58
+
59
+ it "should track on :set" do
60
+ repository(ADAPTER) do
61
+ bob = Actor.new(:name => 'bob')
62
+ bob.save
63
+
64
+ bob.original_values.should_not have_key(:name)
65
+ bob.dirty?.should == false
66
+
67
+ bob.name = "Bob"
68
+ bob.original_values.should have_key(:name)
69
+ bob.original_values[:name].should == 'bob'
70
+ bob.dirty?.should == true
71
+ end
72
+ end
73
+
74
+ it "should track on :get" do
75
+ repository(ADAPTER) do
76
+ jon = Actor.new(:name => 'jon', :location => 'dallas')
77
+ jon.save
78
+
79
+ jon.location
80
+ jon.original_values.should have_key(:location)
81
+ jon.original_values[:location].should == 'dallas'
82
+
83
+ jon.dirty?.should be_false
84
+ jon.save.should be_true
85
+
86
+ jon.location.upcase!
87
+ jon.location.should == 'DALLAS'
88
+ jon.original_values[:location].should == 'dallas'
89
+
90
+ jon.dirty?.should be_true
91
+ jon.save.should be_true
92
+
93
+ jon.location << '!'
94
+ jon.original_values[:location].should == 'DALLAS'
95
+ jon.dirty?.should be_true
96
+ end
97
+ end
98
+
99
+ it "should track on :load" do
100
+ repository(ADAPTER) do
101
+ jan = Actor.create(:name => 'jan', :lead => true)
102
+ jan.lead = false
103
+ jan.original_values[:lead].should be_true
104
+ jan.dirty?.should == true
105
+ end
106
+ repository(ADAPTER) do
107
+ jan = Actor.first
108
+ jan.original_values.should have_key(:lead)
109
+ jan.original_values[:lead].should be_true
110
+ jan.dirty?.should == false
111
+ end
112
+ end
113
+
114
+ it "should track on :hash" do
115
+ cv = { 2005 => "Othello" }
116
+ repository(ADAPTER) do
117
+ tom = Actor.create(:name => 'tom', :cv => cv)
118
+ end
119
+ repository(ADAPTER) do
120
+ tom = Actor.first(:name => 'tom')
121
+ tom.cv.merge!({2006 => "Macbeth"})
122
+
123
+ tom.original_values.should have_key(:cv)
124
+ # tom.original_values[:cv].should == cv.hash
125
+ tom.cv.should == { 2005 => "Othello", 2006 => "Macbeth" }
126
+ tom.dirty?.should == true
127
+ end
128
+ end
129
+
130
+ it "should track with lazy text fields (#342)" do
131
+ repository(ADAPTER) do
132
+ tim = Actor.create(:name => 'tim')
133
+ end
134
+ repository(ADAPTER) do
135
+ tim = Actor.first(:name => 'tim')
136
+ tim.notes # make sure they're loaded...
137
+ tim.dirty?.should be_false
138
+ tim.save.should be_true
139
+ tim.notes = "Testing"
140
+ tim.dirty?.should be_true
141
+ tim.save.should be_true
142
+ end
143
+ repository(ADAPTER) do
144
+ tim = Actor.first(:name => 'tim')
145
+ tim.notes.should == "Testing"
146
+ end
147
+ end
148
+ end
149
+
150
+ describe "lazy loading" do
151
+ before :all do
152
+ class ::RowBoat
153
+ include DataMapper::Resource
154
+ property :id, Serial
155
+ property :notes, String, :lazy => [:notes]
156
+ property :trip_report, String, :lazy => [:notes,:trip]
157
+ property :miles, Integer, :lazy => [:trip]
158
+ end
159
+ end
160
+
161
+ before do
162
+ RowBoat.auto_migrate!(ADAPTER)
163
+
164
+ repository(ADAPTER) do
165
+ RowBoat.create(:id => 1, :notes=>'Note',:trip_report=>'Report',:miles=>23)
166
+ RowBoat.create(:id => 2, :notes=>'Note',:trip_report=>'Report',:miles=>23)
167
+ RowBoat.create(:id => 3, :notes=>'Note',:trip_report=>'Report',:miles=>23)
168
+ end
169
+ end
170
+
171
+ it "should lazy load in context" do
172
+ result = repository(ADAPTER) { RowBoat.all.to_a }
173
+
174
+ result[0].attribute_loaded?(:notes).should be_false
175
+ result[0].attribute_loaded?(:trip_report).should be_false
176
+ result[1].attribute_loaded?(:notes).should be_false
177
+
178
+ result[0].notes.should_not be_nil
179
+
180
+ result[1].attribute_loaded?(:notes).should be_true
181
+ result[1].attribute_loaded?(:trip_report).should be_true
182
+ result[1].attribute_loaded?(:miles).should be_false
183
+
184
+ result = repository(ADAPTER) { RowBoat.all.to_a }
185
+
186
+ result[0].attribute_loaded?(:trip_report).should be_false
187
+ result[0].attribute_loaded?(:miles).should be_false
188
+
189
+ result[1].trip_report.should_not be_nil
190
+ result[2].attribute_loaded?(:miles).should be_true
191
+ end
192
+
193
+ it "should lazy load on Property#set" do
194
+ repository(ADAPTER) do
195
+ boat = RowBoat.first
196
+ boat.attribute_loaded?(:notes).should be_false
197
+ boat.notes = 'New Note'
198
+ boat.original_values[:notes].should == "Note"
199
+ end
200
+ end
201
+ end
202
+
203
+ describe 'defaults' do
204
+ before :all do
205
+ class ::Catamaran
206
+ include DataMapper::Resource
207
+ property :id, Serial
208
+ property :name, String
209
+
210
+ # Boolean
211
+ property :could_be_bool0, TrueClass, :default => true
212
+ property :could_be_bool1, TrueClass, :default => false
213
+ end
214
+
215
+ repository(ADAPTER){ Catamaran.auto_migrate!(ADAPTER) }
216
+ end
217
+
218
+ before :each do
219
+ @cat = Catamaran.new
220
+ end
221
+
222
+ it "should have defaults" do
223
+ @cat.could_be_bool0.should == true
224
+ @cat.could_be_bool1.should_not be_nil
225
+ @cat.could_be_bool1.should == false
226
+
227
+ @cat.name = 'Mary Mayweather'
228
+
229
+ repository(ADAPTER) do
230
+ @cat.save
231
+
232
+ cat = Catamaran.first
233
+ cat.could_be_bool0.should == true
234
+ cat.could_be_bool1.should_not be_nil
235
+ cat.could_be_bool1.should == false
236
+ cat.destroy
237
+ end
238
+
239
+ end
240
+
241
+ it "should have defaults even with creates" do
242
+ repository(ADAPTER) do
243
+ Catamaran.create(:name => 'Jingle All The Way')
244
+ cat = Catamaran.first
245
+ cat.name.should == 'Jingle All The Way'
246
+ cat.could_be_bool0.should == true
247
+ cat.could_be_bool1.should_not be_nil
248
+ cat.could_be_bool1.should == false
249
+ end
250
+ end
251
+ end
252
+ end
253
+ end
@@ -0,0 +1,514 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ if ADAPTER
4
+ module QuerySpec
5
+ class SailBoat
6
+ include DataMapper::Resource
7
+
8
+ property :id, Serial
9
+ property :name, String
10
+ property :port, String
11
+ property :captain, String
12
+
13
+ def self.default_repository_name
14
+ ADAPTER
15
+ end
16
+ end
17
+
18
+ class Permission
19
+ include DataMapper::Resource
20
+
21
+ property :id, Serial
22
+ property :user_id, Integer
23
+ property :resource_id, Integer
24
+ property :resource_type, String
25
+ property :token, String
26
+
27
+ def self.default_repository_name
28
+ ADAPTER
29
+ end
30
+ end
31
+
32
+ class Region
33
+ include DataMapper::Resource
34
+
35
+ property :id, Serial
36
+ property :name, String
37
+ property :type, String
38
+
39
+ def self.default_repository_name
40
+ ADAPTER
41
+ end
42
+ end
43
+
44
+ class Factory
45
+ include DataMapper::Resource
46
+
47
+ property :id, Serial
48
+ property :region_id, Integer
49
+ property :name, String
50
+
51
+ repository(:mock) do
52
+ property :land, String
53
+ end
54
+
55
+ belongs_to :region
56
+
57
+ def self.default_repository_name
58
+ ADAPTER
59
+ end
60
+ end
61
+
62
+ class Vehicle
63
+ include DataMapper::Resource
64
+
65
+ property :id, Serial
66
+ property :factory_id, Integer
67
+ property :name, String
68
+
69
+ belongs_to :factory
70
+
71
+ def self.default_repository_name
72
+ ADAPTER
73
+ end
74
+ end
75
+
76
+ class Group
77
+ include DataMapper::Resource
78
+ property :id, Serial
79
+ property :name, String
80
+ end
81
+ end
82
+
83
+ module Namespace
84
+ class Region
85
+ include DataMapper::Resource
86
+
87
+ property :id, Serial
88
+ property :name, String
89
+
90
+ def self.default_repository_name
91
+ ADAPTER
92
+ end
93
+ end
94
+
95
+ class Factory
96
+ include DataMapper::Resource
97
+
98
+ property :id, Serial
99
+ property :region_id, Integer
100
+ property :name, String
101
+
102
+ repository(:mock) do
103
+ property :land, String
104
+ end
105
+
106
+ belongs_to :region
107
+
108
+ def self.default_repository_name
109
+ ADAPTER
110
+ end
111
+ end
112
+
113
+ class Vehicle
114
+ include DataMapper::Resource
115
+ property :id, Serial
116
+ property :factory_id, Integer
117
+ property :name, String
118
+
119
+ belongs_to :factory
120
+
121
+ def self.default_repository_name
122
+ ADAPTER
123
+ end
124
+ end
125
+ end
126
+
127
+ describe DataMapper::Query, "with #{ADAPTER}" do
128
+ before do
129
+ @query = DataMapper::Query.new(repository(ADAPTER), QuerySpec::SailBoat)
130
+ end
131
+
132
+ it 'should be serializable with Marshal' do
133
+ Marshal.load(Marshal.dump(@query)).should == @query
134
+ end
135
+
136
+ describe '#unique' do
137
+ include LoggingHelper
138
+
139
+ before(:each) do
140
+ QuerySpec::SailBoat.auto_migrate!
141
+
142
+ QuerySpec::SailBoat.create(:name => 'A', :port => 'C')
143
+ QuerySpec::SailBoat.create(:name => 'B', :port => 'B')
144
+ QuerySpec::SailBoat.create(:name => 'C', :port => 'A')
145
+ end
146
+
147
+ def parse_statement(log)
148
+ log.readlines.join.chomp.split(' ~ ').last.sub(/\A\(\d+\.\d+\)\s+/, '')
149
+ end
150
+
151
+ describe 'when true' do
152
+ if [ :postgres, :sqlite3, :mysql ].include?(ADAPTER)
153
+ it 'should add a GROUP BY to the SQL query' do
154
+ logger do |log|
155
+ QuerySpec::SailBoat.all(:unique => true, :fields => [ :id ]).to_a
156
+
157
+ case ADAPTER
158
+ when :postgres, :sqlite3
159
+ parse_statement(log).should == 'SELECT "id" FROM "query_spec_sail_boats" GROUP BY "id" ORDER BY "id"'
160
+ when :mysql
161
+ parse_statement(log).should == 'SELECT `id` FROM `query_spec_sail_boats` GROUP BY `id` ORDER BY `id`'
162
+ end
163
+ end
164
+ end
165
+
166
+ it 'should not add a GROUP BY to the SQL query if no field is a Property' do
167
+ operator = DataMapper::Query::Operator.new(:thing, :test)
168
+
169
+ # make the operator act like a Property
170
+ class << operator
171
+ property = QuerySpec::SailBoat.properties[:id]
172
+ (property.methods - (public_instance_methods - %w[ type ])).each do |method|
173
+ define_method(method) do |*args|
174
+ property.send(method, *args)
175
+ end
176
+ end
177
+ end
178
+
179
+ operator.should_not be_kind_of(DataMapper::Property)
180
+
181
+ logger do |log|
182
+ QuerySpec::SailBoat.all(:unique => true, :fields => [ operator ]).to_a
183
+
184
+ case ADAPTER
185
+ when :postgres, :sqlite3
186
+ parse_statement(log).should == 'SELECT "id" FROM "query_spec_sail_boats" ORDER BY "id"'
187
+ when :mysql
188
+ parse_statement(log).should == 'SELECT `id` FROM `query_spec_sail_boats` ORDER BY `id`'
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
194
+
195
+ describe 'when false' do
196
+ if [ :postgres, :sqlite3, :mysql ].include?(ADAPTER)
197
+ it 'should not add a GROUP BY to the SQL query' do
198
+ logger do |log|
199
+ QuerySpec::SailBoat.all(:unique => false, :fields => [ :id ]).to_a
200
+
201
+ case ADAPTER
202
+ when :postgres, :sqlite3
203
+ parse_statement(log).should == 'SELECT "id" FROM "query_spec_sail_boats" ORDER BY "id"'
204
+ when :mysql
205
+ parse_statement(log).should == 'SELECT `id` FROM `query_spec_sail_boats` ORDER BY `id`'
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end
212
+
213
+ describe 'when ordering' do
214
+ before(:each) do
215
+ QuerySpec::SailBoat.auto_migrate!
216
+
217
+ QuerySpec::SailBoat.create(:name => 'A', :port => 'C')
218
+ QuerySpec::SailBoat.create(:name => 'B', :port => 'B')
219
+ QuerySpec::SailBoat.create(:name => 'C', :port => 'A')
220
+ end
221
+
222
+ it "should find by conditions" do
223
+ lambda do
224
+ repository(ADAPTER) do
225
+ QuerySpec::SailBoat.first(:conditions => [ 'name = ?', 'B' ])
226
+ end
227
+ end.should_not raise_error
228
+
229
+ lambda do
230
+ repository(ADAPTER) do
231
+ QuerySpec::SailBoat.first(:conditions => [ 'name = ?', 'A' ])
232
+ end
233
+ end.should_not raise_error
234
+ end
235
+
236
+ it "should find by conditions passed in as hash" do
237
+ repository(ADAPTER) do
238
+ QuerySpec::SailBoat.create(:name => "couldbe@email.com", :port => 'wee')
239
+
240
+ find = QuerySpec::SailBoat.first(:name => 'couldbe@email.com')
241
+ find.name.should == 'couldbe@email.com'
242
+
243
+ find = QuerySpec::SailBoat.first(:name => 'couldbe@email.com', :port.not => nil)
244
+ find.should_not be_nil
245
+ find.port.should_not be_nil
246
+ find.name.should == 'couldbe@email.com'
247
+ end
248
+ end
249
+
250
+ it "should find by conditions passed in a range" do
251
+ repository(ADAPTER) do
252
+ find = QuerySpec::SailBoat.all(:id => 0..2)
253
+ find.should_not be_nil
254
+ find.should have(2).entries
255
+
256
+ find = QuerySpec::SailBoat.all(:id.not => 0..2)
257
+ find.should have(1).entries
258
+ end
259
+ end
260
+
261
+ it "should find by conditions passed in as an array" do
262
+ repository(ADAPTER) do
263
+ find = QuerySpec::SailBoat.all(:id => [1,2])
264
+ find.should_not be_nil
265
+ find.should have(2).entries
266
+
267
+ find = QuerySpec::SailBoat.all(:id.not => [1,2])
268
+ find.should have(1).entries
269
+ end
270
+ end
271
+
272
+ describe "conditions passed in as an empty array" do
273
+ it "should work when id is an empty Array" do
274
+ repository(ADAPTER) do
275
+ find = QuerySpec::SailBoat.all(:id => [])
276
+ find.should have(0).entries
277
+ end
278
+ end
279
+
280
+ it "should work when id is NOT an empty Array" do
281
+ repository(ADAPTER) do
282
+ find = QuerySpec::SailBoat.all(:id.not => [])
283
+ find.should have(3).entries
284
+ end
285
+ end
286
+
287
+ it "should work when id is an empty Array and other conditions are specified" do
288
+ repository(ADAPTER) do
289
+ find = QuerySpec::SailBoat.all(:id => [], :name => "A")
290
+ find.should have(0).entries
291
+ end
292
+ end
293
+
294
+ it "should work when id is NOT an empty Array and other conditions are specified" do
295
+ repository(ADAPTER) do
296
+ find = QuerySpec::SailBoat.all(:id.not => [], :name => "A")
297
+ find.should have(1).entries
298
+ end
299
+ end
300
+
301
+ it "should work when id is NOT an empty Array and other Array conditions are specified" do
302
+ repository(ADAPTER) do
303
+ find = QuerySpec::SailBoat.all(:id.not => [], :name => ["A", "B"])
304
+ find.should have(2).entries
305
+ end
306
+ end
307
+ end
308
+
309
+ it "should order results" do
310
+ repository(ADAPTER) do
311
+ result = QuerySpec::SailBoat.all(:order => [
312
+ DataMapper::Query::Direction.new(QuerySpec::SailBoat.properties[:name], :asc)
313
+ ])
314
+ result[0].id.should == 1
315
+
316
+ result = QuerySpec::SailBoat.all(:order => [
317
+ DataMapper::Query::Direction.new(QuerySpec::SailBoat.properties[:port], :asc)
318
+ ])
319
+ result[0].id.should == 3
320
+
321
+ result = QuerySpec::SailBoat.all(:order => [
322
+ DataMapper::Query::Direction.new(QuerySpec::SailBoat.properties[:name], :asc),
323
+ DataMapper::Query::Direction.new(QuerySpec::SailBoat.properties[:port], :asc)
324
+ ])
325
+ result[0].id.should == 1
326
+
327
+ result = QuerySpec::SailBoat.all(:order => [
328
+ QuerySpec::SailBoat.properties[:name],
329
+ DataMapper::Query::Direction.new(QuerySpec::SailBoat.properties[:port], :asc)
330
+ ])
331
+ result[0].id.should == 1
332
+
333
+ result = QuerySpec::SailBoat.all(:order => [ :name ])
334
+ result[0].id.should == 1
335
+
336
+ result = QuerySpec::SailBoat.all(:order => [ :name.desc ])
337
+ result[0].id.should == 3
338
+ end
339
+ end
340
+ end
341
+
342
+ describe 'when sub-selecting' do
343
+ before(:each) do
344
+ [ QuerySpec::SailBoat, QuerySpec::Permission ].each { |m| m.auto_migrate! }
345
+
346
+ QuerySpec::SailBoat.create(:id => 1, :name => "Fantasy I", :port => "Cape Town", :captain => 'Joe')
347
+ QuerySpec::SailBoat.create(:id => 2, :name => "Royal Flush II", :port => "Cape Town", :captain => 'James')
348
+ QuerySpec::SailBoat.create(:id => 3, :name => "Infringer III", :port => "Cape Town", :captain => 'Jason')
349
+
350
+ #User 1 permission -- read boat 1 & 2
351
+ QuerySpec::Permission.create(:id => 1, :user_id => 1, :resource_id => 1, :resource_type => 'SailBoat', :token => 'READ')
352
+ QuerySpec::Permission.create(:id => 2, :user_id => 1, :resource_id => 2, :resource_type => 'SailBoat', :token => 'READ')
353
+
354
+ #User 2 permission -- read boat 2 & 3
355
+ QuerySpec::Permission.create(:id => 3, :user_id => 2, :resource_id => 2, :resource_type => 'SailBoat', :token => 'READ')
356
+ QuerySpec::Permission.create(:id => 4, :user_id => 2, :resource_id => 3, :resource_type => 'SailBoat', :token => 'READ')
357
+ end
358
+
359
+ it 'should accept a DM::Query as a value of a condition' do
360
+ # User 1
361
+ acl = DataMapper::Query.new(repository(ADAPTER), QuerySpec::Permission, :user_id => 1, :resource_type => 'SailBoat', :token => 'READ', :fields => [ :resource_id ])
362
+ query = { :port => 'Cape Town', :id => acl, :captain.like => 'J%', :order => [ :id ] }
363
+ boats = repository(ADAPTER) { QuerySpec::SailBoat.all(query) }
364
+ boats.should have(2).entries
365
+ boats.entries[0].id.should == 1
366
+ boats.entries[1].id.should == 2
367
+
368
+ # User 2
369
+ acl = DataMapper::Query.new(repository(ADAPTER), QuerySpec::Permission, :user_id => 2, :resource_type => 'SailBoat', :token => 'READ', :fields => [ :resource_id ])
370
+ query = { :port => 'Cape Town', :id => acl, :captain.like => 'J%', :order => [ :id ] }
371
+ boats = repository(ADAPTER) { QuerySpec::SailBoat.all(query) }
372
+
373
+ boats.should have(2).entries
374
+ boats.entries[0].id.should == 2
375
+ boats.entries[1].id.should == 3
376
+ end
377
+
378
+ it 'when value is NOT IN another query' do
379
+ # Boats that User 1 Cannot see
380
+ acl = DataMapper::Query.new(repository(ADAPTER), QuerySpec::Permission, :user_id => 1, :resource_type => 'SailBoat', :token => 'READ', :fields => [ :resource_id ])
381
+ query = { :port => 'Cape Town', :id.not => acl, :captain.like => 'J%' }
382
+ boats = repository(ADAPTER) { QuerySpec::SailBoat.all(query) }
383
+ boats.should have(1).entries
384
+ boats.entries[0].id.should == 3
385
+ end
386
+ end # describe sub-selecting
387
+
388
+ describe 'when linking associated objects' do
389
+ before(:each) do
390
+ [ QuerySpec::Region, QuerySpec::Factory, QuerySpec::Vehicle ].each { |m| m.auto_migrate! }
391
+
392
+ QuerySpec::Region.create(:id => 1, :name => 'North West', :type => 'commercial')
393
+ QuerySpec::Factory.create(:id => 2000, :region_id => 1, :name => 'North West Plant')
394
+ QuerySpec::Vehicle.create(:id => 1, :factory_id => 2000, :name => '10 ton delivery truck')
395
+
396
+ Namespace::Region.auto_migrate!
397
+ Namespace::Factory.auto_migrate!
398
+ Namespace::Vehicle.auto_migrate!
399
+
400
+ Namespace::Region.create(:id => 1, :name => 'North West')
401
+ Namespace::Factory.create(:id => 2000, :region_id => 1, :name => 'North West Plant')
402
+ Namespace::Vehicle.create(:id => 1, :factory_id => 2000, :name => '10 ton delivery truck')
403
+ end
404
+
405
+ it 'should require that all properties in :fields and all :links come from the same repository' #do
406
+ # land = QuerySpec::Factory.properties(:mock)[:land]
407
+ # fields = []
408
+ # QuerySpec::Vehicle.properties(ADAPTER).map do |property|
409
+ # fields << property
410
+ # end
411
+ # fields << land
412
+ #
413
+ # lambda{
414
+ # begin
415
+ # results = repository(ADAPTER) { QuerySpec::Vehicle.all(:links => [ :factory ], :fields => fields) }
416
+ # rescue RuntimeError
417
+ # $!.message.should == "Property QuerySpec::Factory.land not available in repository #{ADAPTER}"
418
+ # raise $!
419
+ # end
420
+ # }.should raise_error(RuntimeError)
421
+ #end
422
+
423
+ it 'should accept a DM::Assoc::Relationship as a link' do
424
+ factory = DataMapper::Associations::Relationship.new(
425
+ :factory,
426
+ ADAPTER,
427
+ QuerySpec::Vehicle,
428
+ QuerySpec::Factory,
429
+ { :child_key => [ :factory_id ], :parent_key => [ :id ] }
430
+ )
431
+ results = repository(ADAPTER) { QuerySpec::Vehicle.all(:links => [ factory ]) }
432
+ results.should have(1).entries
433
+ end
434
+
435
+ it 'should accept a symbol of an association name as a link' do
436
+ results = repository(ADAPTER) { QuerySpec::Vehicle.all(:links => [ :factory ]) }
437
+ results.should have(1).entries
438
+ end
439
+
440
+ it 'should accept a string of an association name as a link' do
441
+ results = repository(ADAPTER) { QuerySpec::Vehicle.all(:links => [ 'factory' ]) }
442
+ results.should have(1).entries
443
+ end
444
+
445
+ it 'should accept a mixture of items as a set of links' do
446
+ region = DataMapper::Associations::Relationship.new(
447
+ :region,
448
+ ADAPTER,
449
+ QuerySpec::Factory,
450
+ QuerySpec::Region,
451
+ { :child_key => [ :region_id ], :parent_key => [ :id ] }
452
+ )
453
+ results = repository(ADAPTER) { QuerySpec::Vehicle.all(:links => [ 'factory', region ]) }
454
+ results.should have(1).entries
455
+ end
456
+
457
+ it 'should only accept a DM::Assoc::Relationship, String & Symbol as a link' do
458
+ lambda{
459
+ DataMapper::Query.new(repository(ADAPTER), QuerySpec::Vehicle, :links => [1])
460
+ }.should raise_error(ArgumentError)
461
+ end
462
+
463
+ it 'should have a association by the name of the Symbol or String' do
464
+ lambda{
465
+ DataMapper::Query.new(repository(ADAPTER), QuerySpec::Vehicle, :links => [ 'Sailing' ])
466
+ }.should raise_error(ArgumentError)
467
+
468
+ lambda{
469
+ DataMapper::Query.new(repository(ADAPTER), QuerySpec::Vehicle, :links => [ :sailing ])
470
+ }.should raise_error(ArgumentError)
471
+ end
472
+
473
+ it 'should create an n-level query path' do
474
+ QuerySpec::Vehicle.factory.region.model.should == QuerySpec::Region
475
+ QuerySpec::Vehicle.factory.region.name.property.should == QuerySpec::Region.properties(QuerySpec::Region.repository.name)[ :name ]
476
+ end
477
+
478
+ it 'should accept a DM::QueryPath as the key to a condition' do
479
+ vehicle = QuerySpec::Vehicle.first(QuerySpec::Vehicle.factory.region.name => 'North West')
480
+ vehicle.name.should == '10 ton delivery truck'
481
+
482
+ vehicle = Namespace::Vehicle.first(Namespace::Vehicle.factory.region.name => 'North West')
483
+ vehicle.name.should == '10 ton delivery truck'
484
+ end
485
+
486
+ it "should accept a string representing a DM::QueryPath as they key to a condition" do
487
+ vehicle = QuerySpec::Vehicle.first("factory.region.name" => 'North West')
488
+ vehicle.name.should == '10 ton delivery truck'
489
+ end
490
+
491
+ it "should accept 'id' and 'type' as endpoints on ah DM::QueryPath" do
492
+ vehicle = QuerySpec::Vehicle.first( QuerySpec::Vehicle.factory.region.type => 'commercial' )
493
+ vehicle.name.should == '10 ton delivery truck'
494
+ vehicle = QuerySpec::Vehicle.first( QuerySpec::Vehicle.factory.region.id => 1 )
495
+ vehicle.name.should == '10 ton delivery truck'
496
+ end
497
+
498
+ it 'should auto generate the link if a DM::Property from a different resource is in the :fields option'
499
+
500
+ it 'should create links with composite keys'
501
+
502
+ it 'should eager load associations' do
503
+ repository(ADAPTER) do
504
+ vehicle = QuerySpec::Vehicle.first(:includes => [ QuerySpec::Vehicle.factory ])
505
+ end
506
+ end
507
+
508
+ it "should behave when using mocks" do
509
+ QuerySpec::Group.should_receive(:all).with(:order => [ :id.asc ])
510
+ QuerySpec::Group.all(:order => [ :id.asc ])
511
+ end
512
+ end # describe links
513
+ end # DM::Query
514
+ end