sequel 3.9.0 → 3.10.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.
- data/CHANGELOG +56 -0
- data/README.rdoc +1 -1
- data/Rakefile +1 -1
- data/doc/advanced_associations.rdoc +7 -10
- data/doc/release_notes/3.10.0.txt +286 -0
- data/lib/sequel/adapters/do/mysql.rb +4 -0
- data/lib/sequel/adapters/jdbc.rb +5 -0
- data/lib/sequel/adapters/jdbc/as400.rb +58 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +30 -0
- data/lib/sequel/adapters/shared/mssql.rb +23 -9
- data/lib/sequel/adapters/shared/mysql.rb +12 -1
- data/lib/sequel/adapters/shared/postgres.rb +7 -18
- data/lib/sequel/adapters/shared/sqlite.rb +5 -0
- data/lib/sequel/adapters/sqlite.rb +5 -0
- data/lib/sequel/connection_pool/single.rb +3 -3
- data/lib/sequel/database.rb +3 -2
- data/lib/sequel/dataset.rb +6 -5
- data/lib/sequel/dataset/convenience.rb +3 -3
- data/lib/sequel/dataset/query.rb +13 -0
- data/lib/sequel/dataset/sql.rb +31 -1
- data/lib/sequel/extensions/schema_dumper.rb +3 -3
- data/lib/sequel/model.rb +8 -6
- data/lib/sequel/model/associations.rb +144 -102
- data/lib/sequel/model/base.rb +21 -1
- data/lib/sequel/model/plugins.rb +3 -1
- data/lib/sequel/plugins/association_dependencies.rb +14 -7
- data/lib/sequel/plugins/caching.rb +4 -0
- data/lib/sequel/plugins/composition.rb +138 -0
- data/lib/sequel/plugins/identity_map.rb +2 -2
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/nested_attributes.rb +3 -2
- data/lib/sequel/plugins/rcte_tree.rb +281 -0
- data/lib/sequel/plugins/typecast_on_load.rb +16 -5
- data/lib/sequel/sql.rb +18 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +4 -0
- data/spec/adapters/mysql_spec.rb +4 -0
- data/spec/adapters/postgres_spec.rb +55 -5
- data/spec/core/database_spec.rb +5 -3
- data/spec/core/dataset_spec.rb +86 -15
- data/spec/core/expression_filters_spec.rb +23 -6
- data/spec/extensions/association_dependencies_spec.rb +24 -5
- data/spec/extensions/association_proxies_spec.rb +3 -0
- data/spec/extensions/composition_spec.rb +194 -0
- data/spec/extensions/identity_map_spec.rb +16 -0
- data/spec/extensions/nested_attributes_spec.rb +44 -1
- data/spec/extensions/rcte_tree_spec.rb +205 -0
- data/spec/extensions/schema_dumper_spec.rb +6 -0
- data/spec/extensions/spec_helper.rb +6 -0
- data/spec/extensions/typecast_on_load_spec.rb +9 -0
- data/spec/extensions/validation_helpers_spec.rb +5 -5
- data/spec/integration/dataset_test.rb +13 -9
- data/spec/integration/eager_loader_test.rb +56 -1
- data/spec/integration/model_test.rb +8 -0
- data/spec/integration/plugin_test.rb +270 -0
- data/spec/integration/schema_test.rb +1 -1
- data/spec/model/associations_spec.rb +541 -118
- data/spec/model/eager_loading_spec.rb +24 -3
- data/spec/model/record_spec.rb +34 -0
- metadata +9 -2
@@ -46,14 +46,25 @@ module Sequel
|
|
46
46
|
# ensuring the model object will have the correct typecasting even
|
47
47
|
# if the database doesn't typecast the columns correctly.
|
48
48
|
def load(values)
|
49
|
-
|
50
|
-
|
49
|
+
super.load_typecast
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module InstanceMethods
|
54
|
+
def load_typecast
|
55
|
+
model.typecast_on_load_columns.each do |c|
|
51
56
|
if v = values[c]
|
52
|
-
|
57
|
+
send("#{c}=", v)
|
53
58
|
end
|
54
59
|
end
|
55
|
-
|
56
|
-
|
60
|
+
changed_columns.clear
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def _refresh(dataset)
|
67
|
+
super.load_typecast
|
57
68
|
end
|
58
69
|
end
|
59
70
|
end
|
data/lib/sequel/sql.rb
CHANGED
@@ -4,7 +4,17 @@ module Sequel
|
|
4
4
|
# the Ruby 1.9 BasicObject class. This is used in a few places where proxy
|
5
5
|
# objects are needed that respond to any method call.
|
6
6
|
class BasicObject
|
7
|
-
|
7
|
+
# The instance methods to not remove from the class when removing
|
8
|
+
# other methods.
|
9
|
+
KEEP_METHODS = %w"__id__ __send__ __metaclass__ instance_eval == equal? initialize"
|
10
|
+
|
11
|
+
# Remove all but the most basic instance methods from the class. A separate
|
12
|
+
# method so that it can be called again if necessary if you load libraries
|
13
|
+
# after Sequel that add instance methods to Object.
|
14
|
+
def self.remove_methods!
|
15
|
+
((private_instance_methods + instance_methods) - KEEP_METHODS).each{|m| undef_method(m)}
|
16
|
+
end
|
17
|
+
remove_methods!
|
8
18
|
end
|
9
19
|
else
|
10
20
|
# If on 1.9, create a Sequel::BasicObject class that is just like the
|
@@ -18,6 +28,10 @@ module Sequel
|
|
18
28
|
def self.const_missing(name)
|
19
29
|
::Object.const_get(name)
|
20
30
|
end
|
31
|
+
|
32
|
+
# No-op method on ruby 1.9, which has a real BasicObject class.
|
33
|
+
def self.remove_methods!
|
34
|
+
end
|
21
35
|
end
|
22
36
|
end
|
23
37
|
|
@@ -132,6 +146,9 @@ module Sequel
|
|
132
146
|
case op
|
133
147
|
when *N_ARITY_OPERATORS
|
134
148
|
raise(Error, "The #{op} operator requires at least 1 argument") unless args.length >= 1
|
149
|
+
old_args = args
|
150
|
+
args = []
|
151
|
+
old_args.each{|a| a.is_a?(self.class) && a.op == op ? args.concat(a.args) : args.push(a)}
|
135
152
|
when *TWO_ARITY_OPERATORS
|
136
153
|
raise(Error, "The #{op} operator requires precisely 2 arguments") unless args.length == 2
|
137
154
|
when *ONE_ARITY_OPERATORS
|
data/lib/sequel/version.rb
CHANGED
data/spec/adapters/mssql_spec.rb
CHANGED
@@ -53,6 +53,10 @@ context "A MSSQL database" do
|
|
53
53
|
proc{@db.server_version}.should_not raise_error
|
54
54
|
proc{@db.dataset.server_version}.should_not raise_error
|
55
55
|
end
|
56
|
+
|
57
|
+
specify "should work with NOLOCK" do
|
58
|
+
@db.transaction{@db[:test3].nolock.all.should == []}
|
59
|
+
end
|
56
60
|
end
|
57
61
|
|
58
62
|
context "MSSQL Dataset#join_table" do
|
data/spec/adapters/mysql_spec.rb
CHANGED
@@ -83,6 +83,10 @@ context "A MySQL database" do
|
|
83
83
|
specify "should handle the creation and dropping of an InnoDB table with foreign keys" do
|
84
84
|
proc{MYSQL_DB.create_table!(:test_innodb, :engine=>:InnoDB){primary_key :id; foreign_key :fk, :test_innodb, :key=>:id}}.should_not raise_error
|
85
85
|
end
|
86
|
+
|
87
|
+
specify "should support for_share" do
|
88
|
+
MYSQL_DB.transaction{MYSQL_DB[:test2].for_share.all.should == []}
|
89
|
+
end
|
86
90
|
end
|
87
91
|
|
88
92
|
if MYSQL_DB.class.adapter_scheme == :mysql
|
@@ -130,11 +130,6 @@ context "A PostgreSQL dataset" do
|
|
130
130
|
@d.filter(:name => /^bc/).count.should == 1
|
131
131
|
end
|
132
132
|
|
133
|
-
specify "should support for_share and for_update" do
|
134
|
-
@d.for_share.all.should == []
|
135
|
-
@d.for_update.all.should == []
|
136
|
-
end
|
137
|
-
|
138
133
|
specify "#lock should lock tables and yield if a block is given" do
|
139
134
|
@d.lock('EXCLUSIVE'){@d.insert(:name=>'a')}
|
140
135
|
end
|
@@ -153,6 +148,61 @@ context "A PostgreSQL dataset" do
|
|
153
148
|
end
|
154
149
|
end
|
155
150
|
|
151
|
+
if POSTGRES_DB.pool.respond_to?(:max_size) and POSTGRES_DB.pool.max_size > 1
|
152
|
+
describe "Dataset#for_update support" do
|
153
|
+
before do
|
154
|
+
@db = POSTGRES_DB.create_table!(:items) do
|
155
|
+
primary_key :id
|
156
|
+
Integer :number
|
157
|
+
String :name
|
158
|
+
end
|
159
|
+
@ds = POSTGRES_DB[:items]
|
160
|
+
clear_sqls
|
161
|
+
end
|
162
|
+
after do
|
163
|
+
POSTGRES_DB.drop_table(:items)
|
164
|
+
POSTGRES_DB.disconnect
|
165
|
+
end
|
166
|
+
|
167
|
+
specify "should handle FOR UPDATE" do
|
168
|
+
@ds.insert(:number=>20)
|
169
|
+
c = nil
|
170
|
+
t = nil
|
171
|
+
POSTGRES_DB.transaction do
|
172
|
+
@ds.for_update.first(:id=>1)
|
173
|
+
t = Thread.new do
|
174
|
+
POSTGRES_DB.transaction do
|
175
|
+
@ds.filter(:id=>1).update(:name=>'Jim')
|
176
|
+
c = @ds.first(:id=>1)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
sleep 0.01
|
180
|
+
@ds.filter(:id=>1).update(:number=>30)
|
181
|
+
end
|
182
|
+
t.join
|
183
|
+
c.should == {:id=>1, :number=>30, :name=>'Jim'}
|
184
|
+
end
|
185
|
+
|
186
|
+
specify "should handle FOR SHARE" do
|
187
|
+
@ds.insert(:number=>20)
|
188
|
+
c = nil
|
189
|
+
t = nil
|
190
|
+
POSTGRES_DB.transaction do
|
191
|
+
@ds.for_share.first(:id=>1)
|
192
|
+
t = Thread.new do
|
193
|
+
POSTGRES_DB.transaction do
|
194
|
+
c = @ds.for_share.filter(:id=>1).first
|
195
|
+
end
|
196
|
+
end
|
197
|
+
sleep 0.05
|
198
|
+
@ds.filter(:id=>1).update(:name=>'Jim')
|
199
|
+
c.should == {:id=>1, :number=>20, :name=>nil}
|
200
|
+
end
|
201
|
+
t.join
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
156
206
|
context "A PostgreSQL dataset with a timestamp field" do
|
157
207
|
before do
|
158
208
|
@d = POSTGRES_DB[:test3]
|
data/spec/core/database_spec.rb
CHANGED
@@ -794,6 +794,7 @@ context "A Database adapter with a scheme" do
|
|
794
794
|
x = nil
|
795
795
|
y = nil
|
796
796
|
z = nil
|
797
|
+
returnValue = 'anything'
|
797
798
|
|
798
799
|
p = proc do |c|
|
799
800
|
c.should be_a_kind_of(CCC)
|
@@ -802,15 +803,16 @@ context "A Database adapter with a scheme" do
|
|
802
803
|
z = y
|
803
804
|
y = x
|
804
805
|
x = c
|
806
|
+
returnValue
|
805
807
|
end
|
806
|
-
Sequel::Database.connect('ccc://localhost/db', &p).should ==
|
808
|
+
Sequel::Database.connect('ccc://localhost/db', &p).should == returnValue
|
807
809
|
CCC::DISCONNECTS.should == [x]
|
808
810
|
|
809
|
-
Sequel.connect('ccc://localhost/db', &p).should ==
|
811
|
+
Sequel.connect('ccc://localhost/db', &p).should == returnValue
|
810
812
|
CCC::DISCONNECTS.should == [y, x]
|
811
813
|
|
812
814
|
Sequel.send(:def_adapter_method, :ccc)
|
813
|
-
Sequel.ccc('db', :host=>'localhost', &p).should ==
|
815
|
+
Sequel.ccc('db', :host=>'localhost', &p).should == returnValue
|
814
816
|
CCC::DISCONNECTS.should == [z, y, x]
|
815
817
|
end
|
816
818
|
|
data/spec/core/dataset_spec.rb
CHANGED
@@ -702,9 +702,9 @@ context "Dataset#exclude" do
|
|
702
702
|
|
703
703
|
specify "should allow the use of blocks and arguments simultaneously" do
|
704
704
|
@dataset.exclude(:id => (7..11)){:id.sql_number < 6}.sql.should ==
|
705
|
-
'SELECT * FROM test WHERE ((
|
705
|
+
'SELECT * FROM test WHERE ((id < 7) OR (id > 11) OR (id >= 6))'
|
706
706
|
@dataset.exclude([:id, 1], [:x, 3]){:id.sql_number < 6}.sql.should ==
|
707
|
-
'SELECT * FROM test WHERE ((
|
707
|
+
'SELECT * FROM test WHERE ((id != 1) OR (x != 3) OR (id >= 6))'
|
708
708
|
end
|
709
709
|
end
|
710
710
|
|
@@ -1580,36 +1580,36 @@ context "Dataset#group_and_count" do
|
|
1580
1580
|
|
1581
1581
|
specify "should format SQL properly" do
|
1582
1582
|
@ds.group_and_count(:name).sql.should ==
|
1583
|
-
"SELECT name, count(*) AS count FROM test GROUP BY name
|
1583
|
+
"SELECT name, count(*) AS count FROM test GROUP BY name"
|
1584
1584
|
end
|
1585
1585
|
|
1586
1586
|
specify "should accept multiple columns for grouping" do
|
1587
1587
|
@ds.group_and_count(:a, :b).sql.should ==
|
1588
|
-
"SELECT a, b, count(*) AS count FROM test GROUP BY a, b
|
1588
|
+
"SELECT a, b, count(*) AS count FROM test GROUP BY a, b"
|
1589
1589
|
end
|
1590
1590
|
|
1591
1591
|
specify "should format column aliases in the select clause but not in the group clause" do
|
1592
1592
|
@ds.group_and_count(:name___n).sql.should ==
|
1593
|
-
"SELECT name AS n, count(*) AS count FROM test GROUP BY name
|
1593
|
+
"SELECT name AS n, count(*) AS count FROM test GROUP BY name"
|
1594
1594
|
@ds.group_and_count(:name__n).sql.should ==
|
1595
|
-
"SELECT name.n, count(*) AS count FROM test GROUP BY name.n
|
1595
|
+
"SELECT name.n, count(*) AS count FROM test GROUP BY name.n"
|
1596
1596
|
end
|
1597
1597
|
|
1598
1598
|
specify "should handle identifiers" do
|
1599
1599
|
@ds.group_and_count(:name___n.identifier).sql.should ==
|
1600
|
-
"SELECT name___n, count(*) AS count FROM test GROUP BY name___n
|
1600
|
+
"SELECT name___n, count(*) AS count FROM test GROUP BY name___n"
|
1601
1601
|
end
|
1602
1602
|
|
1603
1603
|
specify "should handle literal strings" do
|
1604
1604
|
@ds.group_and_count("name".lit).sql.should ==
|
1605
|
-
"SELECT name, count(*) AS count FROM test GROUP BY name
|
1605
|
+
"SELECT name, count(*) AS count FROM test GROUP BY name"
|
1606
1606
|
end
|
1607
1607
|
|
1608
1608
|
specify "should handle aliased expressions" do
|
1609
1609
|
@ds.group_and_count(:name.as(:n)).sql.should ==
|
1610
|
-
"SELECT name AS n, count(*) AS count FROM test GROUP BY name
|
1610
|
+
"SELECT name AS n, count(*) AS count FROM test GROUP BY name"
|
1611
1611
|
@ds.group_and_count(:name.identifier.as(:n)).sql.should ==
|
1612
|
-
"SELECT name AS n, count(*) AS count FROM test GROUP BY name
|
1612
|
+
"SELECT name AS n, count(*) AS count FROM test GROUP BY name"
|
1613
1613
|
end
|
1614
1614
|
end
|
1615
1615
|
|
@@ -1632,6 +1632,59 @@ context "Dataset#empty?" do
|
|
1632
1632
|
end
|
1633
1633
|
end
|
1634
1634
|
|
1635
|
+
context "Dataset#first_source_alias" do
|
1636
|
+
before do
|
1637
|
+
@ds = Sequel::Dataset.new(nil)
|
1638
|
+
end
|
1639
|
+
|
1640
|
+
specify "should be the entire first source if not aliased" do
|
1641
|
+
@ds.from(:t).first_source_alias.should == :t
|
1642
|
+
@ds.from(:t__a.identifier).first_source_alias.should == :t__a.identifier
|
1643
|
+
@ds.from(:s__t).first_source_alias.should == :s__t
|
1644
|
+
@ds.from(:t.qualify(:s)).first_source_alias.should == :t.qualify(:s)
|
1645
|
+
end
|
1646
|
+
|
1647
|
+
specify "should be the alias if aliased" do
|
1648
|
+
@ds.from(:t___a).first_source_alias.should == :a
|
1649
|
+
@ds.from(:s__t___a).first_source_alias.should == :a
|
1650
|
+
@ds.from(:t.as(:a)).first_source_alias.should == :a
|
1651
|
+
end
|
1652
|
+
|
1653
|
+
specify "should be aliased as first_source" do
|
1654
|
+
@ds.from(:t).first_source.should == :t
|
1655
|
+
@ds.from(:t__a.identifier).first_source.should == :t__a.identifier
|
1656
|
+
@ds.from(:s__t___a).first_source.should == :a
|
1657
|
+
@ds.from(:t.as(:a)).first_source.should == :a
|
1658
|
+
end
|
1659
|
+
|
1660
|
+
specify "should raise exception if table doesn't have a source" do
|
1661
|
+
proc{@ds.first_source_alias.should == :t}.should raise_error(Sequel::Error)
|
1662
|
+
end
|
1663
|
+
end
|
1664
|
+
|
1665
|
+
context "Dataset#first_source_table" do
|
1666
|
+
before do
|
1667
|
+
@ds = Sequel::Dataset.new(nil)
|
1668
|
+
end
|
1669
|
+
|
1670
|
+
specify "should be the entire first source if not aliased" do
|
1671
|
+
@ds.from(:t).first_source_table.should == :t
|
1672
|
+
@ds.from(:t__a.identifier).first_source_table.should == :t__a.identifier
|
1673
|
+
@ds.from(:s__t).first_source_table.should == :s__t
|
1674
|
+
@ds.from(:t.qualify(:s)).first_source_table.should == :t.qualify(:s)
|
1675
|
+
end
|
1676
|
+
|
1677
|
+
specify "should be the unaliased part if aliased" do
|
1678
|
+
@ds.from(:t___a).first_source_table.should == :t.identifier
|
1679
|
+
@ds.from(:s__t___a).first_source_table.should == :t.qualify(:s)
|
1680
|
+
@ds.from(:t.as(:a)).first_source_table.should == :t
|
1681
|
+
end
|
1682
|
+
|
1683
|
+
specify "should raise exception if table doesn't have a source" do
|
1684
|
+
proc{@ds.first_source_table.should == :t}.should raise_error(Sequel::Error)
|
1685
|
+
end
|
1686
|
+
end
|
1687
|
+
|
1635
1688
|
context "Dataset#from_self" do
|
1636
1689
|
before do
|
1637
1690
|
@ds = Sequel::Dataset.new(nil).from(:test).select(:name).limit(1)
|
@@ -1673,7 +1726,7 @@ context "Dataset#join_table" do
|
|
1673
1726
|
|
1674
1727
|
specify "should handle multiple conditions on the same join table column" do
|
1675
1728
|
@d.join_table(:left_outer, :categories, [[:category_id, :id], [:category_id, 0..100]]).sql.should ==
|
1676
|
-
'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON (("categories"."category_id" = "items"."id") AND (
|
1729
|
+
'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON (("categories"."category_id" = "items"."id") AND ("categories"."category_id" >= 0) AND ("categories"."category_id" <= 100))'
|
1677
1730
|
end
|
1678
1731
|
|
1679
1732
|
specify "should include WHERE clause if applicable" do
|
@@ -2955,12 +3008,12 @@ context "Dataset#grep" do
|
|
2955
3008
|
|
2956
3009
|
specify "should support multiple search terms" do
|
2957
3010
|
@ds.grep(:title, ['abc', 'def']).sql.should ==
|
2958
|
-
"SELECT * FROM posts WHERE ((
|
3011
|
+
"SELECT * FROM posts WHERE ((title LIKE 'abc') OR (title LIKE 'def'))"
|
2959
3012
|
end
|
2960
3013
|
|
2961
3014
|
specify "should support multiple columns and search terms" do
|
2962
3015
|
@ds.grep([:title, :body], ['abc', 'def']).sql.should ==
|
2963
|
-
"SELECT * FROM posts WHERE ((
|
3016
|
+
"SELECT * FROM posts WHERE ((title LIKE 'abc') OR (title LIKE 'def') OR (body LIKE 'abc') OR (body LIKE 'def'))"
|
2964
3017
|
end
|
2965
3018
|
|
2966
3019
|
specify "should support regexps though the database may not support it" do
|
@@ -2968,7 +3021,7 @@ context "Dataset#grep" do
|
|
2968
3021
|
"SELECT * FROM posts WHERE ((title ~ 'ruby'))"
|
2969
3022
|
|
2970
3023
|
@ds.grep(:title, [/^ruby/, 'ruby']).sql.should ==
|
2971
|
-
"SELECT * FROM posts WHERE ((
|
3024
|
+
"SELECT * FROM posts WHERE ((title ~ '^ruby') OR (title LIKE 'ruby'))"
|
2972
3025
|
end
|
2973
3026
|
|
2974
3027
|
specify "should support searching against other columns" do
|
@@ -3093,7 +3146,7 @@ context "Dataset prepared statements and bound variables " do
|
|
3093
3146
|
|
3094
3147
|
specify "should handle subselects" do
|
3095
3148
|
@ds.filter(:$b).filter(:num=>@ds.select(:num).filter(:num=>:$n)).filter(:$c).call(:select, :n=>1, :b=>0, :c=>2)
|
3096
|
-
@db.sqls.should == ['SELECT * FROM items WHERE (
|
3149
|
+
@db.sqls.should == ['SELECT * FROM items WHERE (0 AND (num IN (SELECT num FROM items WHERE (num = 1))) AND 2)']
|
3097
3150
|
end
|
3098
3151
|
|
3099
3152
|
specify "should handle subselects in subselects" do
|
@@ -3716,3 +3769,21 @@ context "Modifying joined datasets" do
|
|
3716
3769
|
@ds.db.sqls.should == ['UPDATE b, c INNER JOIN d USING (id) SET a = 1 WHERE (id = 2)']
|
3717
3770
|
end
|
3718
3771
|
end
|
3772
|
+
|
3773
|
+
context "Dataset#lock_style and for_update" do
|
3774
|
+
before do
|
3775
|
+
@ds = MockDatabase.new[:t]
|
3776
|
+
end
|
3777
|
+
|
3778
|
+
specify "#for_update should use FOR UPDATE" do
|
3779
|
+
@ds.for_update.sql.should == "SELECT * FROM t FOR UPDATE"
|
3780
|
+
end
|
3781
|
+
|
3782
|
+
specify "#lock_style should accept symbols" do
|
3783
|
+
@ds.lock_style(:update).sql.should == "SELECT * FROM t FOR UPDATE"
|
3784
|
+
end
|
3785
|
+
|
3786
|
+
specify "#lock_style should accept strings for arbitrary SQL" do
|
3787
|
+
@ds.lock_style("FOR SHARE").sql.should == "SELECT * FROM t FOR SHARE"
|
3788
|
+
end
|
3789
|
+
end
|
@@ -216,7 +216,7 @@ context "Blockless Ruby Filters" do
|
|
216
216
|
it "should support AND conditions via &" do
|
217
217
|
@d.l(:x & :y).should == '(x AND y)'
|
218
218
|
@d.l(:x.sql_boolean & :y).should == '(x AND y)'
|
219
|
-
@d.l(:x & :y & :z).should == '(
|
219
|
+
@d.l(:x & :y & :z).should == '(x AND y AND z)'
|
220
220
|
@d.l(:x & {:y => :z}).should == '(x AND (y = z))'
|
221
221
|
@d.l({:y => :z} & :x).should == '((y = z) AND x)'
|
222
222
|
@d.l({:x => :a} & {:y => :z}).should == '((x = a) AND (y = z))'
|
@@ -229,7 +229,7 @@ context "Blockless Ruby Filters" do
|
|
229
229
|
it "should support OR conditions via |" do
|
230
230
|
@d.l(:x | :y).should == '(x OR y)'
|
231
231
|
@d.l(:x.sql_boolean | :y).should == '(x OR y)'
|
232
|
-
@d.l(:x | :y | :z).should == '(
|
232
|
+
@d.l(:x | :y | :z).should == '(x OR y OR z)'
|
233
233
|
@d.l(:x | {:y => :z}).should == '(x OR (y = z))'
|
234
234
|
@d.l({:y => :z} | :x).should == '((y = z) OR x)'
|
235
235
|
@d.l({:x => :a} | {:y => :z}).should == '((x = a) OR (y = z))'
|
@@ -263,7 +263,7 @@ context "Blockless Ruby Filters" do
|
|
263
263
|
@d.l('x'.lit + 1 > 100).should == '((x + 1) > 100)'
|
264
264
|
@d.l(('x'.lit * :y) < 100.01).should == '((x * y) < 100.01)'
|
265
265
|
@d.l(('x'.lit - :y/2) >= 100000000000000000000000000000000000).should == '((x - (y / 2)) >= 100000000000000000000000000000000000)'
|
266
|
-
@d.l(('z'.lit * (('x'.lit / :y)/(:x + :y))) <= 100).should == '((z * (
|
266
|
+
@d.l(('z'.lit * (('x'.lit / :y)/(:x + :y))) <= 100).should == '((z * (x / y / (x + y))) <= 100)'
|
267
267
|
@d.l(~(((('x'.lit - :y)/(:x + :y))*:z) <= 100)).should == '((((x - y) / (x + y)) * z) > 100)'
|
268
268
|
end
|
269
269
|
|
@@ -350,13 +350,13 @@ context "Blockless Ruby Filters" do
|
|
350
350
|
@d.lit([:x, 1, :y].sql_string_join(:y__z)).should == "(x || y.z || '1' || y.z || y)"
|
351
351
|
@d.lit([:x, 1, :y].sql_string_join(1)).should == "(x || '1' || '1' || '1' || y)"
|
352
352
|
@d.lit([:x, :y].sql_string_join('y.x || x.y'.lit)).should == "(x || y.x || x.y || y)"
|
353
|
-
@d.lit([[:x, :y].sql_string_join, [:a, :b].sql_string_join].sql_string_join).should == "(
|
353
|
+
@d.lit([[:x, :y].sql_string_join, [:a, :b].sql_string_join].sql_string_join).should == "(x || y || a || b)"
|
354
354
|
end
|
355
355
|
|
356
356
|
it "should support StringExpression#+ for concatenation of SQL strings" do
|
357
357
|
@d.lit(:x.sql_string + :y).should == '(x || y)'
|
358
|
-
@d.lit([:x].sql_string_join + :y).should == '(
|
359
|
-
@d.lit([:x, :z].sql_string_join(' ') + :y).should == "(
|
358
|
+
@d.lit([:x].sql_string_join + :y).should == '(x || y)'
|
359
|
+
@d.lit([:x, :z].sql_string_join(' ') + :y).should == "(x || ' ' || z || y)"
|
360
360
|
end
|
361
361
|
|
362
362
|
it "should be supported inside blocks" do
|
@@ -575,4 +575,21 @@ context Sequel::SQL::VirtualRow do
|
|
575
575
|
@d.l{date < Sequel::CURRENT_DATE}.should == "(\"date\" < CURRENT_DATE)"
|
576
576
|
@d.l{num < Math::PI.to_i}.should == "(\"num\" < 3)"
|
577
577
|
end
|
578
|
+
|
579
|
+
it "should deal with methods added to Object after requiring Sequel" do
|
580
|
+
class Object
|
581
|
+
def adsoiwemlsdaf; 42; end
|
582
|
+
end
|
583
|
+
Sequel::BasicObject.remove_methods!
|
584
|
+
@d.l{a > adsoiwemlsdaf}.should == '("a" > "adsoiwemlsdaf")'
|
585
|
+
end
|
586
|
+
|
587
|
+
it "should deal with private methods added to Kernel after requiring Sequel" do
|
588
|
+
module Kernel
|
589
|
+
private
|
590
|
+
def adsoiwemlsdaf2; 42; end
|
591
|
+
end
|
592
|
+
Sequel::BasicObject.remove_methods!
|
593
|
+
@d.l{a > adsoiwemlsdaf2}.should == '("a" > "adsoiwemlsdaf2")'
|
594
|
+
end
|
578
595
|
end
|
@@ -20,7 +20,8 @@ describe "AssociationDependencies plugin" do
|
|
20
20
|
@Artist.columns :id, :name
|
21
21
|
@Album.columns :id, :name, :artist_id
|
22
22
|
@Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id
|
23
|
-
@Artist.
|
23
|
+
@Artist.one_to_one :first_album, :class=>@Album, :key=>:artist_id, :conditions=>{:position=>1}
|
24
|
+
@Artist.many_to_many :other_artists, :class=>@Artist, :join_table=>:aoa, :left_key=>:l, :right_key=>:r
|
24
25
|
@Album.many_to_one :artist, :class=>@Artist
|
25
26
|
MODEL_DB.reset
|
26
27
|
end
|
@@ -28,7 +29,7 @@ describe "AssociationDependencies plugin" do
|
|
28
29
|
specify "should allow destroying associated many_to_one associated object" do
|
29
30
|
@Album.add_association_dependencies :artist=>:destroy
|
30
31
|
@Album.load(:id=>1, :name=>'Al', :artist_id=>2).destroy
|
31
|
-
MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'SELECT * FROM artists WHERE (artists.id = 2)', 'DELETE FROM artists WHERE (id = 2)']
|
32
|
+
MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE (id = 2)']
|
32
33
|
end
|
33
34
|
|
34
35
|
specify "should allow deleting associated many_to_one associated object" do
|
@@ -36,6 +37,18 @@ describe "AssociationDependencies plugin" do
|
|
36
37
|
@Album.load(:id=>1, :name=>'Al', :artist_id=>2).destroy
|
37
38
|
MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'DELETE FROM artists WHERE (artists.id = 2)']
|
38
39
|
end
|
40
|
+
|
41
|
+
specify "should allow destroying associated one_to_one associated object" do
|
42
|
+
@Artist.add_association_dependencies :first_album=>:destroy
|
43
|
+
@Artist.load(:id=>2, :name=>'Ar').destroy
|
44
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums WHERE ((albums.artist_id = 2) AND (position = 1)) LIMIT 1', 'DELETE FROM albums WHERE (id = 1)', 'DELETE FROM artists WHERE (id = 2)']
|
45
|
+
end
|
46
|
+
|
47
|
+
specify "should allow deleting associated one_to_one associated object" do
|
48
|
+
@Artist.add_association_dependencies :first_album=>:delete
|
49
|
+
@Artist.load(:id=>2, :name=>'Ar').destroy
|
50
|
+
MODEL_DB.sqls.should == ['DELETE FROM albums WHERE ((albums.artist_id = 2) AND (position = 1))', 'DELETE FROM artists WHERE (id = 2)']
|
51
|
+
end
|
39
52
|
|
40
53
|
specify "should allow destroying associated one_to_many objects" do
|
41
54
|
@Artist.add_association_dependencies :albums=>:destroy
|
@@ -48,6 +61,12 @@ describe "AssociationDependencies plugin" do
|
|
48
61
|
@Artist.load(:id=>2, :name=>'Ar').destroy
|
49
62
|
MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (albums.artist_id = 2)', 'DELETE FROM artists WHERE (id = 2)']
|
50
63
|
end
|
64
|
+
|
65
|
+
specify "should allow nullifying associated one_to_one objects" do
|
66
|
+
@Artist.add_association_dependencies :first_album=>:nullify
|
67
|
+
@Artist.load(:id=>2, :name=>'Ar').destroy
|
68
|
+
MODEL_DB.sqls.should == ['UPDATE albums SET artist_id = NULL WHERE ((artist_id = 2) AND (position = 1))', 'DELETE FROM artists WHERE (id = 2)']
|
69
|
+
end
|
51
70
|
|
52
71
|
specify "should allow nullifying associated one_to_many objects" do
|
53
72
|
@Artist.add_association_dependencies :albums=>:nullify
|
@@ -86,14 +105,14 @@ describe "AssociationDependencies plugin" do
|
|
86
105
|
specify "should allow specifying association dependencies in the plugin call" do
|
87
106
|
@Album.plugin :association_dependencies, :artist=>:destroy
|
88
107
|
@Album.load(:id=>1, :name=>'Al', :artist_id=>2).destroy
|
89
|
-
MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'SELECT * FROM artists WHERE (artists.id = 2)', 'DELETE FROM artists WHERE (id = 2)']
|
108
|
+
MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE (id = 2)']
|
90
109
|
end
|
91
110
|
|
92
111
|
specify "should work with subclasses" do
|
93
112
|
c = Class.new(@Album)
|
94
113
|
c.add_association_dependencies :artist=>:destroy
|
95
114
|
c.load(:id=>1, :name=>'Al', :artist_id=>2).destroy
|
96
|
-
MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'SELECT * FROM artists WHERE (artists.id = 2)', 'DELETE FROM artists WHERE (id = 2)']
|
115
|
+
MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE (id = 2)']
|
97
116
|
MODEL_DB.reset
|
98
117
|
|
99
118
|
@Album.load(:id=>1, :name=>'Al', :artist_id=>2).destroy
|
@@ -103,6 +122,6 @@ describe "AssociationDependencies plugin" do
|
|
103
122
|
@Album.add_association_dependencies :artist=>:destroy
|
104
123
|
c2 = Class.new(@Album)
|
105
124
|
c2.load(:id=>1, :name=>'Al', :artist_id=>2).destroy
|
106
|
-
MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'SELECT * FROM artists WHERE (artists.id = 2)', 'DELETE FROM artists WHERE (id = 2)']
|
125
|
+
MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE (id = 2)']
|
107
126
|
end
|
108
127
|
end
|