sequel 3.9.0 → 3.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/CHANGELOG +56 -0
  2. data/README.rdoc +1 -1
  3. data/Rakefile +1 -1
  4. data/doc/advanced_associations.rdoc +7 -10
  5. data/doc/release_notes/3.10.0.txt +286 -0
  6. data/lib/sequel/adapters/do/mysql.rb +4 -0
  7. data/lib/sequel/adapters/jdbc.rb +5 -0
  8. data/lib/sequel/adapters/jdbc/as400.rb +58 -0
  9. data/lib/sequel/adapters/jdbc/oracle.rb +30 -0
  10. data/lib/sequel/adapters/shared/mssql.rb +23 -9
  11. data/lib/sequel/adapters/shared/mysql.rb +12 -1
  12. data/lib/sequel/adapters/shared/postgres.rb +7 -18
  13. data/lib/sequel/adapters/shared/sqlite.rb +5 -0
  14. data/lib/sequel/adapters/sqlite.rb +5 -0
  15. data/lib/sequel/connection_pool/single.rb +3 -3
  16. data/lib/sequel/database.rb +3 -2
  17. data/lib/sequel/dataset.rb +6 -5
  18. data/lib/sequel/dataset/convenience.rb +3 -3
  19. data/lib/sequel/dataset/query.rb +13 -0
  20. data/lib/sequel/dataset/sql.rb +31 -1
  21. data/lib/sequel/extensions/schema_dumper.rb +3 -3
  22. data/lib/sequel/model.rb +8 -6
  23. data/lib/sequel/model/associations.rb +144 -102
  24. data/lib/sequel/model/base.rb +21 -1
  25. data/lib/sequel/model/plugins.rb +3 -1
  26. data/lib/sequel/plugins/association_dependencies.rb +14 -7
  27. data/lib/sequel/plugins/caching.rb +4 -0
  28. data/lib/sequel/plugins/composition.rb +138 -0
  29. data/lib/sequel/plugins/identity_map.rb +2 -2
  30. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  31. data/lib/sequel/plugins/nested_attributes.rb +3 -2
  32. data/lib/sequel/plugins/rcte_tree.rb +281 -0
  33. data/lib/sequel/plugins/typecast_on_load.rb +16 -5
  34. data/lib/sequel/sql.rb +18 -1
  35. data/lib/sequel/version.rb +1 -1
  36. data/spec/adapters/mssql_spec.rb +4 -0
  37. data/spec/adapters/mysql_spec.rb +4 -0
  38. data/spec/adapters/postgres_spec.rb +55 -5
  39. data/spec/core/database_spec.rb +5 -3
  40. data/spec/core/dataset_spec.rb +86 -15
  41. data/spec/core/expression_filters_spec.rb +23 -6
  42. data/spec/extensions/association_dependencies_spec.rb +24 -5
  43. data/spec/extensions/association_proxies_spec.rb +3 -0
  44. data/spec/extensions/composition_spec.rb +194 -0
  45. data/spec/extensions/identity_map_spec.rb +16 -0
  46. data/spec/extensions/nested_attributes_spec.rb +44 -1
  47. data/spec/extensions/rcte_tree_spec.rb +205 -0
  48. data/spec/extensions/schema_dumper_spec.rb +6 -0
  49. data/spec/extensions/spec_helper.rb +6 -0
  50. data/spec/extensions/typecast_on_load_spec.rb +9 -0
  51. data/spec/extensions/validation_helpers_spec.rb +5 -5
  52. data/spec/integration/dataset_test.rb +13 -9
  53. data/spec/integration/eager_loader_test.rb +56 -1
  54. data/spec/integration/model_test.rb +8 -0
  55. data/spec/integration/plugin_test.rb +270 -0
  56. data/spec/integration/schema_test.rb +1 -1
  57. data/spec/model/associations_spec.rb +541 -118
  58. data/spec/model/eager_loading_spec.rb +24 -3
  59. data/spec/model/record_spec.rb +34 -0
  60. 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
- o = super
50
- typecast_on_load_columns.each do |c|
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
- o.send("#{c}=", v)
57
+ send("#{c}=", v)
53
58
  end
54
59
  end
55
- o.changed_columns.clear
56
- o
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
@@ -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
- (instance_methods - %w"__id__ __send__ instance_eval == equal?").each{|m| undef_method(m)}
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
@@ -1,6 +1,6 @@
1
1
  module Sequel
2
2
  MAJOR = 3
3
- MINOR = 9
3
+ MINOR = 10
4
4
  TINY = 0
5
5
 
6
6
  VERSION = [MAJOR, MINOR, TINY].join('.')
@@ -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
@@ -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]
@@ -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 == nil
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 == nil
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 == nil
815
+ Sequel.ccc('db', :host=>'localhost', &p).should == returnValue
814
816
  CCC::DISCONNECTS.should == [z, y, x]
815
817
  end
816
818
 
@@ -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 (((id < 7) OR (id > 11)) OR (id >= 6))'
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 (((id != 1) OR (x != 3)) OR (id >= 6))'
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 ORDER BY count"
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 ORDER BY count"
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 ORDER BY count"
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 ORDER BY count"
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 ORDER BY count"
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 ORDER BY count"
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 ORDER BY count"
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 ORDER BY count"
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 (("categories"."category_id" >= 0) AND ("categories"."category_id" <= 100)))'
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 (((title LIKE 'abc') OR (title LIKE 'def')))"
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 (((title LIKE 'abc') OR (title LIKE 'def')) OR ((body LIKE 'abc') OR (body LIKE 'def')))"
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 (((title ~ '^ruby') OR (title LIKE 'ruby')))"
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 ((0 AND (num IN (SELECT num FROM items WHERE (num = 1)))) AND 2)']
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 == '((x AND y) AND z)'
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 == '((x OR y) OR z)'
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 * ((x / y) / (x + y))) <= 100)'
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 == "((x || y) || (a || b))"
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 == '((x) || y)'
359
- @d.lit([:x, :z].sql_string_join(' ') + :y).should == "((x || ' ' || z) || y)"
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.many_to_many :other_artists, :class=>@artist, :join_table=>:aoa, :left_key=>:l, :right_key=>:r
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