"self" and or
============= This lines mean the same: User.smql :self => {:id => 1} User.smql :id => 1 User.smql 'self.id' => 1 User.smql 'self.self.self.self.id' => 1 self is like a reflection to itself. Very userful if you need the new disjunction: User.smql :self => [{:surname => 'Mueller', :givenname => 'Michael'}, {:givenname => 'Horst', :surname => 'Schlemmer'}], :firm => {:name => 'Hotel an der Elbe'} SmqlToAR::Column ---------------- Rejects every `self`, so it is really like no self, but you use `'self'` as like as a simple reflection. You can ask, if it is self via Column#self? SmqlToAR::QueryBuilder ====================== joins: The old `#join`. Renames because SmqlToAR::And and SmqlToAR::Or which has Array#join, so we cannot delegate `#join` to the QueryBuilder. `#where` -------- Now only one argument! Needed for SmqlToAR::And and SmqlToAR::Or. `#build` will generates LEFT OUTER JOINS now. Needed for disjunctions. The most queries will work like before. Problems: User.smql :articles => {} Before it will return all users with articles, now it will return also users without articles. If you want to have only all users with articles, you ask: User.smql :articles => {:id => true} Will fail if id IS NULL, but this should not happen. ;) `SmqlToAR::And` and `SmqlToAR::Or` ================================== `SmqlToAR::QueryBuilder`-proxies. QueryBuilder let them build where-clauses. And will will produce a conjunction and Or a disjunction of course. They delegates all QueryBuilder-methods to QueryBuilder. Only `#where` will stored local and `#build` will do it partial. They have the same superclass: `SmqlToAR::SubBuilder`. The small changes ================= * `SmqlToAR.reload_library`: Reloads SmqlToAR-lib. Useful while development. * `SmqlToAR::ConditionTypes#conditions`: Return all Conditions. `#try_parse` uses it. * Some classes have a new `#inspect`. * `SmqlToAR::ConditionTypes#Exists` / `NotExists`: `{:id => true}` / `{:id => false}`: This object has setted an id or not? `#id` must exists as column of course! Uses `IS NOT NULL` and `IS NULL`.
This commit is contained in:
parent
93fd3eeda4
commit
2fdc45d1d5
3 changed files with 184 additions and 73 deletions
|
@ -107,7 +107,9 @@ class SmqlToAR
|
||||||
|
|
||||||
# Model der Relation `rel` von `model`
|
# Model der Relation `rel` von `model`
|
||||||
def self.model_of model, rel
|
def self.model_of model, rel
|
||||||
model.reflections[ rel.to_sym].andand.klass
|
rel = rel.to_sym
|
||||||
|
r = model.reflections[ rel].andand.klass
|
||||||
|
r.nil? && :self == rel ? model : r
|
||||||
end
|
end
|
||||||
|
|
||||||
# Eine Spalte in einer Tabelle, relativ zu `Column#model`.
|
# Eine Spalte in einer Tabelle, relativ zu `Column#model`.
|
||||||
|
@ -121,7 +123,7 @@ class SmqlToAR
|
||||||
def initialize model, *col
|
def initialize model, *col
|
||||||
@model = model
|
@model = model
|
||||||
@last_model = nil
|
@last_model = nil
|
||||||
*@path, @col = Array.wrap( col).collect( &it.to_s.split( /[.\/]/)).flatten.collect( &:to_sym)
|
*@path, @col = *Array.wrap( col).collect( &it.to_s.split( /[.\/]/)).flatten.collect( &:to_sym).reject( &it==:self)
|
||||||
end
|
end
|
||||||
|
|
||||||
def last_model
|
def last_model
|
||||||
|
@ -131,10 +133,13 @@ class SmqlToAR
|
||||||
def each
|
def each
|
||||||
model = @model
|
model = @model
|
||||||
@path.each do |rel|
|
@path.each do |rel|
|
||||||
|
rel = rel.to_sym
|
||||||
|
unless :self == rel
|
||||||
model = SmqlToAR.model_of model, rel
|
model = SmqlToAR.model_of model, rel
|
||||||
return false unless model
|
return false unless model
|
||||||
yield rel, model
|
yield rel, model
|
||||||
end
|
end
|
||||||
|
end
|
||||||
model
|
model
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -158,20 +163,21 @@ class SmqlToAR
|
||||||
def joins builder = nil, table = nil, &exe
|
def joins builder = nil, table = nil, &exe
|
||||||
pp = []
|
pp = []
|
||||||
table = Array.wrap table
|
table = Array.wrap table
|
||||||
exe ||= builder ? lambda {|j, m| builder.join table+j, m} : Array.method( :[])
|
exe ||= builder ? lambda {|j, m| builder.joins table+j, m} : Array.method( :[])
|
||||||
collect do |rel, model|
|
collect do |rel, model|
|
||||||
pp.push rel
|
pp.push rel
|
||||||
exe.call pp, model
|
exe.call pp, model
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
def length() @path.length+1 end
|
def self?() !@col end
|
||||||
def size() @path.size+1 end
|
def length() @path.length+(self.self? ? 0 : 1) end
|
||||||
def to_a() @path+[@col] end
|
def size() @path.size+(self.self? ? 0 : 1) end
|
||||||
|
def to_a() @path+(self.self? ? [] : [@col]) end
|
||||||
def to_s() to_a.join '.' end
|
def to_s() to_a.join '.' end
|
||||||
def to_sym() to_s.to_sym end
|
def to_sym() to_s.to_sym end
|
||||||
def to_json() to_s end
|
def to_json() to_s end
|
||||||
def inspect() "#<Column: #{model} #{to_s}>" end
|
def inspect() "#<Column: #{model} #{to_s}>" end
|
||||||
def relation() SmqlToAR.model_of last_model, @col end
|
def relation() self.self? ? model : SmqlToAR.model_of( last_model, @col) end
|
||||||
def allowed?() ! self.protected? end
|
def allowed?() ! self.protected? end
|
||||||
def child?() @path.empty? and !!relation end
|
def child?() @path.empty? and !!relation end
|
||||||
end
|
end
|
||||||
|
@ -225,9 +231,9 @@ class SmqlToAR
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
def build prefix = nil, base_table = nil
|
def build prefix = nil
|
||||||
benchmark 'SMQL build query' do
|
benchmark 'SMQL build query' do
|
||||||
@builder = QueryBuilder.new @model, prefix, base_table
|
@builder = QueryBuilder.new @model, prefix
|
||||||
table = @builder.base_table
|
table = @builder.base_table
|
||||||
@conditions.each &it.build( builder, table)
|
@conditions.each &it.build( builder, table)
|
||||||
end
|
end
|
||||||
|
@ -252,4 +258,12 @@ class SmqlToAR
|
||||||
def self.to_ar *params
|
def self.to_ar *params
|
||||||
new( *params).to_ar
|
new( *params).to_ar
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.reload_library
|
||||||
|
lib_dir = File.dirname __FILE__
|
||||||
|
fj = lambda {|*a| File.join lib_dir, *a }
|
||||||
|
load fj.call( 'smql_to_ar.rb')
|
||||||
|
load fj.call( 'smql_to_ar', 'condition_types.rb')
|
||||||
|
load fj.call( 'smql_to_ar', 'query_builder.rb')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,15 +33,29 @@ class SmqlToAR
|
||||||
k.split( '|').collect &:to_sym
|
k.split( '|').collect &:to_sym
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def conditions &e
|
||||||
|
unless block_given?
|
||||||
|
r = Enumerator.new( self, :conditions)
|
||||||
|
s = self
|
||||||
|
r.define_singleton_method :[] do |k|
|
||||||
|
s.conditions.select {|c| c::Operator === k }
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
constants.each do |c|
|
||||||
|
next if :Condition == c
|
||||||
|
c = const_get c
|
||||||
|
next if Condition === c
|
||||||
|
yield c
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Eine Regel parsen.
|
# Eine Regel parsen.
|
||||||
# Ex: Person, "givenname=", "Peter"
|
# Ex: Person, "givenname=", "Peter"
|
||||||
def try_parse_it model, colop, val
|
def try_parse_it model, colop, val
|
||||||
r = nil
|
r = nil
|
||||||
#p :try_parse => { :model => model, :colop => colop, :value => val }
|
#p :try_parse => { :model => model, :colop => colop, :value => val }
|
||||||
constants.each do |c|
|
conditions.each do |c|
|
||||||
next if :Condition == c
|
|
||||||
c = const_get c
|
|
||||||
next if Condition === c
|
|
||||||
raise_unless colop =~ /^(?:\d*:)?(.*?)(\W*)$/, UnexpectedColOpError.new( model, colop, val)
|
raise_unless colop =~ /^(?:\d*:)?(.*?)(\W*)$/, UnexpectedColOpError.new( model, colop, val)
|
||||||
col, op = $1, $2
|
col, op = $1, $2
|
||||||
col = split_keys( col).collect {|c| Column.new model, c }
|
col = split_keys( col).collect {|c| Column.new model, c }
|
||||||
|
@ -81,13 +95,19 @@ class SmqlToAR
|
||||||
Expected = []
|
Expected = []
|
||||||
Where = nil
|
Where = nil
|
||||||
|
|
||||||
|
class <<self
|
||||||
# Versuche das Objekt zu erkennen. Operator und Expected muessen passen.
|
# Versuche das Objekt zu erkennen. Operator und Expected muessen passen.
|
||||||
# Passt das Object, die Klasse instanzieren.
|
# Passt das Object, die Klasse instanzieren.
|
||||||
def self.try_parse model, cols, op, val
|
def try_parse model, cols, op, val
|
||||||
#p :self => name, :try_parse => op, :cols => cols, :with => self::Operator, :value => val, :expected => self::Expected, :model => model.name
|
#p :self => name, :try_parse => op, :cols => cols, :with => self::Operator, :value => val, :expected => self::Expected, :model => model.name
|
||||||
new model, cols, val if self::Operator === op and self::Expected.any?( &it === val)
|
new model, cols, val if self::Operator === op and self::Expected.any?( &it === val)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#{self.name}(:operator=>#{self::Operator.inspect}, :expected=>#{self::Expected.inspect}, :where=>#{self::Where.inspect})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def initialize model, cols, val
|
def initialize model, cols, val
|
||||||
@model, @cols = model, cols
|
@model, @cols = model, cols
|
||||||
@value = case val
|
@value = case val
|
||||||
|
@ -97,6 +117,10 @@ class SmqlToAR
|
||||||
verify
|
verify
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#<#{self.class.name}:0x#{(self.object_id<<1).to_s 16} model: #{self.class.name}, cols: #{@cols.inspect}, value: #{@value.inspect}>"
|
||||||
|
end
|
||||||
|
|
||||||
def verify
|
def verify
|
||||||
@cols.each do |col|
|
@cols.each do |col|
|
||||||
verify_column col
|
verify_column col
|
||||||
|
@ -118,8 +142,8 @@ class SmqlToAR
|
||||||
end
|
end
|
||||||
|
|
||||||
# Erstelle alle noetigen Klauseln. builder nimmt diese entgegen,
|
# Erstelle alle noetigen Klauseln. builder nimmt diese entgegen,
|
||||||
# wobei builder.join, builder.select, builder.where und builder.wobs von interesse sind.
|
# wobei builder.joins, builder.select, builder.where und builder.wobs von interesse sind.
|
||||||
# mehrere Schluessel bedeuten, dass die Values _alle_ zutreffen muessen, wobei die Schluessel geodert werden.
|
# mehrere Schluessel bedeuten, dass die Values _alle_ zutreffen muessen, wobei die Schluessel geODERt werden.
|
||||||
# Ex:
|
# Ex:
|
||||||
# 1) {"givenname=", "Peter"} #=> givenname = 'Peter'
|
# 1) {"givenname=", "Peter"} #=> givenname = 'Peter'
|
||||||
# 2) {"givenname=", ["Peter", "Hans"]} #=> ( givenname = 'Peter' OR givenname = 'Hans' )
|
# 2) {"givenname=", ["Peter", "Hans"]} #=> ( givenname = 'Peter' OR givenname = 'Hans' )
|
||||||
|
@ -132,11 +156,11 @@ class SmqlToAR
|
||||||
@cols.each do |col|
|
@cols.each do |col|
|
||||||
col.joins builder, table
|
col.joins builder, table
|
||||||
col = builder.column table+col.path, col.col
|
col = builder.column table+col.path, col.col
|
||||||
builder.where *values.keys.collect {|vid| self.class::Where % [ col, vid.to_s ] }
|
builder.where values.keys.collect {|vid| self.class::Where % [ col, vid.to_s ] }
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
values.keys.each do |vid|
|
values.keys.each do |vid|
|
||||||
builder.where *@cols.collect {|col|
|
builder.where @cols.collect {|col|
|
||||||
col.joins builder, table
|
col.joins builder, table
|
||||||
col = builder.column table+col.path, col.col
|
col = builder.column table+col.path, col.col
|
||||||
self.class::Where % [ col, vid.to_s ]
|
self.class::Where % [ col, vid.to_s ]
|
||||||
|
@ -189,19 +213,26 @@ class SmqlToAR
|
||||||
|
|
||||||
In = simple_condition NotIn, '|=', '%s IN (%s)', [Array]
|
In = simple_condition NotIn, '|=', '%s IN (%s)', [Array]
|
||||||
In2 = simple_condition In, '', nil, [Array]
|
In2 = simple_condition In, '', nil, [Array]
|
||||||
NotEqual = simple_condition Condition, /\!=|<>/, "%s <> %s", [Array, String, Numeric]
|
NotEqual = simple_condition Condition, '!=', "%s <> %s", [Array, String, Numeric]
|
||||||
|
NotEqual2 = simple_condition Condition, '<>', "%s <> %s", [Array, String, Numeric]
|
||||||
GreaterThanOrEqual = simple_condition Condition, '>=', "%s >= %s", [Array, Numeric]
|
GreaterThanOrEqual = simple_condition Condition, '>=', "%s >= %s", [Array, Numeric]
|
||||||
LesserThanOrEqual = simple_condition Condition, '<=', "%s <= %s", [Array, Numeric]
|
LesserThanOrEqual = simple_condition Condition, '<=', "%s <= %s", [Array, Numeric]
|
||||||
|
|
||||||
|
# Examples:
|
||||||
|
# { 'articles=>' => { id: 1 } }
|
||||||
|
# { 'articles=>' => [ { id: 1 }, { id: 2 } ] }
|
||||||
class EqualJoin <Condition
|
class EqualJoin <Condition
|
||||||
Operator = '='
|
Operator = '=>'
|
||||||
Expected = [Hash]
|
Expected = [Hash, lambda {|x| x.kind_of?( Array) and x.all?( &it.kind_of?( Hash))}]
|
||||||
|
|
||||||
def initialize *pars
|
def initialize *pars
|
||||||
super( *pars)
|
super( *pars)
|
||||||
|
@value = Array.wrap @value
|
||||||
cols = {}
|
cols = {}
|
||||||
|
p self: self, cols: @cols
|
||||||
@cols.each do |col|
|
@cols.each do |col|
|
||||||
col_model = col.relation
|
col_model = col.relation
|
||||||
cols[col] = [col_model] + ConditionTypes.try_parse( col_model, @value)
|
cols[col] = [col_model] + @value.collect {|val| ConditionTypes.try_parse( col_model, val) }
|
||||||
end
|
end
|
||||||
@cols = cols
|
@cols = cols
|
||||||
end
|
end
|
||||||
|
@ -212,11 +243,14 @@ class SmqlToAR
|
||||||
|
|
||||||
def build builder, table
|
def build builder, table
|
||||||
@cols.each do |col, sub|
|
@cols.each do |col, sub|
|
||||||
|
model, *sub = sub
|
||||||
t = table + col.path + [col.col]
|
t = table + col.path + [col.col]
|
||||||
col.joins.each {|j, m| builder.join table+j, m }
|
col.joins.each {|j, m| builder.joins table+j, m }
|
||||||
builder.join t, sub[0]
|
builder.joins t, model
|
||||||
sub[1..-1].each &it.build( builder, t)
|
b2 = 1 == sub.length ? builder : Or.new( builder)
|
||||||
|
sub.each {|i| i.collect( &it.build( And.new( b2), t)); p 'or' => b2 }
|
||||||
end
|
end
|
||||||
|
ap '=>' => builder
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -250,7 +284,7 @@ class SmqlToAR
|
||||||
def build builder, table
|
def build builder, table
|
||||||
@cols.each do |col, sub|
|
@cols.each do |col, sub|
|
||||||
t = table+col.to_a
|
t = table+col.to_a
|
||||||
builder.sub_join t, col, *sub[0..1]
|
builder.sub_joins t, col, *sub[0..1]
|
||||||
sub[2..-1].each &it.build( builder, t)
|
sub[2..-1].each &it.build( builder, t)
|
||||||
end
|
end
|
||||||
self
|
self
|
||||||
|
@ -263,8 +297,9 @@ class SmqlToAR
|
||||||
LesserThan = simple_condition Condition, '<', "%s < %s", [Array, Numeric]
|
LesserThan = simple_condition Condition, '<', "%s < %s", [Array, Numeric]
|
||||||
NotIlike = simple_condition Condition, '!~', "%s NOT ILIKE %s", [Array, String]
|
NotIlike = simple_condition Condition, '!~', "%s NOT ILIKE %s", [Array, String]
|
||||||
Ilike = simple_condition Condition, '~', "%s ILIKE %s", [Array, String]
|
Ilike = simple_condition Condition, '~', "%s ILIKE %s", [Array, String]
|
||||||
|
Exists = simple_condition Condition, '', '%s IS NOT NULL', [true]
|
||||||
|
NotExists = simple_condition Condition, '', '%s IS NULL', [false]
|
||||||
|
|
||||||
####### No Operator #######
|
|
||||||
Join = simple_condition EqualJoin, '', nil, [Hash]
|
Join = simple_condition EqualJoin, '', nil, [Hash]
|
||||||
InRange2 = simple_condition InRange, '', nil, [Range]
|
InRange2 = simple_condition InRange, '', nil, [Range]
|
||||||
class Select < Condition
|
class Select < Condition
|
||||||
|
@ -299,11 +334,17 @@ class SmqlToAR
|
||||||
Expected = []
|
Expected = []
|
||||||
attr_reader :model, :func, :args
|
attr_reader :model, :func, :args
|
||||||
|
|
||||||
def self.try_parse model, func, args
|
class <<self
|
||||||
|
def try_parse model, func, args
|
||||||
SmqlToAR.logger.info( { try_parse: [func,args]}.inspect)
|
SmqlToAR.logger.info( { try_parse: [func,args]}.inspect)
|
||||||
self.new model, func, args if self::Name === func and self::Expected.any?( &it === args)
|
self.new model, func, args if self::Name === func and self::Expected.any?( &it === args)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#{self.name}(:name=>#{self::Name}, :expected=>#{self::Expected})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def initialize model, func, args
|
def initialize model, func, args
|
||||||
@model, @func, @args = model, func, args
|
@model, @func, @args = model, func, args
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,30 +29,34 @@ class SmqlToAR
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :table_alias, :model, :table_model, :base_table, :_where, :_select, :_wobs, :_joins, :prefix, :_vid
|
attr_reader :table_alias, :model, :table_model, :base_table, :_where, :_select, :_wobs, :_joins, :prefix, :_vid
|
||||||
attr_accessor :logger
|
attr_accessor :logger, :limit, :offset
|
||||||
|
|
||||||
def initialize model, prefix = nil, base_table
|
def initialize model, prefix = nil
|
||||||
@prefix = "smql"
|
@prefix = "smql"
|
||||||
@logger = SmqlToAR.logger
|
@logger = SmqlToAR.logger
|
||||||
@table_alias = Hash.new do |h, k|
|
@table_alias = Hash.new do |h, k|
|
||||||
k = Array.wrap k
|
j = Array.wrap( k).compact
|
||||||
h[k] = "#{@prefix},#{k.join(',')}"
|
h[k] = h.key?(j) ? h[j] : "#{@prefix},#{j.join(',')}"
|
||||||
end
|
end
|
||||||
@_vid, @_where, @_wobs, @model, @quoter = 0, [], {}, model, model.connection
|
@_vid, @_where, @_wobs, @model, @quoter = 0, SmqlToAR::And[], {}, model, model.connection
|
||||||
@base_table = base_table.blank? ? [model.table_name.to_sym] : Array.wrap( base_table)
|
@base_table = [model.table_name.to_sym]
|
||||||
@table_alias[ @base_table] = @base_table.first
|
@table_alias[ @base_table] = @base_table.first
|
||||||
t = quote_table_name @table_alias[ @base_table]
|
t = quote_table_name @table_alias[ @base_table]
|
||||||
@_select, @_joins, @_joined, @_includes, @_order = ["DISTINCT #{t}.*"], "", [], [], []
|
@_select, @_joins, @_joined, @_includes, @_order = ["DISTINCT #{t}.*"], "", [@base_table], [], []
|
||||||
@table_model = {@base_table => @model}
|
@table_model = {@base_table => @model}
|
||||||
end
|
end
|
||||||
|
|
||||||
def vid() Vid.new( @_vid+=1) end
|
def vid() Vid.new( @_vid+=1) end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#<#{self.class.name}:#{"0x%x"% (self.object_id<<1)}|#{@prefix}:#{@base_table}:#{@model} vid=#{@_vid} where=#{@_where} wobs=#{@_wobs} select=#{@_select} aliases=#{@_table_alias}>"
|
||||||
|
end
|
||||||
|
|
||||||
# Jede via where uebergebene Condition wird geodert und alle zusammen werden geundet.
|
# Jede via where uebergebene Condition wird geodert und alle zusammen werden geundet.
|
||||||
# "Konjunktive Normalform". Allerdings duerfen Conditions auch Komplexe Abfragen enthalten.
|
# "Konjunktive Normalform". Allerdings duerfen Conditions auch Komplexe Abfragen enthalten.
|
||||||
# Ex: builder.where( 'a = a', 'b = c').where( 'c = d', 'e = e').where( 'x = y').where( '( m = n AND o = p )', 'f = g')
|
# Ex: builder.where( ['a = a', 'b = c']).where( ['c = d', 'e = e']).where( 'x = y').where( ['( m = n AND o = p )', 'f = g'])
|
||||||
# #=> WHERE ( a = a OR b = c ) AND ( c = d OR e = e ) AND x = y ( ( m = n AND o = p ) OR f = g )
|
# #=> WHERE ( a = a OR b = c ) AND ( c = d OR e = e ) AND x = y ( ( m = n AND o = p ) OR f = g )
|
||||||
def where *cond
|
def where cond
|
||||||
@_where.push cond
|
@_where.push cond
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
@ -75,12 +79,12 @@ class SmqlToAR
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_join orig, pretable, table, prekey, key
|
def build_join orig, pretable, table, prekey, key
|
||||||
" JOIN #{orig} AS #{quote_table_name table} ON #{column pretable, prekey} = #{column table, key} "
|
" LEFT OUTER JOIN #{orig} AS #{quote_table_name table} ON #{column pretable, prekey} = #{column table, key} "
|
||||||
end
|
end
|
||||||
|
|
||||||
def sub_join table, col, model, query
|
def sub_joins table, col, model, query
|
||||||
prefix, base_table = "#{@prefix}_sub", col.col
|
prefix, base_table = "#{@prefix}_sub", col.relation.table_name
|
||||||
join_ table, model, "(#{query.build( prefix, base_table).ar.to_sql})"
|
join_ table, model, "(#{query.build( prefix).ar.to_sql})"
|
||||||
end
|
end
|
||||||
|
|
||||||
def join_ table, model, query, pretable = nil
|
def join_ table, model, query, pretable = nil
|
||||||
|
@ -116,7 +120,8 @@ class SmqlToAR
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
def join table, model
|
def joins table, model
|
||||||
|
table = table.flatten.compact
|
||||||
return self if @_joined.include? table # Already joined
|
return self if @_joined.include? table # Already joined
|
||||||
join_ table, model, quote_table_name( model.table_name)
|
join_ table, model, quote_table_name( model.table_name)
|
||||||
@_joined.push table
|
@_joined.push table
|
||||||
|
@ -136,40 +141,22 @@ class SmqlToAR
|
||||||
@_order.push "#{column table, col} #{:DESC == o ? :DESC : :ASC}"
|
@_order.push "#{column table, col} #{:DESC == o ? :DESC : :ASC}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def limit count
|
|
||||||
@_limit = count
|
|
||||||
end
|
|
||||||
|
|
||||||
def offset count
|
|
||||||
@_offset = count
|
|
||||||
end
|
|
||||||
|
|
||||||
class Dummy
|
|
||||||
def method_missing m, *a, &e
|
|
||||||
#p :dummy => m, :pars => a, :block => e
|
|
||||||
self
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def build_ar
|
def build_ar
|
||||||
where_str = @_where.collect do |w|
|
where_str = @_where.type_correction!.optimize!.tap {|x| p x }.build_where
|
||||||
w = Array.wrap w
|
|
||||||
1 == w.length ? w.first : "( #{w.join( ' OR ')} )"
|
|
||||||
end.join ' AND '
|
|
||||||
incls = {}
|
incls = {}
|
||||||
@_includes.each do |inc|
|
@_includes.each do |inc|
|
||||||
b = incls
|
b = incls
|
||||||
inc[1..-1].collect {|rel| b = b[rel] ||= {} }
|
inc[1..-1].collect {|rel| b = b[rel] ||= {} }
|
||||||
end
|
end
|
||||||
@logger.debug incls: incls, joins: @_joins
|
@logger.info where: where_str, wobs: @_wobs
|
||||||
@model = @model.
|
@model = @model.
|
||||||
select( @_select.join( ', ')).
|
select( @_select.join( ', ')).
|
||||||
joins( @_joins).
|
joins( @_joins).
|
||||||
where( where_str, @_wobs).
|
where( where_str, @_wobs).
|
||||||
order( @_order.join( ', ')).
|
order( @_order.join( ', ')).
|
||||||
includes( incls)
|
includes( incls)
|
||||||
@model = @model.limit @_limit if @_limit
|
@model = @model.limit @limit if @limit
|
||||||
@model = @model.offset @_offset if @_offset
|
@model = @model.offset @offset if @offset
|
||||||
@model
|
@model
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -189,4 +176,73 @@ class SmqlToAR
|
||||||
@model
|
@model
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class SubBuilder < Array
|
||||||
|
attr_reader :parent, :_where
|
||||||
|
delegate :wobs, :joins, :includes, :sub_joins, :vid, :quote_column_name, :quoter, :quote_table_name, :column, :to => :parent
|
||||||
|
|
||||||
|
def initialize parent, tmp = false
|
||||||
|
p init: self, parent: parent
|
||||||
|
@parent = parent
|
||||||
|
@parent.where self unless @parend.nil? && tmp
|
||||||
|
end
|
||||||
|
|
||||||
|
def new parent, tmp = false
|
||||||
|
super parent, tmp
|
||||||
|
#return parent if self.class == parent.class
|
||||||
|
#super parent
|
||||||
|
end
|
||||||
|
|
||||||
|
alias where push
|
||||||
|
|
||||||
|
def type_correction!
|
||||||
|
collect! do |sub|
|
||||||
|
if sub.kind_of? Array
|
||||||
|
sub = default[ *sub] unless sub.respond_to?( :type_correction!)
|
||||||
|
sub.type_correction!
|
||||||
|
end
|
||||||
|
sub
|
||||||
|
end
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def optimize!
|
||||||
|
p optimize: self
|
||||||
|
ext = []
|
||||||
|
collect! do |sub|
|
||||||
|
sub = sub.optimize! if sub.kind_of? Array
|
||||||
|
if self.class == sub.class
|
||||||
|
ext.push *sub
|
||||||
|
nil
|
||||||
|
elsif sub.blank?
|
||||||
|
nil
|
||||||
|
else
|
||||||
|
sub
|
||||||
|
end
|
||||||
|
end.compact!
|
||||||
|
p optimized: self
|
||||||
|
p ext: ext
|
||||||
|
push *ext
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#{self.class.name.sub( /.*::/, '')}[ #{collect(&:inspect).join ', '}]"
|
||||||
|
end
|
||||||
|
def default() SmqlToAR::And end
|
||||||
|
def default_new( parent) default.new self, parent, false end
|
||||||
|
end
|
||||||
|
|
||||||
|
class And < SubBuilder
|
||||||
|
def default; SmqlToAR::Or; end
|
||||||
|
def build_where
|
||||||
|
join ' AND '
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Or < SubBuilder
|
||||||
|
def build_where
|
||||||
|
join ' OR '
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Reference in a new issue