From: Ezra Zygmuntowicz Date: 2006-01-04T03:55:22+09:00 Subject: DSL help? 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