Hey rubyists-
I was wondering if someone could help me make this small dsl I wrote into a little bit better syntax. The Cond class just takes a block in its constructor and converts what's inside the block into an sql where clause with ? syntax like this:
c = Cond.new do
first_name ‘=’, ‘Ezra’
start_date ‘between’, ‘2006-01-01′, ‘2006-01-30′
last_name ‘like’, ‘Zyg%’
sql ‘hosts.id = something.id'
end
p c.where
#=> ["first_name = ? and start_date between ? and ? and last_name LIKE ? and hosts.id = something.id", "Ezra", "2006-01-01", "2006-01-30", "Zyg%"]
I would like to be able to get rid of the quotes around the operators '=', '<=', 'LIKE' and so on to become =, <= and LIKE . Also I would like to be able to get rid of the need for commas inside the block as well. Inside of the Cond class initialize method it justs uses instance_eval &block to get the block contents and then uses method_missing to build a nested array for each statement.
Anyone have any better ideas to offer on how to make this interface a little nicer? Thanks in advance.
class Cond
# Uses a block to initialize the condition:
# c = InVisible::Cond.new do
# month '<=', 11
# year '=', 2005
# name 'LIKE', 'ruby%'
# end
···
#
# c.where -> ["month <= ? and year = ? and name LIKE ?", 11, 2005, "ruby%"]
#
# to include direct SQL, use like this:
# c = InVisible::Cond.new do
# sql "hosts.id = logs.host_id and hosts.name", 'like', "123.23.45.67"
# end
# if a value needs to by typed (f.e. in Postgres: "ip < inet ?"), use a form of:
# c = InVisible::Cond.new do
# ip '= inet', '123.34.56.78/24'
# end
#
# to expand an existing condition, use the << method
# c << ['age', '>', 30]
def initialize(&block)
@args = []
instance_eval(&block) if block_given?
end
def method_missing(sym, *args)
@args << [sym,args.flatten].flatten
end
def <<(*args)
@args << [args.flatten].flatten
end
def where(args=@args)
q = []
ary = []
args.each do |pair|
iv = pair[1..99]
unless iv.last.nil? || iv.last.to_s == ''
if pair[0].to_s =~ /^sql.*/ then
pair[0] = iv.shift
end
case iv.size
when 0:
q << "#{pair[0]}" # the case when there is only one (sql) statements
when 1:
q << "#{pair[0]} = ?"
ary << iv.last
when 2:
operator = iv[0]
q << "#{pair[0]} #{operator} ?"
ary << iv.last
when 3:
op = case iv[0]
when 'between': "between ? and ?"
end
q << "#{pair[0]} #{op}"
ary << iv[-2] << iv[-1]
end
end
end
return [q.join(" and ")].concat(ary)
end
end
Cheers-
-Ezra