sam-dm-core 0.9.6 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/Manifest.txt +5 -0
  2. data/QUICKLINKS +1 -2
  3. data/Rakefile +3 -3
  4. data/SPECS +9 -10
  5. data/lib/dm-core.rb +15 -22
  6. data/lib/dm-core/adapters.rb +18 -0
  7. data/lib/dm-core/adapters/abstract_adapter.rb +17 -10
  8. data/lib/dm-core/adapters/data_objects_adapter.rb +18 -16
  9. data/lib/dm-core/adapters/mysql_adapter.rb +1 -1
  10. data/lib/dm-core/adapters/postgres_adapter.rb +2 -2
  11. data/lib/dm-core/adapters/sqlite3_adapter.rb +1 -1
  12. data/lib/dm-core/associations.rb +3 -2
  13. data/lib/dm-core/associations/many_to_many.rb +2 -2
  14. data/lib/dm-core/associations/many_to_one.rb +1 -1
  15. data/lib/dm-core/associations/one_to_many.rb +13 -7
  16. data/lib/dm-core/associations/relationship.rb +20 -15
  17. data/lib/dm-core/auto_migrations.rb +4 -12
  18. data/lib/dm-core/collection.rb +9 -5
  19. data/lib/dm-core/dependency_queue.rb +2 -1
  20. data/lib/dm-core/identity_map.rb +3 -6
  21. data/lib/dm-core/model.rb +44 -27
  22. data/lib/dm-core/property.rb +3 -13
  23. data/lib/dm-core/property_set.rb +29 -22
  24. data/lib/dm-core/query.rb +49 -47
  25. data/lib/dm-core/repository.rb +3 -3
  26. data/lib/dm-core/resource.rb +12 -12
  27. data/lib/dm-core/scope.rb +7 -7
  28. data/lib/dm-core/support/kernel.rb +6 -2
  29. data/lib/dm-core/transaction.rb +7 -7
  30. data/lib/dm-core/version.rb +1 -1
  31. data/script/performance.rb +109 -30
  32. data/script/profile.rb +2 -2
  33. data/spec/integration/association_spec.rb +13 -1
  34. data/spec/integration/associations/one_to_many_spec.rb +40 -3
  35. data/spec/integration/auto_migrations_spec.rb +16 -1
  36. data/spec/integration/dependency_queue_spec.rb +0 -12
  37. data/spec/integration/postgres_adapter_spec.rb +1 -1
  38. data/spec/integration/property_spec.rb +4 -4
  39. data/spec/integration/resource_spec.rb +6 -0
  40. data/spec/integration/sti_spec.rb +22 -0
  41. data/spec/integration/strategic_eager_loading_spec.rb +21 -6
  42. data/spec/integration/type_spec.rb +1 -1
  43. data/spec/lib/model_loader.rb +10 -1
  44. data/spec/models/zoo.rb +1 -0
  45. data/spec/spec_helper.rb +3 -2
  46. data/spec/unit/adapters/data_objects_adapter_spec.rb +3 -3
  47. data/spec/unit/associations/many_to_many_spec.rb +16 -1
  48. data/spec/unit/associations/many_to_one_spec.rb +9 -2
  49. data/spec/unit/model_spec.rb +12 -30
  50. data/spec/unit/property_set_spec.rb +8 -1
  51. data/spec/unit/query_spec.rb +41 -0
  52. data/spec/unit/resource_spec.rb +27 -4
  53. data/spec/unit/transaction_spec.rb +13 -13
  54. data/tasks/ci.rb +4 -36
  55. data/tasks/dm.rb +3 -3
  56. metadata +7 -16
data/script/profile.rb CHANGED
@@ -4,10 +4,10 @@ require File.join(File.dirname(__FILE__), '..', 'lib', 'dm-core')
4
4
 
5
5
  require 'rubygems'
6
6
 
7
- gem 'ruby-prof', '>=0.6.0'
7
+ gem 'ruby-prof', '~>0.7.1'
8
8
  require 'ruby-prof'
9
9
 
10
- gem 'faker', '>=0.3.1'
10
+ gem 'faker', '~>0.3.1'
11
11
  require 'faker'
12
12
 
13
13
  OUTPUT = DataMapper.root / 'profile_results.txt'
@@ -302,6 +302,18 @@ if ADAPTER
302
302
  area.should respond_to(:machine=)
303
303
  end
304
304
 
305
+ it 'should create the foreign key property immediately' do
306
+ class Duck
307
+ include DataMapper::Resource
308
+ property :id, Serial
309
+ belongs_to :sky
310
+ end
311
+ Duck.properties.slice(:sky_id).compact.should_not be_empty
312
+ duck = Duck.new
313
+ duck.should respond_to(:sky_id)
314
+ duck.should respond_to(:sky_id=)
315
+ end
316
+
305
317
  it 'should load without the parent'
306
318
 
307
319
  it 'should allow substituting the parent' do
@@ -331,7 +343,7 @@ if ADAPTER
331
343
  end
332
344
  end
333
345
 
334
- FlightlessBirds::Ostrich.properties.slice(:sky_id).should_not be_empty
346
+ FlightlessBirds::Ostrich.properties(ADAPTER).slice(:sky_id).compact.should_not be_empty
335
347
  end
336
348
  end
337
349
 
@@ -103,6 +103,22 @@ describe "OneToMany" do
103
103
  end
104
104
  end
105
105
 
106
+ describe "parent initialized child" do
107
+ before(:each) do
108
+ @ajax = Team.create
109
+ @vandesar = @ajax.players.new
110
+ @vandesar.save
111
+ end
112
+
113
+ it "child association should return parent" do
114
+ @vandesar.team.should == @ajax
115
+ end
116
+
117
+ it "parent association should return children" do
118
+ @ajax.players.should == [@vandesar]
119
+ end
120
+ end
121
+
106
122
  it "unsaved parent model should accept array of hashes for association" do
107
123
  players = [{ :name => "Brett Favre" }, { :name => "Reggie White" }]
108
124
 
@@ -139,12 +155,33 @@ describe "OneToMany" do
139
155
  end
140
156
 
141
157
  describe "STI" do
142
- it "should work" do
158
+ before(:all) do
159
+ repository(ADAPTER) do
160
+ @player = Player.create(:name => "Barry Bonds", :team => BaseballTeam.first)
161
+ end
162
+ end
163
+
164
+ it "should work for child.parent" do
143
165
  repository(ADAPTER) do
144
- Player.create(:name => "Barry Bonds", :team => BaseballTeam.first)
166
+ @player.team.should == BaseballTeam.first
145
167
  end
168
+ end
169
+
170
+ it "should work for parent.children" do
171
+ repository(ADAPTER) do
172
+ team = BaseballTeam.first
173
+
174
+ team.players.size.should > 0
175
+ team.players.each{|p| p.should be_an_instance_of(Player)}
176
+ end
177
+ end
178
+ end
179
+
180
+ describe "alone" do
181
+ it "should work for parent.children without any parents in IM" do
146
182
  repository(ADAPTER) do
147
- Player.first.team.should == BaseballTeam.first
183
+ team = BaseballTeam.first
184
+ team.players.each{|p| p.should be_an_instance_of(Player)}
148
185
  end
149
186
  end
150
187
  end
@@ -39,7 +39,10 @@ class EveryType
39
39
  end
40
40
 
41
41
  module Publications
42
- class ShortStoryCollection
42
+ class StoryCollection
43
+ end
44
+
45
+ class ShortStoryCollection < StoryCollection
43
46
  include DataMapper::Resource
44
47
  property :serial, Serial
45
48
  property :date, Date, :nullable => false, :default => TODAY, :index => :date_date_time
@@ -153,6 +156,10 @@ if HAS_SQLITE3
153
156
  end
154
157
  end
155
158
 
159
+ it 'should handle a model which inherits from a regular object' do
160
+ lambda { Publications::ShortStoryCollection.auto_migrate!(:sqlite3) }.should_not raise_error
161
+ end
162
+
156
163
  it 'should escape a namespaced model' do
157
164
  Publications::ShortStoryCollection.auto_migrate!(:sqlite3).should be_true
158
165
  @adapter.query('SELECT "name" FROM "sqlite_master" WHERE type = ?', 'table').should include('publications_short_story_collections')
@@ -259,6 +266,10 @@ if HAS_MYSQL
259
266
  end
260
267
  end
261
268
 
269
+ it 'should handle a model which inherits from a regular object' do
270
+ lambda { Publications::ShortStoryCollection.auto_migrate!(:mysql) }.should_not raise_error
271
+ end
272
+
262
273
  it 'should escape a namespaced model' do
263
274
  Publications::ShortStoryCollection.auto_migrate!(:mysql).should be_true
264
275
  @adapter.query('SHOW TABLES').should include('publications_short_story_collections')
@@ -389,6 +400,10 @@ if HAS_POSTGRES
389
400
  pending 'TODO'
390
401
  end
391
402
 
403
+ it 'should handle a model which inherits from a regular object' do
404
+ lambda { Publications::ShortStoryCollection.auto_migrate!(:postgres) }.should_not raise_error
405
+ end
406
+
392
407
  it 'should escape a namespaced model' do
393
408
  Publications::ShortStoryCollection.auto_migrate!(:postgres).should be_true
394
409
  @adapter.query('SELECT "tablename" FROM "pg_tables" WHERE "tablename" NOT LIKE ?', 'pg_%').should include('publications_short_story_collections')
@@ -6,18 +6,6 @@ describe "DataMapper::DependencyQueue" do
6
6
  @dependencies = @q.instance_variable_get("@dependencies")
7
7
  end
8
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
9
  describe "#add" do
22
10
  it "should store the supplied callback in @dependencies" do
23
11
  @q.add('MissingConstant') { true }
@@ -127,7 +127,7 @@ if HAS_POSTGRES
127
127
 
128
128
  class Voyager
129
129
  include DataMapper::Resource
130
- storage_names[:postgres] = 'sattellites.voyagers'
130
+ storage_names[:postgres] = 'voyagers'
131
131
 
132
132
  property :id, Serial
133
133
  property :age, Integer
@@ -1,6 +1,6 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
2
 
3
- gem 'fastercsv', '>=1.2.3'
3
+ gem 'fastercsv', '~>1.4.0'
4
4
  require 'fastercsv'
5
5
 
6
6
  if ADAPTER
@@ -61,7 +61,7 @@ if ADAPTER
61
61
  jon.original_values[:location].should == 'dallas'
62
62
 
63
63
  jon.dirty?.should be_false
64
- jon.save.should be_false
64
+ jon.save.should be_true
65
65
 
66
66
  jon.location.upcase!
67
67
  jon.location.should == 'DALLAS'
@@ -115,10 +115,10 @@ if ADAPTER
115
115
  tim = Actor.first(:name => 'tim')
116
116
  tim.notes # make sure they're loaded...
117
117
  tim.dirty?.should be_false
118
- tim.save.should be_false
118
+ tim.save.should be_true
119
119
  tim.notes = "Testing"
120
120
  tim.dirty?.should be_true
121
- tim.save
121
+ tim.save.should be_true
122
122
  end
123
123
  repository(ADAPTER) do
124
124
  tim = Actor.first(:name => 'tim')
@@ -21,6 +21,12 @@ if ADAPTER
21
21
  lambda { @zoo.destroy.should be_true }.should_not raise_error
22
22
  end
23
23
 
24
+ it 'should not overwrite attributes when lazy loading' do
25
+ zoo = Zoo.first
26
+ zoo.name = 'San Diego'
27
+ lambda { zoo.description }.should_not change(zoo, :name)
28
+ end
29
+
24
30
  describe '#attribute_get' do
25
31
  it 'should provide #attribute_get' do
26
32
  Zoo.new.should respond_to(:attribute_get)
@@ -204,5 +204,27 @@ if HAS_SQLITE3
204
204
  SpaceWestern.properties[:title].should_not be_nil
205
205
  end
206
206
  end
207
+
208
+ describe "with a child class" do
209
+ before :all do
210
+ Book.auto_migrate!(:sqlite3)
211
+ repository(:sqlite3) do
212
+ ShortStory.create(
213
+ :title => "The Science of Happiness",
214
+ :isbn => "129038",
215
+ :moral => "Bullshit might get you to the top, but it won't keep you there.")
216
+ end
217
+ end
218
+
219
+ it "should be able to access the properties from the parent collection" do
220
+ repository(:sqlite3) do
221
+ Book.all.each do |book|
222
+ book.title.should_not be_nil
223
+ book.isbn.should_not be_nil
224
+ book.moral.should_not be_nil
225
+ end
226
+ end
227
+ end
228
+ end
207
229
  end
208
230
  end
@@ -64,8 +64,12 @@ describe "Strategic Eager Loading" do
64
64
  zoos = Zoo.all.entries # load all zoos
65
65
  dallas = zoos.find { |z| z.name == 'Dallas Zoo' }
66
66
 
67
- dallas.exhibits.entries # load all exhibits for zoos in identity_map
68
- dallas.exhibits.size.should == 1
67
+ logger do |log|
68
+ dallas.exhibits.entries # load all exhibits for zoos in identity_map
69
+ dallas.exhibits.size.should == 1
70
+ log.readlines.size.should == 1
71
+ end
72
+
69
73
  repository.identity_map(Zoo).keys.sort.should == zoo_ids
70
74
  repository.identity_map(Exhibit).keys.sort.should == exhibit_ids
71
75
 
@@ -88,16 +92,21 @@ describe "Strategic Eager Loading" do
88
92
  dallas = Zoo.all.entries.find { |z| z.name == 'Dallas Zoo' }
89
93
  exhibits = dallas.exhibits.entries # load all exhibits
90
94
 
95
+ reptiles, primates = nil, nil
96
+
91
97
  logger do |log|
92
98
  reptiles = dallas.exhibits(:name => 'Reptiles')
93
99
  reptiles.size.should == 1
100
+ log.readlines.size.should == 1
101
+ end
94
102
 
103
+ logger do |log|
95
104
  primates = dallas.exhibits(:name => 'Primates')
96
105
  primates.size.should == 1
97
- primates.should_not == reptiles
98
-
99
- log.readlines.size.should == 2
106
+ log.readlines.size.should == 1
100
107
  end
108
+
109
+ primates.should_not == reptiles
101
110
  end
102
111
  end
103
112
 
@@ -109,7 +118,12 @@ describe "Strategic Eager Loading" do
109
118
  repository(ADAPTER) do
110
119
  animals = Animal.all.entries
111
120
  bear = animals.find { |a| a.name == 'Brown Bear' }
112
- bear.exhibit
121
+
122
+ logger do |log|
123
+ bear.exhibit
124
+ log.readlines.size.should == 1
125
+ end
126
+
113
127
  repository.identity_map(Animal).keys.sort.should == animal_ids
114
128
  repository.identity_map(Exhibit).keys.sort.should == exhibit_ids
115
129
  end
@@ -124,6 +138,7 @@ describe "Strategic Eager Loading" do
124
138
  animal.exhibit # load exhibit from IM
125
139
  log.readlines.should be_empty
126
140
  end
141
+
127
142
  repository.identity_map(Exhibit).keys.should == [exhibit.key]
128
143
  end
129
144
  end
@@ -1,6 +1,6 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
2
 
3
- gem 'fastercsv', '>=1.2.3'
3
+ gem 'fastercsv', '~>1.4.0'
4
4
  require 'fastercsv'
5
5
 
6
6
  if ADAPTER
@@ -81,7 +81,16 @@ module ModelLoader
81
81
 
82
82
  def remove_model(klass)
83
83
  DataMapper::Resource.descendants.delete(klass)
84
- Object.module_eval { remove_const klass.to_s }
84
+ # Check to see if the model is living inside a module
85
+ klass_name = klass.to_s
86
+ if klass_name.index("::")
87
+ mod = klass_name.match(/(\S+)::/)[1]
88
+ child_class = klass_name.match(/\S+::(\S+)/)[1]
89
+
90
+ Object.const_get(mod).module_eval { remove_const child_class }
91
+ else
92
+ Object.module_eval { remove_const klass.to_s }
93
+ end
85
94
  end
86
95
  end
87
96
  end
data/spec/models/zoo.rb CHANGED
@@ -7,6 +7,7 @@ class Zoo
7
7
  property :inception, DateTime
8
8
  property :open, Boolean, :default => false
9
9
  property :size, Integer
10
+ property :mission, Text, :writer => :protected
10
11
 
11
12
  has n, :animals
12
13
 
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,8 @@
1
+ require 'pathname'
1
2
  require 'rubygems'
2
- gem 'rspec', '>=1.1.3'
3
+
4
+ gem 'rspec', '~>1.1.11'
3
5
  require 'spec'
4
- require 'pathname'
5
6
 
6
7
  SPEC_ROOT = Pathname(__FILE__).dirname.expand_path
7
8
  require SPEC_ROOT.parent + 'lib/dm-core'
@@ -116,7 +116,7 @@ describe DataMapper::Adapters::DataObjectsAdapter do
116
116
 
117
117
  adapter = DataMapper::Adapters::DataObjectsAdapter.new(:spec, options)
118
118
  adapter.uri.should ==
119
- Addressable::URI.parse("mysql://me:mypass@davidleal.com:5000/you_can_call_me_al?socket=nosock")
119
+ DataObjects::URI.parse("mysql://me:mypass@davidleal.com:5000/you_can_call_me_al?socket=nosock")
120
120
  end
121
121
 
122
122
  it 'should transform a minimal options hash into a URI' do
@@ -126,12 +126,12 @@ describe DataMapper::Adapters::DataObjectsAdapter do
126
126
  }
127
127
 
128
128
  adapter = DataMapper::Adapters::DataObjectsAdapter.new(:spec, options)
129
- adapter.uri.should == Addressable::URI.parse("mysql:you_can_call_me_al")
129
+ adapter.uri.should == DataObjects::URI.parse("mysql:you_can_call_me_al")
130
130
  end
131
131
 
132
132
  it 'should accept the uri when no overrides exist' do
133
133
  uri = Addressable::URI.parse("protocol:///")
134
- DataMapper::Adapters::DataObjectsAdapter.new(:spec, uri).uri.should == uri
134
+ DataMapper::Adapters::DataObjectsAdapter.new(:spec, uri).uri.should == DataObjects::URI.parse(uri)
135
135
  end
136
136
  end
137
137
 
@@ -2,7 +2,7 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_hel
2
2
 
3
3
  describe DataMapper::Associations::ManyToMany do
4
4
 
5
- load_models_for_metaphor :vehicles
5
+ load_models_for_metaphor :vehicles, :content
6
6
 
7
7
  it 'should allow a declaration' do
8
8
  lambda do
@@ -11,6 +11,21 @@ describe DataMapper::Associations::ManyToMany do
11
11
  end
12
12
  end.should_not raise_error
13
13
  end
14
+
15
+ it 'should handle models inside modules' do
16
+ lambda do
17
+ module Content
18
+ class Dialect
19
+ has n, :locales, :through => Resource, :class_name => "Language::Locale"
20
+ end
21
+
22
+ class Locale
23
+ has n, :dialects, :through => Resource, :class_name => "Language::Dialect"
24
+ end
25
+ end
26
+ end.should_not raise_error
27
+ end
28
+
14
29
  end
15
30
 
16
31
  describe DataMapper::Associations::ManyToMany::Proxy do
@@ -19,7 +19,7 @@ describe DataMapper::Associations::ManyToOne::Proxy do
19
19
 
20
20
  before do
21
21
  @child = mock('child', :kind_of? => true)
22
- @parent = mock('parent')
22
+ @parent = mock('parent', :nil? => false, :new_record? => false)
23
23
  @relationship = mock('relationship', :kind_of? => true, :get_parent => @parent, :attach_parent => nil)
24
24
  @association = DataMapper::Associations::ManyToOne::Proxy.new(@relationship, @child)
25
25
 
@@ -30,6 +30,13 @@ describe DataMapper::Associations::ManyToOne::Proxy do
30
30
  @association.should respond_to(:replace)
31
31
  end
32
32
 
33
+ describe '#class' do
34
+ it 'should be forwarded to parent' do
35
+ @parent.should_receive(:class).and_return(Manufacturer)
36
+ @association.class.should == Manufacturer
37
+ end
38
+ end
39
+
33
40
  describe '#replace' do
34
41
  before do
35
42
  @other = mock('other parent')
@@ -62,7 +69,7 @@ describe DataMapper::Associations::ManyToOne::Proxy do
62
69
  describe '#save' do
63
70
  describe 'when the parent is nil' do
64
71
  before do
65
- @parent.should_receive(:nil?).with(no_args).and_return(true)
72
+ @parent.stub!(:nil?).and_return(true)
66
73
  end
67
74
 
68
75
  it 'should not save the parent' do