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