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.
- data/.autotest +26 -0
- data/{CHANGELOG → History.txt} +78 -77
- data/Manifest.txt +123 -0
- data/{README → README.txt} +0 -0
- data/Rakefile +29 -0
- data/SPECS +63 -0
- data/TODO +1 -0
- data/lib/dm-core.rb +6 -1
- data/lib/dm-core/adapters/data_objects_adapter.rb +29 -32
- data/lib/dm-core/adapters/mysql_adapter.rb +1 -1
- data/lib/dm-core/adapters/postgres_adapter.rb +1 -1
- data/lib/dm-core/adapters/sqlite3_adapter.rb +2 -2
- data/lib/dm-core/associations.rb +26 -0
- data/lib/dm-core/associations/many_to_many.rb +34 -25
- data/lib/dm-core/associations/many_to_one.rb +4 -4
- data/lib/dm-core/associations/one_to_many.rb +48 -13
- data/lib/dm-core/associations/one_to_one.rb +4 -4
- data/lib/dm-core/associations/relationship.rb +144 -42
- data/lib/dm-core/associations/relationship_chain.rb +31 -24
- data/lib/dm-core/auto_migrations.rb +0 -4
- data/lib/dm-core/collection.rb +40 -7
- data/lib/dm-core/dependency_queue.rb +31 -0
- data/lib/dm-core/hook.rb +2 -2
- data/lib/dm-core/is.rb +2 -2
- data/lib/dm-core/logger.rb +10 -10
- data/lib/dm-core/model.rb +94 -41
- data/lib/dm-core/property.rb +72 -41
- data/lib/dm-core/property_set.rb +8 -14
- data/lib/dm-core/query.rb +34 -9
- data/lib/dm-core/repository.rb +0 -0
- data/lib/dm-core/resource.rb +13 -13
- data/lib/dm-core/scope.rb +25 -2
- data/lib/dm-core/type.rb +3 -3
- data/lib/dm-core/types/discriminator.rb +10 -8
- data/lib/dm-core/types/object.rb +4 -0
- data/lib/dm-core/types/paranoid_boolean.rb +15 -4
- data/lib/dm-core/types/paranoid_datetime.rb +15 -4
- data/lib/dm-core/version.rb +3 -0
- data/script/all +5 -0
- data/script/performance.rb +191 -0
- data/script/profile.rb +86 -0
- data/spec/integration/association_spec.rb +288 -204
- data/spec/integration/association_through_spec.rb +9 -3
- data/spec/integration/associations/many_to_many_spec.rb +97 -31
- data/spec/integration/associations/many_to_one_spec.rb +41 -6
- data/spec/integration/associations/one_to_many_spec.rb +18 -2
- data/spec/integration/auto_migrations_spec.rb +0 -0
- data/spec/integration/collection_spec.rb +89 -42
- data/spec/integration/dependency_queue_spec.rb +58 -0
- data/spec/integration/model_spec.rb +67 -8
- data/spec/integration/postgres_adapter_spec.rb +19 -20
- data/spec/integration/property_spec.rb +17 -8
- data/spec/integration/query_spec.rb +273 -191
- data/spec/integration/resource_spec.rb +108 -10
- data/spec/integration/strategic_eager_loading_spec.rb +138 -0
- data/spec/integration/transaction_spec.rb +3 -3
- data/spec/integration/type_spec.rb +121 -0
- data/spec/lib/logging_helper.rb +18 -0
- data/spec/lib/model_loader.rb +91 -0
- data/spec/lib/publicize_methods.rb +28 -0
- data/spec/models/vehicles.rb +34 -0
- data/spec/models/zoo.rb +48 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +25 -62
- data/spec/unit/adapters/data_objects_adapter_spec.rb +1 -0
- data/spec/unit/associations/many_to_many_spec.rb +3 -0
- data/spec/unit/associations/many_to_one_spec.rb +9 -1
- data/spec/unit/associations/one_to_many_spec.rb +12 -4
- data/spec/unit/associations/relationship_spec.rb +19 -15
- data/spec/unit/associations_spec.rb +37 -0
- data/spec/unit/collection_spec.rb +8 -0
- data/spec/unit/data_mapper_spec.rb +14 -0
- data/spec/unit/model_spec.rb +2 -2
- data/spec/unit/property_set_spec.rb +0 -13
- data/spec/unit/property_spec.rb +92 -21
- data/spec/unit/query_spec.rb +49 -4
- data/spec/unit/resource_spec.rb +122 -60
- data/spec/unit/scope_spec.rb +11 -0
- data/tasks/ci.rb +68 -0
- data/tasks/dm.rb +63 -0
- data/tasks/doc.rb +20 -0
- data/tasks/hoe.rb +38 -0
- data/tasks/install.rb +20 -0
- metadata +63 -22
@@ -0,0 +1,91 @@
|
|
1
|
+
# ---
|
2
|
+
# Overview
|
3
|
+
# ========
|
4
|
+
# ModelLoader is a method for loading methods models for specs in a way
|
5
|
+
# that will ensure that each spec will be an a pristine state when run.
|
6
|
+
#
|
7
|
+
# The problem is that if a spec needs to modify a model, the modifications
|
8
|
+
# should not carry over to the next spec. As such, all models are
|
9
|
+
# destroyed at the end of the spec and reloaded at the start.
|
10
|
+
#
|
11
|
+
# The second problem is that DataMapper::Resource keeps track
|
12
|
+
# of every class that it is included in. This is used for automigration.
|
13
|
+
# A number of specs run automigrate, and we don't want all the classes
|
14
|
+
# that were defined in other specs to be migrated as well.
|
15
|
+
#
|
16
|
+
# Usage
|
17
|
+
# =====
|
18
|
+
#
|
19
|
+
# Sets the specified model metaphors to be loaded before each spec and
|
20
|
+
# destroyed after each spec in the current example group. This method
|
21
|
+
# can be used in a describe block or in a before block.
|
22
|
+
#
|
23
|
+
# ==== Parameters
|
24
|
+
# *metaphor<Symbol>:: The name of the metaphor to load (this is just the filename of
|
25
|
+
# file in specs/models)
|
26
|
+
#
|
27
|
+
# ==== Example
|
28
|
+
#
|
29
|
+
# describe "DataMapper::Associations" do
|
30
|
+
#
|
31
|
+
# load_models_for_metaphor :zoo, :blog
|
32
|
+
#
|
33
|
+
# it "should be awesome" do
|
34
|
+
# Zoo.new.should be_awesome
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
module ModelLoader
|
38
|
+
|
39
|
+
def self.included(base)
|
40
|
+
base.extend(ClassMethods)
|
41
|
+
base.class_eval { include InstanceMethods }
|
42
|
+
# base.before(:each) { load_models(:global) }
|
43
|
+
base.after(:each) { unload_models }
|
44
|
+
end
|
45
|
+
|
46
|
+
module ClassMethods
|
47
|
+
|
48
|
+
def load_models_for_metaphor(*metaphors)
|
49
|
+
before(:each) { load_models_for_metaphor(*metaphors) }
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
module InstanceMethods
|
55
|
+
|
56
|
+
def load_models_for_metaphor(*metaphors)
|
57
|
+
files = metaphors.map { |m| DataMapper.root / "spec" / "models" / "#{m}.rb" }
|
58
|
+
|
59
|
+
klasses = object_space_classes.dup
|
60
|
+
files.each { |file| load file }
|
61
|
+
loaded_models.concat(object_space_classes - klasses)
|
62
|
+
end
|
63
|
+
|
64
|
+
def unload_models
|
65
|
+
while model = loaded_models.pop
|
66
|
+
remove_model(model)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def loaded_models
|
71
|
+
@loaded_models ||= []
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def object_space_classes
|
77
|
+
klasses = []
|
78
|
+
ObjectSpace.each_object(Class) {|o| klasses << o}
|
79
|
+
klasses
|
80
|
+
end
|
81
|
+
|
82
|
+
def remove_model(klass)
|
83
|
+
DataMapper::Resource.descendants.delete(klass)
|
84
|
+
Object.module_eval { remove_const klass.to_s }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
Spec::Runner.configure do |config|
|
90
|
+
config.include(ModelLoader)
|
91
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Class
|
2
|
+
def publicize_methods
|
3
|
+
klass = class << self; self; end
|
4
|
+
|
5
|
+
saved_private_class_methods = klass.private_instance_methods
|
6
|
+
saved_protected_class_methods = klass.protected_instance_methods
|
7
|
+
saved_private_instance_methods = self.private_instance_methods
|
8
|
+
saved_protected_instance_methods = self.protected_instance_methods
|
9
|
+
|
10
|
+
self.class_eval do
|
11
|
+
klass.send(:public, *saved_private_class_methods)
|
12
|
+
klass.send(:public, *saved_protected_class_methods)
|
13
|
+
public(*saved_private_instance_methods)
|
14
|
+
public(*saved_protected_instance_methods)
|
15
|
+
end
|
16
|
+
|
17
|
+
begin
|
18
|
+
yield
|
19
|
+
ensure
|
20
|
+
self.class_eval do
|
21
|
+
klass.send(:private, *saved_private_class_methods)
|
22
|
+
klass.send(:protected, *saved_protected_class_methods)
|
23
|
+
private(*saved_private_instance_methods)
|
24
|
+
protected(*saved_protected_instance_methods)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# ==========================
|
2
|
+
# Used for Association specs
|
3
|
+
# ---
|
4
|
+
# These models will probably
|
5
|
+
# end up removed. So, I wouldn't
|
6
|
+
# use this metaphor
|
7
|
+
class Vehicle
|
8
|
+
include DataMapper::Resource
|
9
|
+
|
10
|
+
property :id, Serial
|
11
|
+
property :name, String
|
12
|
+
|
13
|
+
class << self
|
14
|
+
attr_accessor :mock_relationship
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Manufacturer
|
19
|
+
include DataMapper::Resource
|
20
|
+
|
21
|
+
property :id, Serial
|
22
|
+
property :name, String
|
23
|
+
|
24
|
+
class << self
|
25
|
+
attr_accessor :mock_relationship
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Supplier
|
30
|
+
include DataMapper::Resource
|
31
|
+
|
32
|
+
property :id, Serial
|
33
|
+
property :name, String
|
34
|
+
end
|
data/spec/models/zoo.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
class Zoo
|
2
|
+
include DataMapper::Resource
|
3
|
+
|
4
|
+
property :id, Serial
|
5
|
+
property :name, String
|
6
|
+
property :description, Text
|
7
|
+
property :inception, DateTime
|
8
|
+
property :open, Boolean, :default => false
|
9
|
+
property :size, Integer
|
10
|
+
|
11
|
+
has n, :animals
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
name
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Species
|
19
|
+
include DataMapper::Resource
|
20
|
+
|
21
|
+
property :id, Serial
|
22
|
+
property :name, String
|
23
|
+
property :classification, String, :reader => :private
|
24
|
+
|
25
|
+
has n, :animals
|
26
|
+
end
|
27
|
+
|
28
|
+
class Animal
|
29
|
+
include DataMapper::Resource
|
30
|
+
|
31
|
+
property :id, Serial
|
32
|
+
property :name, String
|
33
|
+
|
34
|
+
belongs_to :zoo
|
35
|
+
belongs_to :species
|
36
|
+
belongs_to :keeper
|
37
|
+
end
|
38
|
+
|
39
|
+
class Employee
|
40
|
+
include DataMapper::Resource
|
41
|
+
|
42
|
+
property :id, Serial
|
43
|
+
property :name, String
|
44
|
+
end
|
45
|
+
|
46
|
+
class Keeper < Employee
|
47
|
+
has n, :animals
|
48
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
CHANGED
@@ -3,8 +3,13 @@ gem 'rspec', '>=1.1.3'
|
|
3
3
|
require 'spec'
|
4
4
|
require 'pathname'
|
5
5
|
|
6
|
-
|
7
|
-
require
|
6
|
+
SPEC_ROOT = Pathname(__FILE__).dirname.expand_path
|
7
|
+
require SPEC_ROOT.parent + 'lib/dm-core'
|
8
|
+
|
9
|
+
# Load the various helpers for the spec suite
|
10
|
+
Dir[DataMapper.root / 'spec' / 'lib' / '*.rb'].each do |file|
|
11
|
+
require file
|
12
|
+
end
|
8
13
|
|
9
14
|
# setup mock adapters
|
10
15
|
[ :default, :mock, :legacy, :west_coast, :east_coast ].each do |repository_name|
|
@@ -33,6 +38,22 @@ HAS_POSTGRES = setup_adapter(:postgres, 'postgres://postgres@localhost/dm_core_t
|
|
33
38
|
|
34
39
|
DataMapper::Logger.new(nil, :debug)
|
35
40
|
|
41
|
+
# ----------------------------------------------------------------------
|
42
|
+
# --- Do not declare new models unless absolutely necessary. Instead ---
|
43
|
+
# --- pick a metaphor and use those models. If you do need new ---
|
44
|
+
# --- models, define them according to the metaphor being used. ---
|
45
|
+
# ----------------------------------------------------------------------
|
46
|
+
|
47
|
+
Spec::Runner.configure do |config|
|
48
|
+
config.before(:each) do
|
49
|
+
# load_models_for_metaphor :vehicles
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# ----------------------------------------------------------------------
|
54
|
+
# --- All these models are going to be removed. Don't use them!!! ---
|
55
|
+
# ----------------------------------------------------------------------
|
56
|
+
|
36
57
|
class Article
|
37
58
|
include DataMapper::Resource
|
38
59
|
|
@@ -45,68 +66,10 @@ end
|
|
45
66
|
|
46
67
|
class Comment
|
47
68
|
include DataMapper::Resource
|
69
|
+
|
70
|
+
property :id, Serial # blah
|
48
71
|
end
|
49
72
|
|
50
73
|
class NormalClass
|
51
74
|
# should not include DataMapper::Resource
|
52
75
|
end
|
53
|
-
|
54
|
-
# ==========================
|
55
|
-
# Used for Association specs
|
56
|
-
class Vehicle
|
57
|
-
include DataMapper::Resource
|
58
|
-
|
59
|
-
property :id, Serial
|
60
|
-
property :name, String
|
61
|
-
|
62
|
-
class << self
|
63
|
-
attr_accessor :mock_relationship
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
class Manufacturer
|
68
|
-
include DataMapper::Resource
|
69
|
-
|
70
|
-
property :id, Serial
|
71
|
-
property :name, String
|
72
|
-
|
73
|
-
class << self
|
74
|
-
attr_accessor :mock_relationship
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
class Supplier
|
79
|
-
include DataMapper::Resource
|
80
|
-
|
81
|
-
property :id, Serial
|
82
|
-
property :name, String
|
83
|
-
end
|
84
|
-
|
85
|
-
class Class
|
86
|
-
def publicize_methods
|
87
|
-
klass = class << self; self; end
|
88
|
-
|
89
|
-
saved_private_class_methods = klass.private_instance_methods
|
90
|
-
saved_protected_class_methods = klass.protected_instance_methods
|
91
|
-
saved_private_instance_methods = self.private_instance_methods
|
92
|
-
saved_protected_instance_methods = self.protected_instance_methods
|
93
|
-
|
94
|
-
self.class_eval do
|
95
|
-
klass.send(:public, *saved_private_class_methods)
|
96
|
-
klass.send(:public, *saved_protected_class_methods)
|
97
|
-
public(*saved_private_instance_methods)
|
98
|
-
public(*saved_protected_instance_methods)
|
99
|
-
end
|
100
|
-
|
101
|
-
begin
|
102
|
-
yield
|
103
|
-
ensure
|
104
|
-
self.class_eval do
|
105
|
-
klass.send(:private, *saved_private_class_methods)
|
106
|
-
klass.send(:protected, *saved_protected_class_methods)
|
107
|
-
private(*saved_private_instance_methods)
|
108
|
-
protected(*saved_protected_instance_methods)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
@@ -247,6 +247,7 @@ describe DataMapper::Adapters::DataObjectsAdapter do
|
|
247
247
|
@order = [ @direction ]
|
248
248
|
|
249
249
|
@query = mock('query', :model => @model, :kind_of? => true, :links => @links, :fields => @fields, :conditions => @conditions, :order => @order, :limit => 111, :offset => 222, :bind_values => @bind_values)
|
250
|
+
@query.should_receive(:unique?).with(no_args).and_return(false)
|
250
251
|
|
251
252
|
@reader = mock('reader', :close => true, :next! => false)
|
252
253
|
@command = mock('command', :set_types => nil, :execute_reader => @reader)
|
@@ -1,6 +1,9 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
|
2
2
|
|
3
3
|
describe DataMapper::Associations::ManyToOne do
|
4
|
+
|
5
|
+
load_models_for_metaphor :vehicles
|
6
|
+
|
4
7
|
it 'should allow a declaration' do
|
5
8
|
lambda do
|
6
9
|
class Vehicle
|
@@ -11,10 +14,13 @@ describe DataMapper::Associations::ManyToOne do
|
|
11
14
|
end
|
12
15
|
|
13
16
|
describe DataMapper::Associations::ManyToOne::Proxy do
|
17
|
+
|
18
|
+
load_models_for_metaphor :vehicles
|
19
|
+
|
14
20
|
before do
|
15
21
|
@child = mock('child', :kind_of? => true)
|
16
22
|
@parent = mock('parent')
|
17
|
-
@relationship = mock('relationship', :kind_of? => true, :
|
23
|
+
@relationship = mock('relationship', :kind_of? => true, :get_parent => @parent, :attach_parent => nil)
|
18
24
|
@association = DataMapper::Associations::ManyToOne::Proxy.new(@relationship, @child)
|
19
25
|
|
20
26
|
@association.replace(@parent)
|
@@ -89,11 +95,13 @@ describe DataMapper::Associations::ManyToOne::Proxy do
|
|
89
95
|
end
|
90
96
|
|
91
97
|
it 'should save the parent' do
|
98
|
+
@relationship.should_receive(:with_repository).and_yield(@repository)
|
92
99
|
@parent.should_receive(:save).with(no_args)
|
93
100
|
@association.save
|
94
101
|
end
|
95
102
|
|
96
103
|
it 'should return the result of the save' do
|
104
|
+
@relationship.should_receive(:with_repository).and_yield(@repository)
|
97
105
|
save_results = mock('save results')
|
98
106
|
@parent.should_receive(:save).with(no_args).and_return(save_results)
|
99
107
|
@association.save.object_id.should == save_results.object_id
|
@@ -1,6 +1,9 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
|
2
2
|
|
3
3
|
describe DataMapper::Associations::OneToMany do
|
4
|
+
|
5
|
+
load_models_for_metaphor :vehicles
|
6
|
+
|
4
7
|
before do
|
5
8
|
@class = Class.new do
|
6
9
|
def self.name
|
@@ -30,7 +33,7 @@ describe DataMapper::Associations::OneToMany do
|
|
30
33
|
|
31
34
|
it 'should receive the name' do
|
32
35
|
DataMapper::Associations::Relationship.should_receive(:new) do |name,_,_,_,_|
|
33
|
-
name.should == :
|
36
|
+
name.should == :orders
|
34
37
|
end
|
35
38
|
@class.has(@class.n, :orders)
|
36
39
|
end
|
@@ -60,7 +63,7 @@ describe DataMapper::Associations::OneToMany do
|
|
60
63
|
|
61
64
|
it 'should receive the parent model name' do
|
62
65
|
DataMapper::Associations::Relationship.should_receive(:new) do |_,_,_,parent_model_name,_|
|
63
|
-
parent_model_name.should ==
|
66
|
+
parent_model_name.should == @class
|
64
67
|
end
|
65
68
|
@class.has(@class.n, :orders)
|
66
69
|
end
|
@@ -106,8 +109,9 @@ describe DataMapper::Associations::OneToMany::Proxy do
|
|
106
109
|
@parent = mock('parent', :new_record? => true, :kind_of? => true)
|
107
110
|
@resource = mock('resource', :null_object => true)
|
108
111
|
@collection = []
|
112
|
+
@parent_key = mock('parent key', :get => [])
|
109
113
|
@repository = mock('repository', :save => nil, :kind_of? => true)
|
110
|
-
@relationship = mock('relationship', :get_children => @collection, :
|
114
|
+
@relationship = mock('relationship', :get_children => @collection, :query => {}, :kind_of? => true, :child_key => [], :parent_key => @parent_key)
|
111
115
|
@association = DataMapper::Associations::OneToMany::Proxy.new(@relationship, @parent)
|
112
116
|
end
|
113
117
|
|
@@ -124,6 +128,7 @@ describe DataMapper::Associations::OneToMany::Proxy do
|
|
124
128
|
end
|
125
129
|
|
126
130
|
it 'should persist the addition after saving the association' do
|
131
|
+
@relationship.should_receive(:with_repository).with(@resource).and_yield(@repository)
|
127
132
|
do_add.should == return_value
|
128
133
|
@relationship.should_receive(:attach_parent).with(@resource, @parent)
|
129
134
|
@association.save
|
@@ -147,6 +152,7 @@ describe DataMapper::Associations::OneToMany::Proxy do
|
|
147
152
|
end
|
148
153
|
|
149
154
|
it 'should persist the removal after saving the association' do
|
155
|
+
@relationship.should_receive(:with_repository).with(@resource).and_yield(@repository)
|
150
156
|
do_remove.should == return_value
|
151
157
|
@relationship.should_receive(:attach_parent).with(@resource, nil)
|
152
158
|
@association.save
|
@@ -237,6 +243,7 @@ describe DataMapper::Associations::OneToMany::Proxy do
|
|
237
243
|
|
238
244
|
it 'should persist the removal after saving the association' do
|
239
245
|
do_replace.should == return_value
|
246
|
+
@relationship.should_receive(:with_repository).exactly(3).times.and_yield(@repository)
|
240
247
|
@relationship.should_receive(:attach_parent).with(@resource, nil)
|
241
248
|
@association.save
|
242
249
|
end
|
@@ -248,6 +255,7 @@ describe DataMapper::Associations::OneToMany::Proxy do
|
|
248
255
|
|
249
256
|
it 'should persist the addition after saving the association' do
|
250
257
|
do_replace.should == return_value
|
258
|
+
@relationship.should_receive(:with_repository).exactly(3).times.and_yield(@repository)
|
251
259
|
@relationship.should_receive(:attach_parent).with(@children[0], @parent)
|
252
260
|
@relationship.should_receive(:attach_parent).with(@children[1], @parent)
|
253
261
|
@association.save
|
@@ -334,7 +342,7 @@ describe DataMapper::Associations::OneToMany::Proxy do
|
|
334
342
|
it_should_behave_like 'a method that orphans the resource'
|
335
343
|
|
336
344
|
it 'should empty the collection' do
|
337
|
-
@association << mock('other resource')
|
345
|
+
@association << mock('other resource', :new_record? => false)
|
338
346
|
@association.should have(2).entries
|
339
347
|
do_remove
|
340
348
|
@association.should be_empty
|