Если вы хотите именно эту структуру, вы можете сделать что-то вроде этого:
pairs = [['A', 1], ['A', 3], ['Q', 9]]
RejectionCode.where('(code1, code2) in ((?), (?), (?))', *pairs)
Конечно, pairs.length
предположительно не всегда будет три, так что вы можете сказать:
pairs = [['A', 1], ['A', 3], ['Q', 9]]
placeholders = (%w[(?)] * pairs.length).join(', ')
RejectionCode.where("(code1, code2) in (#{placeholders})", *pairs)
Да, для построения фрагмента SQL используется интерполяция строк, но в данном случае это абсолютно безопасно, поскольку вы строите все строки и точно знаете, что в них. Если вы поместите это в область видимости, то, по крайней мере, уродство будет скрыто, и вы легко сможете охватить его своим набором тестов.
Кроме того, вы можете воспользоваться некоторыми эквивалентностями. in
причудливый or
, так что они примерно одинаковы:
c in (x, y, z)
c = x or c = y or c = z
и записи (даже анонимные) сравниваются столбец за столбцом, поэтому они эквивалентны:
(a, b) = (x, y)
a = x and b = y
Это означает, что что-то вроде этого:
pairs = [['A', 1], ['A', 3], ['Q', 9]]
and_pair = ->(a) { RejectionCode.where('code1 = ? and code2 = ?', *a) }
and_pair[pairs[0]].or(and_pair[pairs[1]]).or(and_pair[pairs[2]])
должен дать вам тот же результат. Или более широко:
pairs = [['A', 1], ['A', 3], ['Q', 9], ... ]
and_pair = ->(a) { RejectionCode.where('code1 = ? and code2 = ?', *a) }
query = pairs[1..-1].inject(and_pair[pairs.first]) { |q, a| q.or(and_pair[a]) }
Опять же, вы хотели бы скрыть это уродство в области видимости.