epugh-sequel 0.0.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/README.rdoc +652 -0
- data/VERSION.yml +4 -0
- data/bin/sequel +104 -0
- data/lib/sequel.rb +1 -0
- data/lib/sequel/adapters/ado.rb +85 -0
- data/lib/sequel/adapters/db2.rb +132 -0
- data/lib/sequel/adapters/dbi.rb +101 -0
- data/lib/sequel/adapters/do.rb +197 -0
- data/lib/sequel/adapters/do/mysql.rb +38 -0
- data/lib/sequel/adapters/do/postgres.rb +92 -0
- data/lib/sequel/adapters/do/sqlite.rb +31 -0
- data/lib/sequel/adapters/firebird.rb +307 -0
- data/lib/sequel/adapters/informix.rb +75 -0
- data/lib/sequel/adapters/jdbc.rb +485 -0
- data/lib/sequel/adapters/jdbc/h2.rb +62 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +56 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +23 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +101 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +43 -0
- data/lib/sequel/adapters/mysql.rb +370 -0
- data/lib/sequel/adapters/odbc.rb +184 -0
- data/lib/sequel/adapters/openbase.rb +57 -0
- data/lib/sequel/adapters/oracle.rb +140 -0
- data/lib/sequel/adapters/postgres.rb +453 -0
- data/lib/sequel/adapters/shared/mssql.rb +93 -0
- data/lib/sequel/adapters/shared/mysql.rb +341 -0
- data/lib/sequel/adapters/shared/oracle.rb +62 -0
- data/lib/sequel/adapters/shared/postgres.rb +743 -0
- data/lib/sequel/adapters/shared/progress.rb +34 -0
- data/lib/sequel/adapters/shared/sqlite.rb +263 -0
- data/lib/sequel/adapters/sqlite.rb +243 -0
- data/lib/sequel/adapters/utils/date_format.rb +21 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +75 -0
- data/lib/sequel/adapters/utils/unsupported.rb +62 -0
- data/lib/sequel/connection_pool.rb +258 -0
- data/lib/sequel/core.rb +204 -0
- data/lib/sequel/core_sql.rb +185 -0
- data/lib/sequel/database.rb +687 -0
- data/lib/sequel/database/schema_generator.rb +324 -0
- data/lib/sequel/database/schema_methods.rb +164 -0
- data/lib/sequel/database/schema_sql.rb +324 -0
- data/lib/sequel/dataset.rb +422 -0
- data/lib/sequel/dataset/convenience.rb +237 -0
- data/lib/sequel/dataset/prepared_statements.rb +220 -0
- data/lib/sequel/dataset/sql.rb +1105 -0
- data/lib/sequel/deprecated.rb +529 -0
- data/lib/sequel/exceptions.rb +44 -0
- data/lib/sequel/extensions/blank.rb +42 -0
- data/lib/sequel/extensions/inflector.rb +288 -0
- data/lib/sequel/extensions/pagination.rb +96 -0
- data/lib/sequel/extensions/pretty_table.rb +78 -0
- data/lib/sequel/extensions/query.rb +48 -0
- data/lib/sequel/extensions/string_date_time.rb +47 -0
- data/lib/sequel/metaprogramming.rb +44 -0
- data/lib/sequel/migration.rb +212 -0
- data/lib/sequel/model.rb +142 -0
- data/lib/sequel/model/association_reflection.rb +263 -0
- data/lib/sequel/model/associations.rb +1024 -0
- data/lib/sequel/model/base.rb +911 -0
- data/lib/sequel/model/deprecated.rb +188 -0
- data/lib/sequel/model/deprecated_hooks.rb +103 -0
- data/lib/sequel/model/deprecated_inflector.rb +335 -0
- data/lib/sequel/model/deprecated_validations.rb +384 -0
- data/lib/sequel/model/errors.rb +37 -0
- data/lib/sequel/model/exceptions.rb +7 -0
- data/lib/sequel/model/inflections.rb +230 -0
- data/lib/sequel/model/plugins.rb +74 -0
- data/lib/sequel/object_graph.rb +230 -0
- data/lib/sequel/plugins/caching.rb +122 -0
- data/lib/sequel/plugins/hook_class_methods.rb +122 -0
- data/lib/sequel/plugins/schema.rb +53 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
- data/lib/sequel/plugins/validation_class_methods.rb +373 -0
- data/lib/sequel/sql.rb +854 -0
- data/lib/sequel/version.rb +11 -0
- data/lib/sequel_core.rb +1 -0
- data/lib/sequel_model.rb +1 -0
- data/spec/adapters/ado_spec.rb +46 -0
- data/spec/adapters/firebird_spec.rb +376 -0
- data/spec/adapters/informix_spec.rb +96 -0
- data/spec/adapters/mysql_spec.rb +875 -0
- data/spec/adapters/oracle_spec.rb +272 -0
- data/spec/adapters/postgres_spec.rb +692 -0
- data/spec/adapters/spec_helper.rb +10 -0
- data/spec/adapters/sqlite_spec.rb +550 -0
- data/spec/core/connection_pool_spec.rb +526 -0
- data/spec/core/core_ext_spec.rb +156 -0
- data/spec/core/core_sql_spec.rb +528 -0
- data/spec/core/database_spec.rb +1214 -0
- data/spec/core/dataset_spec.rb +3513 -0
- data/spec/core/expression_filters_spec.rb +363 -0
- data/spec/core/migration_spec.rb +261 -0
- data/spec/core/object_graph_spec.rb +280 -0
- data/spec/core/pretty_table_spec.rb +58 -0
- data/spec/core/schema_generator_spec.rb +167 -0
- data/spec/core/schema_spec.rb +778 -0
- data/spec/core/spec_helper.rb +82 -0
- data/spec/core/version_spec.rb +7 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/caching_spec.rb +201 -0
- data/spec/extensions/hook_class_methods_spec.rb +470 -0
- data/spec/extensions/inflector_spec.rb +122 -0
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/extensions/schema_spec.rb +111 -0
- data/spec/extensions/single_table_inheritance_spec.rb +53 -0
- data/spec/extensions/spec_helper.rb +90 -0
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/extensions/validation_class_methods_spec.rb +1054 -0
- data/spec/integration/dataset_test.rb +160 -0
- data/spec/integration/eager_loader_test.rb +683 -0
- data/spec/integration/prepared_statement_test.rb +130 -0
- data/spec/integration/schema_test.rb +183 -0
- data/spec/integration/spec_helper.rb +75 -0
- data/spec/integration/type_test.rb +96 -0
- data/spec/model/association_reflection_spec.rb +93 -0
- data/spec/model/associations_spec.rb +1780 -0
- data/spec/model/base_spec.rb +494 -0
- data/spec/model/caching_spec.rb +217 -0
- data/spec/model/dataset_methods_spec.rb +78 -0
- data/spec/model/eager_loading_spec.rb +1165 -0
- data/spec/model/hooks_spec.rb +472 -0
- data/spec/model/inflector_spec.rb +126 -0
- data/spec/model/model_spec.rb +588 -0
- data/spec/model/plugins_spec.rb +142 -0
- data/spec/model/record_spec.rb +1243 -0
- data/spec/model/schema_spec.rb +92 -0
- data/spec/model/spec_helper.rb +124 -0
- data/spec/model/validations_spec.rb +1080 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec.opts +0 -0
- data/spec/spec_config.rb.example +10 -0
- metadata +202 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe Sequel::Model::Associations::AssociationReflection, "#associated_class" do
|
|
4
|
+
before do
|
|
5
|
+
@c = Class.new(Sequel::Model)
|
|
6
|
+
class ::ParParent < Sequel::Model; end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "should use the :class value if present" do
|
|
10
|
+
@c.many_to_one :c, :class=>ParParent
|
|
11
|
+
@c.association_reflection(:c).keys.should include(:class)
|
|
12
|
+
@c.association_reflection(:c).associated_class.should == ParParent
|
|
13
|
+
end
|
|
14
|
+
it "should figure out the class if the :class value is not present" do
|
|
15
|
+
@c.many_to_one :c, :class=>'ParParent'
|
|
16
|
+
@c.association_reflection(:c).keys.should_not include(:class)
|
|
17
|
+
@c.association_reflection(:c).associated_class.should == ParParent
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe Sequel::Model::Associations::AssociationReflection, "#primary_key" do
|
|
22
|
+
before do
|
|
23
|
+
@c = Class.new(Sequel::Model)
|
|
24
|
+
class ::ParParent < Sequel::Model; end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should use the :primary_key value if present" do
|
|
28
|
+
@c.many_to_one :c, :class=>ParParent, :primary_key=>:blah__blah
|
|
29
|
+
@c.association_reflection(:c).keys.should include(:primary_key)
|
|
30
|
+
@c.association_reflection(:c).primary_key.should == :blah__blah
|
|
31
|
+
end
|
|
32
|
+
it "should use the associated table's primary key if :primary_key is not present" do
|
|
33
|
+
@c.many_to_one :c, :class=>'ParParent'
|
|
34
|
+
@c.association_reflection(:c).keys.should_not include(:primary_key)
|
|
35
|
+
@c.association_reflection(:c).primary_key.should == :id
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe Sequel::Model::Associations::AssociationReflection, "#reciprocal" do
|
|
40
|
+
it "should use the :reciprocal value if present" do
|
|
41
|
+
@c = Class.new(Sequel::Model)
|
|
42
|
+
@d = Class.new(Sequel::Model)
|
|
43
|
+
@c.many_to_one :c, :class=>@d, :reciprocal=>:xx
|
|
44
|
+
@c.association_reflection(:c).keys.should include(:reciprocal)
|
|
45
|
+
@c.association_reflection(:c).reciprocal.should == :xx
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "should figure out the reciprocal if the :reciprocal value is not present" do
|
|
49
|
+
class ::ParParent < Sequel::Model; end
|
|
50
|
+
class ::ParParentTwo < Sequel::Model; end
|
|
51
|
+
class ::ParParentThree < Sequel::Model; end
|
|
52
|
+
ParParent.many_to_one :par_parent_two
|
|
53
|
+
ParParentTwo.one_to_many :par_parents
|
|
54
|
+
ParParent.many_to_many :par_parent_threes
|
|
55
|
+
ParParentThree.many_to_many :par_parents
|
|
56
|
+
|
|
57
|
+
ParParent.association_reflection(:par_parent_two).keys.should_not include(:reciprocal)
|
|
58
|
+
ParParent.association_reflection(:par_parent_two).reciprocal.should == :par_parents
|
|
59
|
+
ParParentTwo.association_reflection(:par_parents).keys.should_not include(:reciprocal)
|
|
60
|
+
ParParentTwo.association_reflection(:par_parents).reciprocal.should == :par_parent_two
|
|
61
|
+
ParParent.association_reflection(:par_parent_threes).keys.should_not include(:reciprocal)
|
|
62
|
+
ParParent.association_reflection(:par_parent_threes).reciprocal.should == :par_parents
|
|
63
|
+
ParParentThree.association_reflection(:par_parents).keys.should_not include(:reciprocal)
|
|
64
|
+
ParParentThree.association_reflection(:par_parents).reciprocal.should == :par_parent_threes
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
describe Sequel::Model::Associations::AssociationReflection, "#select" do
|
|
69
|
+
before do
|
|
70
|
+
@c = Class.new(Sequel::Model)
|
|
71
|
+
class ::ParParent < Sequel::Model; end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "should use the :select value if present" do
|
|
75
|
+
@c.many_to_one :c, :class=>ParParent, :select=>[:par_parents__id]
|
|
76
|
+
@c.association_reflection(:c).keys.should include(:select)
|
|
77
|
+
@c.association_reflection(:c).select.should == [:par_parents__id]
|
|
78
|
+
end
|
|
79
|
+
it "should be the associated_table.* if :select is not present for a many_to_many associaiton" do
|
|
80
|
+
@c.many_to_many :cs, :class=>'ParParent'
|
|
81
|
+
@c.association_reflection(:cs).keys.should_not include(:select)
|
|
82
|
+
@c.association_reflection(:cs).select.should == :par_parents.*
|
|
83
|
+
end
|
|
84
|
+
it "should be if :select is not present for a many_to_one and one_to_many associaiton" do
|
|
85
|
+
@c.one_to_many :cs, :class=>'ParParent'
|
|
86
|
+
@c.association_reflection(:cs).keys.should_not include(:select)
|
|
87
|
+
@c.association_reflection(:cs).select.should == nil
|
|
88
|
+
@c.many_to_one :c, :class=>'ParParent'
|
|
89
|
+
@c.association_reflection(:c).keys.should_not include(:select)
|
|
90
|
+
@c.association_reflection(:c).select.should == nil
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
@@ -0,0 +1,1780 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe Sequel::Model, "associate" do
|
|
4
|
+
it "should use explicit class if given a class, symbol, or string" do
|
|
5
|
+
MODEL_DB.reset
|
|
6
|
+
klass = Class.new(Sequel::Model(:nodes))
|
|
7
|
+
class ::ParParent < Sequel::Model
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
klass.associate :many_to_one, :par_parent0, :class=>ParParent
|
|
11
|
+
klass.associate :one_to_many, :par_parent1s, :class=>'ParParent'
|
|
12
|
+
klass.associate :many_to_many, :par_parent2s, :class=>:ParParent
|
|
13
|
+
|
|
14
|
+
klass.association_reflection(:"par_parent0").associated_class.should == ParParent
|
|
15
|
+
klass.association_reflection(:"par_parent1s").associated_class.should == ParParent
|
|
16
|
+
klass.association_reflection(:"par_parent2s").associated_class.should == ParParent
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "should add a model_object and association_reflection accessors to the dataset, and return it with the current model object" do
|
|
20
|
+
MODEL_DB.reset
|
|
21
|
+
klass = Class.new(Sequel::Model(:nodes)) do
|
|
22
|
+
columns :id, :a_id
|
|
23
|
+
end
|
|
24
|
+
mod = Module.new do
|
|
25
|
+
def blah
|
|
26
|
+
filter{|o| o.__send__(association_reflection[:key]) > model_object.id*2}
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
klass.associate :many_to_one, :a, :class=>klass
|
|
31
|
+
klass.associate :one_to_many, :bs, :key=>:b_id, :class=>klass, :extend=>mod
|
|
32
|
+
klass.associate :many_to_many, :cs, :class=>klass
|
|
33
|
+
|
|
34
|
+
node = klass.load(:id=>1)
|
|
35
|
+
node.a_dataset.model_object.should == node
|
|
36
|
+
node.bs_dataset.model_object.should == node
|
|
37
|
+
node.cs_dataset.model_object.should == node
|
|
38
|
+
|
|
39
|
+
node.a_dataset.association_reflection.should == klass.association_reflection(:a)
|
|
40
|
+
node.bs_dataset.association_reflection.should == klass.association_reflection(:bs)
|
|
41
|
+
node.cs_dataset.association_reflection.should == klass.association_reflection(:cs)
|
|
42
|
+
|
|
43
|
+
node.bs_dataset.blah.sql.should == 'SELECT * FROM nodes WHERE ((nodes.b_id = 1) AND (b_id > 2))'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "should allow extending the dataset with :extend option" do
|
|
47
|
+
MODEL_DB.reset
|
|
48
|
+
klass = Class.new(Sequel::Model(:nodes)) do
|
|
49
|
+
columns :id, :a_id
|
|
50
|
+
end
|
|
51
|
+
mod = Module.new do
|
|
52
|
+
def blah
|
|
53
|
+
1
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
mod2 = Module.new do
|
|
57
|
+
def blar
|
|
58
|
+
2
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
klass.associate :many_to_one, :a, :class=>klass, :extend=>mod
|
|
63
|
+
klass.associate :one_to_many, :bs, :class=>klass, :extend=>[mod]
|
|
64
|
+
klass.associate :many_to_many, :cs, :class=>klass, :extend=>[mod, mod2]
|
|
65
|
+
|
|
66
|
+
node = klass.load(:id=>1)
|
|
67
|
+
node.a_dataset.blah.should == 1
|
|
68
|
+
node.bs_dataset.blah.should == 1
|
|
69
|
+
node.cs_dataset.blah.should == 1
|
|
70
|
+
node.cs_dataset.blar.should == 2
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "should clone an existing association with the :clone option" do
|
|
74
|
+
MODEL_DB.reset
|
|
75
|
+
klass = Class.new(Sequel::Model(:nodes))
|
|
76
|
+
|
|
77
|
+
klass.many_to_one(:par_parent, :order=>:a){1}
|
|
78
|
+
klass.one_to_many(:par_parent1s, :class=>'ParParent', :limit=>12){4}
|
|
79
|
+
klass.many_to_many(:par_parent2s, :class=>:ParParent, :uniq=>true){2}
|
|
80
|
+
|
|
81
|
+
klass.many_to_one :par, :clone=>:par_parent, :select=>:b
|
|
82
|
+
klass.one_to_many :par1s, :clone=>:par_parent1s, :order=>:b, :limit=>10, :block=>nil
|
|
83
|
+
klass.many_to_many(:par2s, :clone=>:par_parent2s, :order=>:c){3}
|
|
84
|
+
|
|
85
|
+
klass.association_reflection(:par).associated_class.should == ParParent
|
|
86
|
+
klass.association_reflection(:par1s).associated_class.should == ParParent
|
|
87
|
+
klass.association_reflection(:par2s).associated_class.should == ParParent
|
|
88
|
+
|
|
89
|
+
klass.association_reflection(:par)[:order].should == :a
|
|
90
|
+
klass.association_reflection(:par).select.should == :b
|
|
91
|
+
klass.association_reflection(:par)[:block].call.should == 1
|
|
92
|
+
klass.association_reflection(:par1s)[:limit].should == 10
|
|
93
|
+
klass.association_reflection(:par1s)[:order].should == :b
|
|
94
|
+
klass.association_reflection(:par1s)[:block].should == nil
|
|
95
|
+
klass.association_reflection(:par2s)[:after_load].length.should == 1
|
|
96
|
+
klass.association_reflection(:par2s)[:order].should == :c
|
|
97
|
+
klass.association_reflection(:par2s)[:block].call.should == 3
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
describe Sequel::Model, "many_to_one" do
|
|
103
|
+
before do
|
|
104
|
+
MODEL_DB.reset
|
|
105
|
+
|
|
106
|
+
@c2 = Class.new(Sequel::Model(:nodes)) do
|
|
107
|
+
unrestrict_primary_key
|
|
108
|
+
columns :id, :parent_id, :par_parent_id, :blah
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
@dataset = @c2.dataset
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "should use implicit key if omitted" do
|
|
115
|
+
@c2.many_to_one :parent, :class => @c2
|
|
116
|
+
|
|
117
|
+
d = @c2.new(:id => 1, :parent_id => 234)
|
|
118
|
+
p = d.parent
|
|
119
|
+
p.class.should == @c2
|
|
120
|
+
p.values.should == {:x => 1, :id => 1}
|
|
121
|
+
|
|
122
|
+
MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "should use implicit class if omitted" do
|
|
126
|
+
class ::ParParent < Sequel::Model
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
@c2.many_to_one :par_parent
|
|
130
|
+
|
|
131
|
+
d = @c2.new(:id => 1, :par_parent_id => 234)
|
|
132
|
+
p = d.par_parent
|
|
133
|
+
p.class.should == ParParent
|
|
134
|
+
|
|
135
|
+
MODEL_DB.sqls.should == ["SELECT * FROM par_parents WHERE (par_parents.id = 234) LIMIT 1"]
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it "should use class inside module if given as a string" do
|
|
139
|
+
module ::Par
|
|
140
|
+
class Parent < Sequel::Model
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
@c2.many_to_one :par_parent, :class=>"Par::Parent"
|
|
145
|
+
|
|
146
|
+
d = @c2.new(:id => 1, :par_parent_id => 234)
|
|
147
|
+
p = d.par_parent
|
|
148
|
+
p.class.should == Par::Parent
|
|
149
|
+
|
|
150
|
+
MODEL_DB.sqls.should == ["SELECT * FROM parents WHERE (parents.id = 234) LIMIT 1"]
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it "should use explicit key if given" do
|
|
154
|
+
@c2.many_to_one :parent, :class => @c2, :key => :blah
|
|
155
|
+
|
|
156
|
+
d = @c2.new(:id => 1, :blah => 567)
|
|
157
|
+
p = d.parent
|
|
158
|
+
p.class.should == @c2
|
|
159
|
+
p.values.should == {:x => 1, :id => 1}
|
|
160
|
+
|
|
161
|
+
MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 567) LIMIT 1"]
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
it "should use :primary_key option if given" do
|
|
165
|
+
@c2.many_to_one :parent, :class => @c2, :key => :blah, :primary_key => :pk
|
|
166
|
+
@c2.new(:id => 1, :blah => 567).parent
|
|
167
|
+
MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.pk = 567) LIMIT 1"]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it "should use :select option if given" do
|
|
171
|
+
@c2.many_to_one :parent, :class => @c2, :key => :blah, :select=>[:id, :name]
|
|
172
|
+
@c2.new(:id => 1, :blah => 567).parent
|
|
173
|
+
MODEL_DB.sqls.should == ["SELECT id, name FROM nodes WHERE (nodes.id = 567) LIMIT 1"]
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it "should use :conditions option if given" do
|
|
177
|
+
@c2.many_to_one :parent, :class => @c2, :key => :blah, :conditions=>{:a=>32}
|
|
178
|
+
@c2.new(:id => 1, :blah => 567).parent
|
|
179
|
+
MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.id = 567) AND (a = 32)) LIMIT 1"]
|
|
180
|
+
|
|
181
|
+
@c2.many_to_one :parent, :class => @c2, :key => :blah, :conditions=>:a
|
|
182
|
+
MODEL_DB.sqls.clear
|
|
183
|
+
@c2.new(:id => 1, :blah => 567).parent
|
|
184
|
+
MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.id = 567) AND a) LIMIT 1"]
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
it "should support :order, :limit (only for offset), and :dataset options, as well as a block" do
|
|
188
|
+
c2 = @c2
|
|
189
|
+
@c2.many_to_one :child_20, :class => @c2, :key=>:id, :dataset=>proc{c2.filter(:parent_id=>pk)}, :limit=>[10,20], :order=>:name do |ds|
|
|
190
|
+
ds.filter(:x.sql_number > 1)
|
|
191
|
+
end
|
|
192
|
+
@c2.load(:id => 100).child_20
|
|
193
|
+
MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((parent_id = 100) AND (x > 1)) ORDER BY name LIMIT 1 OFFSET 20"]
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
it "should return nil if key value is nil" do
|
|
197
|
+
@c2.many_to_one :parent, :class => @c2
|
|
198
|
+
|
|
199
|
+
d = @c2.new(:id => 1)
|
|
200
|
+
d.parent.should == nil
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
it "should cache negative lookup" do
|
|
204
|
+
@c2.many_to_one :parent, :class => @c2
|
|
205
|
+
ds = @c2.dataset
|
|
206
|
+
def ds.fetch_rows(sql, &block)
|
|
207
|
+
MODEL_DB.sqls << sql
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
d = @c2.new(:id => 1, :parent_id=>555)
|
|
211
|
+
MODEL_DB.sqls.should == []
|
|
212
|
+
d.parent.should == nil
|
|
213
|
+
MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.id = 555) LIMIT 1']
|
|
214
|
+
d.parent.should == nil
|
|
215
|
+
MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.id = 555) LIMIT 1']
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
it "should define a setter method" do
|
|
219
|
+
@c2.many_to_one :parent, :class => @c2
|
|
220
|
+
|
|
221
|
+
d = @c2.new(:id => 1)
|
|
222
|
+
d.parent = @c2.new(:id => 4321)
|
|
223
|
+
d.values.should == {:id => 1, :parent_id => 4321}
|
|
224
|
+
|
|
225
|
+
d.parent = nil
|
|
226
|
+
d.values.should == {:id => 1, :parent_id => nil}
|
|
227
|
+
|
|
228
|
+
e = @c2.new(:id => 6677)
|
|
229
|
+
d.parent = e
|
|
230
|
+
d.values.should == {:id => 1, :parent_id => 6677}
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
it "should have the setter method respect the :primary_key option" do
|
|
234
|
+
@c2.many_to_one :parent, :class => @c2, :primary_key=>:blah
|
|
235
|
+
|
|
236
|
+
d = @c2.new(:id => 1)
|
|
237
|
+
d.parent = @c2.new(:id => 4321, :blah=>444)
|
|
238
|
+
d.values.should == {:id => 1, :parent_id => 444}
|
|
239
|
+
|
|
240
|
+
d.parent = nil
|
|
241
|
+
d.values.should == {:id => 1, :parent_id => nil}
|
|
242
|
+
|
|
243
|
+
e = @c2.new(:id => 6677, :blah=>8)
|
|
244
|
+
d.parent = e
|
|
245
|
+
d.values.should == {:id => 1, :parent_id => 8}
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
it "should not persist changes until saved" do
|
|
249
|
+
@c2.many_to_one :parent, :class => @c2
|
|
250
|
+
|
|
251
|
+
d = @c2.load(:id => 1)
|
|
252
|
+
MODEL_DB.reset
|
|
253
|
+
d.parent = @c2.new(:id => 345)
|
|
254
|
+
MODEL_DB.sqls.should == []
|
|
255
|
+
d.save_changes
|
|
256
|
+
MODEL_DB.sqls.should == ['UPDATE nodes SET parent_id = 345 WHERE (id = 1)']
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
it "should set cached instance variable when accessed" do
|
|
260
|
+
@c2.many_to_one :parent, :class => @c2
|
|
261
|
+
|
|
262
|
+
d = @c2.load(:id => 1)
|
|
263
|
+
MODEL_DB.reset
|
|
264
|
+
d.parent_id = 234
|
|
265
|
+
d.associations[:parent].should == nil
|
|
266
|
+
ds = @c2.dataset
|
|
267
|
+
def ds.fetch_rows(sql, &block); MODEL_DB.sqls << sql; yield({:id=>234}) end
|
|
268
|
+
e = d.parent
|
|
269
|
+
MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
|
|
270
|
+
d.associations[:parent].should == e
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
it "should set cached instance variable when assigned" do
|
|
274
|
+
@c2.many_to_one :parent, :class => @c2
|
|
275
|
+
|
|
276
|
+
d = @c2.create(:id => 1)
|
|
277
|
+
MODEL_DB.reset
|
|
278
|
+
d.associations[:parent].should == nil
|
|
279
|
+
d.parent = @c2.new(:id => 234)
|
|
280
|
+
e = d.parent
|
|
281
|
+
d.associations[:parent].should == e
|
|
282
|
+
MODEL_DB.sqls.should == []
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
it "should use cached instance variable if available" do
|
|
286
|
+
@c2.many_to_one :parent, :class => @c2
|
|
287
|
+
|
|
288
|
+
d = @c2.create(:id => 1, :parent_id => 234)
|
|
289
|
+
MODEL_DB.reset
|
|
290
|
+
d.associations[:parent] = 42
|
|
291
|
+
d.parent.should == 42
|
|
292
|
+
MODEL_DB.sqls.should == []
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
it "should not use cached instance variable if asked to reload" do
|
|
296
|
+
@c2.many_to_one :parent, :class => @c2
|
|
297
|
+
|
|
298
|
+
d = @c2.create(:id => 1)
|
|
299
|
+
MODEL_DB.reset
|
|
300
|
+
d.parent_id = 234
|
|
301
|
+
d.associations[:parent] = 42
|
|
302
|
+
d.parent(true).should_not == 42
|
|
303
|
+
MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
it "should have the setter add to the reciprocal one_to_many cached association list if it exists" do
|
|
307
|
+
@c2.many_to_one :parent, :class => @c2
|
|
308
|
+
@c2.one_to_many :children, :class => @c2, :key=>:parent_id
|
|
309
|
+
ds = @c2.dataset
|
|
310
|
+
def ds.fetch_rows(sql, &block)
|
|
311
|
+
MODEL_DB.sqls << sql
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
d = @c2.new(:id => 1)
|
|
315
|
+
e = @c2.new(:id => 2)
|
|
316
|
+
MODEL_DB.sqls.should == []
|
|
317
|
+
d.parent = e
|
|
318
|
+
e.children.should_not(include(d))
|
|
319
|
+
MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.parent_id = 2)']
|
|
320
|
+
|
|
321
|
+
MODEL_DB.reset
|
|
322
|
+
d = @c2.new(:id => 1)
|
|
323
|
+
e = @c2.new(:id => 2)
|
|
324
|
+
e.children.should_not(include(d))
|
|
325
|
+
MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.parent_id = 2)']
|
|
326
|
+
d.parent = e
|
|
327
|
+
e.children.should(include(d))
|
|
328
|
+
MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.parent_id = 2)']
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
it "should have the setter remove the object from the previous associated object's reciprocal one_to_many cached association list if it exists" do
|
|
332
|
+
@c2.many_to_one :parent, :class => @c2
|
|
333
|
+
@c2.one_to_many :children, :class => @c2, :key=>:parent_id
|
|
334
|
+
ds = @c2.dataset
|
|
335
|
+
def ds.fetch_rows(sql, &block)
|
|
336
|
+
MODEL_DB.sqls << sql
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
d = @c2.new(:id => 1)
|
|
340
|
+
e = @c2.new(:id => 2)
|
|
341
|
+
f = @c2.new(:id => 3)
|
|
342
|
+
e.children.should_not(include(d))
|
|
343
|
+
f.children.should_not(include(d))
|
|
344
|
+
MODEL_DB.reset
|
|
345
|
+
d.parent = e
|
|
346
|
+
e.children.should(include(d))
|
|
347
|
+
d.parent = f
|
|
348
|
+
f.children.should(include(d))
|
|
349
|
+
e.children.should_not(include(d))
|
|
350
|
+
d.parent = nil
|
|
351
|
+
f.children.should_not(include(d))
|
|
352
|
+
MODEL_DB.sqls.should == []
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
it "should get all matching records and only return the first if :key option is set to nil" do
|
|
356
|
+
c2 = @c2
|
|
357
|
+
@c2.one_to_many :children, :class => @c2, :key=>:parent_id
|
|
358
|
+
@c2.many_to_one :first_grand_parent, :class => @c2, :key=>nil, :eager_graph=>:children, :dataset=>proc{c2.filter(:children_id=>parent_id)}
|
|
359
|
+
ds = @c2.dataset
|
|
360
|
+
def ds.columns
|
|
361
|
+
[:id, :parent_id, :par_parent_id, :blah]
|
|
362
|
+
end
|
|
363
|
+
def ds.fetch_rows(sql, &block)
|
|
364
|
+
MODEL_DB.sqls << sql
|
|
365
|
+
yield({:id=>1, :parent_id=>0, :par_parent_id=>3, :blah=>4, :children_id=>2, :children_parent_id=>1, :children_par_parent_id=>5, :children_blah=>6})
|
|
366
|
+
end
|
|
367
|
+
p = @c2.new(:parent_id=>2)
|
|
368
|
+
fgp = p.first_grand_parent
|
|
369
|
+
MODEL_DB.sqls.should == ["SELECT nodes.id, nodes.parent_id, nodes.par_parent_id, nodes.blah, children.id AS children_id, children.parent_id AS children_parent_id, children.par_parent_id AS children_par_parent_id, children.blah AS children_blah FROM nodes LEFT OUTER JOIN nodes AS children ON (children.parent_id = nodes.id) WHERE (children_id = 2)"]
|
|
370
|
+
fgp.values.should == {:id=>1, :parent_id=>0, :par_parent_id=>3, :blah=>4}
|
|
371
|
+
fgp.children.first.values.should == {:id=>2, :parent_id=>1, :par_parent_id=>5, :blah=>6}
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
it "should not create the setter method if :read_only option is used" do
|
|
375
|
+
@c2.many_to_one :parent, :class => @c2, :read_only=>true
|
|
376
|
+
@c2.instance_methods.collect{|x| x.to_s}.should(include('parent'))
|
|
377
|
+
@c2.instance_methods.collect{|x| x.to_s}.should_not(include('parent='))
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
it "should not add associations methods directly to class" do
|
|
381
|
+
@c2.many_to_one :parent, :class => @c2
|
|
382
|
+
@c2.instance_methods.collect{|x| x.to_s}.should(include('parent'))
|
|
383
|
+
@c2.instance_methods.collect{|x| x.to_s}.should(include('parent='))
|
|
384
|
+
@c2.instance_methods(false).collect{|x| x.to_s}.should_not(include('parent'))
|
|
385
|
+
@c2.instance_methods(false).collect{|x| x.to_s}.should_not(include('parent='))
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
it "should raise an error if trying to set a model object that doesn't have a valid primary key" do
|
|
389
|
+
@c2.many_to_one :parent, :class => @c2
|
|
390
|
+
p = @c2.new
|
|
391
|
+
c = @c2.load(:id=>123)
|
|
392
|
+
proc{c.parent = p}.should raise_error(Sequel::Error)
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
deprec_specify "should have belongs_to alias" do
|
|
396
|
+
@c2.belongs_to :parent, :class => @c2
|
|
397
|
+
|
|
398
|
+
d = @c2.load(:id => 1)
|
|
399
|
+
MODEL_DB.reset
|
|
400
|
+
d.parent_id = 234
|
|
401
|
+
d.associations[:parent].should == nil
|
|
402
|
+
ds = @c2.dataset
|
|
403
|
+
def ds.fetch_rows(sql, &block); MODEL_DB.sqls << sql; yield({:id=>234}) end
|
|
404
|
+
e = d.parent
|
|
405
|
+
MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
|
|
406
|
+
d.associations[:parent].should == e
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
it "should make the change to the foreign_key value inside a _association= method" do
|
|
410
|
+
@c2.many_to_one :parent, :class => @c2
|
|
411
|
+
@c2.private_instance_methods.collect{|x| x.to_s}.sort.should(include("_parent="))
|
|
412
|
+
p = @c2.new
|
|
413
|
+
c = @c2.load(:id=>123)
|
|
414
|
+
def p._parent=(x)
|
|
415
|
+
@x = x
|
|
416
|
+
end
|
|
417
|
+
p.should_not_receive(:parent_id=)
|
|
418
|
+
p.parent = c
|
|
419
|
+
p.instance_variable_get(:@x).should == c
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
it "should support (before|after)_(add|remove) callbacks" do
|
|
423
|
+
h = []
|
|
424
|
+
@c2.many_to_one :parent, :class => @c2, :before_add=>[proc{|x,y| h << x.pk; h << -y.pk}, :blah], :after_add=>proc{h << 3}, :before_remove=>:blah, :after_remove=>[:blahr]
|
|
425
|
+
@c2.class_eval do
|
|
426
|
+
@@blah = h
|
|
427
|
+
def []=(a, v)
|
|
428
|
+
a == :parent_id ? (@@blah << (v ? 4 : 5)) : super
|
|
429
|
+
end
|
|
430
|
+
def blah(x)
|
|
431
|
+
@@blah << x.pk
|
|
432
|
+
end
|
|
433
|
+
def blahr(x)
|
|
434
|
+
@@blah << 6
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
p = @c2.load(:id=>10)
|
|
438
|
+
c = @c2.load(:id=>123)
|
|
439
|
+
h.should == []
|
|
440
|
+
p.parent = c
|
|
441
|
+
h.should == [10, -123, 123, 4, 3]
|
|
442
|
+
p.parent = nil
|
|
443
|
+
h.should == [10, -123, 123, 4, 3, 123, 5, 6]
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
it "should support after_load association callback" do
|
|
447
|
+
h = []
|
|
448
|
+
@c2.many_to_one :parent, :class => @c2, :after_load=>[proc{|x,y| h << [x.pk, y.pk]}, :al]
|
|
449
|
+
@c2.class_eval do
|
|
450
|
+
@@blah = h
|
|
451
|
+
def al(v)
|
|
452
|
+
@@blah << v.pk
|
|
453
|
+
end
|
|
454
|
+
def @dataset.fetch_rows(sql)
|
|
455
|
+
yield({:id=>20})
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
p = @c2.load(:id=>10, :parent_id=>20)
|
|
459
|
+
parent = p.parent
|
|
460
|
+
h.should == [[10, 20], 20]
|
|
461
|
+
parent.pk.should == 20
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
it "should raise error and not call internal add or remove method if before callback returns false, even if raise_on_save_failure is false" do
|
|
465
|
+
# The reason for this is that assignment in ruby always returns the argument instead of the result
|
|
466
|
+
# of the method, so we can't return nil to signal that the association callback prevented the modification
|
|
467
|
+
p = @c2.new
|
|
468
|
+
c = @c2.load(:id=>123)
|
|
469
|
+
p.raise_on_save_failure = false
|
|
470
|
+
@c2.many_to_one :parent, :class => @c2, :before_add=>:ba, :before_remove=>:br
|
|
471
|
+
p.should_receive(:ba).once.with(c).and_return(false)
|
|
472
|
+
p.should_not_receive(:_parent=)
|
|
473
|
+
proc{p.parent = c}.should raise_error(Sequel::Error)
|
|
474
|
+
p.parent.should == nil
|
|
475
|
+
p.associations[:parent] = c
|
|
476
|
+
p.parent.should == c
|
|
477
|
+
p.should_receive(:br).once.with(c).and_return(false)
|
|
478
|
+
proc{p.parent = nil}.should raise_error(Sequel::Error)
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
it "should raise an error if a callback is not a proc or symbol" do
|
|
482
|
+
@c2.many_to_one :parent, :class => @c2, :before_add=>Object.new
|
|
483
|
+
proc{@c2.new.parent = @c2.load(:id=>1)}.should raise_error(Sequel::Error)
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
it "should call the remove callbacks for the previous object and the add callbacks for the new object" do
|
|
487
|
+
c = @c2.load(:id=>123)
|
|
488
|
+
d = @c2.load(:id=>321)
|
|
489
|
+
p = @c2.new
|
|
490
|
+
p.associations[:parent] = d
|
|
491
|
+
h = []
|
|
492
|
+
@c2.many_to_one :parent, :class => @c2, :before_add=>:ba, :before_remove=>:br, :after_add=>:aa, :after_remove=>:ar
|
|
493
|
+
@c2.class_eval do
|
|
494
|
+
@@blah = h
|
|
495
|
+
def []=(a, v)
|
|
496
|
+
a == :parent_id ? (@@blah << 5) : super
|
|
497
|
+
end
|
|
498
|
+
def ba(x)
|
|
499
|
+
@@blah << x.pk
|
|
500
|
+
end
|
|
501
|
+
def br(x)
|
|
502
|
+
@@blah << x.pk * -1
|
|
503
|
+
end
|
|
504
|
+
def aa(x)
|
|
505
|
+
@@blah << x.pk * 2
|
|
506
|
+
end
|
|
507
|
+
def ar(x)
|
|
508
|
+
@@blah << x.pk * -2
|
|
509
|
+
end
|
|
510
|
+
end
|
|
511
|
+
p.parent = c
|
|
512
|
+
h.should == [-321, 123, 5, 246, -642]
|
|
513
|
+
end
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
describe Sequel::Model, "one_to_many" do
|
|
517
|
+
|
|
518
|
+
before(:each) do
|
|
519
|
+
MODEL_DB.reset
|
|
520
|
+
|
|
521
|
+
@c1 = Class.new(Sequel::Model(:attributes)) do
|
|
522
|
+
unrestrict_primary_key
|
|
523
|
+
columns :id, :node_id
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
@c2 = Class.new(Sequel::Model(:nodes)) do
|
|
527
|
+
unrestrict_primary_key
|
|
528
|
+
attr_accessor :xxx
|
|
529
|
+
|
|
530
|
+
def self.name; 'Node'; end
|
|
531
|
+
def self.to_s; 'Node'; end
|
|
532
|
+
columns :id
|
|
533
|
+
end
|
|
534
|
+
@dataset = @c2.dataset
|
|
535
|
+
|
|
536
|
+
@c2.dataset.extend(Module.new {
|
|
537
|
+
def fetch_rows(sql)
|
|
538
|
+
@db << sql
|
|
539
|
+
yield Hash.new
|
|
540
|
+
end
|
|
541
|
+
})
|
|
542
|
+
|
|
543
|
+
@c1.dataset.extend(Module.new {
|
|
544
|
+
def fetch_rows(sql)
|
|
545
|
+
@db << sql
|
|
546
|
+
yield Hash.new
|
|
547
|
+
end
|
|
548
|
+
})
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
it "should use implicit key if omitted" do
|
|
552
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
553
|
+
|
|
554
|
+
n = @c2.new(:id => 1234)
|
|
555
|
+
a = n.attributes_dataset
|
|
556
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
|
557
|
+
a.sql.should == 'SELECT * FROM attributes WHERE (attributes.node_id = 1234)'
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
it "should use implicit class if omitted" do
|
|
561
|
+
class ::HistoricalValue < Sequel::Model
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
@c2.one_to_many :historical_values
|
|
565
|
+
|
|
566
|
+
n = @c2.new(:id => 1234)
|
|
567
|
+
v = n.historical_values_dataset
|
|
568
|
+
v.should be_a_kind_of(Sequel::Dataset)
|
|
569
|
+
v.sql.should == 'SELECT * FROM historical_values WHERE (historical_values.node_id = 1234)'
|
|
570
|
+
v.model.should == HistoricalValue
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
it "should use class inside a module if given as a string" do
|
|
574
|
+
module ::Historical
|
|
575
|
+
class Value < Sequel::Model
|
|
576
|
+
end
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
@c2.one_to_many :historical_values, :class=>'Historical::Value'
|
|
580
|
+
|
|
581
|
+
n = @c2.new(:id => 1234)
|
|
582
|
+
v = n.historical_values_dataset
|
|
583
|
+
v.should be_a_kind_of(Sequel::Dataset)
|
|
584
|
+
v.sql.should == 'SELECT * FROM values WHERE (values.node_id = 1234)'
|
|
585
|
+
v.model.should == Historical::Value
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
it "should use explicit key if given" do
|
|
589
|
+
@c2.one_to_many :attributes, :class => @c1, :key => :nodeid
|
|
590
|
+
|
|
591
|
+
n = @c2.new(:id => 1234)
|
|
592
|
+
a = n.attributes_dataset
|
|
593
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
|
594
|
+
a.sql.should == 'SELECT * FROM attributes WHERE (attributes.nodeid = 1234)'
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
it "should define an add_ method" do
|
|
598
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
599
|
+
|
|
600
|
+
n = @c2.new(:id => 1234)
|
|
601
|
+
a = @c1.new(:id => 2345)
|
|
602
|
+
a.save
|
|
603
|
+
MODEL_DB.reset
|
|
604
|
+
a.should == n.add_attribute(a)
|
|
605
|
+
MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 1234 WHERE (id = 2345)']
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
it "should define a remove_ method" do
|
|
609
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
610
|
+
|
|
611
|
+
n = @c2.new(:id => 1234)
|
|
612
|
+
a = @c1.new(:id => 2345)
|
|
613
|
+
a.save
|
|
614
|
+
MODEL_DB.reset
|
|
615
|
+
a.should == n.remove_attribute(a)
|
|
616
|
+
MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE (id = 2345)']
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
it "should have add_ method respect the :primary_key option" do
|
|
620
|
+
@c2.one_to_many :attributes, :class => @c1, :primary_key=>:xxx
|
|
621
|
+
|
|
622
|
+
n = @c2.new(:id => 1234, :xxx=>5)
|
|
623
|
+
a = @c1.new(:id => 2345)
|
|
624
|
+
a.save
|
|
625
|
+
MODEL_DB.reset
|
|
626
|
+
a.should == n.add_attribute(a)
|
|
627
|
+
MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 5 WHERE (id = 2345)']
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
it "should raise an error in add_ and remove_ if the passed object returns false to save (is not valid)" do
|
|
632
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
633
|
+
n = @c2.new(:id => 1234)
|
|
634
|
+
a = @c1.new(:id => 2345)
|
|
635
|
+
def a.valid?; false; end
|
|
636
|
+
proc{n.add_attribute(a)}.should raise_error(Sequel::Error)
|
|
637
|
+
proc{n.remove_attribute(a)}.should raise_error(Sequel::Error)
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
it "should raise an error if the model object doesn't have a valid primary key" do
|
|
641
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
642
|
+
a = @c2.new
|
|
643
|
+
n = @c1.load(:id=>123)
|
|
644
|
+
proc{a.attributes_dataset}.should raise_error(Sequel::Error)
|
|
645
|
+
proc{a.attributes}.should raise_error(Sequel::Error)
|
|
646
|
+
proc{a.add_attribute(n)}.should raise_error(Sequel::Error)
|
|
647
|
+
proc{a.remove_attribute(n)}.should raise_error(Sequel::Error)
|
|
648
|
+
proc{a.remove_all_attributes}.should raise_error(Sequel::Error)
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
it "should use :primary_key option if given" do
|
|
652
|
+
@c1.one_to_many :nodes, :class => @c2, :primary_key => :node_id, :key=>:id
|
|
653
|
+
n = @c1.load(:id => 1234, :node_id=>4321)
|
|
654
|
+
n.nodes_dataset.sql.should == "SELECT * FROM nodes WHERE (nodes.id = 4321)"
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
it "should support a select option" do
|
|
658
|
+
@c2.one_to_many :attributes, :class => @c1, :select => [:id, :name]
|
|
659
|
+
|
|
660
|
+
n = @c2.new(:id => 1234)
|
|
661
|
+
n.attributes_dataset.sql.should == "SELECT id, name FROM attributes WHERE (attributes.node_id = 1234)"
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
it "should support a conditions option" do
|
|
665
|
+
@c2.one_to_many :attributes, :class => @c1, :conditions => {:a=>32}
|
|
666
|
+
n = @c2.new(:id => 1234)
|
|
667
|
+
n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (a = 32))"
|
|
668
|
+
@c2.one_to_many :attributes, :class => @c1, :conditions => ~:a
|
|
669
|
+
n = @c2.new(:id => 1234)
|
|
670
|
+
n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND NOT a)"
|
|
671
|
+
end
|
|
672
|
+
|
|
673
|
+
it "should support an order option" do
|
|
674
|
+
@c2.one_to_many :attributes, :class => @c1, :order => :kind
|
|
675
|
+
|
|
676
|
+
n = @c2.new(:id => 1234)
|
|
677
|
+
n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (attributes.node_id = 1234) ORDER BY kind"
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
it "should support an array for the order option" do
|
|
681
|
+
@c2.one_to_many :attributes, :class => @c1, :order => [:kind1, :kind2]
|
|
682
|
+
|
|
683
|
+
n = @c2.new(:id => 1234)
|
|
684
|
+
n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (attributes.node_id = 1234) ORDER BY kind1, kind2"
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
it "should return array with all members of the association" do
|
|
688
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
689
|
+
|
|
690
|
+
n = @c2.new(:id => 1234)
|
|
691
|
+
atts = n.attributes
|
|
692
|
+
atts.should be_a_kind_of(Array)
|
|
693
|
+
atts.size.should == 1
|
|
694
|
+
atts.first.should be_a_kind_of(@c1)
|
|
695
|
+
atts.first.values.should == {}
|
|
696
|
+
|
|
697
|
+
MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
it "should accept a block" do
|
|
701
|
+
@c2.one_to_many :attributes, :class => @c1 do |ds|
|
|
702
|
+
ds.filter(:xxx => @xxx)
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
n = @c2.new(:id => 1234)
|
|
706
|
+
atts = n.attributes
|
|
707
|
+
atts.should be_a_kind_of(Array)
|
|
708
|
+
atts.size.should == 1
|
|
709
|
+
atts.first.should be_a_kind_of(@c1)
|
|
710
|
+
atts.first.values.should == {}
|
|
711
|
+
|
|
712
|
+
MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx IS NULL))']
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
it "should support :order option with block" do
|
|
716
|
+
@c2.one_to_many :attributes, :class => @c1, :order => :kind do |ds|
|
|
717
|
+
ds.filter(:xxx => @xxx)
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
n = @c2.new(:id => 1234)
|
|
721
|
+
atts = n.attributes
|
|
722
|
+
atts.should be_a_kind_of(Array)
|
|
723
|
+
atts.size.should == 1
|
|
724
|
+
atts.first.should be_a_kind_of(@c1)
|
|
725
|
+
atts.first.values.should == {}
|
|
726
|
+
|
|
727
|
+
MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx IS NULL)) ORDER BY kind']
|
|
728
|
+
end
|
|
729
|
+
|
|
730
|
+
it "should have the block argument affect the _dataset method" do
|
|
731
|
+
@c2.one_to_many :attributes, :class => @c1 do |ds|
|
|
732
|
+
ds.filter(:xxx => 456)
|
|
733
|
+
end
|
|
734
|
+
@c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx = 456))'
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
it "should support a :dataset option that is used instead of the default" do
|
|
738
|
+
c1 = @c1
|
|
739
|
+
@c2.one_to_many :all_other_attributes, :class => @c1, :dataset=>proc{c1.filter(:nodeid=>pk).invert}, :order=>:a, :limit=>10 do |ds|
|
|
740
|
+
ds.filter(:xxx => 5)
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
@c2.new(:id => 1234).all_other_attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE ((nodeid != 1234) AND (xxx = 5)) ORDER BY a LIMIT 10'
|
|
744
|
+
n = @c2.new(:id => 1234)
|
|
745
|
+
atts = n.all_other_attributes
|
|
746
|
+
atts.should be_a_kind_of(Array)
|
|
747
|
+
atts.size.should == 1
|
|
748
|
+
atts.first.should be_a_kind_of(@c1)
|
|
749
|
+
atts.first.values.should == {}
|
|
750
|
+
|
|
751
|
+
MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((nodeid != 1234) AND (xxx = 5)) ORDER BY a LIMIT 10']
|
|
752
|
+
end
|
|
753
|
+
|
|
754
|
+
it "should support a :limit option" do
|
|
755
|
+
@c2.one_to_many :attributes, :class => @c1 , :limit=>10
|
|
756
|
+
@c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE (attributes.node_id = 1234) LIMIT 10'
|
|
757
|
+
@c2.one_to_many :attributes, :class => @c1 , :limit=>[10,10]
|
|
758
|
+
@c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE (attributes.node_id = 1234) LIMIT 10 OFFSET 10'
|
|
759
|
+
end
|
|
760
|
+
|
|
761
|
+
it "should have the :eager option affect the _dataset method" do
|
|
762
|
+
@c2.one_to_many :attributes, :class => @c2 , :eager=>:attributes
|
|
763
|
+
@c2.new(:id => 1234).attributes_dataset.opts[:eager].should == {:attributes=>nil}
|
|
764
|
+
end
|
|
765
|
+
|
|
766
|
+
it "should set cached instance variable when accessed" do
|
|
767
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
768
|
+
|
|
769
|
+
n = @c2.new(:id => 1234)
|
|
770
|
+
MODEL_DB.reset
|
|
771
|
+
n.associations.include?(:attributes).should == false
|
|
772
|
+
atts = n.attributes
|
|
773
|
+
atts.should == n.associations[:attributes]
|
|
774
|
+
MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
it "should use cached instance variable if available" do
|
|
778
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
779
|
+
|
|
780
|
+
n = @c2.new(:id => 1234)
|
|
781
|
+
MODEL_DB.reset
|
|
782
|
+
n.associations[:attributes] = 42
|
|
783
|
+
n.attributes.should == 42
|
|
784
|
+
MODEL_DB.sqls.should == []
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
it "should not use cached instance variable if asked to reload" do
|
|
788
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
789
|
+
|
|
790
|
+
n = @c2.new(:id => 1234)
|
|
791
|
+
MODEL_DB.reset
|
|
792
|
+
n.associations[:attributes] = 42
|
|
793
|
+
n.attributes(true).should_not == 42
|
|
794
|
+
MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
|
|
795
|
+
end
|
|
796
|
+
|
|
797
|
+
it "should add item to cached instance variable if it exists when calling add_" do
|
|
798
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
799
|
+
|
|
800
|
+
n = @c2.new(:id => 1234)
|
|
801
|
+
att = @c1.new(:id => 345)
|
|
802
|
+
MODEL_DB.reset
|
|
803
|
+
a = []
|
|
804
|
+
n.associations[:attributes] = a
|
|
805
|
+
n.add_attribute(att)
|
|
806
|
+
a.should == [att]
|
|
807
|
+
end
|
|
808
|
+
|
|
809
|
+
it "should set object to item's reciprocal cached association variable when calling add_" do
|
|
810
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
811
|
+
@c1.many_to_one :node, :class => @c2
|
|
812
|
+
|
|
813
|
+
n = @c2.new(:id => 1234)
|
|
814
|
+
att = @c1.new(:id => 345)
|
|
815
|
+
n.add_attribute(att)
|
|
816
|
+
att.node.should == n
|
|
817
|
+
end
|
|
818
|
+
|
|
819
|
+
it "should remove item from cached instance variable if it exists when calling remove_" do
|
|
820
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
821
|
+
|
|
822
|
+
n = @c2.load(:id => 1234)
|
|
823
|
+
att = @c1.load(:id => 345)
|
|
824
|
+
MODEL_DB.reset
|
|
825
|
+
a = [att]
|
|
826
|
+
n.associations[:attributes] = a
|
|
827
|
+
n.remove_attribute(att)
|
|
828
|
+
a.should == []
|
|
829
|
+
end
|
|
830
|
+
|
|
831
|
+
it "should remove item's reciprocal cached association variable when calling remove_" do
|
|
832
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
833
|
+
@c1.many_to_one :node, :class => @c2
|
|
834
|
+
|
|
835
|
+
n = @c2.new(:id => 1234)
|
|
836
|
+
att = @c1.new(:id => 345)
|
|
837
|
+
att.associations[:node] = n
|
|
838
|
+
att.node.should == n
|
|
839
|
+
n.remove_attribute(att)
|
|
840
|
+
att.node.should == nil
|
|
841
|
+
end
|
|
842
|
+
|
|
843
|
+
it "should not create the add_, remove_, or remove_all_ methods if :read_only option is used" do
|
|
844
|
+
@c2.one_to_many :attributes, :class => @c1, :read_only=>true
|
|
845
|
+
im = @c2.instance_methods.collect{|x| x.to_s}
|
|
846
|
+
im.should(include('attributes'))
|
|
847
|
+
im.should(include('attributes_dataset'))
|
|
848
|
+
im.should_not(include('add_attribute'))
|
|
849
|
+
im.should_not(include('remove_attribute'))
|
|
850
|
+
im.should_not(include('remove_all_attributes'))
|
|
851
|
+
end
|
|
852
|
+
|
|
853
|
+
it "should not add associations methods directly to class" do
|
|
854
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
855
|
+
im = @c2.instance_methods.collect{|x| x.to_s}
|
|
856
|
+
im.should(include('attributes'))
|
|
857
|
+
im.should(include('attributes_dataset'))
|
|
858
|
+
im.should(include('add_attribute'))
|
|
859
|
+
im.should(include('remove_attribute'))
|
|
860
|
+
im.should(include('remove_all_attributes'))
|
|
861
|
+
im2 = @c2.instance_methods(false).collect{|x| x.to_s}
|
|
862
|
+
im2.should_not(include('attributes'))
|
|
863
|
+
im2.should_not(include('attributes_dataset'))
|
|
864
|
+
im2.should_not(include('add_attribute'))
|
|
865
|
+
im2.should_not(include('remove_attribute'))
|
|
866
|
+
im2.should_not(include('remove_all_attributes'))
|
|
867
|
+
end
|
|
868
|
+
|
|
869
|
+
deprec_specify "should have has_many alias" do
|
|
870
|
+
@c2.has_many :attributes, :class => @c1
|
|
871
|
+
|
|
872
|
+
n = @c2.new(:id => 1234)
|
|
873
|
+
atts = n.attributes
|
|
874
|
+
atts.should be_a_kind_of(Array)
|
|
875
|
+
atts.size.should == 1
|
|
876
|
+
atts.first.should be_a_kind_of(@c1)
|
|
877
|
+
atts.first.values.should == {}
|
|
878
|
+
|
|
879
|
+
MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
|
|
880
|
+
end
|
|
881
|
+
|
|
882
|
+
it "should populate the reciprocal many_to_one instance variable when loading the one_to_many association" do
|
|
883
|
+
@c2.one_to_many :attributes, :class => @c1, :key => :node_id
|
|
884
|
+
@c1.many_to_one :node, :class => @c2, :key => :node_id
|
|
885
|
+
|
|
886
|
+
n = @c2.new(:id => 1234)
|
|
887
|
+
atts = n.attributes
|
|
888
|
+
MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
|
|
889
|
+
atts.should be_a_kind_of(Array)
|
|
890
|
+
atts.size.should == 1
|
|
891
|
+
atts.first.should be_a_kind_of(@c1)
|
|
892
|
+
atts.first.values.should == {}
|
|
893
|
+
atts.first.node.should == n
|
|
894
|
+
|
|
895
|
+
MODEL_DB.sqls.length.should == 1
|
|
896
|
+
end
|
|
897
|
+
|
|
898
|
+
it "should use an explicit reciprocal instance variable if given" do
|
|
899
|
+
@c2.one_to_many :attributes, :class => @c1, :key => :node_id, :reciprocal=>:wxyz
|
|
900
|
+
|
|
901
|
+
n = @c2.new(:id => 1234)
|
|
902
|
+
atts = n.attributes
|
|
903
|
+
MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
|
|
904
|
+
atts.should be_a_kind_of(Array)
|
|
905
|
+
atts.size.should == 1
|
|
906
|
+
atts.first.should be_a_kind_of(@c1)
|
|
907
|
+
atts.first.values.should == {}
|
|
908
|
+
atts.first.associations[:wxyz].should == n
|
|
909
|
+
|
|
910
|
+
MODEL_DB.sqls.length.should == 1
|
|
911
|
+
end
|
|
912
|
+
|
|
913
|
+
it "should have an remove_all_ method that removes all associations" do
|
|
914
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
915
|
+
@c2.new(:id => 1234).remove_all_attributes
|
|
916
|
+
MODEL_DB.sqls.first.should == 'UPDATE attributes SET node_id = NULL WHERE (node_id = 1234)'
|
|
917
|
+
end
|
|
918
|
+
|
|
919
|
+
it "should have the remove_all_ method respect the :primary_key option" do
|
|
920
|
+
@c2.one_to_many :attributes, :class => @c1, :primary_key=>:xxx
|
|
921
|
+
@c2.new(:id => 1234, :xxx=>5).remove_all_attributes
|
|
922
|
+
MODEL_DB.sqls.first.should == 'UPDATE attributes SET node_id = NULL WHERE (node_id = 5)'
|
|
923
|
+
end
|
|
924
|
+
|
|
925
|
+
it "remove_all should set the cached instance variable to []" do
|
|
926
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
927
|
+
node = @c2.new(:id => 1234)
|
|
928
|
+
node.remove_all_attributes
|
|
929
|
+
node.associations[:attributes].should == []
|
|
930
|
+
end
|
|
931
|
+
|
|
932
|
+
it "remove_all should return the array of previously associated items if the cached instance variable exists" do
|
|
933
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
934
|
+
attrib = @c1.new(:id=>3)
|
|
935
|
+
node = @c2.new(:id => 1234)
|
|
936
|
+
d = @c1.dataset
|
|
937
|
+
def d.fetch_rows(s); end
|
|
938
|
+
node.attributes.should == []
|
|
939
|
+
def attrib.save; self end
|
|
940
|
+
node.add_attribute(attrib)
|
|
941
|
+
node.associations[:attributes].should == [attrib]
|
|
942
|
+
node.remove_all_attributes.should == [attrib]
|
|
943
|
+
end
|
|
944
|
+
|
|
945
|
+
it "remove_all should return nil if the cached instance variable does not exist" do
|
|
946
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
947
|
+
@c2.new(:id => 1234).remove_all_attributes.should == nil
|
|
948
|
+
end
|
|
949
|
+
|
|
950
|
+
it "remove_all should remove the current item from all reciprocal instance varaibles if it cached instance variable exists" do
|
|
951
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
952
|
+
@c1.many_to_one :node, :class => @c2
|
|
953
|
+
d = @c1.dataset
|
|
954
|
+
def d.fetch_rows(s); end
|
|
955
|
+
d = @c2.dataset
|
|
956
|
+
def d.fetch_rows(s); end
|
|
957
|
+
attrib = @c1.new(:id=>3)
|
|
958
|
+
node = @c2.new(:id => 1234)
|
|
959
|
+
node.attributes.should == []
|
|
960
|
+
attrib.node.should == nil
|
|
961
|
+
def attrib.save; self end
|
|
962
|
+
node.add_attribute(attrib)
|
|
963
|
+
attrib.associations[:node].should == node
|
|
964
|
+
node.remove_all_attributes
|
|
965
|
+
attrib.associations.fetch(:node, 2).should == nil
|
|
966
|
+
end
|
|
967
|
+
|
|
968
|
+
it "should add a getter method if the :one_to_one option is true" do
|
|
969
|
+
@c2.one_to_many :attributes, :class => @c1, :one_to_one=>true
|
|
970
|
+
att = @c2.new(:id => 1234).attribute
|
|
971
|
+
MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
|
|
972
|
+
att.should be_a_kind_of(@c1)
|
|
973
|
+
att.values.should == {}
|
|
974
|
+
end
|
|
975
|
+
|
|
976
|
+
it "should not add a setter method if the :one_to_one option is true and :read_only option is true" do
|
|
977
|
+
@c2.one_to_many :attributes, :class => @c1, :one_to_one=>true, :read_only=>true
|
|
978
|
+
im = @c2.instance_methods.collect{|x| x.to_s}
|
|
979
|
+
im.should(include('attribute'))
|
|
980
|
+
im.should_not(include('attribute='))
|
|
981
|
+
end
|
|
982
|
+
|
|
983
|
+
it "should have the getter method raise an error if more than one record is found" do
|
|
984
|
+
@c2.one_to_many :attributes, :class => @c1, :one_to_one=>true
|
|
985
|
+
d = @c1.dataset
|
|
986
|
+
def d.fetch_rows(s); 2.times{yield Hash.new} end
|
|
987
|
+
proc{@c2.new(:id => 1234).attribute}.should raise_error(Sequel::Error)
|
|
988
|
+
end
|
|
989
|
+
|
|
990
|
+
it "should add a setter method if the :one_to_one option is true" do
|
|
991
|
+
@c2.one_to_many :attributes, :class => @c1, :one_to_one=>true
|
|
992
|
+
attrib = @c1.new(:id=>3)
|
|
993
|
+
d = @c1.dataset
|
|
994
|
+
def d.fetch_rows(s); yield({:id=>3}) end
|
|
995
|
+
@c2.new(:id => 1234).attribute = attrib
|
|
996
|
+
['INSERT INTO attributes (node_id, id) VALUES (1234, 3)',
|
|
997
|
+
'INSERT INTO attributes (id, node_id) VALUES (3, 1234)'].should(include(MODEL_DB.sqls.first))
|
|
998
|
+
MODEL_DB.sqls.last.should == 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))'
|
|
999
|
+
MODEL_DB.sqls.length.should == 2
|
|
1000
|
+
@c2.new(:id => 1234).attribute.should == attrib
|
|
1001
|
+
MODEL_DB.sqls.clear
|
|
1002
|
+
attrib = @c1.load(:id=>3)
|
|
1003
|
+
@c2.new(:id => 1234).attribute = attrib
|
|
1004
|
+
MODEL_DB.sqls.length.should == 2
|
|
1005
|
+
MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id = 1234, id = 3|id = 3, node_id = 1234) WHERE \(id = 3\)/
|
|
1006
|
+
MODEL_DB.sqls.last.should == 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))'
|
|
1007
|
+
end
|
|
1008
|
+
|
|
1009
|
+
it "should use a transaction in the setter method if the :one_to_one option is true" do
|
|
1010
|
+
@c2.one_to_many :attributes, :class => @c1, :one_to_one=>true
|
|
1011
|
+
@c2.use_transactions = true
|
|
1012
|
+
MODEL_DB.sqls.clear
|
|
1013
|
+
attrib = @c1.load(:id=>3)
|
|
1014
|
+
@c2.new(:id => 1234).attribute = attrib
|
|
1015
|
+
MODEL_DB.sqls.length.should == 4
|
|
1016
|
+
MODEL_DB.sqls.first.should == 'BEGIN'
|
|
1017
|
+
MODEL_DB.sqls[1].should =~ /UPDATE attributes SET (node_id = 1234, id = 3|id = 3, node_id = 1234) WHERE \(id = 3\)/
|
|
1018
|
+
MODEL_DB.sqls[2].should == 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))'
|
|
1019
|
+
MODEL_DB.sqls.last.should == 'COMMIT'
|
|
1020
|
+
end
|
|
1021
|
+
|
|
1022
|
+
it "should have the setter method for the :one_to_one option respect the :primary_key option" do
|
|
1023
|
+
@c2.one_to_many :attributes, :class => @c1, :one_to_one=>true, :primary_key=>:xxx
|
|
1024
|
+
attrib = @c1.new(:id=>3)
|
|
1025
|
+
d = @c1.dataset
|
|
1026
|
+
def d.fetch_rows(s); yield({:id=>3}) end
|
|
1027
|
+
@c2.new(:id => 1234, :xxx=>5).attribute = attrib
|
|
1028
|
+
['INSERT INTO attributes (node_id, id) VALUES (5, 3)',
|
|
1029
|
+
'INSERT INTO attributes (id, node_id) VALUES (3, 5)'].should(include(MODEL_DB.sqls.first))
|
|
1030
|
+
MODEL_DB.sqls.last.should == 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 5) AND (id != 3))'
|
|
1031
|
+
MODEL_DB.sqls.length.should == 2
|
|
1032
|
+
@c2.new(:id => 321, :xxx=>5).attribute.should == attrib
|
|
1033
|
+
MODEL_DB.sqls.clear
|
|
1034
|
+
attrib = @c1.load(:id=>3)
|
|
1035
|
+
@c2.new(:id => 621, :xxx=>5).attribute = attrib
|
|
1036
|
+
MODEL_DB.sqls.length.should == 2
|
|
1037
|
+
MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id = 5, id = 3|id = 3, node_id = 5) WHERE \(id = 3\)/
|
|
1038
|
+
MODEL_DB.sqls.last.should == 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 5) AND (id != 3))'
|
|
1039
|
+
end
|
|
1040
|
+
|
|
1041
|
+
it "should raise an error if the one_to_one getter would be the same as the association name" do
|
|
1042
|
+
proc{@c2.one_to_many :song, :class => @c1, :one_to_one=>true}.should raise_error(Sequel::Error)
|
|
1043
|
+
end
|
|
1044
|
+
|
|
1045
|
+
it "should not create remove_ and remove_all methods if :one_to_one option is used" do
|
|
1046
|
+
@c2.one_to_many :attributes, :class => @c1, :one_to_one=>true
|
|
1047
|
+
@c2.new.should_not(respond_to(:remove_attribute))
|
|
1048
|
+
@c2.new.should_not(respond_to(:remove_all_attributes))
|
|
1049
|
+
end
|
|
1050
|
+
|
|
1051
|
+
it "should make non getter and setter methods private if :one_to_one option is used" do
|
|
1052
|
+
@c2.one_to_many :attributes, :class => @c1, :one_to_one=>true do |ds| end
|
|
1053
|
+
meths = @c2.private_instance_methods.collect{|x| x.to_s}
|
|
1054
|
+
meths.should(include("attributes"))
|
|
1055
|
+
meths.should(include("add_attribute"))
|
|
1056
|
+
meths.should(include("attributes_dataset"))
|
|
1057
|
+
end
|
|
1058
|
+
|
|
1059
|
+
it "should call an _add_ method internally to add attributes" do
|
|
1060
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
1061
|
+
@c2.private_instance_methods.collect{|x| x.to_s}.sort.should(include("_add_attribute"))
|
|
1062
|
+
p = @c2.load(:id=>10)
|
|
1063
|
+
c = @c1.load(:id=>123)
|
|
1064
|
+
def p._add_attribute(x)
|
|
1065
|
+
@x = x
|
|
1066
|
+
end
|
|
1067
|
+
c.should_not_receive(:node_id=)
|
|
1068
|
+
p.add_attribute(c)
|
|
1069
|
+
p.instance_variable_get(:@x).should == c
|
|
1070
|
+
end
|
|
1071
|
+
|
|
1072
|
+
it "should call a _remove_ method internally to remove attributes" do
|
|
1073
|
+
@c2.one_to_many :attributes, :class => @c1
|
|
1074
|
+
@c2.private_instance_methods.collect{|x| x.to_s}.sort.should(include("_remove_attribute"))
|
|
1075
|
+
p = @c2.load(:id=>10)
|
|
1076
|
+
c = @c1.load(:id=>123)
|
|
1077
|
+
def p._remove_attribute(x)
|
|
1078
|
+
@x = x
|
|
1079
|
+
end
|
|
1080
|
+
c.should_not_receive(:node_id=)
|
|
1081
|
+
p.remove_attribute(c)
|
|
1082
|
+
p.instance_variable_get(:@x).should == c
|
|
1083
|
+
end
|
|
1084
|
+
|
|
1085
|
+
it "should support (before|after)_(add|remove) callbacks" do
|
|
1086
|
+
h = []
|
|
1087
|
+
@c2.one_to_many :attributes, :class => @c1, :before_add=>[proc{|x,y| h << x.pk; h << -y.pk}, :blah], :after_add=>proc{h << 3}, :before_remove=>:blah, :after_remove=>[:blahr]
|
|
1088
|
+
@c2.class_eval do
|
|
1089
|
+
@@blah = h
|
|
1090
|
+
def _add_attribute(v)
|
|
1091
|
+
@@blah << 4
|
|
1092
|
+
end
|
|
1093
|
+
def _remove_attribute(v)
|
|
1094
|
+
@@blah << 5
|
|
1095
|
+
end
|
|
1096
|
+
def blah(x)
|
|
1097
|
+
@@blah << x.pk
|
|
1098
|
+
end
|
|
1099
|
+
def blahr(x)
|
|
1100
|
+
@@blah << 6
|
|
1101
|
+
end
|
|
1102
|
+
end
|
|
1103
|
+
p = @c2.load(:id=>10)
|
|
1104
|
+
c = @c1.load(:id=>123)
|
|
1105
|
+
h.should == []
|
|
1106
|
+
p.add_attribute(c)
|
|
1107
|
+
h.should == [10, -123, 123, 4, 3]
|
|
1108
|
+
p.remove_attribute(c)
|
|
1109
|
+
h.should == [10, -123, 123, 4, 3, 123, 5, 6]
|
|
1110
|
+
end
|
|
1111
|
+
|
|
1112
|
+
it "should support after_load association callback" do
|
|
1113
|
+
h = []
|
|
1114
|
+
@c2.one_to_many :attributes, :class => @c1, :after_load=>[proc{|x,y| h << [x.pk, y.collect{|z|z.pk}]}, :al]
|
|
1115
|
+
@c2.class_eval do
|
|
1116
|
+
@@blah = h
|
|
1117
|
+
def al(v)
|
|
1118
|
+
v.each{|x| @@blah << x.pk}
|
|
1119
|
+
end
|
|
1120
|
+
end
|
|
1121
|
+
@c1.class_eval do
|
|
1122
|
+
def @dataset.fetch_rows(sql)
|
|
1123
|
+
yield({:id=>20})
|
|
1124
|
+
yield({:id=>30})
|
|
1125
|
+
end
|
|
1126
|
+
end
|
|
1127
|
+
p = @c2.load(:id=>10, :parent_id=>20)
|
|
1128
|
+
attributes = p.attributes
|
|
1129
|
+
h.should == [[10, [20, 30]], 20, 30]
|
|
1130
|
+
attributes.collect{|a| a.pk}.should == [20, 30]
|
|
1131
|
+
end
|
|
1132
|
+
|
|
1133
|
+
it "should raise error and not call internal add or remove method if before callback returns false if raise_on_save_failure is true" do
|
|
1134
|
+
p = @c2.load(:id=>10)
|
|
1135
|
+
c = @c1.load(:id=>123)
|
|
1136
|
+
@c2.one_to_many :attributes, :class => @c1, :before_add=>:ba, :before_remove=>:br
|
|
1137
|
+
p.should_receive(:ba).once.with(c).and_return(false)
|
|
1138
|
+
p.should_not_receive(:_add_attribute)
|
|
1139
|
+
p.should_not_receive(:_remove_attribute)
|
|
1140
|
+
p.associations[:attributes] = []
|
|
1141
|
+
proc{p.add_attribute(c)}.should raise_error(Sequel::Error)
|
|
1142
|
+
p.attributes.should == []
|
|
1143
|
+
p.associations[:attributes] = [c]
|
|
1144
|
+
p.should_receive(:br).once.with(c).and_return(false)
|
|
1145
|
+
proc{p.remove_attribute(c)}.should raise_error(Sequel::Error)
|
|
1146
|
+
p.attributes.should == [c]
|
|
1147
|
+
end
|
|
1148
|
+
|
|
1149
|
+
it "should return nil and not call internal add or remove method if before callback returns false if raise_on_save_failure is false" do
|
|
1150
|
+
p = @c2.load(:id=>10)
|
|
1151
|
+
c = @c1.load(:id=>123)
|
|
1152
|
+
p.raise_on_save_failure = false
|
|
1153
|
+
@c2.one_to_many :attributes, :class => @c1, :before_add=>:ba, :before_remove=>:br
|
|
1154
|
+
p.should_receive(:ba).once.with(c).and_return(false)
|
|
1155
|
+
p.should_not_receive(:_add_attribute)
|
|
1156
|
+
p.should_not_receive(:_remove_attribute)
|
|
1157
|
+
p.associations[:attributes] = []
|
|
1158
|
+
p.add_attribute(c).should == nil
|
|
1159
|
+
p.attributes.should == []
|
|
1160
|
+
p.associations[:attributes] = [c]
|
|
1161
|
+
p.should_receive(:br).once.with(c).and_return(false)
|
|
1162
|
+
p.remove_attribute(c).should == nil
|
|
1163
|
+
p.attributes.should == [c]
|
|
1164
|
+
end
|
|
1165
|
+
end
|
|
1166
|
+
|
|
1167
|
+
describe Sequel::Model, "many_to_many" do
|
|
1168
|
+
|
|
1169
|
+
before(:each) do
|
|
1170
|
+
MODEL_DB.reset
|
|
1171
|
+
|
|
1172
|
+
@c1 = Class.new(Sequel::Model(:attributes)) do
|
|
1173
|
+
unrestrict_primary_key
|
|
1174
|
+
attr_accessor :yyy
|
|
1175
|
+
def self.name; 'Attribute'; end
|
|
1176
|
+
def self.to_s; 'Attribute'; end
|
|
1177
|
+
columns :id
|
|
1178
|
+
end
|
|
1179
|
+
|
|
1180
|
+
@c2 = Class.new(Sequel::Model(:nodes)) do
|
|
1181
|
+
unrestrict_primary_key
|
|
1182
|
+
attr_accessor :xxx
|
|
1183
|
+
|
|
1184
|
+
def self.name; 'Node'; end
|
|
1185
|
+
def self.to_s; 'Node'; end
|
|
1186
|
+
columns :id
|
|
1187
|
+
end
|
|
1188
|
+
@dataset = @c2.dataset
|
|
1189
|
+
|
|
1190
|
+
[@c1, @c2].each do |c|
|
|
1191
|
+
c.dataset.extend(Module.new {
|
|
1192
|
+
def fetch_rows(sql)
|
|
1193
|
+
@db << sql
|
|
1194
|
+
yield Hash.new
|
|
1195
|
+
end
|
|
1196
|
+
})
|
|
1197
|
+
end
|
|
1198
|
+
end
|
|
1199
|
+
|
|
1200
|
+
it "should use implicit key values and join table if omitted" do
|
|
1201
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1202
|
+
|
|
1203
|
+
n = @c2.new(:id => 1234)
|
|
1204
|
+
a = n.attributes_dataset
|
|
1205
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
|
1206
|
+
a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
|
|
1207
|
+
end
|
|
1208
|
+
|
|
1209
|
+
it "should use implicit class if omitted" do
|
|
1210
|
+
class ::Tag < Sequel::Model
|
|
1211
|
+
end
|
|
1212
|
+
|
|
1213
|
+
@c2.many_to_many :tags
|
|
1214
|
+
|
|
1215
|
+
n = @c2.new(:id => 1234)
|
|
1216
|
+
a = n.tags_dataset
|
|
1217
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
|
1218
|
+
a.sql.should == 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON ((nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234))'
|
|
1219
|
+
end
|
|
1220
|
+
|
|
1221
|
+
it "should use class inside module if given as a string" do
|
|
1222
|
+
module ::Historical
|
|
1223
|
+
class Tag < Sequel::Model
|
|
1224
|
+
end
|
|
1225
|
+
end
|
|
1226
|
+
|
|
1227
|
+
@c2.many_to_many :tags, :class=>'::Historical::Tag'
|
|
1228
|
+
|
|
1229
|
+
n = @c2.new(:id => 1234)
|
|
1230
|
+
a = n.tags_dataset
|
|
1231
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
|
1232
|
+
a.sql.should == 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON ((nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234))'
|
|
1233
|
+
end
|
|
1234
|
+
|
|
1235
|
+
it "should use explicit key values and join table if given" do
|
|
1236
|
+
@c2.many_to_many :attributes, :class => @c1, :left_key => :nodeid, :right_key => :attributeid, :join_table => :attribute2node
|
|
1237
|
+
|
|
1238
|
+
n = @c2.new(:id => 1234)
|
|
1239
|
+
a = n.attributes_dataset
|
|
1240
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
|
1241
|
+
a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attribute2node ON ((attribute2node.attributeid = attributes.id) AND (attribute2node.nodeid = 1234))'
|
|
1242
|
+
end
|
|
1243
|
+
|
|
1244
|
+
it "should support a conditions option" do
|
|
1245
|
+
@c2.many_to_many :attributes, :class => @c1, :conditions => {:a=>32}
|
|
1246
|
+
n = @c2.new(:id => 1234)
|
|
1247
|
+
a = n.attributes_dataset
|
|
1248
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
|
1249
|
+
a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (a = 32)'
|
|
1250
|
+
@c2.many_to_many :attributes, :class => @c1, :conditions => ['a = ?', 32]
|
|
1251
|
+
n = @c2.new(:id => 1234)
|
|
1252
|
+
a = n.attributes_dataset
|
|
1253
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
|
1254
|
+
a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (a = 32)'
|
|
1255
|
+
end
|
|
1256
|
+
|
|
1257
|
+
it "should support an order option" do
|
|
1258
|
+
@c2.many_to_many :attributes, :class => @c1, :order => :blah
|
|
1259
|
+
|
|
1260
|
+
n = @c2.new(:id => 1234)
|
|
1261
|
+
a = n.attributes_dataset
|
|
1262
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
|
1263
|
+
a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) ORDER BY blah'
|
|
1264
|
+
end
|
|
1265
|
+
|
|
1266
|
+
it "should support an array for the order option" do
|
|
1267
|
+
@c2.many_to_many :attributes, :class => @c1, :order => [:blah1, :blah2]
|
|
1268
|
+
|
|
1269
|
+
n = @c2.new(:id => 1234)
|
|
1270
|
+
a = n.attributes_dataset
|
|
1271
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
|
1272
|
+
a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) ORDER BY blah1, blah2'
|
|
1273
|
+
end
|
|
1274
|
+
|
|
1275
|
+
it "should support :left_primary_key and :right_primary_key options" do
|
|
1276
|
+
@c2.many_to_many :attributes, :class => @c1, :left_primary_key=>:xxx, :right_primary_key=>:yyy
|
|
1277
|
+
@c2.new(:id => 1234, :xxx=>5).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.yyy) AND (attributes_nodes.node_id = 5))'
|
|
1278
|
+
end
|
|
1279
|
+
|
|
1280
|
+
it "should support a select option" do
|
|
1281
|
+
@c2.many_to_many :attributes, :class => @c1, :select => :blah
|
|
1282
|
+
|
|
1283
|
+
n = @c2.new(:id => 1234)
|
|
1284
|
+
a = n.attributes_dataset
|
|
1285
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
|
1286
|
+
a.sql.should == 'SELECT blah FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
|
|
1287
|
+
end
|
|
1288
|
+
|
|
1289
|
+
it "should support an array for the select option" do
|
|
1290
|
+
@c2.many_to_many :attributes, :class => @c1, :select => [:attributes.*, :attribute_nodes__blah2]
|
|
1291
|
+
|
|
1292
|
+
n = @c2.new(:id => 1234)
|
|
1293
|
+
a = n.attributes_dataset
|
|
1294
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
|
1295
|
+
a.sql.should == 'SELECT attributes.*, attribute_nodes.blah2 FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
|
|
1296
|
+
end
|
|
1297
|
+
|
|
1298
|
+
it "should accept a block" do
|
|
1299
|
+
@c2.many_to_many :attributes, :class => @c1 do |ds|
|
|
1300
|
+
ds.filter(:xxx => @xxx)
|
|
1301
|
+
end
|
|
1302
|
+
|
|
1303
|
+
n = @c2.new(:id => 1234)
|
|
1304
|
+
n.xxx = 555
|
|
1305
|
+
a = n.attributes
|
|
1306
|
+
a.should be_a_kind_of(Array)
|
|
1307
|
+
a.size.should == 1
|
|
1308
|
+
a.first.should be_a_kind_of(@c1)
|
|
1309
|
+
MODEL_DB.sqls.first.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (xxx = 555)'
|
|
1310
|
+
end
|
|
1311
|
+
|
|
1312
|
+
it "should allow the :order option while accepting a block" do
|
|
1313
|
+
@c2.many_to_many :attributes, :class => @c1, :order=>[:blah1, :blah2] do |ds|
|
|
1314
|
+
ds.filter(:xxx => @xxx)
|
|
1315
|
+
end
|
|
1316
|
+
|
|
1317
|
+
n = @c2.new(:id => 1234)
|
|
1318
|
+
n.xxx = 555
|
|
1319
|
+
a = n.attributes
|
|
1320
|
+
a.should be_a_kind_of(Array)
|
|
1321
|
+
a.size.should == 1
|
|
1322
|
+
a.first.should be_a_kind_of(@c1)
|
|
1323
|
+
MODEL_DB.sqls.first.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (xxx = 555) ORDER BY blah1, blah2'
|
|
1324
|
+
end
|
|
1325
|
+
|
|
1326
|
+
it "should have the block argument affect the _dataset method" do
|
|
1327
|
+
@c2.many_to_many :attributes, :class => @c1 do |ds|
|
|
1328
|
+
ds.filter(:xxx => 456)
|
|
1329
|
+
end
|
|
1330
|
+
@c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (xxx = 456)'
|
|
1331
|
+
end
|
|
1332
|
+
|
|
1333
|
+
it "should support a :dataset option that is used instead of the default" do
|
|
1334
|
+
c1 = @c1
|
|
1335
|
+
@c2.many_to_many :attributes, :class => @c1, :dataset=>proc{c1.join_table(:natural, :an).filter(:an__nodeid=>pk)}, :order=> :a, :limit=>10, :select=>nil do |ds|
|
|
1336
|
+
ds.filter(:xxx => @xxx)
|
|
1337
|
+
end
|
|
1338
|
+
|
|
1339
|
+
n = @c2.new(:id => 1234)
|
|
1340
|
+
n.xxx = 555
|
|
1341
|
+
n.attributes_dataset.sql.should == 'SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 10'
|
|
1342
|
+
a = n.attributes
|
|
1343
|
+
a.should be_a_kind_of(Array)
|
|
1344
|
+
a.size.should == 1
|
|
1345
|
+
a.first.should be_a_kind_of(@c1)
|
|
1346
|
+
MODEL_DB.sqls.first.should == 'SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 10'
|
|
1347
|
+
end
|
|
1348
|
+
|
|
1349
|
+
it "should support a :limit option" do
|
|
1350
|
+
@c2.many_to_many :attributes, :class => @c1 , :limit=>10
|
|
1351
|
+
@c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) LIMIT 10'
|
|
1352
|
+
@c2.many_to_many :attributes, :class => @c1 , :limit=>[10, 10]
|
|
1353
|
+
@c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) LIMIT 10 OFFSET 10'
|
|
1354
|
+
end
|
|
1355
|
+
|
|
1356
|
+
it "should have the :eager option affect the _dataset method" do
|
|
1357
|
+
@c2.many_to_many :attributes, :class => @c2 , :eager=>:attributes
|
|
1358
|
+
@c2.new(:id => 1234).attributes_dataset.opts[:eager].should == {:attributes=>nil}
|
|
1359
|
+
end
|
|
1360
|
+
|
|
1361
|
+
it "should define an add_ method" do
|
|
1362
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1363
|
+
|
|
1364
|
+
n = @c2.new(:id => 1234)
|
|
1365
|
+
a = @c1.new(:id => 2345)
|
|
1366
|
+
a.should == n.add_attribute(a)
|
|
1367
|
+
['INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (1234, 2345)',
|
|
1368
|
+
'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (2345, 1234)'
|
|
1369
|
+
].should(include(MODEL_DB.sqls.first))
|
|
1370
|
+
end
|
|
1371
|
+
|
|
1372
|
+
it "should define a remove_ method" do
|
|
1373
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1374
|
+
|
|
1375
|
+
n = @c2.new(:id => 1234)
|
|
1376
|
+
a = @c1.new(:id => 2345)
|
|
1377
|
+
a.should == n.remove_attribute(a)
|
|
1378
|
+
MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 2345))'
|
|
1379
|
+
end
|
|
1380
|
+
|
|
1381
|
+
it "should have the add_ method respect the :left_primary_key and :right_primary_key options" do
|
|
1382
|
+
@c2.many_to_many :attributes, :class => @c1, :left_primary_key=>:xxx, :right_primary_key=>:yyy
|
|
1383
|
+
|
|
1384
|
+
n = @c2.new(:id => 1234, :xxx=>5)
|
|
1385
|
+
a = @c1.new(:id => 2345, :yyy=>8)
|
|
1386
|
+
a.should == n.add_attribute(a)
|
|
1387
|
+
['INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (5, 8)',
|
|
1388
|
+
'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (8, 5)'
|
|
1389
|
+
].should(include(MODEL_DB.sqls.first))
|
|
1390
|
+
end
|
|
1391
|
+
|
|
1392
|
+
it "should have the remove_ method respect the :left_primary_key and :right_primary_key options" do
|
|
1393
|
+
@c2.many_to_many :attributes, :class => @c1, :left_primary_key=>:xxx, :right_primary_key=>:yyy
|
|
1394
|
+
|
|
1395
|
+
n = @c2.new(:id => 1234, :xxx=>5)
|
|
1396
|
+
a = @c1.new(:id => 2345, :yyy=>8)
|
|
1397
|
+
a.should == n.remove_attribute(a)
|
|
1398
|
+
MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE ((node_id = 5) AND (attribute_id = 8))'
|
|
1399
|
+
end
|
|
1400
|
+
|
|
1401
|
+
it "should raise an error if the model object doesn't have a valid primary key" do
|
|
1402
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1403
|
+
a = @c2.new
|
|
1404
|
+
n = @c1.load(:id=>123)
|
|
1405
|
+
proc{a.attributes_dataset}.should raise_error(Sequel::Error)
|
|
1406
|
+
proc{a.attributes}.should raise_error(Sequel::Error)
|
|
1407
|
+
proc{a.add_attribute(n)}.should raise_error(Sequel::Error)
|
|
1408
|
+
proc{a.remove_attribute(n)}.should raise_error(Sequel::Error)
|
|
1409
|
+
proc{a.remove_all_attributes}.should raise_error(Sequel::Error)
|
|
1410
|
+
end
|
|
1411
|
+
|
|
1412
|
+
it "should raise an error if trying to add/remove a model object that doesn't have a valid primary key" do
|
|
1413
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1414
|
+
n = @c1.new
|
|
1415
|
+
a = @c2.load(:id=>123)
|
|
1416
|
+
proc{a.add_attribute(n)}.should raise_error(Sequel::Error)
|
|
1417
|
+
proc{a.remove_attribute(n)}.should raise_error(Sequel::Error)
|
|
1418
|
+
end
|
|
1419
|
+
|
|
1420
|
+
it "should provide an array with all members of the association" do
|
|
1421
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1422
|
+
|
|
1423
|
+
n = @c2.new(:id => 1234)
|
|
1424
|
+
atts = n.attributes
|
|
1425
|
+
atts.should be_a_kind_of(Array)
|
|
1426
|
+
atts.size.should == 1
|
|
1427
|
+
atts.first.should be_a_kind_of(@c1)
|
|
1428
|
+
|
|
1429
|
+
MODEL_DB.sqls.first.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
|
|
1430
|
+
end
|
|
1431
|
+
|
|
1432
|
+
it "should set cached instance variable when accessed" do
|
|
1433
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1434
|
+
|
|
1435
|
+
n = @c2.new(:id => 1234)
|
|
1436
|
+
MODEL_DB.reset
|
|
1437
|
+
n.associations.include?(:attributes).should == false
|
|
1438
|
+
atts = n.attributes
|
|
1439
|
+
atts.should == n.associations[:attributes]
|
|
1440
|
+
MODEL_DB.sqls.length.should == 1
|
|
1441
|
+
end
|
|
1442
|
+
|
|
1443
|
+
it "should use cached instance variable if available" do
|
|
1444
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1445
|
+
|
|
1446
|
+
n = @c2.new(:id => 1234)
|
|
1447
|
+
MODEL_DB.reset
|
|
1448
|
+
n.associations[:attributes] = 42
|
|
1449
|
+
n.attributes.should == 42
|
|
1450
|
+
MODEL_DB.sqls.should == []
|
|
1451
|
+
end
|
|
1452
|
+
|
|
1453
|
+
it "should not use cached instance variable if asked to reload" do
|
|
1454
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1455
|
+
|
|
1456
|
+
n = @c2.new(:id => 1234)
|
|
1457
|
+
MODEL_DB.reset
|
|
1458
|
+
n.associations[:attributes] = 42
|
|
1459
|
+
n.attributes(true).should_not == 42
|
|
1460
|
+
MODEL_DB.sqls.length.should == 1
|
|
1461
|
+
end
|
|
1462
|
+
|
|
1463
|
+
it "should add item to cached instance variable if it exists when calling add_" do
|
|
1464
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1465
|
+
|
|
1466
|
+
n = @c2.new(:id => 1234)
|
|
1467
|
+
att = @c1.new(:id => 345)
|
|
1468
|
+
MODEL_DB.reset
|
|
1469
|
+
a = []
|
|
1470
|
+
n.associations[:attributes] = a
|
|
1471
|
+
n.add_attribute(att)
|
|
1472
|
+
a.should == [att]
|
|
1473
|
+
end
|
|
1474
|
+
|
|
1475
|
+
it "should add item to reciprocal cached instance variable if it exists when calling add_" do
|
|
1476
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1477
|
+
@c1.many_to_many :nodes, :class => @c2
|
|
1478
|
+
|
|
1479
|
+
n = @c2.new(:id => 1234)
|
|
1480
|
+
att = @c1.new(:id => 345)
|
|
1481
|
+
att.associations[:nodes] = []
|
|
1482
|
+
n.add_attribute(att)
|
|
1483
|
+
att.nodes.should == [n]
|
|
1484
|
+
end
|
|
1485
|
+
|
|
1486
|
+
it "should remove item from cached instance variable if it exists when calling remove_" do
|
|
1487
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1488
|
+
|
|
1489
|
+
n = @c2.new(:id => 1234)
|
|
1490
|
+
att = @c1.new(:id => 345)
|
|
1491
|
+
MODEL_DB.reset
|
|
1492
|
+
a = [att]
|
|
1493
|
+
n.associations[:attributes] = a
|
|
1494
|
+
n.remove_attribute(att)
|
|
1495
|
+
a.should == []
|
|
1496
|
+
end
|
|
1497
|
+
|
|
1498
|
+
it "should remove item from reciprocal cached instance variable if it exists when calling remove_" do
|
|
1499
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1500
|
+
@c1.many_to_many :nodes, :class => @c2
|
|
1501
|
+
|
|
1502
|
+
n = @c2.new(:id => 1234)
|
|
1503
|
+
att = @c1.new(:id => 345)
|
|
1504
|
+
att.associations[:nodes] = [n]
|
|
1505
|
+
n.remove_attribute(att)
|
|
1506
|
+
att.nodes.should == []
|
|
1507
|
+
end
|
|
1508
|
+
|
|
1509
|
+
it "should not create the add_, remove_, or remove_all_ methods if :read_only option is used" do
|
|
1510
|
+
@c2.many_to_many :attributes, :class => @c1, :read_only=>true
|
|
1511
|
+
im = @c2.instance_methods.collect{|x| x.to_s}
|
|
1512
|
+
im.should(include('attributes'))
|
|
1513
|
+
im.should(include('attributes_dataset'))
|
|
1514
|
+
im.should_not(include('add_attribute'))
|
|
1515
|
+
im.should_not(include('remove_attribute'))
|
|
1516
|
+
im.should_not(include('remove_all_attributes'))
|
|
1517
|
+
end
|
|
1518
|
+
|
|
1519
|
+
it "should not add associations methods directly to class" do
|
|
1520
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1521
|
+
im = @c2.instance_methods.collect{|x| x.to_s}
|
|
1522
|
+
im.should(include('attributes'))
|
|
1523
|
+
im.should(include('attributes_dataset'))
|
|
1524
|
+
im.should(include('add_attribute'))
|
|
1525
|
+
im.should(include('remove_attribute'))
|
|
1526
|
+
im.should(include('remove_all_attributes'))
|
|
1527
|
+
im2 = @c2.instance_methods(false).collect{|x| x.to_s}
|
|
1528
|
+
im2.should_not(include('attributes'))
|
|
1529
|
+
im2.should_not(include('attributes_dataset'))
|
|
1530
|
+
im2.should_not(include('add_attribute'))
|
|
1531
|
+
im2.should_not(include('remove_attribute'))
|
|
1532
|
+
im2.should_not(include('remove_all_attributes'))
|
|
1533
|
+
end
|
|
1534
|
+
|
|
1535
|
+
deprec_specify "should have has_and_belongs_to_many alias" do
|
|
1536
|
+
@c2.has_and_belongs_to_many :attributes, :class => @c1
|
|
1537
|
+
|
|
1538
|
+
n = @c2.new(:id => 1234)
|
|
1539
|
+
a = n.attributes_dataset
|
|
1540
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
|
1541
|
+
a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
|
|
1542
|
+
end
|
|
1543
|
+
|
|
1544
|
+
it "should have an remove_all_ method that removes all associations" do
|
|
1545
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1546
|
+
@c2.new(:id => 1234).remove_all_attributes
|
|
1547
|
+
MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE (node_id = 1234)'
|
|
1548
|
+
end
|
|
1549
|
+
|
|
1550
|
+
it "should have the remove_all_ method respect the :left_primary_key option" do
|
|
1551
|
+
@c2.many_to_many :attributes, :class => @c1, :left_primary_key=>:xxx
|
|
1552
|
+
@c2.new(:id => 1234, :xxx=>5).remove_all_attributes
|
|
1553
|
+
MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE (node_id = 5)'
|
|
1554
|
+
end
|
|
1555
|
+
|
|
1556
|
+
it "remove_all should set the cached instance variable to []" do
|
|
1557
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1558
|
+
node = @c2.new(:id => 1234)
|
|
1559
|
+
node.remove_all_attributes
|
|
1560
|
+
node.associations[:attributes].should == []
|
|
1561
|
+
end
|
|
1562
|
+
|
|
1563
|
+
it "remove_all should return the array of previously associated items if the cached instance variable exists" do
|
|
1564
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1565
|
+
attrib = @c1.new(:id=>3)
|
|
1566
|
+
node = @c2.new(:id => 1234)
|
|
1567
|
+
d = @c1.dataset
|
|
1568
|
+
def d.fetch_rows(s); end
|
|
1569
|
+
node.attributes.should == []
|
|
1570
|
+
node.add_attribute(attrib)
|
|
1571
|
+
node.associations[:attributes].should == [attrib]
|
|
1572
|
+
node.remove_all_attributes.should == [attrib]
|
|
1573
|
+
end
|
|
1574
|
+
|
|
1575
|
+
it "remove_all should return nil if the cached instance variable does not exist" do
|
|
1576
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1577
|
+
@c2.new(:id => 1234).remove_all_attributes.should == nil
|
|
1578
|
+
end
|
|
1579
|
+
|
|
1580
|
+
it "remove_all should remove the current item from all reciprocal instance varaibles if it cached instance variable exists" do
|
|
1581
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1582
|
+
@c1.many_to_many :nodes, :class => @c2
|
|
1583
|
+
d = @c1.dataset
|
|
1584
|
+
def d.fetch_rows(s); end
|
|
1585
|
+
d = @c2.dataset
|
|
1586
|
+
def d.fetch_rows(s); end
|
|
1587
|
+
attrib = @c1.new(:id=>3)
|
|
1588
|
+
node = @c2.new(:id => 1234)
|
|
1589
|
+
node.attributes.should == []
|
|
1590
|
+
attrib.nodes.should == []
|
|
1591
|
+
node.add_attribute(attrib)
|
|
1592
|
+
attrib.associations[:nodes].should == [node]
|
|
1593
|
+
node.remove_all_attributes
|
|
1594
|
+
attrib.associations[:nodes].should == []
|
|
1595
|
+
end
|
|
1596
|
+
|
|
1597
|
+
it "should call an _add_ method internally to add attributes" do
|
|
1598
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1599
|
+
@c2.private_instance_methods.collect{|x| x.to_s}.sort.should(include("_add_attribute"))
|
|
1600
|
+
p = @c2.load(:id=>10)
|
|
1601
|
+
c = @c1.load(:id=>123)
|
|
1602
|
+
def p._add_attribute(x)
|
|
1603
|
+
@x = x
|
|
1604
|
+
end
|
|
1605
|
+
p.add_attribute(c)
|
|
1606
|
+
p.instance_variable_get(:@x).should == c
|
|
1607
|
+
MODEL_DB.sqls.should == []
|
|
1608
|
+
end
|
|
1609
|
+
|
|
1610
|
+
it "should call a _remove_ method internally to remove attributes" do
|
|
1611
|
+
@c2.many_to_many :attributes, :class => @c1
|
|
1612
|
+
@c2.private_instance_methods.collect{|x| x.to_s}.sort.should(include("_remove_attribute"))
|
|
1613
|
+
p = @c2.load(:id=>10)
|
|
1614
|
+
c = @c1.load(:id=>123)
|
|
1615
|
+
def p._remove_attribute(x)
|
|
1616
|
+
@x = x
|
|
1617
|
+
end
|
|
1618
|
+
p.remove_attribute(c)
|
|
1619
|
+
p.instance_variable_get(:@x).should == c
|
|
1620
|
+
MODEL_DB.sqls.should == []
|
|
1621
|
+
end
|
|
1622
|
+
|
|
1623
|
+
it "should support (before|after)_(add|remove) callbacks" do
|
|
1624
|
+
h = []
|
|
1625
|
+
@c2.many_to_many :attributes, :class => @c1, :before_add=>[proc{|x,y| h << x.pk; h << -y.pk}, :blah], :after_add=>proc{h << 3}, :before_remove=>:blah, :after_remove=>[:blahr]
|
|
1626
|
+
@c2.class_eval do
|
|
1627
|
+
@@blah = h
|
|
1628
|
+
def _add_attribute(v)
|
|
1629
|
+
@@blah << 4
|
|
1630
|
+
end
|
|
1631
|
+
def _remove_attribute(v)
|
|
1632
|
+
@@blah << 5
|
|
1633
|
+
end
|
|
1634
|
+
def blah(x)
|
|
1635
|
+
@@blah << x.pk
|
|
1636
|
+
end
|
|
1637
|
+
def blahr(x)
|
|
1638
|
+
@@blah << 6
|
|
1639
|
+
end
|
|
1640
|
+
end
|
|
1641
|
+
p = @c2.load(:id=>10)
|
|
1642
|
+
c = @c1.load(:id=>123)
|
|
1643
|
+
h.should == []
|
|
1644
|
+
p.add_attribute(c)
|
|
1645
|
+
h.should == [10, -123, 123, 4, 3]
|
|
1646
|
+
p.remove_attribute(c)
|
|
1647
|
+
h.should == [10, -123, 123, 4, 3, 123, 5, 6]
|
|
1648
|
+
end
|
|
1649
|
+
|
|
1650
|
+
it "should support after_load association callback" do
|
|
1651
|
+
h = []
|
|
1652
|
+
@c2.many_to_many :attributes, :class => @c1, :after_load=>[proc{|x,y| h << [x.pk, y.collect{|z|z.pk}]}, :al]
|
|
1653
|
+
@c2.class_eval do
|
|
1654
|
+
@@blah = h
|
|
1655
|
+
def al(v)
|
|
1656
|
+
v.each{|x| @@blah << x.pk}
|
|
1657
|
+
end
|
|
1658
|
+
end
|
|
1659
|
+
@c1.class_eval do
|
|
1660
|
+
def @dataset.fetch_rows(sql)
|
|
1661
|
+
yield({:id=>20})
|
|
1662
|
+
yield({:id=>30})
|
|
1663
|
+
end
|
|
1664
|
+
end
|
|
1665
|
+
p = @c2.load(:id=>10, :parent_id=>20)
|
|
1666
|
+
attributes = p.attributes
|
|
1667
|
+
h.should == [[10, [20, 30]], 20, 30]
|
|
1668
|
+
attributes.collect{|a| a.pk}.should == [20, 30]
|
|
1669
|
+
end
|
|
1670
|
+
|
|
1671
|
+
it "should raise error and not call internal add or remove method if before callback returns false if raise_on_save_failure is true" do
|
|
1672
|
+
p = @c2.load(:id=>10)
|
|
1673
|
+
c = @c1.load(:id=>123)
|
|
1674
|
+
@c2.many_to_many :attributes, :class => @c1, :before_add=>:ba, :before_remove=>:br
|
|
1675
|
+
p.should_receive(:ba).once.with(c).and_return(false)
|
|
1676
|
+
p.should_not_receive(:_add_attribute)
|
|
1677
|
+
p.should_not_receive(:_remove_attribute)
|
|
1678
|
+
p.associations[:attributes] = []
|
|
1679
|
+
p.raise_on_save_failure = true
|
|
1680
|
+
proc{p.add_attribute(c)}.should raise_error(Sequel::Error)
|
|
1681
|
+
p.attributes.should == []
|
|
1682
|
+
p.associations[:attributes] = [c]
|
|
1683
|
+
p.should_receive(:br).once.with(c).and_return(false)
|
|
1684
|
+
proc{p.remove_attribute(c)}.should raise_error(Sequel::Error)
|
|
1685
|
+
p.attributes.should == [c]
|
|
1686
|
+
end
|
|
1687
|
+
|
|
1688
|
+
it "should return nil and not call internal add or remove method if before callback returns false if raise_on_save_failure is false" do
|
|
1689
|
+
p = @c2.load(:id=>10)
|
|
1690
|
+
c = @c1.load(:id=>123)
|
|
1691
|
+
p.raise_on_save_failure = false
|
|
1692
|
+
@c2.many_to_many :attributes, :class => @c1, :before_add=>:ba, :before_remove=>:br
|
|
1693
|
+
p.should_receive(:ba).once.with(c).and_return(false)
|
|
1694
|
+
p.should_not_receive(:_add_attribute)
|
|
1695
|
+
p.should_not_receive(:_remove_attribute)
|
|
1696
|
+
p.associations[:attributes] = []
|
|
1697
|
+
p.add_attribute(c).should == nil
|
|
1698
|
+
p.attributes.should == []
|
|
1699
|
+
p.associations[:attributes] = [c]
|
|
1700
|
+
p.should_receive(:br).once.with(c).and_return(false)
|
|
1701
|
+
p.remove_attribute(c).should == nil
|
|
1702
|
+
p.attributes.should == [c]
|
|
1703
|
+
end
|
|
1704
|
+
|
|
1705
|
+
it "should support a :uniq option that removes duplicates from the association" do
|
|
1706
|
+
h = []
|
|
1707
|
+
@c2.many_to_many :attributes, :class => @c1, :uniq=>true
|
|
1708
|
+
@c1.class_eval do
|
|
1709
|
+
def @dataset.fetch_rows(sql)
|
|
1710
|
+
yield({:id=>20})
|
|
1711
|
+
yield({:id=>30})
|
|
1712
|
+
yield({:id=>20})
|
|
1713
|
+
yield({:id=>30})
|
|
1714
|
+
end
|
|
1715
|
+
end
|
|
1716
|
+
@c2.load(:id=>10, :parent_id=>20).attributes.should == [@c1.load(:id=>20), @c1.load(:id=>30)]
|
|
1717
|
+
end
|
|
1718
|
+
end
|
|
1719
|
+
|
|
1720
|
+
describe Sequel::Model, " association reflection methods" do
|
|
1721
|
+
before do
|
|
1722
|
+
MODEL_DB.reset
|
|
1723
|
+
@c1 = Class.new(Sequel::Model(:nodes)) do
|
|
1724
|
+
def self.name; 'Node'; end
|
|
1725
|
+
def self.to_s; 'Node'; end
|
|
1726
|
+
end
|
|
1727
|
+
end
|
|
1728
|
+
|
|
1729
|
+
it "#all_association_reflections should include all association reflection hashes" do
|
|
1730
|
+
@c1.all_association_reflections.should == []
|
|
1731
|
+
|
|
1732
|
+
@c1.associate :many_to_one, :parent, :class => @c1
|
|
1733
|
+
@c1.all_association_reflections.collect{|v| v[:name]}.should == [:parent]
|
|
1734
|
+
@c1.all_association_reflections.collect{|v| v[:type]}.should == [:many_to_one]
|
|
1735
|
+
@c1.all_association_reflections.collect{|v| v[:class]}.should == [@c1]
|
|
1736
|
+
|
|
1737
|
+
@c1.associate :one_to_many, :children, :class => @c1
|
|
1738
|
+
@c1.all_association_reflections.sort_by{|x|x[:name].to_s}
|
|
1739
|
+
@c1.all_association_reflections.sort_by{|x|x[:name].to_s}.collect{|v| v[:name]}.should == [:children, :parent]
|
|
1740
|
+
@c1.all_association_reflections.sort_by{|x|x[:name].to_s}.collect{|v| v[:type]}.should == [:one_to_many, :many_to_one]
|
|
1741
|
+
@c1.all_association_reflections.sort_by{|x|x[:name].to_s}.collect{|v| v[:class]}.should == [@c1, @c1]
|
|
1742
|
+
end
|
|
1743
|
+
|
|
1744
|
+
it "#association_reflection should return nil for nonexistent association" do
|
|
1745
|
+
@c1.association_reflection(:blah).should == nil
|
|
1746
|
+
end
|
|
1747
|
+
|
|
1748
|
+
it "#association_reflection should return association reflection hash if association exists" do
|
|
1749
|
+
@c1.associate :many_to_one, :parent, :class => @c1
|
|
1750
|
+
@c1.association_reflection(:parent).should be_a_kind_of(Sequel::Model::Associations::AssociationReflection)
|
|
1751
|
+
@c1.association_reflection(:parent)[:name].should == :parent
|
|
1752
|
+
@c1.association_reflection(:parent)[:type].should == :many_to_one
|
|
1753
|
+
@c1.association_reflection(:parent)[:class].should == @c1
|
|
1754
|
+
|
|
1755
|
+
@c1.associate :one_to_many, :children, :class => @c1
|
|
1756
|
+
@c1.association_reflection(:children).should be_a_kind_of(Sequel::Model::Associations::AssociationReflection)
|
|
1757
|
+
@c1.association_reflection(:children)[:name].should == :children
|
|
1758
|
+
@c1.association_reflection(:children)[:type].should == :one_to_many
|
|
1759
|
+
@c1.association_reflection(:children)[:class].should == @c1
|
|
1760
|
+
end
|
|
1761
|
+
|
|
1762
|
+
it "#associations should include all association names" do
|
|
1763
|
+
@c1.associations.should == []
|
|
1764
|
+
@c1.associate :many_to_one, :parent, :class => @c1
|
|
1765
|
+
@c1.associations.should == [:parent]
|
|
1766
|
+
@c1.associate :one_to_many, :children, :class => @c1
|
|
1767
|
+
@c1.associations.sort_by{|x|x.to_s}.should == [:children, :parent]
|
|
1768
|
+
end
|
|
1769
|
+
|
|
1770
|
+
it "association reflections should be copied upon subclasing" do
|
|
1771
|
+
@c1.associate :many_to_one, :parent, :class => @c1
|
|
1772
|
+
c = Class.new(@c1)
|
|
1773
|
+
@c1.associations.should == [:parent]
|
|
1774
|
+
c.associations.should == [:parent]
|
|
1775
|
+
c.associate :many_to_one, :parent2, :class => @c1
|
|
1776
|
+
@c1.associations.should == [:parent]
|
|
1777
|
+
c.associations.sort_by{|x| x.to_s}.should == [:parent, :parent2]
|
|
1778
|
+
c.instance_methods.map{|x| x.to_s}.should include('parent')
|
|
1779
|
+
end
|
|
1780
|
+
end
|