sequel 3.37.0 → 3.38.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. data/CHANGELOG +56 -0
  2. data/README.rdoc +82 -58
  3. data/Rakefile +6 -5
  4. data/bin/sequel +1 -1
  5. data/doc/active_record.rdoc +67 -52
  6. data/doc/advanced_associations.rdoc +33 -48
  7. data/doc/association_basics.rdoc +41 -51
  8. data/doc/cheat_sheet.rdoc +21 -21
  9. data/doc/core_extensions.rdoc +374 -0
  10. data/doc/dataset_basics.rdoc +5 -5
  11. data/doc/dataset_filtering.rdoc +47 -43
  12. data/doc/mass_assignment.rdoc +1 -1
  13. data/doc/migration.rdoc +4 -5
  14. data/doc/model_hooks.rdoc +3 -3
  15. data/doc/object_model.rdoc +31 -25
  16. data/doc/opening_databases.rdoc +19 -5
  17. data/doc/prepared_statements.rdoc +2 -2
  18. data/doc/querying.rdoc +109 -52
  19. data/doc/reflection.rdoc +6 -6
  20. data/doc/release_notes/3.38.0.txt +234 -0
  21. data/doc/schema_modification.rdoc +22 -13
  22. data/doc/sharding.rdoc +8 -9
  23. data/doc/sql.rdoc +154 -112
  24. data/doc/testing.rdoc +47 -7
  25. data/doc/thread_safety.rdoc +1 -1
  26. data/doc/transactions.rdoc +1 -1
  27. data/doc/validations.rdoc +1 -1
  28. data/doc/virtual_rows.rdoc +29 -43
  29. data/lib/sequel/adapters/do/postgres.rb +1 -4
  30. data/lib/sequel/adapters/jdbc.rb +14 -3
  31. data/lib/sequel/adapters/jdbc/db2.rb +9 -0
  32. data/lib/sequel/adapters/jdbc/derby.rb +41 -4
  33. data/lib/sequel/adapters/jdbc/jtds.rb +11 -0
  34. data/lib/sequel/adapters/jdbc/postgresql.rb +3 -6
  35. data/lib/sequel/adapters/mock.rb +10 -4
  36. data/lib/sequel/adapters/postgres.rb +1 -28
  37. data/lib/sequel/adapters/shared/mssql.rb +23 -13
  38. data/lib/sequel/adapters/shared/postgres.rb +46 -0
  39. data/lib/sequel/adapters/swift.rb +21 -13
  40. data/lib/sequel/adapters/swift/mysql.rb +1 -0
  41. data/lib/sequel/adapters/swift/postgres.rb +4 -5
  42. data/lib/sequel/adapters/swift/sqlite.rb +2 -1
  43. data/lib/sequel/adapters/tinytds.rb +14 -2
  44. data/lib/sequel/adapters/utils/pg_types.rb +5 -0
  45. data/lib/sequel/core.rb +29 -17
  46. data/lib/sequel/database/query.rb +1 -1
  47. data/lib/sequel/database/schema_generator.rb +3 -0
  48. data/lib/sequel/dataset/actions.rb +5 -6
  49. data/lib/sequel/dataset/query.rb +7 -7
  50. data/lib/sequel/dataset/sql.rb +5 -18
  51. data/lib/sequel/extensions/core_extensions.rb +8 -12
  52. data/lib/sequel/extensions/pg_array.rb +59 -33
  53. data/lib/sequel/extensions/pg_array_ops.rb +32 -4
  54. data/lib/sequel/extensions/pg_auto_parameterize.rb +1 -1
  55. data/lib/sequel/extensions/pg_hstore.rb +32 -17
  56. data/lib/sequel/extensions/pg_hstore_ops.rb +32 -3
  57. data/lib/sequel/extensions/pg_inet.rb +1 -2
  58. data/lib/sequel/extensions/pg_interval.rb +0 -1
  59. data/lib/sequel/extensions/pg_json.rb +41 -23
  60. data/lib/sequel/extensions/pg_range.rb +36 -11
  61. data/lib/sequel/extensions/pg_range_ops.rb +32 -4
  62. data/lib/sequel/extensions/pg_row.rb +572 -0
  63. data/lib/sequel/extensions/pg_row_ops.rb +164 -0
  64. data/lib/sequel/extensions/query.rb +3 -3
  65. data/lib/sequel/extensions/schema_dumper.rb +7 -8
  66. data/lib/sequel/extensions/select_remove.rb +1 -1
  67. data/lib/sequel/model/base.rb +1 -0
  68. data/lib/sequel/no_core_ext.rb +1 -1
  69. data/lib/sequel/plugins/pg_row.rb +121 -0
  70. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  71. data/lib/sequel/plugins/validation_helpers.rb +31 -0
  72. data/lib/sequel/sql.rb +64 -44
  73. data/lib/sequel/version.rb +1 -1
  74. data/spec/adapters/mssql_spec.rb +37 -12
  75. data/spec/adapters/mysql_spec.rb +39 -75
  76. data/spec/adapters/oracle_spec.rb +11 -11
  77. data/spec/adapters/postgres_spec.rb +414 -237
  78. data/spec/adapters/spec_helper.rb +1 -1
  79. data/spec/adapters/sqlite_spec.rb +14 -14
  80. data/spec/core/database_spec.rb +6 -6
  81. data/spec/core/dataset_spec.rb +169 -205
  82. data/spec/core/expression_filters_spec.rb +182 -295
  83. data/spec/core/object_graph_spec.rb +6 -6
  84. data/spec/core/schema_spec.rb +14 -14
  85. data/spec/core/spec_helper.rb +1 -0
  86. data/spec/{extensions/core_extensions_spec.rb → core_extensions_spec.rb} +208 -14
  87. data/spec/extensions/columns_introspection_spec.rb +5 -5
  88. data/spec/extensions/hook_class_methods_spec.rb +28 -36
  89. data/spec/extensions/many_through_many_spec.rb +4 -4
  90. data/spec/extensions/pg_array_ops_spec.rb +15 -7
  91. data/spec/extensions/pg_array_spec.rb +81 -48
  92. data/spec/extensions/pg_auto_parameterize_spec.rb +2 -2
  93. data/spec/extensions/pg_hstore_ops_spec.rb +13 -9
  94. data/spec/extensions/pg_hstore_spec.rb +66 -65
  95. data/spec/extensions/pg_inet_spec.rb +2 -4
  96. data/spec/extensions/pg_interval_spec.rb +2 -3
  97. data/spec/extensions/pg_json_spec.rb +20 -18
  98. data/spec/extensions/pg_range_ops_spec.rb +11 -4
  99. data/spec/extensions/pg_range_spec.rb +30 -7
  100. data/spec/extensions/pg_row_ops_spec.rb +48 -0
  101. data/spec/extensions/pg_row_plugin_spec.rb +45 -0
  102. data/spec/extensions/pg_row_spec.rb +323 -0
  103. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  104. data/spec/extensions/query_literals_spec.rb +11 -11
  105. data/spec/extensions/query_spec.rb +3 -3
  106. data/spec/extensions/schema_dumper_spec.rb +20 -4
  107. data/spec/extensions/schema_spec.rb +18 -41
  108. data/spec/extensions/select_remove_spec.rb +4 -4
  109. data/spec/extensions/spec_helper.rb +4 -8
  110. data/spec/extensions/to_dot_spec.rb +5 -5
  111. data/spec/extensions/validation_class_methods_spec.rb +28 -16
  112. data/spec/integration/associations_test.rb +20 -20
  113. data/spec/integration/dataset_test.rb +98 -98
  114. data/spec/integration/eager_loader_test.rb +13 -27
  115. data/spec/integration/plugin_test.rb +5 -5
  116. data/spec/integration/prepared_statement_test.rb +22 -13
  117. data/spec/integration/schema_test.rb +28 -18
  118. data/spec/integration/spec_helper.rb +1 -1
  119. data/spec/integration/timezone_test.rb +2 -2
  120. data/spec/integration/type_test.rb +15 -6
  121. data/spec/model/association_reflection_spec.rb +1 -1
  122. data/spec/model/associations_spec.rb +4 -4
  123. data/spec/model/base_spec.rb +5 -5
  124. data/spec/model/eager_loading_spec.rb +15 -15
  125. data/spec/model/model_spec.rb +32 -32
  126. data/spec/model/record_spec.rb +16 -0
  127. data/spec/model/spec_helper.rb +2 -6
  128. data/spec/model/validations_spec.rb +1 -1
  129. metadata +16 -4
@@ -51,17 +51,17 @@ describe Sequel::Dataset, " graphing" do
51
51
  ds._fetch = {:id=>1, :x=>-1, :lines_id=>2, :lines_x=>3, :y=>4, :graph_id=>5}
52
52
  ds.all.should == [{:points=>{:id=>1, :x=>-1}, :lines=>{:id=>2, :x=>3, :y=>4, :graph_id=>5}}]
53
53
 
54
- ds = @ds1.select(:id.identifier, :x.qualify(:points)).graph(@ds2, :x=>:id)
54
+ ds = @ds1.select(Sequel.identifier(:id), Sequel.qualify(:points, :x)).graph(@ds2, :x=>:id)
55
55
  ds.sql.should == 'SELECT points.id, points.x, lines.id AS lines_id, lines.x AS lines_x, lines.y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
56
56
  ds._fetch = {:id=>1, :x=>-1, :lines_id=>2, :lines_x=>3, :y=>4, :graph_id=>5}
57
57
  ds.all.should == [{:points=>{:id=>1, :x=>-1}, :lines=>{:id=>2, :x=>3, :y=>4, :graph_id=>5}}]
58
58
 
59
- ds = @ds1.select(:id.identifier.qualify(:points), :x.identifier.as(:y)).graph(@ds2, :x=>:id)
59
+ ds = @ds1.select(Sequel.identifier(:id).qualify(:points), Sequel.identifier(:x).as(:y)).graph(@ds2, :x=>:id)
60
60
  ds.sql.should == 'SELECT points.id, points.x AS y, lines.id AS lines_id, lines.x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
61
61
  ds._fetch = {:id=>1, :y=>-1, :lines_id=>2, :x=>3, :lines_y=>4, :graph_id=>5}
62
62
  ds.all.should == [{:points=>{:id=>1, :y=>-1}, :lines=>{:id=>2, :x=>3, :y=>4, :graph_id=>5}}]
63
63
 
64
- ds = @ds1.select(:id, :x.identifier.qualify(:points.identifier).as(:y.identifier)).graph(@ds2, :x=>:id)
64
+ ds = @ds1.select(:id, Sequel.identifier(:x).qualify(Sequel.identifier(:points)).as(Sequel.identifier(:y))).graph(@ds2, :x=>:id)
65
65
  ds.sql.should == 'SELECT points.id, points.x AS y, lines.id AS lines_id, lines.x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
66
66
  ds._fetch = {:id=>1, :y=>-1, :lines_id=>2, :x=>3, :lines_y=>4, :graph_id=>5}
67
67
  ds.all.should == [{:points=>{:id=>1, :y=>-1}, :lines=>{:id=>2, :x=>3, :y=>4, :graph_id=>5}}]
@@ -140,7 +140,7 @@ describe Sequel::Dataset, " graphing" do
140
140
  end
141
141
 
142
142
  it "#graph should accept a block instead of conditions and pass it to join_table" do
143
- ds = @ds1.graph(@ds2){|ja, lja, js| [[:x.qualify(ja), :id.qualify(lja)], [:y.qualify(ja), :id.qualify(lja)]]}
143
+ ds = @ds1.graph(@ds2){|ja, lja, js| [[Sequel.qualify(ja, :x), Sequel.qualify(lja, :id)], [Sequel.qualify(ja, :y), Sequel.qualify(lja, :id)]]}
144
144
  ds.sql.should == 'SELECT points.id, points.x, points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines ON ((lines.x = points.id) AND (lines.y = points.id))'
145
145
  end
146
146
 
@@ -197,7 +197,7 @@ describe Sequel::Dataset, " graphing" do
197
197
  end
198
198
 
199
199
  it "#set_graph_aliases should allow a third entry to specify an expression to use other than the default" do
200
- ds = @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>[:points, :x, 1], :y=>[:lines, :y, :random.sql_function])
200
+ ds = @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>[:points, :x, 1], :y=>[:lines, :y, Sequel.function(:random)])
201
201
  ['SELECT 1 AS x, random() AS y FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)',
202
202
  'SELECT random() AS y, 1 AS x FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
203
203
  ].should(include(ds.sql))
@@ -280,7 +280,7 @@ describe Sequel::Dataset, " graphing" do
280
280
 
281
281
  it "#graph_each should correctly map values when #set_graph_aliases is used with a third argument for each entry" do
282
282
  @db.fetch = [nil, {:x=>2,:y=>3}]
283
- @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>[:points, :z1, 2], :y=>[:lines, :z2, :random.sql_function]).all.should == [{:points=>{:z1=>2}, :lines=>{:z2=>3}}]
283
+ @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>[:points, :z1, 2], :y=>[:lines, :z2, Sequel.function(:random)]).all.should == [{:points=>{:z1=>2}, :lines=>{:z2=>3}}]
284
284
  end
285
285
 
286
286
  it "#graph_each should correctly map values when #set_graph_aliases is used with a single argument for each entry" do
@@ -13,14 +13,14 @@ describe "DB#create_table" do
13
13
  specify "should accept the table name in multiple formats" do
14
14
  @db.create_table(:cats__cats) {}
15
15
  @db.create_table("cats__cats1") {}
16
- @db.create_table(:cats__cats2.identifier) {}
17
- @db.create_table(:cats.qualify(:cats3)) {}
16
+ @db.create_table(Sequel.identifier(:cats__cats2)) {}
17
+ @db.create_table(Sequel.qualify(:cats3, :cats)) {}
18
18
  @db.sqls.should == ['CREATE TABLE cats.cats ()', 'CREATE TABLE cats__cats1 ()', 'CREATE TABLE cats__cats2 ()', 'CREATE TABLE cats3.cats ()']
19
19
  end
20
20
 
21
21
  specify "should raise an error if the table name argument is not valid" do
22
22
  proc{@db.create_table(1) {}}.should raise_error(Sequel::Error)
23
- proc{@db.create_table(:cats.as(:c)) {}}.should raise_error(Sequel::Error)
23
+ proc{@db.create_table(Sequel.as(:cats, :c)) {}}.should raise_error(Sequel::Error)
24
24
  end
25
25
 
26
26
  specify "should remove cached schema entry" do
@@ -391,7 +391,7 @@ describe "DB#create_table" do
391
391
  specify "should accept functional indexes" do
392
392
  @db.create_table(:cats) do
393
393
  integer :id
394
- index :lower.sql_function(:name)
394
+ index Sequel.function(:lower, :name)
395
395
  end
396
396
  @db.sqls.should == ["CREATE TABLE cats (id integer)", "CREATE INDEX cats_lower_name__index ON cats (lower(name))"]
397
397
  end
@@ -399,7 +399,7 @@ describe "DB#create_table" do
399
399
  specify "should accept indexes with identifiers" do
400
400
  @db.create_table(:cats) do
401
401
  integer :id
402
- index :lower__name.identifier
402
+ index Sequel.identifier(:lower__name)
403
403
  end
404
404
  @db.sqls.should == ["CREATE TABLE cats (id integer)", "CREATE INDEX cats_lower__name_index ON cats (lower__name)"]
405
405
  end
@@ -431,7 +431,7 @@ describe "DB#create_table" do
431
431
  specify "should accept unnamed constraint definitions with blocks" do
432
432
  @db.create_table(:cats) do
433
433
  integer :score
434
- check {(:x.sql_number > 0) & (:y.sql_number < 1)}
434
+ check{(x.sql_number > 0) & (y.sql_number < 1)}
435
435
  end
436
436
  @db.sqls.should == ["CREATE TABLE cats (score integer, CHECK ((x > 0) AND (y < 1)))"]
437
437
  end
@@ -460,7 +460,7 @@ describe "DB#create_table" do
460
460
 
461
461
  specify "should accept named constraint definitions with block" do
462
462
  @db.create_table(:cats) do
463
- constraint(:blah_blah) {(:x.sql_number > 0) & (:y.sql_number < 1)}
463
+ constraint(:blah_blah){(x.sql_number > 0) & (y.sql_number < 1)}
464
464
  end
465
465
  @db.sqls.should == ["CREATE TABLE cats (CONSTRAINT blah_blah CHECK ((x > 0) AND (y < 1)))"]
466
466
  end
@@ -787,7 +787,7 @@ describe "DB#alter_table" do
787
787
 
788
788
  specify "should support add_constraint with block" do
789
789
  @db.alter_table(:cats) do
790
- add_constraint(:blah_blah) {(:x.sql_number > 0) & (:y.sql_number < 1)}
790
+ add_constraint(:blah_blah){(x.sql_number > 0) & (y.sql_number < 1)}
791
791
  end
792
792
  @db.sqls.should == ["ALTER TABLE cats ADD CONSTRAINT blah_blah CHECK ((x > 0) AND (y < 1))"]
793
793
  end
@@ -1122,7 +1122,7 @@ describe "Database#create_view" do
1122
1122
  specify "should construct proper SQL with raw SQL" do
1123
1123
  @db.create_view :test, "SELECT * FROM xyz"
1124
1124
  @db.sqls.should == ['CREATE VIEW test AS SELECT * FROM xyz']
1125
- @db.create_view :test.identifier, "SELECT * FROM xyz"
1125
+ @db.create_view Sequel.identifier(:test), "SELECT * FROM xyz"
1126
1126
  @db.sqls.should == ['CREATE VIEW test AS SELECT * FROM xyz']
1127
1127
  end
1128
1128
 
@@ -1136,7 +1136,7 @@ describe "Database#create_view" do
1136
1136
  specify "should construct proper SQL with dataset" do
1137
1137
  @db.create_or_replace_view :test, @db[:items].select(:a, :b).order(:c)
1138
1138
  @db.sqls.should == ['CREATE OR REPLACE VIEW test AS SELECT a, b FROM items ORDER BY c']
1139
- @db.create_or_replace_view :test.identifier, @db[:items].select(:a, :b).order(:c)
1139
+ @db.create_or_replace_view Sequel.identifier(:test), @db[:items].select(:a, :b).order(:c)
1140
1140
  @db.sqls.should == ['CREATE OR REPLACE VIEW test AS SELECT a, b FROM items ORDER BY c']
1141
1141
  end
1142
1142
  end
@@ -1148,9 +1148,9 @@ describe "Database#drop_view" do
1148
1148
 
1149
1149
  specify "should construct proper SQL" do
1150
1150
  @db.drop_view :test
1151
- @db.drop_view :test.identifier
1151
+ @db.drop_view Sequel.identifier(:test)
1152
1152
  @db.drop_view :sch__test
1153
- @db.drop_view :test.qualify(:sch)
1153
+ @db.drop_view Sequel.qualify(:sch, :test)
1154
1154
  @db.sqls.should == ['DROP VIEW test', 'DROP VIEW test', 'DROP VIEW sch.test', 'DROP VIEW sch.test']
1155
1155
  end
1156
1156
 
@@ -1243,11 +1243,11 @@ describe "Schema Parser" do
1243
1243
  s1 = @db.schema(:x)
1244
1244
  s1.should == [['x', {:db_type=>'x', :ruby_default=>nil}]]
1245
1245
  @db.schema(:x).object_id.should == s1.object_id
1246
- @db.schema(:x.identifier).object_id.should == s1.object_id
1246
+ @db.schema(Sequel.identifier(:x)).object_id.should == s1.object_id
1247
1247
  s2 = @db.schema(:x__y)
1248
1248
  s2.should == [['y', {:db_type=>'y', :ruby_default=>nil}]]
1249
1249
  @db.schema(:x__y).object_id.should == s2.object_id
1250
- @db.schema(:y.qualify(:x)).object_id.should == s2.object_id
1250
+ @db.schema(Sequel.qualify(:x, :y)).object_id.should == s2.object_id
1251
1251
  end
1252
1252
 
1253
1253
  specify "should correctly parse all supported data types" do
@@ -2,6 +2,7 @@ require 'rubygems'
2
2
 
3
3
  unless Object.const_defined?('Sequel')
4
4
  $:.unshift(File.join(File.dirname(File.expand_path(__FILE__)), "../../lib/"))
5
+ SEQUEL_NO_CORE_EXTENSIONS = true
5
6
  require 'sequel/core'
6
7
  end
7
8
 
@@ -1,24 +1,25 @@
1
- require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper')
1
+ require 'rubygems'
2
2
 
3
- describe "Sequel core extensions" do
4
- specify "should not be used inside Sequel, to insure Sequel works without them" do
5
- usage = []
6
- match_re = /(\.(sql_value_list|sql_array|sql_expr|sql_negate|sql_or|sql_string_join|lit|to_sequel_blob|case)\W)|:\w+\.(qualify|identifier|as|cast|asc|desc|sql_subscript|\*|sql_function)\W/
7
- comment_re = /^\s*#|# core_sql/
8
- Dir['lib/sequel/**/*.rb'].each do |f|
9
- lines = File.read(f).split("\n").grep(match_re).delete_if{|l| l =~ comment_re}
10
- usage << [f, lines] unless lines.empty?
11
- end
12
- puts usage unless usage.empty?
13
- usage.should be_empty
14
- end
3
+ unless Object.const_defined?('Sequel') && Sequel.const_defined?('Model')
4
+ $:.unshift(File.join(File.dirname(File.expand_path(__FILE__)), "../../lib/"))
5
+ require 'sequel/no_core_ext'
6
+ end
7
+
8
+ Sequel.quote_identifiers = false
9
+ Sequel.identifier_input_method = nil
10
+ Sequel.identifier_output_method = nil
11
+
12
+ Regexp.send(:include, Sequel::SQL::StringMethods)
13
+ String.send(:include, Sequel::SQL::StringMethods)
14
+ Sequel.extension :core_extensions
15
15
 
16
+ describe "Sequel core extensions" do
16
17
  specify "should have Sequel.core_extensions? be true if enabled" do
17
18
  Sequel.core_extensions?.should be_true
18
19
  end
19
20
  end
20
21
 
21
- describe "Array and Hash extensions" do
22
+ describe "Core extensions" do
22
23
  before do
23
24
  db = Sequel::Database.new
24
25
  @d = db[:items]
@@ -30,6 +31,150 @@ describe "Array and Hash extensions" do
30
31
  end
31
32
  end
32
33
 
34
+ if RUBY_VERSION < '1.9.0'
35
+ it "should not allow inequality operations on true, false, or nil" do
36
+ @d.lit(:x > 1).should == "(x > 1)"
37
+ @d.lit(:x < true).should == "(x < 't')"
38
+ @d.lit(:x >= false).should == "(x >= 'f')"
39
+ @d.lit(:x <= nil).should == "(x <= NULL)"
40
+ end
41
+
42
+ it "should not allow inequality operations on boolean complex expressions" do
43
+ @d.lit(:x > (:y > 5)).should == "(x > (y > 5))"
44
+ @d.lit(:x < (:y < 5)).should == "(x < (y < 5))"
45
+ @d.lit(:x >= (:y >= 5)).should == "(x >= (y >= 5))"
46
+ @d.lit(:x <= (:y <= 5)).should == "(x <= (y <= 5))"
47
+ @d.lit(:x > {:y => nil}).should == "(x > (y IS NULL))"
48
+ @d.lit(:x < ~{:y => nil}).should == "(x < (y IS NOT NULL))"
49
+ @d.lit(:x >= {:y => 5}).should == "(x >= (y = 5))"
50
+ @d.lit(:x <= ~{:y => 5}).should == "(x <= (y != 5))"
51
+ @d.lit(:x >= {:y => [1,2,3]}).should == "(x >= (y IN (1, 2, 3)))"
52
+ @d.lit(:x <= ~{:y => [1,2,3]}).should == "(x <= (y NOT IN (1, 2, 3)))"
53
+ end
54
+
55
+ it "should support >, <, >=, and <= via Symbol#>,<,>=,<=" do
56
+ @d.l(:x > 100).should == '(x > 100)'
57
+ @d.l(:x < 100.01).should == '(x < 100.01)'
58
+ @d.l(:x >= 100000000000000000000000000000000000).should == '(x >= 100000000000000000000000000000000000)'
59
+ @d.l(:x <= 100).should == '(x <= 100)'
60
+ end
61
+
62
+ it "should support negation of >, <, >=, and <= via Symbol#~" do
63
+ @d.l(~(:x > 100)).should == '(x <= 100)'
64
+ @d.l(~(:x < 100.01)).should == '(x >= 100.01)'
65
+ @d.l(~(:x >= 100000000000000000000000000000000000)).should == '(x < 100000000000000000000000000000000000)'
66
+ @d.l(~(:x <= 100)).should == '(x > 100)'
67
+ end
68
+
69
+ it "should support double negation via ~" do
70
+ @d.l(~~(:x > 100)).should == '(x > 100)'
71
+ end
72
+ end
73
+ it "should support NOT via Symbol#~" do
74
+ @d.l(~:x).should == 'NOT x'
75
+ @d.l(~:x__y).should == 'NOT x.y'
76
+ end
77
+
78
+ it "should support + - * / via Symbol#+,-,*,/" do
79
+ @d.l(:x + 1 > 100).should == '((x + 1) > 100)'
80
+ @d.l((:x * :y) < 100.01).should == '((x * y) < 100.01)'
81
+ @d.l((:x - :y/2) >= 100000000000000000000000000000000000).should == '((x - (y / 2)) >= 100000000000000000000000000000000000)'
82
+ @d.l((((:x - :y)/(:x + :y))*:z) <= 100).should == '((((x - y) / (x + y)) * z) <= 100)'
83
+ @d.l(~((((:x - :y)/(:x + :y))*:z) <= 100)).should == '((((x - y) / (x + y)) * z) > 100)'
84
+ end
85
+
86
+ it "should support LIKE via Symbol#like" do
87
+ @d.l(:x.like('a')).should == '(x LIKE \'a\')'
88
+ @d.l(:x.like(/a/)).should == '(x ~ \'a\')'
89
+ @d.l(:x.like('a', 'b')).should == '((x LIKE \'a\') OR (x LIKE \'b\'))'
90
+ @d.l(:x.like(/a/, /b/i)).should == '((x ~ \'a\') OR (x ~* \'b\'))'
91
+ @d.l(:x.like('a', /b/)).should == '((x LIKE \'a\') OR (x ~ \'b\'))'
92
+
93
+ @d.l('a'.like(:x)).should == "('a' LIKE x)"
94
+ @d.l('a'.like(:x, 'b')).should == "(('a' LIKE x) OR ('a' LIKE 'b'))"
95
+ @d.l('a'.like(:x, /b/)).should == "(('a' LIKE x) OR ('a' ~ 'b'))"
96
+ @d.l('a'.like(:x, /b/i)).should == "(('a' LIKE x) OR ('a' ~* 'b'))"
97
+
98
+ @d.l(/a/.like(:x)).should == "('a' ~ x)"
99
+ @d.l(/a/.like(:x, 'b')).should == "(('a' ~ x) OR ('a' ~ 'b'))"
100
+ @d.l(/a/.like(:x, /b/)).should == "(('a' ~ x) OR ('a' ~ 'b'))"
101
+ @d.l(/a/.like(:x, /b/i)).should == "(('a' ~ x) OR ('a' ~* 'b'))"
102
+
103
+ @d.l(/a/i.like(:x)).should == "('a' ~* x)"
104
+ @d.l(/a/i.like(:x, 'b')).should == "(('a' ~* x) OR ('a' ~* 'b'))"
105
+ @d.l(/a/i.like(:x, /b/)).should == "(('a' ~* x) OR ('a' ~* 'b'))"
106
+ @d.l(/a/i.like(:x, /b/i)).should == "(('a' ~* x) OR ('a' ~* 'b'))"
107
+ end
108
+
109
+ it "should support NOT LIKE via Symbol#like and Symbol#~" do
110
+ @d.l(~:x.like('a')).should == '(x NOT LIKE \'a\')'
111
+ @d.l(~:x.like(/a/)).should == '(x !~ \'a\')'
112
+ @d.l(~:x.like('a', 'b')).should == '((x NOT LIKE \'a\') AND (x NOT LIKE \'b\'))'
113
+ @d.l(~:x.like(/a/, /b/i)).should == '((x !~ \'a\') AND (x !~* \'b\'))'
114
+ @d.l(~:x.like('a', /b/)).should == '((x NOT LIKE \'a\') AND (x !~ \'b\'))'
115
+
116
+ @d.l(~'a'.like(:x)).should == "('a' NOT LIKE x)"
117
+ @d.l(~'a'.like(:x, 'b')).should == "(('a' NOT LIKE x) AND ('a' NOT LIKE 'b'))"
118
+ @d.l(~'a'.like(:x, /b/)).should == "(('a' NOT LIKE x) AND ('a' !~ 'b'))"
119
+ @d.l(~'a'.like(:x, /b/i)).should == "(('a' NOT LIKE x) AND ('a' !~* 'b'))"
120
+
121
+ @d.l(~/a/.like(:x)).should == "('a' !~ x)"
122
+ @d.l(~/a/.like(:x, 'b')).should == "(('a' !~ x) AND ('a' !~ 'b'))"
123
+ @d.l(~/a/.like(:x, /b/)).should == "(('a' !~ x) AND ('a' !~ 'b'))"
124
+ @d.l(~/a/.like(:x, /b/i)).should == "(('a' !~ x) AND ('a' !~* 'b'))"
125
+
126
+ @d.l(~/a/i.like(:x)).should == "('a' !~* x)"
127
+ @d.l(~/a/i.like(:x, 'b')).should == "(('a' !~* x) AND ('a' !~* 'b'))"
128
+ @d.l(~/a/i.like(:x, /b/)).should == "(('a' !~* x) AND ('a' !~* 'b'))"
129
+ @d.l(~/a/i.like(:x, /b/i)).should == "(('a' !~* x) AND ('a' !~* 'b'))"
130
+ end
131
+
132
+ it "should support ILIKE via Symbol#ilike" do
133
+ @d.l(:x.ilike('a')).should == '(x ILIKE \'a\')'
134
+ @d.l(:x.ilike(/a/)).should == '(x ~* \'a\')'
135
+ @d.l(:x.ilike('a', 'b')).should == '((x ILIKE \'a\') OR (x ILIKE \'b\'))'
136
+ @d.l(:x.ilike(/a/, /b/i)).should == '((x ~* \'a\') OR (x ~* \'b\'))'
137
+ @d.l(:x.ilike('a', /b/)).should == '((x ILIKE \'a\') OR (x ~* \'b\'))'
138
+
139
+ @d.l('a'.ilike(:x)).should == "('a' ILIKE x)"
140
+ @d.l('a'.ilike(:x, 'b')).should == "(('a' ILIKE x) OR ('a' ILIKE 'b'))"
141
+ @d.l('a'.ilike(:x, /b/)).should == "(('a' ILIKE x) OR ('a' ~* 'b'))"
142
+ @d.l('a'.ilike(:x, /b/i)).should == "(('a' ILIKE x) OR ('a' ~* 'b'))"
143
+
144
+ @d.l(/a/.ilike(:x)).should == "('a' ~* x)"
145
+ @d.l(/a/.ilike(:x, 'b')).should == "(('a' ~* x) OR ('a' ~* 'b'))"
146
+ @d.l(/a/.ilike(:x, /b/)).should == "(('a' ~* x) OR ('a' ~* 'b'))"
147
+ @d.l(/a/.ilike(:x, /b/i)).should == "(('a' ~* x) OR ('a' ~* 'b'))"
148
+
149
+ @d.l(/a/i.ilike(:x)).should == "('a' ~* x)"
150
+ @d.l(/a/i.ilike(:x, 'b')).should == "(('a' ~* x) OR ('a' ~* 'b'))"
151
+ @d.l(/a/i.ilike(:x, /b/)).should == "(('a' ~* x) OR ('a' ~* 'b'))"
152
+ @d.l(/a/i.ilike(:x, /b/i)).should == "(('a' ~* x) OR ('a' ~* 'b'))"
153
+ end
154
+
155
+ it "should support NOT ILIKE via Symbol#ilike and Symbol#~" do
156
+ @d.l(~:x.ilike('a')).should == '(x NOT ILIKE \'a\')'
157
+ @d.l(~:x.ilike(/a/)).should == '(x !~* \'a\')'
158
+ @d.l(~:x.ilike('a', 'b')).should == '((x NOT ILIKE \'a\') AND (x NOT ILIKE \'b\'))'
159
+ @d.l(~:x.ilike(/a/, /b/i)).should == '((x !~* \'a\') AND (x !~* \'b\'))'
160
+ @d.l(~:x.ilike('a', /b/)).should == '((x NOT ILIKE \'a\') AND (x !~* \'b\'))'
161
+
162
+ @d.l(~'a'.ilike(:x)).should == "('a' NOT ILIKE x)"
163
+ @d.l(~'a'.ilike(:x, 'b')).should == "(('a' NOT ILIKE x) AND ('a' NOT ILIKE 'b'))"
164
+ @d.l(~'a'.ilike(:x, /b/)).should == "(('a' NOT ILIKE x) AND ('a' !~* 'b'))"
165
+ @d.l(~'a'.ilike(:x, /b/i)).should == "(('a' NOT ILIKE x) AND ('a' !~* 'b'))"
166
+
167
+ @d.l(~/a/.ilike(:x)).should == "('a' !~* x)"
168
+ @d.l(~/a/.ilike(:x, 'b')).should == "(('a' !~* x) AND ('a' !~* 'b'))"
169
+ @d.l(~/a/.ilike(:x, /b/)).should == "(('a' !~* x) AND ('a' !~* 'b'))"
170
+ @d.l(~/a/.ilike(:x, /b/i)).should == "(('a' !~* x) AND ('a' !~* 'b'))"
171
+
172
+ @d.l(~/a/i.ilike(:x)).should == "('a' !~* x)"
173
+ @d.l(~/a/i.ilike(:x, 'b')).should == "(('a' !~* x) AND ('a' !~* 'b'))"
174
+ @d.l(~/a/i.ilike(:x, /b/)).should == "(('a' !~* x) AND ('a' !~* 'b'))"
175
+ @d.l(~/a/i.ilike(:x, /b/i)).should == "(('a' !~* x) AND ('a' !~* 'b'))"
176
+ end
177
+
33
178
  it "should support sql_expr on arrays with all two pairs" do
34
179
  @d.l([[:x, 100],[:y, 'a']].sql_expr).should == '((x = 100) AND (y = \'a\'))'
35
180
  @d.l([[:x, true], [:y, false]].sql_expr).should == '((x IS TRUE) AND (y IS FALSE))'
@@ -429,3 +574,52 @@ describe "Symbol" do
429
574
  :abc.extract(:year).to_s(@ds).should == "extract(year FROM abc)"
430
575
  end
431
576
  end
577
+
578
+ describe "Postgres extensions integration" do
579
+ before do
580
+ @db = Sequel.mock
581
+ Sequel.extension(:pg_array, :pg_array_ops, :pg_hstore, :pg_hstore_ops, :pg_json, :pg_range, :pg_range_ops, :pg_row, :pg_row_ops)
582
+ end
583
+
584
+ it "Symbol#pg_array should return an ArrayOp" do
585
+ @db.literal(:a.pg_array.unnest).should == "unnest(a)"
586
+ end
587
+
588
+ it "Symbol#pg_row should return a PGRowOp" do
589
+ @db.literal(:a.pg_row[:a]).should == "(a).a"
590
+ end
591
+
592
+ it "Symbol#hstore should return an HStoreOp" do
593
+ @db.literal(:a.hstore['a']).should == "(a -> 'a')"
594
+ end
595
+
596
+ it "Symbol#pg_range should return a RangeOp" do
597
+ @db.literal(:a.pg_range.lower).should == "lower(a)"
598
+ end
599
+
600
+ it "Array#pg_array should return a PGArray" do
601
+ @db.literal([1].pg_array.op.unnest).should == "unnest(ARRAY[1])"
602
+ @db.literal([1].pg_array(:int4).op.unnest).should == "unnest(ARRAY[1]::int4[])"
603
+ end
604
+
605
+ it "Array#pg_json should return a JSONArray" do
606
+ @db.literal([1].pg_json).should == "'[1]'::json"
607
+ end
608
+
609
+ it "Array#pg_row should return a ArrayRow" do
610
+ @db.literal([1].pg_row).should == "ROW(1)"
611
+ end
612
+
613
+ it "Hash#hstore should return an HStore" do
614
+ @db.literal({'a'=>1}.hstore.op['a']).should == '(\'"a"=>"1"\'::hstore -> \'a\')'
615
+ end
616
+
617
+ it "Hash#pg_json should return an JSONHash" do
618
+ @db.literal({'a'=>'b'}.pg_json).should == "'{\"a\":\"b\"}'::json"
619
+ end
620
+
621
+ it "Range#pg_range should return an PGRange" do
622
+ @db.literal((1..2).pg_range).should == "'[1,2]'"
623
+ @db.literal((1..2).pg_range(:int4range)).should == "'[1,2]'::int4range"
624
+ end
625
+ end
@@ -58,19 +58,19 @@ describe "columns_introspection extension" do
58
58
  end
59
59
 
60
60
  specify "should handle SQL::Identifiers " do
61
- @ds.select(:x.identifier).columns.should == [:x]
61
+ @ds.select(Sequel.identifier(:x)).columns.should == [:x]
62
62
  @db.sqls.length.should == 0
63
63
  end
64
64
 
65
65
  specify "should handle SQL::QualifiedIdentifiers" do
66
- @ds.select(:x.qualify(:t)).columns.should == [:x]
67
- @ds.select(:x.identifier.qualify(:t)).columns.should == [:x]
66
+ @ds.select(Sequel.qualify(:t, :x)).columns.should == [:x]
67
+ @ds.select(Sequel.identifier(:x).qualify(:t)).columns.should == [:x]
68
68
  @db.sqls.length.should == 0
69
69
  end
70
70
 
71
71
  specify "should handle SQL::AliasedExpressions" do
72
- @ds.select(:x.as(:a)).columns.should == [:a]
73
- @ds.select(:x.as(:a.identifier)).columns.should == [:a]
72
+ @ds.select(Sequel.as(:x, :a)).columns.should == [:a]
73
+ @ds.select(Sequel.as(:x, Sequel.identifier(:a))).columns.should == [:a]
74
74
  @db.sqls.length.should == 0
75
75
  end
76
76
 
@@ -1,14 +1,20 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
- describe "Model hooks" do
3
+ model_class = proc do |klass, &block|
4
+ c = Class.new(klass)
5
+ c.plugin :hook_class_methods
6
+ c.class_eval(&block) if block
7
+ c
8
+ end
9
+
10
+ describe Sequel::Model, "hook_class_methods plugin" do
4
11
  before do
5
12
  MODEL_DB.reset
6
13
  end
7
14
 
8
15
  specify "should be definable using a block" do
9
16
  adds = []
10
- c = Class.new(Sequel::Model)
11
- c.class_eval do
17
+ c = model_class.call Sequel::Model do
12
18
  before_save{adds << 'hi'}
13
19
  end
14
20
 
@@ -18,8 +24,7 @@ describe "Model hooks" do
18
24
 
19
25
  specify "should be definable using a method name" do
20
26
  adds = []
21
- c = Class.new(Sequel::Model)
22
- c.class_eval do
27
+ c = model_class.call Sequel::Model do
23
28
  define_method(:bye){adds << 'bye'}
24
29
  before_save :bye
25
30
  end
@@ -30,8 +35,7 @@ describe "Model hooks" do
30
35
 
31
36
  specify "should be additive" do
32
37
  adds = []
33
- c = Class.new(Sequel::Model)
34
- c.class_eval do
38
+ c = model_class.call Sequel::Model do
35
39
  after_save{adds << 'hyiyie'}
36
40
  after_save{adds << 'byiyie'}
37
41
  end
@@ -42,8 +46,7 @@ describe "Model hooks" do
42
46
 
43
47
  specify "before hooks should run in reverse order" do
44
48
  adds = []
45
- c = Class.new(Sequel::Model)
46
- c.class_eval do
49
+ c = model_class.call Sequel::Model do
47
50
  before_save{adds << 'hyiyie'}
48
51
  before_save{adds << 'byiyie'}
49
52
  end
@@ -54,8 +57,7 @@ describe "Model hooks" do
54
57
 
55
58
  specify "should not be additive if the method or tag already exists" do
56
59
  adds = []
57
- c = Class.new(Sequel::Model)
58
- c.class_eval do
60
+ c = model_class.call Sequel::Model do
59
61
  define_method(:bye){adds << 'bye'}
60
62
  before_save :bye
61
63
  before_save :bye
@@ -65,8 +67,7 @@ describe "Model hooks" do
65
67
  adds.should == ['bye']
66
68
 
67
69
  adds = []
68
- d = Class.new(Sequel::Model)
69
- d.class_eval do
70
+ d = model_class.call Sequel::Model do
70
71
  before_save(:bye){adds << 'hyiyie'}
71
72
  before_save(:bye){adds << 'byiyie'}
72
73
  end
@@ -75,8 +76,7 @@ describe "Model hooks" do
75
76
  adds.should == ['byiyie']
76
77
 
77
78
  adds = []
78
- e = Class.new(Sequel::Model)
79
- e.class_eval do
79
+ e = model_class.call Sequel::Model do
80
80
  define_method(:bye){adds << 'bye'}
81
81
  before_save :bye
82
82
  before_save(:bye){adds << 'byiyie'}
@@ -86,8 +86,7 @@ describe "Model hooks" do
86
86
  adds.should == ['byiyie']
87
87
 
88
88
  adds = []
89
- e = Class.new(Sequel::Model)
90
- e.class_eval do
89
+ e = model_class.call Sequel::Model do
91
90
  define_method(:bye){adds << 'bye'}
92
91
  before_save(:bye){adds << 'byiyie'}
93
92
  before_save :bye
@@ -99,8 +98,7 @@ describe "Model hooks" do
99
98
 
100
99
  specify "should be inheritable" do
101
100
  adds = []
102
- a = Class.new(Sequel::Model)
103
- a.class_eval do
101
+ a = model_class.call Sequel::Model do
104
102
  after_save{adds << '123'}
105
103
  end
106
104
 
@@ -116,8 +114,7 @@ describe "Model hooks" do
116
114
 
117
115
  specify "should be overridable in descendant classes" do
118
116
  adds = []
119
- a = Class.new(Sequel::Model)
120
- a.class_eval do
117
+ a = model_class.call Sequel::Model do
121
118
  before_save{adds << '123'}
122
119
  end
123
120
 
@@ -137,8 +134,7 @@ describe "Model hooks" do
137
134
  flag = true
138
135
  adds = []
139
136
 
140
- a = Class.new(Sequel::Model)
141
- a.class_eval do
137
+ a = model_class.call Sequel::Model do
142
138
  before_save{adds << 'cruel'; flag}
143
139
  before_save{adds << 'blah'; flag}
144
140
  end
@@ -173,8 +169,7 @@ describe "Model#after_initialize" do
173
169
  values1 = nil
174
170
  reached_after_initialized = false
175
171
 
176
- a = Class.new(Sequel::Model)
177
- a.class_eval do
172
+ a = model_class.call Sequel::Model do
178
173
  columns :x, :y
179
174
  after_initialize do
180
175
  values1 = @values.clone
@@ -192,8 +187,7 @@ describe "Model#before_create && Model#after_create" do
192
187
  before do
193
188
  MODEL_DB.reset
194
189
 
195
- @c = Class.new(Sequel::Model(:items))
196
- @c.class_eval do
190
+ @c = model_class.call Sequel::Model(:items) do
197
191
  columns :x
198
192
  no_primary_key
199
193
 
@@ -226,8 +220,7 @@ describe "Model#before_update && Model#after_update" do
226
220
  before do
227
221
  MODEL_DB.reset
228
222
 
229
- @c = Class.new(Sequel::Model(:items))
230
- @c.class_eval do
223
+ @c = model_class.call(Sequel::Model(:items)) do
231
224
  after_update {MODEL_DB << "BLAH after"}
232
225
  end
233
226
  end
@@ -258,8 +251,7 @@ describe "Model#before_save && Model#after_save" do
258
251
  before do
259
252
  MODEL_DB.reset
260
253
 
261
- @c = Class.new(Sequel::Model(:items))
262
- @c.class_eval do
254
+ @c = model_class.call(Sequel::Model(:items)) do
263
255
  columns :x
264
256
  after_save {MODEL_DB << "BLAH after"}
265
257
  end
@@ -298,8 +290,7 @@ describe "Model#before_destroy && Model#after_destroy" do
298
290
  before do
299
291
  MODEL_DB.reset
300
292
 
301
- @c = Class.new(Sequel::Model(:items))
302
- @c.class_eval do
293
+ @c = model_class.call(Sequel::Model(:items)) do
303
294
  after_destroy {MODEL_DB << "BLAH after"}
304
295
  end
305
296
  end
@@ -329,8 +320,8 @@ describe "Model#before_validation && Model#after_validation" do
329
320
  before do
330
321
  MODEL_DB.reset
331
322
 
332
- @c = Class.new(Sequel::Model(:items))
333
- @c.class_eval do
323
+ @c = model_class.call(Sequel::Model(:items)) do
324
+ plugin :validation_class_methods
334
325
  after_validation{MODEL_DB << "BLAH after"}
335
326
 
336
327
  def self.validate(o)
@@ -382,7 +373,7 @@ end
382
373
 
383
374
  describe "Model.has_hooks?" do
384
375
  before do
385
- @c = Class.new(Sequel::Model(:items))
376
+ @c = model_class.call(Sequel::Model(:items))
386
377
  end
387
378
 
388
379
  specify "should return false if no hooks are defined" do
@@ -403,6 +394,7 @@ end
403
394
  describe "Model#add_hook_type" do
404
395
  before do
405
396
  class ::Foo < Sequel::Model(:items)
397
+ plugin :hook_class_methods
406
398
  add_hook_type :before_bar, :after_bar
407
399
 
408
400
  def bar