Meus pontos de vista Rails e controladores estão repletas redirect_to
, link_to
e form_for
chamadas de método. Às vezes link_to
e redirect_to
são explícitos nos caminhos que estão vinculando (por exemplo link_to 'New Person', new_person_path
), mas muitas vezes os caminhos estão implícitos (por exemplo link_to 'Show', person
).
Eu adiciono alguma herança de tabela única (STI) ao meu modelo (por exemplo Employee < Person
), e todos esses métodos são interrompidos para uma instância da subclasse (por exemplo Employee
); quando o rails é executado link_to @person
, ele erro com undefined method employee_path' for #<#<Class:0x000001022bcd40>:0x0000010226d038>
. O Rails está procurando uma rota definida pelo nome da classe do objeto, que é empregado. Essas rotas de funcionários não são definidas e não há um controlador de funcionários, portanto, as ações também não são definidas.
Esta pergunta já foi feita antes:
- No StackOverflow , a resposta é editar todas as instâncias de link_to etc em toda a sua base de código e indicar o caminho explicitamente
- No StackOverflow novamente, duas pessoas sugerem o uso
routes.rb
para mapear os recursos da subclasse para a classe pai (map.resources :employees, :controller => 'people'
). A resposta principal nessa mesma pergunta de SO sugere a conversão de tipos de todos os objetos de instância na base de código usando.becomes
- Ainda outra no StackOverflow , a resposta principal está no campo Do Repeat Yourself e sugere a criação de andaimes duplicados para cada subclasse.
- Aqui está a mesma pergunta novamente na SO, onde a resposta principal parece estar errada (a mágica do Rails simplesmente funciona!)
- Em outras partes da web, encontrei este post no qual a F2Andy recomenda editar o caminho em qualquer parte do código.
- Na postagem do blog Herança de tabela única e rotas RESTful no Logical Reality Design, é recomendável mapear os recursos da subclasse para o controlador da superclasse, como na resposta SO 2 acima.
- Alex Reisner tem um post de Herança de tabela única no Rails , no qual defende o mapeamento dos recursos das classes filho para a classe pai
routes.rb
, pois isso apenas captura falhas de roteamento delink_to
eredirect_to
, mas não deform_for
. Portanto, ele recomenda adicionar um método à classe pai para fazer com que as subclasses mentam sobre a classe. Parece bom, mas o método dele me deu o erroundefined local variable or method `child' for #
.
Portanto, a resposta que parece mais elegante e tem o maior consenso (mas não é tudo o que elegante, nem que muito consenso), é a adicionar os recursos para o seu routes.rb
. Exceto que isso não funciona form_for
. Eu preciso de alguma clareza! Para destilar as opções acima, minhas opções são
- mapeie os recursos da subclasse para o controlador da superclasse em
routes.rb
(e espero não precisar chamar form_for em nenhuma subclasse) - Substituir métodos internos dos trilhos para fazer com que as classes se encontrem
- Edite todas as instâncias no código em que o caminho para a ação de um objeto é invocado implicitamente ou explicitamente, alterando o caminho ou convertendo o objeto em tipo.
Com todas essas respostas conflitantes, preciso de uma decisão. Parece-me que não há uma boa resposta. Isso é uma falha no design dos trilhos? Em caso afirmativo, é um bug que pode ser corrigido? Ou, se não, espero que alguém possa me esclarecer sobre isso, guiar-me pelos prós e contras de cada opção (ou explicar por que isso não é uma opção) e qual é a resposta certa e por quê. Ou existe uma resposta certa que não estou encontrando na web?
fonte
Respostas:
Esta é a solução mais simples que consegui encontrar com um efeito colateral mínimo.
Agora
url_for @person
será mapeadocontact_path
conforme o esperado.Como funciona: os auxiliares de URL dependem
YourModel.model_name
para refletir sobre o modelo e gerar (entre muitas coisas) chaves de rota singular / plural. AquiPerson
está basicamente dizendo que eu sou comoContact
cara, pergunte a ele .fonte
build_x
/create_x
). Por outro lado, o preço de jogar com magia é que você nunca está 100% certo do que pode mudar.Eu tive o mesmo problema. Depois de usar o STI, o
form_for
método estava sendo lançado no URL filho incorreto.Acabei adicionando rotas extras para as classes filho e apontando-as para os mesmos controladores
Além disso:
neste caso, a estrutura é realmente um edifício (classe filho)
Parece funcionar para mim depois de enviar um
form_for
.fonte
Sugiro que você dê uma olhada em: https://stackoverflow.com/a/605172/445908 , o uso desse método permitirá que você use "form_for".
fonte
<%= form_for @child, :as => :child, url: @child.becomes(Parent)
<%= form_for @child.becomes(Parent)
Use o tipo nas rotas:
http://samurails.com/tutorial/single-table-inheritance-with-rails-4-part-2/
fonte
Seguindo a ideia de @ Nathan Thananart, mas tentando não destruir nada. (já que há tanta mágica envolvida)
Agora, url_for @person será mapeado para contact_path conforme o esperado.
fonte
Eu também estava tendo problemas com esse problema e encontrei esta resposta em uma pergunta semelhante à nossa. Funcionou para mim.
Resposta mostrada aqui: Usando caminho STI com o mesmo controlador
O
.becomes
método é definido como usado principalmente para resolver problemas de DST, como o seuform_for
..becomes
informações aqui: http://apidock.com/rails/ActiveRecord/Base/becomesResposta super tardia, mas esta é a melhor resposta que pude encontrar e funcionou bem para mim. Espero que isso ajude alguém. Felicidades!
fonte
Ok, tive muita frustração nesta área do Rails e cheguei à seguinte abordagem, talvez isso ajude outras pessoas.
Em primeiro lugar, lembre-se de que várias soluções acima e ao redor da rede sugerem o uso de constantes nos parâmetros fornecidos pelo cliente. Esse é um vetor de ataque DoS conhecido, pois Ruby não coleta símbolos de lixo, permitindo que um invasor crie símbolos arbitrários e consuma a memória disponível.
Eu implementei a abordagem abaixo, que suporta instanciação de subclasses de modelo e é SAFE do problema de contantização acima. É muito semelhante ao que o Rails 4 faz, mas também permite mais de um nível de subclasse (diferente do Rails 4) e funciona no Rails 3.
Depois de tentar várias abordagens para o 'carregamento de subclasse na questão do desenvolvimento', muito parecido com o sugerido acima, descobri que a única coisa que funcionava de maneira confiável era usar 'require_dependency' nas minhas classes de modelo. Isso garante que o carregamento da classe funcione corretamente no desenvolvimento e não cause problemas na produção. No desenvolvimento, o AR sem 'require_dependency' não saberá sobre todas as subclasses, o que afeta o SQL emitido para correspondência na coluna type. Além disso, sem 'require_dependency', você também pode acabar em uma situação com várias versões das classes de modelo ao mesmo tempo! (por exemplo, isso pode acontecer quando você altera uma classe base ou intermediária, as subclasses nem sempre parecem recarregar e são deixadas em subclasses da classe antiga)
Também não substituo model_name como sugerido acima, porque uso I18n e preciso de cadeias diferentes para os atributos de diferentes subclasses, por exemplo: tax_identifier torna-se 'ABN' para Organização e 'TFN' para Pessoa (na Austrália).
Eu também uso o mapeamento de rotas, como sugerido acima, definindo o tipo:
Além do mapeamento de rota, estou usando InheritedResources e SimpleForm e uso o seguinte invólucro de formulário genérico para novas ações:
... e para ações de edição:
E para fazer isso funcionar, em minha ResourceContoller base, eu exponho resource_request_name de InheritedResource como um método auxiliar para a exibição:
Se você não estiver usando InheritedResources, use algo como o seguinte em seu 'ResourceController':
Sempre feliz em ouvir outras experiências e melhorias.
fonte
Recentemente, documentei minhas tentativas de obter um padrão estável de STI trabalhando em um aplicativo Rails 3.0. Aqui está a versão TL; DR:
Essa abordagem contorna os problemas que você lista, bem como vários outros problemas que outras pessoas tiveram com as abordagens de DST.
fonte
Você pode tentar isso se não tiver rotas aninhadas:
Ou você pode seguir outro caminho e usar alguma mágica OOP como descrito aqui: https://coderwall.com/p/yijmuq
Na segunda maneira, você pode criar ajudantes semelhantes para todos os seus modelos aninhados.
fonte
Aqui está uma maneira segura e limpa de fazê-lo funcionar em formulários e em todo o aplicativo que usamos.
Então eu tenho na minha forma. A peça adicionada para isso é o distrito de::.
Espero que isto ajude.
fonte
A solução mais limpa que encontrei é adicionar o seguinte à classe base:
Ele funciona para todas as subclasses e é muito mais seguro do que substituir todo o objeto de nome do modelo. Ao direcionar apenas as chaves de rota, resolvemos os problemas de roteamento sem interromper o I18n ou arriscar qualquer efeito colateral potencial causado pela substituição do nome do modelo, conforme definido pelo Rails.
fonte
Se eu considerar uma herança STI como esta:
em 'app / models / a_model.rb', adiciono:
E então na classe AModel:
Portanto, posso escolher com facilidade o modelo que desejo usar o padrão, e isso sem sequer tocar na definição da subclasse. Muito seco.
fonte
Desta forma, funciona para mim ok (defina este método na classe base):
fonte
Você pode criar um método que retorne um objeto pai fictício para o roteador
e então simplesmente chame form_for @ employee.routing_object que sem type retornará o objeto da classe Person
fonte
Após a resposta @ prathan-thananart e para as várias classes STI, você pode adicionar o seguinte ao modelo pai ->
Isso fará com que cada formulário com os dados de contato para enviar parâmetros como
params[:contact]
, em vez deparams[:contact_person]
,params[:contact_whatever]
.fonte
hackish, mas apenas mais um na lista de soluções.
funciona nos trilhos 2.xe 3.x
fonte
Child.new
, retorna umaParent
classe em vez da subclasse. Isso significa que você não pode criar as subclasses através da atribuição em massa por meio de um controlador (já quetype
é um atributo protegido por padrão), a menos que você também defina o atributo type explicitamente.