sequel 3.9.0 → 3.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|