johnbender-rquery 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -3,14 +3,28 @@ RQuery
3
3
 
4
4
  RQuery is a small DSL inspired by RSpec for building text queries in languages like SQL. It is meant to be concise, easy to read, and expressive about what the query will be asking for.
5
5
 
6
- Currently only the ActiveRecord extension is implemented with a Sqlite adapter. Mysql should follow shortly, and then support for Grove + JSON queries to Mnesia.
6
+ Currently only the ActiveRecord extension is implemented with a Sqlite adapter. Mysql should be trivial to implement, and I'm hoping to get to supporting my own project Grove.
7
7
 
8
8
  ActiveRecord
9
9
  ------------
10
10
 
11
- RQuery can extend ActiveRecord to provide the `where` method. `where` accepts a single optional argument and a block that represents the query statements
11
+ ###Setup/Config and Symbols vs Block parameters
12
12
 
13
- ###Example compared with `find`
13
+ In you're rails environment file simply require rquery. The default adapter is the included Sqlite adapter but you can created and set your own with
14
+
15
+ RQuery.adapter = class
16
+
17
+ You can view both Sql and Sqlite in the adapters directory if you are interested in writing your own (mysql?). As a side note it would be nice at some point to decide the adapter based on the db chosen for a given environment.
18
+
19
+ Also, you can choose to use symbols as the attribute names in the block or you can use an option block argument to represent an ActiveRecord object. To use the symbols make sure to add the following to your environment.rb
20
+
21
+ RQuery.use_symbols
22
+
23
+ Using the block parameter instead has two benefits. 1: you won't be poluting the Symbol objects with my hackery, and 2: RQuery will tell you when you are attempting use an attribute for the object that doesn't exist. Examples of both below.
24
+
25
+ ###Examples
26
+
27
+ RQuery extend ActiveRecord to provide the `where` method. `where` accepts a single optional argument and a block that represents the query statements
14
28
 
15
29
  In a given UsersController your `show` action might find the User like so:
16
30
 
@@ -20,6 +34,9 @@ Using RQuery:
20
34
 
21
35
  @user = User.where { :id.is == params[:id] }
22
36
 
37
+ Or
38
+
39
+ @user = User.where { |user| user.id.is == params[:id] }
23
40
 
24
41
  In the above case, RQuery doesn't provide much of an improvement over the traditional `find` method, but as the query becomes more complex its expressiveness begins to shine through:
25
42
 
@@ -33,23 +50,23 @@ RQuery:
33
50
 
34
51
  Or:
35
52
 
36
- @users = User.where do
37
- :age.between 10..20
53
+ @users = User.where do |user|
54
+ user.age.between 10..20
38
55
  end
39
56
 
40
57
  Both the `from` and `between` methods accept argument lists `10,20` or an array `[10,20]`.
41
58
 
42
-
43
59
  ###Other Examples
44
60
 
45
61
  RQuery supports most of the common SQL operations: =, <>, >, <, >=, <= as well as in, like (see below for specifics), and between. `:column.is_not` works for `.in`, `.between`, `.contains`, and `==`. All operations are anded as of the current version.
46
62
 
47
63
  Operators:
48
64
 
49
- @obj = ActiveRecordObject.where do
50
- :foo.is > 2
51
- :foo.is_not == 4
65
+ @obj = ActiveRecordObject.where do |obj|
66
+ obj.foo.is > 2
67
+ obj.foo.is_not == 4
52
68
  end
69
+
53
70
  #=> conditions array: ["foo > ? and foo <> ?", 2, 4]
54
71
 
55
72
  Contains:
@@ -62,8 +79,8 @@ Contains:
62
79
 
63
80
  In:
64
81
 
65
- @obj = ActiveRecordObject.where do
66
- :foo.in "bar", "baz", "bak"
82
+ @obj = ActiveRecordObject.where do |obj|
83
+ obj.foo.in "bar", "baz", "bak"
67
84
  end
68
85
  #=> conditions array: ["foo in (?)", ["bar", "baz", "bak"]]
69
86
  #using the default sqlite adapter
@@ -81,6 +98,36 @@ is equivalent to the find call:
81
98
 
82
99
  @obj = ActiveRecordObject.find(:first, conditions => ["foo = ?", "bar"])
83
100
 
101
+ ###Complex queries
102
+
103
+ RQuery supports relatively complex queries including | and & operation groupings. All operations need to be on the same line and in parens and either the | operator or the & operator can be used on a singel line
104
+
105
+ User.where do |user|
106
+ (mock.age.is > 20) | (mock.age.in 16,18)
107
+ end
108
+
109
+ In the following example the & takes precedence and will be grouped with the contains "Alice" which will be or'd with the contains "George"
110
+
111
+ #=> name contains "George" or (name contains "Alice and age from 20 to 30)
112
+ User.where do |user|
113
+ (user.name.contains "George") | (user.name.contains "Alice") & (use.age.from 20..30)
114
+ end
115
+
116
+ To correct the above to the more intuitive version add parens to force precedence of the contains operations
117
+
118
+ #=> (name contains "George" or name contains "Alice) and age from 20 to 30
119
+ User.where do |user|
120
+ ((user.name.contains "George") | (user.name.contains "Alice")) & (use.age.from 20..30)
121
+ end
122
+
123
+ In this sutation it would be cleaner and easier to just move the and'd statement down a line as all seperate lines are and'd and lines have precedence from top to bottom
124
+
125
+ User.where do |user|
126
+ (user.name.contains "George") | (user.name.contains "Alice")
127
+ use.age.from 20..30
128
+ end
129
+
130
+
84
131
 
85
132
 
86
133
 
data/examples/user.rb ADDED
@@ -0,0 +1,49 @@
1
+ #All operations need to be on the same line and in parens
2
+ #either the | operator or the & operator can be used on a singel line
3
+ User.where do |user|
4
+ (mock.age.is > 20) | (mock.age.in 16,18)
5
+ end
6
+
7
+ #the & takes precedence here and will be grouped with the contains "Alice" which will be
8
+ #or'd with the contains "George"
9
+ #=> name contains "George" or (name contains "Alice and age from 20 to 30)
10
+ User.where do |user|
11
+ (user.name.contains "George") | (user.name.contains "Alice") & (use.age.from 20..30)
12
+ end
13
+
14
+ #to correct the above to the more intuitive version add parens to force precedence of the
15
+ #contains operations
16
+ #=> (name contains "George" or name contains "Alice) and age from 20 to 30
17
+ User.where do |user|
18
+ ((user.name.contains "George") | (user.name.contains "Alice")) & (use.age.from 20..30)
19
+ end
20
+
21
+ #in this sutation it would be cleaner and easier to just move the and'd statement down
22
+ #a line as all seperate lines are and'd and lines have precedence from top to bottom
23
+ #additionaly operations on seperate lines don't need parens
24
+ User.where do |user|
25
+ (user.name.contains "George") | (user.name.contains "Alice")
26
+ use.age.from 20..30
27
+ end
28
+
29
+ #should you attempt to use and attribute that doesn't exist for a given model
30
+ #rquery will tell you before it's sent to the db
31
+ User.where do |user|
32
+ user.ssn.is == 123-45-6789
33
+ end
34
+ # RQuery::AttributeNotFoundError: The field 'ssn' doesn't exist for this object
35
+ # from /Users/johnbender/Projects/rquery/lib/rquery/attribute_collection.rb:28:in `method_missing'
36
+ # from (irb):24
37
+ # from /Users/johnbender/Projects/rquery/lib/rquery/active_record/base.rb:16:in `where'
38
+ # from /Users/johnbender/Projects/rquery/lib/rquery/active_record/base.rb:11:in `synchronize'
39
+ # from /Users/johnbender/Projects/rquery/lib/rquery/active_record/base.rb:11:in `where'
40
+ # from (irb):23
41
+
42
+ #environment config
43
+ RQuery.use_symbols
44
+
45
+ #example of using symbols, you can see more at the RQuery page on my site.
46
+ User.where do |user|
47
+ (:name.contains "George") | (:name.contains "Alice")
48
+ :age.from 20..30
49
+ end
@@ -1,35 +1,37 @@
1
1
  module RQuery
2
- module ActiveRecord
3
- @@where_mutex = Mutex.new
4
- def where(*args, &block)
2
+ module ActiveRecord
3
+ @@where_mutex = Mutex.new
4
+ def where(*args, &block)
5
5
 
6
- #establish scope for conditions
7
- conditions = nil
6
+ #establish scope for conditions
7
+ conditions = nil
8
8
 
9
- #make sure only one thread at a time is altering the class
10
- #variables inside RQuery::Serializers::Operations
11
- @@where_mutex.synchronize do
9
+ #make sure only one thread at a time is altering the class
10
+ #variables inside RQuery::Serializers::Operations
11
+ @@where_mutex.synchronize do
12
12
 
13
- #yielding the block will alter the Operations class
14
- yield
13
+ #Passes a new AttributeCollection object to the block
14
+ #if RQuery.use_symbols has been called it may not be used
15
+ #but otherwise will take the form attr_coll_object.attribute.is ...
16
+ yield(RQuery::AttributeCollection.new(self.new.attribute_names))
15
17
 
16
- #record altered conditions and values
17
- conditions = ::RQuery::Serializers::Operations.conditions
18
+ #record altered conditions and values
19
+ conditions = ::RQuery::Serializers::Operations.conditions
18
20
 
19
- #clear the alterations
20
- ::RQuery::Serializers::Operations.clear
21
- end
21
+ #clear the alterations
22
+ RQuery::Serializers::Operations.clear
23
+ end
22
24
 
23
- #limit the records returned (:first, :all, :last)
24
- limit = args.first ? args.first : :all
25
+ #limit the records returned (:first, :all, :last)
26
+ limit = args.first ? args.first : :all
25
27
 
26
- #call find with the conditions and values provided by the block
27
- find(limit, :conditions => conditions)
28
- end
28
+ #call find with the conditions and values provided by the block
29
+ find(limit, :conditions => conditions)
29
30
  end
31
+ end
30
32
  end
31
33
 
32
34
  #extend ActiveRecord::Base with the where method
33
35
  if Object.const_defined?(:ActiveRecord)
34
- ActiveRecord::Base.send :extend, RQuery::ActiveRecord
36
+ ActiveRecord::Base.send :extend, RQuery::ActiveRecord
35
37
  end
@@ -31,9 +31,13 @@ module RQuery
31
31
  def contains
32
32
  "like '%' + ? + '%'"
33
33
  end
34
+
35
+ def and_group(ops)
36
+ "(#{ops.join(" and ")})"
37
+ end
34
38
 
35
- def join(ops)
36
- ops.join(" and ")
39
+ def or_group(ops)
40
+ "(#{ops.join(" or ")})"
37
41
  end
38
42
 
39
43
  [:>, :>=, :<, :<=].each do |operator|
@@ -41,6 +45,8 @@ module RQuery
41
45
  "#{operator} ?"
42
46
  end
43
47
  end
48
+
49
+ alias :join :and_group
44
50
  end
45
51
  end
46
52
  end
@@ -0,0 +1,14 @@
1
+ module RQuery
2
+ class Attribute
3
+ #adds is, is_not, between, in, contains, and from (alias)
4
+ include RQuery::Declarations
5
+
6
+ #define name for the Declarations included methods
7
+ #as it needs to be different than the default
8
+ attr_accessor :name
9
+
10
+ def initialize(field_name)
11
+ @name = field_name
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,35 @@
1
+ module RQuery
2
+ class AttributeCollection
3
+
4
+ def initialize(fields)
5
+ @fields = fields.map{ |x| x.to_s }
6
+ end
7
+
8
+ #if the field was added upon initialization its a valid call
9
+ #otherwise report to the user it is invalid. Reports errors at the ruby level
10
+ #instead of the data store level with something like "column doesn't exist"
11
+ #
12
+ #example
13
+ #
14
+ # >> user = RQuery::FieldCollection.new([:age, :name])
15
+ # => #<RQuery::FieldCollection:0x1a2c21c @fields=[:age, :name]>
16
+ # >> user.age
17
+ # => #<RQuery::Field:0x1a2b240 @name=:age>
18
+ # >> user.name
19
+ # => #<RQuery::Field:0x1a2a390 @name=:name>
20
+ # >> user.weight
21
+ # ArgumentError: The field 'weight' doesn't exist for this object
22
+ # from /Users/johnbender/Projects/rquery/lib/rquery/where_clause.rb:20:in `method_missing'
23
+ # from (irb):5
24
+ def method_missing(symbol, *args)
25
+ if @fields.include?(symbol.to_s)
26
+ Attribute.new(symbol)
27
+ else
28
+ raise AttributeNotFoundError, "The field '#{symbol.to_s}' doesn't exist for this object"
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ class AttributeNotFoundError < ArgumentError; end
35
+ end
@@ -1,39 +1,34 @@
1
-
2
1
  module RQuery
3
- module Declarations
4
- def is
5
- ::RQuery::Serializers::IsOperations.add_operation(self)
6
- ::RQuery::Serializers::IsOperations.prefix = nil
7
- ::RQuery::Serializers::IsOperations
8
- end
9
-
10
- def is_not
11
- ::RQuery::Serializers::IsNotOperations.add_operation(self)
12
- ::RQuery::Serializers::IsNotOperations.prefix = "not_"
13
- ::RQuery::Serializers::IsNotOperations
14
- end
15
-
16
- def in(*args)
17
- ::RQuery::Serializers::IsOperations.add_operation(self)
18
- ::RQuery::Serializers::IsOperations.prefix = nil
19
- ::RQuery::Serializers::IsOperations.in(*args)
20
- end
21
-
22
- def between(*args)
23
- ::RQuery::Serializers::IsOperations.add_operation(self)
24
- ::RQuery::Serializers::IsOperations.prefix = nil
25
- ::RQuery::Serializers::IsOperations.between(*args)
26
- end
27
-
28
- def contains(val)
29
- ::RQuery::Serializers::IsOperations.add_operation(self)
30
- ::RQuery::Serializers::IsOperations.prefix = nil
31
- ::RQuery::Serializers::IsOperations.contains(val)
32
- end
33
-
34
- alias :from :between
2
+ module Declarations
3
+ #Allows the methods below to be included in
4
+ #the Field class and the Symbol class but remain
5
+ #the same in implementation
6
+ #!name is redefined as an attr_accessor in the Field class!
7
+ def name
8
+ self.to_s if @name == nil
9
+ end
10
+
11
+ def is
12
+ Serializers::IsOperations.add_operation(name)
13
+ Serializers::IsOperations.prefix = nil
14
+ Serializers::IsOperations
15
+ end
16
+
17
+ def is_not
18
+ Serializers::IsNotOperations.add_operation(name)
19
+ Serializers::IsNotOperations.prefix = "not_"
20
+ Serializers::IsNotOperations
35
21
  end
22
+
23
+ [:in, :between, :contains].each do |m|
24
+ define_method(m) do |*args|
25
+ Serializers::IsOperations.add_operation(name)
26
+ Serializers::IsOperations.prefix = nil
27
+ Serializers::IsOperations.send(m, *args)
28
+ end
29
+ end
30
+
31
+ alias :from :between
32
+ end
36
33
  end
37
34
 
38
- Symbol.send :include, RQuery::Declarations
39
-
@@ -1,110 +1,184 @@
1
1
  module RQuery
2
- module Serializers
3
- class Operations
2
+ module Serializers
3
+ #The Operations serializer, handles the limiting factors imposed
4
+ #by a given query. The methods in this class apply to both is and is_not
5
+ #operations. An example in sql would look like:
6
+ #
7
+ #where foo = bar and foo > 2
8
+ #
9
+ #This class is a gateway to the selected RQuery.adapter methods.
10
+ #Calls to methods here, will be passed on to the selected adapter
11
+ class Operations
12
+
13
+ @@prefix = nil
14
+ @@ops = []
15
+ @@values = []
16
+
17
+ class << self
18
+ def prefix=(val)
19
+ @@prefix = val
20
+ end
4
21
 
5
- @@prefix = nil
6
- @@ops = []
7
- @@values = []
8
-
9
- class << self
10
- def prefix=(val)
11
- @@prefix = val
12
- end
13
-
14
- def to_s
15
- RQuery.adapter.join(@@ops)
16
- end
17
-
18
- def conditions
19
- [to_s] + @@values
20
- end
21
-
22
- def add_operation(val)
23
- @@ops << val.to_s
24
- end
25
-
26
- def clear
27
- @@ops.clear
28
- @@values.clear
29
- end
30
-
31
- def in(*args)
32
- #flatten our args to prevent having to check for an array first arg
33
- args.flatten!
34
-
35
- #if a range (or anything else that responds to :first) is passed as the first argument
36
- #use it alone, otherwise use the args array
37
- #examples:
38
- #ruby => args.flatten! => stored values
39
- #:id.between 1..100 => [1..100] => 1..100
40
- #:id.between [1, 2, 3] => [1, 2, 3] => [1, 2, 3]
41
- #:id.between 1, 2 => [1, 2] => [1, 2]
42
- #
43
- @@values << (args.first.respond_to?(:first) ? args.first : args)
44
- @@ops[@@ops.length-1] += " #{RQuery.adapter.send("#{@@prefix}in")}"
45
- end
46
-
47
- def between(*args)
48
- #flatten our args to prevent having to check for an array first arg
49
- args.flatten!
50
-
51
- #if a range is passed use its first/last element
52
- #otherwise use the first and last element of the flattened args array
53
- #examples:
54
- #ruby => args.flatten! => stored values
55
- #:id.between 1..100 => [1..100] => 1 100
56
- #:id.between [1, 2, 3] => [1, 2, 3] => 1 3
57
- #:id.between 1, 2 => [1, 2] => 1 2
58
- #
59
- @@values += args.first.respond_to?(:first) ? [args.first.first, args.first.last] : [args.first, args.last]
60
-
61
- #store the operation string
62
- #example:
63
- #:id.between 1..100 "id between ? and ?"
64
- #
65
- @@ops[@@ops.length-1] += " #{RQuery.adapter.send("#{@@prefix}between")}"
66
- end
67
-
68
- def contains(str)
69
- @@values << str
70
- @@ops[@@ops.length-1] += " #{RQuery.adapter.send("#{@@prefix}contains")}"
71
- end
72
-
73
- #allows for is.from
74
- #examples:
75
- #
76
- #:id.is.from 1,2
77
- #:is.is.from 2..10
78
- alias :from :between
79
-
80
- end
81
-
22
+ def to_s
23
+ RQuery.adapter.join(@@ops)
82
24
  end
83
-
84
- class IsOperations < Operations
85
-
86
- #define the normal operators for is to call the adapter for the equivelant sql
87
- class << self
88
- [:==, :>, :>=, :<, :<=].each do |operator|
89
- define_method(operator) do |val|
90
- @@values << val
91
- @@ops[@@ops.length-1] += " #{RQuery.adapter.send(operator)}"
92
- end
93
- end
94
- end
25
+
26
+ #return a conditions array for use with ActiveRecord.find
27
+ def conditions
28
+ [to_s] + @@values
29
+ end
30
+
31
+ #add and operation to the @@ops array which will be popped
32
+ #and pushed depending on the operations sequence and arrangement
33
+ def add_operation(val)
34
+ @@ops << val.to_s
95
35
  end
96
36
 
97
- class IsNotOperations < Operations
98
-
99
- #define the == value for is_not to call the adapter for the equivelant sql
100
- class << self
101
- define_method(:==) do |val|
102
- @@values << val
103
- @@ops[@@ops.length-1] += " #{RQuery.adapter.send(:neq)}"
104
- end
37
+ #clean out the Operations singleton class variables
38
+ def clear
39
+ @@ops.clear
40
+ @@values.clear
41
+ end
42
+
43
+ #grouping is done by using the | and & operators between multiple operations
44
+ #objects on a single line.
45
+ #
46
+ #This works because each operation ie (user.age.is == 2) is
47
+ #evaluated before these two operators thus pushing
48
+ #the equivelant operation string onto the @@ops array (ie 'age = ?').
49
+ #When an operation is evaluated it returns the Operations class which can be compared
50
+ #using the aforementioned operators. Those operators call the group method
51
+ #popping the last two arguments off the stack and dealing with them in one of two ways
52
+ #
53
+ #1. if the second object popped is a string both objects should be
54
+ # added to a new OperationGroup which is then put back onto the stack
55
+ #
56
+ #2. if the second object popped is an OperationGroup the firest belongs to this group as
57
+ # well (it was on the same line). It is added to the OperationGroup and put back on the
58
+ # stack
59
+ def group(type)
60
+ second_op, first_op = @@ops.pop, @@ops.pop
61
+
62
+ #if the previous operation on the stack is an Operation Group we need to add to it
63
+ if first_op.class == RQuery::Serializers::OperationsGroup
64
+ if first_op.type == type
65
+ first_op.ops << second_op
66
+ @@ops << first_op
67
+ else
68
+ @@ops << OperationsGroup.new(first_op.to_s, second_op, type)
105
69
  end
70
+ else
71
+ @@ops << OperationsGroup.new(first_op, second_op, type)
72
+ end
73
+ end
74
+
75
+ #used to group operations for anding on a single line
76
+ #example with sqlite adapter
77
+ #(user.age.in [1,2,3]) | (user.name.contains "foo")
78
+ #=>(age in (?) and name like '%' || 'foo' || '%')
79
+ def &(second)
80
+ self.group(:and)
81
+ self
82
+ end
83
+
84
+ def |(second)
85
+ self.group(:or)
86
+ self
87
+ end
106
88
 
89
+ def in(*args)
90
+ #flatten our args to prevent having to check for an array first arg
91
+ args.flatten!
92
+
93
+ #if a range is passed as the first argument
94
+ #use it alone, otherwise use the args array
95
+ #examples:
96
+ #ruby => args.flatten! => stored values
97
+ #:id.between 1..100 => [1..100] => 1..100
98
+ #:id.between [1, 2, 3] => [1, 2, 3] => [1, 2, 3]
99
+ #:id.between 1, 2 => [1, 2] => [1, 2]
100
+ @@values << (args.first.class == Range ? args.first : args)
101
+ @@ops[@@ops.length-1] += " #{RQuery.adapter.send("#{@@prefix}in")}"
102
+ self
103
+ end
104
+
105
+ def between(*args)
106
+ #flatten our args to prevent having to check for an array first arg
107
+ args.flatten!
108
+
109
+ #if a range is passed use its first/last element
110
+ #otherwise use the first and last element of the flattened args array
111
+ #examples:
112
+ #ruby => args.flatten! => stored values
113
+ #:id.between 1..100 => [1..100] => 1 100
114
+ #:id.between [1, 2, 3] => [1, 2, 3] => 1 3
115
+ #:id.between 1, 2 => [1, 2] => 1 2
116
+ #
117
+ @@values += (args.first.class == Range ? [args.first.first, args.first.last] : [args.first, args.last])
118
+ @@ops[@@ops.length-1] += " #{RQuery.adapter.send("#{@@prefix}between")}"
119
+ self
120
+ end
121
+
122
+ def contains(str)
123
+ @@values << str
124
+ @@ops[@@ops.length-1] += " #{RQuery.adapter.send("#{@@prefix}contains")}"
125
+ self
107
126
  end
127
+
128
+ #allows for is.from
129
+ #examples:
130
+ #
131
+ #:id.is.from 1,2
132
+ #:is.is.from 2..10
133
+ alias :from :between
134
+ end
135
+ end
108
136
 
137
+ class OperationsGroup
138
+ attr_accessor :ops, :type
139
+
140
+ def initialize(left, right, type)
141
+ @ops = Array.new
142
+ @ops << left
143
+ @ops << right
144
+ @type = type
145
+ end
146
+
147
+ def to_s
148
+ RQuery.adapter.send("#{type.to_s}_group", @ops)
149
+ end
150
+ end
151
+
152
+ #The IsOpertaions serializer defines only methods that apply to the .is operator
153
+ #that is added to the Symbol class in the Declarations module.
154
+ class IsOperations < Operations
155
+ #define the normal operators for is to call the adapter for the equivelant sql
156
+ class << self
157
+ [:==, :>, :>=, :<, :<=].each do |operator|
158
+ define_method(operator) do |val|
159
+ @@values << val
160
+ @@ops[@@ops.length-1] += " #{RQuery.adapter.send(operator)}"
161
+ self
162
+ end
163
+ end
164
+ end
165
+
109
166
  end
167
+
168
+ #The IsNotOperations serializer defines only methods that apply to the .is_not operator
169
+ #that is added to the symbol class in the declarations module. Specifically, == as
170
+ #ruby does not allow for the overloading of !=
171
+ class IsNotOperations < Operations
172
+ #define the == value for is_not to call the adapter for the equivelant sql
173
+ class << self
174
+ def ==(val)
175
+ @@values << val
176
+ @@ops[@@ops.length-1] += " #{RQuery.adapter.send(:neq)}"
177
+ self
178
+ end
179
+ end
180
+
181
+ end
182
+
183
+ end
110
184
  end