dm-core 0.9.2 → 0.9.3

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 (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