smql 0.0.4.1 → 0.0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/smql_to_ar/condition_types.rb +45 -136
- data/lib/smql_to_ar/query_builder.rb +42 -126
- data/lib/smql_to_ar.rb +12 -64
- metadata +3 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
@@ -25,44 +25,28 @@ class SmqlToAR
|
|
25
25
|
# Nimmt eine Klasse ein Objekt an, so soll diese Klasse instanziert werden.
|
26
26
|
# Alles weitere siehe Condition.
|
27
27
|
module ConditionTypes
|
28
|
-
extend SmqlToAR::Assertion
|
29
|
-
|
30
28
|
class <<self
|
31
29
|
# Ex: 'givenname|surname|nick' => [:givenname, :surname, :nick]
|
32
30
|
def split_keys k
|
33
31
|
k.split( '|').collect &:to_sym
|
34
32
|
end
|
35
33
|
|
36
|
-
def conditions &e
|
37
|
-
unless block_given?
|
38
|
-
r = Enumerator.new( self, :conditions)
|
39
|
-
s = self
|
40
|
-
r.define_singleton_method :[] do |k|
|
41
|
-
s.conditions.select {|c| c::Operator === k }
|
42
|
-
end
|
43
|
-
return r
|
44
|
-
end
|
45
|
-
constants.each do |c|
|
46
|
-
next if :Condition == c
|
47
|
-
c = const_get c
|
48
|
-
next if Condition === c
|
49
|
-
yield c
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
34
|
# Eine Regel parsen.
|
54
35
|
# Ex: Person, "givenname=", "Peter"
|
55
36
|
def try_parse_it model, colop, val
|
56
37
|
r = nil
|
57
38
|
#p :try_parse => { :model => model, :colop => colop, :value => val }
|
58
|
-
|
59
|
-
|
39
|
+
constants.each do |c|
|
40
|
+
next if :Condition == c
|
41
|
+
c = const_get c
|
42
|
+
next if Condition === c
|
43
|
+
raise UnexpectedColOpError.new( model, colop, val) unless colop =~ /^(?:\d*:)?(.*?)(\W*)$/
|
60
44
|
col, op = $1, $2
|
61
45
|
col = split_keys( col).collect {|c| Column.new model, c }
|
62
46
|
r = c.try_parse model, col, op, val
|
63
47
|
break if r
|
64
48
|
end
|
65
|
-
|
49
|
+
raise UnexpectedError.new( model, colop, val) unless r
|
66
50
|
r
|
67
51
|
end
|
68
52
|
|
@@ -88,24 +72,16 @@ class SmqlToAR
|
|
88
72
|
end
|
89
73
|
|
90
74
|
class Condition
|
91
|
-
include SmqlToAR::Assertion
|
92
|
-
extend SmqlToAR::Assertion
|
93
75
|
attr_reader :value, :cols
|
94
76
|
Operator = nil
|
95
77
|
Expected = []
|
96
78
|
Where = nil
|
97
79
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
new model, cols, val if self::Operator === op and self::Expected.any?( &it === val)
|
104
|
-
end
|
105
|
-
|
106
|
-
def inspect
|
107
|
-
"#{self.name}(:operator=>#{self::Operator.inspect}, :expected=>#{self::Expected.inspect}, :where=>#{self::Where.inspect})"
|
108
|
-
end
|
80
|
+
# Versuche das Objekt zu erkennen. Operator und Expected muessen passen.
|
81
|
+
# Passt das Object, die Klasse instanzieren.
|
82
|
+
def self.try_parse model, cols, op, val
|
83
|
+
#p :self => name, :try_parse => op, :cols => cols, :with => self::Operator, :value => val, :expected => self::Expected, :model => model.name
|
84
|
+
new model, cols, val if self::Operator === op and self::Expected.any? {|i| i === val }
|
109
85
|
end
|
110
86
|
|
111
87
|
def initialize model, cols, val
|
@@ -117,10 +93,6 @@ class SmqlToAR
|
|
117
93
|
verify
|
118
94
|
end
|
119
95
|
|
120
|
-
def inspect
|
121
|
-
"#<#{self.class.name}:0x#{(self.object_id<<1).to_s 16} model: #{self.class.name}, cols: #{@cols.inspect}, value: #{@value.inspect}>"
|
122
|
-
end
|
123
|
-
|
124
96
|
def verify
|
125
97
|
@cols.each do |col|
|
126
98
|
verify_column col
|
@@ -131,19 +103,19 @@ class SmqlToAR
|
|
131
103
|
# Gibt es eine Spalte diesen Namens?
|
132
104
|
# Oder: Gibt es eine Relation diesen Namens? (Hier nicht der Fall)
|
133
105
|
def verify_column col
|
134
|
-
|
106
|
+
raise NonExistingColumnError.new( %w[Column], col) unless col.exist_in?
|
135
107
|
end
|
136
108
|
|
137
109
|
# Modelle koennen Spalten/Relationen verbieten mit Model#smql_protected.
|
138
110
|
# Dieses muss ein Object mit #include?( name_als_string) zurueckliefern,
|
139
111
|
# welches true fuer verboten und false fuer, erlaubt steht.
|
140
112
|
def verify_allowed col
|
141
|
-
|
113
|
+
raise ProtectedColumnError.new( col) if col.protected?
|
142
114
|
end
|
143
115
|
|
144
116
|
# Erstelle alle noetigen Klauseln. builder nimmt diese entgegen,
|
145
|
-
# wobei builder.
|
146
|
-
# mehrere Schluessel bedeuten, dass die Values _alle_ zutreffen muessen, wobei die Schluessel
|
117
|
+
# wobei builder.join, builder.select, builder.where und builder.wobs von interesse sind.
|
118
|
+
# mehrere Schluessel bedeuten, dass die Values _alle_ zutreffen muessen, wobei die Schluessel geodert werden.
|
147
119
|
# Ex:
|
148
120
|
# 1) {"givenname=", "Peter"} #=> givenname = 'Peter'
|
149
121
|
# 2) {"givenname=", ["Peter", "Hans"]} #=> ( givenname = 'Peter' OR givenname = 'Hans' )
|
@@ -156,16 +128,15 @@ class SmqlToAR
|
|
156
128
|
@cols.each do |col|
|
157
129
|
col.joins builder, table
|
158
130
|
col = builder.column table+col.path, col.col
|
159
|
-
builder.where values.keys.collect {|vid| self.class::Where % [ col, vid.to_s ] }
|
131
|
+
builder.where *values.keys.collect {|vid| self.class::Where % [ col, vid.to_s ] }
|
160
132
|
end
|
161
133
|
else
|
162
|
-
b2 = SmqlToAR::And.new builder
|
163
134
|
values.keys.each do |vid|
|
164
|
-
|
135
|
+
builder.where *@cols.collect {|col|
|
165
136
|
col.joins builder, table
|
166
137
|
col = builder.column table+col.path, col.col
|
167
138
|
self.class::Where % [ col, vid.to_s ]
|
168
|
-
}
|
139
|
+
}
|
169
140
|
end
|
170
141
|
end
|
171
142
|
self
|
@@ -214,92 +185,50 @@ class SmqlToAR
|
|
214
185
|
|
215
186
|
In = simple_condition NotIn, '|=', '%s IN (%s)', [Array]
|
216
187
|
In2 = simple_condition In, '', nil, [Array]
|
217
|
-
NotEqual = simple_condition Condition,
|
218
|
-
NotEqual2 = simple_condition Condition, '<>', "%s <> %s", [Array, String, Numeric]
|
188
|
+
NotEqual = simple_condition Condition, /\!=|<>/, "%s <> %s", [Array, String, Numeric]
|
219
189
|
GreaterThanOrEqual = simple_condition Condition, '>=', "%s >= %s", [Array, Numeric]
|
220
190
|
LesserThanOrEqual = simple_condition Condition, '<=', "%s <= %s", [Array, Numeric]
|
221
|
-
|
222
|
-
# Examples:
|
223
|
-
# { 'articles=>' => { id: 1 } }
|
224
|
-
# { 'articles=>' => [ { id: 1 }, { id: 2 } ] }
|
225
191
|
class EqualJoin <Condition
|
226
|
-
Operator = '
|
227
|
-
Expected = [Hash
|
192
|
+
Operator = '='
|
193
|
+
Expected = [Hash]
|
228
194
|
|
229
195
|
def initialize *pars
|
230
196
|
super( *pars)
|
231
|
-
@value = Array.wrap @value
|
232
197
|
cols = {}
|
233
198
|
@cols.each do |col|
|
234
|
-
col_model = col.
|
235
|
-
|
199
|
+
col_model = SmqlToAR.model_of col.last_model, col.col
|
200
|
+
#p col_model: col_model.to_s, value: @value
|
201
|
+
cols[col] = [col_model] + ConditionTypes.try_parse( col_model, @value)
|
236
202
|
end
|
237
203
|
@cols = cols
|
238
204
|
end
|
239
205
|
|
240
206
|
def verify_column col
|
241
|
-
|
207
|
+
refl = SmqlToAR.model_of col.last_model, col.col
|
208
|
+
#p refl: refl, model: @model.name, col: col, :reflections => @model.reflections.keys
|
209
|
+
raise NonExistingRelationError.new( %w[Relation], col) unless refl
|
242
210
|
end
|
243
211
|
|
244
212
|
def build builder, table
|
245
213
|
@cols.each do |col, sub|
|
246
|
-
model, *sub = sub
|
247
214
|
t = table + col.path + [col.col]
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
self
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
# Takes to Queries.
|
258
|
-
# First Query will be a Subquery, second a regular query.
|
259
|
-
# Example:
|
260
|
-
# Person.smql 'sub.articles:' => [{'limit:' => 1, 'order:': 'updated_at desc'}, {'content~' => 'some text'}]
|
261
|
-
# Person must have as last Article (compared by updated_at) owned by Person a Artive which has 'some text' in content.
|
262
|
-
# The last Article needn't to have 'some text' has content, the subquery takes it anyway.
|
263
|
-
# But the second query compares to it and never to any other Article, because these are filtered by first query.
|
264
|
-
# The difference to
|
265
|
-
# Person.smql :articles => {'content~' => 'some text', 'limit:' => 1, 'order:': 'updated_at desc'}
|
266
|
-
# is, second is not allowed (limit and order must be in root) and this means something like
|
267
|
-
# "Person must have the Article owned by Person which has 'some text' in content.
|
268
|
-
# limit and order has no function in this query and this article needn't to be the last."
|
269
|
-
class SubEqualJoin < EqualJoin
|
270
|
-
Operator = '()'
|
271
|
-
Expected = [lambda {|x| x.kind_of?( Array) and (1..2).include?( x.length) and x.all?( &it.kind_of?( Hash))}]
|
272
|
-
|
273
|
-
def initialize model, cols, val
|
274
|
-
super model, cols, val[1]
|
275
|
-
# sub: model, subquery, sub(condition)
|
276
|
-
@cols.each {|col, sub| sub[ 1..-1] = SmqlToAR.new( col.relation, val[0]).parse, *sub[-1] }
|
277
|
-
end
|
278
|
-
|
279
|
-
def verify_column col
|
280
|
-
raise_unless col.child?, ConColumnError.new( [:Column], col)
|
281
|
-
end
|
282
|
-
|
283
|
-
def build builder, table
|
284
|
-
@cols.each do |col, sub|
|
285
|
-
t = table+col.to_a
|
286
|
-
builder.sub_joins t, col, *sub[0..1]
|
287
|
-
#ap sub: sub[2..-1]
|
288
|
-
sub[2..-1].each &it.build( builder, t)
|
215
|
+
#p sub: sub
|
216
|
+
p col: col, joins: col.joins
|
217
|
+
col.joins.each {|j, m| builder.join table+j, m }
|
218
|
+
builder.join t, SmqlToAR.model_of( col.last_model, col.col)
|
219
|
+
sub[1..-1].each {|one| one.build builder, t }
|
289
220
|
end
|
290
221
|
self
|
291
222
|
end
|
292
223
|
end
|
293
|
-
|
294
224
|
Equal = simple_condition Condition, '=', "%s = %s", [Array, String, Numeric]
|
295
225
|
Equal2 = simple_condition Equal, '', "%s = %s", [String, Numeric]
|
296
226
|
GreaterThan = simple_condition Condition, '>', "%s > %s", [Array, Numeric]
|
297
227
|
LesserThan = simple_condition Condition, '<', "%s < %s", [Array, Numeric]
|
298
228
|
NotIlike = simple_condition Condition, '!~', "%s NOT ILIKE %s", [Array, String]
|
299
229
|
Ilike = simple_condition Condition, '~', "%s ILIKE %s", [Array, String]
|
300
|
-
Exists = simple_condition Condition, '', '%s IS NOT NULL', [true]
|
301
|
-
NotExists = simple_condition Condition, '', '%s IS NULL', [false]
|
302
230
|
|
231
|
+
####### No Operator #######
|
303
232
|
Join = simple_condition EqualJoin, '', nil, [Hash]
|
304
233
|
InRange2 = simple_condition InRange, '', nil, [Range]
|
305
234
|
class Select < Condition
|
@@ -307,7 +236,7 @@ class SmqlToAR
|
|
307
236
|
Expected = [nil]
|
308
237
|
|
309
238
|
def verify_column col
|
310
|
-
|
239
|
+
raise NonExistingSelectableError.new( col) unless col.exist_in? or SmqlToAR.model_of( col.last_model, col.col)
|
311
240
|
end
|
312
241
|
|
313
242
|
def build builder, table
|
@@ -329,19 +258,13 @@ class SmqlToAR
|
|
329
258
|
Expected = [String, Array, Hash, Numeric, nil]
|
330
259
|
|
331
260
|
class Function
|
332
|
-
include SmqlToAR::Assertion
|
333
261
|
Name = nil
|
334
262
|
Expected = []
|
335
263
|
attr_reader :model, :func, :args
|
336
264
|
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
end
|
341
|
-
|
342
|
-
def inspect
|
343
|
-
"#{self.name}(:name=>#{self::Name}, :expected=>#{self::Expected})"
|
344
|
-
end
|
265
|
+
def self.try_parse model, func, args
|
266
|
+
SmqlToAR.logger.info( { try_parse: [func,args]}.inspect)
|
267
|
+
self.new model, func, args if self::Name === func and self::Expected.any? {|e| e === args }
|
345
268
|
end
|
346
269
|
|
347
270
|
def initialize model, func, args
|
@@ -354,19 +277,22 @@ class SmqlToAR
|
|
354
277
|
Expected = [String, Array, Hash, nil]
|
355
278
|
|
356
279
|
def initialize model, func, args
|
280
|
+
SmqlToAR.logger.info( {args: args}.inspect)
|
357
281
|
args = case args
|
358
282
|
when String then [args]
|
359
283
|
when Array, Hash then args.to_a
|
360
284
|
when nil then nil
|
361
285
|
else raise 'Oops'
|
362
286
|
end
|
287
|
+
SmqlToAR.logger.info( {args: args}.inspect)
|
363
288
|
args.andand.collect! do |o|
|
364
289
|
o = Array.wrap o
|
365
290
|
col = Column.new model, o.first
|
366
291
|
o = 'desc' == o.last.to_s.downcase ? :DESC : :ASC
|
367
|
-
|
292
|
+
raise NonExistingColumnError.new( [:Column], col) unless col.exist_in?
|
368
293
|
[col, o]
|
369
294
|
end
|
295
|
+
SmqlToAR.logger.info( {args: args}.inspect)
|
370
296
|
super model, func, args
|
371
297
|
end
|
372
298
|
|
@@ -376,39 +302,22 @@ class SmqlToAR
|
|
376
302
|
col, o = o
|
377
303
|
col.joins builder, table
|
378
304
|
t = table + col.path
|
379
|
-
|
305
|
+
#raise OnlyOrderOnBaseError.new( t) unless 1 == t.length
|
380
306
|
builder.order t, col.col, o
|
381
307
|
end
|
382
308
|
end
|
383
309
|
end
|
384
310
|
|
385
|
-
class Limit < Function
|
386
|
-
Name = :limit
|
387
|
-
Expected = [Fixnum]
|
388
|
-
|
389
|
-
def build builder, table
|
390
|
-
raise_unless 1 == table.length, RootOnlyFunctionError.new( table)
|
391
|
-
builder.limit = Array.wrap(@args).first.to_i
|
392
|
-
end
|
393
|
-
end
|
394
|
-
|
395
|
-
class Offset < Function
|
396
|
-
Name = :offset
|
397
|
-
Expected = [Fixnum]
|
398
|
-
|
399
|
-
def build builder, table
|
400
|
-
raise_unless 1 == table.length, RootOnlyFunctionError.new( table)
|
401
|
-
builder.offset = Array.wrap(@args).first.to_i
|
402
|
-
end
|
403
|
-
end
|
404
|
-
|
405
311
|
def self.new model, col, val
|
312
|
+
SmqlToAR.logger.info( { function: col.first.to_sym }.inspect)
|
406
313
|
r = nil
|
407
314
|
constants.each do |c|
|
408
315
|
next if [:Function, :Where, :Expected, :Operator].include? c
|
409
316
|
c = const_get c
|
410
317
|
next if Function === c or not c.respond_to?( :try_parse)
|
318
|
+
SmqlToAR.logger.info( {f: c}.inspect)
|
411
319
|
r = c.try_parse model, col.first.to_sym, val
|
320
|
+
SmqlToAR.logger.info( {r: r}.inspect)
|
412
321
|
break if r
|
413
322
|
end
|
414
323
|
r
|
@@ -22,41 +22,36 @@ class SmqlToAR
|
|
22
22
|
class Vid
|
23
23
|
attr_reader :vid
|
24
24
|
def initialize( vid) @vid = vid end
|
25
|
-
def to_s() ":
|
26
|
-
def to_sym() "
|
25
|
+
def to_s() ":c#{@vid}" end
|
26
|
+
def to_sym() "c#{@vid}".to_sym end
|
27
27
|
alias sym to_sym
|
28
28
|
def to_i() @vid end
|
29
29
|
end
|
30
30
|
|
31
|
-
attr_reader :table_alias, :model, :table_model, :base_table, :_where, :_select, :_wobs, :_joins
|
32
|
-
attr_accessor :logger
|
31
|
+
attr_reader :table_alias, :model, :table_model, :base_table, :_where, :_select, :_wobs, :_joins
|
32
|
+
attr_accessor :logger
|
33
33
|
|
34
|
-
def initialize model
|
35
|
-
@prefix = "smql"
|
34
|
+
def initialize model
|
36
35
|
@logger = SmqlToAR.logger
|
37
36
|
@table_alias = Hash.new do |h, k|
|
38
|
-
|
39
|
-
h[k] =
|
37
|
+
k = Array.wrap k
|
38
|
+
h[k] = "smql,#{k.join(',')}"
|
40
39
|
end
|
41
|
-
@_vid, @_where, @_wobs, @model, @quoter = 0,
|
40
|
+
@_vid, @_where, @_wobs, @model, @quoter = 0, [], {}, model, model.connection
|
42
41
|
@base_table = [model.table_name.to_sym]
|
43
42
|
@table_alias[ @base_table] = @base_table.first
|
44
43
|
t = quote_table_name @table_alias[ @base_table]
|
45
|
-
@_select, @_joins, @_joined, @_includes, @_order = ["DISTINCT #{t}.*"], "", [
|
44
|
+
@_select, @_joins, @_joined, @_includes, @_order = ["DISTINCT #{t}.*"], "", [], [], []
|
46
45
|
@table_model = {@base_table => @model}
|
47
46
|
end
|
48
47
|
|
49
48
|
def vid() Vid.new( @_vid+=1) end
|
50
49
|
|
51
|
-
def inspect
|
52
|
-
"#<#{self.class.name}:#{"0x%x"% (self.object_id<<1)}|#{@prefix}:#{@base_table}:#{@model} vid=#{@_vid} where=#{@_where} wobs=#{@_wobs} select=#{@_select} aliases=#{@_table_alias}>"
|
53
|
-
end
|
54
|
-
|
55
50
|
# Jede via where uebergebene Condition wird geodert und alle zusammen werden geundet.
|
56
51
|
# "Konjunktive Normalform". Allerdings duerfen Conditions auch Komplexe Abfragen enthalten.
|
57
|
-
# Ex: builder.where(
|
52
|
+
# Ex: builder.where( 'a = a', 'b = c').where( 'c = d', 'e = e').where( 'x = y').where( '( m = n AND o = p )', 'f = g')
|
58
53
|
# #=> WHERE ( a = a OR b = c ) AND ( c = d OR e = e ) AND x = y ( ( m = n AND o = p ) OR f = g )
|
59
|
-
def where cond
|
54
|
+
def where *cond
|
60
55
|
@_where.push cond
|
61
56
|
self
|
62
57
|
end
|
@@ -79,52 +74,30 @@ class SmqlToAR
|
|
79
74
|
end
|
80
75
|
|
81
76
|
def build_join orig, pretable, table, prekey, key
|
82
|
-
"
|
77
|
+
" JOIN #{quote_table_name orig.to_sym} AS #{quote_table_name table} ON #{column pretable, prekey} = #{column table, key} "
|
83
78
|
end
|
84
79
|
|
85
|
-
def
|
86
|
-
|
87
|
-
|
88
|
-
end
|
89
|
-
|
90
|
-
def join_ table, model, query, pretable = nil
|
91
|
-
pretable ||= table[0...-1]
|
80
|
+
def join table, model
|
81
|
+
return self if @_joined.include? table # Already joined
|
82
|
+
pretable = table[0...-1]
|
92
83
|
@table_model[ table] = model
|
93
84
|
premodel = @table_model[ pretable]
|
94
85
|
t = @table_alias[ table]
|
95
86
|
pt = quote_table_name @table_alias[ table[ 0...-1]]
|
96
87
|
refl = premodel.reflections[table.last]
|
97
|
-
case refl
|
98
|
-
when
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
case refl.macro
|
108
|
-
when :has_many
|
109
|
-
@_joins += build_join query, pretable, t, premodel.primary_key, refl.primary_key_name
|
110
|
-
when :belongs_to
|
111
|
-
@_joins += build_join query, pretable, t, refl.primary_key_name, premodel.primary_key
|
112
|
-
when :has_and_belongs_to_many
|
113
|
-
jointable = [','] + table
|
114
|
-
@_joins += build_join refl.options[:join_table], pretable, @table_alias[jointable], premodel.primary_key, refl.primary_key_name
|
115
|
-
@_joins += build_join query, jointable, t, refl.association_foreign_key, refl.association_primary_key
|
116
|
-
else raise BuilderError, "Unkown reflection macro: #{refl.macro.inspect}"
|
117
|
-
end
|
118
|
-
else raise BuilderError, "Unkown reflection type: #{refl.class.name}"
|
88
|
+
case refl.macro
|
89
|
+
when :has_many
|
90
|
+
@_joins += build_join model.table_name, pretable, t, premodel.primary_key, refl.primary_key_name
|
91
|
+
when :belongs_to
|
92
|
+
@_joins += build_join model.table_name, pretable, t, refl.primary_key_name, premodel.primary_key
|
93
|
+
when :has_and_belongs_to_many
|
94
|
+
jointable = [','] + table
|
95
|
+
@_joins += build_join refl.options[:join_table], pretable, @table_alias[jointable], premodel.primary_key, refl.primary_key_name
|
96
|
+
@_joins += build_join model.table_name, jointable, t, refl.association_foreign_key, refl.association_primary_key
|
97
|
+
else raise BuilderError, "Unkown reflection macro: #{refl.macro.inspect}"
|
119
98
|
end
|
120
|
-
self
|
121
|
-
end
|
122
|
-
|
123
|
-
def joins table, model
|
124
|
-
table = table.flatten.compact
|
125
|
-
return self if @_joined.include? table # Already joined
|
126
|
-
join_ table, model, quote_table_name( model.table_name)
|
127
99
|
@_joined.push table
|
100
|
+
self
|
128
101
|
end
|
129
102
|
|
130
103
|
def includes table
|
@@ -138,25 +111,36 @@ class SmqlToAR
|
|
138
111
|
end
|
139
112
|
|
140
113
|
def order table, col, o
|
141
|
-
|
114
|
+
tc = column table, col
|
115
|
+
@_select.push ct
|
116
|
+
@_order.push "#{ct} #{:DESC == o ? :DESC : :ASC}"
|
117
|
+
self
|
118
|
+
end
|
119
|
+
|
120
|
+
class Dummy
|
121
|
+
def method_missing m, *a, &e
|
122
|
+
#p :dummy => m, :pars => a, :block => e
|
123
|
+
self
|
124
|
+
end
|
142
125
|
end
|
143
126
|
|
144
127
|
def build_ar
|
145
|
-
where_str = @_where.
|
128
|
+
where_str = @_where.collect do |w|
|
129
|
+
w = Array.wrap w
|
130
|
+
1 == w.length ? w.first : "( #{w.join( ' OR ')} )"
|
131
|
+
end.join ' AND '
|
146
132
|
incls = {}
|
147
133
|
@_includes.each do |inc|
|
148
134
|
b = incls
|
149
135
|
inc[1..-1].collect {|rel| b = b[rel] ||= {} }
|
150
136
|
end
|
137
|
+
@logger.debug incls: incls, joins: @_joins
|
151
138
|
@model = @model.
|
152
139
|
select( @_select.join( ', ')).
|
153
140
|
joins( @_joins).
|
154
141
|
where( where_str, @_wobs).
|
155
142
|
order( @_order.join( ', ')).
|
156
143
|
includes( incls)
|
157
|
-
@model = @model.limit @limit if @limit
|
158
|
-
@model = @model.offset @offset if @offset
|
159
|
-
@model
|
160
144
|
end
|
161
145
|
|
162
146
|
def fix_calculate
|
@@ -175,72 +159,4 @@ class SmqlToAR
|
|
175
159
|
@model
|
176
160
|
end
|
177
161
|
end
|
178
|
-
|
179
|
-
class SubBuilder < Array
|
180
|
-
attr_reader :parent, :_where
|
181
|
-
delegate :wobs, :joins, :includes, :sub_joins, :vid, :quote_column_name, :quoter, :quote_table_name, :column, :to => :parent
|
182
|
-
|
183
|
-
def initialize parent, tmp = false
|
184
|
-
@parent = parent
|
185
|
-
@parent.where self unless @parend.nil? && tmp
|
186
|
-
end
|
187
|
-
|
188
|
-
def new parent, tmp = false
|
189
|
-
super parent, tmp
|
190
|
-
#return parent if self.class == parent.class
|
191
|
-
#super parent
|
192
|
-
end
|
193
|
-
|
194
|
-
alias where push
|
195
|
-
|
196
|
-
def type_correction!
|
197
|
-
collect! do |sub|
|
198
|
-
if sub.kind_of? Array
|
199
|
-
sub = default[ *sub] unless sub.respond_to?( :type_correction!)
|
200
|
-
sub.type_correction!
|
201
|
-
end
|
202
|
-
sub
|
203
|
-
end
|
204
|
-
self
|
205
|
-
end
|
206
|
-
|
207
|
-
def optimize!
|
208
|
-
ext = []
|
209
|
-
collect! do |sub|
|
210
|
-
sub = sub.optimize! if sub.kind_of? Array
|
211
|
-
if self.class == sub.class
|
212
|
-
ext.push *sub
|
213
|
-
nil
|
214
|
-
elsif sub.blank?
|
215
|
-
nil
|
216
|
-
else
|
217
|
-
sub
|
218
|
-
end
|
219
|
-
end.compact!
|
220
|
-
push *ext
|
221
|
-
self
|
222
|
-
end
|
223
|
-
|
224
|
-
def inspect
|
225
|
-
"#{self.class.name.sub( /.*::/, '')}[ #{collect(&:inspect).join ', '}]"
|
226
|
-
end
|
227
|
-
def default() SmqlToAR::And end
|
228
|
-
def default_new( parent) default.new self, parent, false end
|
229
|
-
def collect_build_where
|
230
|
-
collect {|x| x.respond_to?( :build_where) ? x.build_where : x.to_s }
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
class And < SubBuilder
|
235
|
-
def default; SmqlToAR::Or; end
|
236
|
-
def build_where
|
237
|
-
collect_build_where.join ' AND '
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
class Or < SubBuilder
|
242
|
-
def build_where
|
243
|
-
collect_build_where.join ' OR '
|
244
|
-
end
|
245
|
-
end
|
246
162
|
end
|
data/lib/smql_to_ar.rb
CHANGED
@@ -15,18 +15,6 @@
|
|
15
15
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
16
|
|
17
17
|
class SmqlToAR
|
18
|
-
module Assertion
|
19
|
-
def raise_unless cond, exception = nil, *args
|
20
|
-
cond, exception, *args = yield. cond, exception, *args if block_given?
|
21
|
-
raise exception || Exception, *args unless cond
|
22
|
-
end
|
23
|
-
|
24
|
-
def raise_if cond, exception = nil, *args
|
25
|
-
cond, exception, *args = yield. cond, exception, *args if block_given?
|
26
|
-
raise exception || Exception, *args if cond
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
18
|
include ActiveSupport::Benchmarkable
|
31
19
|
############################################################################r
|
32
20
|
# Exceptions
|
@@ -89,27 +77,19 @@ class SmqlToAR
|
|
89
77
|
end
|
90
78
|
end
|
91
79
|
|
92
|
-
class
|
80
|
+
class OnlyOrderOnBaseError < SMQLError
|
93
81
|
def initialize path
|
94
82
|
super :path => path
|
95
83
|
end
|
96
84
|
end
|
97
85
|
|
98
|
-
class ConColumnError < SMQLError
|
99
|
-
def initialize expected, got
|
100
|
-
super :expected => expected, :got => got
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
86
|
class BuilderError < Exception; end
|
105
87
|
|
106
88
|
#############################################################################
|
107
89
|
|
108
90
|
# Model der Relation `rel` von `model`
|
109
91
|
def self.model_of model, rel
|
110
|
-
|
111
|
-
r = model.reflections[ rel].andand.klass
|
112
|
-
r.nil? && :self == rel ? model : r
|
92
|
+
model.reflections[ rel.to_sym].andand.klass
|
113
93
|
end
|
114
94
|
|
115
95
|
# Eine Spalte in einer Tabelle, relativ zu `Column#model`.
|
@@ -123,7 +103,7 @@ class SmqlToAR
|
|
123
103
|
def initialize model, *col
|
124
104
|
@model = model
|
125
105
|
@last_model = nil
|
126
|
-
*@path, @col =
|
106
|
+
*@path, @col = Array.wrap( col).collect {|s| s.to_s.split /[.\/]/ }.flatten.collect &:to_sym
|
127
107
|
end
|
128
108
|
|
129
109
|
def last_model
|
@@ -133,12 +113,9 @@ class SmqlToAR
|
|
133
113
|
def each
|
134
114
|
model = @model
|
135
115
|
@path.each do |rel|
|
136
|
-
|
137
|
-
unless
|
138
|
-
|
139
|
-
return false unless model
|
140
|
-
yield rel, model
|
141
|
-
end
|
116
|
+
model = SmqlToAR.model_of model, rel
|
117
|
+
return false unless model
|
118
|
+
yield rel, model
|
142
119
|
end
|
143
120
|
model
|
144
121
|
end
|
@@ -163,23 +140,19 @@ class SmqlToAR
|
|
163
140
|
def joins builder = nil, table = nil, &exe
|
164
141
|
pp = []
|
165
142
|
table = Array.wrap table
|
166
|
-
exe ||= builder ? lambda {|j, m| builder.
|
143
|
+
exe ||= builder ? lambda {|j, m| builder.join table+j, m} : Array.method( :[])
|
167
144
|
collect do |rel, model|
|
168
145
|
pp.push rel
|
169
146
|
exe.call pp, model
|
170
147
|
end
|
171
148
|
end
|
172
|
-
def
|
173
|
-
def length() @path.length+(self.self? ? 0 : 1) end
|
174
|
-
def size() @path.size+(self.self? ? 0 : 1) end
|
175
|
-
def to_a() @path+(self.self? ? [] : [@col]) end
|
149
|
+
def to_a() @path+[@col] end
|
176
150
|
def to_s() to_a.join '.' end
|
177
151
|
def to_sym() to_s.to_sym end
|
178
152
|
def to_json() to_s end
|
179
153
|
def inspect() "#<Column: #{model} #{to_s}>" end
|
180
|
-
def relation()
|
154
|
+
def relation() SmqlToAR.model_of last_model, @col end
|
181
155
|
def allowed?() ! self.protected? end
|
182
|
-
def child?() @path.empty? and !!relation end
|
183
156
|
end
|
184
157
|
|
185
158
|
attr_reader :model, :query, :conditions, :builder, :order
|
@@ -206,23 +179,6 @@ class SmqlToAR
|
|
206
179
|
#p model: @model, query: @query
|
207
180
|
end
|
208
181
|
|
209
|
-
def self.models models
|
210
|
-
models = Array.wrap models
|
211
|
-
r = Hash.new {|h,k| h[k] = {} }
|
212
|
-
while model = models.tap( &:uniq!).pop
|
213
|
-
refls = model.respond_to?( :reflections) && model.reflections
|
214
|
-
refls && refls.each do |name, refl|
|
215
|
-
r[model.name][name] = case refl
|
216
|
-
when ActiveRecord::Reflection::ThroughReflection then {:macro => refl.macro, :model => refl.klass.name, :through => refl.through_reflection.name}
|
217
|
-
when ActiveRecord::Reflection::AssociationReflection then {:macro => refl.macro, :model => refl.klass.name}
|
218
|
-
else raise "Ups: #{refl.class}"
|
219
|
-
end
|
220
|
-
models.push refl.klass unless r.keys.include? refl.klass.name
|
221
|
-
end
|
222
|
-
end
|
223
|
-
r
|
224
|
-
end
|
225
|
-
|
226
182
|
def parse
|
227
183
|
benchmark 'SMQL parse' do
|
228
184
|
@conditions = ConditionTypes.try_parse @model, @query
|
@@ -231,11 +187,11 @@ class SmqlToAR
|
|
231
187
|
self
|
232
188
|
end
|
233
189
|
|
234
|
-
def build
|
190
|
+
def build
|
235
191
|
benchmark 'SMQL build query' do
|
236
|
-
@builder = QueryBuilder.new @model
|
192
|
+
@builder = QueryBuilder.new @model
|
237
193
|
table = @builder.base_table
|
238
|
-
@conditions.each
|
194
|
+
@conditions.each {|condition| condition.build builder, table }
|
239
195
|
end
|
240
196
|
#p builder: @builder
|
241
197
|
self
|
@@ -258,12 +214,4 @@ class SmqlToAR
|
|
258
214
|
def self.to_ar *params
|
259
215
|
new( *params).to_ar
|
260
216
|
end
|
261
|
-
|
262
|
-
def self.reload_library
|
263
|
-
lib_dir = File.dirname __FILE__
|
264
|
-
fj = lambda {|*a| File.join lib_dir, *a }
|
265
|
-
load fj.call( 'smql_to_ar.rb')
|
266
|
-
load fj.call( 'smql_to_ar', 'condition_types.rb')
|
267
|
-
load fj.call( 'smql_to_ar', 'query_builder.rb')
|
268
|
-
end
|
269
217
|
end
|
metadata
CHANGED
@@ -6,8 +6,8 @@ version: !ruby/object:Gem::Version
|
|
6
6
|
- 0
|
7
7
|
- 0
|
8
8
|
- 4
|
9
|
-
-
|
10
|
-
version: 0.0.4.
|
9
|
+
- 2
|
10
|
+
version: 0.0.4.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Denis Knauf
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-10-
|
18
|
+
date: 2011-10-27 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|