datamapper 0.2.5 → 0.3.0
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.
- data/CHANGELOG +5 -1
- data/FAQ +96 -0
- data/QUICKLINKS +12 -0
- data/README +57 -155
- data/environment.rb +61 -43
- data/example.rb +30 -12
- data/lib/data_mapper.rb +6 -1
- data/lib/data_mapper/adapters/abstract_adapter.rb +0 -57
- data/lib/data_mapper/adapters/data_object_adapter.rb +203 -97
- data/lib/data_mapper/adapters/mysql_adapter.rb +4 -0
- data/lib/data_mapper/adapters/postgresql_adapter.rb +7 -1
- data/lib/data_mapper/adapters/sql/coersion.rb +3 -2
- data/lib/data_mapper/adapters/sql/commands/load_command.rb +29 -10
- data/lib/data_mapper/adapters/sql/mappings/associations_set.rb +4 -0
- data/lib/data_mapper/adapters/sql/mappings/column.rb +13 -9
- data/lib/data_mapper/adapters/sql/mappings/conditions.rb +172 -0
- data/lib/data_mapper/adapters/sql/mappings/table.rb +43 -17
- data/lib/data_mapper/adapters/sqlite3_adapter.rb +9 -2
- data/lib/data_mapper/associations.rb +75 -3
- data/lib/data_mapper/associations/belongs_to_association.rb +70 -36
- data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +195 -86
- data/lib/data_mapper/associations/has_many_association.rb +168 -61
- data/lib/data_mapper/associations/has_n_association.rb +23 -3
- data/lib/data_mapper/attributes.rb +73 -0
- data/lib/data_mapper/auto_migrations.rb +2 -6
- data/lib/data_mapper/base.rb +5 -9
- data/lib/data_mapper/database.rb +4 -3
- data/lib/data_mapper/embedded_value.rb +66 -30
- data/lib/data_mapper/identity_map.rb +1 -3
- data/lib/data_mapper/is/tree.rb +121 -0
- data/lib/data_mapper/migration.rb +155 -0
- data/lib/data_mapper/persistence.rb +532 -218
- data/lib/data_mapper/property.rb +306 -0
- data/lib/data_mapper/query.rb +164 -0
- data/lib/data_mapper/support/blank.rb +2 -2
- data/lib/data_mapper/support/connection_pool.rb +5 -6
- data/lib/data_mapper/support/enumerable.rb +3 -3
- data/lib/data_mapper/support/errors.rb +10 -1
- data/lib/data_mapper/support/inflector.rb +174 -238
- data/lib/data_mapper/support/object.rb +54 -0
- data/lib/data_mapper/support/serialization.rb +19 -1
- data/lib/data_mapper/support/string.rb +7 -16
- data/lib/data_mapper/support/symbol.rb +3 -15
- data/lib/data_mapper/support/typed_set.rb +68 -0
- data/lib/data_mapper/types/base.rb +44 -0
- data/lib/data_mapper/types/string.rb +34 -0
- data/lib/data_mapper/validations/number_validator.rb +40 -0
- data/lib/data_mapper/validations/string_validator.rb +20 -0
- data/lib/data_mapper/validations/validator.rb +13 -0
- data/performance.rb +26 -1
- data/profile_data_mapper.rb +1 -1
- data/rakefile.rb +42 -2
- data/spec/acts_as_tree_spec.rb +11 -3
- data/spec/adapters/data_object_adapter_spec.rb +31 -0
- data/spec/associations/belongs_to_association_spec.rb +98 -0
- data/spec/associations/has_and_belongs_to_many_association_spec.rb +377 -0
- data/spec/associations/has_many_association_spec.rb +337 -0
- data/spec/attributes_spec.rb +23 -1
- data/spec/auto_migrations_spec.rb +86 -29
- data/spec/callbacks_spec.rb +107 -0
- data/spec/column_spec.rb +5 -2
- data/spec/count_command_spec.rb +33 -1
- data/spec/database_spec.rb +18 -0
- data/spec/dependency_spec.rb +4 -2
- data/spec/embedded_value_spec.rb +8 -8
- data/spec/fixtures/people.yaml +1 -1
- data/spec/fixtures/projects.yaml +10 -1
- data/spec/fixtures/tasks.yaml +6 -0
- data/spec/fixtures/tasks_tasks.yaml +2 -0
- data/spec/fixtures/tomatoes.yaml +1 -0
- data/spec/is_a_tree_spec.rb +149 -0
- data/spec/load_command_spec.rb +71 -9
- data/spec/magic_columns_spec.rb +17 -2
- data/spec/migration_spec.rb +267 -0
- data/spec/models/animal.rb +1 -1
- data/spec/models/candidate.rb +8 -0
- data/spec/models/career.rb +1 -1
- data/spec/models/chain.rb +8 -0
- data/spec/models/comment.rb +1 -1
- data/spec/models/exhibit.rb +1 -1
- data/spec/models/fence.rb +7 -0
- data/spec/models/fruit.rb +2 -2
- data/spec/models/job.rb +8 -0
- data/spec/models/person.rb +2 -3
- data/spec/models/post.rb +1 -1
- data/spec/models/project.rb +21 -1
- data/spec/models/section.rb +1 -1
- data/spec/models/serializer.rb +1 -1
- data/spec/models/task.rb +9 -0
- data/spec/models/tomato.rb +27 -0
- data/spec/models/user.rb +8 -2
- data/spec/models/zoo.rb +2 -7
- data/spec/paranoia_spec.rb +1 -1
- data/spec/{base_spec.rb → persistence_spec.rb} +207 -18
- data/spec/postgres_spec.rb +48 -6
- data/spec/property_spec.rb +90 -9
- data/spec/query_spec.rb +71 -5
- data/spec/save_command_spec.rb +11 -0
- data/spec/spec_helper.rb +14 -11
- data/spec/support/blank_spec.rb +8 -0
- data/spec/support/inflector_spec.rb +41 -0
- data/spec/support/object_spec.rb +9 -0
- data/spec/{serialization_spec.rb → support/serialization_spec.rb} +1 -1
- data/spec/support/silence_spec.rb +15 -0
- data/spec/{support_spec.rb → support/string_spec.rb} +3 -3
- data/spec/support/struct_spec.rb +12 -0
- data/spec/support/typed_set_spec.rb +66 -0
- data/spec/table_spec.rb +3 -3
- data/spec/types/string.rb +81 -0
- data/spec/validates_uniqueness_of_spec.rb +17 -0
- data/spec/validations/number_validator.rb +59 -0
- data/spec/validations/string_validator.rb +14 -0
- metadata +59 -17
- data/do_performance.rb +0 -153
- data/lib/data_mapper/support/active_record_impersonation.rb +0 -103
- data/lib/data_mapper/support/weak_hash.rb +0 -46
- data/spec/active_record_impersonation_spec.rb +0 -129
- data/spec/associations_spec.rb +0 -232
- data/spec/conditions_spec.rb +0 -49
- data/spec/has_many_association_spec.rb +0 -173
- data/spec/models/animals_exhibit.rb +0 -8
data/rakefile.rb
CHANGED
@@ -13,7 +13,7 @@ task :default => 'dm:spec'
|
|
13
13
|
|
14
14
|
task :environment => 'dm:environment'
|
15
15
|
|
16
|
-
namespace :dm do
|
16
|
+
dm = namespace :dm do
|
17
17
|
|
18
18
|
desc "Setup Environment"
|
19
19
|
task :environment do
|
@@ -24,6 +24,10 @@ namespace :dm do
|
|
24
24
|
Spec::Rake::SpecTask.new('spec') do |t|
|
25
25
|
t.spec_opts = ["--format", "specdoc", "--colour"]
|
26
26
|
t.spec_files = FileList[(ENV['FILES'] || 'spec/**/*_spec.rb')]
|
27
|
+
unless ENV['NO_RCOV']
|
28
|
+
t.rcov = true
|
29
|
+
t.rcov_opts = ['--exclude', 'examples,spec,environment.rb']
|
30
|
+
end
|
27
31
|
end
|
28
32
|
|
29
33
|
desc "Run comparison with ActiveRecord"
|
@@ -36,12 +40,48 @@ namespace :dm do
|
|
36
40
|
load 'profile_data_mapper.rb'
|
37
41
|
end
|
38
42
|
|
43
|
+
namespace :spec do
|
44
|
+
def set_model_mode(fl, mode)
|
45
|
+
fl.each do |fname|
|
46
|
+
contents = File.open(fname, 'r') { |f| f.read }
|
47
|
+
|
48
|
+
if mode == :compat
|
49
|
+
contents.gsub!(/#< DataMapper::Base #/, '< DataMapper::Base #')
|
50
|
+
contents.gsub!(/include DataMapper::Persistence/, '#include DataMapper::Persistence')
|
51
|
+
elsif mode == :normal
|
52
|
+
contents.gsub!(/< DataMapper::Base #/, '#< DataMapper::Base #')
|
53
|
+
contents.gsub!(/#include DataMapper::Persistence/, 'include DataMapper::Persistence')
|
54
|
+
else
|
55
|
+
raise "Unknown mode #{mode}."
|
56
|
+
end
|
57
|
+
|
58
|
+
File.open(fname, 'w') do |f|
|
59
|
+
f.write(contents)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
desc "Run specifications with DataMapper::Base compatibilty"
|
65
|
+
task :compat do
|
66
|
+
fl = FileList['spec/**/*.rb'].exclude(/\b\.svn/)
|
67
|
+
|
68
|
+
set_model_mode(fl, :compat)
|
69
|
+
|
70
|
+
begin
|
71
|
+
dm[:spec].execute
|
72
|
+
ensure
|
73
|
+
set_model_mode(fl, :normal)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
39
77
|
end
|
40
78
|
|
41
|
-
PACKAGE_VERSION = '0.
|
79
|
+
PACKAGE_VERSION = '0.3.0'
|
42
80
|
|
43
81
|
PACKAGE_FILES = FileList[
|
44
82
|
'README',
|
83
|
+
'FAQ',
|
84
|
+
'QUICKLINKS',
|
45
85
|
'CHANGELOG',
|
46
86
|
'MIT-LICENSE',
|
47
87
|
'*.rb',
|
data/spec/acts_as_tree_spec.rb
CHANGED
@@ -3,13 +3,17 @@ require File.dirname(__FILE__) + "/spec_helper"
|
|
3
3
|
describe('A tree') do
|
4
4
|
|
5
5
|
before(:all) do
|
6
|
-
class Node
|
6
|
+
class Node #< DataMapper::Base # please do not remove this
|
7
7
|
include DataMapper::Persistence
|
8
8
|
|
9
9
|
property :name, :string
|
10
10
|
|
11
11
|
belongs_to :parent, :class => 'Node'
|
12
12
|
has_many :children, :class => 'Node', :foreign_key => 'parent_id'
|
13
|
+
|
14
|
+
def <=>(other)
|
15
|
+
name <=> other.name
|
16
|
+
end
|
13
17
|
end
|
14
18
|
|
15
19
|
Node.auto_migrate!
|
@@ -51,9 +55,13 @@ describe('A tree') do
|
|
51
55
|
grand.should have(2).children
|
52
56
|
|
53
57
|
root.should == grand # true since +root+ and +grand+ are objects with identical types and attributes.
|
54
|
-
root.should_not eql(grand) # false since +root+ and +grand+ are in different sessions.
|
58
|
+
root.object_id.should_not eql(grand.object_id) # false since +root+ and +grand+ are in different sessions.
|
55
59
|
|
56
|
-
grand.children
|
60
|
+
grand.children.should include(one)
|
61
|
+
one.reload
|
62
|
+
one.children.should include(one_one)
|
63
|
+
|
64
|
+
grand.children.first.children.first.should eql(one_one)
|
57
65
|
end
|
58
66
|
|
59
67
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe DataMapper::Adapters::DataObjectAdapter do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
fixtures(:zoos)
|
7
|
+
end
|
8
|
+
|
9
|
+
if ENV['ADAPTER'] == 'postgresql'
|
10
|
+
it "should be able to be given a port" do
|
11
|
+
options = {
|
12
|
+
:adapter => 'postgresql',
|
13
|
+
:database => 'data_mapper_1',
|
14
|
+
:port => 4001
|
15
|
+
}
|
16
|
+
db = DataMapper::Database.setup(:my_postgres, options)
|
17
|
+
db.port.should == 4001
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should use DB defaults when creating an empty record" do
|
22
|
+
comment = Comment.create({})
|
23
|
+
comment.new_record?.should be_false
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should raise an argument error on create if an attribute value is not a primitive" do
|
27
|
+
lambda { Zoo.create(:name => [nil, 'bob']) }.should raise_error(ArgumentError)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should accept a subclass as a valid type if the parent is a valid type"
|
31
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe DataMapper::Associations::BelongsToAssociation do
|
4
|
+
before(:all) do
|
5
|
+
fixtures(:zoos)
|
6
|
+
end
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
@aviary = Exhibit.first(:name => 'Monkey Mayhem')
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should provide a shallow_append method that doesn't impact the complementary association" do
|
13
|
+
project = Project.new
|
14
|
+
section = Section.new
|
15
|
+
section.send(:project_association).shallow_append(project)
|
16
|
+
project.sections.should be_empty
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'has a zoo association' do
|
20
|
+
@aviary.zoo.class.should == Zoo
|
21
|
+
Exhibit.new.zoo.should == nil
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'belongs to a zoo' do
|
25
|
+
@aviary.zoo.should == @aviary.database_context.first(Zoo, :name => 'San Diego')
|
26
|
+
end
|
27
|
+
|
28
|
+
it "is assigned a zoo_id" do
|
29
|
+
zoo = Zoo.first
|
30
|
+
exhibit = Exhibit.new(:name => 'bob')
|
31
|
+
exhibit.zoo = zoo
|
32
|
+
exhibit.instance_variable_get("@zoo_id").should == zoo.id
|
33
|
+
|
34
|
+
exhibit.save.should eql(true)
|
35
|
+
|
36
|
+
zoo2 = Zoo.first
|
37
|
+
zoo2.exhibits.should include(exhibit)
|
38
|
+
|
39
|
+
exhibit.destroy!
|
40
|
+
|
41
|
+
zoo = Zoo.new(:name => 'bob')
|
42
|
+
bob = Exhibit.new(:name => 'bob')
|
43
|
+
zoo.exhibits << bob
|
44
|
+
zoo.save.should eql(true)
|
45
|
+
|
46
|
+
zoo.exhibits.first.should_not be_a_new_record
|
47
|
+
|
48
|
+
bob.destroy!
|
49
|
+
zoo.destroy!
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should not assign zoo_id when passed nil" do
|
53
|
+
# pending "http://wm.lighthouseapp.com/projects/4819-datamapper/tickets/147"
|
54
|
+
exhibit = Exhibit.first
|
55
|
+
exhibit.zoo_id = nil
|
56
|
+
exhibit.zoo_id.should be_nil
|
57
|
+
exhibit.save.should == true
|
58
|
+
|
59
|
+
exhibit.reload
|
60
|
+
exhibit.zoo_id.should be_nil
|
61
|
+
exhibit.zoo.should be_nil
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should be marked dirty if the complementary association is a new_record and the instance is already saved" do
|
65
|
+
zoo = Zoo.new(:name => "My Zoo")
|
66
|
+
tiger = Exhibit.create(:name => "Tiger")
|
67
|
+
zoo.exhibits << tiger
|
68
|
+
tiger.should be_dirty
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'can build its zoo' do
|
72
|
+
database do |db|
|
73
|
+
e = Exhibit.new({:name => 'Super Extra Crazy Monkey Cage'})
|
74
|
+
e.zoo.should == nil
|
75
|
+
e.build_zoo({:name => 'Monkey Zoo'})
|
76
|
+
e.zoo.class == Zoo
|
77
|
+
e.zoo.new_record?.should == true
|
78
|
+
|
79
|
+
e.save
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'can build its zoo' do
|
84
|
+
database do |db|
|
85
|
+
e = Exhibit.new({:name => 'Super Extra Crazy Monkey Cage'})
|
86
|
+
e.zoo.should == nil
|
87
|
+
e.create_zoo({:name => 'Monkey Zoo'})
|
88
|
+
e.zoo.class == Zoo
|
89
|
+
e.zoo.new_record?.should == false
|
90
|
+
e.save
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
after(:all) do
|
95
|
+
fixtures('zoos')
|
96
|
+
fixtures('exhibits')
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,377 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe DataMapper::Associations::HasAndBelongsToManyAssociation do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
fixtures(:animals)
|
7
|
+
fixtures(:exhibits)
|
8
|
+
end
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
@amazonia = Exhibit.first :name => 'Amazonia'
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should generate the SQL for a join statement" do
|
15
|
+
animals_association = database(:mock).schema[Exhibit].associations.find { |a| a.name == :animals }
|
16
|
+
|
17
|
+
animals_association.to_sql.should == <<-EOS.compress_lines
|
18
|
+
JOIN `animals_exhibits` ON `animals_exhibits`.`exhibit_id` = `exhibits`.`id`
|
19
|
+
JOIN `animals` ON `animals`.`id` = `animals_exhibits`.`animal_id`
|
20
|
+
EOS
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should load associations" do
|
24
|
+
database do
|
25
|
+
froggy = Animal.first(:name => 'Frog')
|
26
|
+
froggy.exhibits.size.should == 1
|
27
|
+
froggy.exhibits.first.should == Exhibit.first(:name => 'Amazonia')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'has an animals association' do
|
32
|
+
[@amazonia, Exhibit.new].each do |exhibit|
|
33
|
+
exhibit.animals.class.should == DataMapper::Associations::HasAndBelongsToManyAssociation::Set
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'has many animals' do
|
38
|
+
@amazonia.animals.size.should == 1
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should load associations magically' do
|
42
|
+
Exhibit.all.each do |exhibit|
|
43
|
+
exhibit.animals.each do |animal|
|
44
|
+
animal.exhibits.should include(exhibit)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should allow association of additional objects' do
|
50
|
+
buffalo = Animal.create(:name => "Buffalo")
|
51
|
+
@amazonia.animals << buffalo
|
52
|
+
@amazonia.animals.size.should == 2
|
53
|
+
@amazonia.save!
|
54
|
+
@amazonia.reload!
|
55
|
+
@amazonia.animals.should have(2).entries
|
56
|
+
|
57
|
+
other = Exhibit[@amazonia.id]
|
58
|
+
other.animals.should have(2).entries
|
59
|
+
|
60
|
+
@amazonia.animals.delete(buffalo).should_not be_nil
|
61
|
+
@amazonia.animals.should be_dirty
|
62
|
+
@amazonia.save!
|
63
|
+
|
64
|
+
other = Exhibit[@amazonia.id]
|
65
|
+
other.animals.should have(1).entries
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should allow association of additional objects (CLEAN)" do
|
69
|
+
# pending "http://wm.lighthouseapp.com/projects/4819-datamapper/tickets/92"
|
70
|
+
|
71
|
+
ted = Exhibit.create(:name => 'Ted')
|
72
|
+
ted.should_not be_dirty
|
73
|
+
|
74
|
+
zest = Animal.create(:name => 'Zest')
|
75
|
+
zest.should_not be_dirty
|
76
|
+
|
77
|
+
ted.animals << zest
|
78
|
+
ted.should be_dirty
|
79
|
+
ted.save
|
80
|
+
|
81
|
+
ted.reload!
|
82
|
+
ted.should_not be_dirty
|
83
|
+
ted.should have(1).animals
|
84
|
+
|
85
|
+
ted2 = Exhibit[ted.key]
|
86
|
+
ted2.should_not be_dirty
|
87
|
+
ted2.should have(1).animals
|
88
|
+
|
89
|
+
ted2.destroy!
|
90
|
+
zest.destroy!
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should allow you to fill and clear an association' do
|
94
|
+
marcy = Exhibit.create(:name => 'marcy')
|
95
|
+
|
96
|
+
Animal.all.each do |animal|
|
97
|
+
marcy.animals << animal
|
98
|
+
end
|
99
|
+
|
100
|
+
marcy.save.should eql(true)
|
101
|
+
marcy.should have(Animal.count).animals
|
102
|
+
|
103
|
+
marcy.animals.clear
|
104
|
+
marcy.should have(0).animals
|
105
|
+
|
106
|
+
marcy.save.should eql(true)
|
107
|
+
|
108
|
+
marcys_stand_in = Exhibit[marcy.id]
|
109
|
+
marcys_stand_in.should have(0).animals
|
110
|
+
|
111
|
+
marcy.destroy!
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should allow you to delete a specific association member' do
|
115
|
+
walter = Exhibit.create(:name => 'walter')
|
116
|
+
|
117
|
+
Animal.all.each do |animal|
|
118
|
+
walter.animals << animal
|
119
|
+
end
|
120
|
+
|
121
|
+
walter.save.should eql(true)
|
122
|
+
walter.should have(Animal.count).animals
|
123
|
+
|
124
|
+
delete_me = walter.animals.first
|
125
|
+
walter.animals.delete(delete_me).should eql(delete_me)
|
126
|
+
walter.animals.delete(delete_me).should eql(nil)
|
127
|
+
|
128
|
+
walter.should have(Animal.count - 1).animals
|
129
|
+
walter.save.should eql(true)
|
130
|
+
|
131
|
+
walters_stand_in = Exhibit[walter.id]
|
132
|
+
walters_stand_in.animals.size.should eql(walter.animals.size)
|
133
|
+
|
134
|
+
walter.destroy!
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should allow updates to associations using association_keys=" do
|
138
|
+
# pending "http://wm.lighthouseapp.com/projects/4819-datamapper/tickets/109-associations-should-support-association_keys-methods"
|
139
|
+
database(:default) do
|
140
|
+
meerkat = Animal.create(:name => "Meerkat")
|
141
|
+
dunes = Exhibit.create(:name => "Dunes")
|
142
|
+
|
143
|
+
|
144
|
+
dunes.animals.should be_empty
|
145
|
+
dunes.send(:animals_keys=, meerkat.key)
|
146
|
+
dunes.save.should be_true
|
147
|
+
|
148
|
+
dunes.should have(1).animals
|
149
|
+
dunes.animals.should include(meerkat)
|
150
|
+
|
151
|
+
dunes.reload!
|
152
|
+
dunes.should have(1).animals
|
153
|
+
|
154
|
+
dunes.destroy!
|
155
|
+
meerkat.destroy!
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should allow you to 'append' multiple associated objects at once" do
|
160
|
+
dunes = Exhibit.create(:name => 'Dunes')
|
161
|
+
|
162
|
+
lambda { dunes.animals << @amazonia.animals }.should_not raise_error(ArgumentError)
|
163
|
+
lambda { dunes.animals << Animal.all }.should_not raise_error(ArgumentError)
|
164
|
+
|
165
|
+
dunes.destroy!
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should raise an error when attempting to associate an object not of the correct type (assuming added model doesn't inherit from the correct type)" do
|
169
|
+
# pending("see: http://wm.lighthouseapp.com/projects/4819-datamapper/tickets/91")
|
170
|
+
@amazonia.animals.should_not be_empty
|
171
|
+
chuck = Person.new(:name => "Chuck")
|
172
|
+
|
173
|
+
## InvalidRecord isn't the error we should use here....needs to be changed
|
174
|
+
lambda { @amazonia.animals << chuck }.should raise_error(ArgumentError)
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should associate an object which has inherited from the correct type into an association" do
|
179
|
+
# pending("see: http://wm.lighthouseapp.com/projects/4819-datamapper/tickets/91")
|
180
|
+
programmer = Career.first(:name => 'Programmer')
|
181
|
+
programmer.followers.should_not be_empty
|
182
|
+
|
183
|
+
sales_person = SalesPerson.new(:name => 'Chuck')
|
184
|
+
|
185
|
+
lambda { programmer.followers << sales_person }.should_not raise_error(ArgumentError)
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should correctly handle dependent associations ~cascading destroy~ (:destroy)" do
|
190
|
+
class Chain
|
191
|
+
has_and_belongs_to_many :chains, :dependent => :destroy
|
192
|
+
end
|
193
|
+
Chain.auto_migrate!
|
194
|
+
|
195
|
+
chain = Chain.create(:name => "1")
|
196
|
+
chain.chains << Chain.create(:name => "2")
|
197
|
+
chain.chains << Chain.create(:name => "3")
|
198
|
+
chain.chains << Chain.create(:name => "4")
|
199
|
+
chain.save
|
200
|
+
chain4 = Chain.create(:name => "5")
|
201
|
+
chain.chains.first.chains << chain4
|
202
|
+
chain.chains.first.save
|
203
|
+
chain4.chains << Chain.first(:name => "3")
|
204
|
+
chain4.save
|
205
|
+
chain = Chain[chain.key]
|
206
|
+
|
207
|
+
chain.destroy!
|
208
|
+
Chain.first(:name => "2").should be_nil
|
209
|
+
Chain.first(:name => "3").should be_nil
|
210
|
+
Chain.first(:name => "4").should be_nil
|
211
|
+
Chain.first(:name => "5").should be_nil
|
212
|
+
|
213
|
+
Chain.delete_all
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should correctly handle dependent associations ~no cascade~ (:delete)" do
|
217
|
+
class Chain
|
218
|
+
has_and_belongs_to_many :chains, :dependent => :delete
|
219
|
+
end
|
220
|
+
Chain.auto_migrate!
|
221
|
+
|
222
|
+
chain = Chain.create(:name => "1")
|
223
|
+
chain.chains << Chain.create(:name => "2")
|
224
|
+
chain.chains << Chain.create(:name => "3")
|
225
|
+
chain.chains << Chain.create(:name => "4")
|
226
|
+
chain.save
|
227
|
+
chain5 = Chain.create(:name => "5")
|
228
|
+
chain.chains.first.chains << chain5
|
229
|
+
chain.chains.first.save
|
230
|
+
chain = Chain[chain.key]
|
231
|
+
|
232
|
+
chain.destroy!
|
233
|
+
Chain.first(:name => "2").should be_nil
|
234
|
+
Chain.first(:name => "3").should be_nil
|
235
|
+
Chain.first(:name => "4").should be_nil
|
236
|
+
Chain.first(:name => "5").should_not be_nil
|
237
|
+
|
238
|
+
Chain.delete_all
|
239
|
+
end
|
240
|
+
|
241
|
+
it "should correctly handle dependent associations (:protect)" do
|
242
|
+
class Chain
|
243
|
+
has_and_belongs_to_many :chains, :dependent => :protect
|
244
|
+
end
|
245
|
+
Chain.auto_migrate!
|
246
|
+
|
247
|
+
chain = Chain.create(:name => "1")
|
248
|
+
chain.chains << Chain.create(:name => "2")
|
249
|
+
chain.chains << Chain.create(:name => "3")
|
250
|
+
chain.chains << Chain.create(:name => "4")
|
251
|
+
chain.save
|
252
|
+
chain4 = Chain.create(:name => "5")
|
253
|
+
chain.chains.first.chains << chain4
|
254
|
+
chain.chains.first.save
|
255
|
+
chain = Chain[chain.key]
|
256
|
+
|
257
|
+
lambda { chain.destroy! }.should raise_error(DataMapper::AssociationProtectedError)
|
258
|
+
|
259
|
+
Chain.delete_all
|
260
|
+
end
|
261
|
+
|
262
|
+
it "should throw AssociationProtectedError even when @items have not been loaded yet (:protect)" do
|
263
|
+
class Chain
|
264
|
+
has_and_belongs_to_many :chains, :dependent => :protect
|
265
|
+
end
|
266
|
+
Chain.auto_migrate!
|
267
|
+
|
268
|
+
chain = Chain.create(:name => "1")
|
269
|
+
chain.chains << Chain.create(:name => "2")
|
270
|
+
chain.chains << Chain.create(:name => "3")
|
271
|
+
chain.chains << Chain.create(:name => "4")
|
272
|
+
chain.save
|
273
|
+
chain4 = Chain.create(:name => "5")
|
274
|
+
chain.chains.first.chains << chain4
|
275
|
+
chain.chains.first.save
|
276
|
+
chain = Chain[chain.key]
|
277
|
+
|
278
|
+
lambda { chain.destroy! }.should raise_error(DataMapper::AssociationProtectedError)
|
279
|
+
|
280
|
+
Chain.delete_all
|
281
|
+
end
|
282
|
+
|
283
|
+
it "should correctly handle dependent associations (:nullify)" do
|
284
|
+
class Chain
|
285
|
+
has_and_belongs_to_many :chains, :dependent => :nullify
|
286
|
+
end
|
287
|
+
Chain.auto_migrate!
|
288
|
+
|
289
|
+
chain = Chain.create(:name => "1")
|
290
|
+
chain.chains << Chain.create(:name => "2")
|
291
|
+
chain.chains << Chain.create(:name => "3")
|
292
|
+
chain.chains << Chain.create(:name => "4")
|
293
|
+
chain.save
|
294
|
+
chain4 = Chain.create(:name => "5")
|
295
|
+
chain.chains.first.chains << chain4
|
296
|
+
chain.chains.first.save
|
297
|
+
chain = Chain[chain.key]
|
298
|
+
|
299
|
+
chain.destroy!
|
300
|
+
Chain.first(:name => "2").should_not be_nil
|
301
|
+
Chain.first(:name => "3").should_not be_nil
|
302
|
+
Chain.first(:name => "4").should_not be_nil
|
303
|
+
|
304
|
+
Chain.delete_all
|
305
|
+
end
|
306
|
+
|
307
|
+
end
|
308
|
+
|
309
|
+
describe DataMapper::Associations::HasAndBelongsToManyAssociation, "self-referential relationship" do
|
310
|
+
|
311
|
+
before(:all) do
|
312
|
+
fixtures(:tasks)
|
313
|
+
end
|
314
|
+
|
315
|
+
before(:each) do
|
316
|
+
@task_relax = Task.first(:name => "task_relax")
|
317
|
+
end
|
318
|
+
|
319
|
+
it "should allow a self-referential habtm by creating a related_* column for the right foreign key" do
|
320
|
+
tasks_assocation = database(:mock).schema[Task].associations.find { |a| a.name == :tasks }
|
321
|
+
|
322
|
+
tasks_assocation.right_foreign_key.name.should == :related_task_id
|
323
|
+
end
|
324
|
+
|
325
|
+
it "should load the self-referential association" do
|
326
|
+
database do
|
327
|
+
task_relax = Task.first(:name => "task_relax")
|
328
|
+
task_relax.tasks.size.should == 1
|
329
|
+
task_relax.tasks.first.should == Task.first(:name => "task_drink_heartily")
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
it "should allow a mirrored relationship between two rows (no infinite recursion)" do
|
334
|
+
task_vacation = Task.first(:name => "task_vacation")
|
335
|
+
|
336
|
+
@task_relax.tasks << task_vacation
|
337
|
+
@task_relax.save
|
338
|
+
|
339
|
+
task_vacation.tasks << @task_relax
|
340
|
+
task_vacation.save
|
341
|
+
|
342
|
+
@task_relax.reload!
|
343
|
+
@task_relax.tasks.should include(task_vacation)
|
344
|
+
|
345
|
+
task_vacation.reload!
|
346
|
+
task_vacation.tasks.should include(@task_relax)
|
347
|
+
end
|
348
|
+
|
349
|
+
end
|
350
|
+
|
351
|
+
describe DataMapper::Associations::HasAndBelongsToManyAssociation, "compatibility with belongs_to" do
|
352
|
+
|
353
|
+
it "should be able to save a job without interferring with applications" do
|
354
|
+
programmer = Job.create(:name => 'Programmer')
|
355
|
+
manager = Job.create(:name => 'Manager')
|
356
|
+
|
357
|
+
bob = Candidate.create(:name => 'Bob')
|
358
|
+
|
359
|
+
bob.applications << programmer << manager
|
360
|
+
|
361
|
+
bob.applications.should have(2).entries
|
362
|
+
bob.job.should be_nil
|
363
|
+
|
364
|
+
bob.job = programmer
|
365
|
+
|
366
|
+
bob.should be_dirty
|
367
|
+
bob.applications.should be_dirty
|
368
|
+
bob.job.should_not be_dirty # Because no property is changed on the has_many side.
|
369
|
+
|
370
|
+
bob.save!.should == true
|
371
|
+
|
372
|
+
impostor = Candidate[bob.id]
|
373
|
+
|
374
|
+
impostor.applications.should have(2).entries
|
375
|
+
impostor.job.should == programmer
|
376
|
+
end
|
377
|
+
end
|