sequel 3.21.0 → 3.29.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.
Files changed (200) hide show
  1. data/CHANGELOG +413 -3
  2. data/README.rdoc +20 -6
  3. data/Rakefile +23 -16
  4. data/bin/sequel +1 -5
  5. data/doc/association_basics.rdoc +210 -43
  6. data/doc/dataset_basics.rdoc +4 -4
  7. data/doc/mass_assignment.rdoc +54 -0
  8. data/doc/migration.rdoc +15 -538
  9. data/doc/model_hooks.rdoc +72 -27
  10. data/doc/opening_databases.rdoc +86 -50
  11. data/doc/prepared_statements.rdoc +40 -13
  12. data/doc/reflection.rdoc +8 -2
  13. data/doc/release_notes/3.22.0.txt +39 -0
  14. data/doc/release_notes/3.23.0.txt +172 -0
  15. data/doc/release_notes/3.24.0.txt +420 -0
  16. data/doc/release_notes/3.25.0.txt +88 -0
  17. data/doc/release_notes/3.26.0.txt +88 -0
  18. data/doc/release_notes/3.27.0.txt +82 -0
  19. data/doc/release_notes/3.28.0.txt +304 -0
  20. data/doc/release_notes/3.29.0.txt +459 -0
  21. data/doc/schema_modification.rdoc +547 -0
  22. data/doc/sharding.rdoc +7 -1
  23. data/doc/testing.rdoc +115 -0
  24. data/doc/transactions.rdoc +137 -0
  25. data/doc/virtual_rows.rdoc +2 -2
  26. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  27. data/lib/sequel/adapters/ado.rb +40 -18
  28. data/lib/sequel/adapters/amalgalite.rb +15 -7
  29. data/lib/sequel/adapters/db2.rb +175 -86
  30. data/lib/sequel/adapters/dbi.rb +15 -15
  31. data/lib/sequel/adapters/do/mysql.rb +0 -5
  32. data/lib/sequel/adapters/do/postgres.rb +0 -5
  33. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  34. data/lib/sequel/adapters/do.rb +17 -36
  35. data/lib/sequel/adapters/firebird.rb +27 -208
  36. data/lib/sequel/adapters/ibmdb.rb +448 -0
  37. data/lib/sequel/adapters/informix.rb +6 -23
  38. data/lib/sequel/adapters/jdbc/as400.rb +5 -31
  39. data/lib/sequel/adapters/jdbc/db2.rb +44 -0
  40. data/lib/sequel/adapters/jdbc/derby.rb +217 -0
  41. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  42. data/lib/sequel/adapters/jdbc/h2.rb +26 -17
  43. data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
  44. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  45. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  46. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  47. data/lib/sequel/adapters/jdbc/mysql.rb +9 -10
  48. data/lib/sequel/adapters/jdbc/oracle.rb +67 -25
  49. data/lib/sequel/adapters/jdbc/postgresql.rb +6 -11
  50. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  51. data/lib/sequel/adapters/jdbc/sqlserver.rb +41 -0
  52. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  53. data/lib/sequel/adapters/jdbc.rb +107 -38
  54. data/lib/sequel/adapters/mock.rb +315 -0
  55. data/lib/sequel/adapters/mysql.rb +78 -182
  56. data/lib/sequel/adapters/mysql2.rb +24 -23
  57. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  58. data/lib/sequel/adapters/odbc/mssql.rb +0 -5
  59. data/lib/sequel/adapters/odbc.rb +28 -9
  60. data/lib/sequel/adapters/openbase.rb +2 -4
  61. data/lib/sequel/adapters/oracle.rb +349 -51
  62. data/lib/sequel/adapters/postgres.rb +169 -21
  63. data/lib/sequel/adapters/shared/access.rb +21 -6
  64. data/lib/sequel/adapters/shared/db2.rb +288 -0
  65. data/lib/sequel/adapters/shared/firebird.rb +214 -0
  66. data/lib/sequel/adapters/shared/informix.rb +45 -0
  67. data/lib/sequel/adapters/shared/mssql.rb +108 -65
  68. data/lib/sequel/adapters/shared/mysql.rb +56 -13
  69. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  70. data/lib/sequel/adapters/shared/oracle.rb +185 -34
  71. data/lib/sequel/adapters/shared/postgres.rb +107 -58
  72. data/lib/sequel/adapters/shared/progress.rb +0 -6
  73. data/lib/sequel/adapters/shared/sqlite.rb +168 -41
  74. data/lib/sequel/adapters/sqlite.rb +27 -18
  75. data/lib/sequel/adapters/swift/mysql.rb +9 -5
  76. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  77. data/lib/sequel/adapters/swift/sqlite.rb +6 -4
  78. data/lib/sequel/adapters/swift.rb +5 -5
  79. data/lib/sequel/adapters/tinytds.rb +152 -17
  80. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +63 -0
  81. data/lib/sequel/ast_transformer.rb +190 -0
  82. data/lib/sequel/connection_pool/threaded.rb +3 -2
  83. data/lib/sequel/connection_pool.rb +1 -1
  84. data/lib/sequel/core.rb +46 -5
  85. data/lib/sequel/database/connecting.rb +5 -6
  86. data/lib/sequel/database/dataset.rb +4 -4
  87. data/lib/sequel/database/dataset_defaults.rb +59 -1
  88. data/lib/sequel/database/logging.rb +1 -1
  89. data/lib/sequel/database/misc.rb +100 -19
  90. data/lib/sequel/database/query.rb +153 -58
  91. data/lib/sequel/database/schema_generator.rb +8 -5
  92. data/lib/sequel/database/schema_methods.rb +59 -29
  93. data/lib/sequel/dataset/actions.rb +194 -57
  94. data/lib/sequel/dataset/features.rb +81 -8
  95. data/lib/sequel/dataset/graph.rb +8 -7
  96. data/lib/sequel/dataset/misc.rb +50 -23
  97. data/lib/sequel/dataset/mutation.rb +5 -6
  98. data/lib/sequel/dataset/prepared_statements.rb +32 -15
  99. data/lib/sequel/dataset/query.rb +223 -42
  100. data/lib/sequel/dataset/sql.rb +58 -61
  101. data/lib/sequel/dataset.rb +8 -0
  102. data/lib/sequel/exceptions.rb +4 -0
  103. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  104. data/lib/sequel/extensions/migration.rb +6 -4
  105. data/lib/sequel/extensions/named_timezones.rb +5 -0
  106. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  107. data/lib/sequel/extensions/to_dot.rb +95 -83
  108. data/lib/sequel/model/associations.rb +997 -331
  109. data/lib/sequel/model/base.rb +386 -129
  110. data/lib/sequel/model/errors.rb +1 -1
  111. data/lib/sequel/model/exceptions.rb +5 -1
  112. data/lib/sequel/model.rb +15 -8
  113. data/lib/sequel/plugins/association_pks.rb +22 -4
  114. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  115. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  116. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  117. data/lib/sequel/plugins/force_encoding.rb +6 -6
  118. data/lib/sequel/plugins/identity_map.rb +114 -7
  119. data/lib/sequel/plugins/many_through_many.rb +65 -7
  120. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  121. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  122. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  123. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  124. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  125. data/lib/sequel/plugins/serialization.rb +6 -1
  126. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  127. data/lib/sequel/plugins/sharding.rb +12 -25
  128. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  129. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  130. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  131. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  132. data/lib/sequel/sql.rb +107 -51
  133. data/lib/sequel/timezones.rb +44 -35
  134. data/lib/sequel/version.rb +1 -1
  135. data/spec/adapters/db2_spec.rb +146 -0
  136. data/spec/adapters/mssql_spec.rb +65 -29
  137. data/spec/adapters/mysql_spec.rb +122 -123
  138. data/spec/adapters/oracle_spec.rb +48 -76
  139. data/spec/adapters/postgres_spec.rb +213 -61
  140. data/spec/adapters/spec_helper.rb +6 -5
  141. data/spec/adapters/sqlite_spec.rb +35 -21
  142. data/spec/core/connection_pool_spec.rb +71 -92
  143. data/spec/core/core_sql_spec.rb +20 -31
  144. data/spec/core/database_spec.rb +729 -508
  145. data/spec/core/dataset_spec.rb +980 -1044
  146. data/spec/core/expression_filters_spec.rb +159 -42
  147. data/spec/core/mock_adapter_spec.rb +378 -0
  148. data/spec/core/object_graph_spec.rb +48 -114
  149. data/spec/core/schema_generator_spec.rb +3 -3
  150. data/spec/core/schema_spec.rb +298 -38
  151. data/spec/core/spec_helper.rb +6 -48
  152. data/spec/extensions/association_pks_spec.rb +38 -0
  153. data/spec/extensions/class_table_inheritance_spec.rb +1 -1
  154. data/spec/extensions/columns_introspection_spec.rb +91 -0
  155. data/spec/extensions/dataset_associations_spec.rb +199 -0
  156. data/spec/extensions/defaults_setter_spec.rb +64 -0
  157. data/spec/extensions/identity_map_spec.rb +162 -0
  158. data/spec/extensions/instance_hooks_spec.rb +71 -0
  159. data/spec/extensions/many_through_many_spec.rb +195 -20
  160. data/spec/extensions/migration_spec.rb +17 -17
  161. data/spec/extensions/named_timezones_spec.rb +22 -2
  162. data/spec/extensions/nested_attributes_spec.rb +4 -0
  163. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  164. data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
  165. data/spec/extensions/prepared_statements_spec.rb +72 -0
  166. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  167. data/spec/extensions/schema_dumper_spec.rb +2 -2
  168. data/spec/extensions/schema_spec.rb +13 -21
  169. data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
  170. data/spec/extensions/serialization_spec.rb +5 -8
  171. data/spec/extensions/single_table_inheritance_spec.rb +11 -0
  172. data/spec/extensions/spec_helper.rb +7 -1
  173. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  174. data/spec/extensions/to_dot_spec.rb +3 -5
  175. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  176. data/spec/extensions/xml_serializer_spec.rb +16 -4
  177. data/spec/integration/associations_test.rb +522 -21
  178. data/spec/integration/database_test.rb +4 -2
  179. data/spec/integration/dataset_test.rb +375 -62
  180. data/spec/integration/eager_loader_test.rb +19 -21
  181. data/spec/integration/model_test.rb +80 -1
  182. data/spec/integration/plugin_test.rb +304 -116
  183. data/spec/integration/prepared_statement_test.rb +200 -120
  184. data/spec/integration/schema_test.rb +161 -30
  185. data/spec/integration/spec_helper.rb +39 -30
  186. data/spec/integration/timezone_test.rb +38 -12
  187. data/spec/integration/transaction_test.rb +172 -5
  188. data/spec/integration/type_test.rb +17 -3
  189. data/spec/model/association_reflection_spec.rb +174 -7
  190. data/spec/model/associations_spec.rb +849 -661
  191. data/spec/model/base_spec.rb +255 -95
  192. data/spec/model/dataset_methods_spec.rb +7 -27
  193. data/spec/model/eager_loading_spec.rb +640 -676
  194. data/spec/model/hooks_spec.rb +309 -67
  195. data/spec/model/model_spec.rb +207 -167
  196. data/spec/model/plugins_spec.rb +24 -13
  197. data/spec/model/record_spec.rb +321 -218
  198. data/spec/model/spec_helper.rb +13 -71
  199. data/spec/model/validations_spec.rb +11 -0
  200. metadata +95 -38
@@ -2,42 +2,47 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe Sequel::Model, "associate" do
4
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
5
+ begin
6
+ klass = Class.new(Sequel::Model(:nodes))
7
+ class ::ParParent < Sequel::Model; end
8
+
9
+ klass.associate :many_to_one, :par_parent0, :class=>ParParent
10
+ klass.associate :one_to_many, :par_parent1s, :class=>'ParParent'
11
+ klass.associate :many_to_many, :par_parent2s, :class=>:ParParent
12
+
13
+ klass.association_reflection(:"par_parent0").associated_class.should == ParParent
14
+ klass.association_reflection(:"par_parent1s").associated_class.should == ParParent
15
+ klass.association_reflection(:"par_parent2s").associated_class.should == ParParent
16
+ ensure
17
+ Object.send(:remove_const, :ParParent)
8
18
  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
19
  end
18
20
 
19
21
  it "should default to associating to other models in the same scope" do
20
- class ::AssociationModuleTest
21
- class Album < Sequel::Model
22
- many_to_one :artist
23
- many_to_many :tags
24
- end
25
- class Artist< Sequel::Model
26
- one_to_many :albums
27
- end
28
- class Tag < Sequel::Model
29
- many_to_many :albums
22
+ begin
23
+ class ::AssociationModuleTest
24
+ class Album < Sequel::Model
25
+ many_to_one :artist
26
+ many_to_many :tags
27
+ end
28
+ class Artist< Sequel::Model
29
+ one_to_many :albums
30
+ end
31
+ class Tag < Sequel::Model
32
+ many_to_many :albums
33
+ end
30
34
  end
35
+
36
+ ::AssociationModuleTest::Album.association_reflection(:artist).associated_class.should == ::AssociationModuleTest::Artist
37
+ ::AssociationModuleTest::Album.association_reflection(:tags).associated_class.should == ::AssociationModuleTest::Tag
38
+ ::AssociationModuleTest::Artist.association_reflection(:albums).associated_class.should == ::AssociationModuleTest::Album
39
+ ::AssociationModuleTest::Tag.association_reflection(:albums).associated_class.should == ::AssociationModuleTest::Album
40
+ ensure
41
+ Object.send(:remove_const, :AssociationModuleTest)
31
42
  end
32
-
33
- ::AssociationModuleTest::Album.association_reflection(:artist).associated_class.should == ::AssociationModuleTest::Artist
34
- ::AssociationModuleTest::Album.association_reflection(:tags).associated_class.should == ::AssociationModuleTest::Tag
35
- ::AssociationModuleTest::Artist.association_reflection(:albums).associated_class.should == ::AssociationModuleTest::Album
36
- ::AssociationModuleTest::Tag.association_reflection(:albums).associated_class.should == ::AssociationModuleTest::Album
37
43
  end
38
44
 
39
45
  it "should add a model_object and association_reflection accessors to the dataset, and return it with the current model object" do
40
- MODEL_DB.reset
41
46
  klass = Class.new(Sequel::Model(:nodes)) do
42
47
  columns :id, :a_id
43
48
  end
@@ -64,7 +69,6 @@ describe Sequel::Model, "associate" do
64
69
  end
65
70
 
66
71
  it "should allow extending the dataset with :extend option" do
67
- MODEL_DB.reset
68
72
  klass = Class.new(Sequel::Model(:nodes)) do
69
73
  columns :id, :a_id
70
74
  end
@@ -91,44 +95,46 @@ describe Sequel::Model, "associate" do
91
95
  end
92
96
 
93
97
  it "should clone an existing association with the :clone option" do
94
- MODEL_DB.reset
95
- klass = Class.new(Sequel::Model(:nodes))
96
-
97
- klass.many_to_one(:par_parent, :order=>:a){1}
98
- klass.one_to_many(:par_parent1s, :class=>'ParParent', :limit=>12){4}
99
- klass.many_to_many(:par_parent2s, :class=>:ParParent, :uniq=>true){2}
98
+ begin
99
+ class ::ParParent < Sequel::Model; end
100
+ klass = Class.new(Sequel::Model(:nodes))
101
+
102
+ klass.many_to_one(:par_parent, :order=>:a){1}
103
+ klass.one_to_many(:par_parent1s, :class=>'ParParent', :limit=>12){4}
104
+ klass.many_to_many(:par_parent2s, :class=>:ParParent, :uniq=>true){2}
100
105
 
101
- klass.many_to_one :par, :clone=>:par_parent, :select=>:b
102
- klass.one_to_many :par1s, :clone=>:par_parent1s, :order=>:b, :limit=>10, :block=>nil
103
- klass.many_to_many(:par2s, :clone=>:par_parent2s, :order=>:c){3}
104
-
105
- klass.association_reflection(:par).associated_class.should == ParParent
106
- klass.association_reflection(:par1s).associated_class.should == ParParent
107
- klass.association_reflection(:par2s).associated_class.should == ParParent
108
-
109
- klass.association_reflection(:par)[:order].should == :a
110
- klass.association_reflection(:par).select.should == :b
111
- klass.association_reflection(:par)[:block].call.should == 1
112
- klass.association_reflection(:par1s)[:limit].should == 10
113
- klass.association_reflection(:par1s)[:order].should == :b
114
- klass.association_reflection(:par1s)[:block].should == nil
115
- klass.association_reflection(:par2s)[:after_load].length.should == 1
116
- klass.association_reflection(:par2s)[:order].should == :c
117
- klass.association_reflection(:par2s)[:block].call.should == 3
106
+ klass.many_to_one :par, :clone=>:par_parent, :select=>:b
107
+ klass.one_to_many :par1s, :clone=>:par_parent1s, :order=>:b, :limit=>10, :block=>nil
108
+ klass.many_to_many(:par2s, :clone=>:par_parent2s, :order=>:c){3}
109
+
110
+ klass.association_reflection(:par).associated_class.should == ParParent
111
+ klass.association_reflection(:par1s).associated_class.should == ParParent
112
+ klass.association_reflection(:par2s).associated_class.should == ParParent
113
+
114
+ klass.association_reflection(:par)[:order].should == :a
115
+ klass.association_reflection(:par).select.should == :b
116
+ klass.association_reflection(:par)[:block].call.should == 1
117
+ klass.association_reflection(:par1s)[:limit].should == 10
118
+ klass.association_reflection(:par1s)[:order].should == :b
119
+ klass.association_reflection(:par1s)[:block].should == nil
120
+ klass.association_reflection(:par2s)[:after_load].length.should == 1
121
+ klass.association_reflection(:par2s)[:order].should == :c
122
+ klass.association_reflection(:par2s)[:block].call.should == 3
123
+ ensure
124
+ Object.send(:remove_const, :ParParent)
125
+ end
118
126
  end
119
127
 
120
128
  end
121
129
 
122
130
  describe Sequel::Model, "many_to_one" do
123
131
  before do
124
- MODEL_DB.reset
125
-
126
132
  @c2 = Class.new(Sequel::Model(:nodes)) do
127
133
  unrestrict_primary_key
128
134
  columns :id, :parent_id, :par_parent_id, :blah
129
135
  end
130
-
131
136
  @dataset = @c2.dataset
137
+ MODEL_DB.reset
132
138
  end
133
139
 
134
140
  it "should use implicit key if omitted" do
@@ -143,31 +149,27 @@ describe Sequel::Model, "many_to_one" do
143
149
  end
144
150
 
145
151
  it "should use implicit class if omitted" do
146
- class ::ParParent < Sequel::Model
152
+ begin
153
+ class ::ParParent < Sequel::Model; end
154
+ @c2.many_to_one :par_parent
155
+ @c2.new(:id => 1, :par_parent_id => 234).par_parent.class.should == ParParent
156
+ MODEL_DB.sqls.should == ["SELECT * FROM par_parents WHERE (par_parents.id = 234) LIMIT 1"]
157
+ ensure
158
+ Object.send(:remove_const, :ParParent)
147
159
  end
148
-
149
- @c2.many_to_one :par_parent
150
-
151
- d = @c2.new(:id => 1, :par_parent_id => 234)
152
- p = d.par_parent
153
- p.class.should == ParParent
154
-
155
- MODEL_DB.sqls.should == ["SELECT * FROM par_parents WHERE (par_parents.id = 234) LIMIT 1"]
156
160
  end
157
161
 
158
162
  it "should use class inside module if given as a string" do
159
- module ::Par
160
- class Parent < Sequel::Model
163
+ begin
164
+ module ::Par
165
+ class Parent < Sequel::Model; end
161
166
  end
167
+ @c2.many_to_one :par_parent, :class=>"Par::Parent"
168
+ @c2.new(:id => 1, :par_parent_id => 234).par_parent.class.should == Par::Parent
169
+ MODEL_DB.sqls.should == ["SELECT * FROM parents WHERE (parents.id = 234) LIMIT 1"]
170
+ ensure
171
+ Object.send(:remove_const, :Par)
162
172
  end
163
-
164
- @c2.many_to_one :par_parent, :class=>"Par::Parent"
165
-
166
- d = @c2.new(:id => 1, :par_parent_id => 234)
167
- p = d.par_parent
168
- p.class.should == Par::Parent
169
-
170
- MODEL_DB.sqls.should == ["SELECT * FROM parents WHERE (parents.id = 234) LIMIT 1"]
171
173
  end
172
174
 
173
175
  it "should use explicit key if given" do
@@ -181,6 +183,12 @@ describe Sequel::Model, "many_to_one" do
181
183
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 567) LIMIT 1"]
182
184
  end
183
185
 
186
+ it "should respect :qualify => false option" do
187
+ @c2.many_to_one :parent, :class => @c2, :key => :blah, :qualify=>false
188
+ @c2.new(:id => 1, :blah => 567).parent
189
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (id = 567) LIMIT 1"]
190
+ end
191
+
184
192
  it "should use :primary_key option if given" do
185
193
  @c2.many_to_one :parent, :class => @c2, :key => :blah, :primary_key => :pk
186
194
  @c2.new(:id => 1, :blah => 567).parent
@@ -218,14 +226,12 @@ describe Sequel::Model, "many_to_one" do
218
226
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.id = 567) AND (a = 32)) LIMIT 1"]
219
227
 
220
228
  @c2.many_to_one :parent, :class => @c2, :key => :blah, :conditions=>:a
221
- MODEL_DB.sqls.clear
222
229
  @c2.new(:id => 1, :blah => 567).parent
223
230
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.id = 567) AND a) LIMIT 1"]
224
231
  end
225
232
 
226
233
  it "should support :order, :limit (only for offset), and :dataset options, as well as a block" do
227
- c2 = @c2
228
- @c2.many_to_one :child_20, :class => @c2, :key=>:id, :dataset=>proc{c2.filter(:parent_id=>pk)}, :limit=>[10,20], :order=>:name do |ds|
234
+ @c2.many_to_one :child_20, :class => @c2, :key=>:id, :dataset=>proc{model.filter(:parent_id=>pk)}, :limit=>[10,20], :order=>:name do |ds|
229
235
  ds.filter(:x.sql_number > 1)
230
236
  end
231
237
  @c2.load(:id => 100).child_20
@@ -234,24 +240,19 @@ describe Sequel::Model, "many_to_one" do
234
240
 
235
241
  it "should return nil if key value is nil" do
236
242
  @c2.many_to_one :parent, :class => @c2
237
-
238
- d = @c2.new(:id => 1)
239
- d.parent.should == nil
243
+ @c2.new(:id => 1).parent.should == nil
244
+ MODEL_DB.sqls.should == []
240
245
  end
241
246
 
242
247
  it "should cache negative lookup" do
243
248
  @c2.many_to_one :parent, :class => @c2
244
- ds = @c2.dataset
245
- def ds.fetch_rows(sql, &block)
246
- MODEL_DB.sqls << sql
247
- end
248
-
249
+ @c2.dataset._fetch = []
249
250
  d = @c2.new(:id => 1, :parent_id=>555)
250
251
  MODEL_DB.sqls.should == []
251
252
  d.parent.should == nil
252
253
  MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.id = 555) LIMIT 1']
253
254
  d.parent.should == nil
254
- MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.id = 555) LIMIT 1']
255
+ MODEL_DB.sqls.should == []
255
256
  end
256
257
 
257
258
  it "should define a setter method" do
@@ -314,11 +315,9 @@ describe Sequel::Model, "many_to_one" do
314
315
  @c2.many_to_one :parent, :class => @c2
315
316
 
316
317
  d = @c2.load(:id => 1)
317
- MODEL_DB.reset
318
318
  d.parent_id = 234
319
319
  d.associations[:parent].should == nil
320
- ds = @c2.dataset
321
- def ds.fetch_rows(sql, &block); MODEL_DB.sqls << sql; yield({:id=>234}) end
320
+ @c2.dataset._fetch = {:id=>234}
322
321
  e = d.parent
323
322
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
324
323
  d.associations[:parent].should == e
@@ -357,13 +356,32 @@ describe Sequel::Model, "many_to_one" do
357
356
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
358
357
  end
359
358
 
359
+ it "should use a callback if given one as the argument" do
360
+ @c2.many_to_one :parent, :class => @c2
361
+
362
+ d = @c2.create(:id => 1)
363
+ MODEL_DB.reset
364
+ d.parent_id = 234
365
+ d.associations[:parent] = 42
366
+ d.parent(proc{|ds| ds.filter{name > 'M'}}).should_not == 42
367
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.id = 234) AND (name > 'M')) LIMIT 1"]
368
+ end
369
+
370
+ it "should use a block given to the association method as a callback on ruby 1.8.7+" do
371
+ @c2.many_to_one :parent, :class => @c2
372
+
373
+ d = @c2.create(:id => 1)
374
+ MODEL_DB.reset
375
+ d.parent_id = 234
376
+ d.associations[:parent] = 42
377
+ d.parent{|ds| ds.filter{name > 'M'}}.should_not == 42
378
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.id = 234) AND (name > 'M')) LIMIT 1"]
379
+ end if RUBY_VERSION >= '1.8.7'
380
+
360
381
  it "should have the setter add to the reciprocal one_to_many cached association list if it exists" do
361
382
  @c2.many_to_one :parent, :class => @c2
362
383
  @c2.one_to_many :children, :class => @c2, :key=>:parent_id
363
- ds = @c2.dataset
364
- def ds.fetch_rows(sql, &block)
365
- MODEL_DB.sqls << sql
366
- end
384
+ @c2.dataset._fetch = []
367
385
 
368
386
  d = @c2.new(:id => 1)
369
387
  e = @c2.new(:id => 2)
@@ -372,14 +390,13 @@ describe Sequel::Model, "many_to_one" do
372
390
  e.children.should_not(include(d))
373
391
  MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.parent_id = 2)']
374
392
 
375
- MODEL_DB.reset
376
393
  d = @c2.new(:id => 1)
377
394
  e = @c2.new(:id => 2)
378
395
  e.children.should_not(include(d))
379
396
  MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.parent_id = 2)']
380
397
  d.parent = e
381
398
  e.children.should(include(d))
382
- MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.parent_id = 2)']
399
+ MODEL_DB.sqls.should == []
383
400
  end
384
401
 
385
402
  it "should have many_to_one setter deal with a one_to_one reciprocal" do
@@ -407,10 +424,7 @@ describe Sequel::Model, "many_to_one" do
407
424
  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
408
425
  @c2.many_to_one :parent, :class => @c2
409
426
  @c2.one_to_many :children, :class => @c2, :key=>:parent_id
410
- ds = @c2.dataset
411
- def ds.fetch_rows(sql, &block)
412
- MODEL_DB.sqls << sql
413
- end
427
+ @c2.dataset._fetch = []
414
428
 
415
429
  d = @c2.new(:id => 1)
416
430
  e = @c2.new(:id => 2)
@@ -429,17 +443,9 @@ describe Sequel::Model, "many_to_one" do
429
443
  end
430
444
 
431
445
  it "should get all matching records and only return the first if :key option is set to nil" do
432
- c2 = @c2
433
446
  @c2.one_to_many :children, :class => @c2, :key=>:parent_id
434
- @c2.many_to_one :first_grand_parent, :class => @c2, :key=>nil, :eager_graph=>:children, :dataset=>proc{c2.filter(:children_id=>parent_id)}
435
- ds = @c2.dataset
436
- def ds.columns
437
- [:id, :parent_id, :par_parent_id, :blah]
438
- end
439
- def ds.fetch_rows(sql, &block)
440
- MODEL_DB.sqls << sql
441
- 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})
442
- end
447
+ @c2.many_to_one :first_grand_parent, :class => @c2, :key=>nil, :eager_graph=>:children, :dataset=>proc{model.filter(:children_id=>parent_id)}
448
+ @c2.dataset.columns(:id, :parent_id, :par_parent_id, :blah)._fetch = [{: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}, {}]
443
449
  p = @c2.new(:parent_id=>2)
444
450
  fgp = p.first_grand_parent
445
451
  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)"]
@@ -500,15 +506,15 @@ describe Sequel::Model, "many_to_one" do
500
506
  h = []
501
507
  @c2.many_to_one :parent, :class => @c2, :before_set=>[proc{|x,y| h << x.pk; h << (y ? -y.pk : :y)}, :blah], :after_set=>proc{h << 3}
502
508
  @c2.class_eval do
503
- class_variable_set(:@@blah, h)
509
+ self::Foo = h
504
510
  def []=(a, v)
505
- a == :parent_id ? (self.class.send(:class_variable_get, :@@blah) << (v ? 4 : 5)) : super
511
+ a == :parent_id ? (model::Foo << (v ? 4 : 5)) : super
506
512
  end
507
513
  def blah(x)
508
- self.class.send(:class_variable_get, :@@blah) << (x ? x.pk : :x)
514
+ model::Foo << (x ? x.pk : :x)
509
515
  end
510
516
  def blahr(x)
511
- self.class.send(:class_variable_get, :@@blah) << 6
517
+ model::Foo << 6
512
518
  end
513
519
  end
514
520
  p = @c2.load(:id=>10)
@@ -524,13 +530,11 @@ describe Sequel::Model, "many_to_one" do
524
530
  h = []
525
531
  @c2.many_to_one :parent, :class => @c2, :after_load=>[proc{|x,y| h << [x.pk, y.pk]}, :al]
526
532
  @c2.class_eval do
527
- class_variable_set(:@@blah, h)
533
+ self::Foo = h
528
534
  def al(v)
529
- self.class.send(:class_variable_get, :@@blah) << v.pk
530
- end
531
- def @dataset.fetch_rows(sql)
532
- yield({:id=>20})
535
+ model::Foo << v.pk
533
536
  end
537
+ dataset._fetch = {:id=>20}
534
538
  end
535
539
  p = @c2.load(:id=>10, :parent_id=>20)
536
540
  parent = p.parent
@@ -538,6 +542,18 @@ describe Sequel::Model, "many_to_one" do
538
542
  parent.pk.should == 20
539
543
  end
540
544
 
545
+ it "should support after_load association callback that changes the cached object" do
546
+ @c2.many_to_one :parent, :class => @c2, :after_load=>:al
547
+ @c2.class_eval do
548
+ def al(v)
549
+ associations[:parent] = :foo
550
+ end
551
+ end
552
+ p = @c2.load(:id=>10, :parent_id=>20)
553
+ p.parent.should == :foo
554
+ p.associations[:parent].should == :foo
555
+ end
556
+
541
557
  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
542
558
  # The reason for this is that assignment in ruby always returns the argument instead of the result
543
559
  # of the method, so we can't return nil to signal that the association callback prevented the modification
@@ -565,35 +581,32 @@ describe Sequel::Model, "many_to_one" do
565
581
  d = @c2.load(:id=>321)
566
582
  p = @c2.new
567
583
  p.associations[:parent] = d
568
- h = []
569
584
  @c2.many_to_one :parent, :class => @c2, :before_set=>:bs, :after_set=>:as
570
585
  @c2.class_eval do
571
- class_variable_set(:@@blah, h)
586
+ self::Foo = []
572
587
  def []=(a, v)
573
- a == :parent_id ? (self.class.send(:class_variable_get, :@@blah) << 5) : super
588
+ a == :parent_id ? (model::Foo << 5) : super
574
589
  end
575
590
  def bs(x)
576
- self.class.send(:class_variable_get, :@@blah) << x.pk
591
+ model::Foo << x.pk
577
592
  end
578
593
  def as(x)
579
- self.class.send(:class_variable_get, :@@blah) << x.pk * 2
594
+ model::Foo << x.pk * 2
580
595
  end
581
596
  end
582
597
  p.parent = c
583
- h.should == [123, 5, 246]
598
+ @c2::Foo.should == [123, 5, 246]
584
599
  end
585
600
  end
586
601
 
587
602
  describe Sequel::Model, "one_to_one" do
588
603
  before do
589
604
  @c1 = Class.new(Sequel::Model(:attributes)) do
590
- def _refresh(ds); end
591
605
  unrestrict_primary_key
592
606
  columns :id, :node_id, :y
593
607
  end
594
608
 
595
609
  @c2 = Class.new(Sequel::Model(:nodes)) do
596
- def _refresh(ds); end
597
610
  unrestrict_primary_key
598
611
  attr_accessor :xxx
599
612
 
@@ -602,24 +615,8 @@ describe Sequel::Model, "one_to_one" do
602
615
  columns :id, :x, :parent_id, :par_parent_id, :blah, :node_id
603
616
  end
604
617
  @dataset = @c2.dataset
605
-
606
- @c2.dataset.extend(Module.new {
607
- def empty?; false; end
608
- def fetch_rows(sql)
609
- @db << sql
610
- yield Hash.new
611
- end
612
- })
613
-
614
- @c1.dataset.extend(Module.new {
615
- def empty?; opts.has_key?(:empty) ? (super; true) : false; end
616
- def fetch_rows(sql)
617
- @db << sql
618
- yield Hash.new
619
- end
620
- })
621
-
622
- @dataset = @c2.dataset
618
+ @dataset._fetch = {}
619
+ @c1.dataset._fetch = {}
623
620
  MODEL_DB.reset
624
621
  end
625
622
 
@@ -641,26 +638,24 @@ describe Sequel::Model, "one_to_one" do
641
638
  it "should add a setter method" do
642
639
  @c2.one_to_one :attribute, :class => @c1
643
640
  attrib = @c1.new(:id=>3)
644
- d = @c1.dataset
645
- @c1.class_eval{remove_method :_refresh}
646
- def d.fetch_rows(s); yield({:id=>3}) end
641
+ @c1.dataset._fetch = {:id=>3}
647
642
  @c2.new(:id => 1234).attribute = attrib
643
+ sqls = MODEL_DB.sqls
648
644
  ['INSERT INTO attributes (node_id, id) VALUES (1234, 3)',
649
- 'INSERT INTO attributes (id, node_id) VALUES (3, 1234)'].should(include(MODEL_DB.sqls.last))
650
- MODEL_DB.sqls.first.should == 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))'
651
- MODEL_DB.sqls.length.should == 2
645
+ 'INSERT INTO attributes (id, node_id) VALUES (3, 1234)'].should(include(sqls.slice! 1))
646
+ sqls.should == ['UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))', "SELECT * FROM attributes WHERE (id = 3) LIMIT 1"]
647
+
652
648
  @c2.new(:id => 1234).attribute.should == attrib
653
- MODEL_DB.sqls.clear
654
649
  attrib = @c1.load(:id=>3)
655
650
  @c2.new(:id => 1234).attribute = attrib
656
- MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))',
651
+ MODEL_DB.sqls.should == ["SELECT * FROM attributes WHERE (attributes.node_id = 1234) LIMIT 1",
652
+ 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))',
657
653
  "UPDATE attributes SET node_id = 1234 WHERE (id = 3)"]
658
654
  end
659
655
 
660
656
  it "should use a transaction in the setter method" do
661
657
  @c2.one_to_one :attribute, :class => @c1
662
658
  @c2.use_transactions = true
663
- MODEL_DB.sqls.clear
664
659
  attrib = @c1.load(:id=>3)
665
660
  @c2.new(:id => 1234).attribute = attrib
666
661
  MODEL_DB.sqls.should == ['BEGIN',
@@ -673,7 +668,6 @@ describe Sequel::Model, "one_to_one" do
673
668
  @c2.one_to_one :attribute, :class => @c1, :conditions=>{:a=>1} do |ds|
674
669
  ds.filter(:b=>2)
675
670
  end
676
- MODEL_DB.sqls.clear
677
671
  attrib = @c1.load(:id=>3)
678
672
  @c2.new(:id => 1234).attribute = attrib
679
673
  MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (a = 1) AND (b = 2) AND (id != 3))',
@@ -683,30 +677,30 @@ describe Sequel::Model, "one_to_one" do
683
677
  it "should have the setter method respect the :primary_key option" do
684
678
  @c2.one_to_one :attribute, :class => @c1, :primary_key=>:xxx
685
679
  attrib = @c1.new(:id=>3)
686
- d = @c1.dataset
687
- @c1.class_eval{remove_method :_refresh}
688
- def d.fetch_rows(s); yield({:id=>3}) end
680
+ @c1.dataset._fetch = {:id=>3}
689
681
  @c2.new(:id => 1234, :xxx=>5).attribute = attrib
682
+ sqls = MODEL_DB.sqls
690
683
  ['INSERT INTO attributes (node_id, id) VALUES (5, 3)',
691
- 'INSERT INTO attributes (id, node_id) VALUES (3, 5)'].should(include(MODEL_DB.sqls.last))
692
- MODEL_DB.sqls.first.should == 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 5) AND (id != 3))'
693
- MODEL_DB.sqls.length.should == 2
684
+ 'INSERT INTO attributes (id, node_id) VALUES (3, 5)'].should(include(sqls.slice! 1))
685
+ sqls.should == ['UPDATE attributes SET node_id = NULL WHERE ((node_id = 5) AND (id != 3))', "SELECT * FROM attributes WHERE (id = 3) LIMIT 1"]
686
+
694
687
  @c2.new(:id => 321, :xxx=>5).attribute.should == attrib
695
- MODEL_DB.sqls.clear
696
688
  attrib = @c1.load(:id=>3)
697
689
  @c2.new(:id => 621, :xxx=>5).attribute = attrib
698
- MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE ((node_id = 5) AND (id != 3))',
690
+ MODEL_DB.sqls.should == ["SELECT * FROM attributes WHERE (attributes.node_id = 5) LIMIT 1",
691
+ 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 5) AND (id != 3))',
699
692
  'UPDATE attributes SET node_id = 5 WHERE (id = 3)']
700
693
  end
701
694
 
702
695
  it "should have the setter method respect composite keys" do
703
696
  @c2.one_to_one :attribute, :class => @c1, :key=>[:node_id, :y], :primary_key=>[:id, :x]
704
697
  attrib = @c1.load(:id=>3, :y=>6)
705
- d = @c1.dataset
706
- def d.fetch_rows(s); yield({:id=>3, :y=>6}) end
698
+ @c1.dataset._fetch = {:id=>3, :y=>6}
707
699
  @c2.load(:id => 1234, :x=>5).attribute = attrib
708
- MODEL_DB.sqls.last.should =~ /UPDATE attributes SET (node_id = 1234|y = 5), (node_id = 1234|y = 5) WHERE \(id = 3\)/
709
- MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(\(node_id = 1234\) AND \(y = 5\) AND \(id != 3\)\)/
700
+ sqls = MODEL_DB.sqls
701
+ sqls.last.should =~ /UPDATE attributes SET (node_id = 1234|y = 5), (node_id = 1234|y = 5) WHERE \(id = 3\)/
702
+ sqls.first.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(\(node_id = 1234\) AND \(y = 5\) AND \(id != 3\)\)/
703
+ sqls.length.should == 2
710
704
  end
711
705
 
712
706
  it "should use implicit key if omitted" do
@@ -721,31 +715,27 @@ describe Sequel::Model, "one_to_one" do
721
715
  end
722
716
 
723
717
  it "should use implicit class if omitted" do
724
- class ::ParParent < Sequel::Model
718
+ begin
719
+ class ::ParParent < Sequel::Model; end
720
+ @c2.one_to_one :par_parent
721
+ @c2.new(:id => 234).par_parent.class.should == ParParent
722
+ MODEL_DB.sqls.should == ["SELECT * FROM par_parents WHERE (par_parents.node_id = 234) LIMIT 1"]
723
+ ensure
724
+ Object.send(:remove_const, :ParParent)
725
725
  end
726
-
727
- @c2.one_to_one :par_parent
728
-
729
- d = @c2.new(:id => 234)
730
- p = d.par_parent
731
- p.class.should == ParParent
732
-
733
- MODEL_DB.sqls.should == ["SELECT * FROM par_parents WHERE (par_parents.node_id = 234) LIMIT 1"]
734
726
  end
735
727
 
736
728
  it "should use class inside module if given as a string" do
737
- module ::Par
738
- class Parent < Sequel::Model
729
+ begin
730
+ module ::Par
731
+ class Parent < Sequel::Model; end
739
732
  end
733
+ @c2.one_to_one :par_parent, :class=>"Par::Parent"
734
+ @c2.new(:id => 234).par_parent.class.should == Par::Parent
735
+ MODEL_DB.sqls.should == ["SELECT * FROM parents WHERE (parents.node_id = 234) LIMIT 1"]
736
+ ensure
737
+ Object.send(:remove_const, :Par)
740
738
  end
741
-
742
- @c2.one_to_one :par_parent, :class=>"Par::Parent"
743
-
744
- d = @c2.new(:id => 234)
745
- p = d.par_parent
746
- p.class.should == Par::Parent
747
-
748
- MODEL_DB.sqls.should == ["SELECT * FROM parents WHERE (parents.node_id = 234) LIMIT 1"]
749
739
  end
750
740
 
751
741
  it "should use explicit key if given" do
@@ -796,14 +786,12 @@ describe Sequel::Model, "one_to_one" do
796
786
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.node_id = 567) AND (a = 32)) LIMIT 1"]
797
787
 
798
788
  @c2.one_to_one :parent, :class => @c2, :conditions=>:a
799
- MODEL_DB.sqls.clear
800
789
  @c2.new(:id => 567).parent
801
790
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.node_id = 567) AND a) LIMIT 1"]
802
791
  end
803
792
 
804
793
  it "should support :order, :limit (only for offset), and :dataset options, as well as a block" do
805
- c2 = @c2
806
- @c2.one_to_one :child_20, :class => @c2, :key=>:id, :dataset=>proc{c2.filter(:parent_id=>pk)}, :limit=>[10,20], :order=>:name do |ds|
794
+ @c2.one_to_one :child_20, :class => @c2, :key=>:id, :dataset=>proc{model.filter(:parent_id=>pk)}, :limit=>[10,20], :order=>:name do |ds|
807
795
  ds.filter(:x.sql_number > 1)
808
796
  end
809
797
  @c2.load(:id => 100).child_20
@@ -813,24 +801,19 @@ describe Sequel::Model, "one_to_one" do
813
801
  it "should return nil if primary_key value is nil" do
814
802
  @c2.one_to_one :parent, :class => @c2, :primary_key=>:node_id
815
803
 
816
- d = @c2.new(:id => 1)
817
- d.parent.should == nil
804
+ @c2.new(:id => 1).parent.should be_nil
818
805
  MODEL_DB.sqls.should == []
819
806
  end
820
807
 
821
808
  it "should cache negative lookup" do
822
809
  @c2.one_to_one :parent, :class => @c2
823
- ds = @c2.dataset
824
- def ds.fetch_rows(sql, &block)
825
- MODEL_DB.sqls << sql
826
- end
827
-
810
+ @c2.dataset._fetch = []
828
811
  d = @c2.new(:id => 555)
829
812
  MODEL_DB.sqls.should == []
830
813
  d.parent.should == nil
831
814
  MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.node_id = 555) LIMIT 1']
832
815
  d.parent.should == nil
833
- MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.node_id = 555) LIMIT 1']
816
+ MODEL_DB.sqls.should == []
834
817
  end
835
818
 
836
819
  it "should define a setter method" do
@@ -838,28 +821,44 @@ describe Sequel::Model, "one_to_one" do
838
821
 
839
822
  d = @c2.new(:id => 1)
840
823
  f = @c2.new(:id => 3, :node_id=> 4321)
824
+ @c2.dataset._fetch = {:id => 3, :node_id=>1}
841
825
  d.parent = f
842
826
  f.values.should == {:id => 3, :node_id=>1}
843
827
  d.parent.should == f
828
+ sqls = MODEL_DB.sqls
829
+ ["INSERT INTO nodes (node_id, id) VALUES (1, 3)",
830
+ "INSERT INTO nodes (id, node_id) VALUES (3, 1)"].should include(sqls.slice! 1)
831
+ sqls.should == ["UPDATE nodes SET node_id = NULL WHERE ((node_id = 1) AND (id != 3))", "SELECT * FROM nodes WHERE (id = 3) LIMIT 1"]
844
832
 
845
833
  d.parent = nil
846
834
  d.parent.should == nil
835
+ MODEL_DB.sqls.should == ["UPDATE nodes SET node_id = NULL WHERE (node_id = 1)"]
847
836
  end
848
837
 
849
838
  it "should have the setter method respect the :primary_key option" do
850
839
  @c2.one_to_one :parent, :class => @c2, :primary_key=>:blah
851
840
  d = @c2.new(:id => 1, :blah => 3)
852
841
  e = @c2.new(:id => 4321, :node_id=>444)
842
+ @c2.dataset._fetch = {:id => 4321, :node_id => 3}
853
843
  d.parent = e
854
844
  e.values.should == {:id => 4321, :node_id => 3}
845
+ sqls = MODEL_DB.sqls
846
+ ["INSERT INTO nodes (node_id, id) VALUES (3, 4321)",
847
+ "INSERT INTO nodes (id, node_id) VALUES (4321, 3)"].should include(sqls.slice! 1)
848
+ sqls.should == ["UPDATE nodes SET node_id = NULL WHERE ((node_id = 3) AND (id != 4321))", "SELECT * FROM nodes WHERE (id = 4321) LIMIT 1"]
855
849
  end
856
850
 
857
851
  it "should have the setter method respect the :key option" do
858
852
  @c2.one_to_one :parent, :class => @c2, :key=>:blah
859
853
  d = @c2.new(:id => 3)
860
854
  e = @c2.new(:id => 4321, :blah=>444)
855
+ @c2.dataset._fetch = {:id => 4321, :blah => 3}
861
856
  d.parent = e
862
857
  e.values.should == {:id => 4321, :blah => 3}
858
+ sqls = MODEL_DB.sqls
859
+ ["INSERT INTO nodes (blah, id) VALUES (3, 4321)",
860
+ "INSERT INTO nodes (id, blah) VALUES (4321, 3)"].should include(sqls.slice! 1)
861
+ sqls.should == ["UPDATE nodes SET blah = NULL WHERE ((blah = 3) AND (id != 4321))", "SELECT * FROM nodes WHERE (id = 4321) LIMIT 1"]
863
862
  end
864
863
 
865
864
  it "should persist changes to associated object when the setter is called" do
@@ -875,12 +874,11 @@ describe Sequel::Model, "one_to_one" do
875
874
 
876
875
  d = @c2.load(:id => 1)
877
876
  d.associations[:parent].should == nil
878
- ds = @c2.dataset
879
- def ds.fetch_rows(sql, &block); MODEL_DB.sqls << sql; yield({:id=>234}) end
877
+ @c2.dataset._fetch = {:id=>234}
880
878
  e = d.parent
881
879
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.node_id = 1) LIMIT 1"]
882
880
  d.parent
883
- MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.node_id = 1) LIMIT 1"]
881
+ MODEL_DB.sqls.should == []
884
882
  d.associations[:parent].should == e
885
883
  end
886
884
 
@@ -922,7 +920,6 @@ describe Sequel::Model, "one_to_one" do
922
920
  e.child.should == d
923
921
  MODEL_DB.sqls.should == ["UPDATE nodes SET parent_id = NULL WHERE ((parent_id = 1) AND (id != 2))",
924
922
  "UPDATE nodes SET parent_id = 1 WHERE (id = 2)"]
925
- MODEL_DB.reset
926
923
  d.parent = nil
927
924
  e.child.should == nil
928
925
  MODEL_DB.sqls.should == ["UPDATE nodes SET parent_id = NULL WHERE (parent_id = 1)"]
@@ -931,17 +928,13 @@ describe Sequel::Model, "one_to_one" do
931
928
  it "should have the setter remove the object from the previous associated object's reciprocal many_to_one cached association list if it exists" do
932
929
  @c2.one_to_one :parent, :class => @c2, :key=>:parent_id
933
930
  @c2.many_to_one :child, :class => @c2, :key=>:parent_id
934
- ds = @c2.dataset
935
- def ds.fetch_rows(sql, &block)
936
- MODEL_DB.sqls << sql
937
- end
931
+ @c2.dataset._fetch = []
938
932
 
939
933
  d = @c2.load(:id => 1)
940
934
  e = @c2.load(:id => 2)
941
935
  f = @c2.load(:id => 3)
942
936
  e.child.should == nil
943
937
  f.child.should == nil
944
- MODEL_DB.reset
945
938
  d.parent = e
946
939
  e.child.should == d
947
940
  d.parent = f
@@ -983,12 +976,12 @@ describe Sequel::Model, "one_to_one" do
983
976
  h = []
984
977
  @c2.one_to_one :parent, :class => @c2, :before_set=>[proc{|x,y| h << x.pk; h << (y ? -y.pk : :y)}, :blah], :after_set=>proc{h << 3}
985
978
  @c2.class_eval do
986
- class_variable_set(:@@blah, h)
979
+ self::Foo = h
987
980
  def blah(x)
988
- self.class.send(:class_variable_get, :@@blah) << (x ? x.pk : :x)
981
+ model::Foo << (x ? x.pk : :x)
989
982
  end
990
983
  def blahr(x)
991
- self.class.send(:class_variable_get, :@@blah) << 6
984
+ model::Foo << 6
992
985
  end
993
986
  end
994
987
  p = @c2.load(:id=>10)
@@ -1004,13 +997,11 @@ describe Sequel::Model, "one_to_one" do
1004
997
  h = []
1005
998
  @c2.one_to_one :parent, :class => @c2, :after_load=>[proc{|x,y| h << [x.pk, y.pk]}, :al]
1006
999
  @c2.class_eval do
1007
- class_variable_set(:@@blah, h)
1000
+ self::Foo = h
1008
1001
  def al(v)
1009
- self.class.send(:class_variable_get, :@@blah) << v.pk
1010
- end
1011
- def @dataset.fetch_rows(sql)
1012
- yield({:id=>20})
1002
+ model::Foo << v.pk
1013
1003
  end
1004
+ @dataset._fetch = {:id=>20}
1014
1005
  end
1015
1006
  p = @c2.load(:id=>10)
1016
1007
  parent = p.parent
@@ -1048,15 +1039,15 @@ describe Sequel::Model, "one_to_one" do
1048
1039
  h = []
1049
1040
  @c2.one_to_one :parent, :class => @c2, :before_set=>:bs, :after_set=>:as
1050
1041
  @c2.class_eval do
1051
- class_variable_set(:@@blah, h)
1042
+ self::Foo = h
1052
1043
  def []=(a, v)
1053
- a == :node_id ? (self.class.send(:class_variable_get, :@@blah) << 5) : super
1044
+ a == :node_id ? (model::Foo << 5) : super
1054
1045
  end
1055
1046
  def bs(x)
1056
- self.class.send(:class_variable_get, :@@blah) << x.pk
1047
+ model::Foo << x.pk
1057
1048
  end
1058
1049
  def as(x)
1059
- self.class.send(:class_variable_get, :@@blah) << x.pk * 2
1050
+ model::Foo << x.pk * 2
1060
1051
  end
1061
1052
  end
1062
1053
  p.parent = c
@@ -1072,16 +1063,9 @@ end
1072
1063
 
1073
1064
  describe Sequel::Model, "one_to_many" do
1074
1065
  before do
1075
- MODEL_DB.reset
1076
-
1077
1066
  @c1 = Class.new(Sequel::Model(:attributes)) do
1078
- def _refresh(ds); end
1079
1067
  unrestrict_primary_key
1080
1068
  columns :id, :node_id, :y, :z
1081
-
1082
- def self.[](id)
1083
- load(id.is_a?(Array) ? {:id => id[0], :z => id[1]} : {:id => id})
1084
- end
1085
1069
  end
1086
1070
 
1087
1071
  @c2 = Class.new(Sequel::Model(:nodes)) do
@@ -1094,68 +1078,58 @@ describe Sequel::Model, "one_to_many" do
1094
1078
  columns :id, :x
1095
1079
  end
1096
1080
  @dataset = @c2.dataset
1097
-
1098
- @c2.dataset.extend(Module.new {
1099
- def empty?; false; end
1100
- def fetch_rows(sql)
1101
- @db << sql
1102
- yield Hash.new
1103
- end
1104
- })
1105
-
1106
- @c1.dataset.extend(Module.new {
1107
- def empty?; opts.has_key?(:empty) ? (super; true) : false; end
1108
- def fetch_rows(sql)
1109
- @db << sql
1110
- yield Hash.new
1111
- end
1112
- })
1081
+ @dataset._fetch = {}
1082
+ @c1.dataset._fetch = proc{|sql| sql =~ /SELECT 1/ ? {:a=>1} : {}}
1083
+ MODEL_DB.reset
1113
1084
  end
1114
1085
 
1115
1086
  it "should use implicit key if omitted" do
1116
1087
  @c2.one_to_many :attributes, :class => @c1
1117
-
1118
- n = @c2.new(:id => 1234)
1119
- a = n.attributes_dataset
1120
- a.should be_a_kind_of(Sequel::Dataset)
1121
- a.sql.should == 'SELECT * FROM attributes WHERE (attributes.node_id = 1234)'
1088
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE (attributes.node_id = 1234)'
1122
1089
  end
1123
1090
 
1124
1091
  it "should use implicit class if omitted" do
1125
- class ::HistoricalValue < Sequel::Model
1092
+ begin
1093
+ class ::HistoricalValue < Sequel::Model; end
1094
+ @c2.one_to_many :historical_values
1095
+
1096
+ v = @c2.new(:id => 1234).historical_values_dataset
1097
+ v.should be_a_kind_of(Sequel::Dataset)
1098
+ v.sql.should == 'SELECT * FROM historical_values WHERE (historical_values.node_id = 1234)'
1099
+ v.model.should == HistoricalValue
1100
+ ensure
1101
+ Object.send(:remove_const, :HistoricalValue)
1126
1102
  end
1127
-
1128
- @c2.one_to_many :historical_values
1129
-
1130
- n = @c2.new(:id => 1234)
1131
- v = n.historical_values_dataset
1132
- v.should be_a_kind_of(Sequel::Dataset)
1133
- v.sql.should == 'SELECT * FROM historical_values WHERE (historical_values.node_id = 1234)'
1134
- v.model.should == HistoricalValue
1135
1103
  end
1136
1104
 
1137
1105
  it "should use class inside a module if given as a string" do
1138
- module ::Historical
1139
- class Value < Sequel::Model
1106
+ begin
1107
+ module ::Historical
1108
+ class Value < Sequel::Model; end
1140
1109
  end
1110
+ @c2.one_to_many :historical_values, :class=>'Historical::Value'
1111
+
1112
+ v = @c2.new(:id => 1234).historical_values_dataset
1113
+ v.should be_a_kind_of(Sequel::Dataset)
1114
+ v.sql.should == 'SELECT * FROM values WHERE (values.node_id = 1234)'
1115
+ v.model.should == Historical::Value
1116
+ ensure
1117
+ Object.send(:remove_const, :Historical)
1141
1118
  end
1142
-
1143
- @c2.one_to_many :historical_values, :class=>'Historical::Value'
1144
-
1145
- n = @c2.new(:id => 1234)
1146
- v = n.historical_values_dataset
1147
- v.should be_a_kind_of(Sequel::Dataset)
1148
- v.sql.should == 'SELECT * FROM values WHERE (values.node_id = 1234)'
1149
- v.model.should == Historical::Value
1150
1119
  end
1151
1120
 
1152
- it "should use explicit key if given" do
1121
+ it "should use a callback if given one as the argument" do
1153
1122
  @c2.one_to_many :attributes, :class => @c1, :key => :nodeid
1154
1123
 
1155
- n = @c2.new(:id => 1234)
1156
- a = n.attributes_dataset
1157
- a.should be_a_kind_of(Sequel::Dataset)
1158
- a.sql.should == 'SELECT * FROM attributes WHERE (attributes.nodeid = 1234)'
1124
+ d = @c2.load(:id => 1234)
1125
+ d.associations[:attributes] = []
1126
+ d.attributes(proc{|ds| ds.filter{name > 'M'}}).should_not == []
1127
+ MODEL_DB.sqls.should == ["SELECT * FROM attributes WHERE ((attributes.nodeid = 1234) AND (name > 'M'))"]
1128
+ end
1129
+
1130
+ it "should use explicit key if given" do
1131
+ @c2.one_to_many :attributes, :class => @c1, :key => :nodeid
1132
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE (attributes.nodeid = 1234)'
1159
1133
  end
1160
1134
 
1161
1135
  it "should support_composite keys" do
@@ -1180,10 +1154,8 @@ describe Sequel::Model, "one_to_many" do
1180
1154
  it "should define an add_ method that works on existing records" do
1181
1155
  @c2.one_to_many :attributes, :class => @c1
1182
1156
 
1183
- n = @c2.new(:id => 1234)
1184
- a = @c1.new(:id => 2345)
1185
- a.save
1186
- MODEL_DB.reset
1157
+ n = @c2.load(:id => 1234)
1158
+ a = @c1.load(:id => 2345)
1187
1159
  a.should == n.add_attribute(a)
1188
1160
  a.values.should == {:node_id => 1234, :id => 2345}
1189
1161
  MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 1234 WHERE (id = 2345)']
@@ -1192,34 +1164,32 @@ describe Sequel::Model, "one_to_many" do
1192
1164
  it "should define an add_ method that works on new records" do
1193
1165
  @c2.one_to_many :attributes, :class => @c1
1194
1166
 
1195
- n = @c2.new(:id => 1234)
1167
+ n = @c2.load(:id => 1234)
1196
1168
  a = @c1.new(:id => 234)
1197
- # do not save
1198
- MODEL_DB.reset
1169
+ @c1.dataset._fetch = {:node_id => 1234, :id => 234}
1199
1170
  a.should == n.add_attribute(a)
1200
- MODEL_DB.sqls.first.should =~ /INSERT INTO attributes \((node_)?id, (node_)?id\) VALUES \(1?234, 1?234\)/
1171
+ sqls = MODEL_DB.sqls
1172
+ sqls.shift.should =~ /INSERT INTO attributes \((node_)?id, (node_)?id\) VALUES \(1?234, 1?234\)/
1173
+ sqls.should == ["SELECT * FROM attributes WHERE (id = 234) LIMIT 1"]
1201
1174
  a.values.should == {:node_id => 1234, :id => 234}
1202
1175
  end
1203
1176
 
1204
1177
  it "should define a remove_ method that works on existing records" do
1205
1178
  @c2.one_to_many :attributes, :class => @c1
1206
1179
 
1207
- n = @c2.new(:id => 1234)
1208
- a = @c1.new(:id => 2345, :node_id => 1234)
1209
- a.save
1210
- MODEL_DB.reset
1180
+ n = @c2.load(:id => 1234)
1181
+ a = @c1.load(:id => 2345, :node_id => 1234)
1211
1182
  a.should == n.remove_attribute(a)
1212
1183
  a.values.should == {:node_id => nil, :id => 2345}
1213
- MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE (id = 2345)']
1184
+ MODEL_DB.sqls.should == ["SELECT 1 FROM attributes WHERE ((attributes.node_id = 1234) AND (id = 2345)) LIMIT 1", 'UPDATE attributes SET node_id = NULL WHERE (id = 2345)']
1214
1185
  end
1215
1186
 
1216
1187
  it "should have the remove_ method raise an error if the passed object is not already associated" do
1217
1188
  @c2.one_to_many :attributes, :class => @c1
1218
- @c1.dataset.opts[:empty] = true
1219
1189
 
1220
1190
  n = @c2.new(:id => 1234)
1221
1191
  a = @c1.load(:id => 2345, :node_id => 1234)
1222
- MODEL_DB.reset
1192
+ @c1.dataset._fetch = []
1223
1193
  proc{n.remove_attribute(a)}.should raise_error(Sequel::Error)
1224
1194
  MODEL_DB.sqls.should == ["SELECT 1 FROM attributes WHERE ((attributes.node_id = 1234) AND (id = 2345)) LIMIT 1"]
1225
1195
  end
@@ -1228,16 +1198,19 @@ describe Sequel::Model, "one_to_many" do
1228
1198
  @c2.one_to_many :attributes, :class => @c1
1229
1199
  n = @c2.new(:id => 1234)
1230
1200
  MODEL_DB.reset
1231
- @c1.load(:node_id => 1234, :id => 234).should == n.add_attribute(:id => 234)
1232
- MODEL_DB.sqls.first.should =~ /INSERT INTO attributes \((node_)?id, (node_)?id\) VALUES \(1?234, 1?234\)/
1201
+ @c1.dataset._fetch = {:node_id => 1234, :id => 234}
1202
+ n.add_attribute(:id => 234).should == @c1.load(:node_id => 1234, :id => 234)
1203
+ sqls = MODEL_DB.sqls
1204
+ sqls.shift.should =~ /INSERT INTO attributes \((node_)?id, (node_)?id\) VALUES \(1?234, 1?234\)/
1205
+ sqls.should == ["SELECT * FROM attributes WHERE (id = 234) LIMIT 1"]
1233
1206
  end
1234
1207
 
1235
1208
  it "should accept a primary key for the add_ method" do
1236
1209
  @c2.one_to_many :attributes, :class => @c1
1237
1210
  n = @c2.new(:id => 1234)
1238
- MODEL_DB.reset
1239
- @c1.load(:node_id => 1234, :id => 234).should == n.add_attribute(234)
1240
- MODEL_DB.sqls.first.should == "UPDATE attributes SET node_id = 1234 WHERE (id = 234)"
1211
+ @c1.dataset._fetch = {:id=>234, :node_id=>nil}
1212
+ n.add_attribute(234).should == @c1.load(:node_id => 1234, :id => 234)
1213
+ MODEL_DB.sqls.should == ["SELECT * FROM attributes WHERE id = 234", "UPDATE attributes SET node_id = 1234 WHERE (id = 234)"]
1241
1214
  end
1242
1215
 
1243
1216
  it "should raise an error in the add_ method if the passed associated object is not of the correct type" do
@@ -1248,13 +1221,8 @@ describe Sequel::Model, "one_to_many" do
1248
1221
  it "should accept a primary key for the remove_ method and remove an existing record" do
1249
1222
  @c2.one_to_many :attributes, :class => @c1
1250
1223
  n = @c2.new(:id => 1234)
1251
- ds = @c1.dataset
1252
- def ds.fetch_rows(sql)
1253
- db << sql
1254
- yield({:id=>234, :node_id=>1234})
1255
- end
1256
- MODEL_DB.reset
1257
- @c1.load(:node_id => nil, :id => 234).should == n.remove_attribute(234)
1224
+ @c1.dataset._fetch = {:id=>234, :node_id=>1234}
1225
+ n.remove_attribute(234).should == @c1.load(:node_id => nil, :id => 234)
1258
1226
  MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (attributes.id = 234)) LIMIT 1',
1259
1227
  'UPDATE attributes SET node_id = NULL WHERE (id = 234)']
1260
1228
  end
@@ -1268,10 +1236,8 @@ describe Sequel::Model, "one_to_many" do
1268
1236
  @c2.one_to_many :attributes, :class => @c1, :primary_key=>:xxx
1269
1237
 
1270
1238
  n = @c2.new(:id => 1234, :xxx=>5)
1271
- a = @c1.new(:id => 2345)
1272
- a.save
1273
- MODEL_DB.reset
1274
- a.should == n.add_attribute(a)
1239
+ a = @c1.load(:id => 2345)
1240
+ n.add_attribute(a).should == a
1275
1241
  MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 5 WHERE (id = 2345)']
1276
1242
  end
1277
1243
 
@@ -1279,15 +1245,13 @@ describe Sequel::Model, "one_to_many" do
1279
1245
  @c2.one_to_many :attributes, :class => @c1
1280
1246
 
1281
1247
  n = @c2.new(:id => 1234)
1282
- a = @c1.new(:id => 2345)
1283
- a.save
1284
- MODEL_DB.reset
1248
+ a = @c1.load(:id => 2345)
1285
1249
  n.associations[:attributes] = []
1286
1250
  a.should == n.add_attribute(a)
1287
1251
  a.should == n.add_attribute(a)
1288
1252
  a.values.should == {:node_id => 1234, :id => 2345}
1289
- MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 1234 WHERE (id = 2345)'] * 2
1290
1253
  n.attributes.should == [a]
1254
+ MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 1234 WHERE (id = 2345)'] * 2
1291
1255
  end
1292
1256
 
1293
1257
  it "should have add_ method respect composite keys" do
@@ -1295,18 +1259,24 @@ describe Sequel::Model, "one_to_many" do
1295
1259
 
1296
1260
  n = @c2.load(:id => 1234, :x=>5)
1297
1261
  a = @c1.load(:id => 2345)
1298
- a.should == n.add_attribute(a)
1299
- MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id = 1234|y = 5), (node_id = 1234|y = 5) WHERE \(id = 2345\)/
1262
+ n.add_attribute(a).should == a
1263
+ sqls = MODEL_DB.sqls
1264
+ sqls.shift.should =~ /UPDATE attributes SET (node_id = 1234|y = 5), (node_id = 1234|y = 5) WHERE \(id = 2345\)/
1265
+ sqls.should == []
1300
1266
  end
1301
1267
 
1302
1268
  it "should have add_ method accept a composite key" do
1303
1269
  @c1.set_primary_key :id, :z
1304
1270
  @c2.one_to_many :attributes, :class => @c1, :key =>[:node_id, :y], :primary_key=>[:id, :x]
1271
+ @c1.dataset._fetch = {:id => 2345, :z => 8, :node_id => 1234, :y=>5}
1305
1272
 
1306
1273
  n = @c2.load(:id => 1234, :x=>5)
1307
1274
  a = @c1.load(:id => 2345, :z => 8, :node_id => 1234, :y=>5)
1308
- a.should == n.add_attribute([2345, 8])
1309
- MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id|y) = (1234|5), (node_id|y) = (1234|5) WHERE \(\((id|z) = (2345|8)\) AND \((id|z) = (2345|8)\)\)/
1275
+ n.add_attribute([2345, 8]).should == a
1276
+ sqls = MODEL_DB.sqls
1277
+ sqls.shift.should =~ /SELECT \* FROM attributes WHERE \(\((id|z) = (2345|8)\) AND \((id|z) = (2345|8)\)\) LIMIT 1/
1278
+ sqls.shift.should =~ /UPDATE attributes SET (node_id|y) = (1234|5), (node_id|y) = (1234|5) WHERE \(\((id|z) = (2345|8)\) AND \((id|z) = (2345|8)\)\)/
1279
+ sqls.should == []
1310
1280
  end
1311
1281
 
1312
1282
  it "should have remove_ method respect composite keys" do
@@ -1314,31 +1284,29 @@ describe Sequel::Model, "one_to_many" do
1314
1284
 
1315
1285
  n = @c2.load(:id => 1234, :x=>5)
1316
1286
  a = @c1.load(:id => 2345, :node_id=>1234, :y=>5)
1317
- a.should == n.remove_attribute(a)
1318
- MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(id = 2345\)/
1287
+ n.remove_attribute(a).should == a
1288
+ sqls = MODEL_DB.sqls
1289
+ sqls.pop.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(id = 2345\)/
1290
+ sqls.should == ["SELECT 1 FROM attributes WHERE ((attributes.node_id = 1234) AND (attributes.y = 5) AND (id = 2345)) LIMIT 1"]
1319
1291
  end
1320
1292
 
1321
1293
  it "should accept a array of composite primary key values for the remove_ method and remove an existing record" do
1322
1294
  @c1.set_primary_key :id, :y
1323
1295
  @c2.one_to_many :attributes, :class => @c1, :key=>:node_id, :primary_key=>:id
1324
1296
  n = @c2.new(:id => 123)
1325
- ds = @c1.dataset
1326
- def ds.fetch_rows(sql)
1327
- db << sql
1328
- yield({:id=>234, :node_id=>123, :y=>5})
1329
- end
1330
- MODEL_DB.reset
1331
- @c1.load(:node_id => nil, :y => 5, :id => 234).should == n.remove_attribute([234, 5])
1332
- MODEL_DB.sqls.length.should == 2
1333
- MODEL_DB.sqls.first.should =~ /SELECT \* FROM attributes WHERE \(\(attributes.node_id = 123\) AND \(attributes\.(id|y) = (234|5)\) AND \(attributes\.(id|y) = (234|5)\)\) LIMIT 1/
1334
- MODEL_DB.sqls.last.should =~ /UPDATE attributes SET node_id = NULL WHERE \(\((id|y) = (234|5)\) AND \((id|y) = (234|5)\)\)/
1297
+ @c1.dataset._fetch = {:id=>234, :node_id=>123, :y=>5}
1298
+ n.remove_attribute([234, 5]).should == @c1.load(:node_id => nil, :y => 5, :id => 234)
1299
+ sqls = MODEL_DB.sqls
1300
+ sqls.length.should == 2
1301
+ sqls.first.should =~ /SELECT \* FROM attributes WHERE \(\(attributes.node_id = 123\) AND \(attributes\.(id|y) = (234|5)\) AND \(attributes\.(id|y) = (234|5)\)\) LIMIT 1/
1302
+ sqls.last.should =~ /UPDATE attributes SET node_id = NULL WHERE \(\((id|y) = (234|5)\) AND \((id|y) = (234|5)\)\)/
1335
1303
  end
1336
1304
 
1337
1305
  it "should raise an error in add_ and remove_ if the passed object returns false to save (is not valid)" do
1338
1306
  @c2.one_to_many :attributes, :class => @c1
1339
1307
  n = @c2.new(:id => 1234)
1340
1308
  a = @c1.new(:id => 2345)
1341
- def a.valid?(opts); false; end
1309
+ def a.validate() errors.add(:id, 'foo') end
1342
1310
  proc{n.add_attribute(a)}.should raise_error(Sequel::Error)
1343
1311
  proc{n.remove_attribute(a)}.should raise_error(Sequel::Error)
1344
1312
  end
@@ -1347,7 +1315,7 @@ describe Sequel::Model, "one_to_many" do
1347
1315
  @c2.one_to_many :attributes, :class => @c1, :validate=>false
1348
1316
  n = @c2.new(:id => 1234)
1349
1317
  a = @c1.new(:id => 2345)
1350
- def a.valid?(opts); false; end
1318
+ def a.validate() errors.add(:id, 'foo') end
1351
1319
  n.add_attribute(a).should == a
1352
1320
  n.remove_attribute(a).should == a
1353
1321
  end
@@ -1364,81 +1332,48 @@ describe Sequel::Model, "one_to_many" do
1364
1332
 
1365
1333
  it "should use :primary_key option if given" do
1366
1334
  @c1.one_to_many :nodes, :class => @c2, :primary_key => :node_id, :key=>:id
1367
- n = @c1.load(:id => 1234, :node_id=>4321)
1368
- n.nodes_dataset.sql.should == "SELECT * FROM nodes WHERE (nodes.id = 4321)"
1335
+ @c1.load(:id => 1234, :node_id=>4321).nodes_dataset.sql.should == "SELECT * FROM nodes WHERE (nodes.id = 4321)"
1369
1336
  end
1370
1337
 
1371
1338
  it "should support a select option" do
1372
1339
  @c2.one_to_many :attributes, :class => @c1, :select => [:id, :name]
1373
-
1374
- n = @c2.new(:id => 1234)
1375
- n.attributes_dataset.sql.should == "SELECT id, name FROM attributes WHERE (attributes.node_id = 1234)"
1340
+ @c2.new(:id => 1234).attributes_dataset.sql.should == "SELECT id, name FROM attributes WHERE (attributes.node_id = 1234)"
1376
1341
  end
1377
1342
 
1378
1343
  it "should support a conditions option" do
1379
1344
  @c2.one_to_many :attributes, :class => @c1, :conditions => {:a=>32}
1380
- n = @c2.new(:id => 1234)
1381
- n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (a = 32))"
1345
+ @c2.new(:id => 1234).attributes_dataset.sql.should == "SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (a = 32))"
1382
1346
  @c2.one_to_many :attributes, :class => @c1, :conditions => ~:a
1383
- n = @c2.new(:id => 1234)
1384
- n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND NOT a)"
1347
+ @c2.new(:id => 1234).attributes_dataset.sql.should == "SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND NOT a)"
1385
1348
  end
1386
1349
 
1387
1350
  it "should support an order option" do
1388
1351
  @c2.one_to_many :attributes, :class => @c1, :order => :kind
1389
-
1390
- n = @c2.new(:id => 1234)
1391
- n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (attributes.node_id = 1234) ORDER BY kind"
1352
+ @c2.new(:id => 1234).attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (attributes.node_id = 1234) ORDER BY kind"
1392
1353
  end
1393
1354
 
1394
1355
  it "should support an array for the order option" do
1395
1356
  @c2.one_to_many :attributes, :class => @c1, :order => [:kind1, :kind2]
1396
-
1397
- n = @c2.new(:id => 1234)
1398
- n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (attributes.node_id = 1234) ORDER BY kind1, kind2"
1357
+ @c2.new(:id => 1234).attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (attributes.node_id = 1234) ORDER BY kind1, kind2"
1399
1358
  end
1400
1359
 
1401
1360
  it "should return array with all members of the association" do
1402
1361
  @c2.one_to_many :attributes, :class => @c1
1403
-
1404
- n = @c2.new(:id => 1234)
1405
- atts = n.attributes
1406
- atts.should be_a_kind_of(Array)
1407
- atts.size.should == 1
1408
- atts.first.should be_a_kind_of(@c1)
1409
- atts.first.values.should == {}
1410
-
1411
- MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
1362
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE (attributes.node_id = 1234)'
1412
1363
  end
1413
1364
 
1414
1365
  it "should accept a block" do
1415
1366
  @c2.one_to_many :attributes, :class => @c1 do |ds|
1416
1367
  ds.filter(:xxx => @xxx)
1417
1368
  end
1418
-
1419
- n = @c2.new(:id => 1234)
1420
- atts = n.attributes
1421
- atts.should be_a_kind_of(Array)
1422
- atts.size.should == 1
1423
- atts.first.should be_a_kind_of(@c1)
1424
- atts.first.values.should == {}
1425
-
1426
- MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx IS NULL))']
1369
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx IS NULL))'
1427
1370
  end
1428
1371
 
1429
1372
  it "should support :order option with block" do
1430
1373
  @c2.one_to_many :attributes, :class => @c1, :order => :kind do |ds|
1431
1374
  ds.filter(:xxx => @xxx)
1432
1375
  end
1433
-
1434
- n = @c2.new(:id => 1234)
1435
- atts = n.attributes
1436
- atts.should be_a_kind_of(Array)
1437
- atts.size.should == 1
1438
- atts.first.should be_a_kind_of(@c1)
1439
- atts.first.values.should == {}
1440
-
1441
- MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx IS NULL)) ORDER BY kind']
1376
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx IS NULL)) ORDER BY kind'
1442
1377
  end
1443
1378
 
1444
1379
  it "should have the block argument affect the _dataset method" do
@@ -1450,18 +1385,11 @@ describe Sequel::Model, "one_to_many" do
1450
1385
 
1451
1386
  it "should support a :dataset option that is used instead of the default" do
1452
1387
  c1 = @c1
1453
- @c2.one_to_many :all_other_attributes, :class => @c1, :dataset=>proc{c1.filter(:nodeid=>pk).invert}, :order=>:a, :limit=>10 do |ds|
1388
+ @c2.one_to_many :all_other_attributes, :class => @c1, :dataset=>proc{c1.exclude(:nodeid=>pk)}, :order=>:a, :limit=>10 do |ds|
1454
1389
  ds.filter(:xxx => 5)
1455
1390
  end
1456
-
1457
1391
  @c2.new(:id => 1234).all_other_attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE ((nodeid != 1234) AND (xxx = 5)) ORDER BY a LIMIT 10'
1458
- n = @c2.new(:id => 1234)
1459
- atts = n.all_other_attributes
1460
- atts.should be_a_kind_of(Array)
1461
- atts.size.should == 1
1462
- atts.first.should be_a_kind_of(@c1)
1463
- atts.first.values.should == {}
1464
-
1392
+ @c2.new(:id => 1234).all_other_attributes.should == [@c1.load({})]
1465
1393
  MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((nodeid != 1234) AND (xxx = 5)) ORDER BY a LIMIT 10']
1466
1394
  end
1467
1395
 
@@ -1479,9 +1407,7 @@ describe Sequel::Model, "one_to_many" do
1479
1407
 
1480
1408
  it "should set cached instance variable when accessed" do
1481
1409
  @c2.one_to_many :attributes, :class => @c1
1482
-
1483
1410
  n = @c2.new(:id => 1234)
1484
- MODEL_DB.reset
1485
1411
  n.associations.include?(:attributes).should == false
1486
1412
  atts = n.attributes
1487
1413
  atts.should == n.associations[:attributes]
@@ -1490,9 +1416,7 @@ describe Sequel::Model, "one_to_many" do
1490
1416
 
1491
1417
  it "should use cached instance variable if available" do
1492
1418
  @c2.one_to_many :attributes, :class => @c1
1493
-
1494
1419
  n = @c2.new(:id => 1234)
1495
- MODEL_DB.reset
1496
1420
  n.associations[:attributes] = 42
1497
1421
  n.attributes.should == 42
1498
1422
  MODEL_DB.sqls.should == []
@@ -1500,9 +1424,7 @@ describe Sequel::Model, "one_to_many" do
1500
1424
 
1501
1425
  it "should not use cached instance variable if asked to reload" do
1502
1426
  @c2.one_to_many :attributes, :class => @c1
1503
-
1504
1427
  n = @c2.new(:id => 1234)
1505
- MODEL_DB.reset
1506
1428
  n.associations[:attributes] = 42
1507
1429
  n.attributes(true).should_not == 42
1508
1430
  MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
@@ -1510,10 +1432,8 @@ describe Sequel::Model, "one_to_many" do
1510
1432
 
1511
1433
  it "should add item to cached instance variable if it exists when calling add_" do
1512
1434
  @c2.one_to_many :attributes, :class => @c1
1513
-
1514
1435
  n = @c2.new(:id => 1234)
1515
- att = @c1.new(:id => 345)
1516
- MODEL_DB.reset
1436
+ att = @c1.load(:id => 345)
1517
1437
  a = []
1518
1438
  n.associations[:attributes] = a
1519
1439
  n.add_attribute(att)
@@ -1535,7 +1455,6 @@ describe Sequel::Model, "one_to_many" do
1535
1455
 
1536
1456
  n = @c2.load(:id => 1234)
1537
1457
  att = @c1.load(:id => 345)
1538
- MODEL_DB.reset
1539
1458
  a = [att]
1540
1459
  n.associations[:attributes] = a
1541
1460
  n.remove_attribute(att)
@@ -1587,13 +1506,9 @@ describe Sequel::Model, "one_to_many" do
1587
1506
  n = @c2.new(:id => 1234)
1588
1507
  atts = n.attributes
1589
1508
  MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
1590
- atts.should be_a_kind_of(Array)
1591
- atts.size.should == 1
1592
- atts.first.should be_a_kind_of(@c1)
1593
- atts.first.values.should == {}
1594
- atts.first.node.should == n
1595
-
1596
- MODEL_DB.sqls.length.should == 1
1509
+ atts.should == [@c1.load({})]
1510
+ atts.map{|a| a.node}.should == [n]
1511
+ MODEL_DB.sqls.should == []
1597
1512
  end
1598
1513
 
1599
1514
  it "should use an explicit reciprocal instance variable if given" do
@@ -1602,19 +1517,15 @@ describe Sequel::Model, "one_to_many" do
1602
1517
  n = @c2.new(:id => 1234)
1603
1518
  atts = n.attributes
1604
1519
  MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
1605
- atts.should be_a_kind_of(Array)
1606
- atts.size.should == 1
1607
- atts.first.should be_a_kind_of(@c1)
1608
- atts.first.values.should == {}
1609
- atts.first.associations[:wxyz].should == n
1610
-
1611
- MODEL_DB.sqls.length.should == 1
1520
+ atts.should == [@c1.load({})]
1521
+ atts.map{|a| a.associations[:wxyz]}.should == [n]
1522
+ MODEL_DB.sqls.should == []
1612
1523
  end
1613
1524
 
1614
1525
  it "should have an remove_all_ method that removes all associations" do
1615
1526
  @c2.one_to_many :attributes, :class => @c1
1616
1527
  @c2.new(:id => 1234).remove_all_attributes
1617
- MODEL_DB.sqls.first.should == 'UPDATE attributes SET node_id = NULL WHERE (node_id = 1234)'
1528
+ MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE (node_id = 1234)']
1618
1529
  end
1619
1530
 
1620
1531
  it "should have remove_all method respect association filters" do
@@ -1628,13 +1539,15 @@ describe Sequel::Model, "one_to_many" do
1628
1539
  it "should have the remove_all_ method respect the :primary_key option" do
1629
1540
  @c2.one_to_many :attributes, :class => @c1, :primary_key=>:xxx
1630
1541
  @c2.new(:id => 1234, :xxx=>5).remove_all_attributes
1631
- MODEL_DB.sqls.first.should == 'UPDATE attributes SET node_id = NULL WHERE (node_id = 5)'
1542
+ MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE (node_id = 5)']
1632
1543
  end
1633
1544
 
1634
1545
  it "should have the remove_all_ method respect composite keys" do
1635
1546
  @c2.one_to_many :attributes, :class => @c1, :key=>[:node_id, :y], :primary_key=>[:id, :x]
1636
1547
  @c2.new(:id => 1234, :x=>5).remove_all_attributes
1637
- MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(\(node_id = 1234\) AND \(y = 5\)\)/
1548
+ sqls = MODEL_DB.sqls
1549
+ sqls.pop.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(\(node_id = 1234\) AND \(y = 5\)\)/
1550
+ sqls.should == []
1638
1551
  end
1639
1552
 
1640
1553
  it "remove_all should set the cached instance variable to []" do
@@ -1648,10 +1561,8 @@ describe Sequel::Model, "one_to_many" do
1648
1561
  @c2.one_to_many :attributes, :class => @c1
1649
1562
  attrib = @c1.new(:id=>3)
1650
1563
  node = @c2.new(:id => 1234)
1651
- d = @c1.dataset
1652
- def d.fetch_rows(s); end
1564
+ @c1.dataset._fetch = [[], [{:id=>3, :node_id=>1234}]]
1653
1565
  node.attributes.should == []
1654
- def attrib.save(*); self end
1655
1566
  node.add_attribute(attrib)
1656
1567
  node.associations[:attributes].should == [attrib]
1657
1568
  node.remove_all_attributes.should == [attrib]
@@ -1665,15 +1576,12 @@ describe Sequel::Model, "one_to_many" do
1665
1576
  it "remove_all should remove the current item from all reciprocal instance varaibles if it cached instance variable exists" do
1666
1577
  @c2.one_to_many :attributes, :class => @c1
1667
1578
  @c1.many_to_one :node, :class => @c2
1668
- d = @c1.dataset
1669
- def d.fetch_rows(s); end
1670
- d = @c2.dataset
1671
- def d.fetch_rows(s); end
1579
+ @c2.dataset._fetch = []
1580
+ @c1.dataset._fetch = [[], [{:id=>3, :node_id=>1234}]]
1672
1581
  attrib = @c1.new(:id=>3)
1673
- node = @c2.new(:id => 1234)
1582
+ node = @c2.load(:id => 1234)
1674
1583
  node.attributes.should == []
1675
1584
  attrib.node.should == nil
1676
- def attrib.save(*); self end
1677
1585
  node.add_attribute(attrib)
1678
1586
  attrib.associations[:node].should == node
1679
1587
  node.remove_all_attributes
@@ -1761,18 +1669,18 @@ describe Sequel::Model, "one_to_many" do
1761
1669
  h = []
1762
1670
  @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]
1763
1671
  @c2.class_eval do
1764
- class_variable_set(:@@blah, h)
1672
+ self::Foo = h
1765
1673
  def _add_attribute(v)
1766
- self.class.send(:class_variable_get, :@@blah) << 4
1674
+ model::Foo << 4
1767
1675
  end
1768
1676
  def _remove_attribute(v)
1769
- self.class.send(:class_variable_get, :@@blah) << 5
1677
+ model::Foo << 5
1770
1678
  end
1771
1679
  def blah(x)
1772
- self.class.send(:class_variable_get, :@@blah) << x.pk
1680
+ model::Foo << x.pk
1773
1681
  end
1774
1682
  def blahr(x)
1775
- self.class.send(:class_variable_get, :@@blah) << 6
1683
+ model::Foo << 6
1776
1684
  end
1777
1685
  end
1778
1686
  p = @c2.load(:id=>10)
@@ -1788,17 +1696,12 @@ describe Sequel::Model, "one_to_many" do
1788
1696
  h = []
1789
1697
  @c2.one_to_many :attributes, :class => @c1, :after_load=>[proc{|x,y| h << [x.pk, y.collect{|z|z.pk}]}, :al]
1790
1698
  @c2.class_eval do
1791
- class_variable_set(:@@blah, h)
1699
+ self::Foo = h
1792
1700
  def al(v)
1793
- v.each{|x| self.class.send(:class_variable_get, :@@blah) << x.pk}
1794
- end
1795
- end
1796
- @c1.class_eval do
1797
- def @dataset.fetch_rows(sql)
1798
- yield({:id=>20})
1799
- yield({:id=>30})
1701
+ v.each{|x| model::Foo << x.pk}
1800
1702
  end
1801
1703
  end
1704
+ @c1.dataset._fetch = [{:id=>20}, {:id=>30}]
1802
1705
  p = @c2.load(:id=>10, :parent_id=>20)
1803
1706
  attributes = p.attributes
1804
1707
  h.should == [[10, [20, 30]], 20, 30]
@@ -1845,23 +1748,13 @@ describe Sequel::Model, "one_to_many" do
1845
1748
  end
1846
1749
 
1847
1750
  describe Sequel::Model, "many_to_many" do
1848
-
1849
1751
  before do
1850
- MODEL_DB.reset
1851
-
1852
1752
  @c1 = Class.new(Sequel::Model(:attributes)) do
1853
1753
  unrestrict_primary_key
1854
1754
  attr_accessor :yyy
1855
1755
  def self.name; 'Attribute'; end
1856
1756
  def self.to_s; 'Attribute'; end
1857
1757
  columns :id, :y
1858
- def _refresh(ds)
1859
- self
1860
- end
1861
-
1862
- def self.[](id)
1863
- load(id.is_a?(Array) ? {:id => id[0], :y => id[1]} : {:id => id})
1864
- end
1865
1758
  end
1866
1759
 
1867
1760
  @c2 = Class.new(Sequel::Model(:nodes)) do
@@ -1873,98 +1766,61 @@ describe Sequel::Model, "many_to_many" do
1873
1766
  columns :id, :x
1874
1767
  end
1875
1768
  @dataset = @c2.dataset
1769
+ @c1.dataset.autoid = 1
1876
1770
 
1877
- [@c1, @c2].each do |c|
1878
- c.dataset.extend(Module.new {
1879
- def fetch_rows(sql)
1880
- @db << sql
1881
- yield Hash.new
1882
- end
1883
- })
1884
- end
1771
+ [@c1, @c2].each{|c| c.dataset._fetch = {}}
1772
+ MODEL_DB.reset
1885
1773
  end
1886
1774
 
1887
1775
  it "should use implicit key values and join table if omitted" do
1888
1776
  @c2.many_to_many :attributes, :class => @c1
1889
-
1890
- n = @c2.new(:id => 1234)
1891
- a = n.attributes_dataset
1892
- a.should be_a_kind_of(Sequel::Dataset)
1893
- a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
1777
+ @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))'
1894
1778
  end
1895
1779
 
1896
1780
  it "should use implicit class if omitted" do
1897
- class ::Tag < Sequel::Model
1781
+ begin
1782
+ class ::Tag < Sequel::Model; end
1783
+ @c2.many_to_many :tags
1784
+ @c2.new(:id => 1234).tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON ((nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234))'
1785
+ ensure
1786
+ Object.send(:remove_const, :Tag)
1898
1787
  end
1899
-
1900
- @c2.many_to_many :tags
1901
-
1902
- n = @c2.new(:id => 1234)
1903
- a = n.tags_dataset
1904
- a.should be_a_kind_of(Sequel::Dataset)
1905
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON ((nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234))'
1906
1788
  end
1907
1789
 
1908
1790
  it "should use class inside module if given as a string" do
1909
- module ::Historical
1910
- class Tag < Sequel::Model
1791
+ begin
1792
+ module ::Historical
1793
+ class Tag < Sequel::Model; end
1911
1794
  end
1795
+ @c2.many_to_many :tags, :class=>'::Historical::Tag'
1796
+ @c2.new(:id => 1234).tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON ((nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234))'
1797
+ ensure
1798
+ Object.send(:remove_const, :Historical)
1912
1799
  end
1913
-
1914
- @c2.many_to_many :tags, :class=>'::Historical::Tag'
1915
-
1916
- n = @c2.new(:id => 1234)
1917
- a = n.tags_dataset
1918
- a.should be_a_kind_of(Sequel::Dataset)
1919
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON ((nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234))'
1920
1800
  end
1921
1801
 
1922
1802
  it "should use explicit key values and join table if given" do
1923
1803
  @c2.many_to_many :attributes, :class => @c1, :left_key => :nodeid, :right_key => :attributeid, :join_table => :attribute2node
1924
-
1925
- n = @c2.new(:id => 1234)
1926
- a = n.attributes_dataset
1927
- a.should be_a_kind_of(Sequel::Dataset)
1928
- a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attribute2node ON ((attribute2node.attributeid = attributes.id) AND (attribute2node.nodeid = 1234))'
1929
- end
1930
-
1931
- it "should handle an aliased join table when eager loading" do
1932
- r = @c2.many_to_many(:attributes, :class => @c1, :join_table => :attribute2node___attributes_nodes)
1933
-
1934
- a = @c2.eager_loading_dataset(r, @c2.dataset, [:id], nil)
1935
- a.sql.should == 'SELECT id, attributes_nodes.node_id AS x_foreign_key_x FROM nodes'
1804
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attribute2node ON ((attribute2node.attributeid = attributes.id) AND (attribute2node.nodeid = 1234))'
1936
1805
  end
1937
1806
 
1938
1807
  it "should support a conditions option" do
1939
1808
  @c2.many_to_many :attributes, :class => @c1, :conditions => {:a=>32}
1940
- n = @c2.new(:id => 1234)
1941
- a = n.attributes_dataset
1942
- a.should be_a_kind_of(Sequel::Dataset)
1943
- 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)'
1809
+ @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 (a = 32)'
1810
+
1944
1811
  @c2.many_to_many :attributes, :class => @c1, :conditions => ['a = ?', 32]
1945
- n = @c2.new(:id => 1234)
1946
- a = n.attributes_dataset
1947
- a.should be_a_kind_of(Sequel::Dataset)
1948
- 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)'
1949
- n.attributes.should == [@c1.load({})]
1812
+ @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 (a = 32)'
1813
+ @c2.new(:id => 1234).attributes.should == [@c1.load({})]
1950
1814
  end
1951
1815
 
1952
1816
  it "should support an order option" do
1953
1817
  @c2.many_to_many :attributes, :class => @c1, :order => :blah
1954
-
1955
- n = @c2.new(:id => 1234)
1956
- a = n.attributes_dataset
1957
- a.should be_a_kind_of(Sequel::Dataset)
1958
- 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'
1818
+ @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)) ORDER BY blah'
1959
1819
  end
1960
1820
 
1961
1821
  it "should support an array for the order option" do
1962
1822
  @c2.many_to_many :attributes, :class => @c1, :order => [:blah1, :blah2]
1963
-
1964
- n = @c2.new(:id => 1234)
1965
- a = n.attributes_dataset
1966
- a.should be_a_kind_of(Sequel::Dataset)
1967
- 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'
1823
+ @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)) ORDER BY blah1, blah2'
1968
1824
  end
1969
1825
 
1970
1826
  it "should support :left_primary_key and :right_primary_key options" do
@@ -1999,19 +1855,13 @@ describe Sequel::Model, "many_to_many" do
1999
1855
  it "should support a select option" do
2000
1856
  @c2.many_to_many :attributes, :class => @c1, :select => :blah
2001
1857
 
2002
- n = @c2.new(:id => 1234)
2003
- a = n.attributes_dataset
2004
- a.should be_a_kind_of(Sequel::Dataset)
2005
- a.sql.should == 'SELECT blah FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
1858
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT blah FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
2006
1859
  end
2007
1860
 
2008
1861
  it "should support an array for the select option" do
2009
1862
  @c2.many_to_many :attributes, :class => @c1, :select => [:attributes.*, :attribute_nodes__blah2]
2010
1863
 
2011
- n = @c2.new(:id => 1234)
2012
- a = n.attributes_dataset
2013
- a.should be_a_kind_of(Sequel::Dataset)
2014
- 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))'
1864
+ @c2.new(:id => 1234).attributes_dataset.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))'
2015
1865
  end
2016
1866
 
2017
1867
  it "should accept a block" do
@@ -2021,11 +1871,7 @@ describe Sequel::Model, "many_to_many" do
2021
1871
 
2022
1872
  n = @c2.new(:id => 1234)
2023
1873
  n.xxx = 555
2024
- a = n.attributes
2025
- a.should be_a_kind_of(Array)
2026
- a.size.should == 1
2027
- a.first.should be_a_kind_of(@c1)
2028
- 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)'
1874
+ n.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 = 555)'
2029
1875
  end
2030
1876
 
2031
1877
  it "should allow the :order option while accepting a block" do
@@ -2035,20 +1881,9 @@ describe Sequel::Model, "many_to_many" do
2035
1881
 
2036
1882
  n = @c2.new(:id => 1234)
2037
1883
  n.xxx = 555
2038
- a = n.attributes
2039
- a.should be_a_kind_of(Array)
2040
- a.size.should == 1
2041
- a.first.should be_a_kind_of(@c1)
2042
- 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'
1884
+ n.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 = 555) ORDER BY blah1, blah2'
2043
1885
  end
2044
1886
 
2045
- it "should have the block argument affect the _dataset method" do
2046
- @c2.many_to_many :attributes, :class => @c1 do |ds|
2047
- ds.filter(:xxx => 456)
2048
- end
2049
- @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)'
2050
- end
2051
-
2052
1887
  it "should support a :dataset option that is used instead of the default" do
2053
1888
  c1 = @c1
2054
1889
  @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|
@@ -2058,11 +1893,8 @@ describe Sequel::Model, "many_to_many" do
2058
1893
  n = @c2.new(:id => 1234)
2059
1894
  n.xxx = 555
2060
1895
  n.attributes_dataset.sql.should == 'SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 10'
2061
- a = n.attributes
2062
- a.should be_a_kind_of(Array)
2063
- a.size.should == 1
2064
- a.first.should be_a_kind_of(@c1)
2065
- MODEL_DB.sqls.first.should == 'SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 10'
1896
+ n.attributes.should == [@c1.load({})]
1897
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 10']
2066
1898
  end
2067
1899
 
2068
1900
  it "should support a :limit option" do
@@ -2077,15 +1909,32 @@ describe Sequel::Model, "many_to_many" do
2077
1909
  @c2.new(:id => 1234).attributes_dataset.opts[:eager].should == {:attributes=>nil}
2078
1910
  end
2079
1911
 
1912
+ it "should handle an aliased join table" do
1913
+ @c2.many_to_many :attributes, :class => @c1, :join_table => :attribute2node___attributes_nodes
1914
+ n = @c2.load(:id => 1234)
1915
+ a = @c1.load(:id => 2345)
1916
+ n.attributes_dataset.sql.should == "SELECT attributes.* FROM attributes INNER JOIN attribute2node AS attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))"
1917
+ a.should == n.add_attribute(a)
1918
+ a.should == n.remove_attribute(a)
1919
+ n.remove_all_attributes
1920
+ sqls = MODEL_DB.sqls
1921
+ ['INSERT INTO attribute2node (node_id, attribute_id) VALUES (1234, 2345)',
1922
+ 'INSERT INTO attribute2node (attribute_id, node_id) VALUES (2345, 1234)'].should(include(sqls.shift))
1923
+ ["DELETE FROM attribute2node WHERE ((node_id = 1234) AND (attribute_id = 2345))",
1924
+ "DELETE FROM attribute2node WHERE ((attribute_id = 2345) AND (node_id = 1234))"].should(include(sqls.shift))
1925
+ sqls.should == ["DELETE FROM attribute2node WHERE (node_id = 1234)"]
1926
+ end
1927
+
2080
1928
  it "should define an add_ method that works on existing records" do
2081
1929
  @c2.many_to_many :attributes, :class => @c1
2082
1930
 
2083
1931
  n = @c2.load(:id => 1234)
2084
1932
  a = @c1.load(:id => 2345)
2085
- a.should == n.add_attribute(a)
1933
+ n.add_attribute(a).should == a
1934
+ sqls = MODEL_DB.sqls
2086
1935
  ['INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (1234, 2345)',
2087
- 'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (2345, 1234)'
2088
- ].should(include(MODEL_DB.sqls.first))
1936
+ 'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (2345, 1234)'].should(include(sqls.shift))
1937
+ sqls.should == []
2089
1938
  end
2090
1939
 
2091
1940
  it "should define an add_ method that works with a primary key" do
@@ -2093,21 +1942,25 @@ describe Sequel::Model, "many_to_many" do
2093
1942
 
2094
1943
  n = @c2.load(:id => 1234)
2095
1944
  a = @c1.load(:id => 2345)
2096
- a.should == n.add_attribute(2345)
1945
+ @c1.dataset._fetch = {:id=>2345}
1946
+ n.add_attribute(2345).should == a
1947
+ sqls = MODEL_DB.sqls
2097
1948
  ['INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (1234, 2345)',
2098
- 'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (2345, 1234)'
2099
- ].should(include(MODEL_DB.sqls.first))
1949
+ 'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (2345, 1234)'].should(include(sqls.pop))
1950
+ sqls.should == ["SELECT * FROM attributes WHERE id = 2345"]
2100
1951
  end
2101
1952
 
2102
1953
  it "should allow passing a hash to the add_ method which creates a new record" do
2103
1954
  @c2.many_to_many :attributes, :class => @c1
2104
1955
 
2105
1956
  n = @c2.load(:id => 1234)
2106
- @c1.load(:id => 1).should == n.add_attribute(:id => 1)
2107
- MODEL_DB.sqls.first.should == 'INSERT INTO attributes (id) VALUES (1)'
1957
+ @c1.dataset._fetch = {:id=>1}
1958
+ n.add_attribute(:id => 1).should == @c1.load(:id => 1)
1959
+ sqls = MODEL_DB.sqls
2108
1960
  ['INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (1234, 1)',
2109
1961
  'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (1, 1234)'
2110
- ].should(include(MODEL_DB.sqls.last))
1962
+ ].should(include(sqls.pop))
1963
+ sqls.should == ['INSERT INTO attributes (id) VALUES (1)', "SELECT * FROM attributes WHERE (id = 1) LIMIT 1"]
2111
1964
  end
2112
1965
 
2113
1966
  it "should define a remove_ method that works on existing records" do
@@ -2115,8 +1968,8 @@ describe Sequel::Model, "many_to_many" do
2115
1968
 
2116
1969
  n = @c2.new(:id => 1234)
2117
1970
  a = @c1.new(:id => 2345)
2118
- a.should == n.remove_attribute(a)
2119
- MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 2345))'
1971
+ n.remove_attribute(a).should == a
1972
+ MODEL_DB.sqls.should == ['DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 2345))']
2120
1973
  end
2121
1974
 
2122
1975
  it "should raise an error in the add_ method if the passed associated object is not of the correct type" do
@@ -2127,13 +1980,8 @@ describe Sequel::Model, "many_to_many" do
2127
1980
  it "should accept a primary key for the remove_ method and remove an existing record" do
2128
1981
  @c2.many_to_many :attributes, :class => @c1
2129
1982
  n = @c2.new(:id => 1234)
2130
- ds = @c1.dataset
2131
- def ds.fetch_rows(sql)
2132
- db << sql
2133
- yield({:id=>234})
2134
- end
2135
- MODEL_DB.reset
2136
- @c1.load(:id => 234).should == n.remove_attribute(234)
1983
+ @c1.dataset._fetch = {:id=>234}
1984
+ n.remove_attribute(234).should == @c1.load(:id => 234)
2137
1985
  MODEL_DB.sqls.should == ["SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (attributes.id = 234) LIMIT 1",
2138
1986
  "DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 234))"]
2139
1987
  end
@@ -2148,10 +1996,12 @@ describe Sequel::Model, "many_to_many" do
2148
1996
 
2149
1997
  n = @c2.load(:id => 1234).set(:xxx=>5)
2150
1998
  a = @c1.load(:id => 2345).set(:yyy=>8)
2151
- a.should == n.add_attribute(a)
1999
+ n.add_attribute(a).should == a
2000
+ sqls = MODEL_DB.sqls
2152
2001
  ['INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (5, 8)',
2153
2002
  'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (8, 5)'
2154
- ].should(include(MODEL_DB.sqls.first))
2003
+ ].should(include(sqls.pop))
2004
+ sqls.should == []
2155
2005
  end
2156
2006
 
2157
2007
  it "should have add_ method not add the same object to the cached association array if the object is already in the array" do
@@ -2170,7 +2020,9 @@ describe Sequel::Model, "many_to_many" do
2170
2020
  n = @c2.load(:id => 1234, :x=>5)
2171
2021
  a = @c1.load(:id => 2345, :y=>8)
2172
2022
  a.should == n.add_attribute(a)
2173
- m = /INSERT INTO attributes_nodes \((\w+), (\w+), (\w+), (\w+)\) VALUES \((\d+), (\d+), (\d+), (\d+)\)/.match(MODEL_DB.sqls.first)
2023
+ sqls = MODEL_DB.sqls
2024
+ m = /INSERT INTO attributes_nodes \((\w+), (\w+), (\w+), (\w+)\) VALUES \((\d+), (\d+), (\d+), (\d+)\)/.match(sqls.pop)
2025
+ sqls.should == []
2174
2026
  m.should_not == nil
2175
2027
  map = {'l1'=>1234, 'l2'=>5, 'r1'=>2345, 'r2'=>8}
2176
2028
  %w[l1 l2 r1 r2].each do |x|
@@ -2187,10 +2039,15 @@ describe Sequel::Model, "many_to_many" do
2187
2039
 
2188
2040
  it "should have the add_ method respect composite keys" do
2189
2041
  @c2.many_to_many :attributes, :class => @c1, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :left_primary_key=>[:id, :x], :right_primary_key=>[:id, :y]
2042
+ @c1.set_primary_key [:id, :y]
2190
2043
  n = @c2.load(:id => 1234, :x=>5)
2191
2044
  a = @c1.load(:id => 2345, :y=>8)
2192
- a.should == n.add_attribute([2345, 8])
2193
- MODEL_DB.sqls.first.should =~ /INSERT INTO attributes_nodes \([lr][12], [lr][12], [lr][12], [lr][12]\) VALUES \((1234|5|2345|8), (1234|5|2345|8), (1234|5|2345|8), (1234|5|2345|8)\)/
2045
+ @c1.dataset._fetch = {:id => 2345, :y=>8}
2046
+ n.add_attribute([2345, 8]).should == a
2047
+ sqls = MODEL_DB.sqls
2048
+ sqls.shift.should =~ /SELECT \* FROM attributes WHERE \(\((id|y) = (8|2345)\) AND \((id|y) = (8|2345)\)\) LIMIT 1/
2049
+ sqls.pop.should =~ /INSERT INTO attributes_nodes \([lr][12], [lr][12], [lr][12], [lr][12]\) VALUES \((1234|5|2345|8), (1234|5|2345|8), (1234|5|2345|8), (1234|5|2345|8)\)/
2050
+ sqls.should == []
2194
2051
  end
2195
2052
 
2196
2053
  it "should have the remove_ method respect the :left_primary_key and :right_primary_key options" do
@@ -2198,8 +2055,8 @@ describe Sequel::Model, "many_to_many" do
2198
2055
 
2199
2056
  n = @c2.new(:id => 1234, :xxx=>5)
2200
2057
  a = @c1.new(:id => 2345, :yyy=>8)
2201
- a.should == n.remove_attribute(a)
2202
- MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE ((node_id = 5) AND (attribute_id = 8))'
2058
+ n.remove_attribute(a).should == a
2059
+ MODEL_DB.sqls.should == ['DELETE FROM attributes_nodes WHERE ((node_id = 5) AND (attribute_id = 8))']
2203
2060
  end
2204
2061
 
2205
2062
  it "should have the remove_ method respect composite keys" do
@@ -2214,17 +2071,12 @@ describe Sequel::Model, "many_to_many" do
2214
2071
  @c1.set_primary_key [:id, :y]
2215
2072
  @c2.many_to_many :attributes, :class => @c1
2216
2073
  n = @c2.new(:id => 1234)
2217
- ds = @c1.dataset
2218
- def ds.fetch_rows(sql)
2219
- db << sql
2220
- yield({:id=>234, :y=>8})
2221
- end
2222
- MODEL_DB.reset
2074
+ @c1.dataset._fetch = {:id=>234, :y=>8}
2223
2075
  @c1.load(:id => 234, :y=>8).should == n.remove_attribute([234, 8])
2076
+ sqls = MODEL_DB.sqls
2224
2077
  ["SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE ((attributes.id = 234) AND (attributes.y = 8)) LIMIT 1",
2225
- "SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE ((attributes.y = 8) AND (attributes.id = 234)) LIMIT 1"].should include(MODEL_DB.sqls.first)
2226
- MODEL_DB.sqls.last.should == "DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 234))"
2227
- MODEL_DB.sqls.length.should == 2
2078
+ "SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE ((attributes.y = 8) AND (attributes.id = 234)) LIMIT 1"].should include(sqls.shift)
2079
+ sqls.should == ["DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 234))"]
2228
2080
  end
2229
2081
 
2230
2082
  it "should raise an error if the model object doesn't have a valid primary key" do
@@ -2242,6 +2094,7 @@ describe Sequel::Model, "many_to_many" do
2242
2094
  n = @c1.new
2243
2095
  a = @c2.load(:id=>123)
2244
2096
  n.new?.should == true
2097
+ @c1.dataset._fetch = {:id=>1}
2245
2098
  a.add_attribute(n)
2246
2099
  n.new?.should == false
2247
2100
  end
@@ -2250,7 +2103,7 @@ describe Sequel::Model, "many_to_many" do
2250
2103
  @c2.many_to_many :attributes, :class => @c1
2251
2104
  n = @c1.new
2252
2105
  a = @c2.load(:id=>123)
2253
- def n.valid?(opts); false; end
2106
+ def n.validate() errors.add(:id, 'foo') end
2254
2107
  proc{a.add_attribute(n)}.should raise_error(Sequel::ValidationFailed)
2255
2108
  end
2256
2109
 
@@ -2259,7 +2112,7 @@ describe Sequel::Model, "many_to_many" do
2259
2112
  n = @c1.new
2260
2113
  n.raise_on_save_failure = false
2261
2114
  a = @c2.load(:id=>123)
2262
- def n.valid?(opts); false; end
2115
+ def n.validate() errors.add(:id, 'foo') end
2263
2116
  proc{a.add_attribute(n)}.should raise_error(Sequel::Error)
2264
2117
  end
2265
2118
 
@@ -2267,7 +2120,8 @@ describe Sequel::Model, "many_to_many" do
2267
2120
  @c2.many_to_many :attributes, :class => @c1, :validate=>false
2268
2121
  n = @c1.new
2269
2122
  a = @c2.load(:id=>123)
2270
- def n.valid?(opts); false; end
2123
+ def n.validate() errors.add(:id, 'foo') end
2124
+ @c1.dataset._fetch = {:id=>1}
2271
2125
  a.add_attribute(n)
2272
2126
  n.new?.should == false
2273
2127
  end
@@ -2282,31 +2136,23 @@ describe Sequel::Model, "many_to_many" do
2282
2136
  it "should provide an array with all members of the association" do
2283
2137
  @c2.many_to_many :attributes, :class => @c1
2284
2138
 
2285
- n = @c2.new(:id => 1234)
2286
- atts = n.attributes
2287
- atts.should be_a_kind_of(Array)
2288
- atts.size.should == 1
2289
- atts.first.should be_a_kind_of(@c1)
2290
-
2291
- 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))'
2139
+ @c2.new(:id => 1234).attributes.should == [@c1.load({})]
2140
+ MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))']
2292
2141
  end
2293
2142
 
2294
2143
  it "should set cached instance variable when accessed" do
2295
2144
  @c2.many_to_many :attributes, :class => @c1
2296
2145
 
2297
2146
  n = @c2.new(:id => 1234)
2298
- MODEL_DB.reset
2299
2147
  n.associations.include?(:attributes).should == false
2300
2148
  atts = n.attributes
2301
2149
  atts.should == n.associations[:attributes]
2302
- MODEL_DB.sqls.length.should == 1
2303
2150
  end
2304
2151
 
2305
2152
  it "should use cached instance variable if available" do
2306
2153
  @c2.many_to_many :attributes, :class => @c1
2307
2154
 
2308
2155
  n = @c2.new(:id => 1234)
2309
- MODEL_DB.reset
2310
2156
  n.associations[:attributes] = 42
2311
2157
  n.attributes.should == 42
2312
2158
  MODEL_DB.sqls.should == []
@@ -2316,18 +2162,16 @@ describe Sequel::Model, "many_to_many" do
2316
2162
  @c2.many_to_many :attributes, :class => @c1
2317
2163
 
2318
2164
  n = @c2.new(:id => 1234)
2319
- MODEL_DB.reset
2320
2165
  n.associations[:attributes] = 42
2321
2166
  n.attributes(true).should_not == 42
2322
- MODEL_DB.sqls.length.should == 1
2167
+ MODEL_DB.sqls.should == ["SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))"]
2323
2168
  end
2324
2169
 
2325
2170
  it "should add item to cached instance variable if it exists when calling add_" do
2326
2171
  @c2.many_to_many :attributes, :class => @c1
2327
2172
 
2328
2173
  n = @c2.new(:id => 1234)
2329
- att = @c1.new(:id => 345)
2330
- MODEL_DB.reset
2174
+ att = @c1.load(:id => 345)
2331
2175
  a = []
2332
2176
  n.associations[:attributes] = a
2333
2177
  n.add_attribute(att)
@@ -2339,7 +2183,7 @@ describe Sequel::Model, "many_to_many" do
2339
2183
  @c1.many_to_many :nodes, :class => @c2
2340
2184
 
2341
2185
  n = @c2.new(:id => 1234)
2342
- att = @c1.new(:id => 345)
2186
+ att = @c1.load(:id => 345)
2343
2187
  att.associations[:nodes] = []
2344
2188
  n.add_attribute(att)
2345
2189
  att.nodes.should == [n]
@@ -2349,8 +2193,7 @@ describe Sequel::Model, "many_to_many" do
2349
2193
  @c2.many_to_many :attributes, :class => @c1
2350
2194
 
2351
2195
  n = @c2.new(:id => 1234)
2352
- att = @c1.new(:id => 345)
2353
- MODEL_DB.reset
2196
+ att = @c1.load(:id => 345)
2354
2197
  a = [att]
2355
2198
  n.associations[:attributes] = a
2356
2199
  n.remove_attribute(att)
@@ -2397,13 +2240,13 @@ describe Sequel::Model, "many_to_many" do
2397
2240
  it "should have an remove_all_ method that removes all associations" do
2398
2241
  @c2.many_to_many :attributes, :class => @c1
2399
2242
  @c2.new(:id => 1234).remove_all_attributes
2400
- MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE (node_id = 1234)'
2243
+ MODEL_DB.sqls.should == ['DELETE FROM attributes_nodes WHERE (node_id = 1234)']
2401
2244
  end
2402
2245
 
2403
2246
  it "should have the remove_all_ method respect the :left_primary_key option" do
2404
2247
  @c2.many_to_many :attributes, :class => @c1, :left_primary_key=>:xxx
2405
2248
  @c2.new(:id => 1234, :xxx=>5).remove_all_attributes
2406
- MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE (node_id = 5)'
2249
+ MODEL_DB.sqls.should == ['DELETE FROM attributes_nodes WHERE (node_id = 5)']
2407
2250
  end
2408
2251
 
2409
2252
  it "should have the remove_all_ method respect composite keys" do
@@ -2421,10 +2264,9 @@ describe Sequel::Model, "many_to_many" do
2421
2264
 
2422
2265
  it "remove_all should return the array of previously associated items if the cached instance variable exists" do
2423
2266
  @c2.many_to_many :attributes, :class => @c1
2424
- attrib = @c1.new(:id=>3)
2425
- node = @c2.new(:id => 1234)
2426
- d = @c1.dataset
2427
- def d.fetch_rows(s); end
2267
+ attrib = @c1.load(:id=>3)
2268
+ node = @c2.load(:id => 1234)
2269
+ @c1.dataset._fetch = []
2428
2270
  node.attributes.should == []
2429
2271
  node.add_attribute(attrib)
2430
2272
  node.associations[:attributes].should == [attrib]
@@ -2439,11 +2281,9 @@ describe Sequel::Model, "many_to_many" do
2439
2281
  it "remove_all should remove the current item from all reciprocal instance varaibles if it cached instance variable exists" do
2440
2282
  @c2.many_to_many :attributes, :class => @c1
2441
2283
  @c1.many_to_many :nodes, :class => @c2
2442
- d = @c1.dataset
2443
- def d.fetch_rows(s); end
2444
- d = @c2.dataset
2445
- def d.fetch_rows(s); end
2446
- attrib = @c1.new(:id=>3)
2284
+ @c1.dataset._fetch = []
2285
+ @c2.dataset._fetch = []
2286
+ attrib = @c1.load(:id=>3)
2447
2287
  node = @c2.new(:id => 1234)
2448
2288
  node.attributes.should == []
2449
2289
  attrib.nodes.should == []
@@ -2459,8 +2299,9 @@ describe Sequel::Model, "many_to_many" do
2459
2299
  o.add_attribute(@c1.load(:id=>44))
2460
2300
  o.remove_attribute(@c1.load(:id=>45))
2461
2301
  o.remove_all_attributes
2462
- MODEL_DB.sqls.first =~ /INSERT INTO attributes_nodes \((node_id|attribute_id|x), (node_id|attribute_id|x), (node_id|attribute_id|x)\) VALUES \((1234|123|44), (1234|123|44), (1234|123|44)\)/
2463
- MODEL_DB.sqls[1..-1].should == ["DELETE FROM attributes_nodes WHERE ((x = 123) AND (node_id = 1234) AND (attribute_id = 45))",
2302
+ sqls = MODEL_DB.sqls
2303
+ sqls.shift =~ /INSERT INTO attributes_nodes \((node_id|attribute_id|x), (node_id|attribute_id|x), (node_id|attribute_id|x)\) VALUES \((1234|123|44), (1234|123|44), (1234|123|44)\)/
2304
+ sqls.should == ["DELETE FROM attributes_nodes WHERE ((x = 123) AND (node_id = 1234) AND (attribute_id = 45))",
2464
2305
  "DELETE FROM attributes_nodes WHERE ((x = 123) AND (node_id = 1234))"]
2465
2306
  end
2466
2307
 
@@ -2543,18 +2384,18 @@ describe Sequel::Model, "many_to_many" do
2543
2384
  h = []
2544
2385
  @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]
2545
2386
  @c2.class_eval do
2546
- class_variable_set(:@@blah, h)
2387
+ self::Foo = h
2547
2388
  def _add_attribute(v)
2548
- self.class.send(:class_variable_get, :@@blah) << 4
2389
+ model::Foo << 4
2549
2390
  end
2550
2391
  def _remove_attribute(v)
2551
- self.class.send(:class_variable_get, :@@blah) << 5
2392
+ model::Foo << 5
2552
2393
  end
2553
2394
  def blah(x)
2554
- self.class.send(:class_variable_get, :@@blah) << x.pk
2395
+ model::Foo << x.pk
2555
2396
  end
2556
2397
  def blahr(x)
2557
- self.class.send(:class_variable_get, :@@blah) << 6
2398
+ model::Foo << 6
2558
2399
  end
2559
2400
  end
2560
2401
  p = @c2.load(:id=>10)
@@ -2570,17 +2411,12 @@ describe Sequel::Model, "many_to_many" do
2570
2411
  h = []
2571
2412
  @c2.many_to_many :attributes, :class => @c1, :after_load=>[proc{|x,y| h << [x.pk, y.collect{|z|z.pk}]}, :al]
2572
2413
  @c2.class_eval do
2573
- class_variable_set(:@@blah, h)
2414
+ self::Foo = h
2574
2415
  def al(v)
2575
- v.each{|x| self.class.send(:class_variable_get, :@@blah) << x.pk}
2576
- end
2577
- end
2578
- @c1.class_eval do
2579
- def @dataset.fetch_rows(sql)
2580
- yield({:id=>20})
2581
- yield({:id=>30})
2416
+ v.each{|x| model::Foo << x.pk}
2582
2417
  end
2583
2418
  end
2419
+ @c1.dataset._fetch = [{:id=>20}, {:id=>30}]
2584
2420
  p = @c2.load(:id=>10, :parent_id=>20)
2585
2421
  attributes = p.attributes
2586
2422
  h.should == [[10, [20, 30]], 20, 30]
@@ -2623,14 +2459,7 @@ describe Sequel::Model, "many_to_many" do
2623
2459
 
2624
2460
  it "should support a :uniq option that removes duplicates from the association" do
2625
2461
  @c2.many_to_many :attributes, :class => @c1, :uniq=>true
2626
- @c1.class_eval do
2627
- def @dataset.fetch_rows(sql)
2628
- yield({:id=>20})
2629
- yield({:id=>30})
2630
- yield({:id=>20})
2631
- yield({:id=>30})
2632
- end
2633
- end
2462
+ @c1.dataset._fetch = [{:id=>20}, {:id=>30}, {:id=>20}, {:id=>30}]
2634
2463
  @c2.load(:id=>10, :parent_id=>20).attributes.should == [@c1.load(:id=>20), @c1.load(:id=>30)]
2635
2464
  end
2636
2465
 
@@ -2651,76 +2480,435 @@ describe Sequel::Model, "many_to_many" do
2651
2480
  @c2.many_to_many :attributes, :class => @c1 do |ds|
2652
2481
  ds.filter(:join_table_att=>3)
2653
2482
  end
2654
- @c1.dataset.instance_eval do
2655
- def fetch_rows(sql, &block)
2656
- db.sqls << sql
2657
- yield(:id=>2)
2658
- end
2659
- end
2483
+ @c1.dataset._fetch = {:id=>2}
2660
2484
  @c2.load(:id=>1).remove_attribute(2)
2661
2485
  MODEL_DB.sqls.should == ["SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1)) WHERE ((join_table_att = 3) AND (attributes.id = 2)) LIMIT 1",
2662
2486
  "DELETE FROM attributes_nodes WHERE ((node_id = 1) AND (attribute_id = 2))"]
2663
2487
  end
2664
2488
  end
2665
2489
 
2666
- describe Sequel::Model, " association reflection methods" do
2490
+ describe "Filtering by associations" do
2667
2491
  before do
2668
- MODEL_DB.reset
2669
- @c1 = Class.new(Sequel::Model(:nodes)) do
2670
- def self.name; 'Node'; end
2671
- def self.to_s; 'Node'; end
2492
+ @Album = Class.new(Sequel::Model(:albums))
2493
+ artist = @Artist = Class.new(Sequel::Model(:artists))
2494
+ tag = @Tag = Class.new(Sequel::Model(:tags))
2495
+ track = @Track = Class.new(Sequel::Model(:tracks))
2496
+ album_info = @AlbumInfo = Class.new(Sequel::Model(:album_infos))
2497
+ @Artist.columns :id, :id1, :id2
2498
+ @Tag.columns :id, :tid1, :tid2
2499
+ @Track.columns :id, :album_id, :album_id1, :album_id2
2500
+ @AlbumInfo.columns :id, :album_id, :album_id1, :album_id2
2501
+ @Album.class_eval do
2502
+ columns :id, :id1, :id2, :artist_id, :artist_id1, :artist_id2
2503
+ many_to_one :artist, :class=>artist
2504
+ one_to_many :tracks, :class=>track, :key=>:album_id
2505
+ one_to_one :album_info, :class=>album_info, :key=>:album_id
2506
+ many_to_many :tags, :class=>tag, :left_key=>:album_id, :join_table=>:albums_tags
2507
+
2508
+ many_to_one :cartist, :class=>artist, :key=>[:artist_id1, :artist_id2], :primary_key=>[:id1, :id2]
2509
+ one_to_many :ctracks, :class=>track, :key=>[:album_id1, :album_id2], :primary_key=>[:id1, :id2]
2510
+ one_to_one :calbum_info, :class=>album_info, :key=>[:album_id1, :album_id2], :primary_key=>[:id1, :id2]
2511
+ many_to_many :ctags, :class=>tag, :left_key=>[:album_id1, :album_id2], :left_primary_key=>[:id1, :id2], :right_key=>[:tag_id1, :tag_id2], :right_primary_key=>[:tid1, :tid2], :join_table=>:albums_tags
2672
2512
  end
2673
2513
  end
2674
-
2675
- it "#all_association_reflections should include all association reflection hashes" do
2676
- @c1.all_association_reflections.should == []
2677
2514
 
2678
- @c1.associate :many_to_one, :parent, :class => @c1
2679
- @c1.all_association_reflections.collect{|v| v[:name]}.should == [:parent]
2680
- @c1.all_association_reflections.collect{|v| v[:type]}.should == [:many_to_one]
2681
- @c1.all_association_reflections.collect{|v| v[:class]}.should == [@c1]
2515
+ it "should be able to filter on many_to_one associations" do
2516
+ @Album.filter(:artist=>@Artist.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE (artist_id = 3)'
2517
+ end
2518
+
2519
+ it "should be able to filter on one_to_many associations" do
2520
+ @Album.filter(:tracks=>@Track.load(:album_id=>3)).sql.should == 'SELECT * FROM albums WHERE (id = 3)'
2521
+ end
2522
+
2523
+ it "should be able to filter on one_to_one associations" do
2524
+ @Album.filter(:album_info=>@AlbumInfo.load(:album_id=>3)).sql.should == 'SELECT * FROM albums WHERE (id = 3)'
2525
+ end
2526
+
2527
+ it "should be able to filter on many_to_many associations" do
2528
+ @Album.filter(:tags=>@Tag.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE (id IN (SELECT album_id FROM albums_tags WHERE ((tag_id = 3) AND (album_id IS NOT NULL))))'
2529
+ end
2530
+
2531
+ it "should be able to filter on many_to_one associations with composite keys" do
2532
+ @Album.filter(:cartist=>@Artist.load(:id1=>3, :id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((artist_id1 = 3) AND (artist_id2 = 4))'
2533
+ end
2534
+
2535
+ it "should be able to filter on one_to_many associations with composite keys" do
2536
+ @Album.filter(:ctracks=>@Track.load(:album_id1=>3, :album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((id1 = 3) AND (id2 = 4))'
2537
+ end
2538
+
2539
+ it "should be able to filter on one_to_one associations with composite keys" do
2540
+ @Album.filter(:calbum_info=>@AlbumInfo.load(:album_id1=>3, :album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((id1 = 3) AND (id2 = 4))'
2541
+ end
2542
+
2543
+ it "should be able to filter on many_to_many associations with composite keys" do
2544
+ @Album.filter(:ctags=>@Tag.load(:tid1=>3, :tid2=>4)).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN (SELECT album_id1, album_id2 FROM albums_tags WHERE ((tag_id1 = 3) AND (tag_id2 = 4) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL))))'
2545
+ end
2546
+
2547
+ it "should work inside a complex filter" do
2548
+ artist = @Artist.load(:id=>3)
2549
+ @Album.filter{foo & {:artist=>artist}}.sql.should == 'SELECT * FROM albums WHERE (foo AND (artist_id = 3))'
2550
+ track = @Track.load(:album_id=>4)
2551
+ @Album.filter{foo & [[:artist, artist], [:tracks, track]]}.sql.should == 'SELECT * FROM albums WHERE (foo AND (artist_id = 3) AND (id = 4))'
2552
+ end
2553
+
2554
+ it "should raise for an invalid association name" do
2555
+ proc{@Album.filter(:foo=>@Artist.load(:id=>3)).sql}.should raise_error(Sequel::Error)
2556
+ end
2557
+
2558
+ it "should raise for an invalid association type" do
2559
+ @Album.many_to_many :iatags, :clone=>:tags
2560
+ @Album.association_reflection(:iatags)[:type] = :foo
2561
+ proc{@Album.filter(:iatags=>@Tag.load(:id=>3)).sql}.should raise_error(Sequel::Error)
2562
+ end
2563
+
2564
+ it "should raise for an invalid associated object class " do
2565
+ proc{@Album.filter(:tags=>@Artist.load(:id=>3)).sql}.should raise_error(Sequel::Error)
2566
+ end
2567
+
2568
+ it "should raise for an invalid associated object class when multiple objects are used" do
2569
+ proc{@Album.filter(:tags=>[@Tag.load(:id=>3), @Artist.load(:id=>3)]).sql}.should raise_error(Sequel::Error)
2570
+ end
2571
+
2572
+ it "should correctly handle case when a multiple value association is used" do
2573
+ proc{@Album.filter(:tags=>[@Tag.load(:id=>3), @Artist.load(:id=>3)]).sql}.should raise_error(Sequel::Error)
2574
+ end
2575
+
2576
+ it "should not affect non-association IN/NOT IN filtering with an empty array" do
2577
+ @Album.filter(:tag_id=>[]).sql.should == 'SELECT * FROM albums WHERE (tag_id != tag_id)'
2578
+ @Album.exclude(:tag_id=>[]).sql.should == 'SELECT * FROM albums WHERE (1 = 1)'
2579
+ end
2580
+
2581
+ it "should work correctly in subclasses" do
2582
+ c = Class.new(@Album)
2583
+ c.many_to_one :sartist, :class=>@Artist
2584
+ c.filter(:sartist=>@Artist.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE (sartist_id = 3)'
2585
+ end
2586
+
2587
+ it "should be able to exclude on many_to_one associations" do
2588
+ @Album.exclude(:artist=>@Artist.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE ((artist_id != 3) OR (artist_id IS NULL))'
2589
+ end
2590
+
2591
+ it "should be able to exclude on one_to_many associations" do
2592
+ @Album.exclude(:tracks=>@Track.load(:album_id=>3)).sql.should == 'SELECT * FROM albums WHERE ((id != 3) OR (id IS NULL))'
2593
+ end
2594
+
2595
+ it "should be able to exclude on one_to_one associations" do
2596
+ @Album.exclude(:album_info=>@AlbumInfo.load(:album_id=>3)).sql.should == 'SELECT * FROM albums WHERE ((id != 3) OR (id IS NULL))'
2597
+ end
2598
+
2599
+ it "should be able to exclude on many_to_many associations" do
2600
+ @Album.exclude(:tags=>@Tag.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (SELECT album_id FROM albums_tags WHERE ((tag_id = 3) AND (album_id IS NOT NULL)))) OR (id IS NULL))'
2601
+ end
2602
+
2603
+ it "should be able to exclude on many_to_one associations with composite keys" do
2604
+ @Album.exclude(:cartist=>@Artist.load(:id1=>3, :id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((artist_id1 != 3) OR (artist_id2 != 4) OR (artist_id1 IS NULL) OR (artist_id2 IS NULL))'
2605
+ end
2606
+
2607
+ it "should be able to exclude on one_to_many associations with composite keys" do
2608
+ @Album.exclude(:ctracks=>@Track.load(:album_id1=>3, :album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((id1 != 3) OR (id2 != 4) OR (id1 IS NULL) OR (id2 IS NULL))'
2609
+ end
2610
+
2611
+ it "should be able to exclude on one_to_one associations with composite keys" do
2612
+ @Album.exclude(:calbum_info=>@AlbumInfo.load(:album_id1=>3, :album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((id1 != 3) OR (id2 != 4) OR (id1 IS NULL) OR (id2 IS NULL))'
2613
+ end
2614
+
2615
+ it "should be able to exclude on many_to_many associations with composite keys" do
2616
+ @Album.exclude(:ctags=>@Tag.load(:tid1=>3, :tid2=>4)).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN (SELECT album_id1, album_id2 FROM albums_tags WHERE ((tag_id1 = 3) AND (tag_id2 = 4) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL)))) OR (id1 IS NULL) OR (id2 IS NULL))'
2617
+ end
2618
+
2619
+ it "should be able to filter on multiple many_to_one associations" do
2620
+ @Album.filter(:artist=>[@Artist.load(:id=>3), @Artist.load(:id=>4)]).sql.should == 'SELECT * FROM albums WHERE (artist_id IN (3, 4))'
2621
+ end
2622
+
2623
+ it "should be able to filter on multiple one_to_many associations" do
2624
+ @Album.filter(:tracks=>[@Track.load(:album_id=>3), @Track.load(:album_id=>4)]).sql.should == 'SELECT * FROM albums WHERE (id IN (3, 4))'
2625
+ end
2626
+
2627
+ it "should be able to filter on multiple one_to_one associations" do
2628
+ @Album.filter(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.load(:album_id=>4)]).sql.should == 'SELECT * FROM albums WHERE (id IN (3, 4))'
2629
+ end
2682
2630
 
2683
- @c1.associate :one_to_many, :children, :class => @c1
2684
- @c1.all_association_reflections.sort_by{|x|x[:name].to_s}
2685
- @c1.all_association_reflections.sort_by{|x|x[:name].to_s}.collect{|v| v[:name]}.should == [:children, :parent]
2686
- @c1.all_association_reflections.sort_by{|x|x[:name].to_s}.collect{|v| v[:type]}.should == [:one_to_many, :many_to_one]
2687
- @c1.all_association_reflections.sort_by{|x|x[:name].to_s}.collect{|v| v[:class]}.should == [@c1, @c1]
2631
+ it "should be able to filter on multiple many_to_many associations" do
2632
+ @Album.filter(:tags=>[@Tag.load(:id=>3), @Tag.load(:id=>4)]).sql.should == 'SELECT * FROM albums WHERE (id IN (SELECT album_id FROM albums_tags WHERE ((tag_id IN (3, 4)) AND (album_id IS NOT NULL))))'
2688
2633
  end
2689
2634
 
2690
- it "#association_reflection should return nil for nonexistent association" do
2691
- @c1.association_reflection(:blah).should == nil
2635
+ it "should be able to filter on multiple many_to_one associations with composite keys" do
2636
+ @Album.filter(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>5, :id2=>6)]).sql.should == 'SELECT * FROM albums WHERE ((artist_id1, artist_id2) IN ((3, 4), (5, 6)))'
2692
2637
  end
2693
2638
 
2694
- it "#association_reflection should return association reflection hash if association exists" do
2695
- @c1.associate :many_to_one, :parent, :class => @c1
2696
- @c1.association_reflection(:parent).should be_a_kind_of(Sequel::Model::Associations::AssociationReflection)
2697
- @c1.association_reflection(:parent)[:name].should == :parent
2698
- @c1.association_reflection(:parent)[:type].should == :many_to_one
2699
- @c1.association_reflection(:parent)[:class].should == @c1
2639
+ it "should be able to filter on multiple one_to_many associations with composite keys" do
2640
+ @Album.filter(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>5, :album_id2=>6)]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN ((3, 4), (5, 6)))'
2641
+ end
2642
+
2643
+ it "should be able to filter on multiple one_to_one associations with composite keys" do
2644
+ @Album.filter(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5, :album_id2=>6)]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN ((3, 4), (5, 6)))'
2645
+ end
2646
+
2647
+ it "should be able to filter on multiple many_to_many associations with composite keys" do
2648
+ @Album.filter(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5, :tid2=>6)]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN (SELECT album_id1, album_id2 FROM albums_tags WHERE (((tag_id1, tag_id2) IN ((3, 4), (5, 6))) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL))))'
2649
+ end
2650
+
2651
+ it "should be able to exclude on multiple many_to_one associations" do
2652
+ @Album.exclude(:artist=>[@Artist.load(:id=>3), @Artist.load(:id=>4)]).sql.should == 'SELECT * FROM albums WHERE ((artist_id NOT IN (3, 4)) OR (artist_id IS NULL))'
2653
+ end
2654
+
2655
+ it "should be able to exclude on multiple one_to_many associations" do
2656
+ @Album.exclude(:tracks=>[@Track.load(:album_id=>3), @Track.load(:album_id=>4)]).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (3, 4)) OR (id IS NULL))'
2657
+ end
2658
+
2659
+ it "should be able to exclude on multiple one_to_one associations" do
2660
+ @Album.exclude(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.load(:album_id=>4)]).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (3, 4)) OR (id IS NULL))'
2661
+ end
2662
+
2663
+ it "should be able to exclude on multiple many_to_many associations" do
2664
+ @Album.exclude(:tags=>[@Tag.load(:id=>3), @Tag.load(:id=>4)]).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (SELECT album_id FROM albums_tags WHERE ((tag_id IN (3, 4)) AND (album_id IS NOT NULL)))) OR (id IS NULL))'
2665
+ end
2666
+
2667
+ it "should be able to exclude on multiple many_to_one associations with composite keys" do
2668
+ @Album.exclude(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>5, :id2=>6)]).sql.should == 'SELECT * FROM albums WHERE (((artist_id1, artist_id2) NOT IN ((3, 4), (5, 6))) OR (artist_id1 IS NULL) OR (artist_id2 IS NULL))'
2669
+ end
2670
+
2671
+ it "should be able to exclude on multiple one_to_many associations with composite keys" do
2672
+ @Album.exclude(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>5, :album_id2=>6)]).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN ((3, 4), (5, 6))) OR (id1 IS NULL) OR (id2 IS NULL))'
2673
+ end
2674
+
2675
+ it "should be able to exclude on multiple one_to_one associations with composite keys" do
2676
+ @Album.exclude(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5, :album_id2=>6)]).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN ((3, 4), (5, 6))) OR (id1 IS NULL) OR (id2 IS NULL))'
2677
+ end
2678
+
2679
+ it "should be able to exclude on multiple many_to_many associations with composite keys" do
2680
+ @Album.exclude(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5, :tid2=>6)]).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN (SELECT album_id1, album_id2 FROM albums_tags WHERE (((tag_id1, tag_id2) IN ((3, 4), (5, 6))) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL)))) OR (id1 IS NULL) OR (id2 IS NULL))'
2681
+ end
2682
+
2683
+ it "should be able to handle NULL values when filtering many_to_one associations" do
2684
+ @Album.filter(:artist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2685
+ end
2686
+
2687
+ it "should be able to handle NULL values when filtering one_to_many associations" do
2688
+ @Album.filter(:tracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2689
+ end
2690
+
2691
+ it "should be able to handle NULL values when filtering one_to_one associations" do
2692
+ @Album.filter(:album_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2693
+ end
2694
+
2695
+ it "should be able to handle NULL values when filtering many_to_many associations" do
2696
+ @Album.filter(:tags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2697
+ end
2698
+
2699
+ it "should be able to handle filteringing with NULL values for many_to_one associations with composite keys" do
2700
+ @Album.filter(:cartist=>@Artist.load(:id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2701
+ @Album.filter(:cartist=>@Artist.load(:id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2702
+ @Album.filter(:cartist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2703
+ end
2704
+
2705
+ it "should be able to filtering with NULL values for one_to_many associations with composite keys" do
2706
+ @Album.filter(:ctracks=>@Track.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2707
+ @Album.filter(:ctracks=>@Track.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2708
+ @Album.filter(:ctracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2709
+ end
2710
+
2711
+ it "should be able to filtering with NULL values for one_to_one associations with composite keys" do
2712
+ @Album.filter(:calbum_info=>@AlbumInfo.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2713
+ @Album.filter(:calbum_info=>@AlbumInfo.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2714
+ @Album.filter(:calbum_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2715
+ end
2716
+
2717
+ it "should be able to filtering with NULL values for many_to_many associations with composite keys" do
2718
+ @Album.filter(:ctags=>@Tag.load(:tid1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2719
+ @Album.filter(:ctags=>@Tag.load(:tid2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2720
+ @Album.filter(:ctags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2721
+ end
2722
+
2723
+ it "should be able to handle NULL values when excluding many_to_one associations" do
2724
+ @Album.exclude(:artist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2725
+ end
2726
+
2727
+ it "should be able to handle NULL values when excluding one_to_many associations" do
2728
+ @Album.exclude(:tracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2729
+ end
2730
+
2731
+ it "should be able to handle NULL values when excluding one_to_one associations" do
2732
+ @Album.exclude(:album_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2733
+ end
2734
+
2735
+ it "should be able to handle NULL values when excluding many_to_many associations" do
2736
+ @Album.exclude(:tags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2737
+ end
2738
+
2739
+ it "should be able to handle excluding with NULL values for many_to_one associations with composite keys" do
2740
+ @Album.exclude(:cartist=>@Artist.load(:id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2741
+ @Album.exclude(:cartist=>@Artist.load(:id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2742
+ @Album.exclude(:cartist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2743
+ end
2744
+
2745
+ it "should be able to excluding with NULL values for one_to_many associations with composite keys" do
2746
+ @Album.exclude(:ctracks=>@Track.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2747
+ @Album.exclude(:ctracks=>@Track.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2748
+ @Album.exclude(:ctracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2749
+ end
2750
+
2751
+ it "should be able to excluding with NULL values for one_to_one associations with composite keys" do
2752
+ @Album.exclude(:calbum_info=>@AlbumInfo.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2753
+ @Album.exclude(:calbum_info=>@AlbumInfo.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2754
+ @Album.exclude(:calbum_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2755
+ end
2756
+
2757
+ it "should be able to excluding with NULL values for many_to_many associations with composite keys" do
2758
+ @Album.exclude(:ctags=>@Tag.load(:tid1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2759
+ @Album.exclude(:ctags=>@Tag.load(:tid2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2760
+ @Album.exclude(:ctags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2761
+ end
2762
+
2763
+ it "should be able to handle NULL values when filtering multiple many_to_one associations" do
2764
+ @Album.filter(:artist=>[@Artist.load(:id=>3), @Artist.new]).sql.should == 'SELECT * FROM albums WHERE (artist_id IN (3))'
2765
+ @Album.filter(:artist=>[@Artist.new, @Artist.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
2766
+ end
2767
+
2768
+ it "should be able to handle NULL values when filtering multiple one_to_many associations" do
2769
+ @Album.filter(:tracks=>[@Track.load(:album_id=>3), @Track.new]).sql.should == 'SELECT * FROM albums WHERE (id IN (3))'
2770
+ @Album.filter(:tracks=>[@Track.new, @Track.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
2771
+ end
2772
+
2773
+ it "should be able to handle NULL values when filtering multiple one_to_one associations" do
2774
+ @Album.filter(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE (id IN (3))'
2775
+ @Album.filter(:album_info=>[@AlbumInfo.new, @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
2776
+ end
2777
+
2778
+ it "should be able to handle NULL values when filtering multiple many_to_many associations" do
2779
+ @Album.filter(:tags=>[@Tag.load(:id=>3), @Tag.new]).sql.should == 'SELECT * FROM albums WHERE (id IN (SELECT album_id FROM albums_tags WHERE ((tag_id IN (3)) AND (album_id IS NOT NULL))))'
2780
+ @Album.filter(:tags=>[@Tag.new, @Tag.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
2781
+ end
2782
+
2783
+ it "should be able to handle NULL values when filtering multiple many_to_one associations with composite keys" do
2784
+ @Album.filter(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>3)]).sql.should == 'SELECT * FROM albums WHERE ((artist_id1, artist_id2) IN ((3, 4)))'
2785
+ @Album.filter(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.new]).sql.should == 'SELECT * FROM albums WHERE ((artist_id1, artist_id2) IN ((3, 4)))'
2786
+ end
2787
+
2788
+ it "should be able handle NULL values when filtering multiple one_to_many associations with composite keys" do
2789
+ @Album.filter(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>3)]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN ((3, 4)))'
2790
+ @Album.filter(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.new]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN ((3, 4)))'
2791
+ end
2792
+
2793
+ it "should be able to handle NULL values when filtering multiple one_to_one associations with composite keys" do
2794
+ @Album.filter(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5)]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN ((3, 4)))'
2795
+ @Album.filter(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN ((3, 4)))'
2796
+ end
2797
+
2798
+ it "should be able to handle NULL values when filtering multiple many_to_many associations with composite keys" do
2799
+ @Album.filter(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5)]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN (SELECT album_id1, album_id2 FROM albums_tags WHERE (((tag_id1, tag_id2) IN ((3, 4))) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL))))'
2800
+ @Album.filter(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.new]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN (SELECT album_id1, album_id2 FROM albums_tags WHERE (((tag_id1, tag_id2) IN ((3, 4))) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL))))'
2801
+ end
2802
+
2803
+ it "should be able to handle NULL values when excluding multiple many_to_one associations" do
2804
+ @Album.exclude(:artist=>[@Artist.load(:id=>3), @Artist.new]).sql.should == 'SELECT * FROM albums WHERE ((artist_id NOT IN (3)) OR (artist_id IS NULL))'
2805
+ @Album.exclude(:artist=>[@Artist.new, @Artist.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
2806
+ end
2807
+
2808
+ it "should be able to handle NULL values when excluding multiple one_to_many associations" do
2809
+ @Album.exclude(:tracks=>[@Track.load(:album_id=>3), @Track.new]).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (3)) OR (id IS NULL))'
2810
+ @Album.exclude(:tracks=>[@Track.new, @Track.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
2811
+ end
2812
+
2813
+ it "should be able to handle NULL values when excluding multiple one_to_one associations" do
2814
+ @Album.exclude(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (3)) OR (id IS NULL))'
2815
+ @Album.exclude(:album_info=>[@AlbumInfo.new, @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
2816
+ end
2817
+
2818
+ it "should be able to handle NULL values when excluding multiple many_to_many associations" do
2819
+ @Album.exclude(:tags=>[@Tag.load(:id=>3), @Tag.new]).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (SELECT album_id FROM albums_tags WHERE ((tag_id IN (3)) AND (album_id IS NOT NULL)))) OR (id IS NULL))'
2820
+ @Album.exclude(:tags=>[@Tag.new, @Tag.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
2821
+ end
2822
+
2823
+ it "should be able to handle NULL values when excluding multiple many_to_one associations with composite keys" do
2824
+ @Album.exclude(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>3)]).sql.should == 'SELECT * FROM albums WHERE (((artist_id1, artist_id2) NOT IN ((3, 4))) OR (artist_id1 IS NULL) OR (artist_id2 IS NULL))'
2825
+ @Album.exclude(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.new]).sql.should == 'SELECT * FROM albums WHERE (((artist_id1, artist_id2) NOT IN ((3, 4))) OR (artist_id1 IS NULL) OR (artist_id2 IS NULL))'
2826
+ end
2827
+
2828
+ it "should be able handle NULL values when excluding multiple one_to_many associations with composite keys" do
2829
+ @Album.exclude(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>3)]).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN ((3, 4))) OR (id1 IS NULL) OR (id2 IS NULL))'
2830
+ @Album.exclude(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.new]).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN ((3, 4))) OR (id1 IS NULL) OR (id2 IS NULL))'
2831
+ end
2832
+
2833
+ it "should be able to handle NULL values when excluding multiple one_to_one associations with composite keys" do
2834
+ @Album.exclude(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5)]).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN ((3, 4))) OR (id1 IS NULL) OR (id2 IS NULL))'
2835
+ @Album.exclude(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN ((3, 4))) OR (id1 IS NULL) OR (id2 IS NULL))'
2836
+ end
2837
+
2838
+ it "should be able to handle NULL values when excluding multiple many_to_many associations with composite keys" do
2839
+ @Album.exclude(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5)]).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN (SELECT album_id1, album_id2 FROM albums_tags WHERE (((tag_id1, tag_id2) IN ((3, 4))) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL)))) OR (id1 IS NULL) OR (id2 IS NULL))'
2840
+ @Album.exclude(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.new]).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN (SELECT album_id1, album_id2 FROM albums_tags WHERE (((tag_id1, tag_id2) IN ((3, 4))) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL)))) OR (id1 IS NULL) OR (id2 IS NULL))'
2841
+ end
2842
+
2843
+ it "should be able to filter on many_to_one association datasets" do
2844
+ @Album.filter(:artist=>@Artist.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (artist_id IN (SELECT id FROM artists WHERE ((x = 1) AND (id IS NOT NULL))))'
2845
+ end
2846
+
2847
+ it "should be able to filter on one_to_many association datasets" do
2848
+ @Album.filter(:tracks=>@Track.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (id IN (SELECT album_id FROM tracks WHERE ((x = 1) AND (album_id IS NOT NULL))))'
2849
+ end
2850
+
2851
+ it "should be able to filter on one_to_one association datasets" do
2852
+ @Album.filter(:album_info=>@AlbumInfo.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (id IN (SELECT album_id FROM album_infos WHERE ((x = 1) AND (album_id IS NOT NULL))))'
2853
+ end
2854
+
2855
+ it "should be able to filter on many_to_many association datasets" do
2856
+ @Album.filter(:tags=>@Tag.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (id IN (SELECT album_id FROM albums_tags WHERE ((tag_id IN (SELECT id FROM tags WHERE ((x = 1) AND (id IS NOT NULL)))) AND (album_id IS NOT NULL))))'
2857
+ end
2858
+
2859
+ it "should be able to filter on many_to_one association datasets with composite keys" do
2860
+ @Album.filter(:cartist=>@Artist.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((artist_id1, artist_id2) IN (SELECT id1, id2 FROM artists WHERE ((x = 1) AND (id1 IS NOT NULL) AND (id2 IS NOT NULL))))'
2861
+ end
2862
+
2863
+ it "should be able to filter on one_to_many association datasets with composite keys" do
2864
+ @Album.filter(:ctracks=>@Track.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN (SELECT album_id1, album_id2 FROM tracks WHERE ((x = 1) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL))))'
2865
+ end
2866
+
2867
+ it "should be able to filter on one_to_one association datasets with composite keys" do
2868
+ @Album.filter(:calbum_info=>@AlbumInfo.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN (SELECT album_id1, album_id2 FROM album_infos WHERE ((x = 1) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL))))'
2869
+ end
2870
+
2871
+ it "should be able to filter on many_to_many association datasets with composite keys" do
2872
+ @Album.filter(:ctags=>@Tag.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN (SELECT album_id1, album_id2 FROM albums_tags WHERE (((tag_id1, tag_id2) IN (SELECT tid1, tid2 FROM tags WHERE ((x = 1) AND (tid1 IS NOT NULL) AND (tid2 IS NOT NULL)))) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL))))'
2873
+ end
2874
+
2875
+ it "should be able to exclude on many_to_one association datasets" do
2876
+ @Album.exclude(:artist=>@Artist.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((artist_id NOT IN (SELECT id FROM artists WHERE ((x = 1) AND (id IS NOT NULL)))) OR (artist_id IS NULL))'
2877
+ end
2878
+
2879
+ it "should be able to exclude on one_to_many association datasets" do
2880
+ @Album.exclude(:tracks=>@Track.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (SELECT album_id FROM tracks WHERE ((x = 1) AND (album_id IS NOT NULL)))) OR (id IS NULL))'
2881
+ end
2882
+
2883
+ it "should be able to exclude on one_to_one association datasets" do
2884
+ @Album.exclude(:album_info=>@AlbumInfo.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (SELECT album_id FROM album_infos WHERE ((x = 1) AND (album_id IS NOT NULL)))) OR (id IS NULL))'
2885
+ end
2886
+
2887
+ it "should be able to exclude on many_to_many association datasets" do
2888
+ @Album.exclude(:tags=>@Tag.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (SELECT album_id FROM albums_tags WHERE ((tag_id IN (SELECT id FROM tags WHERE ((x = 1) AND (id IS NOT NULL)))) AND (album_id IS NOT NULL)))) OR (id IS NULL))'
2889
+ end
2890
+
2891
+ it "should be able to exclude on many_to_one association datasets with composite keys" do
2892
+ @Album.exclude(:cartist=>@Artist.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (((artist_id1, artist_id2) NOT IN (SELECT id1, id2 FROM artists WHERE ((x = 1) AND (id1 IS NOT NULL) AND (id2 IS NOT NULL)))) OR (artist_id1 IS NULL) OR (artist_id2 IS NULL))'
2893
+ end
2894
+
2895
+ it "should be able to exclude on one_to_many association datasets with composite keys" do
2896
+ @Album.exclude(:ctracks=>@Track.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN (SELECT album_id1, album_id2 FROM tracks WHERE ((x = 1) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL)))) OR (id1 IS NULL) OR (id2 IS NULL))'
2897
+ end
2898
+
2899
+ it "should be able to exclude on one_to_one association datasets with composite keys" do
2900
+ @Album.exclude(:calbum_info=>@AlbumInfo.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN (SELECT album_id1, album_id2 FROM album_infos WHERE ((x = 1) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL)))) OR (id1 IS NULL) OR (id2 IS NULL))'
2901
+ end
2700
2902
 
2701
- @c1.associate :one_to_many, :children, :class => @c1
2702
- @c1.association_reflection(:children).should be_a_kind_of(Sequel::Model::Associations::AssociationReflection)
2703
- @c1.association_reflection(:children)[:name].should == :children
2704
- @c1.association_reflection(:children)[:type].should == :one_to_many
2705
- @c1.association_reflection(:children)[:class].should == @c1
2903
+ it "should be able to exclude on many_to_many association datasets with composite keys" do
2904
+ @Album.exclude(:ctags=>@Tag.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN (SELECT album_id1, album_id2 FROM albums_tags WHERE (((tag_id1, tag_id2) IN (SELECT tid1, tid2 FROM tags WHERE ((x = 1) AND (tid1 IS NOT NULL) AND (tid2 IS NOT NULL)))) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL)))) OR (id1 IS NULL) OR (id2 IS NULL))'
2706
2905
  end
2707
2906
 
2708
- it "#associations should include all association names" do
2709
- @c1.associations.should == []
2710
- @c1.associate :many_to_one, :parent, :class => @c1
2711
- @c1.associations.should == [:parent]
2712
- @c1.associate :one_to_many, :children, :class => @c1
2713
- @c1.associations.sort_by{|x|x.to_s}.should == [:children, :parent]
2907
+ it "should do a regular IN query if the dataset for a different model is used" do
2908
+ @Album.filter(:artist=>@Album.select(:x)).sql.should == 'SELECT * FROM albums WHERE (artist IN (SELECT x FROM albums))'
2714
2909
  end
2715
2910
 
2716
- it "association reflections should be copied upon subclasing" do
2717
- @c1.associate :many_to_one, :parent, :class => @c1
2718
- c = Class.new(@c1)
2719
- @c1.associations.should == [:parent]
2720
- c.associations.should == [:parent]
2721
- c.associate :many_to_one, :parent2, :class => @c1
2722
- @c1.associations.should == [:parent]
2723
- c.associations.sort_by{|x| x.to_s}.should == [:parent, :parent2]
2724
- c.instance_methods.map{|x| x.to_s}.should include('parent')
2911
+ it "should do a regular IN query if a non-model dataset is used" do
2912
+ @Album.filter(:artist=>@Album.db.from(:albums).select(:x)).sql.should == 'SELECT * FROM albums WHERE (artist IN (SELECT x FROM albums))'
2725
2913
  end
2726
2914
  end