sql_query_executor 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/sql_query_executor/base.rb +36 -16
- data/lib/sql_query_executor/operators/base.rb +6 -12
- data/lib/sql_query_executor/operators/between.rb +3 -3
- data/lib/sql_query_executor/operators/default.rb +2 -2
- data/lib/sql_query_executor/operators/in.rb +2 -2
- data/lib/sql_query_executor/operators/is.rb +7 -3
- data/lib/sql_query_executor/query/query_normalizer.rb +17 -14
- data/lib/sql_query_executor/query/sentence.rb +4 -3
- data/lib/sql_query_executor/version.rb +1 -1
- data/spec/sql_query_executor/base_spec.rb +51 -487
- metadata +2 -3
- data/lib/sql_query_executor/query/base.rb +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ff8930d2561d2c1093c10f8027ac682025034ae
|
4
|
+
data.tar.gz: 9045d8b249a633ae530be73a3dc2b7ba29effac3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa8d67868b67353de090839b61c5b8c847cbc4d28ec552b3b11ca85378cf49f2666002013cd7cd0a121d0f0e6d595448f699f3fe5fcfade389e9be9334a9a77c
|
7
|
+
data.tar.gz: 03413bc22a1e936bec0eac260f8e0ba7d39f0decce5f0442f535204b3509a3587834feb04a015622f6192b9eef28df13c76be9bbc097f38cf4f1c035608a669b
|
@@ -1,34 +1,54 @@
|
|
1
1
|
require 'ostruct'
|
2
|
-
require 'sql_query_executor/query/
|
2
|
+
require 'sql_query_executor/query/query_normalizer'
|
3
3
|
require 'sql_query_executor/query/sub_query'
|
4
4
|
|
5
5
|
# Simulates a SQL where clause to filter objects from the database
|
6
6
|
module SqlQueryExecutor #:nodoc:
|
7
7
|
class Base #:nodoc:
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
if collection.any? && collection.first.is_a?(Hash)
|
12
|
-
convert_collection(collection)
|
13
|
-
else
|
14
|
-
@collection = conforming_collection?(collection) ? collection : []
|
15
|
-
end
|
16
|
-
end
|
8
|
+
STRING_SPACE = "$SS$"
|
9
|
+
QUERY_SPACE = "$QS$"
|
10
|
+
TEMP_SPACE = "$TS$"
|
17
11
|
|
18
|
-
|
19
|
-
|
20
|
-
def where(*query)
|
21
|
-
query = query.first if query.respond_to?(:size) && query.size == 1
|
12
|
+
def initialize(collection, query)
|
13
|
+
query = query.first if query.respond_to?(:size) && query.size == 1 && !query.is_a?(Hash)
|
22
14
|
|
23
15
|
message = check_query(query)
|
24
16
|
raise ArgumentError.new(message) if message
|
25
17
|
|
26
|
-
|
27
|
-
query.execute
|
18
|
+
get_collection(collection)
|
19
|
+
query = Query::QueryNormalizer.execute(query)
|
20
|
+
@query = SqlQueryExecutor::Query::SubQuery.new query, @collection
|
21
|
+
end
|
22
|
+
|
23
|
+
def execute!
|
24
|
+
@query.execute!
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_sql
|
28
|
+
@query.to_sql
|
29
|
+
end
|
30
|
+
|
31
|
+
def selector
|
32
|
+
@query.selector
|
33
|
+
end
|
34
|
+
|
35
|
+
# Recursive method that divides the query in sub queries, executes each part individually
|
36
|
+
# and finally relates its results as specified in the query.
|
37
|
+
def self.where(collection, *query)
|
38
|
+
self.new(collection, query).execute!
|
28
39
|
end
|
29
40
|
|
30
41
|
private
|
42
|
+
def get_collection(collection)
|
43
|
+
if collection.any? && collection.first.is_a?(Hash)
|
44
|
+
convert_collection(collection)
|
45
|
+
else
|
46
|
+
@collection = conforming_collection?(collection) ? collection : []
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
31
50
|
def convert_collection(collection)
|
51
|
+
@collection = []
|
32
52
|
collection.each do |object|
|
33
53
|
attributes = object.is_a?(Hash) ? object : object.attributes
|
34
54
|
register = OpenStruct.new(attributes)
|
@@ -1,10 +1,12 @@
|
|
1
|
+
require 'sql_query_executor/base'
|
2
|
+
|
1
3
|
module SqlQueryExecutor
|
2
4
|
module Operators
|
3
5
|
class Base
|
4
6
|
def initialize(query, collection)
|
5
|
-
@query =
|
7
|
+
@query = SqlQueryExecutor::Query::QueryNormalizer.execute(query).gsub(SqlQueryExecutor::Base::QUERY_SPACE, ' ')
|
6
8
|
@collection = collection
|
7
|
-
@array = query.split(' ')
|
9
|
+
@array = @query.split(' ')
|
8
10
|
@operator = @query.split(' ')[1]
|
9
11
|
@field = get_field
|
10
12
|
@value = get_value
|
@@ -20,7 +22,7 @@ module SqlQueryExecutor
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def get_value
|
23
|
-
value = @array.last.gsub(SqlQueryExecutor::
|
25
|
+
value = @array.last.gsub(SqlQueryExecutor::Base::STRING_SPACE, ' ')
|
24
26
|
|
25
27
|
convert_value(value)
|
26
28
|
end
|
@@ -32,21 +34,13 @@ module SqlQueryExecutor
|
|
32
34
|
methods = {3 => "convert_date", 7 => "convert_time"}
|
33
35
|
|
34
36
|
array = split(value)
|
35
|
-
|
37
|
+
|
36
38
|
value = (send(methods[array.size], array) || value) if methods.keys.include?(array.size)
|
37
39
|
|
38
40
|
value
|
39
41
|
end
|
40
42
|
|
41
43
|
private
|
42
|
-
def sanitize_query(query)
|
43
|
-
params = query.scan(/(\(.*?\))/).flatten.compact
|
44
|
-
|
45
|
-
params.each { |param| query.gsub!(param, param.gsub(" ", "")) }
|
46
|
-
|
47
|
-
query
|
48
|
-
end
|
49
|
-
|
50
44
|
def is_a_number?(value)
|
51
45
|
value.to_s == value.to_i.to_s
|
52
46
|
end
|
@@ -3,7 +3,7 @@ require 'sql_query_executor/operators/base'
|
|
3
3
|
module SqlQueryExecutor
|
4
4
|
module Operators
|
5
5
|
class Between < SqlQueryExecutor::Operators::Base
|
6
|
-
def execute!
|
6
|
+
def execute!
|
7
7
|
@collection.select do |record|
|
8
8
|
value = convert_value(record.send(@field).to_s)
|
9
9
|
|
@@ -26,8 +26,8 @@ module SqlQueryExecutor
|
|
26
26
|
def get_value
|
27
27
|
value = []
|
28
28
|
|
29
|
-
value << convert_value(@array[2].gsub(SqlQueryExecutor::
|
30
|
-
value << convert_value(@array[4].gsub(SqlQueryExecutor::
|
29
|
+
value << convert_value(@array[2].gsub(SqlQueryExecutor::Base::STRING_SPACE, ' '))
|
30
|
+
value << convert_value(@array[4].gsub(SqlQueryExecutor::Base::STRING_SPACE, ' '))
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
@@ -17,7 +17,7 @@ module SqlQueryExecutor
|
|
17
17
|
convert_operator
|
18
18
|
end
|
19
19
|
|
20
|
-
def execute!
|
20
|
+
def execute!
|
21
21
|
@collection.select do |record|
|
22
22
|
value = record.send(@field.to_s)
|
23
23
|
|
@@ -28,7 +28,7 @@ module SqlQueryExecutor
|
|
28
28
|
def selector
|
29
29
|
operator = SELECTORS[@operator]
|
30
30
|
|
31
|
-
{ @field
|
31
|
+
{ @field => operator ? {operator => @value} : @value}
|
32
32
|
end
|
33
33
|
|
34
34
|
private
|
@@ -3,7 +3,7 @@ require 'sql_query_executor/operators/base'
|
|
3
3
|
module SqlQueryExecutor
|
4
4
|
module Operators
|
5
5
|
class In < SqlQueryExecutor::Operators::Base
|
6
|
-
def execute!
|
6
|
+
def execute!
|
7
7
|
result = @collection.select do |record|
|
8
8
|
value = record.send(@field)
|
9
9
|
|
@@ -19,7 +19,7 @@ module SqlQueryExecutor
|
|
19
19
|
def get_value
|
20
20
|
value = super
|
21
21
|
|
22
|
-
value.gsub(SqlQueryExecutor::
|
22
|
+
value.gsub(SqlQueryExecutor::Base::STRING_SPACE, '').split(',').map{ |v| convert_value(v) }
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
@@ -8,17 +8,21 @@ module SqlQueryExecutor
|
|
8
8
|
convert_operator
|
9
9
|
end
|
10
10
|
|
11
|
-
def execute!
|
11
|
+
def execute!
|
12
12
|
@collection.select do |record|
|
13
13
|
value = record.send(@field)
|
14
14
|
|
15
|
-
value.send(@operator,
|
15
|
+
value.send(@operator, @value)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
def selector
|
20
|
+
@operator == '==' ? { @field => @value } : { @field => {'$ne' => @value} }
|
21
|
+
end
|
22
|
+
|
19
23
|
private
|
20
24
|
def get_value
|
21
|
-
@array.include?('null') ?
|
25
|
+
@array.include?('null') ? nil : convert_value(@array.last)
|
22
26
|
end
|
23
27
|
|
24
28
|
def convert_operator
|
@@ -4,19 +4,17 @@ module SqlQueryExecutor
|
|
4
4
|
module Query
|
5
5
|
class QueryNormalizer
|
6
6
|
class << self
|
7
|
-
CONVERT_METHODS = {"String" =>
|
7
|
+
CONVERT_METHODS = {"String" => "get_query", "Array" => "interpolate_query", "Hash" => "concatenate_hash"}
|
8
8
|
|
9
9
|
def execute(query)
|
10
10
|
query = clean_query_attribute(query)
|
11
|
-
|
11
|
+
method = CONVERT_METHODS[query.class.name]
|
12
12
|
|
13
|
-
query = sanitize(send(
|
13
|
+
query = sanitize(send(method, query))
|
14
14
|
end
|
15
15
|
|
16
16
|
def clean_query(query)
|
17
|
-
|
18
|
-
|
19
|
-
remove_placeholders(query)
|
17
|
+
remove_placeholders execute(query)
|
20
18
|
end
|
21
19
|
|
22
20
|
private
|
@@ -84,6 +82,7 @@ module SqlQueryExecutor
|
|
84
82
|
end
|
85
83
|
|
86
84
|
def remove_spaces(query)
|
85
|
+
query.gsub!(",#{Base::QUERY_SPACE}", ',')
|
87
86
|
query.gsub!(/\[.*?\]/) { |substr| substr.gsub(' ', '') }
|
88
87
|
query
|
89
88
|
end
|
@@ -91,14 +90,16 @@ module SqlQueryExecutor
|
|
91
90
|
# Returns converted #param based on its Class, so it can be used on the query
|
92
91
|
def convert_param(param)
|
93
92
|
case param.class.name
|
93
|
+
when "NilClass"
|
94
|
+
nil
|
94
95
|
when "String"
|
95
|
-
|
96
|
+
"'#{param}'".gsub("''", "'").gsub('""', '"')
|
96
97
|
when "Date"
|
97
|
-
|
98
|
+
"'#{param.strftime("%Y-%m-%d")}'"
|
98
99
|
when "Time"
|
99
|
-
|
100
|
+
"'#{param.strftime("%Y-%m-%d %H:%M:%S %z")}'"
|
100
101
|
else
|
101
|
-
param
|
102
|
+
param.to_s
|
102
103
|
end
|
103
104
|
end
|
104
105
|
|
@@ -109,14 +110,15 @@ module SqlQueryExecutor
|
|
109
110
|
|
110
111
|
query.each do |key, value|
|
111
112
|
if value.is_a?(Array)
|
112
|
-
if [
|
113
|
+
if ['$and', '$or'].include?(key)
|
114
|
+
key = key.gsub('$', '')
|
113
115
|
queries = []
|
114
116
|
|
115
117
|
value.each do |hash|
|
116
118
|
queries << concatenate_hash(hash)
|
117
119
|
end
|
118
120
|
|
119
|
-
query_array << queries.join(" #{key.to_s} ")
|
121
|
+
query_array << "(#{queries.join(" #{key.to_s} ")})"
|
120
122
|
else
|
121
123
|
value = value.first.is_a?(Numeric) ? value : value.map{ |v| "'#{v}'" }
|
122
124
|
query_array << "#{key} in (#{value.join(',')})"
|
@@ -130,11 +132,12 @@ module SqlQueryExecutor
|
|
130
132
|
value = convert_param(value.values.first)
|
131
133
|
end
|
132
134
|
|
133
|
-
value =
|
134
|
-
query_array << "#{key} #{operator} #{value}"
|
135
|
+
value = convert_param(value)
|
136
|
+
query_array << (value.nil? ? "#{key} is null" : "#{key} #{operator} #{value}")
|
135
137
|
end
|
136
138
|
end
|
137
139
|
|
140
|
+
|
138
141
|
query_array.join(" and ")
|
139
142
|
end
|
140
143
|
end
|
@@ -6,7 +6,7 @@ require 'sql_query_executor/operators/in'
|
|
6
6
|
module SqlQueryExecutor
|
7
7
|
module Query
|
8
8
|
class Sentence
|
9
|
-
attr_reader :query
|
9
|
+
attr_reader :query
|
10
10
|
|
11
11
|
OPERATORS = {
|
12
12
|
"between" => SqlQueryExecutor::Operators::Between,
|
@@ -31,10 +31,11 @@ module SqlQueryExecutor
|
|
31
31
|
set_operator
|
32
32
|
end
|
33
33
|
|
34
|
-
|
34
|
+
# The data parameter is only declared for SubQuery compatibility purposes
|
35
|
+
def execute!(data=[])
|
35
36
|
return [] unless @operator
|
36
37
|
|
37
|
-
@operator.execute!
|
38
|
+
@operator.execute!
|
38
39
|
end
|
39
40
|
|
40
41
|
def selector
|
@@ -1,527 +1,91 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
require 'sql_query_executor'
|
4
|
+
require 'sql_query_executor/base'
|
4
5
|
|
5
|
-
describe SqlQueryExecutor
|
6
|
-
before do
|
7
|
-
@data = []
|
8
|
-
|
9
|
-
@data << {id: 1, name: "US", language: 'English'}
|
10
|
-
@data << {id: 2, name: "Canada", language: 'English', monarch: "The Crown of England"}
|
11
|
-
@data << {id: 3, name: "Mexico", language: 'Spanish'}
|
12
|
-
@data << {id: 4, name: "UK", language: 'English', monarch: "The Crown of England"}
|
13
|
-
@data << {id: 5, name: "Brazil", founded_at: Time.parse('1500-04-22 13:34:25')}
|
14
|
-
end
|
15
|
-
|
16
|
-
subject { SqlQueryExecutor::Base.new(@data) }
|
17
|
-
|
18
|
-
describe "initialize" do
|
19
|
-
before do
|
20
|
-
class Model
|
21
|
-
attr_reader :attributes
|
22
|
-
|
23
|
-
def initialize(attributes)
|
24
|
-
@attributes = attributes
|
25
|
-
end
|
26
|
-
|
27
|
-
def id
|
28
|
-
@attributes[:id]
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
class Collection
|
33
|
-
def initialize(collection)
|
34
|
-
@collection = []
|
35
|
-
|
36
|
-
collection.each do |hash|
|
37
|
-
@collection << Model.new(hash)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def all
|
42
|
-
@collection
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
subject { SqlQueryExecutor::Base.new(Collection.new(@data).all) }
|
48
|
-
|
49
|
-
it 'initializes with a conforming collection' do
|
50
|
-
expect(subject.where(id: 1).first.attributes).to eq (@data.first)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
6
|
+
describe SqlQueryExecutor::Base do
|
54
7
|
describe ".where" do
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
describe "=" do
|
64
|
-
context "when attribute is string" do
|
65
|
-
it "matches a record" do
|
66
|
-
record = subject.where("name = 'US'")
|
67
|
-
record.size.should == 1
|
68
|
-
record.first.id.should == 1
|
69
|
-
record.first.name.should == 'US'
|
70
|
-
end
|
71
|
-
|
72
|
-
it "doesn't match any record" do
|
73
|
-
record = subject.where("name = 'Argentina'")
|
74
|
-
record.count.should == 0
|
75
|
-
record.should == []
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
context "when attribute is integer" do
|
80
|
-
it "matches a record" do
|
81
|
-
record = subject.where("id = 1")
|
82
|
-
record.count.should == 1
|
83
|
-
record.first.id.should == 1
|
84
|
-
record.first.name.should == 'US'
|
85
|
-
end
|
86
|
-
|
87
|
-
it "doesn't match any record" do
|
88
|
-
record = subject.where("id = 43")
|
89
|
-
record.count.should == 0
|
90
|
-
record.should == []
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
context "when attribute is datetime" do
|
95
|
-
it "matches a record" do
|
96
|
-
record = subject.where("founded_at = ?", Time.parse('1500-04-22 13:34:25'))
|
97
|
-
record.count.should == 1
|
98
|
-
record.first.id.should == 5
|
99
|
-
record.first.name.should == 'Brazil'
|
100
|
-
end
|
101
|
-
|
102
|
-
it "doesn't match any record" do
|
103
|
-
record = subject.where("id = ?", Time.parse('1500-09-07 13:34:25'))
|
104
|
-
record.count.should == 0
|
105
|
-
record.should == []
|
106
|
-
end
|
107
|
-
end
|
8
|
+
let(:data) do
|
9
|
+
[
|
10
|
+
{id: 1, name: "US", language: 'English'},
|
11
|
+
{id: 2, name: "Canada", language: 'English', monarch: "The Crown of England"},
|
12
|
+
{id: 3, name: "Mexico", language: 'Spanish'},
|
13
|
+
{id: 4, name: "UK", language: 'English', monarch: "The Crown of England"},
|
14
|
+
{id: 5, name: "Brazil", founded_at: Time.parse('1500-04-22 13:34:25')}
|
15
|
+
]
|
108
16
|
end
|
109
17
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
records.count.should == 2
|
115
|
-
records.first.id.should == 1
|
18
|
+
context 'conforming collection' do
|
19
|
+
before do
|
20
|
+
class Model
|
21
|
+
attr_reader :attributes
|
116
22
|
|
117
|
-
|
118
|
-
|
23
|
+
def initialize(attributes)
|
24
|
+
@attributes = attributes
|
119
25
|
end
|
120
26
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
it "doesn't match any record" do
|
125
|
-
record = subject.where("name > 'Z'")
|
126
|
-
record.count.should == 0
|
127
|
-
record.should == []
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
context "when attribute is integer" do
|
132
|
-
it "matches a record" do
|
133
|
-
records = subject.where("id > 3")
|
134
|
-
records.count.should == 2
|
135
|
-
records.first.id.should == 4
|
136
|
-
|
137
|
-
records.map! do |record|
|
138
|
-
record.to_h
|
27
|
+
def id
|
28
|
+
@attributes[:id]
|
139
29
|
end
|
140
|
-
|
141
|
-
records.should == [@data[3], @data[4]]
|
142
30
|
end
|
143
31
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
record.should == []
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
context "when attribute is datetime" do
|
152
|
-
it "matches a record" do
|
153
|
-
record = subject.where("founded_at > ?", Time.parse('1500-04-20 13:34:25'))
|
154
|
-
record.count.should == 1
|
155
|
-
record.first.id.should == 5
|
156
|
-
record.first.name.should == 'Brazil'
|
157
|
-
end
|
158
|
-
|
159
|
-
it "doesn't match any record" do
|
160
|
-
record = subject.where("founded_at > ?", Time.parse('1500-04-23 13:34:25'))
|
161
|
-
record.count.should == 0
|
162
|
-
record.should == []
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
describe ">=" do
|
168
|
-
context "when attribute is a string" do
|
169
|
-
it "matches a record" do
|
170
|
-
records = subject.where("name >= 'U'")
|
171
|
-
records.count.should == 2
|
172
|
-
records.first.id.should == 1
|
32
|
+
class Collection
|
33
|
+
def initialize(collection)
|
34
|
+
@collection = []
|
173
35
|
|
174
|
-
|
175
|
-
|
36
|
+
collection.each do |hash|
|
37
|
+
@collection << Model.new(hash)
|
38
|
+
end
|
176
39
|
end
|
177
40
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
it "doesn't match any record" do
|
182
|
-
record = subject.where("name >= 'Z'")
|
183
|
-
record.count.should == 0
|
184
|
-
record.should == []
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
context "when attribute is integer" do
|
189
|
-
it "matches a record" do
|
190
|
-
records = subject.where("id >= 4")
|
191
|
-
records.count.should == 2
|
192
|
-
records.first.id.should == 4
|
193
|
-
|
194
|
-
records.map! do |record|
|
195
|
-
record.to_h
|
41
|
+
def all
|
42
|
+
@collection
|
196
43
|
end
|
197
|
-
|
198
|
-
records.should == [@data[3], @data[4]]
|
199
|
-
end
|
200
|
-
|
201
|
-
it "doesn't match any record" do
|
202
|
-
record = subject.where("id >= 6")
|
203
|
-
record.count.should == 0
|
204
|
-
record.should == []
|
205
44
|
end
|
206
45
|
end
|
207
46
|
|
208
|
-
|
209
|
-
|
210
|
-
record = subject.where("founded_at >= ?", Time.parse('1500-04-22 13:34:25'))
|
211
|
-
record.count.should == 1
|
212
|
-
record.first.id.should == 5
|
213
|
-
record.first.name.should == 'Brazil'
|
214
|
-
end
|
215
|
-
|
216
|
-
it "doesn't match any record" do
|
217
|
-
record = subject.where("founded_at >= ?", Time.parse('1500-04-23 13:34:25'))
|
218
|
-
record.count.should == 0
|
219
|
-
record.should == []
|
220
|
-
end
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
describe "<" do
|
225
|
-
context "when attribute is a string" do
|
226
|
-
it "matches a record" do
|
227
|
-
records = subject.where("name < 'C'")
|
228
|
-
records.count.should == 1
|
229
|
-
records.first.id.should == 5
|
230
|
-
|
231
|
-
records.map! do |record|
|
232
|
-
record.to_h
|
233
|
-
end
|
234
|
-
|
235
|
-
records.should == [@data[4]]
|
236
|
-
end
|
237
|
-
|
238
|
-
it "doesn't match any record" do
|
239
|
-
record = subject.where("name < 'B'")
|
240
|
-
record.count.should == 0
|
241
|
-
record.should == []
|
242
|
-
end
|
47
|
+
it 'initializes with a conforming collection' do
|
48
|
+
expect(described_class.where(Collection.new(data).all, id: 1).first.attributes).to eq (data.first)
|
243
49
|
end
|
50
|
+
|
51
|
+
context "when invalid query is passed" do
|
52
|
+
it "raises an ArgumentError" do
|
53
|
+
query = [{name: "John"}]
|
244
54
|
|
245
|
-
|
246
|
-
it "matches a record" do
|
247
|
-
records = subject.where("id < 3")
|
248
|
-
records.count.should == 2
|
249
|
-
records.first.id.should == 1
|
250
|
-
|
251
|
-
records.map! do |record|
|
252
|
-
record.to_h
|
253
|
-
end
|
254
|
-
|
255
|
-
records.should == [@data[0], @data[1]]
|
256
|
-
end
|
257
|
-
|
258
|
-
it "doesn't match any record" do
|
259
|
-
record = subject.where("id < 1")
|
260
|
-
record.count.should == 0
|
261
|
-
record.should == []
|
262
|
-
end
|
263
|
-
end
|
264
|
-
|
265
|
-
context "when attribute is datetime" do
|
266
|
-
it "matches a record" do
|
267
|
-
record = subject.where("founded_at < ?", Time.parse('1500-04-22 13:34:26'))
|
268
|
-
record.count.should == 1
|
269
|
-
record.first.id.should == 5
|
270
|
-
record.first.name.should == 'Brazil'
|
271
|
-
end
|
272
|
-
|
273
|
-
it "doesn't match any record" do
|
274
|
-
record = subject.where("founded_at < ?", Time.parse('1500-04-22 13:34:25'))
|
275
|
-
record.count.should == 0
|
276
|
-
record.should == []
|
55
|
+
expect { described_class.where(Collection.new(data).all, query) }.to raise_error(ArgumentError, "First element from array must be a String. eg: [\"name = ?\", \"John\"]")
|
277
56
|
end
|
278
57
|
end
|
279
58
|
end
|
280
59
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
records = subject.where("name <= 'Brb'")
|
285
|
-
records.count.should == 1
|
286
|
-
records.first.id.should == 5
|
287
|
-
|
288
|
-
records.map! do |record|
|
289
|
-
record.to_h
|
290
|
-
end
|
291
|
-
|
292
|
-
records.should == [@data[4]]
|
293
|
-
end
|
294
|
-
|
295
|
-
it "doesn't match any record" do
|
296
|
-
record = subject.where("name <= 'A'")
|
297
|
-
record.count.should == 0
|
298
|
-
record.should == []
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
context "when attribute is integer" do
|
303
|
-
it "matches a record" do
|
304
|
-
records = subject.where("id <= 2")
|
305
|
-
records.count.should == 2
|
306
|
-
records.first.id.should == 1
|
307
|
-
|
308
|
-
records.map! do |record|
|
309
|
-
record.to_h
|
310
|
-
end
|
311
|
-
|
312
|
-
records.should == [@data[0], @data[1]]
|
313
|
-
end
|
314
|
-
|
315
|
-
it "doesn't match any record" do
|
316
|
-
record = subject.where("id <= 0")
|
317
|
-
record.count.should == 0
|
318
|
-
record.should == []
|
319
|
-
end
|
320
|
-
end
|
321
|
-
|
322
|
-
context "when attribute is datetime" do
|
323
|
-
it "matches a record" do
|
324
|
-
record = subject.where("founded_at <= ?", Time.parse('1500-04-22 13:34:25'))
|
325
|
-
record.count.should == 1
|
326
|
-
record.first.id.should == 5
|
327
|
-
record.first.name.should == 'Brazil'
|
328
|
-
end
|
329
|
-
|
330
|
-
it "doesn't match any record" do
|
331
|
-
record = subject.where("founded_at <= ?", Time.parse('1500-04-22 13:34:24'))
|
332
|
-
record.count.should == 0
|
333
|
-
record.should == []
|
334
|
-
end
|
335
|
-
end
|
336
|
-
end
|
337
|
-
|
338
|
-
describe "between" do
|
339
|
-
context "when attribute is a string" do
|
340
|
-
it "matches a record" do
|
341
|
-
records = subject.where("name between 'A' and 'C'")
|
342
|
-
records.count.should == 1
|
343
|
-
records.first.id.should == 5
|
344
|
-
|
345
|
-
records.map! do |record|
|
346
|
-
record.to_h
|
347
|
-
end
|
348
|
-
|
349
|
-
records.should == [@data[4]]
|
350
|
-
end
|
351
|
-
|
352
|
-
it "doesn't match any record" do
|
353
|
-
record = subject.where("name between 'K' and 'M'")
|
354
|
-
record.count.should == 0
|
355
|
-
record.should == []
|
356
|
-
end
|
357
|
-
end
|
358
|
-
|
359
|
-
context "when attribute is integer" do
|
360
|
-
it "matches a record" do
|
361
|
-
records = subject.where("id between 1 and 2")
|
362
|
-
records.count.should == 2
|
363
|
-
records.first.id.should == 1
|
364
|
-
|
365
|
-
records.map! do |record|
|
366
|
-
record.to_h
|
367
|
-
end
|
368
|
-
|
369
|
-
records.should == [@data[0], @data[1]]
|
370
|
-
end
|
371
|
-
|
372
|
-
it "doesn't match any record" do
|
373
|
-
record = subject.where("id between 6 and 10")
|
374
|
-
record.count.should == 0
|
375
|
-
record.should == []
|
376
|
-
end
|
377
|
-
end
|
378
|
-
|
379
|
-
context "when attribute is datetime" do
|
380
|
-
it "matches a record" do
|
381
|
-
record = subject.where("founded_at between ? and ?", Time.parse('1500-04-22 13:34:24'), Time.parse('1500-04-22 13:34:26'))
|
382
|
-
record.count.should == 1
|
383
|
-
record.first.id.should == 5
|
384
|
-
record.first.name.should == 'Brazil'
|
385
|
-
end
|
386
|
-
|
387
|
-
it "doesn't match any record" do
|
388
|
-
record = subject.where("founded_at between ? and ?", Time.parse('1500-04-22 13:34:26'), Time.parse('1500-09-22 13:34:25'))
|
389
|
-
record.count.should == 0
|
390
|
-
record.should == []
|
391
|
-
end
|
392
|
-
end
|
393
|
-
end
|
394
|
-
|
395
|
-
describe "is" do
|
396
|
-
it "attribute is condition" do
|
397
|
-
records = subject.where("founded_at is null")
|
398
|
-
records.count.should == 4
|
399
|
-
records.first.id.should == 1
|
400
|
-
|
401
|
-
records.map! do |record|
|
402
|
-
record.to_h
|
403
|
-
end
|
404
|
-
|
405
|
-
records.should == [@data[0], @data[1], @data[2], @data[3]]
|
60
|
+
context 'not conforming collection' do
|
61
|
+
it 'initializes with a conforming collection' do
|
62
|
+
expect(described_class.where(data, id: 1)).to eq([OpenStruct.new(data.first)])
|
406
63
|
end
|
64
|
+
|
65
|
+
context "when invalid query is passed" do
|
66
|
+
it "raises an ArgumentError" do
|
67
|
+
query = [{name: "John"}]
|
407
68
|
|
408
|
-
|
409
|
-
id = @data.last[:id]
|
410
|
-
records = subject.where("founded_at is not null")
|
411
|
-
records.count.should == 1
|
412
|
-
records.first.id.should == id
|
413
|
-
|
414
|
-
records.map! do |record|
|
415
|
-
record.to_h
|
69
|
+
expect { described_class.where(data, query) }.to raise_error(ArgumentError, "First element from array must be a String. eg: [\"name = ?\", \"John\"]")
|
416
70
|
end
|
417
|
-
|
418
|
-
records.should == [@data[id-1]]
|
419
71
|
end
|
420
72
|
end
|
421
73
|
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
records.count.should == 2
|
426
|
-
records.first.id.should == 2
|
427
|
-
|
428
|
-
records.map! do |record|
|
429
|
-
record.to_h
|
430
|
-
end
|
431
|
-
|
432
|
-
records.should == [@data[1], @data[3]]
|
433
|
-
end
|
434
|
-
|
435
|
-
it "integer attribute and condition" do
|
436
|
-
records = subject.where("id = 2 and language = 'English'")
|
437
|
-
records.count.should == 1
|
438
|
-
records.first.id.should == 2
|
74
|
+
context 'selector' do
|
75
|
+
let(:query) { 'id > 3' }
|
76
|
+
let(:selector) { {'id' => {'$gt' => 3}} }
|
439
77
|
|
440
|
-
|
441
|
-
|
442
|
-
end
|
443
|
-
|
444
|
-
records.should == [@data[1]]
|
78
|
+
it 'converts query' do
|
79
|
+
expect(described_class.new([], query).selector).to eq selector
|
445
80
|
end
|
446
81
|
end
|
447
82
|
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
records.count.should == 4
|
452
|
-
records.first.id.should == 1
|
453
|
-
|
454
|
-
records.map! do |record|
|
455
|
-
record.to_h
|
456
|
-
end
|
457
|
-
|
458
|
-
records.should == [@data[0], @data[1], @data[2], @data[3]]
|
459
|
-
end
|
460
|
-
|
461
|
-
context "nested queries" do
|
462
|
-
it "respects priority" do
|
463
|
-
records = subject.where("(language = 'English' and name = 'US') or (language is null)")
|
464
|
-
records.count.should == 2
|
465
|
-
records.first.id.should == 1
|
466
|
-
|
467
|
-
records.map! do |record|
|
468
|
-
record.to_h
|
469
|
-
end
|
470
|
-
|
471
|
-
records.should == [@data[0], @data[4]]
|
472
|
-
end
|
473
|
-
|
474
|
-
it "respects priority" do
|
475
|
-
records = subject.where("(language is null) or (language = 'English' and name = 'US')")
|
476
|
-
records.count.should == 2
|
477
|
-
records.first.id.should == 1
|
478
|
-
|
479
|
-
records.map! do |record|
|
480
|
-
record.to_h
|
481
|
-
end
|
482
|
-
|
483
|
-
records.should == [@data[0], @data[4]]
|
484
|
-
end
|
485
|
-
end
|
486
|
-
end
|
487
|
-
|
488
|
-
describe "in" do
|
489
|
-
it "attribute in condition" do
|
490
|
-
records = subject.where("language in ('English', 'Spanish')")
|
491
|
-
records.count.should == 4
|
492
|
-
records.first.id.should == 1
|
493
|
-
|
494
|
-
records.map! do |record|
|
495
|
-
record.to_h
|
496
|
-
end
|
497
|
-
|
498
|
-
records.should == [@data[0], @data[1], @data[2], @data[3]]
|
499
|
-
end
|
500
|
-
|
501
|
-
xit "attribute not in condition" do
|
502
|
-
records = subject.where("language not in ('English', 'Spanish')")
|
503
|
-
records.count.should == 1
|
504
|
-
records.first.id.should == 5
|
505
|
-
|
506
|
-
records.map! do |record|
|
507
|
-
record.to_h
|
508
|
-
end
|
509
|
-
|
510
|
-
records.should == [@data[4]]
|
511
|
-
end
|
512
|
-
end
|
513
|
-
|
514
|
-
describe "not" do
|
515
|
-
xit "attribute not condition" do
|
516
|
-
records = subject.where("not language = 'English'")
|
517
|
-
records.count.should == 1
|
518
|
-
records.first.id.should == 5
|
519
|
-
|
520
|
-
records.map! do |record|
|
521
|
-
record.to_h
|
522
|
-
end
|
83
|
+
context 'to_sql' do
|
84
|
+
let(:query) { "name = 'Brazil'" }
|
85
|
+
let(:selector) { {name: 'Brazil'} }
|
523
86
|
|
524
|
-
|
87
|
+
it 'converts selector' do
|
88
|
+
expect(described_class.new([], selector).to_sql).to eq query
|
525
89
|
end
|
526
90
|
end
|
527
91
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sql_query_executor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Caio Torres
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-03-
|
11
|
+
date: 2014-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -68,7 +68,6 @@ files:
|
|
68
68
|
- lib/sql_query_executor/operators/default.rb
|
69
69
|
- lib/sql_query_executor/operators/in.rb
|
70
70
|
- lib/sql_query_executor/operators/is.rb
|
71
|
-
- lib/sql_query_executor/query/base.rb
|
72
71
|
- lib/sql_query_executor/query/query_normalizer.rb
|
73
72
|
- lib/sql_query_executor/query/sentence.rb
|
74
73
|
- lib/sql_query_executor/query/sub_query.rb
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'sql_query_executor/query/query_normalizer'
|
2
|
-
|
3
|
-
module SqlQueryExecutor
|
4
|
-
module Query
|
5
|
-
class Base
|
6
|
-
STRING_SPACE = "$SS$"
|
7
|
-
QUERY_SPACE = "$QS$"
|
8
|
-
TEMP_SPACE = "$TS$"
|
9
|
-
|
10
|
-
def initialize(query, collection)
|
11
|
-
query = QueryNormalizer.execute(query)
|
12
|
-
@query = SqlQueryExecutor::Query::SubQuery.new query, collection
|
13
|
-
end
|
14
|
-
|
15
|
-
def execute!
|
16
|
-
@query.execute!
|
17
|
-
end
|
18
|
-
|
19
|
-
def to_sql
|
20
|
-
@query.to_sql
|
21
|
-
end
|
22
|
-
|
23
|
-
def selector
|
24
|
-
@query.selector
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|