sequel 4.24.0 → 4.25.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +40 -0
  3. data/doc/association_basics.rdoc +2 -5
  4. data/doc/dataset_basics.rdoc +1 -1
  5. data/doc/postgresql.rdoc +47 -0
  6. data/doc/querying.rdoc +5 -0
  7. data/doc/release_notes/4.25.0.txt +181 -0
  8. data/lib/sequel/adapters/ibmdb.rb +0 -28
  9. data/lib/sequel/adapters/shared/db2.rb +31 -2
  10. data/lib/sequel/adapters/shared/mssql.rb +12 -12
  11. data/lib/sequel/adapters/shared/postgres.rb +102 -3
  12. data/lib/sequel/adapters/shared/sqlite.rb +1 -0
  13. data/lib/sequel/adapters/swift/sqlite.rb +12 -0
  14. data/lib/sequel/database/schema_generator.rb +4 -0
  15. data/lib/sequel/database/schema_methods.rb +3 -1
  16. data/lib/sequel/dataset/actions.rb +1 -1
  17. data/lib/sequel/dataset/prepared_statements.rb +15 -7
  18. data/lib/sequel/dataset/query.rb +16 -2
  19. data/lib/sequel/dataset/sql.rb +19 -16
  20. data/lib/sequel/extensions/empty_array_consider_nulls.rb +35 -0
  21. data/lib/sequel/extensions/empty_array_ignore_nulls.rb +3 -34
  22. data/lib/sequel/extensions/pg_json_ops.rb +9 -1
  23. data/lib/sequel/extensions/query_literals.rb +1 -1
  24. data/lib/sequel/model/base.rb +7 -11
  25. data/lib/sequel/model/dataset_module.rb +1 -1
  26. data/lib/sequel/plugins/association_pks.rb +6 -0
  27. data/lib/sequel/plugins/dirty.rb +6 -1
  28. data/lib/sequel/plugins/inverted_subsets.rb +48 -0
  29. data/lib/sequel/plugins/serialization.rb +2 -0
  30. data/lib/sequel/plugins/singular_table_names.rb +31 -0
  31. data/lib/sequel/plugins/static_cache.rb +17 -0
  32. data/lib/sequel/sql.rb +1 -0
  33. data/lib/sequel/version.rb +1 -1
  34. data/spec/adapters/db2_spec.rb +12 -0
  35. data/spec/adapters/mysql_spec.rb +1 -0
  36. data/spec/adapters/postgres_spec.rb +41 -1
  37. data/spec/core/database_spec.rb +1 -0
  38. data/spec/core/dataset_spec.rb +55 -7
  39. data/spec/core/expression_filters_spec.rb +18 -0
  40. data/spec/core/schema_spec.rb +10 -2
  41. data/spec/extensions/association_pks_spec.rb +12 -0
  42. data/spec/extensions/{empty_array_ignore_nulls_spec.rb → empty_array_consider_nulls_spec.rb} +7 -7
  43. data/spec/extensions/inverted_subsets_spec.rb +33 -0
  44. data/spec/extensions/query_literals_spec.rb +16 -0
  45. data/spec/extensions/serialization_spec.rb +21 -0
  46. data/spec/extensions/singular_table_names_spec.rb +22 -0
  47. data/spec/integration/dataset_test.rb +2 -1
  48. data/spec/integration/prepared_statement_test.rb +35 -1
  49. data/spec/model/associations_spec.rb +2 -2
  50. data/spec/model/base_spec.rb +13 -8
  51. data/spec/model/class_dataset_methods_spec.rb +1 -0
  52. metadata +10 -5
  53. data/lib/sequel/adapters/firebird.rb +0 -105
  54. data/lib/sequel/adapters/informix.rb +0 -68
@@ -45,7 +45,7 @@ module Sequel
45
45
  # Note that if you pass a block to these methods, it will use the default
46
46
  # implementation without the special literal handling.
47
47
  module QueryLiterals
48
- %w'select select_append select_group select_more group group_and_count order order_append order_more'.each do |m|
48
+ %w'select select_append select_group select_more group group_and_count group_append order order_append order_more'.each do |m|
49
49
  class_eval(<<-END, __FILE__, __LINE__ + 1)
50
50
  def #{m}(*args)
51
51
  if !block_given? && (l = query_literal(args))
@@ -508,16 +508,12 @@ module Sequel
508
508
  end
509
509
  subclass.instance_variable_set(iv, sup_class_value)
510
510
  end
511
- unless ivs.include?("@dataset")
512
- if self == Model || !@dataset
513
- n = subclass.name
514
- unless n.nil? || n.empty?
515
- db
516
- subclass.set_dataset(subclass.implicit_table_name) rescue nil
517
- end
518
- elsif @dataset
519
- subclass.set_dataset(@dataset.clone, :inherited=>true) rescue nil
520
- end
511
+
512
+ if @dataset && self != Model
513
+ subclass.set_dataset(@dataset.clone, :inherited=>true) rescue nil
514
+ elsif (n = subclass.name) && !n.to_s.empty?
515
+ db
516
+ subclass.set_dataset(subclass.implicit_table_name) rescue nil
521
517
  end
522
518
  end
523
519
 
@@ -756,7 +752,7 @@ module Sequel
756
752
  # dataset methods that accept arguments, you should use define a
757
753
  # method directly inside a #dataset_module block.
758
754
  def subset(name, *args, &block)
759
- dataset_module.subset(name, *args, &block)
755
+ def_dataset_method(name){filter(*args, &block)}
760
756
  end
761
757
 
762
758
  # Returns name of primary table for the dataset. If the table for the dataset
@@ -15,7 +15,7 @@ module Sequel
15
15
  # Define a named filter for this dataset, see
16
16
  # Model.subset for details.
17
17
  def subset(name, *args, &block)
18
- define_method(name){filter(*args, &block)}
18
+ @model.subset(name, *args, &block)
19
19
  end
20
20
 
21
21
  private
@@ -154,6 +154,12 @@ module Sequel
154
154
  super
155
155
  end
156
156
 
157
+ # Clear the associated pks if explicitly refreshing.
158
+ def refresh
159
+ @_association_pks = nil
160
+ super
161
+ end
162
+
157
163
  private
158
164
 
159
165
  # Return the primary keys of the associated objects.
@@ -38,7 +38,12 @@ module Sequel
38
38
  # artist.update(:name=>'Bar')
39
39
  # artist.column_changes # => {}
40
40
  # artist.previous_changes # => {:name=>['Foo', 'Bar']}
41
- #
41
+ #
42
+ # There is one caveat; when used with a column that also uses the
43
+ # serialization plugin, setting the column back to its original value
44
+ # after changing it is not correctly detected and will leave an entry
45
+ # in changed_columns.
46
+ #
42
47
  # Usage:
43
48
  #
44
49
  # # Make all model subclass instances record previous values (called before loading subclasses)
@@ -0,0 +1,48 @@
1
+ module Sequel
2
+ module Plugins
3
+ # The inverted_subsets plugin adds another method for each defined
4
+ # subset, which inverts the condition supplied. By default, inverted
5
+ # subset method names are prefixed with not_.
6
+ #
7
+ # You can change the prefix, or indeed entirely customise the inverted names,
8
+ # by passing a block to the plugin configuration:
9
+ #
10
+ # # Use an exclude_ prefix for inverted subsets instead of not_
11
+ # Album.plugin(:inverted_subsets){|name| "exclude_#{name}"}
12
+ #
13
+ # Usage:
14
+ #
15
+ # # Add inverted subsets in the Album class
16
+ # Album.plugin :inverted_subsets
17
+ #
18
+ # # This will now create two methods, published and not_published
19
+ # Album.subset :published, :published => true
20
+ #
21
+ # Album.published.sql
22
+ # # SELECT * FROM albums WHERE (published IS TRUE)
23
+ #
24
+ # Album.not_published.sql
25
+ # # SELECT * FROM albums WHERE (published IS NOT TRUE)
26
+ #
27
+ module InvertedSubsets
28
+ # Default naming for inverted subsets
29
+ DEFAULT_NAME_BLOCK = lambda{|name| "not_#{name}"}
30
+
31
+ # Store the supplied block for calling later when subsets are defined, or
32
+ # create a default one if we need to.
33
+ def self.configure(model, &block)
34
+ model.instance_variable_set(:@inverted_subsets_name_block, block || DEFAULT_NAME_BLOCK)
35
+ end
36
+
37
+ module ClassMethods
38
+ Plugins.inherited_instance_variables(self, :@inverted_subsets_name_block => nil)
39
+
40
+ # Define a not_ prefixed subset which inverts the subset condition.
41
+ def subset(name, *args, &block)
42
+ super
43
+ def_dataset_method(@inverted_subsets_name_block.call(name)){exclude(*args, &block)}
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -165,6 +165,8 @@ module Sequel
165
165
  define_method("#{column}=") do |v|
166
166
  if !changed_columns.include?(column) && (new? || get_column_value(column) != v)
167
167
  changed_columns << column
168
+
169
+ will_change_column(column) if respond_to?(:will_change_column)
168
170
  end
169
171
 
170
172
  deserialized_values[column] = v
@@ -0,0 +1,31 @@
1
+ module Sequel
2
+ module Plugins
3
+ # The singular_table_names plugin changes the default
4
+ # table names for subclasses to not assume a plural version.
5
+ # By default, Sequel assumes table names for models use
6
+ # the plural versions.
7
+ #
8
+ # Note that this plugin only affects subclasses of the
9
+ # class it is loaded into, it does not affect the
10
+ # current class. So it only makes sense to load this
11
+ # into Sequel::Model itself, or a subclass of Sequel::Model
12
+ # that is created via Class.new.
13
+ #
14
+ # Usage:
15
+ #
16
+ # # Don't assume pluralized table names
17
+ # Sequel::Model.plugin :singular_table_names
18
+ module SingularTableNames
19
+ module ClassMethods
20
+ # Returns the implicit table name for the model class, which is the demodulized,
21
+ # underscored, name of the class.
22
+ #
23
+ # Artist.implicit_table_name # => :artist
24
+ # Foo::ArtistAlias.implicit_table_name # => :artist_alias
25
+ def implicit_table_name
26
+ underscore(demodulize(name)).to_sym
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -35,6 +35,23 @@ module Sequel
35
35
  # # Cache the AlbumType class statically, but return unfrozen instances
36
36
  # # that can be modified.
37
37
  # AlbumType.plugin :static_cache, :frozen=>false
38
+ #
39
+ # If you would like the speed benefits of keeping :frozen=>true but still need
40
+ # to occasionally update objects, you can side-step the before_ hooks by
41
+ # overriding the class method +static_cache_allow_modifications?+ to return true:
42
+ #
43
+ # class Model
44
+ # plugin :static_cache
45
+ #
46
+ # def self.static_cache_allow_modifications?
47
+ # true
48
+ # end
49
+ # end
50
+ #
51
+ # Now if you +#dup+ a Model object (the resulting object is not frozen), you
52
+ # will be able to update and save the duplicate.
53
+ # Note the caveats around your responsibility to update the cache still applies.
54
+ #
38
55
  module StaticCache
39
56
  # Populate the static caches when loading the plugin. Options:
40
57
  # :frozen :: Whether retrieved model objects are frozen. The default is true,
data/lib/sequel/sql.rb CHANGED
@@ -1178,6 +1178,7 @@ module Sequel
1178
1178
  include AliasMethods
1179
1179
  include CastMethods
1180
1180
  include OrderMethods
1181
+ include PatternMatchMethods
1181
1182
  include SubscriptMethods
1182
1183
 
1183
1184
  # Return a BooleanExpression with the same op and args.
@@ -3,7 +3,7 @@ module Sequel
3
3
  MAJOR = 4
4
4
  # The minor version of Sequel. Bumped for every non-patch level
5
5
  # release, generally around once a month.
6
- MINOR = 24
6
+ MINOR = 25
7
7
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
8
8
  # releases that fix regressions from previous versions.
9
9
  TINY = 0
@@ -66,6 +66,7 @@ describe Sequel::Database do
66
66
  after do
67
67
  @db.drop_table(:items)
68
68
  end
69
+
69
70
  it "should parse primary keys from the schema properly" do
70
71
  @db.create_table!(:items){Integer :number}
71
72
  @db.schema(:items).collect{|k,v| k if v[:primary_key]}.compact.must_equal []
@@ -74,6 +75,17 @@ describe Sequel::Database do
74
75
  @db.create_table!(:items){Integer :number1, :null => false; Integer :number2, :null => false; primary_key [:number1, :number2]}
75
76
  @db.schema(:items).collect{|k,v| k if v[:primary_key]}.compact.must_equal [:number1, :number2]
76
77
  end
78
+
79
+ it "should not error on alter_table operations that need REORG" do
80
+ @db.create_table!(:items) do
81
+ varchar :a
82
+ end
83
+ @db.alter_table(:items) do
84
+ add_column :b, :varchar, :null => true
85
+ set_column_allow_null :a, false
86
+ add_index :a, :unique => true
87
+ end
88
+ end
77
89
  end
78
90
 
79
91
  describe "Sequel::IBMDB.convert_smallint_to_bool" do
@@ -24,6 +24,7 @@ SQL_COMMIT = 'COMMIT'
24
24
  describe "MySQL", '#create_table' do
25
25
  before do
26
26
  @db = DB
27
+ @db.test_connection
27
28
  DB.sqls.clear
28
29
  end
29
30
  after do
@@ -11,7 +11,11 @@ def logger.method_missing(m, msg)
11
11
  end
12
12
  DB.loggers << logger
13
13
 
14
- DB.extension :pg_array, :pg_hstore, :pg_range, :pg_interval, :pg_row, :pg_inet, :pg_json, :pg_enum
14
+ DB.extension :pg_array, :pg_hstore, :pg_range, :pg_row, :pg_inet, :pg_json, :pg_enum
15
+ begin
16
+ DB.extension :pg_interval
17
+ rescue LoadError
18
+ end
15
19
 
16
20
  describe "PostgreSQL", '#create_table' do
17
21
  before do
@@ -168,6 +172,42 @@ describe "PostgreSQL views" do
168
172
  end
169
173
  end
170
174
 
175
+ describe "PostgreSQL", 'INSERT ON CONFLICT' do
176
+ before(:all) do
177
+ @db = DB
178
+ @db.create_table!(:ic_test){Integer :a; Integer :b; unique :a, :name=>:ic_test_a_uidx}
179
+ @ds = @db[:ic_test]
180
+ end
181
+ before do
182
+ @ds.delete
183
+ end
184
+ after(:all) do
185
+ @db.drop_table?(:ic_test)
186
+ end
187
+
188
+ it "Dataset#insert_ignore and insert_constraint should ignore uniqueness violations" do
189
+ @ds.insert(1, 2)
190
+ proc{@ds.insert(1, 3)}.must_raise Sequel::UniqueConstraintViolation
191
+ @ds.insert_ignore.insert(1, 3).must_equal nil
192
+ @ds.insert_conflict.insert(1, 3).must_equal nil
193
+ @ds.insert_conflict(:target=>:a).insert(1, 3).must_equal nil
194
+ @ds.insert_conflict(:constraint=>:ic_test_a_uidx).insert(1, 3).must_equal nil
195
+ @ds.all.must_equal [{:a=>1, :b=>2}]
196
+ end
197
+
198
+ it "Dataset#insert_constraint should handle upserts" do
199
+ @ds.insert(1, 2)
200
+ @ds.insert_conflict(:target=>:a, :update=>{:b=>3}).insert(1, 3).must_equal nil
201
+ @ds.all.must_equal [{:a=>1, :b=>3}]
202
+ @ds.insert_conflict(:constraint=>:ic_test_a_uidx, :update=>{:b=>4}).insert(1, 3).must_equal nil
203
+ @ds.all.must_equal [{:a=>1, :b=>4}]
204
+ @ds.insert_conflict(:constraint=>:ic_test_a_uidx, :update=>{:b=>5}, :update_where=>{:ic_test__b=>4}).insert(1, 3).must_equal nil
205
+ @ds.all.must_equal [{:a=>1, :b=>5}]
206
+ @ds.insert_conflict(:constraint=>:ic_test_a_uidx, :update=>{:b=>6}, :update_where=>{:ic_test__b=>4}).insert(1, 3).must_equal nil
207
+ @ds.all.must_equal [{:a=>1, :b=>5}]
208
+ end
209
+ end if DB.server_version >= 90500
210
+
171
211
  describe "A PostgreSQL database" do
172
212
  before(:all) do
173
213
  @db = DB
@@ -1683,6 +1683,7 @@ end
1683
1683
 
1684
1684
  describe "Database#each_server with do/jdbc adapter connection string without :adapter option" do
1685
1685
  it "should yield a separate database object for each server" do
1686
+ require 'sequel/adapters/mock'
1686
1687
  klass = Class.new(Sequel::Database)
1687
1688
  def klass.adapter_class(v)
1688
1689
  raise unless v == :jdbc
@@ -475,10 +475,10 @@ describe "Dataset#where" do
475
475
  end
476
476
 
477
477
  it "should handle all types of IN/NOT IN queries with empty arrays" do
478
- @dataset.filter(:id => []).sql.must_equal "SELECT * FROM test WHERE (id != id)"
479
- @dataset.filter([:id1, :id2] => []).sql.must_equal "SELECT * FROM test WHERE ((id1 != id1) AND (id2 != id2))"
480
- @dataset.exclude(:id => []).sql.must_equal "SELECT * FROM test WHERE (id = id)"
481
- @dataset.exclude([:id1, :id2] => []).sql.must_equal "SELECT * FROM test WHERE ((id1 = id1) AND (id2 = id2))"
478
+ @dataset.filter(:id => []).sql.must_equal "SELECT * FROM test WHERE (1 = 0)"
479
+ @dataset.filter([:id1, :id2] => []).sql.must_equal "SELECT * FROM test WHERE (1 = 0)"
480
+ @dataset.exclude(:id => []).sql.must_equal "SELECT * FROM test WHERE (1 = 1)"
481
+ @dataset.exclude([:id1, :id2] => []).sql.must_equal "SELECT * FROM test WHERE (1 = 1)"
482
482
  end
483
483
 
484
484
  it "should handle all types of IN/NOT IN queries" do
@@ -517,9 +517,9 @@ describe "Dataset#where" do
517
517
  meta_def(@dataset, :supports_multiple_column_in?){false}
518
518
  db = Sequel.mock
519
519
  d1 = db[:test].select(:id1, :id2).filter(:region=>'Asia').columns(:id1, :id2)
520
- @dataset.filter([:id1, :id2] => d1).sql.must_equal "SELECT * FROM test WHERE ((id1 != id1) AND (id2 != id2))"
520
+ @dataset.filter([:id1, :id2] => d1).sql.must_equal "SELECT * FROM test WHERE (1 = 0)"
521
521
  db.sqls.must_equal ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
522
- @dataset.exclude([:id1, :id2] => d1).sql.must_equal "SELECT * FROM test WHERE ((id1 = id1) AND (id2 = id2))"
522
+ @dataset.exclude([:id1, :id2] => d1).sql.must_equal "SELECT * FROM test WHERE (1 = 1)"
523
523
  db.sqls.must_equal ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
524
524
  end
525
525
 
@@ -875,6 +875,25 @@ describe "Dataset#group_by" do
875
875
  end
876
876
  end
877
877
 
878
+ describe "Dataset#group_append" do
879
+ before do
880
+ @d = Sequel.mock.dataset.from(:test)
881
+ end
882
+
883
+ it "should group by the given columns if no existing columns are present" do
884
+ @d.group_append(:a).sql.must_equal 'SELECT * FROM test GROUP BY a'
885
+ end
886
+
887
+ it "should add to the currently grouped columns" do
888
+ @d.group(:a).group_append(:b).sql.must_equal 'SELECT * FROM test GROUP BY a, b'
889
+ end
890
+
891
+ it "should accept a block that yields a virtual row" do
892
+ @d.group(:a).group_append{:b}.sql.must_equal 'SELECT * FROM test GROUP BY a, b'
893
+ @d.group(:a).group_append(:c){b}.sql.must_equal 'SELECT * FROM test GROUP BY a, c, b'
894
+ end
895
+ end
896
+
878
897
  describe "Dataset#as" do
879
898
  it "should set up an alias" do
880
899
  dataset = Sequel.mock.dataset.from(:test)
@@ -3405,10 +3424,15 @@ describe "Dataset default #fetch_rows, #insert, #update, #delete, #truncate, #ex
3405
3424
  proc{@ds.having(:a=>1).truncate}.must_raise(Sequel::InvalidOperation)
3406
3425
  end
3407
3426
 
3408
- it "#execute should execute the SQL on the database" do
3427
+ it "#execute should execute the SQL on the read_only database" do
3409
3428
  @ds.send(:execute, 'SELECT 1')
3410
3429
  @db.sqls.must_equal ["SELECT 1 -- read_only"]
3411
3430
  end
3431
+
3432
+ it "#execute should execute the SQL on the default database if locking is used" do
3433
+ @ds.for_update.send(:execute, 'SELECT 1')
3434
+ @db.sqls.must_equal ["SELECT 1"]
3435
+ end
3412
3436
  end
3413
3437
 
3414
3438
  describe "Dataset#with_sql_*" do
@@ -3542,6 +3566,30 @@ describe "Dataset prepared statements and bound variables " do
3542
3566
  'INSERT INTO items (num) VALUES (1) RETURNING *']
3543
3567
  end
3544
3568
 
3569
+ it "#call and #prepare should handle returning" do
3570
+ meta_def(@ds, :supports_returning?){|_| true}
3571
+ meta_def(@ds, :insert_sql){|*v| "#{super(*v)} RETURNING *" }
3572
+ meta_def(@ds, :update_sql){|*v| "#{super(*v)} RETURNING *" }
3573
+ meta_def(@ds, :delete_sql){"#{super()} RETURNING *" }
3574
+ @ds = @ds.returning
3575
+ @ds.call(:insert, {:n=>1}, :num=>:$n)
3576
+ @ds.filter(:num=>:$n).call(:update, {:n=>1, :n2=>2}, :num=>:$n2)
3577
+ @ds.filter(:num=>:$n).call(:delete, :n=>1)
3578
+ @ds.prepare(:insert, :insert_rn, :num=>:$n).call(:n=>1)
3579
+ @ds.filter(:num=>:$n).prepare(:update, :update_rn, :num=>:$n2).call(:n=>1, :n2=>2)
3580
+ @ds.filter(:num=>:$n).prepare(:delete, :delete_rn).call(:n=>1)
3581
+ @db.sqls.must_equal([
3582
+ 'INSERT INTO items (num) VALUES (1) RETURNING *',
3583
+ 'UPDATE items SET num = 2 WHERE (num = 1) RETURNING *',
3584
+ 'DELETE FROM items WHERE (num = 1) RETURNING *',
3585
+ ]*2)
3586
+ end
3587
+
3588
+ it "PreparedStatement#prepare should raise an error" do
3589
+ ps = @ds.prepare(:select, :select_n)
3590
+ proc{ps.prepare(:select, :select_n2)}.must_raise Sequel::Error
3591
+ end
3592
+
3545
3593
  it "#call should default to using :all if an invalid type is given" do
3546
3594
  @ds.filter(:num=>:$n).call(:select_all, :n=>1)
3547
3595
  @db.sqls.must_equal ['SELECT * FROM items WHERE (num = 1)']
@@ -68,8 +68,17 @@ describe "Blockless Ruby Filters" do
68
68
  @d.l{x =~ (1...5)}.must_equal '((x >= 1) AND (x < 5))'
69
69
  @d.l{x =~ [1,2,3]}.must_equal '(x IN (1, 2, 3))'
70
70
 
71
+ @d.l{(x + y) =~ 100}.must_equal '((x + y) = 100)'
72
+ @d.l{(x + y) =~ 'a'}.must_equal '((x + y) = \'a\')'
73
+ @d.l{(x + y) =~ true}.must_equal '((x + y) IS TRUE)'
74
+ @d.l{(x + y) =~ false}.must_equal '((x + y) IS FALSE)'
75
+ @d.l{(x + y) =~ nil}.must_equal '((x + y) IS NULL)'
76
+ @d.l{(x + y) =~ (1...5)}.must_equal '(((x + y) >= 1) AND ((x + y) < 5))'
77
+ @d.l{(x + y) =~ [1,2,3]}.must_equal '((x + y) IN (1, 2, 3))'
78
+
71
79
  def @d.supports_regexp?; true end
72
80
  @d.l{x =~ /blah/}.must_equal '(x ~ \'blah\')'
81
+ @d.l{(x + y) =~ /blah/}.must_equal '((x + y) ~ \'blah\')'
73
82
  end
74
83
 
75
84
  if RUBY_VERSION >= '1.9'
@@ -82,8 +91,17 @@ describe "Blockless Ruby Filters" do
82
91
  @d.l{x !~ (1...5)}.must_equal '((x < 1) OR (x >= 5))'
83
92
  @d.l{x !~ [1,2,3]}.must_equal '(x NOT IN (1, 2, 3))'
84
93
 
94
+ @d.l{(x + y) !~ 100}.must_equal '((x + y) != 100)'
95
+ @d.l{(x + y) !~ 'a'}.must_equal '((x + y) != \'a\')'
96
+ @d.l{(x + y) !~ true}.must_equal '((x + y) IS NOT TRUE)'
97
+ @d.l{(x + y) !~ false}.must_equal '((x + y) IS NOT FALSE)'
98
+ @d.l{(x + y) !~ nil}.must_equal '((x + y) IS NOT NULL)'
99
+ @d.l{(x + y) !~ (1...5)}.must_equal '(((x + y) < 1) OR ((x + y) >= 5))'
100
+ @d.l{(x + y) !~ [1,2,3]}.must_equal '((x + y) NOT IN (1, 2, 3))'
101
+
85
102
  def @d.supports_regexp?; true end
86
103
  @d.l{x !~ /blah/}.must_equal '(x !~ \'blah\')'
104
+ @d.l{(x + y) !~ /blah/}.must_equal '((x + y) !~ \'blah\')'
87
105
  end
88
106
  end
89
107