native-query 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +25 -0
- data/LICENSE.txt +20 -0
- data/README.md +462 -0
- data/Rakefile +31 -0
- data/VERSION +1 -0
- data/lib/native-query/join.rb +318 -0
- data/lib/native-query/model.rb +76 -0
- data/lib/native-query/query.rb +430 -0
- data/lib/native-query/result.rb +113 -0
- data/lib/native-query/row.rb +75 -0
- data/native-query.gemspec +60 -0
- metadata +113 -0
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
|
6
|
+
begin
|
7
|
+
Bundler.setup(:default, :development)
|
8
|
+
rescue Bundler::BundlerError => e
|
9
|
+
$stderr.puts e.message
|
10
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
11
|
+
exit e.status_code
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'rake'
|
15
|
+
require 'jeweler'
|
16
|
+
|
17
|
+
Jeweler::Tasks.new do |gem|
|
18
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
19
|
+
gem.name = "native-query"
|
20
|
+
gem.homepage = "http://github.com/martinkozak/native-query"
|
21
|
+
gem.license = "MIT"
|
22
|
+
gem.summary = 'Cool way how to speak with database server. It\'s ellegant and very ruby SQL query helper which works by similar way as Arel or another ORM selecting logic. It\'s derived from Dibi database layer in its ideas, so is much more simple and (of sure) much more KISS, readable and straightforward.'
|
23
|
+
gem.email = "martinkozak@martinkozak.net"
|
24
|
+
gem.authors = ["Martin Kozák"]
|
25
|
+
# Include your dependencies below. Runtime dependencies are required when using your gem,
|
26
|
+
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
27
|
+
# gem.add_runtime_dependency 'jabber4r', '> 0.1'
|
28
|
+
# gem.add_development_dependency 'rspec', '> 1.2.3'
|
29
|
+
end
|
30
|
+
Jeweler::RubygemsDotOrgTasks.new
|
31
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.9.0
|
@@ -0,0 +1,318 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "native-query/query"
|
3
|
+
require "hash-utils/object" # >= 0.17.0
|
4
|
+
require "hash-utils/array"
|
5
|
+
require "hash-utils/string" # >= 0.18.0
|
6
|
+
|
7
|
+
module NativeQuery
|
8
|
+
|
9
|
+
##
|
10
|
+
# Represents join request.
|
11
|
+
#
|
12
|
+
|
13
|
+
class Join
|
14
|
+
|
15
|
+
##
|
16
|
+
# Indicates table to join from.
|
17
|
+
#
|
18
|
+
|
19
|
+
@original
|
20
|
+
|
21
|
+
##
|
22
|
+
# Indicates table to join.
|
23
|
+
#
|
24
|
+
|
25
|
+
@table
|
26
|
+
|
27
|
+
##
|
28
|
+
# Indicates fields for select.
|
29
|
+
#
|
30
|
+
|
31
|
+
@fields
|
32
|
+
|
33
|
+
##
|
34
|
+
# Holds joins specifiaction.
|
35
|
+
#
|
36
|
+
|
37
|
+
@joins
|
38
|
+
attr_reader :joins
|
39
|
+
|
40
|
+
##
|
41
|
+
# Holds join conditions.
|
42
|
+
#
|
43
|
+
|
44
|
+
@where
|
45
|
+
|
46
|
+
##
|
47
|
+
# Indicates type of joining.
|
48
|
+
# Possible values are:
|
49
|
+
#
|
50
|
+
# * :indirect for M:N relation,
|
51
|
+
# * :direct for 1:N relation
|
52
|
+
#
|
53
|
+
|
54
|
+
@type
|
55
|
+
|
56
|
+
##
|
57
|
+
# Contains indirect joining manual specification.
|
58
|
+
#
|
59
|
+
|
60
|
+
@indirect
|
61
|
+
|
62
|
+
##
|
63
|
+
# Holds specificarion of direct joining.
|
64
|
+
#
|
65
|
+
|
66
|
+
@direct
|
67
|
+
|
68
|
+
##
|
69
|
+
# Constructor.
|
70
|
+
#
|
71
|
+
|
72
|
+
def initialize(original, table)
|
73
|
+
@table = table
|
74
|
+
@original = original
|
75
|
+
@fields = [ ]
|
76
|
+
@where = [ ]
|
77
|
+
@joins = [ ]
|
78
|
+
@type = :direct
|
79
|
+
|
80
|
+
@indirect_source = original
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# Sets hash for select from joined table.
|
85
|
+
# Without arguments returns fields list.
|
86
|
+
#
|
87
|
+
|
88
|
+
def fields(*args)
|
89
|
+
if args.empty?
|
90
|
+
result = { }
|
91
|
+
@fields.each do |i|
|
92
|
+
if not i.kind_of? Hash
|
93
|
+
i = {i => i}
|
94
|
+
end
|
95
|
+
|
96
|
+
i.each_pair do |from, to|
|
97
|
+
result[__fix_field(from)] = @table.to_s << "_" << to.to_s
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
return result
|
102
|
+
else
|
103
|
+
@fields += args
|
104
|
+
return self
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# Selects where conditions to load.
|
110
|
+
#
|
111
|
+
|
112
|
+
def where(*args)
|
113
|
+
@where << args
|
114
|
+
return self
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# Indicates indirect joining. (M:N)
|
119
|
+
#
|
120
|
+
|
121
|
+
def indirect(*args)
|
122
|
+
@type = :indirect
|
123
|
+
if args.first.array? and (args.first.first == :backward) and (args.first.second.array?)
|
124
|
+
@indirect = args.first
|
125
|
+
else
|
126
|
+
@indirect = args
|
127
|
+
end
|
128
|
+
|
129
|
+
return self
|
130
|
+
end
|
131
|
+
|
132
|
+
##
|
133
|
+
# Indicates direct joining. (1:M)
|
134
|
+
#
|
135
|
+
|
136
|
+
def direct(*args)
|
137
|
+
@type = :direct
|
138
|
+
if args.first.array? and (args.first.first == :backward) and (args.first.second.array?)
|
139
|
+
@direct = args.first
|
140
|
+
else
|
141
|
+
@direct = args
|
142
|
+
end
|
143
|
+
|
144
|
+
return self
|
145
|
+
end
|
146
|
+
|
147
|
+
##
|
148
|
+
# Indicates backward joining.
|
149
|
+
#
|
150
|
+
|
151
|
+
def backward(*args)
|
152
|
+
[:backward, args]
|
153
|
+
end
|
154
|
+
|
155
|
+
##
|
156
|
+
# Builds ON join string.
|
157
|
+
#
|
158
|
+
|
159
|
+
def build
|
160
|
+
result = nil
|
161
|
+
|
162
|
+
case @type
|
163
|
+
when :indirect
|
164
|
+
result = __indirect
|
165
|
+
when :direct
|
166
|
+
result = __direct
|
167
|
+
end
|
168
|
+
|
169
|
+
return result
|
170
|
+
end
|
171
|
+
|
172
|
+
##
|
173
|
+
# Return wheres.
|
174
|
+
#
|
175
|
+
|
176
|
+
def wheres
|
177
|
+
self._fix_where
|
178
|
+
end
|
179
|
+
|
180
|
+
##
|
181
|
+
# Calls mapping to joins specification. Call name is name of
|
182
|
+
# the target table.
|
183
|
+
#
|
184
|
+
# Block works by the same way as query, but for join. But
|
185
|
+
# intra-join calls doesn't work because it returns Query too.
|
186
|
+
#
|
187
|
+
|
188
|
+
def method_missing(sym, *args, &block)
|
189
|
+
join = self.class::new(@table, sym)
|
190
|
+
|
191
|
+
if args and not args.empty?
|
192
|
+
join.fields(*args)
|
193
|
+
end
|
194
|
+
|
195
|
+
join.instance_eval(&block)
|
196
|
+
@joins << join
|
197
|
+
|
198
|
+
return self
|
199
|
+
end
|
200
|
+
|
201
|
+
##
|
202
|
+
# Fixes field name. Joins table name if SQL table joining
|
203
|
+
# required.
|
204
|
+
#
|
205
|
+
|
206
|
+
private
|
207
|
+
def __fix_field(name, formatted = false)
|
208
|
+
NativeQuery::Query::fix_field(name, @table, formatted)
|
209
|
+
end
|
210
|
+
|
211
|
+
|
212
|
+
##
|
213
|
+
# Fixes where specification(s) if it's hash with symbol key.
|
214
|
+
#
|
215
|
+
|
216
|
+
protected
|
217
|
+
def _fix_where
|
218
|
+
NativeQuery::Query::fix_conditions(@where) do |args|
|
219
|
+
args.each do |i|
|
220
|
+
__fix_field(i)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
##
|
226
|
+
# Builds indirect join.
|
227
|
+
#
|
228
|
+
|
229
|
+
private
|
230
|
+
def __indirect
|
231
|
+
|
232
|
+
if (@indirect.first == :backward) and (@indirect.second.array?)
|
233
|
+
backward = true
|
234
|
+
indirect = @indirect.second
|
235
|
+
else
|
236
|
+
backward = false
|
237
|
+
indirect = @indirect
|
238
|
+
end
|
239
|
+
|
240
|
+
##
|
241
|
+
|
242
|
+
result = { }
|
243
|
+
to = @table.to_s
|
244
|
+
from = @original.to_s
|
245
|
+
arg1, arg2, arg3 = indirect
|
246
|
+
|
247
|
+
# automatic joining
|
248
|
+
if indirect.empty?
|
249
|
+
from.swap_with(to) if backward
|
250
|
+
through = from + "_" + to
|
251
|
+
joining_table = through.to_sym
|
252
|
+
result[joining_table] = "[" << from << ".id] = [" << through << "." << from << "_id]"
|
253
|
+
result[@table] = "[" << through << "." << to << "_id] = [" << to << ".id]"
|
254
|
+
|
255
|
+
# standard specification (semiautomatic joining)
|
256
|
+
elsif arg1.symbol? and arg2.hash?
|
257
|
+
through = arg1.to_s
|
258
|
+
joining_table = arg1
|
259
|
+
result[joining_table] = "[" << from << "." << arg2.keys.first.to_s << "] = [" << through << "." << from << "_id]"
|
260
|
+
result[@table] = "[" << through << "." << to << "_id] = [" << to << "." << arg2.values.first.to_s << "]"
|
261
|
+
|
262
|
+
# fluent query specification (manual joining)
|
263
|
+
elsif arg1.symbol? and arg2.string? and arg3.string?
|
264
|
+
joining_table = arg1
|
265
|
+
result[joining_table] = arg2
|
266
|
+
result[@table] = arg3
|
267
|
+
|
268
|
+
# error
|
269
|
+
else
|
270
|
+
raise Exception::new("Symbol and Hash or Symbol and two Strings expected.")
|
271
|
+
|
272
|
+
end
|
273
|
+
|
274
|
+
return result
|
275
|
+
end
|
276
|
+
|
277
|
+
##
|
278
|
+
# Builds direct join.
|
279
|
+
#
|
280
|
+
|
281
|
+
private
|
282
|
+
def __direct
|
283
|
+
|
284
|
+
if (@direct.first == :backward) and (@direct.second.array?)
|
285
|
+
backward = true
|
286
|
+
direct = @direct.second
|
287
|
+
else
|
288
|
+
backward = false
|
289
|
+
direct = @direct
|
290
|
+
end
|
291
|
+
|
292
|
+
##
|
293
|
+
|
294
|
+
empty = direct.empty?
|
295
|
+
direct = direct.first
|
296
|
+
from = @original.to_s
|
297
|
+
to = @table.to_s
|
298
|
+
result = { }
|
299
|
+
|
300
|
+
# automatic joining
|
301
|
+
if empty
|
302
|
+
from.swap_with(to) if backward
|
303
|
+
result[@table] = "[" << from << ".id] = [" << to << "." << from << "_id]"
|
304
|
+
# manual joining
|
305
|
+
elsif direct.hash?
|
306
|
+
result[@table] = "[" << from << "." << direct.keys.first.to_s << "] = [" << to << "." << direct.values.first.to_s << "]"
|
307
|
+
# special joining
|
308
|
+
elsif direct.string?
|
309
|
+
result[@table] = direct
|
310
|
+
# error
|
311
|
+
else
|
312
|
+
raise Exception::new("Hash or String expected.")
|
313
|
+
end
|
314
|
+
|
315
|
+
return result
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "fluent-query/connection"
|
3
|
+
require "native-query/query"
|
4
|
+
|
5
|
+
module NativeQuery
|
6
|
+
|
7
|
+
##
|
8
|
+
# Represents instance of ORM model.
|
9
|
+
#
|
10
|
+
|
11
|
+
class Model
|
12
|
+
|
13
|
+
##
|
14
|
+
# Brings database connection object.
|
15
|
+
#
|
16
|
+
|
17
|
+
@connection
|
18
|
+
|
19
|
+
##
|
20
|
+
# Brings driver setting.
|
21
|
+
#
|
22
|
+
|
23
|
+
@driver
|
24
|
+
|
25
|
+
##
|
26
|
+
# Brings database connection configuration setting.
|
27
|
+
#
|
28
|
+
|
29
|
+
@configuration
|
30
|
+
|
31
|
+
##
|
32
|
+
# Constructor.
|
33
|
+
#
|
34
|
+
|
35
|
+
def initialize(driver, configuration)
|
36
|
+
@driver = driver
|
37
|
+
@configuration = configuration
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Returns connection.
|
42
|
+
#
|
43
|
+
|
44
|
+
def connection
|
45
|
+
if not @connection
|
46
|
+
@connection = FluentQuery::Connection::new(@driver, @configuration)
|
47
|
+
end
|
48
|
+
|
49
|
+
@connection # returns
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Maps missing calls to tables.
|
54
|
+
#
|
55
|
+
# Arguments are expected to be field names, so given to
|
56
|
+
# field query method.
|
57
|
+
#
|
58
|
+
|
59
|
+
def method_missing(sym, *args, &block)
|
60
|
+
query = Query::new(self.connection, sym)
|
61
|
+
|
62
|
+
if args and not args.empty?
|
63
|
+
query.fields(*args)
|
64
|
+
end
|
65
|
+
|
66
|
+
if block
|
67
|
+
result = query.instance_eval(&block)
|
68
|
+
else
|
69
|
+
result = query
|
70
|
+
end
|
71
|
+
|
72
|
+
return result
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,430 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "native-query/result"
|
4
|
+
require "native-query/join"
|
5
|
+
require "hash-utils/object"
|
6
|
+
require "hash-utils/array"
|
7
|
+
require "hash-utils/hash"
|
8
|
+
|
9
|
+
module NativeQuery
|
10
|
+
|
11
|
+
##
|
12
|
+
# Represents ORM query.
|
13
|
+
#
|
14
|
+
|
15
|
+
class Query
|
16
|
+
|
17
|
+
##
|
18
|
+
# Brings database connection object.
|
19
|
+
#
|
20
|
+
|
21
|
+
@connection
|
22
|
+
|
23
|
+
##
|
24
|
+
# Indicates upon which table query works.
|
25
|
+
#
|
26
|
+
|
27
|
+
@table
|
28
|
+
|
29
|
+
##
|
30
|
+
# Holds list of fields to load.
|
31
|
+
#
|
32
|
+
|
33
|
+
@fields
|
34
|
+
|
35
|
+
##
|
36
|
+
# Holds list of where conditions.
|
37
|
+
#
|
38
|
+
|
39
|
+
@where
|
40
|
+
|
41
|
+
##
|
42
|
+
# Holds list of having conditions.
|
43
|
+
#
|
44
|
+
|
45
|
+
@having
|
46
|
+
|
47
|
+
##
|
48
|
+
# Holds order specification.
|
49
|
+
#
|
50
|
+
|
51
|
+
@order
|
52
|
+
|
53
|
+
##
|
54
|
+
# Joins for the query.
|
55
|
+
#
|
56
|
+
|
57
|
+
@joins
|
58
|
+
|
59
|
+
##
|
60
|
+
# Indicates limit to load.
|
61
|
+
#
|
62
|
+
|
63
|
+
@limit
|
64
|
+
|
65
|
+
##
|
66
|
+
# Indicates offset to load.
|
67
|
+
#
|
68
|
+
|
69
|
+
@offset
|
70
|
+
|
71
|
+
##
|
72
|
+
# Holds group by settings.
|
73
|
+
#
|
74
|
+
|
75
|
+
@group
|
76
|
+
|
77
|
+
##
|
78
|
+
# Constructor.
|
79
|
+
#
|
80
|
+
|
81
|
+
def initialize(connection, table, &block)
|
82
|
+
@connection = connection
|
83
|
+
@table = table
|
84
|
+
@fields = [ ]
|
85
|
+
@where = [ ]
|
86
|
+
@having = [ ]
|
87
|
+
@joins = [ ]
|
88
|
+
@order = [ ]
|
89
|
+
@group = [ ]
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# Calls mapping to joins specification. Call name is name of
|
94
|
+
# the target table.
|
95
|
+
#
|
96
|
+
# Block works by the same way as query, but for join. But
|
97
|
+
# intra-join calls doesn't work because it returns Query too.
|
98
|
+
#
|
99
|
+
|
100
|
+
def method_missing(sym, *args, &block)
|
101
|
+
join = Join::new(@table, sym)
|
102
|
+
|
103
|
+
if args and not args.empty?
|
104
|
+
join.fields(*args)
|
105
|
+
end
|
106
|
+
|
107
|
+
if not block.nil?
|
108
|
+
join.instance_eval(&block)
|
109
|
+
end
|
110
|
+
|
111
|
+
@joins << join
|
112
|
+
return self
|
113
|
+
end
|
114
|
+
|
115
|
+
##
|
116
|
+
# Selects fields which to load.
|
117
|
+
#
|
118
|
+
|
119
|
+
def fields(*args)
|
120
|
+
@fields += args
|
121
|
+
return self
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# Selects where conditions to load.
|
126
|
+
#
|
127
|
+
|
128
|
+
def where(*args)
|
129
|
+
@where << args
|
130
|
+
return self
|
131
|
+
end
|
132
|
+
|
133
|
+
##
|
134
|
+
# Selects having conditions to load.
|
135
|
+
#
|
136
|
+
|
137
|
+
def having(*args)
|
138
|
+
@having << args
|
139
|
+
return self
|
140
|
+
end
|
141
|
+
|
142
|
+
##
|
143
|
+
# Selects fields for ordering according them.
|
144
|
+
# Default order is :asc.
|
145
|
+
#
|
146
|
+
# Expects [:<field>, [:asc|:desc]]+ arguments.
|
147
|
+
#
|
148
|
+
|
149
|
+
def order(*args)
|
150
|
+
@order += args
|
151
|
+
return self
|
152
|
+
end
|
153
|
+
|
154
|
+
##
|
155
|
+
# Sets limit to load.
|
156
|
+
#
|
157
|
+
|
158
|
+
def limit(limit)
|
159
|
+
@limit = limit
|
160
|
+
return self
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# Sets offset to load.
|
165
|
+
#
|
166
|
+
|
167
|
+
def offset(offset)
|
168
|
+
@offset = offset
|
169
|
+
return self
|
170
|
+
end
|
171
|
+
|
172
|
+
##
|
173
|
+
# Sets group by to load.
|
174
|
+
#
|
175
|
+
|
176
|
+
def group(*fields)
|
177
|
+
@group = fields
|
178
|
+
return self
|
179
|
+
end
|
180
|
+
|
181
|
+
##
|
182
|
+
# Returns result object.
|
183
|
+
#
|
184
|
+
|
185
|
+
def get
|
186
|
+
|
187
|
+
# Builds query
|
188
|
+
|
189
|
+
# Process joins
|
190
|
+
join_fields, wheres, append_joins = self._process_joins
|
191
|
+
|
192
|
+
# Checkouts regular fields
|
193
|
+
if @joins.empty?
|
194
|
+
fields = @fields
|
195
|
+
hashes = [ ]
|
196
|
+
group = @group
|
197
|
+
else
|
198
|
+
fields = @fields.map { |i| __fix_field(i) }
|
199
|
+
hashes = fields.reject { |i| not i.hash? }
|
200
|
+
fields -= hashes
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
query = @connection.query
|
205
|
+
|
206
|
+
if not fields.empty?
|
207
|
+
query.select(fields)
|
208
|
+
end
|
209
|
+
|
210
|
+
if not join_fields.empty?
|
211
|
+
query.select(join_fields)
|
212
|
+
end
|
213
|
+
|
214
|
+
if not hashes.empty?
|
215
|
+
hashes.each do |hash|
|
216
|
+
query.select(hash)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
|
221
|
+
query.from(@table)
|
222
|
+
|
223
|
+
# Appends joins
|
224
|
+
append_joins.call(query)
|
225
|
+
|
226
|
+
# Where conditions
|
227
|
+
wheres += self._fix_where
|
228
|
+
wheres.each { |i| query.where(*i) }
|
229
|
+
|
230
|
+
# Where conditions
|
231
|
+
havings = self._fix_having
|
232
|
+
havings.each { |i| query.having(*i) }
|
233
|
+
|
234
|
+
# Grouping, ordering and having settings
|
235
|
+
self._process_grouping(query)
|
236
|
+
self._process_ordering(query)
|
237
|
+
|
238
|
+
# Limit and offset
|
239
|
+
if not @limit.nil?
|
240
|
+
query.limit(@limit)
|
241
|
+
end
|
242
|
+
|
243
|
+
if not @offset.nil?
|
244
|
+
query.offset(@offset)
|
245
|
+
end
|
246
|
+
|
247
|
+
# Returns
|
248
|
+
return Result::new(query)
|
249
|
+
|
250
|
+
end
|
251
|
+
|
252
|
+
##
|
253
|
+
# Builds itself to string.
|
254
|
+
#
|
255
|
+
|
256
|
+
def build
|
257
|
+
self.get.query.build
|
258
|
+
end
|
259
|
+
|
260
|
+
alias :"build!" :build
|
261
|
+
|
262
|
+
##
|
263
|
+
# Process joins.
|
264
|
+
#
|
265
|
+
|
266
|
+
protected
|
267
|
+
def _process_joins
|
268
|
+
fields = { }
|
269
|
+
wheres = [ ]
|
270
|
+
specs = { }
|
271
|
+
joins = [ ]
|
272
|
+
|
273
|
+
new_joins = @joins.dup
|
274
|
+
add_joins = [ ]
|
275
|
+
|
276
|
+
# Agregates subjoins (e.g. joins from joins)
|
277
|
+
while not new_joins.empty?
|
278
|
+
new_joins.each do |join|
|
279
|
+
add_joins += join.joins
|
280
|
+
end
|
281
|
+
|
282
|
+
joins += new_joins
|
283
|
+
new_joins = add_joins
|
284
|
+
add_joins = [ ]
|
285
|
+
end
|
286
|
+
|
287
|
+
# Process joins
|
288
|
+
joins.each do |join|
|
289
|
+
fields.merge! join.fields
|
290
|
+
wheres += join.wheres
|
291
|
+
specs.merge! join.build
|
292
|
+
end
|
293
|
+
|
294
|
+
callback = Proc::new do |query|
|
295
|
+
specs.each_pair do |join, on|
|
296
|
+
query.join(join).on(on)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
return fields, wheres, callback
|
301
|
+
end
|
302
|
+
|
303
|
+
##
|
304
|
+
# Process ordering settngs.
|
305
|
+
#
|
306
|
+
|
307
|
+
protected
|
308
|
+
def _process_ordering(query)
|
309
|
+
# Ordering settings
|
310
|
+
if not @order.nil?
|
311
|
+
@order.each do |i|
|
312
|
+
case i
|
313
|
+
when :asc
|
314
|
+
query.asc
|
315
|
+
when :desc
|
316
|
+
query.desc
|
317
|
+
else
|
318
|
+
if i.array?
|
319
|
+
query.orderBy("[" << i.first.to_s << "." << i.second.to_s << "]")
|
320
|
+
else
|
321
|
+
query.orderBy(__fix_field(i, true))
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
|
329
|
+
##
|
330
|
+
# Process ordering settngs.
|
331
|
+
#
|
332
|
+
|
333
|
+
protected
|
334
|
+
def _process_grouping(query)
|
335
|
+
# Grouping settings
|
336
|
+
@group.each do |i|
|
337
|
+
if i.array?
|
338
|
+
query.groupBy("[" << i.first.to_s << "." << i.second.to_s << "]")
|
339
|
+
else
|
340
|
+
query.groupBy(__fix_field(i, true))
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
##
|
346
|
+
# Fixes field name. Joins table name if SQL table joining
|
347
|
+
# required.
|
348
|
+
#
|
349
|
+
|
350
|
+
private
|
351
|
+
def __fix_field(name, formatted = false)
|
352
|
+
if not @joins.empty?
|
353
|
+
result = self.class::fix_field(name, @table, formatted)
|
354
|
+
else
|
355
|
+
result = name
|
356
|
+
end
|
357
|
+
|
358
|
+
return result
|
359
|
+
end
|
360
|
+
|
361
|
+
##
|
362
|
+
# Fixes field name. Joins table name if SQL table joining
|
363
|
+
# required.
|
364
|
+
#
|
365
|
+
|
366
|
+
def self.fix_field(name, table, formatted = false)
|
367
|
+
if name.hash?
|
368
|
+
result = name.map_keys { |k| self.fix_field(k, table, formatted) }
|
369
|
+
else
|
370
|
+
result = table.to_s + "." + name.to_s
|
371
|
+
end
|
372
|
+
|
373
|
+
if formatted
|
374
|
+
result = "[" << result << "]"
|
375
|
+
end
|
376
|
+
|
377
|
+
return result
|
378
|
+
end
|
379
|
+
|
380
|
+
##
|
381
|
+
# Fixes where specification(s) if it's hash with symbol key.
|
382
|
+
#
|
383
|
+
|
384
|
+
protected
|
385
|
+
def _fix_where
|
386
|
+
Query::fix_conditions(@where) do |args|
|
387
|
+
args.each do |i|
|
388
|
+
__fix_field(i)
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
##
|
394
|
+
# Fixes having specification(s) if it's hash with symbol key.
|
395
|
+
#
|
396
|
+
|
397
|
+
protected
|
398
|
+
def _fix_having
|
399
|
+
Query::fix_conditions(@having) do |args|
|
400
|
+
args.each do |i|
|
401
|
+
__fix_field(i)
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
##
|
407
|
+
# Fixes where specification(s) if it's hash with symbol key.
|
408
|
+
# Block is fixer.
|
409
|
+
#
|
410
|
+
|
411
|
+
def self.fix_conditions(where, &block)
|
412
|
+
where.map do |specification|
|
413
|
+
if specification.hash?
|
414
|
+
new = specification.map_keys do |k|
|
415
|
+
if k.symbol?
|
416
|
+
block.call(k)
|
417
|
+
else
|
418
|
+
k
|
419
|
+
end
|
420
|
+
end
|
421
|
+
else
|
422
|
+
new = specification
|
423
|
+
end
|
424
|
+
|
425
|
+
new # returns
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
end
|
430
|
+
end
|