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
@@ -0,0 +1,58 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe "DataMapper::DependencyQueue" do
4
+ before :each do
5
+ @q = DataMapper::DependencyQueue.new
6
+ @dependencies = @q.instance_variable_get("@dependencies")
7
+ end
8
+
9
+ describe "#initialize" do
10
+ describe "@dependencies" do
11
+ it "should be a hash after initialize" do
12
+ @dependencies.should be_a_kind_of(Hash)
13
+ end
14
+
15
+ it "should set value to [] when new key is accessed" do
16
+ @dependencies['New Key'].should == []
17
+ end
18
+ end
19
+ end
20
+
21
+ describe "#add" do
22
+ it "should store the supplied callback in @dependencies" do
23
+ @q.add('MissingConstant') { true }
24
+ @dependencies['MissingConstant'].first.call.should == true
25
+ end
26
+ end
27
+
28
+ describe "#resolve!" do
29
+ describe "(when dependency is not defined)" do
30
+ it "should not alter @dependencies" do
31
+ @q.add('MissingConstant') { true }
32
+ old_dependencies = @dependencies.dup
33
+ @q.resolve!
34
+ old_dependencies.should == @dependencies
35
+ end
36
+ end
37
+
38
+ describe "(when dependency is defined)" do
39
+ before :each do
40
+ @q.add('MissingConstant') { |klass| klass.instance_variable_set("@resolved", true) } # add before MissingConstant is loaded
41
+
42
+ class MissingConstant
43
+ end
44
+ end
45
+
46
+ it "should execute stored callbacks" do
47
+ @q.resolve!
48
+ MissingConstant.instance_variable_get("@resolved").should == true
49
+ end
50
+
51
+ it "should clear @dependencies" do
52
+ @q.resolve!
53
+ @dependencies['MissingConstant'].should be_empty
54
+ end
55
+ end
56
+ end
57
+
58
+ end
@@ -13,6 +13,9 @@ if ADAPTER
13
13
  property :name, String
14
14
  property :type, Discriminator
15
15
  end
16
+
17
+ class STIDescendant < STI
18
+ end
16
19
  end
17
20
 
18
21
  describe "DataMapper::Model with #{ADAPTER}" do
@@ -20,18 +23,29 @@ if ADAPTER
20
23
  repository(ADAPTER) do
21
24
  ModelSpec::STI.auto_migrate!
22
25
  end
23
- end
24
26
 
25
- describe '.new' do
26
- before :all do
27
- @planet = DataMapper::Model.new('planet') do
28
- property :name, String, :key => true
29
- property :distance, Integer
30
- end
27
+ @planet = DataMapper::Model.new('planet') do
28
+ def self.default_repository_name; ADAPTER end
29
+ property :name, String, :key => true
30
+ property :distance, Integer
31
+ end
31
32
 
32
- @planet.auto_migrate!(ADAPTER)
33
+ @moon = DataMapper::Model.new('moon') do
34
+ def self.default_repository_name; ADAPTER end
35
+ property :id, DM::Serial
36
+ property :name, String
33
37
  end
34
38
 
39
+ @planet.auto_migrate!(ADAPTER)
40
+ @moon.auto_migrate!(ADAPTER)
41
+
42
+ repository(ADAPTER) do
43
+ @moon.create(:name => "Charon")
44
+ @moon.create(:name => "Phobos")
45
+ end
46
+ end
47
+
48
+ describe '.new' do
35
49
  it 'should be able to persist' do
36
50
  repository(ADAPTER) do
37
51
  pluto = @planet.new
@@ -46,6 +60,51 @@ if ADAPTER
46
60
  end
47
61
  end
48
62
 
63
+ describe ".get" do
64
+ include LoggingHelper
65
+
66
+ it "should typecast key" do
67
+ resource = nil
68
+ lambda {
69
+ repository(ADAPTER) do
70
+ resource = @moon.get("1")
71
+ end
72
+ }.should_not raise_error
73
+ resource.should be_kind_of(DataMapper::Resource)
74
+ end
75
+
76
+ it "should use the identity map within a repository block" do
77
+ logger do |log|
78
+ repository(ADAPTER) do
79
+ @moon.get("1")
80
+ @moon.get(1)
81
+ end
82
+ log.readlines.size.should == 1
83
+ end
84
+ end
85
+
86
+ it "should not use the identity map outside a repository block" do
87
+ logger do |log|
88
+ @moon.get(1)
89
+ @moon.get(1)
90
+ log.readlines.size.should == 2
91
+ end
92
+ end
93
+ end
94
+
95
+ describe ".base_model" do
96
+ describe "(when called on base model)" do
97
+ it "should refer to itself" do
98
+ ModelSpec::STI.base_model.should == ModelSpec::STI
99
+ end
100
+ end
101
+ describe "(when called on descendant model)" do
102
+ it "should refer to the base model" do
103
+ ModelSpec::STIDescendant.base_model.should == ModelSpec::STI.base_model
104
+ end
105
+ end
106
+ end
107
+
49
108
  it 'should provide #load' do
50
109
  ModelSpec::STI.should respond_to(:load)
51
110
  end
@@ -146,7 +146,7 @@ if HAS_POSTGRES
146
146
  pending "This works, but no create-schema support in PostgresAdapter to easily test with"
147
147
  lambda do
148
148
  repository(:postgres) do
149
- Voyager.create!(:age => 1_000)
149
+ Voyager.create(:age => 1_000)
150
150
  end
151
151
  end.should_not raise_error
152
152
  end
@@ -388,9 +388,9 @@ if HAS_POSTGRES
388
388
  SailBoat.auto_migrate!(:postgres)
389
389
 
390
390
  repository(:postgres) do
391
- SailBoat.create!(:id => 1, :name => "A", :port => "C")
392
- SailBoat.create!(:id => 2, :name => "B", :port => "B")
393
- SailBoat.create!(:id => 3, :name => "C", :port => "A")
391
+ SailBoat.create(:id => 1, :name => "A", :port => "C")
392
+ SailBoat.create(:id => 2, :name => "B", :port => "B")
393
+ SailBoat.create(:id => 3, :name => "C", :port => "A")
394
394
  end
395
395
  end
396
396
 
@@ -436,9 +436,9 @@ if HAS_POSTGRES
436
436
  SailBoat.auto_migrate!(:postgres)
437
437
 
438
438
  repository(:postgres) do
439
- SailBoat.create!(:id => 1, :notes=>'Note',:trip_report=>'Report',:miles=>23)
440
- SailBoat.create!(:id => 2, :notes=>'Note',:trip_report=>'Report',:miles=>23)
441
- SailBoat.create!(:id => 3, :notes=>'Note',:trip_report=>'Report',:miles=>23)
439
+ SailBoat.create(:id => 1, :notes=>'Note',:trip_report=>'Report',:miles=>23)
440
+ SailBoat.create(:id => 2, :notes=>'Note',:trip_report=>'Report',:miles=>23)
441
+ SailBoat.create(:id => 3, :notes=>'Note',:trip_report=>'Report',:miles=>23)
442
442
  end
443
443
  end
444
444
 
@@ -480,7 +480,7 @@ if HAS_POSTGRES
480
480
 
481
481
  repository(:postgres) do
482
482
  100.times do
483
- SerialFinderSpec.create!(:sample => rand.to_s)
483
+ SerialFinderSpec.create(:sample => rand.to_s)
484
484
  end
485
485
  end
486
486
  end
@@ -507,8 +507,9 @@ if HAS_POSTGRES
507
507
  sfs.should be_a_kind_of(SerialFinderSpec)
508
508
  sfs.should_not be_a_new_record
509
509
 
510
- sfs.instance_variables.should_not include('@sample')
511
- sfs.sample.should_not be_nil
510
+ sfs.attribute_loaded?(:sample).should be_false
511
+ sfs.sample
512
+ sfs.attribute_loaded?(:sample).should be_true
512
513
  end
513
514
 
514
515
  it "should translate an Array to an IN clause" do
@@ -529,6 +530,7 @@ if HAS_POSTGRES
529
530
  before :all do
530
531
  class Engine
531
532
  include DataMapper::Resource
533
+ def self.default_repository_name; :postgres end
532
534
 
533
535
  property :id, Serial
534
536
  property :name, String
@@ -536,14 +538,13 @@ if HAS_POSTGRES
536
538
 
537
539
  class Yard
538
540
  include DataMapper::Resource
541
+ def self.default_repository_name; :postgres end
539
542
 
540
543
  property :id, Serial
541
544
  property :name, String
542
545
  property :engine_id, Integer
543
546
 
544
- repository(:postgres) do
545
- belongs_to :engine
546
- end
547
+ belongs_to :engine
547
548
  end
548
549
  end
549
550
 
@@ -591,7 +592,7 @@ if HAS_POSTGRES
591
592
  it 'should save the association key in the child' do
592
593
  repository(:postgres) do
593
594
  e = Engine.first(:id => 2)
594
- Yard.create!(:id => 2, :name => 'yard2', :engine => e)
595
+ Yard.create(:id => 2, :name => 'yard2', :engine => e)
595
596
  end
596
597
 
597
598
  repository(:postgres) do
@@ -618,25 +619,23 @@ if HAS_POSTGRES
618
619
  before :all do
619
620
  class Host
620
621
  include DataMapper::Resource
622
+ def self.default_repository_name; :postgres end
621
623
 
622
624
  property :id, Serial
623
625
  property :name, String
624
626
 
625
- repository(:postgres) do
626
- has n, :slices
627
- end
627
+ has n, :slices
628
628
  end
629
629
 
630
630
  class Slice
631
631
  include DataMapper::Resource
632
+ def self.default_repository_name; :postgres end
632
633
 
633
634
  property :id, Serial
634
635
  property :name, String
635
636
  property :host_id, Integer
636
637
 
637
- repository(:postgres) do
638
- belongs_to :host
639
- end
638
+ belongs_to :host
640
639
  end
641
640
  end
642
641
 
@@ -13,7 +13,7 @@ if ADAPTER
13
13
  property :id, Serial
14
14
  property :name, String, :track => :set # :track default is :get for mutable types
15
15
  property :notes, DataMapper::Types::Text
16
- property :age, Integer # :track default is :set for mutable types
16
+ property :age, Integer # :track default is :set for immutable types
17
17
  property :rating, Integer
18
18
  property :location, String
19
19
  property :lead, TrueClass, :track => :load
@@ -78,7 +78,7 @@ if ADAPTER
78
78
 
79
79
  it "should track on :load" do
80
80
  repository(ADAPTER) do
81
- jan = Actor.create!(:name => 'jan', :lead => true)
81
+ jan = Actor.create(:name => 'jan', :lead => true)
82
82
  jan.lead = false
83
83
  jan.original_values[:lead].should be_true
84
84
  jan.dirty?.should == true
@@ -94,7 +94,7 @@ if ADAPTER
94
94
  it "should track on :hash" do
95
95
  cv = { 2005 => "Othello" }
96
96
  repository(ADAPTER) do
97
- tom = Actor.create!(:name => 'tom', :cv => cv)
97
+ tom = Actor.create(:name => 'tom', :cv => cv)
98
98
  end
99
99
  repository(ADAPTER) do
100
100
  tom = Actor.first(:name => 'tom')
@@ -109,7 +109,7 @@ if ADAPTER
109
109
 
110
110
  it "should track with lazy text fields (#342)" do
111
111
  repository(ADAPTER) do
112
- tim = Actor.create!(:name => 'tim')
112
+ tim = Actor.create(:name => 'tim')
113
113
  end
114
114
  repository(ADAPTER) do
115
115
  tim = Actor.first(:name => 'tim')
@@ -142,9 +142,9 @@ if ADAPTER
142
142
  RowBoat.auto_migrate!(ADAPTER)
143
143
 
144
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)
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
148
  end
149
149
  end
150
150
 
@@ -169,6 +169,15 @@ if ADAPTER
169
169
  result[1].trip_report.should_not be_nil
170
170
  result[2].attribute_loaded?(:miles).should be_true
171
171
  end
172
+
173
+ it "should lazy load on Property#set" do
174
+ repository(ADAPTER) do
175
+ boat = RowBoat.first
176
+ boat.attribute_loaded?(:notes).should be_false
177
+ boat.notes = 'New Note'
178
+ boat.original_values[:notes].should == "Note"
179
+ end
180
+ end
172
181
  end
173
182
 
174
183
  describe 'defaults' do
@@ -211,7 +220,7 @@ if ADAPTER
211
220
 
212
221
  it "should have defaults even with creates" do
213
222
  repository(ADAPTER) do
214
- Catamaran.create!(:name => 'Jingle All The Way')
223
+ Catamaran.create(:name => 'Jingle All The Way')
215
224
  cat = Catamaran.first
216
225
  cat.name.should == 'Jingle All The Way'
217
226
  cat.could_be_bool0.should == true
@@ -1,49 +1,238 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
2
 
3
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
+
4
127
  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
128
+ describe '#unique' do
129
+ include LoggingHelper
130
+
131
+ before do
132
+ QuerySpec::SailBoat.auto_migrate!
133
+
134
+ QuerySpec::SailBoat.create(:name => 'A', :port => 'C')
135
+ QuerySpec::SailBoat.create(:name => 'B', :port => 'B')
136
+ QuerySpec::SailBoat.create(:name => 'C', :port => 'A')
137
+ end
138
+
139
+ def parse_statement(log)
140
+ log.readlines.join.chomp.split(' ~ ').last
141
+ end
142
+
143
+ describe 'when true' do
144
+ if [ :postgres, :sqlite3, :mysql ].include?(ADAPTER)
145
+ it 'should add a GROUP BY to the SQL query' do
146
+ logger do |log|
147
+ QuerySpec::SailBoat.all(:unique => true, :fields => [ :id ]).to_a
148
+
149
+ case ADAPTER
150
+ when :postgres, :sqlite3
151
+ parse_statement(log).should == 'SELECT "id" FROM "query_spec_sail_boats" GROUP BY "id" ORDER BY "id"'
152
+ when :mysql
153
+ parse_statement(log).should == 'SELECT `id` FROM `query_spec_sail_boats` GROUP BY `id` ORDER BY `id`'
154
+ end
155
+ end
156
+ end
157
+
158
+ it 'should not add a GROUP BY to the SQL query if no field is a Property' do
159
+ operator = DataMapper::Query::Operator.new(:thing, :test)
160
+
161
+ # make the operator act like a Property
162
+ class << operator
163
+ property = QuerySpec::SailBoat.properties[:id]
164
+ (property.methods - (public_instance_methods - %w[ type ])).each do |method|
165
+ define_method(method) do |*args|
166
+ property.send(method, *args)
167
+ end
168
+ end
169
+ end
170
+
171
+ operator.should_not be_kind_of(DataMapper::Property)
172
+
173
+ logger do |log|
174
+ QuerySpec::SailBoat.all(:unique => true, :fields => [ operator ]).to_a
175
+
176
+ case ADAPTER
177
+ when :postgres, :sqlite3
178
+ parse_statement(log).should == 'SELECT "id" FROM "query_spec_sail_boats" ORDER BY "id"'
179
+ when :mysql
180
+ parse_statement(log).should == 'SELECT `id` FROM `query_spec_sail_boats` ORDER BY `id`'
181
+ end
182
+ end
183
+ end
12
184
  end
13
185
  end
14
186
 
187
+ describe 'when false' do
188
+ if [ :postgres, :sqlite3, :mysql ].include?(ADAPTER)
189
+ it 'should not add a GROUP BY to the SQL query' do
190
+ logger do |log|
191
+ QuerySpec::SailBoat.all(:unique => false, :fields => [ :id ]).to_a
192
+
193
+ case ADAPTER
194
+ when :postgres, :sqlite3
195
+ parse_statement(log).should == 'SELECT "id" FROM "query_spec_sail_boats" ORDER BY "id"'
196
+ when :mysql
197
+ parse_statement(log).should == 'SELECT `id` FROM `query_spec_sail_boats` ORDER BY `id`'
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ describe 'when ordering' do
15
206
  before do
16
- SailBoat.auto_migrate!(ADAPTER)
207
+ QuerySpec::SailBoat.auto_migrate!
17
208
 
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
209
+ QuerySpec::SailBoat.create(:name => 'A', :port => 'C')
210
+ QuerySpec::SailBoat.create(:name => 'B', :port => 'B')
211
+ QuerySpec::SailBoat.create(:name => 'C', :port => 'A')
23
212
  end
24
213
 
25
214
  it "should find by conditions" do
26
215
  lambda do
27
216
  repository(ADAPTER) do
28
- SailBoat.first(:conditions => ['name = ?', 'B'])
217
+ QuerySpec::SailBoat.first(:conditions => [ 'name = ?', 'B' ])
29
218
  end
30
219
  end.should_not raise_error
31
220
 
32
221
  lambda do
33
222
  repository(ADAPTER) do
34
- SailBoat.first(:conditions => ['name = ?', 'A'])
223
+ QuerySpec::SailBoat.first(:conditions => [ 'name = ?', 'A' ])
35
224
  end
36
225
  end.should_not raise_error
37
226
  end
38
227
 
39
228
  it "should find by conditions passed in as hash" do
40
229
  repository(ADAPTER) do
41
- SailBoat.create!(:name => "couldbe@email.com", :port => 'wee')
230
+ QuerySpec::SailBoat.create(:name => "couldbe@email.com", :port => 'wee')
42
231
 
43
- find = SailBoat.first(:name => 'couldbe@email.com')
232
+ find = QuerySpec::SailBoat.first(:name => 'couldbe@email.com')
44
233
  find.name.should == 'couldbe@email.com'
45
234
 
46
- find = SailBoat.first(:name => 'couldbe@email.com', :port.not => nil)
235
+ find = QuerySpec::SailBoat.first(:name => 'couldbe@email.com', :port.not => nil)
47
236
  find.should_not be_nil
48
237
  find.port.should_not be_nil
49
238
  find.name.should == 'couldbe@email.com'
@@ -52,100 +241,78 @@ if ADAPTER
52
241
 
53
242
  it "should find by conditions passed in a range" do
54
243
  repository(ADAPTER) do
55
- find = SailBoat.all(:id => 0..2)
244
+ find = QuerySpec::SailBoat.all(:id => 0..2)
56
245
  find.should_not be_nil
57
246
  find.should have(2).entries
58
247
 
59
- find = SailBoat.all(:id.not => 0..2)
248
+ find = QuerySpec::SailBoat.all(:id.not => 0..2)
60
249
  find.should have(1).entries
61
250
  end
62
251
  end
63
252
 
64
253
  it "should order results" do
65
254
  repository(ADAPTER) do
66
- result = SailBoat.all(:order => [
67
- DataMapper::Query::Direction.new(SailBoat.properties[:name], :asc)
255
+ result = QuerySpec::SailBoat.all(:order => [
256
+ DataMapper::Query::Direction.new(QuerySpec::SailBoat.properties[:name], :asc)
68
257
  ])
69
258
  result[0].id.should == 1
70
259
 
71
- result = SailBoat.all(:order => [
72
- DataMapper::Query::Direction.new(SailBoat.properties[:port], :asc)
260
+ result = QuerySpec::SailBoat.all(:order => [
261
+ DataMapper::Query::Direction.new(QuerySpec::SailBoat.properties[:port], :asc)
73
262
  ])
74
263
  result[0].id.should == 3
75
264
 
76
- result = SailBoat.all(:order => [
77
- DataMapper::Query::Direction.new(SailBoat.properties[:name], :asc),
78
- DataMapper::Query::Direction.new(SailBoat.properties[:port], :asc)
265
+ result = QuerySpec::SailBoat.all(:order => [
266
+ DataMapper::Query::Direction.new(QuerySpec::SailBoat.properties[:name], :asc),
267
+ DataMapper::Query::Direction.new(QuerySpec::SailBoat.properties[:port], :asc)
79
268
  ])
80
269
  result[0].id.should == 1
81
270
 
82
- result = SailBoat.all(:order => [
83
- SailBoat.properties[:name],
84
- DataMapper::Query::Direction.new(SailBoat.properties[:port], :asc)
271
+ result = QuerySpec::SailBoat.all(:order => [
272
+ QuerySpec::SailBoat.properties[:name],
273
+ DataMapper::Query::Direction.new(QuerySpec::SailBoat.properties[:port], :asc)
85
274
  ])
86
275
  result[0].id.should == 1
87
276
 
88
- result = SailBoat.all(:order => [:name])
277
+ result = QuerySpec::SailBoat.all(:order => [ :name ])
89
278
  result[0].id.should == 1
90
279
 
91
- result = SailBoat.all(:order => [:name.desc])
280
+ result = QuerySpec::SailBoat.all(:order => [ :name.desc ])
92
281
  result[0].id.should == 3
93
282
  end
94
283
  end
95
284
  end
96
285
 
97
286
  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
287
  before do
118
- Permission.auto_migrate!(ADAPTER)
119
- SailBoat.auto_migrate!(ADAPTER)
288
+ [ QuerySpec::SailBoat, QuerySpec::Permission ].each { |m| m.auto_migrate! }
120
289
 
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')
290
+ QuerySpec::SailBoat.create(:id => 1, :name => "Fantasy I", :port => "Cape Town", :captain => 'Joe')
291
+ QuerySpec::SailBoat.create(:id => 2, :name => "Royal Flush II", :port => "Cape Town", :captain => 'James')
292
+ QuerySpec::SailBoat.create(:id => 3, :name => "Infringer III", :port => "Cape Town", :captain => 'Jason')
125
293
 
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')
294
+ #User 1 permission -- read boat 1 & 2
295
+ QuerySpec::Permission.create(:id => 1, :user_id => 1, :resource_id => 1, :resource_type => 'SailBoat', :token => 'READ')
296
+ QuerySpec::Permission.create(:id => 2, :user_id => 1, :resource_id => 2, :resource_type => 'SailBoat', :token => 'READ')
129
297
 
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
298
+ #User 2 permission -- read boat 2 & 3
299
+ QuerySpec::Permission.create(:id => 3, :user_id => 2, :resource_id => 2, :resource_type => 'SailBoat', :token => 'READ')
300
+ QuerySpec::Permission.create(:id => 4, :user_id => 2, :resource_id => 3, :resource_type => 'SailBoat', :token => 'READ')
134
301
  end
135
302
 
136
303
  it 'should accept a DM::Query as a value of a condition' do
137
304
  # User 1
138
- acl = DataMapper::Query.new(repository(ADAPTER), Permission, :user_id => 1, :resource_type => 'SailBoat', :token => 'READ', :fields => [ :resource_id ])
305
+ acl = DataMapper::Query.new(repository(ADAPTER), QuerySpec::Permission, :user_id => 1, :resource_type => 'SailBoat', :token => 'READ', :fields => [ :resource_id ])
139
306
  query = { :port => 'Cape Town', :id => acl, :captain.like => 'J%', :order => [ :id ] }
140
- boats = repository(ADAPTER) { SailBoat.all(query) }
307
+ boats = repository(ADAPTER) { QuerySpec::SailBoat.all(query) }
141
308
  boats.should have(2).entries
142
309
  boats.entries[0].id.should == 1
143
310
  boats.entries[1].id.should == 2
144
311
 
145
312
  # User 2
146
- acl = DataMapper::Query.new(repository(ADAPTER), Permission, :user_id => 2, :resource_type => 'SailBoat', :token => 'READ', :fields => [ :resource_id ])
313
+ acl = DataMapper::Query.new(repository(ADAPTER), QuerySpec::Permission, :user_id => 2, :resource_type => 'SailBoat', :token => 'READ', :fields => [ :resource_id ])
147
314
  query = { :port => 'Cape Town', :id => acl, :captain.like => 'J%', :order => [ :id ] }
148
- boats = repository(ADAPTER) { SailBoat.all(query) }
315
+ boats = repository(ADAPTER) { QuerySpec::SailBoat.all(query) }
149
316
 
150
317
  boats.should have(2).entries
151
318
  boats.entries[0].id.should == 2
@@ -154,130 +321,44 @@ if ADAPTER
154
321
 
155
322
  it 'when value is NOT IN another query' do
156
323
  # 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])
324
+ acl = DataMapper::Query.new(repository(ADAPTER), QuerySpec::Permission, :user_id => 1, :resource_type => 'SailBoat', :token => 'READ', :fields => [ :resource_id ])
158
325
  query = { :port => 'Cape Town', :id.not => acl, :captain.like => 'J%' }
159
- boats = repository(ADAPTER) { SailBoat.all(query) }
326
+ boats = repository(ADAPTER) { QuerySpec::SailBoat.all(query) }
160
327
  boats.should have(1).entries
161
328
  boats.entries[0].id.should == 3
162
329
  end
163
330
  end # describe sub-selecting
164
331
 
165
332
  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
333
  before do
251
- Region.auto_migrate!
252
- Factory.auto_migrate!
253
- Vehicle.auto_migrate!
334
+ [ QuerySpec::Region, QuerySpec::Factory, QuerySpec::Vehicle ].each { |m| m.auto_migrate! }
254
335
 
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
336
+ QuerySpec::Region.create(:id => 1, :name => 'North West', :type => 'commercial')
337
+ QuerySpec::Factory.create(:id => 2000, :region_id => 1, :name => 'North West Plant')
338
+ QuerySpec::Vehicle.create(:id => 1, :factory_id => 2000, :name => '10 ton delivery truck')
258
339
 
259
340
  Namespace::Region.auto_migrate!
260
341
  Namespace::Factory.auto_migrate!
261
342
  Namespace::Vehicle.auto_migrate!
262
343
 
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
344
+ Namespace::Region.create(:id => 1, :name => 'North West')
345
+ Namespace::Factory.create(:id => 2000, :region_id => 1, :name => 'North West Plant')
346
+ Namespace::Vehicle.create(:id => 1, :factory_id => 2000, :name => '10 ton delivery truck')
266
347
  end
267
348
 
268
349
  it 'should require that all properties in :fields and all :links come from the same repository' #do
269
- # land = Factory.properties(:mock)[:land]
350
+ # land = QuerySpec::Factory.properties(:mock)[:land]
270
351
  # fields = []
271
- # Vehicle.properties(ADAPTER).map do |property|
352
+ # QuerySpec::Vehicle.properties(ADAPTER).map do |property|
272
353
  # fields << property
273
354
  # end
274
355
  # fields << land
275
356
  #
276
357
  # lambda{
277
358
  # begin
278
- # results = repository(ADAPTER) { Vehicle.all(:links => [:factory], :fields => fields) }
359
+ # results = repository(ADAPTER) { QuerySpec::Vehicle.all(:links => [ :factory ], :fields => fields) }
279
360
  # rescue RuntimeError
280
- # $!.message.should == "Property Factory.land not available in repository #{ADAPTER}"
361
+ # $!.message.should == "Property QuerySpec::Factory.land not available in repository #{ADAPTER}"
281
362
  # raise $!
282
363
  # end
283
364
  # }.should raise_error(RuntimeError)
@@ -287,21 +368,21 @@ if ADAPTER
287
368
  factory = DataMapper::Associations::Relationship.new(
288
369
  :factory,
289
370
  ADAPTER,
290
- 'Vehicle',
291
- 'Factory',
371
+ 'QuerySpec::Vehicle',
372
+ 'QuerySpec::Factory',
292
373
  { :child_key => [ :factory_id ], :parent_key => [ :id ] }
293
374
  )
294
- results = repository(ADAPTER) { Vehicle.all(:links => [factory]) }
375
+ results = repository(ADAPTER) { QuerySpec::Vehicle.all(:links => [ factory ]) }
295
376
  results.should have(1).entries
296
377
  end
297
378
 
298
379
  it 'should accept a symbol of an association name as a link' do
299
- results = repository(ADAPTER) { Vehicle.all(:links => [:factory]) }
380
+ results = repository(ADAPTER) { QuerySpec::Vehicle.all(:links => [ :factory ]) }
300
381
  results.should have(1).entries
301
382
  end
302
383
 
303
384
  it 'should accept a string of an association name as a link' do
304
- results = repository(ADAPTER) { Vehicle.all(:links => ['factory']) }
385
+ results = repository(ADAPTER) { QuerySpec::Vehicle.all(:links => [ 'factory' ]) }
305
386
  results.should have(1).entries
306
387
  end
307
388
 
@@ -309,37 +390,37 @@ if ADAPTER
309
390
  region = DataMapper::Associations::Relationship.new(
310
391
  :region,
311
392
  ADAPTER,
312
- 'Factory',
313
- 'Region',
393
+ 'QuerySpec::Factory',
394
+ 'QuerySpec::Region',
314
395
  { :child_key => [ :region_id ], :parent_key => [ :id ] }
315
396
  )
316
- results = repository(ADAPTER) { Vehicle.all(:links => ['factory',region]) }
397
+ results = repository(ADAPTER) { QuerySpec::Vehicle.all(:links => [ 'factory', region ]) }
317
398
  results.should have(1).entries
318
399
  end
319
400
 
320
401
  it 'should only accept a DM::Assoc::Relationship, String & Symbol as a link' do
321
402
  lambda{
322
- DataMapper::Query.new(repository(ADAPTER), Vehicle, :links => [1])
403
+ DataMapper::Query.new(repository(ADAPTER), QuerySpec::Vehicle, :links => [1])
323
404
  }.should raise_error(ArgumentError)
324
405
  end
325
406
 
326
407
  it 'should have a association by the name of the Symbol or String' do
327
408
  lambda{
328
- DataMapper::Query.new(repository(ADAPTER), Vehicle, :links=>['Sailing'])
409
+ DataMapper::Query.new(repository(ADAPTER), QuerySpec::Vehicle, :links => [ 'Sailing' ])
329
410
  }.should raise_error(ArgumentError)
330
411
 
331
412
  lambda{
332
- DataMapper::Query.new(repository(ADAPTER), Vehicle, :links=>[:sailing])
413
+ DataMapper::Query.new(repository(ADAPTER), QuerySpec::Vehicle, :links => [ :sailing ])
333
414
  }.should raise_error(ArgumentError)
334
415
  end
335
416
 
336
417
  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]
418
+ QuerySpec::Vehicle.factory.region.model.should == QuerySpec::Region
419
+ QuerySpec::Vehicle.factory.region.name.property.should == QuerySpec::Region.properties(QuerySpec::Region.repository.name)[ :name ]
339
420
  end
340
421
 
341
422
  it 'should accept a DM::QueryPath as the key to a condition' do
342
- vehicle = Vehicle.first(Vehicle.factory.region.name => 'North West')
423
+ vehicle = QuerySpec::Vehicle.first(QuerySpec::Vehicle.factory.region.name => 'North West')
343
424
  vehicle.name.should == '10 ton delivery truck'
344
425
 
345
426
  vehicle = Namespace::Vehicle.first(Namespace::Vehicle.factory.region.name => 'North West')
@@ -347,29 +428,30 @@ if ADAPTER
347
428
  end
348
429
 
349
430
  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')
431
+ vehicle = QuerySpec::Vehicle.first("factory.region.name" => 'North West')
351
432
  vehicle.name.should == '10 ton delivery truck'
352
433
  end
353
434
 
435
+ it "should accept 'id' and 'type' as endpoints on ah DM::QueryPath" do
436
+ vehicle = QuerySpec::Vehicle.first( QuerySpec::Vehicle.factory.region.type => 'commercial' )
437
+ vehicle.name.should == '10 ton delivery truck'
438
+ vehicle = QuerySpec::Vehicle.first( QuerySpec::Vehicle.factory.region.id => 1 )
439
+ vehicle.name.should == '10 ton delivery truck'
440
+ end
441
+
354
442
  it 'should auto generate the link if a DM::Property from a different resource is in the :fields option'
355
443
 
356
444
  it 'should create links with composite keys'
357
445
 
358
446
  it 'should eager load associations' do
359
447
  repository(ADAPTER) do
360
- vehicle = Vehicle.first(:includes => [Vehicle.factory])
448
+ vehicle = QuerySpec::Vehicle.first(:includes => [ QuerySpec::Vehicle.factory ])
361
449
  end
362
450
  end
363
451
 
364
452
  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])
453
+ QuerySpec::Group.should_receive(:all).with(:order => [ :id.asc ])
454
+ QuerySpec::Group.all(:order => [ :id.asc ])
373
455
  end
374
456
  end # describe links
375
457
  end # DM::Query