sequel 3.35.0 → 3.36.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +78 -0
- data/Rakefile +3 -3
- data/bin/sequel +3 -1
- data/doc/advanced_associations.rdoc +154 -11
- data/doc/migration.rdoc +18 -0
- data/doc/object_model.rdoc +541 -0
- data/doc/opening_databases.rdoc +4 -1
- data/doc/release_notes/3.36.0.txt +245 -0
- data/doc/schema_modification.rdoc +0 -6
- data/lib/sequel/adapters/do/mysql.rb +7 -0
- data/lib/sequel/adapters/jdbc.rb +11 -3
- data/lib/sequel/adapters/jdbc/mysql.rb +3 -5
- data/lib/sequel/adapters/jdbc/postgresql.rb +10 -8
- data/lib/sequel/adapters/jdbc/progress.rb +21 -0
- data/lib/sequel/adapters/mock.rb +2 -6
- data/lib/sequel/adapters/mysql.rb +3 -9
- data/lib/sequel/adapters/mysql2.rb +12 -11
- data/lib/sequel/adapters/postgres.rb +32 -40
- data/lib/sequel/adapters/shared/mssql.rb +15 -11
- data/lib/sequel/adapters/shared/mysql.rb +28 -3
- data/lib/sequel/adapters/shared/oracle.rb +5 -0
- data/lib/sequel/adapters/shared/postgres.rb +59 -5
- data/lib/sequel/adapters/shared/sqlite.rb +3 -13
- data/lib/sequel/adapters/sqlite.rb +0 -7
- data/lib/sequel/adapters/swift/mysql.rb +2 -5
- data/lib/sequel/adapters/tinytds.rb +1 -2
- data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
- data/lib/sequel/connection_pool/threaded.rb +9 -1
- data/lib/sequel/database/dataset_defaults.rb +3 -1
- data/lib/sequel/database/misc.rb +7 -1
- data/lib/sequel/database/query.rb +11 -3
- data/lib/sequel/database/schema_generator.rb +40 -9
- data/lib/sequel/database/schema_methods.rb +6 -1
- data/lib/sequel/dataset/actions.rb +5 -5
- data/lib/sequel/dataset/prepared_statements.rb +3 -1
- data/lib/sequel/dataset/query.rb +1 -1
- data/lib/sequel/extensions/migration.rb +28 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +0 -9
- data/lib/sequel/extensions/pg_inet.rb +89 -0
- data/lib/sequel/extensions/pg_json.rb +178 -0
- data/lib/sequel/extensions/schema_dumper.rb +24 -6
- data/lib/sequel/model/associations.rb +19 -15
- data/lib/sequel/model/base.rb +11 -12
- data/lib/sequel/plugins/composition.rb +1 -2
- data/lib/sequel/plugins/eager_each.rb +59 -0
- data/lib/sequel/plugins/json_serializer.rb +41 -4
- data/lib/sequel/plugins/nested_attributes.rb +72 -52
- data/lib/sequel/plugins/optimistic_locking.rb +8 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +271 -1
- data/spec/adapters/sqlite_spec.rb +11 -0
- data/spec/core/connection_pool_spec.rb +26 -1
- data/spec/core/database_spec.rb +19 -0
- data/spec/core/dataset_spec.rb +45 -5
- data/spec/core/expression_filters_spec.rb +31 -67
- data/spec/core/mock_adapter_spec.rb +4 -0
- data/spec/extensions/core_extensions_spec.rb +83 -0
- data/spec/extensions/eager_each_spec.rb +34 -0
- data/spec/extensions/inflector_spec.rb +0 -4
- data/spec/extensions/json_serializer_spec.rb +32 -1
- data/spec/extensions/migration_spec.rb +28 -0
- data/spec/extensions/nested_attributes_spec.rb +134 -1
- data/spec/extensions/optimistic_locking_spec.rb +15 -1
- data/spec/extensions/pg_hstore_spec.rb +1 -1
- data/spec/extensions/pg_inet_spec.rb +44 -0
- data/spec/extensions/pg_json_spec.rb +101 -0
- data/spec/extensions/prepared_statements_spec.rb +30 -0
- data/spec/extensions/rcte_tree_spec.rb +9 -0
- data/spec/extensions/schema_dumper_spec.rb +195 -7
- data/spec/extensions/serialization_spec.rb +4 -0
- data/spec/extensions/spec_helper.rb +9 -1
- data/spec/extensions/tactical_eager_loading_spec.rb +8 -0
- data/spec/integration/database_test.rb +5 -1
- data/spec/integration/prepared_statement_test.rb +20 -2
- data/spec/model/associations_spec.rb +27 -0
- data/spec/model/base_spec.rb +54 -0
- data/spec/model/model_spec.rb +6 -0
- data/spec/model/record_spec.rb +18 -0
- data/spec/rcov.opts +2 -0
- 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",
|
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
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
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
|
-
|
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
|
-
|
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
|
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'))
|
data/spec/model/base_spec.rb
CHANGED
@@ -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
|