Construindo condições de registro ativo em uma matriz - Método privado 'Scan' chamado Erro
Pergunta
Estou tentando construir um conjunto de condições dinamicamente usando uma matriz, conforme sugerido na primeira resposta aqui: Um ou mais parâmetros no modelo encontram condições com rubi nos trilhos. No entanto, pareço estar fazendo algo incorretamente e não tenho certeza se o que estou tentando é fundamentalmente doentia ou se estou simplesmente estragado minha sintaxe.
Estou simplificando para uma única condição aqui para tentar ilustrar o problema, pois tentei criar uma prova simples de conceito nesse sentido antes de colocar os 5 estilos de condição diferentes com os quais estou disputando.
Isso funciona:
excluded.push 12
excluded.push 30
@allsites = Site.all(:conditions => ["id not in (?)", excluded])
Isso resulta em um método privado 'Scan' chamado Erro:
conditionsSet = []
excluded.push 12
excluded.push 30
conditionsSet << ["id not in (?)", excluded]
@allsites = Site.all(:conditions => conditionsSet)
Obrigado por qualquer conselho. Eu não tinha certeza se a coisa correta era colocar isso como um item de acompanhamento na pergunta/respostas relacionadas que observei no topo. Já que tenho um problema, não uma resposta. Se houver uma maneira melhor de postar isso relacionado à postagem existente, entre em contato.
Solução
Experimente isso:
Rails 2.3
class Site < ActiveRecord::Base
def self.build_conditions(ids, name=nil, state=nil)
cond = []
cond << send(:sanitize_sql_array, ["id NOT IN (?)", ids]) unless ids.empty?
cond << send(:sanitize_sql_array, ["name = ? ", name]) unless name
cond << send(:sanitize_sql_array, ["state = ? ", state]) unless state
cond.join(" and ")
end
end
Agora em algum lugar do seu controlador:
Site.all(:conditions => Site.build_conditions([1,2]))
Site.all(:conditions => Site.build_conditions(nil, "ABC"))
Rails 3
class Site < ActiveRecord::Base
def self.exclude_ids_by_name_and_state(ids, name=nil, state=nil)
result = scoped
result = result.where("id NOT IN (?)", ids) if ids.present?
result = result.where(:name => name) if name.present?
result = result.where(:state => state) if state.present?
result
end
end
Agora em algum lugar do seu controlador:
Site.exclude_ids_by_name_and_state([1,2])).all
Site.exclude_ids_by_name_and_state(nil, "ABC").all
Outras dicas
Você quer:
conditionsSet += ["id not in (?)", excluded]
ao invés de:
conditionsSet << ["id not in (?)", excluded]
+=
adiciona as duas matrizes (pense nisso como fusão dos dois em uma matriz) enquanto <<
empurra um novo elemento para a matriz. Então você está recebendo: [["id not in (?)", excluded]]
Ao usar <<
, e :conditions
quer uma matriz em que este primeiro elemento é uma string (não uma matriz).
Tentar SmartTuple, ele foi projetado especificamente para casos como este.
def self.build_conditions(options = {})
[
SmartTuple.new(" AND "),
(["id NOT IN (?)", ids] if options[:ids].present?),
({:name => options[:name]} if options[:name].present?),
({:state => options[:state]} if options[:state].present?),
].sum.compile
end
...
Site.all(:conditions => Site.build_conditions(:ids => {1,2]))
Site.all(:conditions => Site.build_conditions(:name => "abc", :state => "disabled")
Para mim, também é preferível usar options
hash em vez de argumentos ordenados. À medida que seu projeto cresce, mais condições podem aparecer e você perderá a noção dos quais. A aparência de hash e se comporta mais clara e você pode validá -lo facilmente para evitar erros de software.