sequel 4.24.0 → 4.25.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 (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