sequel 3.37.0 → 3.38.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.
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