Compare commits
19 commits
Author | SHA1 | Date | |
---|---|---|---|
a4ce8a1e6b | |||
7f43e9fb86 | |||
3d922c5e47 | |||
7229fd185c | |||
08d1610987 | |||
ce81d21bd8 | |||
fbf5a7019c | |||
27639e24b7 | |||
40deac93bf | |||
6f8569f80a | |||
a4a1d1b9e9 | |||
f6b9579073 | |||
3ba285548c | |||
3d08496ec4 | |||
db1eec6fa7 | |||
edcc68a4ed | |||
2b3922e158 | |||
493556fda2 | |||
1c97319d9c |
1
Rakefile
1
Rakefile
|
@ -15,7 +15,6 @@ begin
|
|||
gem.add_dependency 'activerecord'
|
||||
gem.add_dependency 'activesupport'
|
||||
gem.add_dependency 'json'
|
||||
gem.add_dependency 'methodphitamine'
|
||||
end
|
||||
Jeweler::GemcutterTasks.new
|
||||
rescue LoadError
|
||||
|
|
|
@ -43,9 +43,9 @@ class SmqlToAR
|
|||
# Die eigentliche Exception wird in @data[:exception] hinterlegt.
|
||||
class SubSMQLError < SMQLError
|
||||
def initialize query, model, exception
|
||||
ex = {:class => exception.class, :message => exception.message}
|
||||
ex = {class: exception.class, message: exception.message}
|
||||
ex[:data] = exception.data if exception.respond_to? :data
|
||||
super :query => query, :model => model.to_s, :exception => ex
|
||||
super query: query, model: model.to_s, exception: ex
|
||||
set_backtrace exception.backtrace
|
||||
end
|
||||
end
|
||||
|
@ -55,49 +55,49 @@ class SmqlToAR
|
|||
# Malformed ColOp
|
||||
class UnexpectedColOpError < ParseError
|
||||
def initialize model, colop, val
|
||||
super :got => colop, :val => val, :model => model.to_s
|
||||
super got: colop, val: val, model: model.to_s
|
||||
end
|
||||
end
|
||||
|
||||
class UnexpectedError < ParseError
|
||||
def initialize model, colop, val
|
||||
super :got => {colop => val}, :model => model.to_s
|
||||
super got: {colop => val}, model: model.to_s
|
||||
end
|
||||
end
|
||||
|
||||
class NonExistingSelectableError < SMQLError
|
||||
def initialize got
|
||||
super :got => got
|
||||
super got: got
|
||||
end
|
||||
end
|
||||
|
||||
class NonExistingColumnError < SMQLError
|
||||
def initialize expected, got
|
||||
super :expected => expected, :got => got
|
||||
super expected: expected, got: got
|
||||
end
|
||||
end
|
||||
|
||||
class NonExistingRelationError < SMQLError
|
||||
def initialize expected, got
|
||||
super :expected => expected, :got => got
|
||||
super expected: expected, got: got
|
||||
end
|
||||
end
|
||||
|
||||
class ProtectedColumnError < SMQLError
|
||||
def initialize protected_column
|
||||
super :protected_column => protected_column
|
||||
super protected_column: protected_column
|
||||
end
|
||||
end
|
||||
|
||||
class RootOnlyFunctionError < SMQLError
|
||||
def initialize path
|
||||
super :path => path
|
||||
super path: path
|
||||
end
|
||||
end
|
||||
|
||||
class ConColumnError < SMQLError
|
||||
def initialize expected, got
|
||||
super :expected => expected, :got => got
|
||||
super expected: expected, got: got
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -120,6 +120,7 @@ class SmqlToAR
|
|||
attr_accessor :model
|
||||
|
||||
class Col
|
||||
include Comparable
|
||||
attr_accessor :col, :as
|
||||
def initialize col, as = nil
|
||||
if col.kind_of? Col
|
||||
|
@ -139,12 +140,11 @@ class SmqlToAR
|
|||
|
||||
def == other
|
||||
other = Col.new other
|
||||
@col = other.col && @as == other.col
|
||||
@col == other.col && @as == other.as
|
||||
end
|
||||
|
||||
def === other
|
||||
other = Col.new other
|
||||
@col == other.col
|
||||
@col == Col.new( other).col
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -152,10 +152,10 @@ class SmqlToAR
|
|||
@model = model
|
||||
@last_model = nil
|
||||
*@path, @col = *Array.wrap( col).
|
||||
collect( &it.to_s.split( /[.\/]/)).
|
||||
collect {|x| x.to_s.split /[.\/]/ }.
|
||||
flatten.
|
||||
collect( &Col.method( :new)).
|
||||
reject( &it===:self)
|
||||
reject {|x| x === :self }
|
||||
end
|
||||
|
||||
def last_model
|
||||
|
@ -222,10 +222,8 @@ class SmqlToAR
|
|||
SmqlToAR.logger = ::Rails.logger
|
||||
end
|
||||
end
|
||||
else
|
||||
require 'logger'
|
||||
@@logger = Logger.new $stdout
|
||||
end
|
||||
@@logger = ::Rails.logger || begin require 'logger'; Logger.new( $stdout) end
|
||||
|
||||
class <<self
|
||||
def logger=(logger) @@logger = logger end
|
||||
|
@ -245,8 +243,8 @@ class SmqlToAR
|
|||
refls = model.respond_to?( :reflections) && model.reflections
|
||||
refls && refls.each do |name, refl|
|
||||
r[model.name][name] = case refl
|
||||
when ActiveRecord::Reflection::ThroughReflection then {:macro => refl.macro, :model => refl.klass.name, :through => refl.through_reflection.name}
|
||||
when ActiveRecord::Reflection::AssociationReflection then {:macro => refl.macro, :model => refl.klass.name}
|
||||
when ActiveRecord::Reflection::ThroughReflection then {macro: refl.macro, model: refl.klass.name, through: refl.through_reflection.name}
|
||||
when ActiveRecord::Reflection::AssociationReflection then {macro: refl.macro, model: refl.klass.name}
|
||||
else raise "Ups: #{refl.class}"
|
||||
end
|
||||
models.push refl.klass unless r.keys.include? refl.klass.name
|
||||
|
@ -294,8 +292,27 @@ class SmqlToAR
|
|||
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')
|
||||
Object.send :remove_const, :SmqlToAR
|
||||
load fj[ 'smql_to_ar.rb']
|
||||
load fj[ 'smql_to_ar', 'condition_types.rb']
|
||||
load fj[ 'smql_to_ar', 'query_builder.rb']
|
||||
end
|
||||
|
||||
module ActiveRecordExtensions
|
||||
def self.included base
|
||||
base.extend ClassMethods
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def smql *params
|
||||
SmqlToAR.to_ar self, *params
|
||||
end
|
||||
|
||||
def smql_protected
|
||||
[]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ActiveRecord::Base.send :include, SmqlToAR::ActiveRecordExtensions
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# TODO:
|
||||
# * Array als Typ ist zu allgemein. Allgemeine Lösung gesucht, um die Typen im Array angeben zu können.
|
||||
|
||||
class SmqlToAR
|
||||
#############################################################################
|
||||
# Alle Subklassen (qualitativ: ConditionTypes::*), die als Superklasse Condition haben,
|
||||
|
@ -100,7 +103,7 @@ class SmqlToAR
|
|||
# Passt das Object, die Klasse instanzieren.
|
||||
def try_parse model, cols, op, val
|
||||
#p :class => self, :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? {|x| x === val}
|
||||
end
|
||||
|
||||
def inspect
|
||||
|
@ -150,7 +153,7 @@ class SmqlToAR
|
|||
# 2) {"givenname=", ["Peter", "Hans"]} #=> ( givenname = 'Peter' OR givenname = 'Hans' )
|
||||
# 3) {"givenname|surname=", ["Peter", "Mueller"]}
|
||||
# #=> ( givenname = 'Peter' OR surname = 'Peter' ) AND ( givenname = 'Mueller' OR surname = 'Mueller' )
|
||||
def build builder, table
|
||||
def condition_build builder, table
|
||||
values = Hash[ @value.collect {|value| [ builder.vid, value ] } ]
|
||||
values.each {|k, v| builder.wobs k.to_sym => v }
|
||||
if 1 == @cols.length
|
||||
|
@ -171,15 +174,16 @@ class SmqlToAR
|
|||
end
|
||||
self
|
||||
end
|
||||
alias build condition_build
|
||||
end
|
||||
|
||||
class NotInRange < Condition
|
||||
Operator = '!..'
|
||||
Where = "%s NOT BETWEEN %s AND %s"
|
||||
Where = '%s NOT BETWEEN %s AND %s'
|
||||
Expected = [Range, lambda {|val| Array === val && 2 == val.length } ]
|
||||
|
||||
def initialize model, cols, val
|
||||
if Array === val && 2 == val.length
|
||||
if Array === val
|
||||
f, l = val
|
||||
f, l = Time.parse(f), Time.parse(l) if f.kind_of? String
|
||||
val = f..l
|
||||
|
@ -187,7 +191,7 @@ class SmqlToAR
|
|||
super model, cols, val
|
||||
end
|
||||
|
||||
def build builder, table
|
||||
def not_in_range_build builder, table
|
||||
builder.wobs (v1 = builder.vid).to_sym => @value.begin, (v2 = builder.vid).to_sym => @value.end
|
||||
@cols.each do |col|
|
||||
col.joins builder, table
|
||||
|
@ -195,15 +199,57 @@ class SmqlToAR
|
|||
end
|
||||
self
|
||||
end
|
||||
alias build not_in_range_build
|
||||
end
|
||||
InRange = simple_condition NotInRange, '..', "%s BETWEEN %s AND %s"
|
||||
InRange = simple_condition NotInRange, '..', '%s BETWEEN %s AND %s'
|
||||
|
||||
# Every key-pair will be ORed. No multiple values possible.
|
||||
class Overlaps < Condition
|
||||
Operator, Where = '<=>', '(%s, %s) OVERLAPS (%s, %s)'
|
||||
Expected = [Range, lambda {|val|
|
||||
Array === val && 2 == val.length &&
|
||||
[Time, Date, String].any? {|v|v===val[0]} &&
|
||||
[Numeric, String].any? {|v|v===val[1]}
|
||||
}]
|
||||
|
||||
def initialize model, cols, val
|
||||
if Array === val
|
||||
f = Time.parse( val[0]).localtime
|
||||
l = val[1]
|
||||
l = case l
|
||||
when String then Time.parse( l).localtime
|
||||
when Numeric then f+l
|
||||
else raise ArgumentError, "Unexpected type for end-value #{l.inspect}"
|
||||
end
|
||||
f += f.utc_offset
|
||||
l += l.utc_offset
|
||||
val = f.utc..l.utc
|
||||
end
|
||||
super model, cols, val
|
||||
end
|
||||
|
||||
def overlaps_build builder, table
|
||||
builder = Or.new builder
|
||||
builder.wobs (v1 = builder.vid).to_sym => @value.begin, (v2 = builder.vid).to_sym => @value.end
|
||||
v1 = "TIMESTAMP #{v1}"
|
||||
v2 = "TIMESTAMP #{v2}"
|
||||
@cols.each do |col|
|
||||
col.joins builder, table
|
||||
end.each_slice 2 do |f,s|
|
||||
builder.where self.class::Where % [
|
||||
builder.column( table+f.path, f.col), builder.column( table+s.path, s.col), v1, v2]
|
||||
end
|
||||
end
|
||||
alias build overlaps_build
|
||||
end
|
||||
NotOverlaps = simple_condition Overlaps, '<=>', 'NOT (%s, %s) OVERLAPS (%s, %s)'
|
||||
|
||||
class NotIn < Condition
|
||||
Operator = '!|='
|
||||
Where = "%s NOT IN (%s)"
|
||||
Expected = [Array]
|
||||
|
||||
def build builder, table
|
||||
def not_in_build builder, table
|
||||
builder.wobs (v = builder.vid).to_sym => @value
|
||||
@cols.each do |col|
|
||||
col.joins builder, table
|
||||
|
@ -211,6 +257,7 @@ class SmqlToAR
|
|||
end
|
||||
self
|
||||
end
|
||||
alias build not_in_build
|
||||
end
|
||||
|
||||
In = simple_condition NotIn, '|=', '%s IN (%s)', [Array]
|
||||
|
@ -219,13 +266,20 @@ class SmqlToAR
|
|||
NotEqual2 = simple_condition Condition, '<>', "%s <> %s", [Array, String, Numeric]
|
||||
GreaterThanOrEqual = simple_condition Condition, '>=', "%s >= %s", [Array, Numeric]
|
||||
LesserThanOrEqual = simple_condition Condition, '<=', "%s <= %s", [Array, Numeric]
|
||||
class StringTimeGreaterThanOrEqual < Condition
|
||||
Operator, Where, Expected = '>=', '%s >= %s', [Time, Date, String]
|
||||
def initialize model, cols, val
|
||||
super model, cols, Time.parse( val.to_s)
|
||||
end
|
||||
end
|
||||
StringTimeLesserThanOrEqual = simple_condition StringTimeGreaterThanOrEqual, '<=', "%s <= %s"
|
||||
|
||||
# Examples:
|
||||
# { 'articles=>' => { id: 1 } }
|
||||
# { 'articles=>' => [ { id: 1 }, { id: 2 } ] }
|
||||
class EqualJoin <Condition
|
||||
Operator = '=>'
|
||||
Expected = [Hash, lambda {|x| x.kind_of?( Array) and x.all?( &it.kind_of?( Hash))}]
|
||||
Expected = [Hash, lambda {|x| x.kind_of?( Array) and x.all? {|y| y.kind_of?( Hash) }}]
|
||||
|
||||
def initialize *pars
|
||||
super( *pars)
|
||||
|
@ -242,7 +296,7 @@ class SmqlToAR
|
|||
raise_unless col.relation, NonExistingRelationError.new( %w[Relation], col)
|
||||
end
|
||||
|
||||
def build builder, table
|
||||
def equal_join_build builder, table
|
||||
if 2 < @cols.first.second.length
|
||||
b2, b3 = And, Or
|
||||
else
|
||||
|
@ -252,13 +306,17 @@ class SmqlToAR
|
|||
@cols.each do |col, sub|
|
||||
model, *sub = sub
|
||||
t = table + col.path + [col.col]
|
||||
col.joins.each {|j, m| builder.joins table+j, m }
|
||||
col.joins builder, table
|
||||
builder.joins t, model
|
||||
b4 = b3.new( b2)
|
||||
sub.each {|i| i.collect( &it.build( And.new( b4), t)) }
|
||||
sub.each do |i|
|
||||
b5 = And.new b4
|
||||
i.collect {|j| j.build b5, t }
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
alias build equal_join_build
|
||||
end
|
||||
|
||||
# Takes to Queries.
|
||||
|
@ -288,22 +346,25 @@ class SmqlToAR
|
|||
raise_unless col.child?, ConColumnError.new( [:Column], col)
|
||||
end
|
||||
|
||||
def build builder, table
|
||||
def sub_equal_join_build builder, table
|
||||
@cols.each do |col, sub|
|
||||
t = table+col.to_a
|
||||
builder.sub_joins t, col, *sub[0..1]
|
||||
#ap sub: sub[2..-1]
|
||||
sub[2..-1].each &it.build( builder, t)
|
||||
sub[2..-1].each {|x| x.build builder, t }
|
||||
end
|
||||
self
|
||||
end
|
||||
alias build sub_equal_join_build
|
||||
end
|
||||
=end
|
||||
|
||||
Equal = simple_condition Condition, '=', "%s = %s", [Array, String, Numeric]
|
||||
Equal2 = simple_condition Equal, '', "%s = %s", [String, Numeric]
|
||||
Equal = simple_condition Condition, '=', "%s = %s", [Array, String, Numeric, Date, Time]
|
||||
Equal2 = simple_condition Equal, '', "%s = %s", [String, Numeric, Date, Time]
|
||||
GreaterThan = simple_condition Condition, '>', "%s > %s", [Array, Numeric]
|
||||
StringTimeGreaterThan = simple_condition StringTimeGreaterThanOrEqual, '>', "%s > %s"
|
||||
LesserThan = simple_condition Condition, '<', "%s < %s", [Array, Numeric]
|
||||
StringTimeLesserThan = simple_condition StringTimeGreaterThanOrEqual, '<', "%s < %s"
|
||||
NotIlike = simple_condition Condition, '!~', "%s NOT ILIKE %s", [Array, String]
|
||||
Ilike = simple_condition Condition, '~', "%s ILIKE %s", [Array, String]
|
||||
Exists = simple_condition Condition, '', '%s IS NOT NULL', [TrueClass]
|
||||
|
@ -319,7 +380,7 @@ class SmqlToAR
|
|||
raise_unless col.exist_in? || SmqlToAR.model_of( col.last_model, col.col), NonExistingSelectableError.new( col)
|
||||
end
|
||||
|
||||
def build builder, table
|
||||
def select_build builder, table
|
||||
@cols.each do |col|
|
||||
if col.exist_in?
|
||||
col.joins builder, table
|
||||
|
@ -331,6 +392,7 @@ class SmqlToAR
|
|||
end
|
||||
self
|
||||
end
|
||||
alias build select_build
|
||||
end
|
||||
|
||||
class Functions < Condition
|
||||
|
@ -345,7 +407,7 @@ class SmqlToAR
|
|||
|
||||
class <<self
|
||||
def try_parse model, func, args
|
||||
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? {|x| x === args }
|
||||
end
|
||||
|
||||
def inspect
|
||||
|
@ -379,7 +441,7 @@ class SmqlToAR
|
|||
super model, func, args
|
||||
end
|
||||
|
||||
def build builder, table
|
||||
def order_build builder, table
|
||||
return if @args.blank?
|
||||
@args.each do |o|
|
||||
col, o = o
|
||||
|
@ -389,26 +451,29 @@ class SmqlToAR
|
|||
builder.order t, col.col, o
|
||||
end
|
||||
end
|
||||
alias build order_build
|
||||
end
|
||||
|
||||
class Limit < Function
|
||||
Name = :limit
|
||||
Expected = [Fixnum]
|
||||
|
||||
def build builder, table
|
||||
def limit_build builder, table
|
||||
raise_unless 1 == table.length, RootOnlyFunctionError.new( table)
|
||||
builder.limit = Array.wrap(@args).first.to_i
|
||||
end
|
||||
alias build limit_build
|
||||
end
|
||||
|
||||
class Offset < Function
|
||||
Name = :offset
|
||||
Expected = [Fixnum]
|
||||
|
||||
def build builder, table
|
||||
def offset_build builder, table
|
||||
raise_unless 1 == table.length, RootOnlyFunctionError.new( table)
|
||||
builder.offset = Array.wrap(@args).first.to_i
|
||||
end
|
||||
alias build offset_build
|
||||
end
|
||||
|
||||
def self.new model, col, val
|
||||
|
|
|
@ -32,13 +32,36 @@ class SmqlToAR
|
|||
end
|
||||
|
||||
class Aliases < Hash
|
||||
def self.new prefix, *a, &e
|
||||
e ||= lambda do |h, k|
|
||||
j = Array.wrap( k).compact
|
||||
h[k] = h.key?(j) ? h[j] : "#{prefix},#{j.collect( &:to_alias).join( ',')}"
|
||||
end
|
||||
attr_accessor :counter, :prefix
|
||||
|
||||
def initialize prefix, *a, &e
|
||||
@counter, @prefix = 0, prefix || 'smql'
|
||||
super *a, &e
|
||||
end
|
||||
|
||||
def format name
|
||||
pre, suf = name.split( ',', 2)
|
||||
return name unless suf
|
||||
pre += ",#{@counter += 1},"
|
||||
l = 60-pre.length
|
||||
n = suf[(suf.length<=l ? 0 : -l)..-1]
|
||||
n == suf ? pre+n : "#{pre},,,#{n}"
|
||||
end
|
||||
|
||||
def name n
|
||||
n.collect( &:to_alias).join ','
|
||||
end
|
||||
|
||||
def [] k
|
||||
n = name k
|
||||
v = super n
|
||||
v = self[k] = format( "#{prefix},#{n}") unless v
|
||||
v
|
||||
end
|
||||
|
||||
def []= k, v
|
||||
super name( k), v
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :table_alias, :model, :table_model, :base_table, :_where, :_select, :_wobs, :_joins, :prefix, :_vid
|
||||
|
@ -48,7 +71,7 @@ class SmqlToAR
|
|||
@prefix = "smql"
|
||||
@logger = SmqlToAR.logger
|
||||
@table_alias = Aliases.new @prefix
|
||||
@_vid, @_where, @_wobs, @model, @quoter = 0, SmqlToAR::And[], {}, model, model.connection
|
||||
@_vid, @_where, @_wobs, @model, @quote = 0, SmqlToAR::And[], {}, model, model.connection
|
||||
@base_table = [Column::Col.new( model.table_name)]
|
||||
@table_alias[ @base_table] = @base_table.first
|
||||
t = quote_table_name @base_table.first.col
|
||||
|
@ -77,7 +100,7 @@ class SmqlToAR
|
|||
end
|
||||
|
||||
def quote_column_name name
|
||||
@quoter.quote_column_name( name).gsub /"\."/, ','
|
||||
@quote.quote_column_name( name).gsub /"\."/, ','
|
||||
end
|
||||
|
||||
def quote_table_name name
|
||||
|
@ -85,7 +108,7 @@ class SmqlToAR
|
|||
when Array, Column::Col then @table_alias[Array.wrap name]
|
||||
else name.to_s
|
||||
end
|
||||
@quoter.quote_table_name( name).gsub /"\."/, ','
|
||||
@quote.quote_table_name name
|
||||
end
|
||||
|
||||
def column table, name
|
||||
|
@ -93,7 +116,7 @@ class SmqlToAR
|
|||
end
|
||||
|
||||
def build_join orig, pretable, table, prekey, key
|
||||
" LEFT JOIN #{orig} AS #{quote_table_name table} ON #{column pretable, prekey} = #{column table, key} "
|
||||
"\tLEFT JOIN #{orig} AS #{quote_table_name table}\n\tON #{column pretable, prekey} = #{column table, key}\n"
|
||||
end
|
||||
|
||||
def sub_joins table, col, model, query
|
||||
|
@ -104,32 +127,33 @@ class SmqlToAR
|
|||
def join_ table, model, query, pretable = nil
|
||||
pretable ||= table[0...-1]
|
||||
@table_model[ table] = model
|
||||
premodel = @table_model[ pretable]
|
||||
@table_model.rehash
|
||||
premodel = @table_model.find {|k,v| pretable == k }[1]
|
||||
t = @table_alias[ table]
|
||||
pt = quote_table_name table[ 0...-1]
|
||||
refl = premodel.reflections[table.last.to_sym]
|
||||
case refl
|
||||
when ActiveRecord::Reflection::ThroughReflection
|
||||
through = refl.through_reflection
|
||||
throughtable = table[0...-1]+[Column::Col.new( through.name, table.last.as)]
|
||||
srctable = throughtable+[Column::Col.new( refl.source_reflection.name, table.last.as)]
|
||||
through_table = table[0...-1]+[Column::Col.new( through.name, table.last.as)]
|
||||
srctable = through_table+[Column::Col.new( refl.source_reflection.name, table.last.as)]
|
||||
@table_model[ srctable] = model
|
||||
@table_alias[ table] = @table_alias[ srctable]
|
||||
join_ throughtable, through.klass, quote_table_name( through.table_name)
|
||||
join_ srctable, refl.klass, query, throughtable
|
||||
join_ through_table, through.klass, quote_table_name( through.table_name)
|
||||
join_ srctable, refl.klass, query, through_table
|
||||
when ActiveRecord::Reflection::AssociationReflection
|
||||
case refl.macro
|
||||
when :has_many, :has_one
|
||||
@_joins += build_join query, pretable, t, premodel.primary_key, refl.primary_key_name
|
||||
@_joins += build_join query, pretable, t, premodel.primary_key, refl.foreign_key
|
||||
when :belongs_to
|
||||
@_joins += build_join query, pretable, t, refl.primary_key_name, premodel.primary_key
|
||||
@_joins += build_join query, pretable, t, refl.foreign_key, premodel.primary_key
|
||||
when :has_and_belongs_to_many
|
||||
jointable = [','] + table
|
||||
@_joins += build_join refl.options[:join_table], pretable, @table_alias[jointable], premodel.primary_key, refl.primary_key_name
|
||||
jointable = [Column::Col.new(',')] + table
|
||||
@_joins += build_join refl.options[:join_table], pretable, @table_alias[ jointable], premodel.primary_key, refl.foreign_key
|
||||
@_joins += build_join query, jointable, t, refl.association_foreign_key, refl.association_primary_key
|
||||
else raise BuilderError, "Unkown reflection macro: #{refl.macro.inspect}"
|
||||
end
|
||||
else raise BuilderError, "Unkown reflection type: #{refl.class.name}"
|
||||
else raise BuilderError, "Unkown reflection type: #{refl.class.name} #{refl.macro.inspect}"
|
||||
end
|
||||
self
|
||||
end
|
||||
|
@ -195,7 +219,7 @@ class SmqlToAR
|
|||
|
||||
class SubBuilder < Array
|
||||
attr_reader :parent, :_where
|
||||
delegate :wobs, :joins, :includes, :sub_joins, :vid, :quote_column_name, :quoter, :quote_table_name, :column, :to => :parent
|
||||
delegate :wobs, :joins, :includes, :sub_joins, :vid, :quote_column_name, :quote, :quote_table_name, :column, :to => :parent
|
||||
|
||||
def initialize parent, tmp = false
|
||||
@parent = parent
|
||||
|
@ -224,12 +248,14 @@ class SmqlToAR
|
|||
def optimize!
|
||||
ext = []
|
||||
collect! do |sub|
|
||||
sub = sub.optimize! if sub.kind_of? Array
|
||||
sub = sub.optimize! if sub.kind_of? SubBuilder
|
||||
if self.class == sub.class
|
||||
ext.push *sub
|
||||
nil
|
||||
elsif sub.blank?
|
||||
nil
|
||||
elsif 1 == sub.size
|
||||
sub.first
|
||||
else
|
||||
sub
|
||||
end
|
||||
|
@ -243,21 +269,22 @@ class SmqlToAR
|
|||
end
|
||||
def default() SmqlToAR::And end
|
||||
def default_new( parent) default.new self, parent, false end
|
||||
def collect_build_where
|
||||
collect {|x| x.respond_to?( :build_where) ? x.build_where : x.to_s }
|
||||
def collect_build_where indent = nil
|
||||
indent = (indent||0) + 1
|
||||
collect {|x| "(#{x.respond_to?( :build_where) ? x.build_where( indent) : x.to_s})" }
|
||||
end
|
||||
end
|
||||
|
||||
class And < SubBuilder
|
||||
def default; SmqlToAR::Or; end
|
||||
def build_where
|
||||
collect_build_where.join ' AND '
|
||||
def build_where indent = nil
|
||||
collect_build_where( indent).join " AND\n"+"\t"*(indent||0)
|
||||
end
|
||||
end
|
||||
|
||||
class Or < SubBuilder
|
||||
def build_where
|
||||
collect_build_where.join ' OR '
|
||||
def build_where indent = nil
|
||||
collect_build_where( indent).join " OR\n"+"\t"*(indent||0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue