sequel 3.35.0 → 3.36.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 (81) hide show
  1. data/CHANGELOG +78 -0
  2. data/Rakefile +3 -3
  3. data/bin/sequel +3 -1
  4. data/doc/advanced_associations.rdoc +154 -11
  5. data/doc/migration.rdoc +18 -0
  6. data/doc/object_model.rdoc +541 -0
  7. data/doc/opening_databases.rdoc +4 -1
  8. data/doc/release_notes/3.36.0.txt +245 -0
  9. data/doc/schema_modification.rdoc +0 -6
  10. data/lib/sequel/adapters/do/mysql.rb +7 -0
  11. data/lib/sequel/adapters/jdbc.rb +11 -3
  12. data/lib/sequel/adapters/jdbc/mysql.rb +3 -5
  13. data/lib/sequel/adapters/jdbc/postgresql.rb +10 -8
  14. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  15. data/lib/sequel/adapters/mock.rb +2 -6
  16. data/lib/sequel/adapters/mysql.rb +3 -9
  17. data/lib/sequel/adapters/mysql2.rb +12 -11
  18. data/lib/sequel/adapters/postgres.rb +32 -40
  19. data/lib/sequel/adapters/shared/mssql.rb +15 -11
  20. data/lib/sequel/adapters/shared/mysql.rb +28 -3
  21. data/lib/sequel/adapters/shared/oracle.rb +5 -0
  22. data/lib/sequel/adapters/shared/postgres.rb +59 -5
  23. data/lib/sequel/adapters/shared/sqlite.rb +3 -13
  24. data/lib/sequel/adapters/sqlite.rb +0 -7
  25. data/lib/sequel/adapters/swift/mysql.rb +2 -5
  26. data/lib/sequel/adapters/tinytds.rb +1 -2
  27. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
  28. data/lib/sequel/connection_pool/threaded.rb +9 -1
  29. data/lib/sequel/database/dataset_defaults.rb +3 -1
  30. data/lib/sequel/database/misc.rb +7 -1
  31. data/lib/sequel/database/query.rb +11 -3
  32. data/lib/sequel/database/schema_generator.rb +40 -9
  33. data/lib/sequel/database/schema_methods.rb +6 -1
  34. data/lib/sequel/dataset/actions.rb +5 -5
  35. data/lib/sequel/dataset/prepared_statements.rb +3 -1
  36. data/lib/sequel/dataset/query.rb +1 -1
  37. data/lib/sequel/extensions/migration.rb +28 -0
  38. data/lib/sequel/extensions/pg_auto_parameterize.rb +0 -9
  39. data/lib/sequel/extensions/pg_inet.rb +89 -0
  40. data/lib/sequel/extensions/pg_json.rb +178 -0
  41. data/lib/sequel/extensions/schema_dumper.rb +24 -6
  42. data/lib/sequel/model/associations.rb +19 -15
  43. data/lib/sequel/model/base.rb +11 -12
  44. data/lib/sequel/plugins/composition.rb +1 -2
  45. data/lib/sequel/plugins/eager_each.rb +59 -0
  46. data/lib/sequel/plugins/json_serializer.rb +41 -4
  47. data/lib/sequel/plugins/nested_attributes.rb +72 -52
  48. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  49. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  50. data/lib/sequel/version.rb +1 -1
  51. data/spec/adapters/postgres_spec.rb +271 -1
  52. data/spec/adapters/sqlite_spec.rb +11 -0
  53. data/spec/core/connection_pool_spec.rb +26 -1
  54. data/spec/core/database_spec.rb +19 -0
  55. data/spec/core/dataset_spec.rb +45 -5
  56. data/spec/core/expression_filters_spec.rb +31 -67
  57. data/spec/core/mock_adapter_spec.rb +4 -0
  58. data/spec/extensions/core_extensions_spec.rb +83 -0
  59. data/spec/extensions/eager_each_spec.rb +34 -0
  60. data/spec/extensions/inflector_spec.rb +0 -4
  61. data/spec/extensions/json_serializer_spec.rb +32 -1
  62. data/spec/extensions/migration_spec.rb +28 -0
  63. data/spec/extensions/nested_attributes_spec.rb +134 -1
  64. data/spec/extensions/optimistic_locking_spec.rb +15 -1
  65. data/spec/extensions/pg_hstore_spec.rb +1 -1
  66. data/spec/extensions/pg_inet_spec.rb +44 -0
  67. data/spec/extensions/pg_json_spec.rb +101 -0
  68. data/spec/extensions/prepared_statements_spec.rb +30 -0
  69. data/spec/extensions/rcte_tree_spec.rb +9 -0
  70. data/spec/extensions/schema_dumper_spec.rb +195 -7
  71. data/spec/extensions/serialization_spec.rb +4 -0
  72. data/spec/extensions/spec_helper.rb +9 -1
  73. data/spec/extensions/tactical_eager_loading_spec.rb +8 -0
  74. data/spec/integration/database_test.rb +5 -1
  75. data/spec/integration/prepared_statement_test.rb +20 -2
  76. data/spec/model/associations_spec.rb +27 -0
  77. data/spec/model/base_spec.rb +54 -0
  78. data/spec/model/model_spec.rb +6 -0
  79. data/spec/model/record_spec.rb +18 -0
  80. data/spec/rcov.opts +2 -0
  81. metadata +14 -3
@@ -54,4 +54,34 @@ describe "prepared_statements plugin" do
54
54
  c[1].should == c.load(:id=>1, :name=>'foo', :i=>2)
55
55
  @db.sqls.should == ["SELECT * FROM people WHERE (id = 1) LIMIT 1 -- read_only"]
56
56
  end
57
+
58
+ describe " with placeholder type specifiers" do
59
+ before do
60
+ @ds.meta_def(:requires_placeholder_type_specifiers?){true}
61
+ end
62
+
63
+ specify "should correctly handle without schema type" do
64
+ @c[1].should == @p
65
+ @db.sqls.should == ["SELECT * FROM people WHERE (id = 1) LIMIT 1 -- read_only"]
66
+ end
67
+
68
+ specify "should correctly handle with schema type" do
69
+ @c.db_schema[:id][:type] = :integer
70
+ ds = @c.send(:prepared_lookup)
71
+ def ds.literal_symbol_append(sql, v)
72
+ if @opts[:bind_vars] and match = /\A\$(.*)\z/.match(v.to_s)
73
+ s = match[1].split('__')[0].to_sym
74
+ if prepared_arg?(s)
75
+ literal_append(sql, prepared_arg(s))
76
+ else
77
+ sql << v.to_s
78
+ end
79
+ else
80
+ super
81
+ end
82
+ end
83
+ @c[1].should == @p
84
+ @db.sqls.should == ["SELECT * FROM people WHERE (id = 1) LIMIT 1 -- read_only"]
85
+ end
86
+ end
57
87
  end
@@ -30,6 +30,15 @@ describe Sequel::Model, "rcte_tree" do
30
30
  @o.descendants_dataset.sql.should == 'WITH t AS (SELECT * FROM nodes WHERE (parent_id = 2) UNION ALL SELECT nodes.* FROM nodes INNER JOIN t ON (t.id = nodes.parent_id)) SELECT * FROM t AS nodes'
31
31
  end
32
32
 
33
+ it "should use the correct SQL for lazy associations when recursive CTEs require column aliases" do
34
+ @c.dataset.meta_def(:recursive_cte_requires_column_aliases?){true}
35
+ @c.plugin :rcte_tree
36
+ @o.parent_dataset.sql.should == 'SELECT * FROM nodes WHERE (nodes.id = 1) LIMIT 1'
37
+ @o.children_dataset.sql.should == 'SELECT * FROM nodes WHERE (nodes.parent_id = 2)'
38
+ @o.ancestors_dataset.sql.should == 'WITH t(id, name, parent_id, i, pi) AS (SELECT id, name, parent_id, i, pi FROM nodes WHERE (id = 1) UNION ALL SELECT nodes.id, nodes.name, nodes.parent_id, nodes.i, nodes.pi FROM nodes INNER JOIN t ON (t.parent_id = nodes.id)) SELECT * FROM t AS nodes'
39
+ @o.descendants_dataset.sql.should == 'WITH t(id, name, parent_id, i, pi) AS (SELECT id, name, parent_id, i, pi FROM nodes WHERE (parent_id = 2) UNION ALL SELECT nodes.id, nodes.name, nodes.parent_id, nodes.i, nodes.pi FROM nodes INNER JOIN t ON (t.id = nodes.parent_id)) SELECT * FROM t AS nodes'
40
+ end
41
+
33
42
  it "should use the correct SQL for lazy associations when giving options" do
34
43
  @c.plugin :rcte_tree, :primary_key=>:i, :key=>:pi, :cte_name=>:cte, :order=>:name, :ancestors=>{:name=>:as}, :children=>{:name=>:cs}, :descendants=>{:name=>:ds}, :parent=>{:name=>:p}
35
44
  @o.p_dataset.sql.should == 'SELECT * FROM nodes WHERE (nodes.i = 4) ORDER BY name LIMIT 1'
@@ -50,9 +50,9 @@ add_index :t, [:b, :c], :unique=>true
50
50
  END_CODE
51
51
 
52
52
  g.dump_indexes(:drop_index=>:t).should == (<<END_CODE).strip
53
- drop_index :t, [:a]
54
- drop_index :t, [:c, :e], :name=>:blah
55
53
  drop_index :t, [:b, :c], :unique=>true
54
+ drop_index :t, [:c, :e], :name=>:blah
55
+ drop_index :t, [:a]
56
56
  END_CODE
57
57
  end
58
58
 
@@ -340,6 +340,112 @@ end
340
340
  END_MIG
341
341
  end
342
342
 
343
+ it "should honor the :index_names => false option to not include names of indexes" do
344
+ @d.meta_def(:indexes) do |t|
345
+ {:i1=>{:columns=>[:c1], :unique=>false},
346
+ :t1_c2_c1_index=>{:columns=>[:c2, :c1], :unique=>true}}
347
+ end
348
+ @d.dump_table_schema(:t1, :index_names=>false).should == "create_table(:t1, :ignore_index_errors=>true) do\n primary_key :c1\n String :c2, :size=>20\n \n index [:c1]\n index [:c2, :c1], :unique=>true\nend"
349
+ @d.dump_schema_migration(:index_names=>false).should == <<-END_MIG
350
+ Sequel.migration do
351
+ up do
352
+ create_table(:t1, :ignore_index_errors=>true) do
353
+ primary_key :c1
354
+ String :c2, :size=>20
355
+
356
+ index [:c1]
357
+ index [:c2, :c1], :unique=>true
358
+ end
359
+
360
+ create_table(:t2, :ignore_index_errors=>true) do
361
+ Integer :c1, :null=>false
362
+ BigDecimal :c2, :null=>false
363
+
364
+ primary_key [:c1, :c2]
365
+
366
+ index [:c1]
367
+ index [:c2, :c1], :unique=>true
368
+ end
369
+ end
370
+
371
+ down do
372
+ drop_table(:t2, :t1)
373
+ end
374
+ end
375
+ END_MIG
376
+ end
377
+
378
+ it "should make :index_names => :namespace option a noop if there is a global index namespace" do
379
+ @d.meta_def(:indexes) do |t|
380
+ {:i1=>{:columns=>[:c1], :unique=>false},
381
+ :t1_c2_c1_index=>{:columns=>[:c2, :c1], :unique=>false}}
382
+ end
383
+ @d.dump_table_schema(:t1, :index_names=>:namespace).should == "create_table(:t1, :ignore_index_errors=>true) do\n primary_key :c1\n String :c2, :size=>20\n \n index [:c1], :name=>:i1\n index [:c2, :c1]\nend"
384
+ @d.dump_schema_migration(:index_names=>:namespace).should == <<-END_MIG
385
+ Sequel.migration do
386
+ up do
387
+ create_table(:t1, :ignore_index_errors=>true) do
388
+ primary_key :c1
389
+ String :c2, :size=>20
390
+
391
+ index [:c1], :name=>:i1
392
+ index [:c2, :c1]
393
+ end
394
+
395
+ create_table(:t2, :ignore_index_errors=>true) do
396
+ Integer :c1, :null=>false
397
+ BigDecimal :c2, :null=>false
398
+
399
+ primary_key [:c1, :c2]
400
+
401
+ index [:c1], :name=>:i1
402
+ index [:c2, :c1], :name=>:t1_c2_c1_index
403
+ end
404
+ end
405
+
406
+ down do
407
+ drop_table(:t2, :t1)
408
+ end
409
+ end
410
+ END_MIG
411
+ end
412
+
413
+ it "should honor the :index_names => :namespace option to include names of indexes with prepended table name if there is no global index namespace" do
414
+ @d.meta_def(:global_index_namespace?){false}
415
+ @d.meta_def(:indexes) do |t|
416
+ {:i1=>{:columns=>[:c1], :unique=>false},
417
+ :t1_c2_c1_index=>{:columns=>[:c2, :c1], :unique=>false}}
418
+ end
419
+ @d.dump_table_schema(:t1, :index_names=>:namespace).should == "create_table(:t1, :ignore_index_errors=>true) do\n primary_key :c1\n String :c2, :size=>20\n \n index [:c1], :name=>:t1_i1\n index [:c2, :c1]\nend"
420
+ @d.dump_schema_migration(:index_names=>:namespace).should == <<-END_MIG
421
+ Sequel.migration do
422
+ up do
423
+ create_table(:t1, :ignore_index_errors=>true) do
424
+ primary_key :c1
425
+ String :c2, :size=>20
426
+
427
+ index [:c1], :name=>:t1_i1
428
+ index [:c2, :c1]
429
+ end
430
+
431
+ create_table(:t2, :ignore_index_errors=>true) do
432
+ Integer :c1, :null=>false
433
+ BigDecimal :c2, :null=>false
434
+
435
+ primary_key [:c1, :c2]
436
+
437
+ index [:c1], :name=>:t2_i1
438
+ index [:c2, :c1], :name=>:t2_t1_c2_c1_index
439
+ end
440
+ end
441
+
442
+ down do
443
+ drop_table(:t2, :t1)
444
+ end
445
+ end
446
+ END_MIG
447
+ end
448
+
343
449
  it "should honor the :indexes => false option to not include indexes" do
344
450
  @d.meta_def(:indexes) do |t|
345
451
  {:i1=>{:columns=>[:c1], :unique=>false},
@@ -397,8 +503,84 @@ Sequel.migration do
397
503
  end
398
504
 
399
505
  down do
506
+ drop_index :t1, [:c2, :c1], :ignore_errors=>true, :unique=>true
400
507
  drop_index :t1, [:c1], :ignore_errors=>true, :name=>:i1
508
+ end
509
+ end
510
+ END_MIG
511
+ end
512
+
513
+ it "should honor the :index_names => false option to not include names of indexes when dumping just indexes as a migration" do
514
+ @d.meta_def(:tables){|o| [:t1]}
515
+ @d.meta_def(:indexes) do |t|
516
+ {:i1=>{:columns=>[:c1], :unique=>false},
517
+ :t1_c2_c1_index=>{:columns=>[:c2, :c1], :unique=>true}}
518
+ end
519
+ @d.dump_indexes_migration(:index_names=>false).should == <<-END_MIG
520
+ Sequel.migration do
521
+ up do
522
+ add_index :t1, [:c1], :ignore_errors=>true
523
+ add_index :t1, [:c2, :c1], :ignore_errors=>true, :unique=>true
524
+ end
525
+
526
+ down do
401
527
  drop_index :t1, [:c2, :c1], :ignore_errors=>true, :unique=>true
528
+ drop_index :t1, [:c1], :ignore_errors=>true
529
+ end
530
+ end
531
+ END_MIG
532
+ end
533
+
534
+ it "should honor the :index_names => :namespace option be a noop if there is a global index namespace" do
535
+ @d.meta_def(:tables){|o| [:t1, :t2]}
536
+ @d.meta_def(:indexes) do |t|
537
+ {:i1=>{:columns=>[:c1], :unique=>false},
538
+ :t1_c2_c1_index=>{:columns=>[:c2, :c1], :unique=>false}}
539
+ end
540
+ @d.dump_indexes_migration(:index_names=>:namespace).should == <<-END_MIG
541
+ Sequel.migration do
542
+ up do
543
+ add_index :t1, [:c1], :ignore_errors=>true, :name=>:i1
544
+ add_index :t1, [:c2, :c1], :ignore_errors=>true
545
+
546
+ add_index :t2, [:c1], :ignore_errors=>true, :name=>:i1
547
+ add_index :t2, [:c2, :c1], :ignore_errors=>true, :name=>:t1_c2_c1_index
548
+ end
549
+
550
+ down do
551
+ drop_index :t2, [:c2, :c1], :ignore_errors=>true, :name=>:t1_c2_c1_index
552
+ drop_index :t2, [:c1], :ignore_errors=>true, :name=>:i1
553
+
554
+ drop_index :t1, [:c2, :c1], :ignore_errors=>true
555
+ drop_index :t1, [:c1], :ignore_errors=>true, :name=>:i1
556
+ end
557
+ end
558
+ END_MIG
559
+ end
560
+
561
+ it "should honor the :index_names => :namespace option to include names of indexes with prepended table name when dumping just indexes as a migration if there is no global index namespace" do
562
+ @d.meta_def(:global_index_namespace?){false}
563
+ @d.meta_def(:tables){|o| [:t1, :t2]}
564
+ @d.meta_def(:indexes) do |t|
565
+ {:i1=>{:columns=>[:c1], :unique=>false},
566
+ :t1_c2_c1_index=>{:columns=>[:c2, :c1], :unique=>false}}
567
+ end
568
+ @d.dump_indexes_migration(:index_names=>:namespace).should == <<-END_MIG
569
+ Sequel.migration do
570
+ up do
571
+ add_index :t1, [:c1], :ignore_errors=>true, :name=>:t1_i1
572
+ add_index :t1, [:c2, :c1], :ignore_errors=>true
573
+
574
+ add_index :t2, [:c1], :ignore_errors=>true, :name=>:t2_i1
575
+ add_index :t2, [:c2, :c1], :ignore_errors=>true, :name=>:t2_t1_c2_c1_index
576
+ end
577
+
578
+ down do
579
+ drop_index :t2, [:c2, :c1], :ignore_errors=>true, :name=>:t2_t1_c2_c1_index
580
+ drop_index :t2, [:c1], :ignore_errors=>true, :name=>:t2_i1
581
+
582
+ drop_index :t1, [:c2, :c1], :ignore_errors=>true
583
+ drop_index :t1, [:c1], :ignore_errors=>true, :name=>:t1_i1
402
584
  end
403
585
  end
404
586
  END_MIG
@@ -508,7 +690,9 @@ END_MIG
508
690
  ["double precision", "timestamp with time zone", "timestamp without time zone",
509
691
  "time with time zone", "time without time zone", "character varying(20)"] +
510
692
  %w"nvarchar ntext smalldatetime smallmoney binary varbinary nchar" +
511
- ["timestamp(6) without time zone", "timestamp(6) with time zone", "int(12) unsigned", 'bigint unsigned', 'tinyint(3) unsigned', 'identity', 'int identity']
693
+ ["timestamp(6) without time zone", "timestamp(6) with time zone", 'mediumint(10) unsigned', 'int(9) unsigned',
694
+ 'int(10) unsigned', "int(12) unsigned", 'bigint unsigned', 'tinyint(3) unsigned', 'identity', 'int identity'] +
695
+ %w"integer(10)"
512
696
  @d.meta_def(:schema) do |t, *o|
513
697
  i = 0
514
698
  types.map{|x| [:"c#{i+=1}", {:db_type=>x, :allow_null=>true}]}
@@ -579,10 +763,14 @@ create_table(:x) do
579
763
  DateTime :c62, :size=>6
580
764
  DateTime :c63, :size=>6
581
765
  Integer :c64
582
- Bignum :c65
583
- Integer :c66
584
- Integer :c67
585
- Integer :c68
766
+ Integer :c65
767
+ Bignum :c66
768
+ Bignum :c67
769
+ Bignum :c68
770
+ Integer :c69
771
+ Integer :c70
772
+ Integer :c71
773
+ Integer :c72
586
774
  end
587
775
  END_MIG
588
776
  end
@@ -72,6 +72,10 @@ describe "Serialization plugin" do
72
72
  MODEL_DB.sqls.should == ["INSERT INTO items (abc) VALUES ('olleh')"]
73
73
  end
74
74
 
75
+ it "should raise an error if specificing serializer as an unregistered symbol" do
76
+ proc{@c.plugin :serialization, :foo, :abc}.should raise_error(Sequel::Error)
77
+ end
78
+
75
79
  it "should translate values to and from yaml serialization format using accessor methods" do
76
80
  @c.set_primary_key :id
77
81
  @c.plugin :serialization, :yaml, :abc, :def
@@ -8,7 +8,15 @@ unless Sequel.const_defined?('Model')
8
8
  require 'sequel/model'
9
9
  end
10
10
 
11
- Sequel.extension(*%w'string_date_time inflector pagination query pretty_table blank migration schema_dumper looser_typecasting sql_expr thread_local_timezones to_dot columns_introspection server_block arbitrary_servers pg_auto_parameterize pg_statement_cache pg_hstore pg_hstore_ops schema_caching null_dataset select_remove query_literals')
11
+ begin
12
+ # Attempt to load ActiveSupport inflector first, so Sequel inflector
13
+ # can override it.
14
+ require 'active_support/inflector'
15
+ require 'active_support/string/inflections'
16
+ rescue LoadError
17
+ end
18
+
19
+ Sequel.extension(*%w'string_date_time inflector pagination query pretty_table blank migration schema_dumper looser_typecasting sql_expr thread_local_timezones to_dot columns_introspection server_block arbitrary_servers pg_auto_parameterize pg_statement_cache pg_hstore pg_hstore_ops pg_inet schema_caching null_dataset select_remove query_literals')
12
20
  {:hook_class_methods=>[], :schema=>[], :validation_class_methods=>[]}.each{|p, opts| Sequel::Model.plugin(p, *opts)}
13
21
 
14
22
  Sequel::Dataset.introspect_all_columns if ENV['SEQUEL_COLUMNS_INTROSPECTION']
@@ -52,4 +52,12 @@ describe "Sequel::Plugins::TacticalEagerLoading" do
52
52
  @ds.should_not_receive(:eager_load)
53
53
  ts.map{|x| x.parent}.should == [ts[2], ts[3], nil, nil]
54
54
  end
55
+
56
+ it "should handle case where an association is valid on an instance, but not on all instances" do
57
+ c = Class.new(@c)
58
+ c.many_to_one :parent2, :class=>@c, :key=>:parent_id
59
+ @c.dataset.row_proc = proc{|r| (r[:parent_id] == 101 ? c : @c).call(r)}
60
+ @c.all{|x| x.parent2 if x.is_a?(c)}
61
+ end
62
+
55
63
  end
@@ -24,7 +24,11 @@ describe Sequel::Database do
24
24
  begin
25
25
  INTEGRATION_DB << "SELECT"
26
26
  rescue Sequel::DatabaseError=>e
27
- e.wrapped_exception.should be_a_kind_of(Exception)
27
+ if defined?(Java::JavaLang::Exception)
28
+ (e.wrapped_exception.is_a?(Exception) || e.wrapped_exception.is_a?(Java::JavaLang::Exception)).should be_true
29
+ else
30
+ e.wrapped_exception.should be_a_kind_of(Exception)
31
+ end
28
32
  end
29
33
  end
30
34
 
@@ -17,6 +17,7 @@ describe "Prepared Statements and Bound Arguments" do
17
17
  end
18
18
 
19
19
  specify "should support bound variables when selecting" do
20
+ @ds.filter(:numb=>:$n).call(:each, :n=>10){|h| h.should == {:id=>1, :numb=>10}}
20
21
  @ds.filter(:numb=>:$n).call(:select, :n=>10).should == [{:id=>1, :numb=>10}]
21
22
  @ds.filter(:numb=>:$n).call(:all, :n=>10).should == [{:id=>1, :numb=>10}]
22
23
  @ds.filter(:numb=>:$n).call(:first, :n=>10).should == {:id=>1, :numb=>10}
@@ -25,7 +26,9 @@ describe "Prepared Statements and Bound Arguments" do
25
26
  @ds.filter(:numb=>:$n).call([:to_hash_groups, :id, :numb], :n=>10).should == {1=>[10]}
26
27
  end
27
28
 
28
- specify "should support blocks for select, all, and map " do
29
+ specify "should support blocks for each, select, all, and map when using bound variables" do
30
+ a = []
31
+ @ds.filter(:numb=>:$n).call(:each, :n=>10){|r| r[:numb] *= 2; a << r}; a.should == [{:id=>1, :numb=>20}]
29
32
  @ds.filter(:numb=>:$n).call(:select, :n=>10){|r| r[:numb] *= 2}.should == [{:id=>1, :numb=>20}]
30
33
  @ds.filter(:numb=>:$n).call(:all, :n=>10){|r| r[:numb] *= 2}.should == [{:id=>1, :numb=>20}]
31
34
  @ds.filter(:numb=>:$n).call([:map], :n=>10){|r| r[:numb] * 2}.should == [20]
@@ -125,6 +128,8 @@ describe "Prepared Statements and Bound Arguments" do
125
128
  end
126
129
 
127
130
  specify "should support prepared statements when selecting" do
131
+ @ds.filter(:numb=>:$n).prepare(:each, :select_n)
132
+ @db.call(:select_n, :n=>10){|h| h.should == {:id=>1, :numb=>10}}
128
133
  @ds.filter(:numb=>:$n).prepare(:select, :select_n)
129
134
  @db.call(:select_n, :n=>10).should == [{:id=>1, :numb=>10}]
130
135
  @ds.filter(:numb=>:$n).prepare(:all, :select_n)
@@ -137,7 +142,20 @@ describe "Prepared Statements and Bound Arguments" do
137
142
  @db.call(:select_n, :n=>10).should == {1=>10}
138
143
  end
139
144
 
140
- specify "should support prepared statements being call multiple times with different arguments" do
145
+ specify "should support blocks for each, select, all, and map when using prepared statements" do
146
+ a = []
147
+ @ds.filter(:numb=>:$n).prepare(:each, :select_n).call(:n=>10){|r| r[:numb] *= 2; a << r}; a.should == [{:id=>1, :numb=>20}]
148
+ a = []
149
+ @db.call(:select_n, :n=>10){|r| r[:numb] *= 2; a << r}; a.should == [{:id=>1, :numb=>20}]
150
+ @ds.filter(:numb=>:$n).prepare(:select, :select_n).call(:n=>10){|r| r[:numb] *= 2}.should == [{:id=>1, :numb=>20}]
151
+ @db.call(:select_n, :n=>10){|r| r[:numb] *= 2}.should == [{:id=>1, :numb=>20}]
152
+ @ds.filter(:numb=>:$n).prepare(:all, :select_n).call(:n=>10){|r| r[:numb] *= 2}.should == [{:id=>1, :numb=>20}]
153
+ @db.call(:select_n, :n=>10){|r| r[:numb] *= 2}.should == [{:id=>1, :numb=>20}]
154
+ @ds.filter(:numb=>:$n).prepare([:map], :select_n).call(:n=>10){|r| r[:numb] *= 2}.should == [20]
155
+ @db.call(:select_n, :n=>10){|r| r[:numb] *= 2}.should == [20]
156
+ end
157
+
158
+ specify "should support prepared statements being called multiple times with different arguments" do
141
159
  @ds.filter(:numb=>:$n).prepare(:select, :select_n)
142
160
  @db.call(:select_n, :n=>10).should == [{:id=>1, :numb=>10}]
143
161
  @db.call(:select_n, :n=>0).should == []
@@ -457,6 +457,20 @@ describe Sequel::Model, "many_to_one" do
457
457
  MODEL_DB.sqls.should == []
458
458
  end
459
459
 
460
+ it "should have the setter not modify the reciprocal if set to same value as current" do
461
+ @c2.many_to_one :parent, :class => @c2
462
+ @c2.one_to_many :children, :class => @c2, :key=>:parent_id
463
+
464
+ c1 = @c2.load(:id => 1, :parent_id=>nil)
465
+ c2 = @c2.load(:id => 2, :parent_id=>1)
466
+ c3 = @c2.load(:id => 3, :parent_id=>1)
467
+ c1.associations[:children] = [c2, c3]
468
+ c2.associations[:parent] = c1
469
+ c2.parent = c1
470
+ c1.children.should == [c2, c3]
471
+ MODEL_DB.sqls.should == []
472
+ end
473
+
460
474
  it "should get all matching records and only return the first if :key option is set to nil" do
461
475
  @c2.one_to_many :children, :class => @c2, :key=>:parent_id
462
476
  @c2.many_to_one :first_grand_parent, :class => @c2, :key=>nil, :eager_graph=>:children, :dataset=>proc{model.filter(:children_id=>parent_id)}
@@ -959,6 +973,19 @@ describe Sequel::Model, "one_to_one" do
959
973
  f.child.should == nil
960
974
  end
961
975
 
976
+ it "should have the setter not modify the reciprocal if set to same value as current" do
977
+ @c2.one_to_one :parent, :class => @c2, :key=>:parent_id
978
+ @c2.many_to_one :child, :class => @c2, :key=>:parent_id
979
+
980
+ c1 = @c2.load(:id => 1, :parent_id=>nil)
981
+ c2 = @c2.load(:id => 2, :parent_id=>1)
982
+ c1.associations[:child] = c2
983
+ c2.associations[:parent] = c1
984
+ c2.parent = c1
985
+ c1.child.should == c2
986
+ MODEL_DB.sqls.should == []
987
+ end
988
+
962
989
  it "should not add associations methods directly to class" do
963
990
  @c2.one_to_one :parent, :class => @c2
964
991
  @c2.instance_methods.collect{|x| x.to_s}.should(include('parent'))
@@ -9,6 +9,12 @@ describe "Model attribute setters" do
9
9
  MODEL_DB.reset
10
10
  end
11
11
 
12
+ specify "refresh should return self" do
13
+ @o = @c[1]
14
+ @o.stub(:_refresh).and_return([])
15
+ @o.refresh.should == @o
16
+ end
17
+
12
18
  it "should mark the column value as changed" do
13
19
  @o.changed_columns.should == []
14
20
 
@@ -349,6 +355,54 @@ describe "Model.qualified_primary_key_hash" do
349
355
  end
350
356
  end
351
357
 
358
+ describe "Model.db" do
359
+ before do
360
+ @db = Sequel.mock
361
+ @databases = Sequel::DATABASES.dup
362
+ @model_db = Sequel::Model.db
363
+ Sequel::Model.db = nil
364
+ Sequel::DATABASES.clear
365
+ end
366
+ after do
367
+ Sequel::Model.instance_variable_get(:@db).should == nil
368
+ Sequel::DATABASES.replace(@databases)
369
+ Sequel::Model.db = @model_db
370
+ end
371
+
372
+ specify "should be required when create named model classes" do
373
+ begin
374
+ proc{class ModelTest < Sequel::Model; end}.should raise_error(Sequel::Error)
375
+ ensure
376
+ Object.send(:remove_const, :ModelTest)
377
+ end
378
+ end
379
+
380
+ specify "should be required when creating anonymous model classes without a database" do
381
+ proc{Sequel::Model(:foo)}.should raise_error(Sequel::Error)
382
+ end
383
+
384
+ specify "should not be required when creating anonymous model classes with a database" do
385
+ Sequel::Model(@db).db.should == @db
386
+ Sequel::Model(@db[:foo]).db.should == @db
387
+ end
388
+
389
+ specify "should work correctly when subclassing anonymous model classes with a database" do
390
+ begin
391
+ Class.new(Sequel::Model(@db)).db.should == @db
392
+ Class.new(Sequel::Model(@db[:foo])).db.should == @db
393
+ class ModelTest < Sequel::Model(@db)
394
+ db.should == @db
395
+ end
396
+ class ModelTest2 < Sequel::Model(@db[:foo])
397
+ db.should == @db
398
+ end
399
+ ensure
400
+ Object.send(:remove_const, :ModelTest)
401
+ Object.send(:remove_const, :ModelTest2)
402
+ end
403
+ end
404
+ end
405
+
352
406
  describe "Model.db=" do
353
407
  before do
354
408
  @db1 = Sequel.mock