oedipus 0.0.8 → 0.0.9
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.
- data/ext/oedipus/oedipus.c +69 -8
- data/ext/oedipus/oedipus.h +14 -5
- data/lib/oedipus/comparison/between.rb +2 -7
- data/lib/oedipus/comparison/equal.rb +2 -2
- data/lib/oedipus/comparison/gt.rb +2 -2
- data/lib/oedipus/comparison/gte.rb +2 -2
- data/lib/oedipus/comparison/in.rb +2 -2
- data/lib/oedipus/comparison/lt.rb +2 -2
- data/lib/oedipus/comparison/lte.rb +2 -2
- data/lib/oedipus/comparison/not.rb +2 -2
- data/lib/oedipus/comparison/not_equal.rb +2 -2
- data/lib/oedipus/comparison/not_in.rb +2 -2
- data/lib/oedipus/comparison/outside.rb +2 -7
- data/lib/oedipus/comparison.rb +5 -5
- data/lib/oedipus/connection.rb +15 -48
- data/lib/oedipus/index.rb +14 -9
- data/lib/oedipus/query_builder.rb +49 -26
- data/lib/oedipus/version.rb +1 -1
- data/spec/integration/connection_spec.rb +59 -2
- data/spec/unit/comparison/between_spec.rb +4 -4
- data/spec/unit/comparison/equal_spec.rb +2 -2
- data/spec/unit/comparison/gt_spec.rb +2 -2
- data/spec/unit/comparison/gte_spec.rb +2 -2
- data/spec/unit/comparison/in_spec.rb +2 -2
- data/spec/unit/comparison/lt_spec.rb +2 -2
- data/spec/unit/comparison/lte_spec.rb +2 -2
- data/spec/unit/comparison/not_equal_spec.rb +2 -2
- data/spec/unit/comparison/not_in_spec.rb +2 -2
- data/spec/unit/comparison/not_spec.rb +2 -2
- data/spec/unit/comparison/outside_spec.rb +4 -4
- data/spec/unit/query_builder_spec.rb +64 -27
- metadata +8 -8
data/ext/oedipus/oedipus.c
CHANGED
@@ -85,10 +85,15 @@ static VALUE odp_close(VALUE self) {
|
|
85
85
|
return Qtrue;
|
86
86
|
}
|
87
87
|
|
88
|
-
static VALUE odp_execute(VALUE
|
88
|
+
static VALUE odp_execute(int argc, VALUE * args, VALUE self) {
|
89
|
+
VALUE sql;
|
89
90
|
OdpMysql * conn;
|
90
91
|
|
91
|
-
|
92
|
+
if (0 == argc) {
|
93
|
+
rb_raise(rb_eArgError, "Wrong number of arguments (0 for 1..*)");
|
94
|
+
}
|
95
|
+
|
96
|
+
Check_Type(args[0], T_STRING);
|
92
97
|
|
93
98
|
Data_Get_Struct(self, OdpMysql, conn);
|
94
99
|
|
@@ -96,6 +101,8 @@ static VALUE odp_execute(VALUE self, VALUE sql) {
|
|
96
101
|
odp_raise(self, "Cannot execute query on a closed connection");
|
97
102
|
}
|
98
103
|
|
104
|
+
sql = odp_replace_bind_values(conn, args[0], &args[1], argc - 1);
|
105
|
+
|
99
106
|
if (mysql_query(conn->ptr, RSTRING_PTR(sql))) {
|
100
107
|
odp_raise(self, "Failed to execute statement(s)");
|
101
108
|
}
|
@@ -103,7 +110,8 @@ static VALUE odp_execute(VALUE self, VALUE sql) {
|
|
103
110
|
return INT2NUM(mysql_affected_rows(conn->ptr));
|
104
111
|
}
|
105
112
|
|
106
|
-
static VALUE odp_query(VALUE
|
113
|
+
static VALUE odp_query(int argc, VALUE * args, VALUE self) {
|
114
|
+
VALUE sql;
|
107
115
|
OdpMysql * conn;
|
108
116
|
MYSQL_RES * rs;
|
109
117
|
int status;
|
@@ -116,7 +124,11 @@ static VALUE odp_query(VALUE self, VALUE sql) {
|
|
116
124
|
VALUE hash;
|
117
125
|
VALUE results;
|
118
126
|
|
119
|
-
|
127
|
+
if (0 == argc) {
|
128
|
+
rb_raise(rb_eArgError, "Wrong number of arguments (0 for 1..*)");
|
129
|
+
}
|
130
|
+
|
131
|
+
Check_Type(args[0], T_STRING);
|
120
132
|
|
121
133
|
Data_Get_Struct(self, OdpMysql, conn);
|
122
134
|
|
@@ -124,6 +136,8 @@ static VALUE odp_query(VALUE self, VALUE sql) {
|
|
124
136
|
odp_raise(self, "Cannot execute query on a closed connection");
|
125
137
|
}
|
126
138
|
|
139
|
+
sql = odp_replace_bind_values(conn, args[0], &args[1], argc - 1);
|
140
|
+
|
127
141
|
if (mysql_query(conn->ptr, RSTRING_PTR(sql))) {
|
128
142
|
odp_raise(self, "Failed to execute statement(s)");
|
129
143
|
}
|
@@ -175,6 +189,50 @@ static void odp_free(OdpMysql * conn) {
|
|
175
189
|
free(conn);
|
176
190
|
}
|
177
191
|
|
192
|
+
static VALUE odp_replace_bind_values(OdpMysql * conn, VALUE sql, VALUE * bind_values, int num_values) {
|
193
|
+
int i;
|
194
|
+
VALUE v;
|
195
|
+
VALUE q;
|
196
|
+
VALUE idx;
|
197
|
+
|
198
|
+
q = rb_str_new("?", 1);
|
199
|
+
sql = rb_funcall(sql, rb_intern("dup"), 0);
|
200
|
+
|
201
|
+
// FIXME: Do a real lexical scan of the string, to avoid replacing '?' inside comments/strings
|
202
|
+
|
203
|
+
for (i = 0; i < num_values; ++i) {
|
204
|
+
if ((idx = rb_funcall(sql, rb_intern("index"), 1, q)) == Qnil) {
|
205
|
+
break;
|
206
|
+
}
|
207
|
+
|
208
|
+
v = bind_values[i];
|
209
|
+
|
210
|
+
if (ODP_KIND_OF_P(v, rb_cInteger)) {
|
211
|
+
ODP_STR_SUB(sql, idx, ODP_TO_S(v));
|
212
|
+
} else if (ODP_KIND_OF_P(v, rb_cNumeric)) {
|
213
|
+
ODP_STR_SUB(sql, idx, ODP_TO_S(ODP_TO_F(v)));
|
214
|
+
} else {
|
215
|
+
if (T_STRING != TYPE(v)) {
|
216
|
+
v = ODP_TO_S(v);
|
217
|
+
}
|
218
|
+
|
219
|
+
{
|
220
|
+
char * v_ptr = RSTRING_PTR(v);
|
221
|
+
unsigned long v_len = strlen(v_ptr);
|
222
|
+
|
223
|
+
char escaped_str [v_len * 2 + 1];
|
224
|
+
char quoted_str [v_len * 2 + 3];
|
225
|
+
|
226
|
+
mysql_real_escape_string(conn->ptr, escaped_str, v_ptr, v_len);
|
227
|
+
sprintf(quoted_str, "'%s'", escaped_str);
|
228
|
+
ODP_STR_SUB(sql, idx, rb_str_new2(quoted_str));
|
229
|
+
}
|
230
|
+
}
|
231
|
+
}
|
232
|
+
|
233
|
+
return sql;
|
234
|
+
}
|
235
|
+
|
178
236
|
static VALUE odp_cast_value(MYSQL_FIELD f, char * v, unsigned long len) {
|
179
237
|
short s;
|
180
238
|
int i;
|
@@ -225,16 +283,19 @@ static VALUE odp_cast_value(MYSQL_FIELD f, char * v, unsigned long len) {
|
|
225
283
|
/* -- Extension initialization -- */
|
226
284
|
|
227
285
|
void Init_oedipus(void) {
|
286
|
+
VALUE mOedipus;
|
287
|
+
VALUE cMysql;
|
288
|
+
|
228
289
|
rb_require("bigdecimal");
|
229
290
|
|
230
|
-
|
231
|
-
|
291
|
+
mOedipus = rb_define_module("Oedipus");
|
292
|
+
cMysql = rb_define_class_under(mOedipus, "Mysql", rb_cObject);
|
232
293
|
|
233
294
|
rb_define_method(cMysql, "initialize", odp_initialize, 2);
|
234
295
|
rb_define_method(cMysql, "open", odp_open, 0);
|
235
296
|
rb_define_method(cMysql, "close", odp_close, 0);
|
236
|
-
rb_define_method(cMysql, "execute", odp_execute,
|
237
|
-
rb_define_method(cMysql, "query", odp_query,
|
297
|
+
rb_define_method(cMysql, "execute", odp_execute, -1);
|
298
|
+
rb_define_method(cMysql, "query", odp_query, -1);
|
238
299
|
|
239
300
|
rb_define_singleton_method(cMysql, "new", odp_new, 2);
|
240
301
|
}
|
data/ext/oedipus/oedipus.h
CHANGED
@@ -10,6 +10,12 @@
|
|
10
10
|
#include <ruby.h>
|
11
11
|
#include <mysql.h>
|
12
12
|
|
13
|
+
// Macros for lazy fingers
|
14
|
+
#define ODP_TO_S(v) rb_funcall(v, rb_intern("to_s"), 0)
|
15
|
+
#define ODP_TO_F(n) rb_funcall(n, rb_intern("to_f"), 0)
|
16
|
+
#define ODP_KIND_OF_P(v, type) (rb_funcall(v, rb_intern("kind_of?"), 1, type) == Qtrue)
|
17
|
+
#define ODP_STR_SUB(s, pos, replace) rb_funcall(s, rb_intern("[]="), 2, pos, replace)
|
18
|
+
|
13
19
|
/*! Internal struct used to reference a mysql connection */
|
14
20
|
typedef struct {
|
15
21
|
/*! Boolean representing the connected state */
|
@@ -33,13 +39,10 @@ static VALUE odp_open(VALUE self);
|
|
33
39
|
static VALUE odp_close(VALUE self);
|
34
40
|
|
35
41
|
/*! Execute an SQL non-read query and return the number of rows affected */
|
36
|
-
static VALUE odp_execute(VALUE
|
42
|
+
static VALUE odp_execute(int argc, VALUE * args, VALUE self);
|
37
43
|
|
38
44
|
/*! Execute several SQL read queries and return the result sets */
|
39
|
-
static VALUE odp_query(VALUE
|
40
|
-
|
41
|
-
/*! Cast the given field to a ruby data type */
|
42
|
-
static VALUE odp_cast_value(MYSQL_FIELD f, char * v, unsigned long len);
|
45
|
+
static VALUE odp_query(int argc, VALUE * args, VALUE self);
|
43
46
|
|
44
47
|
/* -- Internal methods -- */
|
45
48
|
|
@@ -48,3 +51,9 @@ static void odp_raise(VALUE self, const char *msg);
|
|
48
51
|
|
49
52
|
/*! Free memory allocated to mysql */
|
50
53
|
static void odp_free(OdpMysql * conn);
|
54
|
+
|
55
|
+
/*! Substitute all ? markers with the values in bind_values */
|
56
|
+
static VALUE odp_replace_bind_values(OdpMysql * conn, VALUE sql, VALUE * bind_values, int num_values);
|
57
|
+
|
58
|
+
/*! Cast the given field to a ruby data type */
|
59
|
+
static VALUE odp_cast_value(MYSQL_FIELD f, char * v, unsigned long len);
|
@@ -10,13 +10,8 @@
|
|
10
10
|
module Oedipus
|
11
11
|
# Between comparison of range.
|
12
12
|
class Comparison::Between < Comparison
|
13
|
-
def
|
14
|
-
[
|
15
|
-
"BETWEEN",
|
16
|
-
Connection.quote(v.first),
|
17
|
-
"AND",
|
18
|
-
Connection.quote(v.exclude_end? ? v.end - 1 : v.end)
|
19
|
-
].join(" ")
|
13
|
+
def to_sql
|
14
|
+
["BETWEEN ? AND ?", v.first, v.exclude_end? ? v.end - 1 : v.end]
|
20
15
|
end
|
21
16
|
|
22
17
|
def inverse
|
@@ -10,13 +10,8 @@
|
|
10
10
|
module Oedipus
|
11
11
|
# Outside comparison of range.
|
12
12
|
class Comparison::Outside < Comparison
|
13
|
-
def
|
14
|
-
[
|
15
|
-
"NOT BETWEEN",
|
16
|
-
Connection.quote(v.first),
|
17
|
-
"AND",
|
18
|
-
Connection.quote(v.exclude_end? ? v.end - 1 : v.end)
|
19
|
-
].join(" ")
|
13
|
+
def to_sql
|
14
|
+
["NOT BETWEEN ? AND ?", v.first, v.exclude_end? ? v.end - 1 : v.end]
|
20
15
|
end
|
21
16
|
|
22
17
|
def inverse
|
data/lib/oedipus/comparison.rb
CHANGED
@@ -77,12 +77,12 @@ module Oedipus
|
|
77
77
|
raise NotImplementedError, "Comparison#inverse must be defined by subclasses"
|
78
78
|
end
|
79
79
|
|
80
|
-
# Represent the comparison as
|
80
|
+
# Represent the comparison as SQL arguments.
|
81
81
|
#
|
82
|
-
# @return [
|
83
|
-
# an expression to compare a LHS against v
|
84
|
-
def
|
85
|
-
raise NotImplementedError, "Comparison#
|
82
|
+
# @return [Array]
|
83
|
+
# an SQL expression to compare a LHS against v
|
84
|
+
def to_sql
|
85
|
+
raise NotImplementedError, "Comparison#to_sql must be defined by subclasses"
|
86
86
|
end
|
87
87
|
end
|
88
88
|
end
|
data/lib/oedipus/connection.rb
CHANGED
@@ -12,48 +12,6 @@ module Oedipus
|
|
12
12
|
#
|
13
13
|
# Currently this class wraps a native mysql extension.
|
14
14
|
class Connection
|
15
|
-
class << self
|
16
|
-
# Quote a value (of any type) for use in SphinxQL.
|
17
|
-
#
|
18
|
-
# @param [Object] v
|
19
|
-
# the value to quote
|
20
|
-
#
|
21
|
-
# @return [Object]
|
22
|
-
# the safe value
|
23
|
-
#
|
24
|
-
# Note that single quotes are added to strings.
|
25
|
-
def quote(v)
|
26
|
-
require "bigdecimal" unless defined? BigDecimal
|
27
|
-
case v
|
28
|
-
when BigDecimal, Rational, Complex
|
29
|
-
v.to_f
|
30
|
-
when Numeric
|
31
|
-
v
|
32
|
-
else
|
33
|
-
"'#{escape_str(v.to_s)}'"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# Escape a string, without adding enclosing quotes.
|
38
|
-
#
|
39
|
-
# @param [String] str
|
40
|
-
# the unsafe input string
|
41
|
-
#
|
42
|
-
# @return [String]
|
43
|
-
# a safe string for use in SphinxQL
|
44
|
-
def escape_str(str)
|
45
|
-
str.gsub(/[\0\n\r\\\'\"\x1a]/) do |s|
|
46
|
-
case s
|
47
|
-
when "\0" then "\\0"
|
48
|
-
when "\n" then "\\n"
|
49
|
-
when "\r" then "\\r"
|
50
|
-
when "\x1a" then "\\Z"
|
51
|
-
else "\\#{s}"
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
15
|
# Instantiate a new Connection to a SphinxQL host.
|
58
16
|
#
|
59
17
|
# @param [String] server
|
@@ -99,12 +57,15 @@ module Oedipus
|
|
99
57
|
# @param [String] sql
|
100
58
|
# one or more SphinxQL statements, separated by semicolons
|
101
59
|
#
|
60
|
+
# @param [Object...] bind_values
|
61
|
+
# values to be substituted in place of '?' in the query
|
62
|
+
#
|
102
63
|
# @return [Array]
|
103
64
|
# an array of arrays, containing the returned records
|
104
65
|
#
|
105
66
|
# Note that SphinxQL does not support prepared statements.
|
106
|
-
def multi_query(sql)
|
107
|
-
@pool.acquire { |conn| conn.query(sql) }
|
67
|
+
def multi_query(sql, *bind_values)
|
68
|
+
@pool.acquire { |conn| conn.query(sql, *bind_values) }
|
108
69
|
end
|
109
70
|
|
110
71
|
# Execute a single read query.
|
@@ -112,12 +73,15 @@ module Oedipus
|
|
112
73
|
# @param [String] sql
|
113
74
|
# a single SphinxQL statement
|
114
75
|
#
|
76
|
+
# @param [Object...] bind_values
|
77
|
+
# values to be substituted in place of '?' in the query
|
78
|
+
#
|
115
79
|
# @return [Array]
|
116
80
|
# an array of Hashes containing the matched records
|
117
81
|
#
|
118
82
|
# Note that SphinxQL does not support prepared statements.
|
119
|
-
def query(sql)
|
120
|
-
@pool.acquire { |conn| conn.query(sql).first }
|
83
|
+
def query(sql, *bind_values)
|
84
|
+
@pool.acquire { |conn| conn.query(sql, *bind_values).first }
|
121
85
|
end
|
122
86
|
|
123
87
|
# Execute a non-read query.
|
@@ -125,12 +89,15 @@ module Oedipus
|
|
125
89
|
# @param [String] sql
|
126
90
|
# a SphinxQL query, such as INSERT or REPLACE
|
127
91
|
#
|
92
|
+
# @param [Object...] bind_values
|
93
|
+
# values to be substituted in place of '?' in the query
|
94
|
+
#
|
128
95
|
# @return [Fixnum]
|
129
96
|
# the number of affected rows
|
130
97
|
#
|
131
98
|
# Note that SphinxQL does not support prepared statements.
|
132
|
-
def execute(sql)
|
133
|
-
@pool.acquire { |conn| conn.execute(sql) }
|
99
|
+
def execute(sql, *bind_values)
|
100
|
+
@pool.acquire { |conn| conn.execute(sql, *bind_values) }
|
134
101
|
end
|
135
102
|
|
136
103
|
private
|
data/lib/oedipus/index.rb
CHANGED
@@ -39,7 +39,7 @@ module Oedipus
|
|
39
39
|
# @return [Fixnum]
|
40
40
|
# the number of rows inserted (currently always 1)
|
41
41
|
def insert(id, hash)
|
42
|
-
@conn.execute(
|
42
|
+
@conn.execute(*@builder.insert(id, hash))
|
43
43
|
end
|
44
44
|
|
45
45
|
# Update the record with the ID +id+.
|
@@ -56,7 +56,7 @@ module Oedipus
|
|
56
56
|
# @return [Fixnum]
|
57
57
|
# the number of rows updated (1 or 0)
|
58
58
|
def update(id, hash)
|
59
|
-
@conn.execute(
|
59
|
+
@conn.execute(*@builder.update(id, hash))
|
60
60
|
end
|
61
61
|
|
62
62
|
# Completely replace the record with the ID +id+.
|
@@ -73,7 +73,7 @@ module Oedipus
|
|
73
73
|
# @return [Fixnum]
|
74
74
|
# the number of rows inserted (currentl always 1)
|
75
75
|
def replace(id, hash)
|
76
|
-
@conn.execute(
|
76
|
+
@conn.execute(*@builder.replace(id, hash))
|
77
77
|
end
|
78
78
|
|
79
79
|
# Delete the record with the ID +id+.
|
@@ -87,7 +87,7 @@ module Oedipus
|
|
87
87
|
# @return [Fixnum]
|
88
88
|
# the number of rows deleted (currently always 1 or 0)
|
89
89
|
def delete(id)
|
90
|
-
@conn.execute(
|
90
|
+
@conn.execute(*@builder.delete(id))
|
91
91
|
end
|
92
92
|
|
93
93
|
# Fetch a single document by its ID.
|
@@ -220,11 +220,16 @@ module Oedipus
|
|
220
220
|
raise ArgumentError, "Argument must be a Hash of named queries (#{queries.class} given)"
|
221
221
|
end
|
222
222
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
223
|
+
stmts = []
|
224
|
+
bind_values = []
|
225
|
+
|
226
|
+
queries.each do |key, args|
|
227
|
+
str, *values = @builder.select(*extract_query_data(args))
|
228
|
+
stmts.push(str, "SHOW META")
|
229
|
+
bind_values.push(*values)
|
230
|
+
end
|
231
|
+
|
232
|
+
rs = @conn.multi_query(stmts.join(";\n"), *bind_values)
|
228
233
|
|
229
234
|
Hash[].tap do |result|
|
230
235
|
queries.keys.each do |key|
|
@@ -29,12 +29,16 @@ module Oedipus
|
|
29
29
|
# @return [String]
|
30
30
|
# a SphinxQL query
|
31
31
|
def select(query, filters)
|
32
|
+
where, *bind_values = conditions(query, filters)
|
32
33
|
[
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
[
|
35
|
+
from(filters),
|
36
|
+
where,
|
37
|
+
order_by(filters),
|
38
|
+
limits(filters)
|
39
|
+
].join(" "),
|
40
|
+
*bind_values
|
41
|
+
]
|
38
42
|
end
|
39
43
|
|
40
44
|
# Build a SphinxQL query to insert the record identified by +id+ with the given attributes.
|
@@ -62,11 +66,15 @@ module Oedipus
|
|
62
66
|
# @return [String]
|
63
67
|
# the SphinxQL to update the record
|
64
68
|
def update(id, attributes)
|
69
|
+
set_attrs, *bind_values = update_attributes(attributes)
|
65
70
|
[
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
71
|
+
[
|
72
|
+
"UPDATE #{@index_name} SET",
|
73
|
+
set_attrs,
|
74
|
+
"WHERE id = ?"
|
75
|
+
].join(" "),
|
76
|
+
*bind_values.push(id)
|
77
|
+
]
|
70
78
|
end
|
71
79
|
|
72
80
|
# Build a SphinxQL query to replace the record identified by +id+ with the given attributes.
|
@@ -91,11 +99,13 @@ module Oedipus
|
|
91
99
|
# @return [String]
|
92
100
|
# the SphinxQL to delete the record
|
93
101
|
def delete(id)
|
94
|
-
"DELETE FROM #{@index_name} WHERE id =
|
102
|
+
["DELETE FROM #{@index_name} WHERE id = ?", id]
|
95
103
|
end
|
96
104
|
|
97
105
|
private
|
98
106
|
|
107
|
+
RESERVED = [:attrs, :limit, :offset, :order]
|
108
|
+
|
99
109
|
def fields(filters)
|
100
110
|
filters.fetch(:attrs, [:*]).dup.tap do |fields|
|
101
111
|
if fields.none? { |a| /\brelevance\n/ === a } && normalize_order(filters).key?(:relevance)
|
@@ -114,32 +124,45 @@ module Oedipus
|
|
114
124
|
end
|
115
125
|
|
116
126
|
def into(type, id, attributes)
|
127
|
+
attrs, values = attributes.inject([[:id], [id]]) do |(a, b), (k, v)|
|
128
|
+
[a.push(k), b.push(v)]
|
129
|
+
end
|
130
|
+
|
117
131
|
[
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
132
|
+
[
|
133
|
+
type,
|
134
|
+
"INTO #{@index_name}",
|
135
|
+
"(#{attrs.join(', ')})",
|
136
|
+
"VALUES",
|
137
|
+
"(#{(['?'] * attrs.size).join(', ')})"
|
138
|
+
].join(" "),
|
139
|
+
*values
|
140
|
+
]
|
124
141
|
end
|
125
142
|
|
126
143
|
def conditions(query, filters)
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
144
|
+
sql = []
|
145
|
+
sql << ["MATCH(?)", query] unless query.empty?
|
146
|
+
sql.push(*attribute_conditions(filters))
|
147
|
+
|
148
|
+
exprs, bind_values = sql.inject([[], []]) do |(strs, values), v|
|
149
|
+
[strs.push(v.shift), values.push(*v)]
|
150
|
+
end
|
151
|
+
|
152
|
+
["WHERE " << exprs.join(" AND "), *bind_values] if exprs.any?
|
131
153
|
end
|
132
154
|
|
133
155
|
def attribute_conditions(filters)
|
134
|
-
filters
|
135
|
-
.
|
136
|
-
|
156
|
+
filters.reject{ |k, v| RESERVED.include?(k.to_sym) }.map do |k, v|
|
157
|
+
Comparison.of(v).to_sql.tap { |c| c[0].insert(0, "#{k} ") }
|
158
|
+
end
|
137
159
|
end
|
138
160
|
|
139
161
|
def update_attributes(attributes)
|
140
|
-
|
141
|
-
.map
|
142
|
-
.
|
162
|
+
[
|
163
|
+
attributes.keys.map{ |k| "#{k} = ?" }.join(", "),
|
164
|
+
*attributes.values
|
165
|
+
]
|
143
166
|
end
|
144
167
|
|
145
168
|
def order_by(filters)
|
data/lib/oedipus/version.rb
CHANGED
@@ -23,6 +23,8 @@ describe Oedipus::Connection do
|
|
23
23
|
|
24
24
|
before(:each) { empty_indexes }
|
25
25
|
|
26
|
+
let(:conn) { Oedipus::Connection.new(searchd_host) }
|
27
|
+
|
26
28
|
describe "#initialize" do
|
27
29
|
context "with a hosname:port string" do
|
28
30
|
context "on successful connection" do
|
@@ -58,10 +60,65 @@ describe Oedipus::Connection do
|
|
58
60
|
end
|
59
61
|
|
60
62
|
describe "#[]" do
|
61
|
-
let(:conn) { Oedipus::Connection.new(searchd_host) }
|
62
|
-
|
63
63
|
it "returns an index" do
|
64
64
|
conn[:posts_rt].should be_a_kind_of(Oedipus::Index)
|
65
65
|
end
|
66
66
|
end
|
67
|
+
|
68
|
+
describe "#query" do
|
69
|
+
it "accepts integer bind parameters" do
|
70
|
+
conn.query("SELECT * FROM posts_rt WHERE views = ? AND user_id = ?", 1, 7)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "accepts float bind parameters" do
|
74
|
+
conn.query("SELECT * FROM posts_rt WHERE views = ? AND user_id = ?", 1.2, 7.2)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "accepts decimal bind parameters" do
|
78
|
+
require "bigdecimal"
|
79
|
+
conn.query("SELECT * FROM posts_rt WHERE views = ? AND user_id = ?", BigDecimal("1.2"), BigDecimal("7.2"))
|
80
|
+
end
|
81
|
+
|
82
|
+
xit "accepts string bind parameters" do
|
83
|
+
conn.query("SELECT * FROM posts_rt WHERE state = ?", "something")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "#multi_query" do
|
88
|
+
it "accepts integer bind parameters" do
|
89
|
+
conn.multi_query("SELECT * FROM posts_rt WHERE views = ? AND user_id = ?", 1, 7)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "accepts float bind parameters" do
|
93
|
+
conn.multi_query("SELECT * FROM posts_rt WHERE views = ? AND user_id = ?", 1.2, 7.2)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "accepts decimal bind parameters" do
|
97
|
+
require "bigdecimal"
|
98
|
+
conn.multi_query("SELECT * FROM posts_rt WHERE views = ? AND user_id = ?", BigDecimal("1.2"), BigDecimal("7.2"))
|
99
|
+
end
|
100
|
+
|
101
|
+
xit "accepts string bind parameters" do
|
102
|
+
conn.multi_query("SELECT * FROM posts_rt WHERE state = ?", "something")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "#execute" do
|
107
|
+
it "accepts integer bind parameters" do
|
108
|
+
conn.execute("REPLACE INTO posts_rt (id, views) VALUES (?, ?)", 1, 7)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "accepts float bind parameters" do
|
112
|
+
conn.execute("REPLACE INTO posts_rt (id, views) VALUES (?, ?)", 1, 7.2)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "accepts decimal bind parameters" do
|
116
|
+
require "bigdecimal"
|
117
|
+
conn.execute("REPLACE INTO posts_rt (id, views) VALUES (?, ?)", 1, BigDecimal("7.2"))
|
118
|
+
end
|
119
|
+
|
120
|
+
it "accepts string bind parameters" do
|
121
|
+
conn.execute("REPLACE INTO posts_rt (id, title) VALUES (?, ?)", 1, "an example with `\"this (quoted) string\\'")
|
122
|
+
end
|
123
|
+
end
|
67
124
|
end
|
@@ -14,11 +14,11 @@ describe Oedipus::Comparison::Between do
|
|
14
14
|
let(:comparison) { Oedipus::Comparison::Between.new(42..100) }
|
15
15
|
|
16
16
|
it "draws as BETWEEN x AND y" do
|
17
|
-
comparison.
|
17
|
+
comparison.to_sql.should == ["BETWEEN ? AND ?", 42, 100]
|
18
18
|
end
|
19
19
|
|
20
20
|
it "inverses as NOT BETWEEN x AND y" do
|
21
|
-
comparison.inverse.
|
21
|
+
comparison.inverse.to_sql.should == ["NOT BETWEEN ? AND ?", 42, 100]
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -26,11 +26,11 @@ describe Oedipus::Comparison::Between do
|
|
26
26
|
let(:comparison) { Oedipus::Comparison::Between.new(42...100) }
|
27
27
|
|
28
28
|
it "draws as BETWEEN x AND y-1" do
|
29
|
-
comparison.
|
29
|
+
comparison.to_sql.should == ["BETWEEN ? AND ?", 42, 99]
|
30
30
|
end
|
31
31
|
|
32
32
|
it "inverses as NOT BETWEEN x AND y-1" do
|
33
|
-
comparison.inverse.
|
33
|
+
comparison.inverse.to_sql.should == ["NOT BETWEEN ? AND ?", 42, 99]
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
@@ -13,10 +13,10 @@ describe Oedipus::Comparison::Equal do
|
|
13
13
|
let(:comparison) { Oedipus::Comparison::Equal.new('test') }
|
14
14
|
|
15
15
|
it "draws as = v" do
|
16
|
-
comparison.
|
16
|
+
comparison.to_sql.should == ["= ?", "test"]
|
17
17
|
end
|
18
18
|
|
19
19
|
it "inverses as != v" do
|
20
|
-
comparison.inverse.
|
20
|
+
comparison.inverse.to_sql.should == ["!= ?", "test"]
|
21
21
|
end
|
22
22
|
end
|
@@ -13,10 +13,10 @@ describe Oedipus::Comparison::GT do
|
|
13
13
|
let(:comparison) { Oedipus::Comparison::GT.new(42) }
|
14
14
|
|
15
15
|
it "draws as > v" do
|
16
|
-
comparison.
|
16
|
+
comparison.to_sql.should == ["> ?", 42]
|
17
17
|
end
|
18
18
|
|
19
19
|
it "inverses as <= v" do
|
20
|
-
comparison.inverse.
|
20
|
+
comparison.inverse.to_sql.should == ["<= ?", 42]
|
21
21
|
end
|
22
22
|
end
|
@@ -13,10 +13,10 @@ describe Oedipus::Comparison::GTE do
|
|
13
13
|
let(:comparison) { Oedipus::Comparison::GTE.new(42) }
|
14
14
|
|
15
15
|
it "draws as >= v" do
|
16
|
-
comparison.
|
16
|
+
comparison.to_sql.should == [">= ?", 42]
|
17
17
|
end
|
18
18
|
|
19
19
|
it "inverses as < v" do
|
20
|
-
comparison.inverse.
|
20
|
+
comparison.inverse.to_sql.should == ["< ?", 42]
|
21
21
|
end
|
22
22
|
end
|
@@ -13,10 +13,10 @@ describe Oedipus::Comparison::In do
|
|
13
13
|
let(:comparison) { Oedipus::Comparison::In.new([1, 2, 3]) }
|
14
14
|
|
15
15
|
it "draws as IN (x, y, z)" do
|
16
|
-
comparison.
|
16
|
+
comparison.to_sql.should == ["IN (?, ?, ?)", 1, 2, 3]
|
17
17
|
end
|
18
18
|
|
19
19
|
it "inverses as NOT IN (x, y, z)" do
|
20
|
-
comparison.inverse.
|
20
|
+
comparison.inverse.to_sql.should == ["NOT IN (?, ?, ?)", 1, 2, 3]
|
21
21
|
end
|
22
22
|
end
|
@@ -13,10 +13,10 @@ describe Oedipus::Comparison::LT do
|
|
13
13
|
let(:comparison) { Oedipus::Comparison::LT.new(42) }
|
14
14
|
|
15
15
|
it "draws as < v" do
|
16
|
-
comparison.
|
16
|
+
comparison.to_sql.should == ["< ?", 42]
|
17
17
|
end
|
18
18
|
|
19
19
|
it "inverses as >= v" do
|
20
|
-
comparison.inverse.
|
20
|
+
comparison.inverse.to_sql.should == [">= ?", 42]
|
21
21
|
end
|
22
22
|
end
|
@@ -13,10 +13,10 @@ describe Oedipus::Comparison::LTE do
|
|
13
13
|
let(:comparison) { Oedipus::Comparison::LTE.new(42) }
|
14
14
|
|
15
15
|
it "draws as <= v" do
|
16
|
-
comparison.
|
16
|
+
comparison.to_sql.should == ["<= ?", 42]
|
17
17
|
end
|
18
18
|
|
19
19
|
it "inverses as > v" do
|
20
|
-
comparison.inverse.
|
20
|
+
comparison.inverse.to_sql.should == ["> ?", 42]
|
21
21
|
end
|
22
22
|
end
|
@@ -13,10 +13,10 @@ describe Oedipus::Comparison::NotEqual do
|
|
13
13
|
let(:comparison) { Oedipus::Comparison::NotEqual.new('test') }
|
14
14
|
|
15
15
|
it "draws as != v" do
|
16
|
-
comparison.
|
16
|
+
comparison.to_sql.should == ["!= ?", "test"]
|
17
17
|
end
|
18
18
|
|
19
19
|
it "inverses as = v" do
|
20
|
-
comparison.inverse.
|
20
|
+
comparison.inverse.to_sql.should == ["= ?", "test"]
|
21
21
|
end
|
22
22
|
end
|
@@ -13,10 +13,10 @@ describe Oedipus::Comparison::NotIn do
|
|
13
13
|
let(:comparison) { Oedipus::Comparison::NotIn.new([1, 2, 3]) }
|
14
14
|
|
15
15
|
it "draws as NOT IN (x, y, z)" do
|
16
|
-
comparison.
|
16
|
+
comparison.to_sql.should == ["NOT IN (?, ?, ?)", 1, 2, 3]
|
17
17
|
end
|
18
18
|
|
19
19
|
it "inverses as IN (x, y, z)" do
|
20
|
-
comparison.inverse.
|
20
|
+
comparison.inverse.to_sql.should == ["IN (?, ?, ?)", 1, 2, 3]
|
21
21
|
end
|
22
22
|
end
|
@@ -27,11 +27,11 @@ describe Oedipus::Comparison::In do
|
|
27
27
|
let(:comparison) { Oedipus::Comparison::Not.new(original) }
|
28
28
|
|
29
29
|
it "draws as the inverse of the comparison" do
|
30
|
-
comparison.
|
30
|
+
comparison.to_sql.should == original.inverse.to_sql
|
31
31
|
end
|
32
32
|
|
33
33
|
it "inverses as the original" do
|
34
|
-
comparison.inverse.
|
34
|
+
comparison.inverse.to_sql == original.to_sql
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
@@ -14,11 +14,11 @@ describe Oedipus::Comparison::Outside do
|
|
14
14
|
let(:comparison) { Oedipus::Comparison::Outside.new(42..100) }
|
15
15
|
|
16
16
|
it "draws as NOT BETWEEN x AND y" do
|
17
|
-
comparison.
|
17
|
+
comparison.to_sql.should == ["NOT BETWEEN ? AND ?", 42, 100]
|
18
18
|
end
|
19
19
|
|
20
20
|
it "inverses as BETWEEN x AND y" do
|
21
|
-
comparison.inverse.
|
21
|
+
comparison.inverse.to_sql.should == ["BETWEEN ? AND ?", 42, 100]
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -26,11 +26,11 @@ describe Oedipus::Comparison::Outside do
|
|
26
26
|
let(:comparison) { Oedipus::Comparison::Outside.new(42...100) }
|
27
27
|
|
28
28
|
it "draws as NOT BETWEEN x AND y-1" do
|
29
|
-
comparison.
|
29
|
+
comparison.to_sql.should == ["NOT BETWEEN ? AND ?", 42, 99]
|
30
30
|
end
|
31
31
|
|
32
32
|
it "inverses as BETWEEN x AND y-1" do
|
33
|
-
comparison.inverse.
|
33
|
+
comparison.inverse.to_sql.should == ["BETWEEN ? AND ?", 42, 99]
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
@@ -15,124 +15,157 @@ describe Oedipus::QueryBuilder do
|
|
15
15
|
describe "#select" do
|
16
16
|
context "with a fulltext search" do
|
17
17
|
it "uses MATCH()" do
|
18
|
-
builder.select("dogs AND cats", {})
|
18
|
+
sql = builder.select("dogs AND cats", {})
|
19
|
+
sql[0].should =~ /SELECT .* FROM posts WHERE MATCH\(\?\)/
|
20
|
+
sql.slice(1..-1).should == ["dogs AND cats"]
|
19
21
|
end
|
20
22
|
end
|
21
23
|
|
22
24
|
context "without conditions" do
|
23
25
|
it "does not add a where clause" do
|
24
|
-
builder.select("", {}).should_not =~ /WHERE/
|
26
|
+
builder.select("", {})[0].should_not =~ /WHERE/
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
28
30
|
context "with equal attribute filters" do
|
29
31
|
it "uses the '=' operator" do
|
30
|
-
builder.select("dogs", author_id: 7)
|
32
|
+
sql = builder.select("dogs", author_id: 7)
|
33
|
+
sql[0].should =~ /SELECT .* FROM posts WHERE .* author_id = \?/
|
34
|
+
sql.slice(1..-1).should == ["dogs", 7]
|
31
35
|
end
|
32
36
|
end
|
33
37
|
|
34
38
|
context "with not equal attribute filters" do
|
35
39
|
it "uses the '!=' operator" do
|
36
|
-
builder.select("dogs", author_id: Oedipus.not(7))
|
40
|
+
sql = builder.select("dogs", author_id: Oedipus.not(7))
|
41
|
+
sql[0].should =~ /SELECT .* FROM posts WHERE .* author_id != \?/
|
42
|
+
sql.slice(1..-1).should == ["dogs", 7]
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
40
46
|
context "with inclusive range-filtered attribute filters" do
|
41
47
|
it "uses the BETWEEN operator" do
|
42
|
-
builder.select("cats", views: 10..20)
|
48
|
+
sql = builder.select("cats", views: 10..20)
|
49
|
+
sql[0].should =~ /SELECT .* FROM posts WHERE .* views BETWEEN \? AND \?/
|
50
|
+
sql.slice(1..-1).should == ["cats", 10, 20]
|
43
51
|
end
|
44
52
|
end
|
45
53
|
|
46
54
|
context "with exclusive range-filtered attribute filters" do
|
47
55
|
it "uses the BETWEEN operator" do
|
48
|
-
builder.select("cats", views: 10...20)
|
56
|
+
sql = builder.select("cats", views: 10...20)
|
57
|
+
sql[0].should =~ /SELECT .* FROM posts WHERE .* views BETWEEN \? AND \?/
|
58
|
+
sql.slice(1..-1).should == ["cats", 10, 19]
|
49
59
|
end
|
50
60
|
end
|
51
61
|
|
52
62
|
context "with a greater than or equal comparison" do
|
53
63
|
it "uses the >= operator" do
|
54
|
-
builder.select("cats", views: 50..(1/0.0))
|
64
|
+
sql = builder.select("cats", views: 50..(1/0.0))
|
65
|
+
sql[0].should =~ /SELECT .* FROM posts WHERE .* views >= \?/
|
66
|
+
sql.slice(1..-1).should == ["cats", 50]
|
55
67
|
end
|
56
68
|
end
|
57
69
|
|
58
70
|
context "with a greater than comparison" do
|
59
71
|
it "uses the > operator" do
|
60
|
-
builder.select("cats", views: 50...(1/0.0))
|
72
|
+
sql = builder.select("cats", views: 50...(1/0.0))
|
73
|
+
sql[0].should =~ /SELECT .* FROM posts WHERE .* views > \?/
|
74
|
+
sql.slice(1..-1).should == ["cats", 50]
|
61
75
|
end
|
62
76
|
end
|
63
77
|
|
64
78
|
context "with a less than or equal comparison" do
|
65
79
|
it "uses the <= operator" do
|
66
|
-
builder.select("cats", views: -(1/0.0)..50)
|
80
|
+
sql = builder.select("cats", views: -(1/0.0)..50)
|
81
|
+
sql[0].should =~ /SELECT .* FROM posts WHERE .* views <= \?/
|
82
|
+
sql.slice(1..-1).should == ["cats", 50]
|
67
83
|
end
|
68
84
|
end
|
69
85
|
|
70
86
|
context "with a less than comparison" do
|
71
87
|
it "uses the < operator" do
|
72
|
-
builder.select("cats", views: -(1/0.0)...50)
|
88
|
+
sql = builder.select("cats", views: -(1/0.0)...50)
|
89
|
+
sql[0].should =~ /SELECT .* FROM posts WHERE .* views < \?/
|
90
|
+
sql.slice(1..-1).should == ["cats", 50]
|
73
91
|
end
|
74
92
|
end
|
75
93
|
|
76
94
|
context "with a negated range comparison" do
|
77
95
|
it "uses the NOT BETWEEN operator" do
|
78
|
-
builder.select("cats", views: Oedipus.not(50..100))
|
96
|
+
sql = builder.select("cats", views: Oedipus.not(50..100))
|
97
|
+
sql[0].should =~ /SELECT .* FROM posts WHERE .* views NOT BETWEEN \? AND \?/
|
98
|
+
sql.slice(1..-1).should == ["cats", 50, 100]
|
79
99
|
end
|
80
100
|
end
|
81
101
|
|
82
102
|
context "with an attributed filtered by collection" do
|
83
103
|
it "uses the IN operator" do
|
84
|
-
builder.select("cats", author_id: [7, 11])
|
104
|
+
sql = builder.select("cats", author_id: [7, 11])
|
105
|
+
sql[0].should =~ /SELECT .* FROM posts WHERE .* author_id IN \(\?, \?\)/
|
106
|
+
sql.slice(1..-1).should == ["cats", 7, 11]
|
85
107
|
end
|
86
108
|
end
|
87
109
|
|
88
110
|
context "with an attributed filtered by negated collection" do
|
89
111
|
it "uses the NOT IN operator" do
|
90
|
-
builder.select("cats", author_id: Oedipus.not([7, 11]))
|
112
|
+
sql = builder.select("cats", author_id: Oedipus.not([7, 11]))
|
113
|
+
sql[0].should =~ /SELECT .* FROM posts WHERE .* author_id NOT IN \(\?, \?\)/
|
114
|
+
sql.slice(1..-1).should == ["cats", 7, 11]
|
91
115
|
end
|
92
116
|
end
|
93
117
|
|
94
118
|
context "with explicit attributes" do
|
95
119
|
it "puts the attributes in the select clause" do
|
96
|
-
builder.select("cats", attrs: [:*, "FOO() AS f"])
|
120
|
+
sql = builder.select("cats", attrs: [:*, "FOO() AS f"])
|
121
|
+
sql[0].should =~ /SELECT \*, FOO\(\) AS f FROM posts/
|
97
122
|
end
|
98
123
|
end
|
99
124
|
|
100
125
|
context "with a limit" do
|
101
126
|
it "applies a LIMIT with an offset of 0" do
|
102
|
-
builder.select("dogs", limit: 50)
|
127
|
+
sql = builder.select("dogs", limit: 50)
|
128
|
+
sql[0].should =~ /SELECT .* FROM posts WHERE .* LIMIT 0, 50/
|
103
129
|
end
|
104
130
|
|
105
131
|
it "is not considered an attribute" do
|
106
|
-
builder.select("dogs", limit: 50)
|
132
|
+
sql = builder.select("dogs", limit: 50)
|
133
|
+
sql.slice(1..-1).should == ["dogs"]
|
107
134
|
end
|
108
135
|
end
|
109
136
|
|
110
137
|
context "with an offset" do
|
111
138
|
it "applies a LIMIT with an offset" do
|
112
|
-
builder.select("dogs", limit: 50, offset: 200)
|
139
|
+
sql = builder.select("dogs", limit: 50, offset: 200)
|
140
|
+
sql[0].should =~ /SELECT .* FROM posts WHERE .* LIMIT 200, 50/
|
113
141
|
end
|
114
142
|
|
115
143
|
it "is not considered an attribute" do
|
116
|
-
builder.select("dogs", limit: 50, offset: 200)
|
144
|
+
sql = builder.select("dogs", limit: 50, offset: 200)
|
145
|
+
sql.slice(1..-1).should == ["dogs"]
|
117
146
|
end
|
118
147
|
end
|
119
148
|
|
120
149
|
context "with an order clause" do
|
121
150
|
it "applies an ORDER BY" do
|
122
|
-
builder.select("cats", order: {views: :desc})
|
151
|
+
sql = builder.select("cats", order: {views: :desc})
|
152
|
+
sql[0].should =~ /SELECT .* FROM posts WHERE .* ORDER BY views DESC/
|
123
153
|
end
|
124
154
|
|
125
155
|
it "defaults to ASC" do
|
126
|
-
builder.select("cats", order: :views)
|
156
|
+
sql = builder.select("cats", order: :views)
|
157
|
+
sql[0].should =~ /SELECT .* FROM posts WHERE .* ORDER BY views ASC/
|
127
158
|
end
|
128
159
|
|
129
160
|
it "supports multiple orders" do
|
130
|
-
builder.select("cats", order: {views: :asc, author_id: :desc})
|
161
|
+
sql = builder.select("cats", order: {views: :asc, author_id: :desc})
|
162
|
+
sql[0].should =~ /SELECT .* FROM posts WHERE .* ORDER BY views ASC, author_id DESC/
|
131
163
|
end
|
132
164
|
|
133
165
|
context "by relevance" do
|
134
166
|
it "injects a weight() attribute" do
|
135
|
-
builder.select("cats", order: {relevance: :desc})
|
167
|
+
sql = builder.select("cats", order: {relevance: :desc})
|
168
|
+
sql[0].should =~ /SELECT \*, WEIGHT\(\) AS relevance FROM posts WHERE .* ORDER BY relevance DESC/
|
136
169
|
end
|
137
170
|
end
|
138
171
|
end
|
@@ -140,29 +173,33 @@ describe Oedipus::QueryBuilder do
|
|
140
173
|
|
141
174
|
describe "#insert" do
|
142
175
|
it "includes the ID and the attributes" do
|
143
|
-
builder.insert(3, title: "example", views: 9).should == "INSERT INTO posts (id, title, views) VALUES (3,
|
176
|
+
builder.insert(3, title: "example", views: 9).should == ["INSERT INTO posts (id, title, views) VALUES (?, ?, ?)", 3, "example", 9]
|
144
177
|
end
|
145
178
|
end
|
146
179
|
|
147
180
|
describe "#update" do
|
148
181
|
it "includes the ID in the WHERE clause" do
|
149
|
-
builder.update(3, title: "example", views: 9)
|
182
|
+
sql = builder.update(3, title: "example", views: 9)
|
183
|
+
sql[0].should =~ /UPDATE posts SET .* WHERE id = \?/
|
184
|
+
sql.last.should == 3
|
150
185
|
end
|
151
186
|
|
152
187
|
it "includes the changed attributes" do
|
153
|
-
builder.update(3, title: "example", views: 9)
|
188
|
+
sql = builder.update(3, title: "example", views: 9)
|
189
|
+
sql[0].should =~ /UPDATE posts SET title = \?, views = \?/
|
190
|
+
sql.slice(1..-1).should == ["example", 9, 3]
|
154
191
|
end
|
155
192
|
end
|
156
193
|
|
157
194
|
describe "#replace" do
|
158
195
|
it "includes the ID and the attributes" do
|
159
|
-
builder.replace(3, title: "example", views: 9).should == "REPLACE INTO posts (id, title, views) VALUES (3,
|
196
|
+
builder.replace(3, title: "example", views: 9).should == ["REPLACE INTO posts (id, title, views) VALUES (?, ?, ?)", 3, "example", 9]
|
160
197
|
end
|
161
198
|
end
|
162
199
|
|
163
200
|
describe "#delete" do
|
164
201
|
it "includes the ID" do
|
165
|
-
builder.delete(3).should == "DELETE FROM posts WHERE id = 3
|
202
|
+
builder.delete(3).should == ["DELETE FROM posts WHERE id = ?", 3]
|
166
203
|
end
|
167
204
|
end
|
168
205
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oedipus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-05-
|
12
|
+
date: 2012-05-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &20663420 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *20663420
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rake-compiler
|
27
|
-
requirement: &
|
27
|
+
requirement: &20662600 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *20662600
|
36
36
|
description: ! "== Sphinx 2 Comes to Ruby\n\nOedipus brings full support for Sphinx
|
37
37
|
2 to Ruby:\n\n - real-time indexes (insert, replace, update, delete)\n - faceted
|
38
38
|
search (variations on a base query)\n - multi-queries (multiple queries executed
|
@@ -109,7 +109,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
109
109
|
version: '0'
|
110
110
|
segments:
|
111
111
|
- 0
|
112
|
-
hash:
|
112
|
+
hash: -658671341905469446
|
113
113
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
114
|
none: false
|
115
115
|
requirements:
|
@@ -118,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
118
118
|
version: '0'
|
119
119
|
segments:
|
120
120
|
- 0
|
121
|
-
hash:
|
121
|
+
hash: -658671341905469446
|
122
122
|
requirements: []
|
123
123
|
rubyforge_project: oedipus
|
124
124
|
rubygems_version: 1.8.11
|