smql 0.0.4.1 → 0.0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|