ruby-on-rails - tutorial rails ita




Migliori pratiche per gestire percorsi per sottoclassi STI in binari (10)

Le viste e i controller di My Rails sono disseminati di redirect_to , link_to e form_for chiamate di metodi. A volte link_to e redirect_to sono espliciti nei percorsi che stanno collegando (es. link_to 'New Person', new_person_path ), ma molte volte i percorsi sono impliciti (es. link_to 'Show', person ).

Aggiungo alcune ereditarietà della tabella singola (STI) al mio modello (ad esempio Employee < Person ) e tutti questi metodi si interrompono per un'istanza della sottoclasse (ad esempio Employee ); quando rails esegue link_to @person , link_to @person errori con undefined method employee_path' for #<#<Class:0x000001022bcd40>:0x0000010226d038> . Rails sta cercando un percorso definito dal nome della classe dell'oggetto, che è dipendente. Queste rotte dei dipendenti non sono definite e non esiste un controller per i dipendenti, quindi le azioni non sono definite.

Questa domanda è stata chiesta prima:

  1. In StackOverflow , la risposta è di modificare ogni istanza di link_to etc nell'intera base di codice e indicare esplicitamente il percorso
  2. Di nuovo su StackOverflow , due persone suggeriscono di usare routes.rb per mappare le risorse della sottoclasse alla classe genitrice ( map.resources :employees, :controller => 'people' ). La risposta più alta nella stessa domanda SO suggerisce di digitare il cast di ogni oggetto istanza nella base di codice usando .becomes
  3. Ancora un altro a StackOverflow , la risposta migliore è il modo nel campo Do Repeat Yourself e suggerisce di creare duplicati per ogni sottoclasse.
  4. Here's la stessa domanda di nuovo in SO, dove la risposta più importante sembra essere semplicemente sbagliata (Rails magic funziona solo!)
  5. Altrove sul web, ho trovato questo post sul blog in cui F2Andy consiglia di modificare il percorso ovunque nel codice.
  6. Sul blog post Ereditarietà di tabelle singole e RESTful Routes in Logical Reality Design, si consiglia di mappare le risorse per la sottoclasse al controller della superclasse, come nella risposta SO numero 2 sopra.
  7. Alex Reisner ha una post -ereditarietà di tabelle singole in Rails , nella quale sostiene di mappare le risorse delle classi routes.rb alla classe genitrice in routes.rb , poiché routes.rb solo le rotture di routing da link_to e redirect_to , ma non da form_for . Quindi raccomanda invece di aggiungere un metodo alla classe genitore per far sì che le sottoclassi si riferiscano alla loro classe. Sembra buono, ma il suo metodo mi ha dato l'errore undefined local variable or method `child' for # .

Quindi la risposta che sembra più elegante e ha il maggior consenso (ma non è così elegante, né tanto consenso) è aggiungere le risorse ai tuoi routes.rb . Tranne che questo non funziona per form_for . Ho bisogno di una certa chiarezza! Per distillare le scelte di cui sopra, le mie opzioni sono

  1. mappare le risorse della sottoclasse al controller della superclasse in routes.rb (e spero di non aver bisogno di chiamare form_for in qualsiasi sottoclasse)
  2. Sostituisci i metodi interni delle rotaie per far sì che le classi si ingannino l'una con l'altra
  3. Modifica ogni istanza nel codice in cui il percorso all'azione di un oggetto viene richiamato in modo implicito o esplicito, modificando il percorso o digitando l'oggetto.

Con tutte queste risposte contrastanti, ho bisogno di una sentenza. Mi sembra che non ci sia una buona risposta. Si tratta di un difetto nel design delle rotaie? Se è così, è un bug che potrebbe essere corretto? O se no, allora spero che qualcuno possa mettermi direttamente su questo, guidarmi attraverso i pro e i contro di ciascuna opzione (o spiegare perché non è un'opzione), e qual è la risposta giusta, e perché. O c'è una risposta giusta che non trovo sul web?


È possibile creare un metodo che restituisca un oggetto padre fittizio per il routing purpouse

class Person < ActiveRecord::Base      
  def routing_object
    Person.new(id: id)
  end
end

e quindi chiama semplicemente form_for @ employee.routing_object che senza tipo restituirà l'oggetto classe Person


Anch'io stavo avendo problemi con questo problema e ho trovato questa risposta su una domanda simile alla nostra. Ha funzionato per me.

form_for @list.becomes(List)

Risposta mostrata qui: Utilizzo del percorso STI con lo stesso controller

Il metodo .becomes è definito come principalmente usato per risolvere problemi STI come il tuo form_for one.

.becomes informazioni qui: http://apidock.com/rails/ActiveRecord/Base/becomes

Risposta super ritardata, ma questa è la risposta migliore che ho trovato e ha funzionato bene per me. Spero che questo aiuti qualcuno. Saluti!


Ho avuto lo stesso problema. Dopo aver utilizzato STI, il metodo form_for pubblicato l'URL figlio errato.

NoMethodError (undefined method `building_url' for

Ho finito per aggiungere le rotte extra per le classi figlio e indirizzarle agli stessi controller

 resources :structures
 resources :buildings, :controller => 'structures'
 resources :bridges, :controller => 'structures'

Inoltre:

<% form_for(@structure, :as => :structure) do |f| %>

in questo caso la struttura è in realtà un edificio (classe dell'infanzia)

Sembra funzionare per me dopo aver fatto un invio con form_for .


In questo modo funziona per me ok (definisci questo metodo nella classe base):

def self.inherited(child)
  child.instance_eval do
    alias :original_model_name :model_name
    def model_name
      Task::Base.model_name
    end
  end
  super
end

Puoi provare questo, se non hai percorsi nidificati:

resources :employee, path: :person, controller: :person

Oppure puoi andare in un altro modo e usare qualche magia OOP come descritto qui: https://coderwall.com/p/yijmuq

In secondo luogo puoi creare assistenti simili per tutti i tuoi modelli annidati.


Questa è la soluzione più semplice che sono riuscito a ottenere con un effetto collaterale minimo.

class Person < Contact
  def self.model_name
    Contact.model_name
  end
end

Ora url_for @person verrà url_for @person a contact_path come previsto.

Come funziona: gli helper degli URL si affidano a YourModel.model_name per riflettere sul modello e generare (tra molte cose) chiavi di percorso singolare / plurale. Qui la Person sta fondamentalmente dicendo che sono proprio come un amico di Contact , chiediglielo .


Se considero un'eredità STI come questa:

class AModel < ActiveRecord::Base ; end
class BModel < AModel ; end
class CModel < AModel ; end
class DModel < AModel ; end
class EModel < AModel ; end

in "app / models / a_model.rb" aggiungo:

module ManagedAtAModelLevel
  def model_name
    AModel.model_name
  end
end

E poi nella classe AModel:

class AModel < ActiveRecord::Base
  def self.instanciate_STI
    managed_deps = { 
      :b_model => true,
      :c_model => true,
      :d_model => true,
      :e_model => true
    }
    managed_deps.each do |dep, managed|
      require_dependency dep.to_s
      klass = dep.to_s.camelize.constantize
      # Inject behavior to be managed at AModel level for classes I chose
      klass.send(:extend, ManagedAtAModelLevel) if managed
    end
  end

  instanciate_STI
end

Quindi posso anche facilmente scegliere quale modello voglio usare quello predefinito, e questo senza nemmeno toccare la definizione della sottoclasse. Molto secco.


Seguendo l'idea di @Prathan Thananart ma cercando di non distruggere nulla. (dato che c'è così tanta magia in gioco)

class Person < Contact
  model_name.class_eval do
    def route_key
     "contacts"
    end
    def singular_route_key
      superclass.model_name.singular_route_key
    end
  end
end

Ora url_for @person verrà mappato a contact_path come previsto.



hackish, ma solo un altro alla lista di soluzioni.

class Parent < ActiveRecord::Base; end

Class Child < Parent
  def class
    Parent
  end
end

funziona su binari 2.xe 3.x





single-table-inheritance