sam-dm-core 0.9.6 → 0.9.7

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