rquery 0.1.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +57 -25
- data/lib/rquery.rb +7 -13
- data/lib/rquery/active_record/base.rb +19 -28
- data/lib/rquery/adapters/sql.rb +8 -2
- data/lib/rquery/adapters/sqlite.rb +1 -1
- data/lib/rquery/attribute_collection.rb +57 -0
- data/lib/rquery/operation_collector.rb +175 -0
- data/rakefile.rb +2 -1
- data/spec/active_record_base_spec_attribute_collection.rb +195 -0
- data/spec/mock_active_record.rb +13 -0
- data/spec/or_and_operations_spec.rb +54 -0
- data/spec/sqlite_adapter_spec.rb +1 -1
- metadata +13 -11
- data/lib/rquery/declarations.rb +0 -39
- data/lib/rquery/serializers.rb +0 -118
- data/spec/active_record_base_spec.rb +0 -220
- data/spec/declarations_spec.rb +0 -47
- data/spec/serializers_spec.rb +0 -30
data/README.markdown
CHANGED
@@ -1,25 +1,33 @@
|
|
1
|
+
|
1
2
|
RQuery
|
2
3
|
======
|
3
4
|
|
4
5
|
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
6
|
|
6
|
-
Currently only the ActiveRecord extension is implemented with a Sqlite adapter. Mysql should
|
7
|
+
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
8
|
|
8
9
|
ActiveRecord
|
9
10
|
------------
|
10
11
|
|
11
|
-
|
12
|
+
###Setup/Config
|
13
|
+
|
14
|
+
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
|
15
|
+
|
16
|
+
RQuery::Config.adapter = class
|
12
17
|
|
13
|
-
|
18
|
+
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.
|
19
|
+
|
20
|
+
###Examples
|
21
|
+
|
22
|
+
RQuery extend ActiveRecord to provide the `where` method. `where` accepts a single optional argument and a block that represents the query statements
|
14
23
|
|
15
24
|
In a given UsersController your `show` action might find the User like so:
|
16
25
|
|
17
|
-
@user = User.find(params[:
|
26
|
+
@user = User.find(params[:age])
|
18
27
|
|
19
28
|
Using RQuery:
|
20
29
|
|
21
|
-
@user = User.where {
|
22
|
-
|
30
|
+
@user = User.where { |user| user.age == params[:age] }
|
23
31
|
|
24
32
|
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
33
|
|
@@ -27,43 +35,37 @@ In the above case, RQuery doesn't provide much of an improvement over the tradit
|
|
27
35
|
|
28
36
|
RQuery:
|
29
37
|
|
30
|
-
@users = User.where do
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
Or:
|
35
|
-
|
36
|
-
@users = User.where do
|
37
|
-
:age.between 10..20
|
38
|
+
@users = User.where do |user|
|
39
|
+
user.age.between 10..20
|
38
40
|
end
|
39
41
|
|
40
42
|
Both the `from` and `between` methods accept argument lists `10,20` or an array `[10,20]`.
|
41
43
|
|
42
|
-
|
43
44
|
###Other Examples
|
44
45
|
|
45
|
-
RQuery supports most of the common SQL operations: =, <>, >, <, >=, <= as well as in, like (see below for specifics), and between.
|
46
|
+
RQuery supports most of the common SQL operations: =, <>, >, <, >=, <= as well as in, like (see below for specifics), and between. `obj.foo.not_<operation>` works for `.in` and `.between` with the negation of == being `obj.foo.not = ` and the negation of `obj.foo.contains` as `obj.foo.without`.
|
46
47
|
|
47
48
|
Operators:
|
48
49
|
|
49
|
-
@obj = ActiveRecordObject.where do
|
50
|
-
|
51
|
-
|
50
|
+
@obj = ActiveRecordObject.where do |obj|
|
51
|
+
obj.foo > 2
|
52
|
+
obj.foo.not = 4
|
52
53
|
end
|
54
|
+
|
53
55
|
#=> conditions array: ["foo > ? and foo <> ?", 2, 4]
|
54
56
|
|
55
57
|
Contains:
|
56
58
|
|
57
|
-
@obj = ActiveRecordObject.where do
|
58
|
-
|
59
|
+
@obj = ActiveRecordObject.where do |obj|
|
60
|
+
obj.foo.contains "bar"
|
59
61
|
end
|
60
62
|
#=> conditions array: ["foo like '%' || ? || '%'", "bar"]
|
61
63
|
#using the default sqlite adapter
|
62
64
|
|
63
65
|
In:
|
64
66
|
|
65
|
-
|
66
|
-
|
67
|
+
@obj = ActiveRecordObject.where do |obj|
|
68
|
+
obj.foo.in "bar", "baz", "bak"
|
67
69
|
end
|
68
70
|
#=> conditions array: ["foo in (?)", ["bar", "baz", "bak"]]
|
69
71
|
#using the default sqlite adapter
|
@@ -73,14 +75,44 @@ You can also limit the results returned in a similar manner to the `find` method
|
|
73
75
|
|
74
76
|
First:
|
75
77
|
|
76
|
-
@obj = ActiveRecordObject.where(:first) do
|
77
|
-
|
78
|
+
@obj = ActiveRecordObject.where(:first) do |obj|
|
79
|
+
obj.foo == "bar"
|
78
80
|
end
|
79
81
|
|
80
82
|
is equivalent to the find call:
|
81
83
|
|
82
84
|
@obj = ActiveRecordObject.find(:first, conditions => ["foo = ?", "bar"])
|
83
85
|
|
86
|
+
###Complex queries
|
87
|
+
|
88
|
+
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
|
89
|
+
|
90
|
+
User.where do |user|
|
91
|
+
(user.age > 20) | (user.age.in 16,18)
|
92
|
+
end
|
93
|
+
|
94
|
+
In the following example the & takes precedence and will be grouped with the contains "Alice" which will be or'd with the contains "George"
|
95
|
+
|
96
|
+
#=> name contains "George" or (name contains "Alice and age from 20 to 30)
|
97
|
+
User.where do |user|
|
98
|
+
(user.name.contains "George") | (user.name.contains "Alice") & (use.age.from 20..30)
|
99
|
+
end
|
100
|
+
|
101
|
+
To correct the above to the more intuitive version add parens to force precedence of the contains operations
|
102
|
+
|
103
|
+
#=> (name contains "George" or name contains "Alice) and age from 20 to 30
|
104
|
+
User.where do |user|
|
105
|
+
((user.name.contains "George") | (user.name.contains "Alice")) & (use.age.from 20..30)
|
106
|
+
end
|
107
|
+
|
108
|
+
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
|
109
|
+
|
110
|
+
User.where do |user|
|
111
|
+
(user.name.contains "George") | (user.name.contains "Alice")
|
112
|
+
use.age.from 20..30
|
113
|
+
end
|
114
|
+
|
115
|
+
|
84
116
|
|
85
117
|
|
86
118
|
|
data/lib/rquery.rb
CHANGED
@@ -2,24 +2,18 @@ $: << File.expand_path(File.dirname(__FILE__) + "/../lib/")
|
|
2
2
|
|
3
3
|
#RQuery is a small DSL for building queries in query languages like SQL. It is meant to be concise, easy to read
|
4
4
|
#and expressive.
|
5
|
-
#
|
6
5
|
|
7
|
-
|
8
|
-
require "rquery/
|
9
|
-
require "rquery/declarations.rb"
|
6
|
+
require "rquery/operation_collector.rb"
|
7
|
+
require "rquery/attribute_collection.rb"
|
10
8
|
require "rquery/adapters.rb"
|
11
9
|
require "rquery/active_record.rb"
|
12
10
|
|
13
11
|
module RQuery
|
12
|
+
class Config
|
14
13
|
@@adapter = RQuery::Adapters::Sqlite
|
15
|
-
|
16
|
-
|
17
|
-
#to convert the "rqueries" into the adapters query language
|
18
|
-
def RQuery.adapter=(val)
|
19
|
-
@@adapter = val
|
20
|
-
end
|
21
|
-
def RQuery.adapter
|
22
|
-
@@adapter
|
14
|
+
def self.adapter=(value)
|
15
|
+
@@adapter = value
|
23
16
|
end
|
17
|
+
def self.adapter; @@adapter end
|
18
|
+
end
|
24
19
|
end
|
25
|
-
|
@@ -1,35 +1,26 @@
|
|
1
1
|
module RQuery
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
#clear the alterations
|
20
|
-
::RQuery::Serializers::Operations.clear
|
21
|
-
end
|
22
|
-
|
23
|
-
#limit the records returned (:first, :all, :last)
|
24
|
-
limit = args.first ? args.first : :all
|
25
|
-
|
26
|
-
#call find with the conditions and values provided by the block
|
27
|
-
find(limit, :conditions => conditions)
|
28
|
-
end
|
2
|
+
module ActiveRecord
|
3
|
+
def where(*args, &block)
|
4
|
+
collector = RQuery::AttributeCollection.new(self.new.attribute_names)
|
5
|
+
|
6
|
+
#Passes a new AttributeCollection object to the block
|
7
|
+
#if RQuery.use_symbols has been called it may not be used
|
8
|
+
#but otherwise will take the form attr_coll_object.attribute.is ...
|
9
|
+
yield(collector)
|
10
|
+
|
11
|
+
#record altered conditions and values
|
12
|
+
conditions = collector.clauses.to_conditions
|
13
|
+
|
14
|
+
#limit the records returned (:first, :all, :last)
|
15
|
+
limit = args.first ? args.first : :all
|
16
|
+
|
17
|
+
#call find with the conditions and values provided by the block
|
18
|
+
find(limit, :conditions => conditions)
|
29
19
|
end
|
20
|
+
end
|
30
21
|
end
|
31
22
|
|
32
23
|
#extend ActiveRecord::Base with the where method
|
33
24
|
if Object.const_defined?(:ActiveRecord)
|
34
|
-
|
25
|
+
ActiveRecord::Base.send :extend, RQuery::ActiveRecord
|
35
26
|
end
|
data/lib/rquery/adapters/sql.rb
CHANGED
@@ -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
|
36
|
-
|
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,57 @@
|
|
1
|
+
module RQuery
|
2
|
+
class AttributeCollection
|
3
|
+
attr_reader :clauses
|
4
|
+
|
5
|
+
def initialize(fields)
|
6
|
+
@fields = fields.map{ |x| x.to_s }
|
7
|
+
@clauses = OperationCollector.new
|
8
|
+
end
|
9
|
+
|
10
|
+
#if the field was added upon initialization its a valid call
|
11
|
+
#otherwise report to the user it is invalid. Reports errors at the ruby level
|
12
|
+
#instead of the data store level with something like "column doesn't exist"
|
13
|
+
#
|
14
|
+
#example
|
15
|
+
#
|
16
|
+
# >> user = RQuery::FieldCollection.new([:age, :name])
|
17
|
+
# => #<RQuery::FieldCollection:0x1a2c21c @fields=[:age, :name]>
|
18
|
+
# >> user.age
|
19
|
+
# => #<RQuery::Field:0x1a2b240 @name=:age>
|
20
|
+
# >> user.name
|
21
|
+
# => #<RQuery::Field:0x1a2a390 @name=:name>
|
22
|
+
# >> user.weight
|
23
|
+
# ArgumentError: The field 'weight' doesn't exist for this object
|
24
|
+
# from /Users/johnbender/Projects/rquery/lib/rquery/where_clause.rb:20:in `method_missing'
|
25
|
+
# from (irb):5
|
26
|
+
def method_missing(symbol, *args)
|
27
|
+
attr_str = symbol.to_s
|
28
|
+
eq_str = attr_str.gsub(/=/, '')
|
29
|
+
|
30
|
+
if @fields.include?(attr_str) #if the method is part of the attributes
|
31
|
+
add_clause(attr_str)
|
32
|
+
elsif @fields.include?(eq_str) #if the method sans '=' is part of the attributes
|
33
|
+
add_clause(eq_str)
|
34
|
+
eq(*args)
|
35
|
+
else
|
36
|
+
raise AttributeNotFoundError, "The field '#{symbol.to_s}' doesn't exist for this object"
|
37
|
+
end
|
38
|
+
|
39
|
+
#explicit return of OperationCollector, same instance returned by methods of Operation collector
|
40
|
+
#but included here for clarity
|
41
|
+
@clauses
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
private
|
46
|
+
def add_clause(str)
|
47
|
+
@clauses.add_operation(str)
|
48
|
+
end
|
49
|
+
|
50
|
+
def eq(*args)
|
51
|
+
@clauses.send(:==, *args)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
class AttributeNotFoundError < ArgumentError; end
|
57
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
module RQuery
|
2
|
+
class OperationCollector
|
3
|
+
NOT_PREFIX = 'not_'
|
4
|
+
NIL_PREFIX = ''
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
# @name, @prefix = optns[:name], optns[:prefix]
|
8
|
+
@operations = []
|
9
|
+
@values = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
RQuery::Config.adapter.join(@operations)
|
14
|
+
end
|
15
|
+
|
16
|
+
#return a conditions array for use with ActiveRecord.find
|
17
|
+
def to_conditions
|
18
|
+
[to_s] + @values
|
19
|
+
end
|
20
|
+
|
21
|
+
#add and operation to the @operations array which will be popped
|
22
|
+
#and pushed depending on the operations sequence and arrangement
|
23
|
+
def add_operation(val)
|
24
|
+
@operations << val.to_s
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
#grouping is done by using the | and & operators between multiple operations
|
29
|
+
#objects on a single line.
|
30
|
+
#
|
31
|
+
#This works because each operation ie (user.age.is == 2) is
|
32
|
+
#evaluated before these two operators thus pushing
|
33
|
+
#the equivelant operation string onto the @operations array (ie 'age = ?').
|
34
|
+
#When an operation is evaluated it returns the Operations class which can be compared
|
35
|
+
#using the aforementioned operators. Those operators call the group method
|
36
|
+
#popping the last two arguments off the stack and dealing with them in one of two ways
|
37
|
+
#
|
38
|
+
#1. if the second object popped is a string both objects should be
|
39
|
+
# added to a new OperationGroup which is then put back onto the stack
|
40
|
+
#
|
41
|
+
#2. if the second object popped is an OperationGroup the firest belongs to this group as
|
42
|
+
# well (it was on the same line). It is added to the OperationGroup and put back on the
|
43
|
+
# stack
|
44
|
+
#
|
45
|
+
#TODO requires refactoring
|
46
|
+
def group(type)
|
47
|
+
second_op, first_op = @operations.pop, @operations.pop
|
48
|
+
|
49
|
+
#if the previous operation on the stack is an Operation Group we need to add to it
|
50
|
+
#and push it back on the @operations stack
|
51
|
+
if first_op.class == OperationsGroup
|
52
|
+
if first_op.type == type
|
53
|
+
first_op.ops << second_op
|
54
|
+
@operations << first_op
|
55
|
+
else
|
56
|
+
@operations << OperationsGroup.new(first_op.to_s, second_op, type)
|
57
|
+
end
|
58
|
+
else
|
59
|
+
@operations << OperationsGroup.new(first_op, second_op, type)
|
60
|
+
end
|
61
|
+
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
#used to group operations for anding on a single line
|
66
|
+
#example with sqlite adapter
|
67
|
+
#(user.age.in [1,2,3]) | (user.name.contains "foo")
|
68
|
+
#=>(age in (?) and name like '%' || 'foo' || '%')
|
69
|
+
def &(second)
|
70
|
+
group :and
|
71
|
+
end
|
72
|
+
|
73
|
+
def |(second)
|
74
|
+
group :or
|
75
|
+
end
|
76
|
+
|
77
|
+
def in(*args)
|
78
|
+
call_in(NIL_PREFIX, *args)
|
79
|
+
end
|
80
|
+
|
81
|
+
def not_in(*args)
|
82
|
+
call_in(NOT_PREFIX, *args)
|
83
|
+
end
|
84
|
+
|
85
|
+
def between(*args)
|
86
|
+
call_between(NIL_PREFIX, *args)
|
87
|
+
end
|
88
|
+
|
89
|
+
def not_between(*args)
|
90
|
+
call_between(NOT_PREFIX, *args)
|
91
|
+
end
|
92
|
+
|
93
|
+
def contains(str)
|
94
|
+
@values << str
|
95
|
+
chain :contains
|
96
|
+
end
|
97
|
+
|
98
|
+
def without(str)
|
99
|
+
@values << str
|
100
|
+
chain :without
|
101
|
+
end
|
102
|
+
|
103
|
+
def not=(arg)
|
104
|
+
@values << arg
|
105
|
+
chain :neq
|
106
|
+
end
|
107
|
+
|
108
|
+
[:==, :>, :>=, :<, :<=, :neq].each do |operator|
|
109
|
+
define_method(operator) do |val|
|
110
|
+
@values << val
|
111
|
+
chain operator
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
#allows for is.from
|
116
|
+
#examples:
|
117
|
+
#
|
118
|
+
#:id.is.from 1,2
|
119
|
+
#:is.is.from 2..10
|
120
|
+
alias :from :between
|
121
|
+
alias :not_from :not_between
|
122
|
+
|
123
|
+
private
|
124
|
+
def chain(method)
|
125
|
+
@operations[@operations.length-1] += " #{RQuery::Config.adapter.send(method)}"
|
126
|
+
self
|
127
|
+
end
|
128
|
+
|
129
|
+
def call_in(prefix=nil, *args)
|
130
|
+
#flatten our args to prevent having to check for an array first arg
|
131
|
+
args.flatten!
|
132
|
+
|
133
|
+
#if a range is passed as the first argument
|
134
|
+
#use it alone, otherwise use the args array
|
135
|
+
#examples:
|
136
|
+
#ruby => args.flatten! => stored values
|
137
|
+
#:id.between 1..100 => [1..100] => 1..100
|
138
|
+
#:id.between [1, 2, 3] => [1, 2, 3] => [1, 2, 3]
|
139
|
+
#:id.between 1, 2 => [1, 2] => [1, 2]
|
140
|
+
@values << (args.first.class == Range ? args.first : args)
|
141
|
+
chain :"#{prefix}in"
|
142
|
+
end
|
143
|
+
|
144
|
+
def call_between(prefix=nil, *args)
|
145
|
+
#flatten our args to prevent having to check for an array first arg
|
146
|
+
args.flatten!
|
147
|
+
|
148
|
+
#if a range is passed use its first/last element
|
149
|
+
#otherwise use the first and last element of the flattened args array
|
150
|
+
#examples:
|
151
|
+
#ruby => args.flatten! => stored values
|
152
|
+
#:id.between 1..100 => [1..100] => 1 100
|
153
|
+
#:id.between [1, 2, 3] => [1, 2, 3] => 1 3
|
154
|
+
#:id.between 1, 2 => [1, 2] => 1 2
|
155
|
+
|
156
|
+
@values += (args.first.class == Range ? [args.first.first, args.first.last] : [args.first, args.last])
|
157
|
+
chain :"#{prefix}between"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
class OperationsGroup
|
162
|
+
attr_accessor :ops, :type
|
163
|
+
|
164
|
+
def initialize(left, right, type)
|
165
|
+
@ops = Array.new
|
166
|
+
@ops << left
|
167
|
+
@ops << right
|
168
|
+
@type = type
|
169
|
+
end
|
170
|
+
|
171
|
+
def to_s
|
172
|
+
RQuery::Config.adapter.send("#{type.to_s}_group", @ops)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
data/rakefile.rb
CHANGED
@@ -0,0 +1,195 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/mock_active_record.rb")
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/rquery.rb")
|
3
|
+
|
4
|
+
describe ActiveRecord do
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
RQuery::Config.adapter = RQuery::Adapters::Sqlite
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should set up a where method" do
|
11
|
+
ActiveRecord::MockObject.respond_to?(:where).should == true
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should return sql with foo, the operations, and the values for mock.foo <operation> <value>" do
|
15
|
+
ActiveRecord::MockObject.where{ |mock|
|
16
|
+
mock.foo == "bar"
|
17
|
+
mock.foo = "bar"
|
18
|
+
}.should == [:all, {:conditions => ["(foo = ? and foo = ?)", "bar", "bar"]}]
|
19
|
+
|
20
|
+
ActiveRecord::MockObject.where{ |mock|
|
21
|
+
mock.foo > 1
|
22
|
+
}.should == [:all, {:conditions => ["(foo > ?)", 1]}]
|
23
|
+
|
24
|
+
ActiveRecord::MockObject.where{ |mock|
|
25
|
+
mock.foo < 2
|
26
|
+
}.should == [:all, {:conditions => ["(foo < ?)", 2]}]
|
27
|
+
|
28
|
+
ActiveRecord::MockObject.where{ |mock|
|
29
|
+
mock.foo >= 3
|
30
|
+
}.should == [:all, {:conditions => ["(foo >= ?)", 3]}]
|
31
|
+
|
32
|
+
ActiveRecord::MockObject.where{ |mock|
|
33
|
+
mock.foo <= 4
|
34
|
+
}.should == [:all, {:conditions => ["(foo <= ?)", 4]}]
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should return sql with foo, the operations, and the values for mock.foo.not_<operation> <value>" do
|
38
|
+
ActiveRecord::MockObject.where{ |mock|
|
39
|
+
mock.foo.not = "bar"
|
40
|
+
}.should == [:all, {:conditions => ["(foo <> ?)", "bar"]}]
|
41
|
+
|
42
|
+
ActiveRecord::MockObject.where{ |mock|
|
43
|
+
mock.foo.not_in 1,2
|
44
|
+
}.should == [:all, {:conditions => ["(foo not in (?))", [1,2]]}]
|
45
|
+
|
46
|
+
ActiveRecord::MockObject.where{ |mock|
|
47
|
+
mock.foo.not_between 1..3
|
48
|
+
}.should == [:all, {:conditions => ["(foo not between ? and ?)", 1, 3]}]
|
49
|
+
|
50
|
+
ActiveRecord::MockObject.where{ |mock|
|
51
|
+
mock.foo.not_from 1..3
|
52
|
+
}.should == [:all, {:conditions => ["(foo not between ? and ?)", 1, 3]}]
|
53
|
+
|
54
|
+
ActiveRecord::MockObject.where{ |mock|
|
55
|
+
mock.foo.without "bar"
|
56
|
+
}.should == [:all, {:conditions => ["(foo not like '%' || ? || '%')", "bar"]}]
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should return sql with foo, the operations, and values for mock.foo.in and mock.foo.in when used with a list of args, array, and range" do
|
60
|
+
resulting_conditions = [:all, {:conditions => ["(foo in (?))", [1,2,3,4]]}]
|
61
|
+
|
62
|
+
ActiveRecord::MockObject.where{ |mock|
|
63
|
+
mock.foo.in 1,2,3,4
|
64
|
+
}.should == resulting_conditions
|
65
|
+
|
66
|
+
ActiveRecord::MockObject.where{ |mock|
|
67
|
+
mock.foo.in [1,2,3,4]
|
68
|
+
}.should == resulting_conditions
|
69
|
+
|
70
|
+
ActiveRecord::MockObject.where{ |mock|
|
71
|
+
mock.foo.in 1..4
|
72
|
+
}.should == [:all, {:conditions => ["(foo in (?))", 1..4]}]
|
73
|
+
|
74
|
+
ActiveRecord::MockObject.where{ |mock|
|
75
|
+
mock.foo.in 1,2,3,4
|
76
|
+
}.should == resulting_conditions
|
77
|
+
|
78
|
+
ActiveRecord::MockObject.where{ |mock|
|
79
|
+
mock.foo.in [1,2,3,4]
|
80
|
+
}.should == resulting_conditions
|
81
|
+
|
82
|
+
ActiveRecord::MockObject.where{ |mock|
|
83
|
+
mock.foo.in 1..4
|
84
|
+
}.should == [:all, {:conditions => ["(foo in (?))", 1..4]}]
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should return sql with foo, operations, and values for mock.foo.between and mock.foo.between when used with a list of args, array, and range" do
|
88
|
+
resulting_conditions = [:all, {:conditions => ["(foo between ? and ?)", 1, 2]}]
|
89
|
+
|
90
|
+
ActiveRecord::MockObject.where{ |mock|
|
91
|
+
mock.foo.between 1,2
|
92
|
+
}.should == resulting_conditions
|
93
|
+
|
94
|
+
ActiveRecord::MockObject.where{ |mock|
|
95
|
+
mock.foo.between [1,2]
|
96
|
+
}.should == resulting_conditions
|
97
|
+
|
98
|
+
ActiveRecord::MockObject.where{ |mock|
|
99
|
+
mock.foo.between 1..2
|
100
|
+
}.should == resulting_conditions
|
101
|
+
|
102
|
+
ActiveRecord::MockObject.where{ |mock|
|
103
|
+
mock.foo.between 1,2
|
104
|
+
}.should == resulting_conditions
|
105
|
+
|
106
|
+
ActiveRecord::MockObject.where{ |mock|
|
107
|
+
mock.foo.between [1,2]
|
108
|
+
}.should == resulting_conditions
|
109
|
+
|
110
|
+
ActiveRecord::MockObject.where{ |mock|
|
111
|
+
mock.foo.between 1..2
|
112
|
+
}.should == resulting_conditions
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should return sql with foo, operations, and values for mock.foo.from when used with a list of args, array, and range" do
|
116
|
+
resulting_conditions = [:all, {:conditions => ["(foo between ? and ?)", 1, 2]}]
|
117
|
+
|
118
|
+
ActiveRecord::MockObject.where{ |mock|
|
119
|
+
mock.foo.from 1,2
|
120
|
+
}.should == resulting_conditions
|
121
|
+
|
122
|
+
ActiveRecord::MockObject.where{ |mock|
|
123
|
+
mock.foo.from [1,2]
|
124
|
+
}.should == resulting_conditions
|
125
|
+
|
126
|
+
ActiveRecord::MockObject.where{ |mock|
|
127
|
+
mock.foo.from 1..2
|
128
|
+
}.should == resulting_conditions
|
129
|
+
|
130
|
+
ActiveRecord::MockObject.where{ |mock|
|
131
|
+
mock.foo.from 1,2
|
132
|
+
}.should == resulting_conditions
|
133
|
+
|
134
|
+
ActiveRecord::MockObject.where{ |mock|
|
135
|
+
mock.foo.from [1,2]
|
136
|
+
}.should == resulting_conditions
|
137
|
+
|
138
|
+
ActiveRecord::MockObject.where{ |mock|
|
139
|
+
mock.foo.from 1..2
|
140
|
+
}.should == resulting_conditions
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should return sql with foo, operations, and values for mock.foo.contains when used with a range, array, and list" do
|
144
|
+
resulting_conditions = [:all, {:conditions => ["(foo like '%' || ? || '%')", "bar"]}]
|
145
|
+
|
146
|
+
ActiveRecord::MockObject.where{ |mock|
|
147
|
+
mock.foo.contains "bar"
|
148
|
+
}.should == resulting_conditions
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should return return the correct group of joined sql after multiple operations" do
|
152
|
+
ActiveRecord::MockObject.where{ |mock|
|
153
|
+
mock.foo == "bar"
|
154
|
+
mock.foo.not_in 1,2,3,4,5
|
155
|
+
}.should == [:all, {:conditions => ["(foo = ? and foo not in (?))", "bar", [1,2,3,4,5]]}]
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should return return the correct limit value passed" do
|
159
|
+
ActiveRecord::MockObject.where(:first){ |mock|
|
160
|
+
mock.foo == "bar"
|
161
|
+
mock.foo.not_in 1,2,3,4,5
|
162
|
+
}.should == [:first, {:conditions => ["(foo = ? and foo not in (?))", "bar", [1,2,3,4,5]]}]
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should have the correct 'not' keywords in alternating operations" do
|
166
|
+
ActiveRecord::MockObject.where(:first){ |mock|
|
167
|
+
mock.foo == "bar"
|
168
|
+
mock.foo.not_in 1,2,3,4,5
|
169
|
+
mock.foo > 3
|
170
|
+
}.should == [:first, {:conditions => ["(foo = ? and foo not in (?) and foo > ?)", "bar", [1,2,3,4,5], 3]}]
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should return return strings as arguments when passed to between, in, and from (used for date strings)" do
|
174
|
+
ActiveRecord::MockObject.where{ |mock|
|
175
|
+
mock.foo.between "some string", "2007-01-01"
|
176
|
+
}.should == [:all, {:conditions => ["(foo between ? and ?)", "some string", "2007-01-01"]}]
|
177
|
+
|
178
|
+
ActiveRecord::MockObject.where{ |mock|
|
179
|
+
mock.foo.not_between "some string", "2007-01-01"
|
180
|
+
}.should == [:all, {:conditions => ["(foo not between ? and ?)", "some string", "2007-01-01"]}]
|
181
|
+
|
182
|
+
ActiveRecord::MockObject.where{ |mock|
|
183
|
+
mock.foo.from "some string", "2007-01-01"
|
184
|
+
}.should == [:all, {:conditions => ["(foo between ? and ?)", "some string", "2007-01-01"]}]
|
185
|
+
|
186
|
+
ActiveRecord::MockObject.where{ |mock|
|
187
|
+
mock.foo.in "some string", "2007-01-01"
|
188
|
+
}.should == [:all, {:conditions => ["(foo in (?))", ["some string", "2007-01-01"]]}]
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should throw and exception when trying to use a field not in the objects attributes" do
|
192
|
+
attribute = "arbitrary_attribute_name"
|
193
|
+
lambda { ActiveRecord::MockObject.where{ |mock| mock.send(attribute) == 2 }}.should raise_error(RQuery::AttributeNotFoundError)
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/mock_active_record.rb")
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/rquery.rb")
|
3
|
+
|
4
|
+
describe RQuery::OperationCollector do
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
RQuery::Config.adapter = RQuery::Adapters::Sqlite
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should group two operations on the same line with parens and the 'or' keyword when the | operator is used" do
|
11
|
+
|
12
|
+
ActiveRecord::MockObject.where{ |mock|
|
13
|
+
(mock.foo == 2) | (mock.foo.in 1,2,3)
|
14
|
+
}.should == [:all, {:conditions => ["((foo = ? or foo in (?)))", 2, [1,2,3]]}]
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should group two operations on the same line with parns and the 'and' keyword when the & operator is used" do
|
19
|
+
|
20
|
+
ActiveRecord::MockObject.where{ |mock|
|
21
|
+
(mock.foo == 2) & (mock.foo.in 1,2,3)
|
22
|
+
}.should == [:all, {:conditions => ["((foo = ? and foo in (?)))", 2, [1,2,3]]}]
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should group two operations on the same line and continue to add subsequent operations" do
|
27
|
+
|
28
|
+
ActiveRecord::MockObject.where{ |mock|
|
29
|
+
(mock.foo == 2) & (mock.foo.in 1,2,3)
|
30
|
+
mock.foo > 3
|
31
|
+
}.should == [:all, {:conditions => ["((foo = ? and foo in (?)) and foo > ?)", 2, [1,2,3], 3]}]
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should properly group multiple nested groupings on the same line" do
|
36
|
+
|
37
|
+
ActiveRecord::MockObject.where{ |mock|
|
38
|
+
(mock.foo == 2) | (mock.foo.in 1,2,3) | (mock.foo.contains "george")
|
39
|
+
mock.foo > 3
|
40
|
+
(mock.foo == 2) & (mock.foo.in 1,2,3)
|
41
|
+
}.should == [:all, {:conditions => ["((foo = ? or foo in (?) or foo like '%' || ? || '%') and foo > ? and (foo = ? and foo in (?)))", 2, [1,2,3], "george", 3, 2, [1,2,3]]}]
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
it "& should have precedence when evaluating multiple operation group types on a single line" do
|
46
|
+
|
47
|
+
ActiveRecord::MockObject.where{ |mock|
|
48
|
+
(mock.foo == 2) | (mock.foo.in 1,2,3) & (mock.foo.contains "george")
|
49
|
+
}.should == [:all, {:conditions => ["((foo = ? or (foo in (?) and foo like '%' || ? || '%')))", 2, [1,2,3], "george"]}]
|
50
|
+
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/spec/sqlite_adapter_spec.rb
CHANGED
@@ -15,7 +15,7 @@ describe RQuery::Adapters::Sqlite do
|
|
15
15
|
@adapter.not_between.should == "not " + @adapter.between
|
16
16
|
@adapter.neq.should == "<> ?"
|
17
17
|
@adapter.contains.should == "like '%' || ? || '%'"
|
18
|
-
@adapter.
|
18
|
+
@adapter.without.should == "not " + @adapter.contains
|
19
19
|
end
|
20
20
|
|
21
21
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rquery
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John bender
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2009-08-22 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -25,19 +25,21 @@ files:
|
|
25
25
|
- README.markdown
|
26
26
|
- rakefile.rb
|
27
27
|
- lib/rquery.rb
|
28
|
-
- lib/rquery/
|
29
|
-
- lib/rquery/serializers.rb
|
28
|
+
- lib/rquery/attribute_collection.rb
|
30
29
|
- lib/rquery/active_record.rb
|
31
30
|
- lib/rquery/active_record/base.rb
|
32
31
|
- lib/rquery/adapters.rb
|
33
32
|
- lib/rquery/adapters/sql.rb
|
34
33
|
- lib/rquery/adapters/sqlite.rb
|
35
|
-
-
|
36
|
-
- spec/
|
37
|
-
- spec/
|
34
|
+
- lib/rquery/operation_collector.rb
|
35
|
+
- spec/active_record_base_spec_attribute_collection.rb
|
36
|
+
- spec/mock_active_record.rb
|
37
|
+
- spec/or_and_operations_spec.rb
|
38
38
|
- spec/sqlite_adapter_spec.rb
|
39
39
|
has_rdoc: true
|
40
|
-
homepage: http://
|
40
|
+
homepage: http://github.com/johnbender/rquery
|
41
|
+
licenses: []
|
42
|
+
|
41
43
|
post_install_message:
|
42
44
|
rdoc_options: []
|
43
45
|
|
@@ -58,9 +60,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
58
60
|
requirements: []
|
59
61
|
|
60
62
|
rubyforge_project:
|
61
|
-
rubygems_version: 1.3.
|
63
|
+
rubygems_version: 1.3.4
|
62
64
|
signing_key:
|
63
|
-
specification_version:
|
64
|
-
summary:
|
65
|
+
specification_version: 3
|
66
|
+
summary: An ActiveRecord extension providing a DSL replacement for complex find queries.
|
65
67
|
test_files: []
|
66
68
|
|
data/lib/rquery/declarations.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
|
2
|
-
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
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
Symbol.send :include, RQuery::Declarations
|
39
|
-
|
data/lib/rquery/serializers.rb
DELETED
@@ -1,118 +0,0 @@
|
|
1
|
-
module RQuery
|
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
|
21
|
-
|
22
|
-
def to_s
|
23
|
-
RQuery.adapter.join(@@ops)
|
24
|
-
end
|
25
|
-
|
26
|
-
def conditions
|
27
|
-
[to_s] + @@values
|
28
|
-
end
|
29
|
-
|
30
|
-
def add_operation(val)
|
31
|
-
@@ops << val.to_s
|
32
|
-
end
|
33
|
-
|
34
|
-
def clear
|
35
|
-
@@ops.clear
|
36
|
-
@@values.clear
|
37
|
-
end
|
38
|
-
|
39
|
-
def in(*args)
|
40
|
-
#flatten our args to prevent having to check for an array first arg
|
41
|
-
args.flatten!
|
42
|
-
|
43
|
-
#if a range is passed as the first argument
|
44
|
-
#use it alone, otherwise use the args array
|
45
|
-
#examples:
|
46
|
-
#ruby => args.flatten! => stored values
|
47
|
-
#:id.between 1..100 => [1..100] => 1..100
|
48
|
-
#:id.between [1, 2, 3] => [1, 2, 3] => [1, 2, 3]
|
49
|
-
#:id.between 1, 2 => [1, 2] => [1, 2]
|
50
|
-
#
|
51
|
-
@@values << (args.first.class == Range ? args.first : args)
|
52
|
-
@@ops[@@ops.length-1] += " #{RQuery.adapter.send("#{@@prefix}in")}"
|
53
|
-
end
|
54
|
-
|
55
|
-
def between(*args)
|
56
|
-
#flatten our args to prevent having to check for an array first arg
|
57
|
-
args.flatten!
|
58
|
-
|
59
|
-
#if a range is passed use its first/last element
|
60
|
-
#otherwise use the first and last element of the flattened args array
|
61
|
-
#examples:
|
62
|
-
#ruby => args.flatten! => stored values
|
63
|
-
#:id.between 1..100 => [1..100] => 1 100
|
64
|
-
#:id.between [1, 2, 3] => [1, 2, 3] => 1 3
|
65
|
-
#:id.between 1, 2 => [1, 2] => 1 2
|
66
|
-
#
|
67
|
-
@@values += (args.first.class == Range ? [args.first.first, args.first.last] : [args.first, args.last])
|
68
|
-
@@ops[@@ops.length-1] += " #{RQuery.adapter.send("#{@@prefix}between")}"
|
69
|
-
end
|
70
|
-
|
71
|
-
def contains(str)
|
72
|
-
@@values << str
|
73
|
-
@@ops[@@ops.length-1] += " #{RQuery.adapter.send("#{@@prefix}contains")}"
|
74
|
-
end
|
75
|
-
|
76
|
-
#allows for is.from
|
77
|
-
#examples:
|
78
|
-
#
|
79
|
-
#:id.is.from 1,2
|
80
|
-
#:is.is.from 2..10
|
81
|
-
alias :from :between
|
82
|
-
|
83
|
-
end
|
84
|
-
|
85
|
-
end
|
86
|
-
|
87
|
-
#The IsOpertaions serializer defines only methods that apply to the .is operator
|
88
|
-
#that is added to the Symbol class in the Declarations module.
|
89
|
-
class IsOperations < Operations
|
90
|
-
|
91
|
-
#define the normal operators for is to call the adapter for the equivelant sql
|
92
|
-
class << self
|
93
|
-
[:==, :>, :>=, :<, :<=].each do |operator|
|
94
|
-
define_method(operator) do |val|
|
95
|
-
@@values << val
|
96
|
-
@@ops[@@ops.length-1] += " #{RQuery.adapter.send(operator)}"
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
#The IsNotOperations serializer defines only methods that apply to the .is_not operator
|
103
|
-
#that is added to the symbol class in the declarations module. Specifically, == as
|
104
|
-
#ruby does not allow for the overloading of !=
|
105
|
-
class IsNotOperations < Operations
|
106
|
-
|
107
|
-
#define the == value for is_not to call the adapter for the equivelant sql
|
108
|
-
class << self
|
109
|
-
define_method(:==) do |val|
|
110
|
-
@@values << val
|
111
|
-
@@ops[@@ops.length-1] += " #{RQuery.adapter.send(:neq)}"
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
end
|
116
|
-
|
117
|
-
end
|
118
|
-
end
|
@@ -1,220 +0,0 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
class Base
|
3
|
-
def Base.find(limit, conditions)
|
4
|
-
return [limit, conditions]
|
5
|
-
end
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
require File.expand_path(File.dirname(__FILE__) + "/../lib/rquery.rb")
|
10
|
-
|
11
|
-
describe ActiveRecord do
|
12
|
-
|
13
|
-
before(:all) do
|
14
|
-
RQuery.adapter = RQuery::Adapters::Sqlite
|
15
|
-
end
|
16
|
-
|
17
|
-
#should really set up the find method defined above to use the ruby db libraries and
|
18
|
-
#create the final sql string
|
19
|
-
#all run with default adapter
|
20
|
-
|
21
|
-
it "should set up a where method" do
|
22
|
-
ActiveRecord::Base.respond_to?(:where).should == true
|
23
|
-
end
|
24
|
-
|
25
|
-
it "should return sql with foo, the operations, and the values for :foo.is <operation> <value>" do
|
26
|
-
|
27
|
-
ActiveRecord::Base.where{
|
28
|
-
:foo.is == "bar"
|
29
|
-
}.should == [:all, {:conditions => ["foo = ?", "bar"]}]
|
30
|
-
|
31
|
-
ActiveRecord::Base.where{
|
32
|
-
:foo.is > 1
|
33
|
-
}.should == [:all, {:conditions => ["foo > ?", 1]}]
|
34
|
-
|
35
|
-
ActiveRecord::Base.where{
|
36
|
-
:foo.is < 2
|
37
|
-
}.should == [:all, {:conditions => ["foo < ?", 2]}]
|
38
|
-
|
39
|
-
ActiveRecord::Base.where{
|
40
|
-
:foo.is >= 3
|
41
|
-
}.should == [:all, {:conditions => ["foo >= ?", 3]}]
|
42
|
-
|
43
|
-
ActiveRecord::Base.where{
|
44
|
-
:foo.is <= 4
|
45
|
-
}.should == [:all, {:conditions => ["foo <= ?", 4]}]
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
|
-
it "should return sql with foo, the operations, and the values for :foo.is_not <operation> <value>" do
|
50
|
-
ActiveRecord::Base.where{
|
51
|
-
:foo.is_not == "bar"
|
52
|
-
}.should == [:all, {:conditions => ["foo <> ?", "bar"]}]
|
53
|
-
|
54
|
-
ActiveRecord::Base.where{
|
55
|
-
:foo.is_not.in 1,2
|
56
|
-
}.should == [:all, {:conditions => ["foo not in (?)", [1,2]]}]
|
57
|
-
|
58
|
-
ActiveRecord::Base.where{
|
59
|
-
:foo.is_not.between 1..3
|
60
|
-
}.should == [:all, {:conditions => ["foo not between ? and ?", 1, 3]}]
|
61
|
-
|
62
|
-
ActiveRecord::Base.where{
|
63
|
-
:foo.is_not.from 1..3
|
64
|
-
}.should == [:all, {:conditions => ["foo not between ? and ?", 1, 3]}]
|
65
|
-
|
66
|
-
end
|
67
|
-
|
68
|
-
it "should return sql with foo, the operations, and values for :foo.is.in and :foo.in when used with a list of args, array, and range" do
|
69
|
-
|
70
|
-
resulting_conditions = [:all, {:conditions => ["foo in (?)", [1,2,3,4]]}]
|
71
|
-
|
72
|
-
ActiveRecord::Base.where{
|
73
|
-
:foo.is.in 1,2,3,4
|
74
|
-
}.should == resulting_conditions
|
75
|
-
|
76
|
-
ActiveRecord::Base.where{
|
77
|
-
:foo.is.in [1,2,3,4]
|
78
|
-
}.should == resulting_conditions
|
79
|
-
|
80
|
-
ActiveRecord::Base.where{
|
81
|
-
:foo.is.in 1..4
|
82
|
-
}.should == [:all, {:conditions => ["foo in (?)", 1..4]}]
|
83
|
-
|
84
|
-
ActiveRecord::Base.where{
|
85
|
-
:foo.in 1,2,3,4
|
86
|
-
}.should == resulting_conditions
|
87
|
-
|
88
|
-
ActiveRecord::Base.where{
|
89
|
-
:foo.in [1,2,3,4]
|
90
|
-
}.should == resulting_conditions
|
91
|
-
|
92
|
-
ActiveRecord::Base.where{
|
93
|
-
:foo.in 1..4
|
94
|
-
}.should == [:all, {:conditions => ["foo in (?)", 1..4]}]
|
95
|
-
|
96
|
-
end
|
97
|
-
|
98
|
-
it "should return sql with foo, operations, and values for :foo.is.between and :foo.between when used with a list of args, array, and range" do
|
99
|
-
|
100
|
-
resulting_conditions = [:all, {:conditions => ["foo between ? and ?", 1, 2]}]
|
101
|
-
|
102
|
-
ActiveRecord::Base.where{
|
103
|
-
:foo.is.between 1,2
|
104
|
-
}.should == resulting_conditions
|
105
|
-
|
106
|
-
ActiveRecord::Base.where{
|
107
|
-
:foo.is.between [1,2]
|
108
|
-
}.should == resulting_conditions
|
109
|
-
|
110
|
-
ActiveRecord::Base.where{
|
111
|
-
:foo.is.between 1..2
|
112
|
-
}.should == resulting_conditions
|
113
|
-
|
114
|
-
ActiveRecord::Base.where{
|
115
|
-
:foo.between 1,2
|
116
|
-
}.should == resulting_conditions
|
117
|
-
|
118
|
-
ActiveRecord::Base.where{
|
119
|
-
:foo.between [1,2]
|
120
|
-
}.should == resulting_conditions
|
121
|
-
|
122
|
-
ActiveRecord::Base.where{
|
123
|
-
:foo.between 1..2
|
124
|
-
}.should == resulting_conditions
|
125
|
-
|
126
|
-
end
|
127
|
-
|
128
|
-
it "should return sql with foo, operations, and values for :foo.is.from when used with a list of args, array, and range" do
|
129
|
-
|
130
|
-
resulting_conditions = [:all, {:conditions => ["foo between ? and ?", 1, 2]}]
|
131
|
-
|
132
|
-
ActiveRecord::Base.where{
|
133
|
-
:foo.is.from 1,2
|
134
|
-
}.should == resulting_conditions
|
135
|
-
|
136
|
-
ActiveRecord::Base.where{
|
137
|
-
:foo.is.from [1,2]
|
138
|
-
}.should == resulting_conditions
|
139
|
-
|
140
|
-
ActiveRecord::Base.where{
|
141
|
-
:foo.is.from 1..2
|
142
|
-
}.should == resulting_conditions
|
143
|
-
|
144
|
-
ActiveRecord::Base.where{
|
145
|
-
:foo.from 1,2
|
146
|
-
}.should == resulting_conditions
|
147
|
-
|
148
|
-
ActiveRecord::Base.where{
|
149
|
-
:foo.from [1,2]
|
150
|
-
}.should == resulting_conditions
|
151
|
-
|
152
|
-
ActiveRecord::Base.where{
|
153
|
-
:foo.from 1..2
|
154
|
-
}.should == resulting_conditions
|
155
|
-
|
156
|
-
|
157
|
-
end
|
158
|
-
|
159
|
-
it "should return sql with foo, operations, and values for :foo.contains when used with a range, array, and list" do
|
160
|
-
|
161
|
-
resulting_conditions = [:all, {:conditions => ["foo like '%' || ? || '%'", "bar"]}]
|
162
|
-
|
163
|
-
ActiveRecord::Base.where{
|
164
|
-
:foo.contains "bar"
|
165
|
-
}.should == resulting_conditions
|
166
|
-
|
167
|
-
end
|
168
|
-
|
169
|
-
|
170
|
-
it "should return return the correct group of joined sql after multiple operations" do
|
171
|
-
|
172
|
-
ActiveRecord::Base.where{
|
173
|
-
:foo.is == "bar"
|
174
|
-
:foo.is_not.in 1,2,3,4,5
|
175
|
-
}.should == [:all, {:conditions => ["foo = ? and foo not in (?)", "bar", [1,2,3,4,5]]}]
|
176
|
-
|
177
|
-
end
|
178
|
-
|
179
|
-
it "should return return the correct limit value passed" do
|
180
|
-
|
181
|
-
ActiveRecord::Base.where(:first){
|
182
|
-
:foo.is == "bar"
|
183
|
-
:foo.is_not.in 1,2,3,4,5
|
184
|
-
}.should == [:first, {:conditions => ["foo = ? and foo not in (?)", "bar", [1,2,3,4,5]]}]
|
185
|
-
|
186
|
-
end
|
187
|
-
|
188
|
-
it "should have the correct 'not' keywords in alternating operations" do
|
189
|
-
|
190
|
-
ActiveRecord::Base.where(:first){
|
191
|
-
:foo.is == "bar"
|
192
|
-
:foo.is_not.in 1,2,3,4,5
|
193
|
-
:foo.is > 3
|
194
|
-
}.should == [:first, {:conditions => ["foo = ? and foo not in (?) and foo > ?", "bar", [1,2,3,4,5], 3]}]
|
195
|
-
|
196
|
-
end
|
197
|
-
|
198
|
-
it "should return return strings as arguments when passed to between, in, and from (used for date strings)" do
|
199
|
-
|
200
|
-
ActiveRecord::Base.where{
|
201
|
-
:foo.is.between "some string", "2007-01-01"
|
202
|
-
}.should == [:all, {:conditions => ["foo between ? and ?", "some string", "2007-01-01"]}]
|
203
|
-
|
204
|
-
ActiveRecord::Base.where{
|
205
|
-
:foo.is_not.between "some string", "2007-01-01"
|
206
|
-
}.should == [:all, {:conditions => ["foo not between ? and ?", "some string", "2007-01-01"]}]
|
207
|
-
|
208
|
-
ActiveRecord::Base.where{
|
209
|
-
:foo.is.from "some string", "2007-01-01"
|
210
|
-
}.should == [:all, {:conditions => ["foo between ? and ?", "some string", "2007-01-01"]}]
|
211
|
-
|
212
|
-
ActiveRecord::Base.where{
|
213
|
-
:foo.in "some string", "2007-01-01"
|
214
|
-
}.should == [:all, {:conditions => ["foo in (?)", ["some string", "2007-01-01"]]}]
|
215
|
-
|
216
|
-
end
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
end
|
data/spec/declarations_spec.rb
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
|
2
|
-
require File.expand_path(File.dirname(__FILE__) + "/../lib/rquery.rb")
|
3
|
-
|
4
|
-
describe RQuery::Declarations do
|
5
|
-
|
6
|
-
before(:all) do
|
7
|
-
RQuery.adapter = RQuery::Adapters::Sqlite
|
8
|
-
end
|
9
|
-
|
10
|
-
after(:each) do
|
11
|
-
RQuery::Serializers::Operations.clear
|
12
|
-
end
|
13
|
-
|
14
|
-
it "any Object should respond to is, is_not, in, between, from, and contains methods" do
|
15
|
-
:foo.respond_to?(:is).should == true
|
16
|
-
:foo.respond_to?(:is_not).should == true
|
17
|
-
:foo.respond_to?(:in).should == true
|
18
|
-
:foo.respond_to?(:from).should == true
|
19
|
-
:foo.respond_to?(:between).should == true
|
20
|
-
:foo.respond_to?(:from).should == true
|
21
|
-
end
|
22
|
-
|
23
|
-
it "should return an Serializer Class when is, is_not are called" do
|
24
|
-
:foo.is.class.should == Class
|
25
|
-
:foo.is_not.class.should == Class
|
26
|
-
end
|
27
|
-
|
28
|
-
it "should add an Operation for each call to is, is_not, and in" do
|
29
|
-
:foo.is
|
30
|
-
:bar.is
|
31
|
-
RQuery::Serializers::Operations.conditions.should == ["foo and bar"]
|
32
|
-
end
|
33
|
-
|
34
|
-
it "should add an Operation and the arguments for each call to from, contain, and between" do
|
35
|
-
:foo.between 1,2
|
36
|
-
RQuery::Serializers::Operations.conditions.should == ["foo between ? and ?", 1 , 2]
|
37
|
-
RQuery::Serializers::Operations.clear
|
38
|
-
:bar.from 1,2
|
39
|
-
RQuery::Serializers::Operations.conditions.should == ["bar between ? and ?", 1 , 2]
|
40
|
-
RQuery::Serializers::Operations.clear
|
41
|
-
:baz.contains "something"
|
42
|
-
RQuery::Serializers::Operations.conditions.should == ["baz like '%' || ? || '%'", "something"]
|
43
|
-
RQuery::Serializers::Operations.clear
|
44
|
-
end
|
45
|
-
|
46
|
-
|
47
|
-
end
|
data/spec/serializers_spec.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
|
2
|
-
require File.expand_path(File.dirname(__FILE__) + "/../lib/rquery.rb")
|
3
|
-
|
4
|
-
describe RQuery::Serializers do
|
5
|
-
|
6
|
-
it "Object.is should define ==, <, <=, >, >=, in, and between" do
|
7
|
-
:foo.is.respond_to?(:==).should == true
|
8
|
-
:foo.is.respond_to?(:<).should == true
|
9
|
-
:foo.is.respond_to?(:<=).should == true
|
10
|
-
:foo.is.respond_to?(:>).should == true
|
11
|
-
:foo.is.respond_to?(:>=).should == true
|
12
|
-
:foo.is.respond_to?(:in).should == true
|
13
|
-
:foo.is.respond_to?(:between).should == true
|
14
|
-
end
|
15
|
-
|
16
|
-
it "Object.is_not should define == and in" do
|
17
|
-
:foo.is_not.respond_to?(:==).should == true
|
18
|
-
:foo.is_not.respond_to?(:in).should == true
|
19
|
-
:foo.is_not.respond_to?(:between).should == true
|
20
|
-
end
|
21
|
-
|
22
|
-
it "Object.is_not should not redefine <, <=, >, >= in the same way that .is did" do
|
23
|
-
lambda {:foo.is_not.send(:<)}.should raise_error(ArgumentError)
|
24
|
-
lambda {:foo.is_not.send(:<=)}.should raise_error(ArgumentError)
|
25
|
-
lambda {:foo.is_not.send(:>)}.should raise_error(ArgumentError)
|
26
|
-
lambda {:foo.is_not.send(:>=)}.should raise_error(ArgumentError)
|
27
|
-
end
|
28
|
-
|
29
|
-
|
30
|
-
end
|