Können Rails-Routing-Helfer (dh mymodel_path (model)) in Modellen verwendet werden?

368

Angenommen, ich habe ein Rails-Modell namens Thing. Thing verfügt über ein URL-Attribut, das optional irgendwo im Internet auf eine URL festgelegt werden kann. Im Ansichtscode benötige ich eine Logik, die Folgendes bewirkt:

<% if thing.url.blank? %>
<%= link_to('Text', thing_path(thing)) %>
<% else %>
<%= link_to('Text', thing.url) %>
<% end %>

Diese bedingte Logik in der Ansicht ist hässlich. Natürlich könnte ich eine Hilfsfunktion erstellen, die die Ansicht dahingehend ändern würde:

<%= thing_link('Text', thing) %>

Das löst das Problem der Ausführlichkeit, aber ich würde es wirklich vorziehen, die Funktionalität im Modell selbst zu haben. In diesem Fall wäre der Ansichtscode:

<%= link_to('Text', thing.link) %>

Dies würde offensichtlich eine Verknüpfungsmethode für das Modell erfordern. Folgendes müsste es enthalten:

def link
  (self.url.blank?) ? thing_path(self) : self.url
end

Bis zur Frage ist thing_path () eine undefinierte Methode im Modellcode. Ich gehe davon aus, dass es möglich ist, einige Hilfsmethoden in das Modell zu "ziehen", aber wie? Und gibt es einen wirklichen Grund dafür, dass das Routing nur auf dem Controller und den Ansichtsebenen der App ausgeführt wird? Ich kann mir viele Fälle vorstellen, in denen Modellcode möglicherweise mit URLs umgehen muss (Integration in externe Systeme usw.).

Aaron Longwell
quelle
Ein Anwendungsfall wäre: um eine verkürzte URL aus goo.gl in einem Aftersave zu generieren,
lulalala
2
Sie sollten Ihr Modell wahrscheinlich in einen Präsentator einbinden, wenn Sie eine Ansichtslogik hinzufügen möchten. Dadurch bleiben die MVC-Ebenen getrennt. Siehe Draper ( github.com/jcasimir/draper ).
Kris
2
Siehe auch den Abschnitt "URL-Generierung für benannte Routen" in der Dokumentation unter api.rubyonrails.org/classes/ActionDispatch/Routing/UrlFor.html
Backo

Antworten:

698

In den Schienen 3, 4 und 5 können Sie Folgendes verwenden:

Rails.application.routes.url_helpers

z.B

Rails.application.routes.url_helpers.posts_path
Rails.application.routes.url_helpers.posts_url(:host => "example.com")
Paul Horsfall
quelle
14
Ypu kann dies in jedes Modul aufnehmen und danach können Sie dort auf URL-Helfer zugreifen. Thx
Viacheslav
41
Sparen Sie sich das Kopieren Ihrer :hostOption überall und legen Sie sie einmal in Ihren Umgebungskonfigurationsdateien fest:Rails.application.routes.default_url_options[:host] = 'localhost:3000'
Andrew
5
include Rails.application.routes.url_helpersfunktioniert für mich in Rails 4.1
Jan
1
Wenn Sie nur den Pfad benötigen, können Sie Folgendes verwenden: only_path => true als Option, ohne den Host-Parameter zu übergeben.
TrueinViso
1
Dies scheint eine gute Option zu sein: hawkins.io/2012/03/…
Renars Sirotins
182

Ich habe die Antwort gefunden, wie ich das selbst machen soll. Fügen Sie im Modellcode einfach Folgendes ein:

Für Schienen <= 2:

include ActionController::UrlWriter

Für Schienen 3:

include Rails.application.routes.url_helpers

Dadurch wird auf magische Weise thing_path(self)die URL für das aktuelle Objekt oder other_model_path(self.association_to_other_model)eine andere URL zurückgegeben.

Aaron Longwell
quelle
27
Nur ein Update, da das oben genannte veraltet ist. Dies ist das aktuelle Include:include Rails.application.routes.url_helpers
Yalestar
2
Ich erhalte NoMethodError (undefinierte Methode `optimize_routes_generation? 'Für # <ActionView :: Base: 0x007fe8c0eecbd0>), wenn ich dies versuche
moger777
118

Möglicherweise finden Sie auch den folgenden Ansatz sauberer als jede Methode einzuschließen:

class Thing
  delegate :url_helpers, to: 'Rails.application.routes' 

  def url
    url_helpers.thing_path(self)
  end
end
matthuhiggins
quelle
7
Eine Dokumentation hierzu schien schwer zu finden, daher hier ein anständiger Link zur Verwendung von Delegaten in ActiveSupport. simonecarletti.com/blog/2009/12/inside-ruby-on-rails-delegate
tlbrack
2
Ich bekomme eine undefined local variable or method 'url_helpers' for Event:ClassFehlermeldung ... :(
Augustin Riedinger
Ich verstehe undefined method url_helpers. Was werde ich tun?
Asiniy
@asiniy, sind Sie sicher, dass Sie die Delegate-Option für url_helpers verwendet haben, die nach der Klassendeklaration geschrieben wurde?
Krzysztof Witczak
Funktioniert nicht, aber es kann sein, dass dies nur funktioniert, wenn Sie Ihre eigenen erstellen class, wie in der Antwort gezeigt. Wenn Ihre Modellklasse bereits erweitert < ApplicationRecordist, funktioniert dies nicht?
Skplunkerin
13

Jede Logik, die mit dem zu tun hat, was in der Ansicht angezeigt wird, sollte an eine Hilfsmethode delegiert werden, da die Methoden im Modell ausschließlich für den Umgang mit Daten vorgesehen sind.

Folgendes können Sie tun:

# In the helper...

def link_to_thing(text, thing)
  (thing.url?) ? link_to(text, thing_path(thing)) : link_to(text, thing.url)
end

# In the view...

<%= link_to_thing("text", @thing) %>
Josh Delsman
quelle
1
Was ich an Hilfsmethoden in diesem Fall nicht mag: Wenn ich mir link_to_thing () ansehe, muss ich entscheiden, ob es sich um einen dingspezifischen oder einen anwendungsweiten Helfer handelt (es kann auch leicht sein). Ich muss in Betracht ziehen, 2 Dateien auf die Quelle zu überprüfen. thing.link lässt keine Frage zur Quelldatei.
Aaron Longwell
Was wäre auch, wenn ich diese Funktionalität in einer Rake-Aufgabe verwenden müsste (um möglicherweise eine CSV-Datei mit Ding-URLs zu exportieren) ... wäre es auch in diesem Fall viel besser, direkt zum Modell zu gehen.
Aaron Longwell
Der Unterschied besteht jedoch darin, dass link_to im Modell nicht verfügbar ist, da es sich um einen ActionView-Helfer handelt. Das würde also nicht funktionieren. Sie könnten einige Attributstandards im Modell hacken. Wenn es also nicht festgelegt ist, wird standardmäßig etwas verwendet, aber das liegt bei Ihnen.
Josh Delsman
12
Angesichts der wachsenden APIs für Webdienste müssen Modelle häufig externe Ressourcen mit ihren eigenen Daten kontaktieren und Rückruf-URLs bereitstellen. Beispielsweise muss sich ein Fotoobjekt in Socialmod veröffentlichen, das bei der Moderation auf die URL des Fotos zurückruft. Ein after_create () - Hook wäre sinnvoll, um ihn in Socialmod zu veröffentlichen. Aber woher kennen Sie die Rückruf-URL? Ich denke, die eigene Antwort des Autors ist sinnvoll.
JasonSmith
3
Während Sie im rein technischen Sinne Recht haben, gibt es Zeiten, in denen Menschen nur Dinge zum Arbeiten benötigen, ohne jedes Yak rasieren zu müssen, um zu vermeiden, dass ein heiliges Designmuster verletzt wird oder was Sie haben. Vielleicht müssen sie die URL abrufen und tatsächlich speichern In der Datenbank wäre es dann praktisch, wenn ein Modell nur weiß, wie das geht, anstatt Dinge hin und her zu geben. Manchmal können Regeln gebrochen werden.
Nitecoder
1

Es könnte zwar einen Weg geben, diese Art von Logik aus dem Modell herauszuhalten. Ich bin damit einverstanden, dass Sie dies nicht in die Ansicht einfügen sollten ( halten Sie es dünn ), aber wenn das Modell keine URL als Datenelement an den Controller zurückgibt, sollte sich das Routing-Material im Controller befinden.

Ryan Montgomery
quelle
4
Betreff: "Es sei denn, das Modell gibt eine URL als Datenelement zurück". Genau das passiert hier ... das Modell "besitzt" diese Daten, die entweder ein Link zu einer URL vor Ort oder außerhalb der Website sind. In einigen Fällen wird die URL aus dem Rails-Routing generiert. In anderen Fällen handelt es sich bei der URL um vom Benutzer bereitgestellte Daten.
Aaron Longwell
0

(Edit: Vergiss mein vorheriges Geschwätz ...)

Ok, es könnte Situationen geben, in denen Sie entweder zum Modell oder zu einer anderen URL wechseln ... Aber ich glaube nicht, dass dies zum Modell gehört, die Ansicht (oder vielleicht das Modell) klingt angemessener.

Über die Routen, soweit ich weiß, beziehen sich die Routen auf die Aktionen in Controllern (die normalerweise "magisch" eine Ansicht verwenden), nicht direkt auf Ansichten. Der Controller sollte alle Anforderungen verarbeiten, die Ansicht sollte die Ergebnisse präsentieren und das Modell sollte die Daten verarbeiten und sie der Ansicht oder dem Controller bereitstellen. Ich habe hier viele Leute über Routen zu Modellen sprechen hören (bis zu dem Punkt, an dem ich fast anfange, es zu glauben), aber so wie ich es verstehe: Routen gehen an Controller. Natürlich sind viele Controller Controller für ein Modell und werden oft genannt<modelname>sController (z B. "UsersController" ist der Controller des Modells "User").

Wenn Sie in einer Ansicht unangenehme Mengen an Logik schreiben, versuchen Sie, die Logik an einen geeigneteren Ort zu verschieben. Anforderungs- und interne Kommunikationslogik gehören wahrscheinlich in die Steuerung, datenbezogene Logik kann in das Modell eingefügt werden (aber keine Anzeigelogik, die Verbindungs-Tags usw. enthält), und Logik, die rein anzeigebezogen ist, würde in einen Helfer eingefügt.

Stein G. Strindhaug
quelle
Angenommen, Sie haben ein Bildmodell. Wenn dem Bild eine externe URL zugeordnet ist, zeigen Sie auf diese URL. Andernfalls auf die Show-Seite des Bildes zeigen, um ein Bild hochzuladen? Nur eine Idee.
Josh Delsman
Wie wäre es mit diesem weniger allgemeinen Beispiel: link_to "Full Size Image", image.link Die Link-Methode im Modell würde entweder auf die URL vor Ort (image_path) oder beispielsweise auf eine Flickr-URL verweisen, wenn der Benutzer eine und angegeben hat .url wurde auf das Bild gesetzt.
Aaron Longwell