sequel 3.21.0 → 3.28.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 (158) hide show
  1. data/CHANGELOG +294 -0
  2. data/README.rdoc +20 -6
  3. data/Rakefile +20 -15
  4. data/doc/association_basics.rdoc +210 -43
  5. data/doc/dataset_basics.rdoc +4 -4
  6. data/doc/mass_assignment.rdoc +54 -0
  7. data/doc/migration.rdoc +15 -538
  8. data/doc/model_hooks.rdoc +64 -27
  9. data/doc/opening_databases.rdoc +37 -10
  10. data/doc/prepared_statements.rdoc +16 -10
  11. data/doc/reflection.rdoc +8 -2
  12. data/doc/release_notes/3.22.0.txt +39 -0
  13. data/doc/release_notes/3.23.0.txt +172 -0
  14. data/doc/release_notes/3.24.0.txt +420 -0
  15. data/doc/release_notes/3.25.0.txt +88 -0
  16. data/doc/release_notes/3.26.0.txt +88 -0
  17. data/doc/release_notes/3.27.0.txt +82 -0
  18. data/doc/release_notes/3.28.0.txt +304 -0
  19. data/doc/schema_modification.rdoc +547 -0
  20. data/doc/testing.rdoc +106 -0
  21. data/doc/transactions.rdoc +97 -0
  22. data/doc/virtual_rows.rdoc +2 -2
  23. data/lib/sequel/adapters/ado.rb +12 -1
  24. data/lib/sequel/adapters/amalgalite.rb +4 -0
  25. data/lib/sequel/adapters/db2.rb +95 -58
  26. data/lib/sequel/adapters/do.rb +12 -0
  27. data/lib/sequel/adapters/firebird.rb +25 -203
  28. data/lib/sequel/adapters/ibmdb.rb +440 -0
  29. data/lib/sequel/adapters/informix.rb +4 -19
  30. data/lib/sequel/adapters/jdbc/as400.rb +0 -7
  31. data/lib/sequel/adapters/jdbc/db2.rb +49 -0
  32. data/lib/sequel/adapters/jdbc/firebird.rb +34 -0
  33. data/lib/sequel/adapters/jdbc/h2.rb +16 -5
  34. data/lib/sequel/adapters/jdbc/informix.rb +31 -0
  35. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  36. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  37. data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
  38. data/lib/sequel/adapters/jdbc/oracle.rb +2 -27
  39. data/lib/sequel/adapters/jdbc/postgresql.rb +6 -0
  40. data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
  41. data/lib/sequel/adapters/jdbc/transactions.rb +34 -0
  42. data/lib/sequel/adapters/jdbc.rb +62 -29
  43. data/lib/sequel/adapters/mysql.rb +22 -139
  44. data/lib/sequel/adapters/mysql2.rb +9 -14
  45. data/lib/sequel/adapters/odbc/db2.rb +21 -0
  46. data/lib/sequel/adapters/odbc.rb +15 -3
  47. data/lib/sequel/adapters/oracle.rb +17 -1
  48. data/lib/sequel/adapters/postgres.rb +111 -16
  49. data/lib/sequel/adapters/shared/access.rb +21 -0
  50. data/lib/sequel/adapters/shared/db2.rb +290 -0
  51. data/lib/sequel/adapters/shared/firebird.rb +214 -0
  52. data/lib/sequel/adapters/shared/informix.rb +45 -0
  53. data/lib/sequel/adapters/shared/mssql.rb +85 -47
  54. data/lib/sequel/adapters/shared/mysql.rb +50 -7
  55. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +143 -0
  56. data/lib/sequel/adapters/shared/oracle.rb +0 -4
  57. data/lib/sequel/adapters/shared/postgres.rb +75 -43
  58. data/lib/sequel/adapters/shared/sqlite.rb +56 -8
  59. data/lib/sequel/adapters/sqlite.rb +12 -11
  60. data/lib/sequel/adapters/swift/mysql.rb +9 -0
  61. data/lib/sequel/adapters/tinytds.rb +139 -7
  62. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +55 -0
  63. data/lib/sequel/ast_transformer.rb +190 -0
  64. data/lib/sequel/connection_pool/threaded.rb +3 -2
  65. data/lib/sequel/connection_pool.rb +1 -1
  66. data/lib/sequel/core.rb +6 -5
  67. data/lib/sequel/database/connecting.rb +5 -5
  68. data/lib/sequel/database/dataset.rb +1 -1
  69. data/lib/sequel/database/dataset_defaults.rb +1 -1
  70. data/lib/sequel/database/logging.rb +1 -1
  71. data/lib/sequel/database/misc.rb +38 -17
  72. data/lib/sequel/database/query.rb +50 -19
  73. data/lib/sequel/database/schema_generator.rb +8 -5
  74. data/lib/sequel/database/schema_methods.rb +52 -27
  75. data/lib/sequel/dataset/actions.rb +167 -48
  76. data/lib/sequel/dataset/features.rb +57 -8
  77. data/lib/sequel/dataset/graph.rb +1 -1
  78. data/lib/sequel/dataset/misc.rb +39 -20
  79. data/lib/sequel/dataset/mutation.rb +3 -3
  80. data/lib/sequel/dataset/prepared_statements.rb +29 -14
  81. data/lib/sequel/dataset/query.rb +182 -32
  82. data/lib/sequel/dataset/sql.rb +31 -58
  83. data/lib/sequel/dataset.rb +8 -0
  84. data/lib/sequel/exceptions.rb +4 -0
  85. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  86. data/lib/sequel/extensions/migration.rb +6 -4
  87. data/lib/sequel/extensions/to_dot.rb +95 -83
  88. data/lib/sequel/model/associations.rb +893 -309
  89. data/lib/sequel/model/base.rb +302 -105
  90. data/lib/sequel/model/errors.rb +1 -1
  91. data/lib/sequel/model/exceptions.rb +5 -1
  92. data/lib/sequel/model.rb +13 -7
  93. data/lib/sequel/plugins/association_pks.rb +22 -4
  94. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  95. data/lib/sequel/plugins/identity_map.rb +113 -6
  96. data/lib/sequel/plugins/many_through_many.rb +67 -5
  97. data/lib/sequel/plugins/prepared_statements.rb +140 -0
  98. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  99. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  100. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  101. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  102. data/lib/sequel/plugins/sharding.rb +12 -20
  103. data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
  104. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  105. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  106. data/lib/sequel/sql.rb +107 -51
  107. data/lib/sequel/timezones.rb +12 -3
  108. data/lib/sequel/version.rb +1 -1
  109. data/spec/adapters/db2_spec.rb +146 -0
  110. data/spec/adapters/mssql_spec.rb +36 -0
  111. data/spec/adapters/mysql_spec.rb +36 -19
  112. data/spec/adapters/postgres_spec.rb +115 -28
  113. data/spec/adapters/spec_helper.rb +6 -0
  114. data/spec/adapters/sqlite_spec.rb +11 -0
  115. data/spec/core/connection_pool_spec.rb +62 -77
  116. data/spec/core/database_spec.rb +244 -287
  117. data/spec/core/dataset_spec.rb +383 -34
  118. data/spec/core/expression_filters_spec.rb +159 -41
  119. data/spec/core/schema_spec.rb +326 -3
  120. data/spec/core/spec_helper.rb +45 -0
  121. data/spec/extensions/association_pks_spec.rb +38 -0
  122. data/spec/extensions/columns_introspection_spec.rb +91 -0
  123. data/spec/extensions/defaults_setter_spec.rb +64 -0
  124. data/spec/extensions/identity_map_spec.rb +162 -0
  125. data/spec/extensions/many_through_many_spec.rb +195 -20
  126. data/spec/extensions/migration_spec.rb +17 -17
  127. data/spec/extensions/nested_attributes_spec.rb +1 -0
  128. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  129. data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
  130. data/spec/extensions/prepared_statements_spec.rb +72 -0
  131. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  132. data/spec/extensions/schema_dumper_spec.rb +2 -2
  133. data/spec/extensions/schema_spec.rb +12 -20
  134. data/spec/extensions/serialization_modification_detection_spec.rb +36 -0
  135. data/spec/extensions/single_table_inheritance_spec.rb +11 -0
  136. data/spec/extensions/spec_helper.rb +3 -1
  137. data/spec/extensions/to_dot_spec.rb +3 -5
  138. data/spec/extensions/xml_serializer_spec.rb +16 -4
  139. data/spec/integration/associations_test.rb +405 -15
  140. data/spec/integration/database_test.rb +4 -2
  141. data/spec/integration/dataset_test.rb +240 -20
  142. data/spec/integration/plugin_test.rb +142 -5
  143. data/spec/integration/prepared_statement_test.rb +174 -95
  144. data/spec/integration/schema_test.rb +128 -16
  145. data/spec/integration/spec_helper.rb +15 -0
  146. data/spec/integration/transaction_test.rb +40 -0
  147. data/spec/integration/type_test.rb +16 -2
  148. data/spec/model/association_reflection_spec.rb +91 -0
  149. data/spec/model/associations_spec.rb +476 -5
  150. data/spec/model/base_spec.rb +91 -1
  151. data/spec/model/eager_loading_spec.rb +519 -31
  152. data/spec/model/hooks_spec.rb +161 -0
  153. data/spec/model/model_spec.rb +89 -2
  154. data/spec/model/plugins_spec.rb +17 -0
  155. data/spec/model/record_spec.rb +184 -12
  156. data/spec/model/spec_helper.rb +5 -0
  157. data/spec/model/validations_spec.rb +11 -0
  158. metadata +85 -34
@@ -268,4 +268,165 @@ describe "Model#before_validation && Model#after_validation" do
268
268
  @c.load(:id => 2233).save.should == nil
269
269
  MODEL_DB.sqls.should == []
270
270
  end
271
+
272
+ specify "#valid? should return false if before_validation returns false" do
273
+ @c.send(:define_method, :before_validation){false}
274
+ @c.load(:id => 2233).valid?.should == false
275
+ end
276
+ end
277
+
278
+ describe "Model around filters" do
279
+ before do
280
+ MODEL_DB.reset
281
+
282
+ @c = Class.new(Sequel::Model(:items))
283
+ @c.class_eval do
284
+ columns :id, :x
285
+ def _save_refresh(*a) end
286
+ end
287
+ end
288
+
289
+ specify "around_create should be called around new record creation" do
290
+ @c.class_eval do
291
+ def around_create
292
+ MODEL_DB << 'ac_before'
293
+ super
294
+ MODEL_DB << 'ac_after'
295
+ end
296
+ end
297
+ @c.create(:x => 2)
298
+ MODEL_DB.sqls.should == [ 'ac_before', 'INSERT INTO items (x) VALUES (2)', 'ac_after' ]
299
+ end
300
+
301
+ specify "around_delete should be called around record destruction" do
302
+ @c.class_eval do
303
+ def around_destroy
304
+ MODEL_DB << 'ad_before'
305
+ super
306
+ MODEL_DB << 'ad_after'
307
+ end
308
+ end
309
+ @c.load(:id=>1, :x => 2).destroy
310
+ MODEL_DB.sqls.should == [ 'ad_before', 'DELETE FROM items WHERE (id = 1)', 'ad_after' ]
311
+ end
312
+
313
+ specify "around_update should be called around updating existing records" do
314
+ @c.class_eval do
315
+ def around_update
316
+ MODEL_DB << 'au_before'
317
+ super
318
+ MODEL_DB << 'au_after'
319
+ end
320
+ end
321
+ @c.load(:id=>1, :x => 2).save
322
+ MODEL_DB.sqls.should == [ 'au_before', 'UPDATE items SET x = 2 WHERE (id = 1)', 'au_after' ]
323
+ end
324
+
325
+ specify "around_update should be called around saving both new and existing records, around either after_create and after_update" do
326
+ @c.class_eval do
327
+ def around_update
328
+ MODEL_DB << 'au_before'
329
+ super
330
+ MODEL_DB << 'au_after'
331
+ end
332
+ def around_create
333
+ MODEL_DB << 'ac_before'
334
+ super
335
+ MODEL_DB << 'ac_after'
336
+ end
337
+ def around_save
338
+ MODEL_DB << 'as_before'
339
+ super
340
+ MODEL_DB << 'as_after'
341
+ end
342
+ end
343
+ @c.create(:x => 2)
344
+ MODEL_DB.sqls.should == [ 'as_before', 'ac_before', 'INSERT INTO items (x) VALUES (2)', 'ac_after', 'as_after' ]
345
+ MODEL_DB.sqls.clear
346
+ @c.load(:id=>1, :x => 2).save
347
+ MODEL_DB.sqls.should == [ 'as_before', 'au_before', 'UPDATE items SET x = 2 WHERE (id = 1)', 'au_after', 'as_after' ]
348
+ end
349
+
350
+ specify "around_validation should be called around validating records" do
351
+ @c.class_eval do
352
+ def around_validation
353
+ MODEL_DB << 'av_before'
354
+ super
355
+ MODEL_DB << 'av_after'
356
+ end
357
+ def validate
358
+ MODEL_DB << 'validate'
359
+ end
360
+ end
361
+ @c.new(:x => 2).valid?.should == true
362
+ MODEL_DB.sqls.should == [ 'av_before', 'validate', 'av_after' ]
363
+ end
364
+
365
+ specify "around_validation should be able to catch validation errors and modify them" do
366
+ @c.class_eval do
367
+ def validate
368
+ errors.add(:x, 'foo')
369
+ end
370
+ end
371
+ @c.new(:x => 2).valid?.should == false
372
+ @c.class_eval do
373
+ def around_validation
374
+ super
375
+ errors.clear
376
+ end
377
+ end
378
+ @c.new(:x => 2).valid?.should == true
379
+ end
380
+
381
+ specify "around_create that doesn't call super should raise a HookFailed" do
382
+ @c.send(:define_method, :around_create){}
383
+ proc{@c.create(:x => 2)}.should raise_error(Sequel::HookFailed)
384
+ end
385
+
386
+ specify "around_update that doesn't call super should raise a HookFailed" do
387
+ @c.send(:define_method, :around_update){}
388
+ proc{@c.load(:x => 2).save}.should raise_error(Sequel::HookFailed)
389
+ end
390
+
391
+ specify "around_save that doesn't call super should raise a HookFailed" do
392
+ @c.send(:define_method, :around_save){}
393
+ proc{@c.create(:x => 2)}.should raise_error(Sequel::HookFailed)
394
+ proc{@c.load(:x => 2).save}.should raise_error(Sequel::HookFailed)
395
+ end
396
+
397
+ specify "around_destroy that doesn't call super should raise a HookFailed" do
398
+ @c.send(:define_method, :around_destroy){}
399
+ proc{@c.load(:x => 2).destroy}.should raise_error(Sequel::HookFailed)
400
+ end
401
+
402
+ specify "around_validation that doesn't call super should raise a HookFailed" do
403
+ @c.send(:define_method, :around_validation){}
404
+ proc{@c.new.save}.should raise_error(Sequel::HookFailed)
405
+ end
406
+
407
+ specify "around_validation that doesn't call super should have valid? return false" do
408
+ @c.send(:define_method, :around_validation){}
409
+ @c.new.valid?.should == false
410
+ end
411
+
412
+ specify "around_* that doesn't call super should return nil if raise_on_save_failure is false" do
413
+ @c.raise_on_save_failure = false
414
+
415
+ o = @c.load(:id => 1)
416
+ o.meta_def(:around_save){}
417
+ o.save.should == nil
418
+
419
+ o = @c.load(:id => 1)
420
+ o.meta_def(:around_update){}
421
+ o.save.should == nil
422
+
423
+ o = @c.new
424
+ o.meta_def(:around_create){}
425
+ o.save.should == nil
426
+
427
+ o = @c.new
428
+ o.meta_def(:around_validation){}
429
+ o.save.should == nil
430
+ end
431
+
271
432
  end
@@ -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,27 @@ 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
+
46
102
  it "should work without raising an exception with a database" do
47
103
  proc do
48
104
  class ::Album < Sequel::Model(@db); end
@@ -56,6 +112,13 @@ describe "Sequel::Model()" do
56
112
  class ::Album < Sequel::Model(@db[:table]); end
57
113
  end.should_not raise_error
58
114
  end
115
+
116
+ it "should work without raising an exception with a dataset with an SQL::Identifier" do
117
+ proc do
118
+ class ::Album < Sequel::Model(@db[:table.identifier]); end
119
+ class ::Album < Sequel::Model(@db[:table.identifier]); end
120
+ end.should_not raise_error
121
+ end
59
122
  end
60
123
  end
61
124
 
@@ -106,12 +169,36 @@ describe Sequel::Model, "dataset & schema" do
106
169
  @model.table_name.should == :foo
107
170
  end
108
171
 
109
- it "set_dataset should take a symbol" do
172
+ it "set_dataset should take a Symbol" do
110
173
  @model.db = MODEL_DB
111
174
  @model.set_dataset(:foo)
112
175
  @model.table_name.should == :foo
113
176
  end
114
177
 
178
+ it "set_dataset should take a LiteralString" do
179
+ @model.db = MODEL_DB
180
+ @model.set_dataset('foo'.lit)
181
+ @model.table_name.should == 'foo'.lit
182
+ end
183
+
184
+ it "set_dataset should take an SQL::Identifier" do
185
+ @model.db = MODEL_DB
186
+ @model.set_dataset(:foo.identifier)
187
+ @model.table_name.should == :foo.identifier
188
+ end
189
+
190
+ it "set_dataset should take an SQL::QualifiedIdentifier" do
191
+ @model.db = MODEL_DB
192
+ @model.set_dataset(:foo.qualify(:bar))
193
+ @model.table_name.should == :foo.qualify(:bar)
194
+ end
195
+
196
+ it "set_dataset should take an SQL::AliasedExpression" do
197
+ @model.db = MODEL_DB
198
+ @model.set_dataset(:foo.as(:bar))
199
+ @model.table_name.should == :bar
200
+ end
201
+
115
202
  it "table_name should respect table aliases" do
116
203
  @model.set_dataset(:foo___x)
117
204
  @model.table_name.should == :x
@@ -233,4 +233,21 @@ describe Sequel::Model, ".plugin" do
233
233
  end
234
234
  lambda{@c.plugin m}.should_not raise_error
235
235
  end
236
+
237
+ it "should not raise an error if plugin submodule names exist higher up in the namespace hierarchy" do
238
+ class ::ClassMethods; end
239
+ @c.plugin(m = Module.new)
240
+ Object.send(:remove_const, :ClassMethods)
241
+ @c.plugins.should include(m)
242
+
243
+ class ::InstanceMethods; end
244
+ @c.plugin(m = Module.new)
245
+ Object.send(:remove_const, :InstanceMethods)
246
+ @c.plugins.should include(m)
247
+
248
+ class ::DatasetMethods; end
249
+ @c.plugin(m = Module.new)
250
+ Object.send(:remove_const, :DatasetMethods)
251
+ @c.plugins.should include(m)
252
+ end
236
253
  end
@@ -52,6 +52,7 @@ describe "Model#save" do
52
52
 
53
53
  it "should use dataset's insert_select method if present" do
54
54
  ds = @c.dataset = @c.dataset.clone
55
+ def ds.supports_insert_select?() true end
55
56
  def ds.insert_select(hash)
56
57
  execute("INSERT INTO items (y) VALUES (2)")
57
58
  {:y=>2}
@@ -63,6 +64,12 @@ describe "Model#save" do
63
64
  MODEL_DB.sqls.should == ["INSERT INTO items (y) VALUES (2)"]
64
65
  end
65
66
 
67
+ it "should not use dataset's insert_select method if specific columns are selected" do
68
+ ds = @c.dataset = @c.dataset.select(:y)
69
+ ds.should_not_receive(:insert_select)
70
+ @c.new(:x => 1).save
71
+ end
72
+
66
73
  it "should use value returned by insert as the primary key and refresh the object" do
67
74
  @c.dataset.meta_def(:insert){|h| super(h); 13}
68
75
  o = @c.new(:x => 11)
@@ -623,6 +630,40 @@ describe "Model#pk" do
623
630
  end
624
631
  end
625
632
 
633
+ describe "Model#pk_or_nil" do
634
+ before(:each) do
635
+ @m = Class.new(Sequel::Model)
636
+ @m.columns :id, :x, :y
637
+ end
638
+
639
+ it "should be default return the value of the :id column" do
640
+ m = @m.load(:id => 111, :x => 2, :y => 3)
641
+ m.pk_or_nil.should == 111
642
+ end
643
+
644
+ it "should be return the primary key value for custom primary key" do
645
+ @m.set_primary_key :x
646
+ m = @m.load(:id => 111, :x => 2, :y => 3)
647
+ m.pk_or_nil.should == 2
648
+ end
649
+
650
+ it "should be return the primary key value for composite primary key" do
651
+ @m.set_primary_key [:y, :x]
652
+ m = @m.load(:id => 111, :x => 2, :y => 3)
653
+ m.pk_or_nil.should == [3, 2]
654
+ end
655
+
656
+ it "should raise if no primary key" do
657
+ @m.set_primary_key nil
658
+ m = @m.new(:id => 111, :x => 2, :y => 3)
659
+ m.pk_or_nil.should be_nil
660
+
661
+ @m.no_primary_key
662
+ m = @m.new(:id => 111, :x => 2, :y => 3)
663
+ m.pk_or_nil.should be_nil
664
+ end
665
+ end
666
+
626
667
  describe "Model#pk_hash" do
627
668
  before(:each) do
628
669
  @m = Class.new(Sequel::Model)
@@ -716,6 +757,29 @@ describe Sequel::Model, "#set" do
716
757
  MODEL_DB.sqls.should == []
717
758
  end
718
759
 
760
+ it "should raise error if strict_param_setting is true and method does not exist" do
761
+ @o1.strict_param_setting = true
762
+ proc{@o1.set('foo' => 1)}.should raise_error(Sequel::Error)
763
+ end
764
+
765
+ it "should raise error if strict_param_setting is true and column is a primary key" do
766
+ @o1.strict_param_setting = true
767
+ proc{@o1.set('id' => 1)}.should raise_error(Sequel::Error)
768
+ end
769
+
770
+ it "should raise error if strict_param_setting is true and column is restricted" do
771
+ @o1.strict_param_setting = true
772
+ @c.set_restricted_columns :x
773
+ proc{@o1.set('x' => 1)}.should raise_error(Sequel::Error)
774
+ end
775
+
776
+ it "should not create a symbol if strict_param_setting is true and string is given" do
777
+ @o1.strict_param_setting = true
778
+ l = Symbol.all_symbols.length
779
+ proc{@o1.set('sadojafdso' => 1)}.should raise_error(Sequel::Error)
780
+ Symbol.all_symbols.length.should == l
781
+ end
782
+
719
783
  it "#set should correctly handle cases where an instance method is added to the class" do
720
784
  @o1.set(:x => 1)
721
785
  @o1.values.should == {:x => 1}
@@ -739,6 +803,32 @@ describe Sequel::Model, "#set" do
739
803
  @o1.set(:x => 2, :z => 3)
740
804
  @o1.values.should == {:x => 2, :z=>3}
741
805
  end
806
+
807
+ it "#set should correctly handle cases where a module with a setter method is included in the class" do
808
+ @o1.set(:x => 1)
809
+ @o1.values.should == {:x => 1}
810
+
811
+ @c.send(:include, Module.new do
812
+ def z=(v)
813
+ self[:z] = v
814
+ end
815
+ end)
816
+ @o1.set(:x => 2, :z => 3)
817
+ @o1.values.should == {:x => 2, :z=>3}
818
+ end
819
+
820
+ it "#set should correctly handle cases where the object extends a module with a setter method " do
821
+ @o1.set(:x => 1)
822
+ @o1.values.should == {:x => 1}
823
+
824
+ @o1.extend(Module.new do
825
+ def z=(v)
826
+ self[:z] = v
827
+ end
828
+ end)
829
+ @o1.set(:x => 2, :z => 3)
830
+ @o1.values.should == {:x => 2, :z=>3}
831
+ end
742
832
  end
743
833
 
744
834
  describe Sequel::Model, "#update" do
@@ -997,6 +1087,11 @@ describe Sequel::Model, "#exists?" do
997
1087
  @model.load(:id=>2).exists?.should be_false
998
1088
  MODEL_DB.sqls.should == ['SELECT 1 FROM items WHERE (id = 2) LIMIT 1']
999
1089
  end
1090
+
1091
+ it "should return false without issuing a query if the model object is new" do
1092
+ @model.new.exists?.should be_false
1093
+ MODEL_DB.sqls.should == []
1094
+ end
1000
1095
  end
1001
1096
 
1002
1097
  describe Sequel::Model, "#each" do
@@ -1094,13 +1189,10 @@ describe Sequel::Model, "#hash" do
1094
1189
  y = Class.new(Sequel::Model)
1095
1190
  y.columns :id, :x
1096
1191
  a = z.load(:id => 1, :x => 3)
1097
- b = z.load(:id => 1, :x => 4)
1098
- c = z.load(:id => 2, :x => 3)
1099
- d = y.load(:id => 1, :x => 3)
1100
1192
 
1101
- a.hash.should == b.hash
1102
- a.hash.should_not == c.hash
1103
- a.hash.should_not == d.hash
1193
+ a.hash.should == z.load(:id => 1, :x => 4).hash
1194
+ a.hash.should_not == z.load(:id => 2, :x => 3).hash
1195
+ a.hash.should_not == y.load(:id => 1, :x => 3).hash
1104
1196
  end
1105
1197
 
1106
1198
  specify "should be the same only for objects with the same class and values if the pk is nil" do
@@ -1109,14 +1201,71 @@ describe Sequel::Model, "#hash" do
1109
1201
  y = Class.new(Sequel::Model)
1110
1202
  y.columns :id, :x
1111
1203
  a = z.new(:x => 3)
1112
- b = z.new(:x => 4)
1113
- c = z.new(:x => 3)
1114
- d = y.new(:x => 3)
1115
1204
 
1116
- a.hash.should_not == b.hash
1117
- a.hash.should == c.hash
1118
- a.hash.should_not == d.hash
1205
+ a.hash.should_not == z.new(:x => 4).hash
1206
+ a.hash.should == z.new(:x => 3).hash
1207
+ a.hash.should_not == y.new(:x => 3).hash
1208
+ end
1209
+
1210
+ specify "should be the same only for objects with the same class and pk if pk is composite and all values are non-NULL" do
1211
+ z = Class.new(Sequel::Model)
1212
+ z.columns :id, :id2, :x
1213
+ z.set_primary_key(:id, :id2)
1214
+ y = Class.new(Sequel::Model)
1215
+ y.columns :id, :id2, :x
1216
+ y.set_primary_key(:id, :id2)
1217
+ a = z.load(:id => 1, :id2=>2, :x => 3)
1218
+
1219
+ a.hash.should == z.load(:id => 1, :id2=>2, :x => 4).hash
1220
+ a.hash.should_not == z.load(:id => 2, :id2=>1, :x => 3).hash
1221
+ a.hash.should_not == y.load(:id => 1, :id2=>1, :x => 3).hash
1222
+ end
1223
+
1224
+ specify "should be the same only for objects with the same class and value if pk is composite and one values is NULL" do
1225
+ z = Class.new(Sequel::Model)
1226
+ z.columns :id, :id2, :x
1227
+ z.set_primary_key(:id, :id2)
1228
+ y = Class.new(Sequel::Model)
1229
+ y.columns :id, :id2, :x
1230
+ y.set_primary_key(:id, :id2)
1231
+
1232
+ a = z.load(:id => 1, :id2 => nil, :x => 3)
1233
+ a.hash.should == z.load(:id => 1, :id2=>nil, :x => 3).hash
1234
+ a.hash.should_not == z.load(:id => 1, :id2=>nil, :x => 4).hash
1235
+ a.hash.should_not == y.load(:id => 1, :id2=>nil, :x => 3).hash
1236
+
1237
+ a = z.load(:id =>nil, :id2 => nil, :x => 3)
1238
+ a.hash.should == z.load(:id => nil, :id2=>nil, :x => 3).hash
1239
+ a.hash.should_not == z.load(:id => nil, :id2=>nil, :x => 4).hash
1240
+ a.hash.should_not == y.load(:id => nil, :id2=>nil, :x => 3).hash
1241
+
1242
+ a = z.load(:id => 1, :x => 3)
1243
+ a.hash.should == z.load(:id => 1, :x => 3).hash
1244
+ a.hash.should_not == z.load(:id => 1, :id2=>nil, :x => 3).hash
1245
+ a.hash.should_not == z.load(:id => 1, :x => 4).hash
1246
+ a.hash.should_not == y.load(:id => 1, :x => 3).hash
1247
+
1248
+ a = z.load(:x => 3)
1249
+ a.hash.should == z.load(:x => 3).hash
1250
+ a.hash.should_not == z.load(:id => nil, :id2=>nil, :x => 3).hash
1251
+ a.hash.should_not == z.load(:x => 4).hash
1252
+ a.hash.should_not == y.load(:x => 3).hash
1253
+ end
1254
+
1255
+ specify "should be the same only for objects with the same class and values if the no primary key" do
1256
+ z = Class.new(Sequel::Model)
1257
+ z.columns :id, :x
1258
+ z.no_primary_key
1259
+ y = Class.new(Sequel::Model)
1260
+ y.columns :id, :x
1261
+ y.no_primary_key
1262
+ a = z.new(:x => 3)
1263
+
1264
+ a.hash.should_not == z.new(:x => 4).hash
1265
+ a.hash.should == z.new(:x => 3).hash
1266
+ a.hash.should_not == y.new(:x => 3).hash
1119
1267
  end
1268
+
1120
1269
  end
1121
1270
 
1122
1271
  describe Sequel::Model, "#initialize" do
@@ -1161,6 +1310,29 @@ describe Sequel::Model, "#initialize" do
1161
1310
  m.values.should == {:x => 2}
1162
1311
  end
1163
1312
  end
1313
+
1314
+ describe Sequel::Model, "#initialize_set" do
1315
+ before do
1316
+ @c = Class.new(Sequel::Model){columns :id, :x, :y}
1317
+ end
1318
+
1319
+ specify "should be called by initialize to set the column values" do
1320
+ @c.send(:define_method, :initialize_set){|h| set(:y => 3)}
1321
+ @c.new(:x => 2).values.should == {:y => 3}
1322
+ end
1323
+
1324
+ specify "should be called with the hash given to initialize " do
1325
+ x = nil
1326
+ @c.send(:define_method, :initialize_set){|y| x = y}
1327
+ @c.new(:x => 2)
1328
+ x.should == {:x => 2}
1329
+ end
1330
+
1331
+ specify "should not cause columns modified by the method to be considered as changed" do
1332
+ @c.send(:define_method, :initialize_set){|h| set(:y => 3)}
1333
+ @c.new(:x => 2).changed_columns.should == []
1334
+ end
1335
+ end
1164
1336
 
1165
1337
  describe Sequel::Model, ".create" do
1166
1338
 
@@ -8,6 +8,11 @@ unless Sequel.const_defined?('Model')
8
8
  require 'sequel/model'
9
9
  end
10
10
 
11
+ if ENV['SEQUEL_COLUMNS_INTROSPECTION']
12
+ Sequel.extension :columns_introspection
13
+ Sequel::Dataset.introspect_all_columns
14
+ end
15
+
11
16
  class MockDataset < Sequel::Dataset
12
17
  def insert(*args)
13
18
  @db.execute insert_sql(*args)
@@ -61,6 +61,17 @@ describe Sequel::Model::Errors do
61
61
  msgs.should include('blow blieuh', 'blow blich', 'blay bliu')
62
62
  end
63
63
 
64
+ specify "should not add column names for LiteralStrings" do
65
+ @errors.full_messages.should == []
66
+
67
+ @errors[:blow] << 'blieuh'
68
+ @errors[:blow] << 'blich'.lit
69
+ @errors[:blay] << 'bliu'
70
+ msgs = @errors.full_messages
71
+ msgs.size.should == 3
72
+ msgs.should include('blow blieuh', 'blich', 'blay bliu')
73
+ end
74
+
64
75
  specify "should return the number of error messages using #count" do
65
76
  @errors.count.should == 0
66
77
  @errors.add(:a, 'b')