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
@@ -12,13 +12,48 @@ describe "Sequel::Model()" do
12
12
  c.dataset.should == ds
13
13
  end
14
14
 
15
- it "should return a model subclass with a dataset with the default database and given table name if given a symbol" do
15
+ it "should return a model subclass with a dataset with the default database and given table name if given a Symbol" do
16
16
  c = Sequel::Model(:blah)
17
17
  c.superclass.should == Sequel::Model
18
18
  c.db.should == @db
19
19
  c.table_name.should == :blah
20
20
  end
21
21
 
22
+ it "should return a model subclass with a dataset with the default database and given table name if given a LiteralString" do
23
+ c = Sequel::Model('blah'.lit)
24
+ c.superclass.should == Sequel::Model
25
+ c.db.should == @db
26
+ c.table_name.should == 'blah'.lit
27
+ end
28
+
29
+ it "should return a model subclass with a dataset with the default database and given table name if given an SQL::Identifier" do
30
+ c = Sequel::Model(:blah.identifier)
31
+ c.superclass.should == Sequel::Model
32
+ c.db.should == @db
33
+ c.table_name.should == :blah.identifier
34
+ end
35
+
36
+ it "should return a model subclass with a dataset with the default database and given table name if given an SQL::QualifiedIdentifier" do
37
+ c = Sequel::Model(:blah.qualify(:boo))
38
+ c.superclass.should == Sequel::Model
39
+ c.db.should == @db
40
+ c.table_name.should == :blah.qualify(:boo)
41
+ end
42
+
43
+ it "should return a model subclass with a dataset with the default database and given table name if given an SQL::AliasedExpression" do
44
+ c = Sequel::Model(:blah.as(:boo))
45
+ c.superclass.should == Sequel::Model
46
+ c.db.should == @db
47
+ c.table_name.should == :boo
48
+ end
49
+
50
+ it "should return a model subclass with the given dataset if given a dataset using an SQL::Identifier" do
51
+ ds = @db[:blah.identifier]
52
+ c = Sequel::Model(ds)
53
+ c.superclass.should == Sequel::Model
54
+ c.dataset.should == ds
55
+ end
56
+
22
57
  it "should return a model subclass associated to the given database if given a database" do
23
58
  db = Sequel::Database.new
24
59
  c = Sequel::Model(db)
@@ -43,6 +78,34 @@ describe "Sequel::Model()" do
43
78
  end.should_not raise_error
44
79
  end
45
80
 
81
+ it "should work without raising an exception with an SQL::Identifier " do
82
+ proc do
83
+ class ::Album < Sequel::Model(:table.identifier); end
84
+ class ::Album < Sequel::Model(:table.identifier); end
85
+ end.should_not raise_error
86
+ end
87
+
88
+ it "should work without raising an exception with an SQL::QualifiedIdentifier " do
89
+ proc do
90
+ class ::Album < Sequel::Model(:table.qualify(:schema)); end
91
+ class ::Album < Sequel::Model(:table.qualify(:schema)); end
92
+ end.should_not raise_error
93
+ end
94
+
95
+ it "should work without raising an exception with an SQL::AliasedExpression" do
96
+ proc do
97
+ class ::Album < Sequel::Model(:table.as(:alias)); end
98
+ class ::Album < Sequel::Model(:table.as(:alias)); end
99
+ end.should_not raise_error
100
+ end
101
+
102
+ it "should work without raising an exception with an LiteralString" do
103
+ proc do
104
+ class ::Album < Sequel::Model('table'.lit); end
105
+ class ::Album < Sequel::Model('table'.lit); end
106
+ end.should_not raise_error
107
+ end
108
+
46
109
  it "should work without raising an exception with a database" do
47
110
  proc do
48
111
  class ::Album < Sequel::Model(@db); end
@@ -56,6 +119,13 @@ describe "Sequel::Model()" do
56
119
  class ::Album < Sequel::Model(@db[:table]); end
57
120
  end.should_not raise_error
58
121
  end
122
+
123
+ it "should work without raising an exception with a dataset with an SQL::Identifier" do
124
+ proc do
125
+ class ::Album < Sequel::Model(@db[:table.identifier]); end
126
+ class ::Album < Sequel::Model(@db[:table.identifier]); end
127
+ end.should_not raise_error
128
+ end
59
129
  end
60
130
  end
61
131
 
@@ -70,17 +140,16 @@ describe Sequel::Model do
70
140
  it "should be associated with a dataset" do
71
141
  model_a = Class.new(Sequel::Model) { set_dataset MODEL_DB[:as] }
72
142
 
73
- model_a.dataset.should be_a_kind_of(MockDataset)
143
+ model_a.dataset.should be_a_kind_of(Sequel::Mock::Dataset)
74
144
  model_a.dataset.opts[:from].should == [:as]
75
145
 
76
146
  model_b = Class.new(Sequel::Model) { set_dataset MODEL_DB[:bs] }
77
147
 
78
- model_b.dataset.should be_a_kind_of(MockDataset)
148
+ model_b.dataset.should be_a_kind_of(Sequel::Mock::Dataset)
79
149
  model_b.dataset.opts[:from].should == [:bs]
80
150
 
81
151
  model_a.dataset.opts[:from].should == [:as]
82
152
  end
83
-
84
153
  end
85
154
 
86
155
  describe Sequel::Model, "dataset & schema" do
@@ -106,12 +175,36 @@ describe Sequel::Model, "dataset & schema" do
106
175
  @model.table_name.should == :foo
107
176
  end
108
177
 
109
- it "set_dataset should take a symbol" do
178
+ it "set_dataset should take a Symbol" do
110
179
  @model.db = MODEL_DB
111
180
  @model.set_dataset(:foo)
112
181
  @model.table_name.should == :foo
113
182
  end
114
183
 
184
+ it "set_dataset should take a LiteralString" do
185
+ @model.db = MODEL_DB
186
+ @model.set_dataset('foo'.lit)
187
+ @model.table_name.should == 'foo'.lit
188
+ end
189
+
190
+ it "set_dataset should take an SQL::Identifier" do
191
+ @model.db = MODEL_DB
192
+ @model.set_dataset(:foo.identifier)
193
+ @model.table_name.should == :foo.identifier
194
+ end
195
+
196
+ it "set_dataset should take an SQL::QualifiedIdentifier" do
197
+ @model.db = MODEL_DB
198
+ @model.set_dataset(:foo.qualify(:bar))
199
+ @model.table_name.should == :foo.qualify(:bar)
200
+ end
201
+
202
+ it "set_dataset should take an SQL::AliasedExpression" do
203
+ @model.db = MODEL_DB
204
+ @model.set_dataset(:foo.as(:bar))
205
+ @model.table_name.should == :bar
206
+ end
207
+
115
208
  it "table_name should respect table aliases" do
116
209
  @model.set_dataset(:foo___x)
117
210
  @model.table_name.should == :x
@@ -130,7 +223,7 @@ describe Sequel::Model, "dataset & schema" do
130
223
 
131
224
  it "should raise an error on set_dataset if there is an error connecting to the database" do
132
225
  @model.meta_def(:columns){raise Sequel::DatabaseConnectionError}
133
- proc{@model.set_dataset(MODEL_DB[:foo].join(:blah))}.should raise_error
226
+ proc{@model.set_dataset(Sequel::Database.new[:foo].join(:blah))}.should raise_error
134
227
  end
135
228
 
136
229
  it "should not raise an error if there is a problem getting the columns for a dataset" do
@@ -149,9 +242,8 @@ describe Sequel::Model, "dataset & schema" do
149
242
  end
150
243
  end
151
244
 
152
- describe Sequel::Model, "constructor" do
153
-
154
- before(:each) do
245
+ describe Sequel::Model, "constructors" do
246
+ before do
155
247
  @m = Class.new(Sequel::Model)
156
248
  @m.columns :a, :b
157
249
  end
@@ -170,11 +262,38 @@ describe Sequel::Model, "constructor" do
170
262
  m.values[:a].should == 1
171
263
  end
172
264
 
265
+ it "should have dataset row_proc create an existing object" do
266
+ @m.dataset = Sequel::Dataset.new(nil)
267
+ o = @m.dataset.row_proc.call(:a=>1)
268
+ o.should be_a_kind_of(@m)
269
+ o.values.should == {:a=>1}
270
+ o.new?.should be_false
271
+ end
272
+
273
+ it "should have .call create an existing object" do
274
+ o = @m.call(:a=>1)
275
+ o.should be_a_kind_of(@m)
276
+ o.values.should == {:a=>1}
277
+ o.new?.should be_false
278
+ end
279
+
280
+ it "should have .load create an existing object" do
281
+ o = @m.load(:a=>1)
282
+ o.should be_a_kind_of(@m)
283
+ o.values.should == {:a=>1}
284
+ o.new?.should be_false
285
+ end
286
+
287
+ it "should have .new with a second true argument create an existing object" do
288
+ o = @m.new({:a=>1}, true)
289
+ o.should be_a_kind_of(@m)
290
+ o.values.should == {:a=>1}
291
+ o.new?.should be_false
292
+ end
173
293
  end
174
294
 
175
295
  describe Sequel::Model, "new" do
176
-
177
- before(:each) do
296
+ before do
178
297
  @m = Class.new(Sequel::Model) do
179
298
  set_dataset MODEL_DB[:items]
180
299
  columns :x, :id
@@ -194,15 +313,8 @@ describe Sequel::Model, "new" do
194
313
  end
195
314
 
196
315
  it "should use the last inserted id as primary key if not in values" do
197
- d = @m.dataset
198
- def d.insert(*args)
199
- super
200
- 1234
201
- end
202
-
203
- def d.first
204
- {:x => 1, :id => 1234}
205
- end
316
+ @m.dataset._fetch = {:x => 1, :id => 1234}
317
+ @m.dataset.autoid = 1234
206
318
 
207
319
  o = @m.new(:x => 1)
208
320
  o.save
@@ -212,14 +324,12 @@ describe Sequel::Model, "new" do
212
324
  o.save
213
325
  o.id.should == 333
214
326
  end
215
-
216
327
  end
217
328
 
218
329
  describe Sequel::Model, ".subset" do
219
330
  before do
220
- MODEL_DB.reset
221
-
222
331
  @c = Class.new(Sequel::Model(:items))
332
+ MODEL_DB.reset
223
333
  end
224
334
 
225
335
  specify "should create a filter on the underlying dataset" do
@@ -247,44 +357,31 @@ describe Sequel::Model, ".subset" do
247
357
  end
248
358
 
249
359
  describe Sequel::Model, ".find" do
250
-
251
- before(:each) do
252
- MODEL_DB.reset
253
-
360
+ before do
254
361
  @c = Class.new(Sequel::Model(:items))
255
-
256
- $cache_dataset_row = {:name => 'sharon', :id => 1}
257
- @dataset = @c.dataset
258
- $sqls = []
259
- @dataset.extend(Module.new {
260
- def fetch_rows(sql)
261
- $sqls << sql
262
- yield $cache_dataset_row
263
- end
264
- })
362
+ @c.dataset._fetch = {:name => 'sharon', :id => 1}
363
+ MODEL_DB.reset
265
364
  end
266
365
 
267
366
  it "should return the first record matching the given filter" do
268
367
  @c.find(:name => 'sharon').should be_a_kind_of(@c)
269
- $sqls.last.should == "SELECT * FROM items WHERE (name = 'sharon') LIMIT 1"
368
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE (name = 'sharon') LIMIT 1"]
270
369
 
271
370
  @c.find(:name.like('abc%')).should be_a_kind_of(@c)
272
- $sqls.last.should == "SELECT * FROM items WHERE (name LIKE 'abc%') LIMIT 1"
371
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE (name LIKE 'abc%') LIMIT 1"]
273
372
  end
274
373
 
275
374
  specify "should accept filter blocks" do
276
375
  @c.find{:id.sql_number > 1}.should be_a_kind_of(@c)
277
- $sqls.last.should == "SELECT * FROM items WHERE (id > 1) LIMIT 1"
376
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE (id > 1) LIMIT 1"]
278
377
 
279
378
  @c.find {(:x.sql_number > 1) & (:y.sql_number < 2)}.should be_a_kind_of(@c)
280
- $sqls.last.should == "SELECT * FROM items WHERE ((x > 1) AND (y < 2)) LIMIT 1"
379
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE ((x > 1) AND (y < 2)) LIMIT 1"]
281
380
  end
282
-
283
381
  end
284
382
 
285
383
  describe Sequel::Model, ".fetch" do
286
-
287
- before(:each) do
384
+ before do
288
385
  MODEL_DB.reset
289
386
  @c = Class.new(Sequel::Model(:items))
290
387
  end
@@ -301,80 +398,53 @@ describe Sequel::Model, ".fetch" do
301
398
  end
302
399
 
303
400
  describe Sequel::Model, ".find_or_create" do
304
-
305
- before(:each) do
306
- MODEL_DB.reset
401
+ before do
307
402
  @c = Class.new(Sequel::Model(:items)) do
308
- no_primary_key
403
+ set_primary_key :id
309
404
  columns :x
310
405
  end
406
+ MODEL_DB.reset
311
407
  end
312
408
 
313
409
  it "should find the record" do
314
- @c.find_or_create(:x => 1)
410
+ @c.find_or_create(:x => 1).should == @c.load(:x=>1, :id=>1)
315
411
  MODEL_DB.sqls.should == ["SELECT * FROM items WHERE (x = 1) LIMIT 1"]
316
-
317
- MODEL_DB.reset
318
412
  end
319
413
 
320
414
  it "should create the record if not found" do
321
- @c.meta_def(:find) do |*args|
322
- dataset.filter(*args).first
323
- nil
324
- end
325
-
326
- @c.find_or_create(:x => 1)
327
- MODEL_DB.sqls.should == [
328
- "SELECT * FROM items WHERE (x = 1) LIMIT 1",
329
- "INSERT INTO items (x) VALUES (1)"
330
- ]
415
+ @c.dataset._fetch = [[], {:x=>1, :id=>1}]
416
+ @c.dataset.autoid = 1
417
+ @c.find_or_create(:x => 1).should == @c.load(:x=>1, :id=>1)
418
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE (x = 1) LIMIT 1",
419
+ "INSERT INTO items (x) VALUES (1)",
420
+ "SELECT * FROM items WHERE (id = 1) LIMIT 1"]
331
421
  end
332
422
 
333
423
  it "should pass the new record to be created to the block if no record is found" do
334
- @c.meta_def(:find){|*|}
335
- @c.find_or_create(:x => 1){|x| x[:y] = 2}
336
- ["INSERT INTO items (x, y) VALUES (1, 2)", "INSERT INTO items (y, x) VALUES (2, 1)"].should include(MODEL_DB.sqls.first)
424
+ @c.dataset._fetch = [[], {:x=>1, :id=>1}]
425
+ @c.dataset.autoid = 1
426
+ @c.find_or_create(:x => 1){|x| x[:y] = 2}.should == @c.load(:x=>1, :id=>1)
427
+ sqls = MODEL_DB.sqls
428
+ sqls.first.should == "SELECT * FROM items WHERE (x = 1) LIMIT 1"
429
+ ["INSERT INTO items (x, y) VALUES (1, 2)", "INSERT INTO items (y, x) VALUES (2, 1)"].should include(sqls[1])
430
+ sqls.last.should == "SELECT * FROM items WHERE (id = 1) LIMIT 1"
337
431
  end
338
432
  end
339
433
 
340
434
  describe Sequel::Model, ".all" do
341
-
342
- before(:each) do
343
- MODEL_DB.reset
344
- @c = Class.new(Sequel::Model(:items)) do
345
- no_primary_key
346
- end
347
-
348
- @c.dataset.meta_def(:all) {1234}
349
- end
350
-
351
435
  it "should return all records in the dataset" do
352
- @c.all.should == 1234
353
- end
354
-
355
- end
356
-
357
- class DummyModelBased < Sequel::Model(:blog)
358
- end
359
-
360
- describe Sequel::Model, "(:tablename)" do
361
-
362
- it "should allow reopening of descendant classes" do
363
- proc do
364
- eval "class DummyModelBased < Sequel::Model(:blog); end"
365
- end.should_not raise_error
436
+ c = Class.new(Sequel::Model(:items))
437
+ c.all.should == [c.load(:x=>1, :id=>1)]
366
438
  end
367
-
368
439
  end
369
440
 
370
441
  describe Sequel::Model, "A model class without a primary key" do
371
-
372
- before(:each) do
373
- MODEL_DB.reset
442
+ before do
374
443
  @c = Class.new(Sequel::Model(:items)) do
375
444
  columns :x
376
445
  no_primary_key
377
446
  end
447
+ MODEL_DB.reset
378
448
  end
379
449
 
380
450
  it "should be able to insert records without selecting them back" do
@@ -387,8 +457,11 @@ describe Sequel::Model, "A model class without a primary key" do
387
457
  end
388
458
 
389
459
  it "should raise when deleting" do
390
- o = @c.new
391
- proc {o.delete}.should raise_error
460
+ proc{@c.load(:x=>1).delete}.should raise_error
461
+ end
462
+
463
+ it "should raise when updating" do
464
+ proc{@c.load(:x=>1).update(:x=>2)}.should raise_error
392
465
  end
393
466
 
394
467
  it "should insert a record when saving" do
@@ -397,24 +470,17 @@ describe Sequel::Model, "A model class without a primary key" do
397
470
  o.save
398
471
  MODEL_DB.sqls.should == ['INSERT INTO items (x) VALUES (2)']
399
472
  end
400
-
401
473
  end
402
474
 
403
475
  describe Sequel::Model, "attribute accessors" do
404
476
  before do
405
- MODEL_DB.reset
406
- @dataset = Sequel::Dataset.new(MODEL_DB)
407
- def @dataset.columns; [:x, :y]; end
408
- @c = Class.new(Sequel::Model) do
409
- def self.db_schema
410
- set_columns(Array(@columns))
411
- @db_schema = {:x=>{:type=>:integer}, :y=>{:type=>:integer}}
412
- end
413
- def self.set_dataset(ds, opts={})
414
- @columns = ds.columns
415
- db_schema
416
- end
477
+ db = Sequel.mock
478
+ def db.schema(*)
479
+ [[:x, {:type=>:integer}], [:y, {:type=>:integer}]]
417
480
  end
481
+ @dataset = db[:items].columns(:x, :y)
482
+ @c = Class.new(Sequel::Model)
483
+ MODEL_DB.reset
418
484
  end
419
485
 
420
486
  it "should be created on set_dataset" do
@@ -446,7 +512,7 @@ describe Sequel::Model, "attribute accessors" do
446
512
  end
447
513
 
448
514
  it "should have a working typecasting setter even if the column is not selected" do
449
- @c.set_dataset(@dataset.select(:y))
515
+ @c.set_dataset(@dataset.select(:y).columns(:y))
450
516
  o = @c.new
451
517
 
452
518
  o.x = '34'
@@ -454,7 +520,7 @@ describe Sequel::Model, "attribute accessors" do
454
520
  end
455
521
 
456
522
  it "should typecast if the new value is the same as the existing but has a different class" do
457
- @c.set_dataset(@dataset.select(:y))
523
+ @c.set_dataset(@dataset.select(:y).columns(:y))
458
524
  o = @c.new
459
525
 
460
526
  o.x = 34
@@ -466,58 +532,51 @@ describe Sequel::Model, "attribute accessors" do
466
532
  end
467
533
 
468
534
  describe Sequel::Model, ".[]" do
469
-
470
- before(:each) do
471
- MODEL_DB.reset
472
-
535
+ before do
473
536
  @c = Class.new(Sequel::Model(:items))
474
-
475
- $cache_dataset_row = {:name => 'sharon', :id => 1}
476
- @dataset = @c.dataset
477
- $sqls = []
478
- @dataset.extend(Module.new {
479
- def fetch_rows(sql)
480
- $sqls << sql
481
- yield $cache_dataset_row
482
- end
483
- })
537
+ @c.dataset._fetch = {:name => 'sharon', :id => 1}
538
+ MODEL_DB.reset
484
539
  end
485
540
 
486
541
  it "should return the first record for the given pk" do
487
- @c[1].should be_a_kind_of(@c)
488
- $sqls.last.should == "SELECT * FROM items WHERE (id = 1) LIMIT 1"
489
- @c[9999].should be_a_kind_of(@c)
490
- $sqls.last.should == "SELECT * FROM items WHERE (id = 9999) LIMIT 1"
542
+ @c[1].should == @c.load(:name => 'sharon', :id => 1)
543
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE id = 1"]
544
+ @c[9999].should == @c.load(:name => 'sharon', :id => 1)
545
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE id = 9999"]
491
546
  end
492
547
 
493
548
  it "should work correctly for custom primary key" do
494
549
  @c.set_primary_key :name
495
- @c['sharon'].should be_a_kind_of(@c)
496
- $sqls.last.should == "SELECT * FROM items WHERE (name = 'sharon') LIMIT 1"
550
+ @c['sharon'].should == @c.load(:name => 'sharon', :id => 1)
551
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE name = 'sharon'"]
552
+ end
553
+
554
+ it "should return the first record for the given pk for a filtered dataset" do
555
+ @c.dataset = @c.dataset.filter(:active=>true)
556
+ @c[1].should == @c.load(:name => 'sharon', :id => 1)
557
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE ((active IS TRUE) AND (id = 1)) LIMIT 1"]
497
558
  end
498
559
 
499
560
  it "should work correctly for composite primary key specified as array" do
500
561
  @c.set_primary_key [:node_id, :kind]
501
562
  @c[3921, 201].should be_a_kind_of(@c)
502
- $sqls.last.should =~ \
503
- /^SELECT \* FROM items WHERE \((\(node_id = 3921\) AND \(kind = 201\))|(\(kind = 201\) AND \(node_id = 3921\))\) LIMIT 1$/
563
+ sqls = MODEL_DB.sqls
564
+ sqls.length.should == 1
565
+ sqls.first.should =~ /^SELECT \* FROM items WHERE \((\(node_id = 3921\) AND \(kind = 201\))|(\(kind = 201\) AND \(node_id = 3921\))\) LIMIT 1$/
504
566
  end
505
567
 
506
568
  it "should work correctly for composite primary key specified as separate arguments" do
507
569
  @c.set_primary_key :node_id, :kind
508
570
  @c[3921, 201].should be_a_kind_of(@c)
509
- $sqls.last.should =~ \
510
- /^SELECT \* FROM items WHERE \((\(node_id = 3921\) AND \(kind = 201\))|(\(kind = 201\) AND \(node_id = 3921\))\) LIMIT 1$/
571
+ sqls = MODEL_DB.sqls
572
+ sqls.length.should == 1
573
+ sqls.first.should =~ /^SELECT \* FROM items WHERE \((\(node_id = 3921\) AND \(kind = 201\))|(\(kind = 201\) AND \(node_id = 3921\))\) LIMIT 1$/
511
574
  end
512
575
  end
513
576
 
514
577
  describe "Model#inspect" do
515
- before do
516
- @o = Sequel::Model.load(:x => 333)
517
- end
518
-
519
578
  specify "should include the class name and the values" do
520
- @o.inspect.should == '#<Sequel::Model @values={:x=>333}>'
579
+ Sequel::Model.load(:x => 333).inspect.should == '#<Sequel::Model @values={:x=>333}>'
521
580
  end
522
581
  end
523
582
 
@@ -526,16 +585,13 @@ describe "Model.db_schema" do
526
585
  @c = Class.new(Sequel::Model(:items)) do
527
586
  def self.columns; orig_columns; end
528
587
  end
529
- @dataset = Sequel::Dataset.new(nil).from(:items)
530
- @dataset.meta_def(:db){@db ||= Sequel::Database.new}
531
- def @dataset.naked; self; end
532
- def @dataset.columns; []; end
533
- def @dataset.def_mutation_method(*names); end
588
+ @db = Sequel.mock
589
+ @dataset = @db[:items]
534
590
  end
535
591
 
536
592
  specify "should use the database's schema_for_table and set the columns and dataset columns" do
537
593
  d = @dataset.db
538
- def d.schema(table, opts = {})
594
+ def @db.schema(table, opts = {})
539
595
  [[:x, {:type=>:integer}], [:y, {:type=>:string}]]
540
596
  end
541
597
  @c.dataset = @dataset
@@ -545,36 +601,20 @@ describe "Model.db_schema" do
545
601
  end
546
602
 
547
603
  specify "should not restrict the schema for datasets with a :select option" do
548
- ds = @dataset.select(:x, :y___z)
549
- d = ds.db
550
- def d.schema(table, opts = {})
604
+ def @c.columns; [:x, :z]; end
605
+ def @db.schema(table, opts = {})
551
606
  [[:x, {:type=>:integer}], [:y, {:type=>:string}]]
552
607
  end
553
- def @c.columns; [:x, :z]; end
554
- @c.dataset = ds
608
+ @c.dataset = @dataset.select(:x, :y___z)
555
609
  @c.db_schema.should == {:x=>{:type=>:integer}, :z=>{}, :y=>{:type=>:string}}
556
610
  end
557
611
 
558
- specify "should not use schema if the dataset uses multiple tables or custom sql" do
559
- ds = @dataset.join(:x, :id)
560
- d = ds.db
561
- e = false
562
- d.meta_def(:schema){|table, *opts| e = true}
563
- def @c.columns; [:x]; end
564
- @c.dataset = ds
565
- @c.db_schema.should == {:x=>{}}
566
- e.should == false
567
- end
568
-
569
612
  specify "should fallback to fetching records if schema raises an error" do
570
- ds = @dataset.join(:x, :id)
571
- d = ds.db
572
- def d.schema(table, opts={})
573
- raise StandardError
613
+ def @db.schema(table, opts={})
614
+ raise Sequel::Error
574
615
  end
575
- def @c.columns; [:x]; end
576
- @c.dataset = ds
577
- @c.db_schema.should == {:x=>{}}
616
+ @c.dataset = @dataset.join(:x, :id).columns(:id, :x)
617
+ @c.db_schema.should == {:x=>{}, :id=>{}}
578
618
  end
579
619
 
580
620
  specify "should automatically set a singular primary key based on the schema" do
@@ -125,24 +125,21 @@ describe Sequel::Model, ".plugin" do
125
125
  def self.configure(model, *args, &block)
126
126
  @args << :configure
127
127
  end
128
- im = Module.new do
128
+ self::InstanceMethods = Module.new do
129
129
  def self.included(model)
130
130
  model.plugins.last.args << :im
131
131
  end
132
132
  end
133
- cm = Module.new do
133
+ self::ClassMethods = Module.new do
134
134
  def self.extended(model)
135
135
  model.plugins.last.args << :cm
136
136
  end
137
137
  end
138
- dm = Module.new do
138
+ self::DatasetMethods = Module.new do
139
139
  def self.extended(dataset)
140
140
  dataset.model.plugins.last.args << :dm
141
141
  end
142
142
  end
143
- const_set(:InstanceMethods, im)
144
- const_set(:ClassMethods, cm)
145
- const_set(:DatasetMethods, dm)
146
143
  end
147
144
 
148
145
  b = lambda{44}
@@ -192,11 +189,10 @@ describe Sequel::Model, ".plugin" do
192
189
 
193
190
  it "should define class methods for all public instance methods in DatasetMethod" do
194
191
  m = Module.new do
195
- dm = Module.new do
192
+ self::DatasetMethods = Module.new do
196
193
  def a; 1; end
197
194
  def b; 2; end
198
195
  end
199
- const_set(:DatasetMethods, dm)
200
196
  end
201
197
  @c.plugin m
202
198
  @c.dataset.a.should == 1
@@ -205,14 +201,13 @@ describe Sequel::Model, ".plugin" do
205
201
  @c.b.should == 2
206
202
  end
207
203
 
208
- it "should define class methods for all public instance methods in DatasetMethod" do
204
+ it "should not define class methods for private instance methods in DatasetMethod" do
209
205
  m = Module.new do
210
- dm = Module.new do
206
+ self::DatasetMethods = Module.new do
211
207
  def b; 2; end
212
208
  private
213
209
  def a; 1; end
214
210
  end
215
- const_set(:DatasetMethods, dm)
216
211
  end
217
212
  @c.plugin m
218
213
  @c.dataset.b.should == 2
@@ -225,12 +220,28 @@ describe Sequel::Model, ".plugin" do
225
220
 
226
221
  it "should not raise an error if the DatasetMethod module has no public instance methods" do
227
222
  m = Module.new do
228
- dm = Module.new do
223
+ self::DatasetMethods = Module.new do
229
224
  private
230
225
  def a; 1; end
231
226
  end
232
- const_set(:DatasetMethods, dm)
233
227
  end
234
228
  lambda{@c.plugin m}.should_not raise_error
235
229
  end
230
+
231
+ it "should not raise an error if plugin submodule names exist higher up in the namespace hierarchy" do
232
+ class ::ClassMethods; end
233
+ @c.plugin(m = Module.new)
234
+ Object.send(:remove_const, :ClassMethods)
235
+ @c.plugins.should include(m)
236
+
237
+ class ::InstanceMethods; end
238
+ @c.plugin(m = Module.new)
239
+ Object.send(:remove_const, :InstanceMethods)
240
+ @c.plugins.should include(m)
241
+
242
+ class ::DatasetMethods; end
243
+ @c.plugin(m = Module.new)
244
+ Object.send(:remove_const, :DatasetMethods)
245
+ @c.plugins.should include(m)
246
+ end
236
247
  end