Rubernate 0.1.4 → 0.1.5

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.
@@ -1,5 +1,8 @@
1
+ require 'rubernate/impl/dbi_generic'
2
+
1
3
  # Contains users methods for building queries
2
4
  module Rubernate
5
+ # This module contains classes and modules used for query building
3
6
  module Queries
4
7
  # Log instance for Queries
5
8
  Log = Rubernate::Log
@@ -9,12 +12,12 @@ module Queries
9
12
 
10
13
  # Contains operations definitions
11
14
  module Operations
12
- # Declares And sql clause.
15
+ # Declares AND sql clause.
13
16
  def And expr1, expr2
14
17
  @factory.bin_op expr1, expr2, 'and'
15
18
  end
16
19
 
17
- # Declares Or sql clause.
20
+ # Declares OR sql clause.
18
21
  def Or expr1, expr2
19
22
  @factory.bin_op expr1, expr2, 'or', true
20
23
  end
@@ -24,22 +27,22 @@ module Queries
24
27
  @factory.bin_op expr1, expr2, '='
25
28
  end
26
29
 
27
- # Declares Not sql clause.
30
+ # Declares NOT sql clause.
28
31
  def Not expr
29
32
  @factory.un_op expr, 'not', true
30
33
  end
31
34
 
32
- # Declares is null sql clause
35
+ # Declares IS NULL sql clause
33
36
  def IsNil expr
34
37
  @factory.bin_op expr, @factory.expr(nil), 'is'
35
38
  end
36
39
 
37
- # Declares is not null sql clause
40
+ # Declares IS NOT NULL sql clause
38
41
  def IsNotNil expr
39
42
  @factory.bin_op expr, @factory.expr(nil), 'is not'
40
43
  end
41
44
 
42
- # Declares in sql clause
45
+ # Declares IN sql clause
43
46
  def In expr, list
44
47
  @factory.bin_op expr, @factory.list(list), 'in'
45
48
  end
@@ -54,8 +57,8 @@ module Queries
54
57
  end
55
58
  end
56
59
 
57
- # Defines query elements factory. By default it creates elements defined
58
- # in module Generic, but this befavior can be changed by seting of appropriate class.
60
+ # Defines query elements factory. By default it creates elements defined in module Queries.
61
+ # This behaviour can be changed to provide support for kinds of queries other then ANSI SQL.
59
62
  # Following example changes implementation of BinOpConst
60
63
  #
61
64
  # :call-seq:
@@ -68,15 +71,15 @@ module Queries
68
71
  attr_reader :cache # Queries cache. TODO: limit size!
69
72
  # Initalizes default implementations
70
73
  def initialize
71
- @expr = Generic::Expr
72
- @un_op = Generic::UnOpConstr
73
- @bin_op = Generic::BinOpConstr
74
- @field = Generic::FieldExpr
75
- @key_ref = Generic::KeyRefExpr
76
- @list = Generic::ExprsList
77
- @r_param = Generic::RParam
78
- @r_object = Generic::RObject
79
- @query = Generic::Query
74
+ @expr = Expr
75
+ @un_op = UnOpConstr
76
+ @bin_op = BinOpConstr
77
+ @field = FieldExpr
78
+ @key_ref = KeyRefExpr
79
+ @list = ExprsList
80
+ @r_param = RParam
81
+ @r_object = RObject
82
+ @query = Query
80
83
  @cache = {}
81
84
  end
82
85
  def query query=nil, &block
@@ -100,365 +103,364 @@ module Queries
100
103
  end
101
104
 
102
105
  # Contains classes for standart ANSY SQL.
103
- module Generic
104
- # Represent abstract expression
105
- class Expr
106
- include Operations
107
- attr_reader :r_params, :markers
106
+ # Represent abstract expression
107
+ class Expr
108
+ include Operations
109
+ attr_reader :r_params, :markers
108
110
 
109
- def initialize factory, value = nil
110
- @factory, @value, @r_params, @markers = factory, value, [], []
111
- @markers << @value if @value.is_a? Symbol
112
- end
113
-
114
- # Generates SQL for expression
115
- def to_sql
116
- case @value
117
- when Symbol: '?'
118
- when Integer: @value
119
- when nil: 'null'
120
- else "'#{@value.to_s}'"
121
- end
122
- end
123
- private
124
- def fit_type expr
125
- case expr
126
- when RObject: expr.pk
127
- when RParam: expr.ref
128
- when Expr: expr
129
- else @factory.expr expr
130
- end
131
- end
132
- def self.def_bin_op ruby_op, sql_op
133
- module_eval %{
134
- def #{ruby_op} (other) @factory.bin_op self, other, '#{sql_op}'; end
135
- }
136
- end
137
- for op in [['==', '='], ['=~', '<>'], '<', '>', '<=', '>=']
138
- if op.is_a? Array
139
- def_bin_op(*op)
140
- else
141
- def_bin_op op, op
142
- end
143
- end
144
- end
111
+ def initialize factory, value = nil
112
+ @factory, @value, @r_params, @markers = factory, value, [], []
113
+ @markers << @value if @value.is_a? Symbol
114
+ end
145
115
 
146
- # Represents constraint for one columns.
147
- class UnOpConstr < Expr
148
- def initialize factory, expr, op, braces = false
149
- @factory, @op, @braces = factory, op, braces
150
- @expr = fit_type expr
151
- end
152
-
153
- def r_params
154
- @expr.r_params
116
+ # Generates SQL for expression
117
+ def to_sql
118
+ case @value
119
+ when Symbol: '?'
120
+ when Integer: @value
121
+ when nil: 'null'
122
+ else "'#{@value.to_s}'"
123
+ end
124
+ end
125
+ private
126
+ def fit_type expr
127
+ case expr
128
+ when RObject: expr.pk
129
+ when RParam: expr.name
130
+ when Expr: expr
131
+ else @factory.expr expr
132
+ end
155
133
  end
156
-
157
- def markers
158
- @expr.markers
134
+ def self.def_bin_op ruby_op, sql_op
135
+ module_eval %{
136
+ def #{ruby_op} (other) @factory.bin_op self, other, '#{sql_op}'; end
137
+ }
159
138
  end
160
-
161
- def to_sql
162
- if @braces
163
- "#{@op} (#{@expr.to_sql})"
139
+ for op in [['==', '='], ['=~', '<>'], '<', '>', '<=', '>=']
140
+ if op.is_a? Array
141
+ def_bin_op(*op)
164
142
  else
165
- "#{@op} #{@expr.to_sql}"
143
+ def_bin_op op, op
166
144
  end
167
145
  end
146
+ end
147
+
148
+ # Represents constraint for one columns.
149
+ class UnOpConstr < Expr
150
+ def initialize factory, expr, op, braces = false
151
+ @factory, @op, @braces = factory, op, braces
152
+ @expr = fit_type expr
168
153
  end
169
154
 
170
- # Represent constraint that applied on two columns.
171
- class BinOpConstr < Expr
172
- def initialize factory, expr1, expr2, sign, braces = false
173
- @factory, @sign, @braces = factory, sign, braces
174
- @expr1, @expr2 = fit_type(expr1), fit_type(expr2)
175
- end
176
-
177
- # Returns r_params used in both expressions
178
- def r_params
179
- @expr1.r_params + @expr2.r_params
180
- end
181
-
182
- # Returns markers used in both expressions
183
- def markers
184
- @expr1.markers + @expr2.markers
185
- end
186
-
187
- # Generates SQL for constraint
188
- def to_sql
189
- if @braces
190
- "(#{@expr1.to_sql} #{@sign} #{@expr2.to_sql})"
191
- else
192
- "#{@expr1.to_sql} #{@sign} #{@expr2.to_sql}"
193
- end
194
- end
195
- end
155
+ def r_params
156
+ @expr.r_params
157
+ end
196
158
 
197
- # Represent expression with table's field
198
- class FieldExpr < Expr
199
- def initialize factory, table, field
200
- @factory, @table, @field, @markers = factory, table, field, []
201
- @r_params = table.is_a?(RParam) ? [table] : []
202
- end
203
- # Creates constraint that check if this field is nil
204
- def is_nil
205
- IsNil self
206
- end
207
- # Creates constraint that check if this field is not nil
208
- def is_not_nil
209
- IsNotNil self
210
- end
211
-
212
- def to_sql
213
- @table.to_sql + '.' + @field
214
- end
159
+ def markers
160
+ @expr.markers
215
161
  end
216
162
 
217
- # Represent r_params for hashes and arrays constrained by key
218
- class KeyRefExpr < BinOpConstr
219
- def initialize factory, r_param, key_field, key_value
220
- @factory, @r_param, @key_field, @key_value = factory, r_param, key_field, key_value
221
- super factory, key_field, key_value, '='
222
- end
223
- def == expr
224
- And self, Eq(@r_param.ref, expr)
163
+ def to_sql
164
+ if @braces
165
+ "#{@op} (#{@expr.to_sql})"
166
+ else
167
+ "#{@op} #{@expr.to_sql}"
225
168
  end
226
169
  end
170
+ end
227
171
 
228
- # Reresents List of expressions
229
- class ExprsList < Expr
230
- def initialize factory, list
231
- @factory, @exprs = factory, list.collect{|expr| Expr === expr ? expr : factory.expr(expr)}
232
- end
233
- def to_sql
234
- '(' + @exprs.collect{|expr| expr.to_sql}.join(', ') + ')'
235
- end
236
- def markers
237
- @exprs.inject([]) {|res, expr| res.concat expr.markers}
238
- end
239
- def r_params
240
- @exprs.inject([]) {|res, expr| res.concat expr.r_params}
241
- end
172
+ # Represent constraint that applied on two columns.
173
+ class BinOpConstr < Expr
174
+ def initialize factory, expr1, expr2, sign, braces = false
175
+ @factory, @sign, @braces = factory, sign, braces
176
+ @expr1, @expr2 = fit_type(expr1), fit_type(expr2)
242
177
  end
243
178
 
244
- # Represents r_params table
245
- class RParam < Expr
246
- include Rubernate::DBI
247
-
248
- attr_reader :r_object, :name
249
-
250
- # Init Param accepts table and name of param
251
- def initialize factory, r_object, name
252
- @factory, @r_object, @name = factory, r_object, name
253
- @marker, @r_params = [], [self]
254
- end
255
-
256
- # Returns full param table name with object tables prefix
257
- def to_sql
258
- @r_object.to_sql + @name
179
+ # Returns r_params used in both expressions
180
+ def r_params
181
+ @expr1.r_params + @expr2.r_params
182
+ end
183
+
184
+ # Returns markers used in both expressions
185
+ def markers
186
+ @expr1.markers + @expr2.markers
187
+ end
188
+
189
+ # Generates SQL for constraint
190
+ def to_sql
191
+ if @braces
192
+ "(#{@expr1.to_sql} #{@sign} #{@expr2.to_sql})"
193
+ else
194
+ "#{@expr1.to_sql} #{@sign} #{@expr2.to_sql}"
195
+ end
196
+ end
197
+ end
198
+
199
+ # Represent expression with table's field
200
+ class FieldExpr < Expr
201
+ def initialize factory, table, field
202
+ @factory, @table, @field, @markers = factory, table, field, []
203
+ @r_params = table.is_a?(RParam) ? [table] : []
204
+ end
205
+ # Creates constraint that check if this field is nil
206
+ def is_nil
207
+ IsNil self
208
+ end
209
+ # Creates constraint that check if this field is not nil
210
+ def is_not_nil
211
+ IsNotNil self
212
+ end
213
+
214
+ def to_sql
215
+ @table.to_sql + '.' + @field
216
+ end
217
+ end
218
+
219
+ # Represent r_params for hashes and arrays constrained by key
220
+ class KeyRefExpr < BinOpConstr
221
+ def initialize factory, r_param, key_field, key_value
222
+ @factory, @r_param, @key_field, @key_value = factory, r_param, key_field, key_value
223
+ super factory, key_field, key_value, '='
224
+ end
225
+ def == expr
226
+ And self, Eq(@r_param.ref, expr)
227
+ end
228
+ end
229
+
230
+ # Reresents List of expressions
231
+ class ExprsList < Expr
232
+ def initialize factory, list
233
+ @factory, @exprs = factory, list.collect{|expr| Expr === expr ? expr : factory.expr(expr)}
234
+ end
235
+ def to_sql
236
+ '(' + @exprs.collect{|expr| expr.to_sql}.join(', ') + ')'
237
+ end
238
+ def markers
239
+ @exprs.inject([]) {|res, expr| res.concat expr.markers}
240
+ end
241
+ def r_params
242
+ @exprs.inject([]) {|res, expr| res.concat expr.r_params}
243
+ end
244
+ end
245
+
246
+ # Represents r_params table
247
+ class RParam < Expr
248
+ include Rubernate::DBI
249
+
250
+ attr_reader :r_object, :p_name
251
+
252
+ # Init Param accepts table and name of param
253
+ def initialize factory, r_object, p_name
254
+ @factory, @r_object, @p_name = factory, r_object, p_name
255
+ @markers, @r_params = [], [self]
256
+ end
257
+
258
+ # Returns full param table name with object tables prefix
259
+ def to_sql
260
+ @r_object.to_sql + @p_name
261
+ end
262
+
263
+ # Fields accessors.
264
+ def pk () f_expr 'object_pk'; end
265
+ def int () f_expr 'int_value'; end
266
+ def float() f_expr 'flt_value'; end
267
+ def str () f_expr 'str_value'; end
268
+ def time () f_expr 'dat_value'; end
269
+ def date () f_expr 'dat_value'; end
270
+ def ref () f_expr 'ref_value'; end
271
+ def flags() f_expr 'flags'; end
272
+ def name () f_expr 'name'; end
273
+
274
+ # The following methods checks r_param.flags value. (r_param.flags)
275
+ def is_int () Eq flags, PARAM_FLAG_INT; end
276
+ def is_str () Eq flags, PARAM_FLAG_STRING; end
277
+ def is_time() Eq flags, PARAM_FLAG_TIME; end
278
+ def is_ref () Eq flags, PARAM_FLAG_REF; end
279
+
280
+ # Shortcut for Eq method
281
+ def == expr
282
+ expr.is_a?(RObject) ?
283
+ @factory.bin_op(ref, expr.pk, '=') :
284
+ @factory.bin_op(ref, expr, '=')
285
+ end
286
+
287
+ # Shortcut for arrays and hashes
288
+ def [] key
289
+ case key
290
+ when Integer: key_ref int, key
291
+ when String: key_ref str, key
292
+ when Time: key_ref dat, key
293
+ when Symbol: key_ref int, key
294
+ else raise "invalid key value #{key}"
259
295
  end
260
-
261
- # Fields accessors.
262
- def pk () f_expr 'object_pk'; end
263
- def int () f_expr 'int_value'; end
264
- def float() f_expr 'flt_value'; end
265
- def str () f_expr 'str_value'; end
266
- def time () f_expr 'dat_value'; end
267
- def date () f_expr 'dat_value'; end
268
- def ref () f_expr 'ref_value'; end
269
- def flags() f_expr 'flags'; end
270
-
271
- # The following methods checks r_param.flags value. (r_param.flags)
272
- def is_int () Eq flags, PARAM_FLAG_INT; end
273
- def is_str () Eq flags, PARAM_FLAG_STRING; end
274
- def is_time() Eq flags, PARAM_FLAG_TIME; end
275
- def is_ref () Eq flags, PARAM_FLAG_REF; end
276
-
277
- # Shortcut for Eq method
278
- def == expr
279
- expr.is_a?(RObject) ?
280
- @factory.bin_op(ref, expr.pk, '=') :
281
- @factory.bin_op(ref, expr, '=')
296
+ end
297
+ private
298
+ # Creates FieldExpr witch field of this (r_param) table
299
+ def f_expr field
300
+ @factory.field self, field
282
301
  end
283
-
284
- # Shortcut for arrays and hashes
285
- def [] key
286
- case key
287
- when Integer: key_ref int, key
288
- when String: key_ref str, key
289
- when Time: key_ref dat, key
290
- when Symbol: key_ref int, key
291
- else raise "invalid key value #{key}"
292
- end
302
+ def key_ref key_field, key_value
303
+ @factory.key_ref self, key_field, key_value
293
304
  end
294
- private
295
- # Creates FieldExpr witch field of this (r_param) table
296
- def f_expr field
297
- @factory.field self, field
298
- end
299
- def key_ref key_field, key_value
300
- @factory.key_ref self, key_field, key_value
301
- end
305
+ end
306
+
307
+ # Represent r_objects table
308
+ class RObject < Expr
309
+ # Init Table accepts query and name of table
310
+ def initialize factory, name
311
+ @factory, @to_sql, @r_params = factory, name.to_s + '_', {}
312
+ @pk, @klass = @factory.field(self, 'object_pk'), @factory.field(self, 'object_class')
302
313
  end
303
314
 
304
- # Represent r_objects table
305
- class RObject < Expr
306
- # Init Table accepts query and name of table
307
- def initialize factory, name
308
- @factory, @to_sql, @r_params = factory, name.to_s + '_', {}
309
- @pk, @klass = @factory.field(self, 'object_pk'), @factory.field(self, 'object_class')
310
- end
311
-
312
- # Fiends access expressions
313
- attr_reader :pk, :klass, :to_sql
315
+ # Fiends access expressions
316
+ attr_reader :pk, :klass, :to_sql
314
317
 
315
- # Creates subclasses constraint
316
- def derived klass
317
- if klass.subclasses and not klass.subclasses.empty?
318
- In self.klass, [klass].concat(klass.subclasses)
319
- else
320
- Eq self.klass, klass
321
- end
322
- end
323
-
324
- # Tracks missing methods and creates accessor for params.
325
- def method_missing name, *params
326
- return super if params.size != 0
327
- def_param name
328
- end
329
-
330
- private
331
- # Defines accessor for new param. (joins r_param to r_objects)
332
- def def_param name
333
- instance_eval "def #{name}() @r_params[:#{name}]; end"
334
- @r_params[name] = @factory.r_param self, name.to_s
335
- end
318
+ # Creates subclasses constraint
319
+ def derived klass
320
+ if klass.subclasses and not klass.subclasses.empty?
321
+ In self.klass, [klass].concat(klass.subclasses)
322
+ else
323
+ Eq self.klass, klass
324
+ end
325
+ end
326
+
327
+ # Tracks missing methods and creates accessor for params.
328
+ def method_missing name, *params
329
+ return super if params.size != 0
330
+ def_param name
336
331
  end
337
332
 
338
- # Represents context in which query building executes
339
- # Holds constraints tables and so on.
340
- class Query < Expr
341
- include Operations
342
-
343
- # Accepts query as string or as block and executes it.
344
- def initialize factory, query = nil, &block
345
- @factory, @r_objects, @exprs, @order, @query = factory, {}, [], [], query
346
- @query = block if block_given?
333
+ private
334
+ # Defines accessor for new param. (joins r_param to r_objects)
335
+ def def_param name
336
+ instance_eval "def #{name}() @r_params[:#{name}]; end"
337
+ @r_params[name] = @factory.r_param self, name.to_s
347
338
  end
348
-
349
- # Next section contains methods available during query construction.
350
- # Declares objects for selection. The first argument will be result object.
351
- def Select main, *tables
352
- for t in [main, *tables].flatten
353
- @r_objects[t] = @factory.r_object t.to_s
354
- instance_eval "def #{t}() @r_objects[:#{t}]; end"
339
+ end
340
+
341
+ # Represents context in which query building executes
342
+ # Holds constraints tables and so on.
343
+ class Query < Expr
344
+ include Operations
345
+
346
+ # Accepts query as string or as block and executes it.
347
+ def initialize factory, query = nil, &block
348
+ @factory, @r_objects, @exprs, @order, @query = factory, {}, [], [], query
349
+ @query = block if block_given?
350
+ end
351
+
352
+ # Next section contains methods available during query construction.
353
+ # Declares objects for selection. The first argument will be result object.
354
+ def Select main, *tables
355
+ for t in [main, *tables].flatten
356
+ @r_objects[t] = @factory.r_object t.to_s
357
+ instance_eval "def #{t}() @r_objects[:#{t}]; end"
358
+ end
359
+ @main = @r_objects[main]
360
+ end
361
+
362
+ # Declares +Where+ clause.
363
+ def Where *exprs
364
+ @exprs = exprs
365
+ end
366
+
367
+ # Declares +Order By+ clause.
368
+ def OrderBy expr, *exprs
369
+ @order << expr
370
+ @order.concat exprs
371
+ end
372
+
373
+ # Returns markers used in query in valid order.
374
+ def markers
375
+ to_sql
376
+ @markers
377
+ end
378
+
379
+ # Arranges map: values {marker=>value} to ordered array of values
380
+ # accroding to markers in query.
381
+ def params values
382
+ markers.inject([]){|r, m| r << values[m]}
383
+ end
384
+
385
+ # Generates SQL for entire query.
386
+ def to_sql
387
+ return @sql if @sql
388
+ if @query.is_a? String
389
+ @sql, @markers = @factory.cache[@query]
390
+ unless @sql
391
+ @sql, @markers = eval_query
392
+ @factory.cache[@query] = [@sql, @markers]
355
393
  end
356
- @main = @r_objects[main]
394
+ else
395
+ @sql, @markers = eval_query
357
396
  end
358
-
359
- # Declares +Where+ clause.
360
- def Where *exprs
361
- @exprs = exprs
397
+ @sql
398
+ end
399
+
400
+ private
401
+ # Prints debug message
402
+ def log_debug_query sql
403
+ return unless Log.debug?
404
+ query = @query.is_a?(Proc) ? 'query given as block' : @query
405
+ Log.debug "Translate: <<#{query}>> to sql: <<#{sql}>>"
362
406
  end
363
407
 
364
- # Declares +Order By+ clause.
365
- def OrderBy expr, *exprs
366
- @order << expr
367
- @order.concat exprs
368
- end
369
-
370
- # Returns markers used in query in valid order.
371
- def markers
372
- to_sql
373
- @markers
408
+ # Evaluates query withing the object context. Returns sql, and array of markers: [sql, [m1,m2,..]]
409
+ def eval_query
410
+ if @query.is_a? Proc
411
+ instance_eval(&@query)
412
+ else
413
+ instance_eval @query
414
+ end
415
+ sql = "select #{@main.to_sql}.* from #{tables_sql}"
416
+ sql+= "\n\twhere #{where_sql}"
417
+ sql+= "\n\torder by #{order_by_sql}" unless @order.empty?
418
+ log_debug_query sql
419
+ [sql, @exprs.inject([]){|result, expr| result.concat expr.markers}]
420
+ end
421
+
422
+ # Generates where clause for each expression joined by and clause
423
+ def where_sql
424
+ r_params.collect{|rp| rp.to_sql + '.name = \'' + rp.p_name + '\''}.concat(
425
+ @exprs.collect{|ex| ex.to_sql}).join(" and\n\t\t")
374
426
  end
375
427
 
376
- # Arranges map: values {marker=>value} to ordered array of values
377
- # accroding to markers in query.
378
- def params values
379
- markers.inject([]){|r, m| r << values[m]}
428
+ # Generates order by sql clause
429
+ def order_by_sql
430
+ @order.collect{|ord| ord.to_sql}.join(', ')
380
431
  end
381
432
 
382
- # Generates SQL for entire query.
383
- def to_sql
384
- return @sql if @sql
385
- if @query.is_a? String
386
- @sql, @markers = @factory.cache[@query]
387
- unless @sql
388
- @sql, @markers = eval_query
389
- @factory.cache[@query] = [@sql, @markers]
433
+ # Generates left outer join for each r_params used in query
434
+ def tables_sql
435
+ r_joins = {}
436
+ r_objects.each{|r_o| r_joins[r_o.to_sql] ||= []} # Collect all r_objects used in query
437
+ r_params.each{|r_p| r_joins[r_p.r_object.to_sql] << r_p.to_sql} #Join all r_params to r_objects
438
+
439
+ result = nil
440
+ for obj, params in r_joins
441
+ result = result ? result << ",\n\t\t" : ''
442
+ result << 'r_objects ' + obj
443
+ for param in params
444
+ result << " left outer join r_params #{param} on (#{obj}.object_pk = #{param}.object_pk)"
390
445
  end
391
- else
392
- @sql, @markers = eval_query
393
446
  end
394
- @sql
447
+ result
448
+ end
449
+
450
+ # Retruns r_params used in query. List of RParam.
451
+ def r_params
452
+ res = @exprs.inject([]) {|res, exp| res.concat exp.r_params}
453
+ @order.inject(res) {|res, ord| res.concat ord.r_params}
454
+ res.uniq!
455
+ res
456
+ end
457
+
458
+ # Retruns r_objects used in query. List of RObject.
459
+ def r_objects
460
+ res = (@r_objects.values + r_params.inject([]){|res, rp| res << rp.r_object})
461
+ res.uniq!
462
+ res
395
463
  end
396
-
397
- private
398
- # Prints debug message
399
- def log_debug_query sql
400
- return unless Log.debug?
401
- query = @query.is_a?(Proc) ? 'query given as block' : @query
402
- Log.debug "Translate: <<#{query}>> to sql: <<#{sql}>>"
403
- end
404
-
405
- # Evaluates query withing the object context. Returns sql, and array of markers: [sql, [m1,m2,..]]
406
- def eval_query
407
- if @query.is_a? Proc
408
- instance_eval(&@query)
409
- else
410
- instance_eval @query
411
- end
412
- sql = "select #{@main.to_sql}.* from #{tables_sql}"
413
- sql+= "\n\twhere #{where_sql}"
414
- sql+= "\n\torder by #{order_by_sql}" unless @order.empty?
415
- log_debug_query sql
416
- [sql, @exprs.inject([]){|result, expr| result.concat expr.markers}]
417
- end
418
-
419
- # Generates where clause for each expression joined by and clause
420
- def where_sql
421
- r_params.collect{|rp| rp.to_sql + '.name = \'' + rp.name + '\''}.concat(
422
- @exprs.collect{|ex| ex.to_sql}).join(" and\n\t\t")
423
- end
424
-
425
- # Generates order by sql clause
426
- def order_by_sql
427
- @order.collect{|ord| ord.to_sql}.join(', ')
428
- end
429
-
430
- # Generates left outer join for each r_params used in query
431
- def tables_sql
432
- r_joins = {}
433
- r_objects.each{|r_o| r_joins[r_o.to_sql] ||= []} # Collect all r_objects used in query
434
- r_params.each{|r_p| r_joins[r_p.r_object.to_sql] << r_p.to_sql} #Join all r_params to r_objects
435
-
436
- result = nil
437
- for obj, params in r_joins
438
- result = result ? result << ",\n\t\t" : ''
439
- result << 'r_objects ' + obj
440
- for param in params
441
- result << " left outer join r_params #{param} on (#{obj}.object_pk = #{param}.object_pk)"
442
- end
443
- end
444
- result
445
- end
446
-
447
- # Retruns r_params used in query. List of RParam.
448
- def r_params
449
- res = @exprs.inject([]) {|res, exp| res.concat exp.r_params}
450
- @order.inject(res) {|res, ord| res.concat ord.r_params}
451
- res.uniq!
452
- res
453
- end
454
-
455
- # Retruns r_objects used in query. List of RObject.
456
- def r_objects
457
- res = (@r_objects.values + r_params.inject([]){|res, rp| res << rp.r_object})
458
- res.uniq!
459
- res
460
- end
461
- end
462
464
  end
463
465
  @@generic_factory = Factory.new
464
466
  end