Dapatkah Rails Routing Helper (mis. Mymodel_path (model)) digunakan dalam Model?

368

Katakanlah saya punya Model Rails yang disebut Thing. Hal ini memiliki atribut url yang secara opsional dapat diatur ke URL di suatu tempat di Internet. Dalam kode tampilan, saya perlu logika yang melakukan hal berikut:

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

Logika kondisional dalam tampilan ini jelek. Tentu saja, saya bisa membangun fungsi pembantu, yang akan mengubah pandangan menjadi ini:

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

Itu memecahkan masalah verbosity, tapi saya benar-benar lebih suka memiliki fungsi dalam model itu sendiri. Dalam hal ini, kode tampilan adalah:

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

Ini jelas membutuhkan metode tautan pada model. Inilah yang perlu dikandungnya:

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

Pada titik pertanyaan, thing_path () adalah metode yang tidak didefinisikan di dalam kode Model. Saya berasumsi bahwa mungkin untuk "menarik" beberapa metode pembantu ke dalam model, tetapi bagaimana caranya? Dan apakah ada alasan nyata bahwa perutean hanya beroperasi pada pengontrol dan melihat lapisan aplikasi? Saya dapat memikirkan banyak kasus di mana kode model mungkin perlu berurusan dengan URL (berintegrasi dengan sistem eksternal, dll).

Aaron Longwell
sumber
Sebuah use case adalah: untuk menghasilkan url yang disingkat dari goo.gl di aftersave,
lulalala
2
Anda mungkin harus membungkus Anda model dalam presenter jika Anda ingin menambahkan logika tampilan, ini akan membuat lapisan MVC terpisah. Lihat Draper ( github.com/jcasimir/draper ).
Kris
2
Lihat juga bagian " Pembuatan
Backo

Jawaban:

698

Di Rails 3, 4, dan 5 Anda dapat menggunakan:

Rails.application.routes.url_helpers

misalnya

Rails.application.routes.url_helpers.posts_path
Rails.application.routes.url_helpers.posts_url(:host => "example.com")
Paul Horsfall
sumber
14
Ypu dapat memasukkan ini ke modul apa saja dan setelah itu Anda dapat mengakses url-helpers di sana. Thx
Viacheslav Molokov
41
Simpan diri Anda dari menyalin :hostopsi Anda di mana-mana dan mengaturnya sekali di file konfigurasi lingkungan Anda:Rails.application.routes.default_url_options[:host] = 'localhost:3000'
Andrew
5
include Rails.application.routes.url_helpersbekerja untuk saya di Rails 4.1
Jan
1
Jika Anda hanya perlu jalur yang dapat Anda gunakan: only_path => true sebagai opsi tanpa melewati parameter host.
trueinViso
1
ini sepertinya pilihan yang bagus: hawkins.io/2012/03/…
Renars Sirotins
182

Saya telah menemukan jawaban mengenai bagaimana melakukan ini sendiri. Di dalam kode model, cukup cantumkan:

Untuk Rails <= 2:

include ActionController::UrlWriter

Untuk Rails 3:

include Rails.application.routes.url_helpers

Ini secara ajaib thing_path(self)mengembalikan URL untuk hal saat ini, atau other_model_path(self.association_to_other_model)mengembalikan beberapa URL lainnya.

Aaron Longwell
sumber
27
Hanya pembaruan, karena di atas sudah usang. Ini adalah saat ini termasuk:include Rails.application.routes.url_helpers
yalestar
2
Saya mendapatkan NoMethodError (metode yang tidak ditentukan `optim_routes_generation? 'Untuk # <ActionView :: Base: 0x007fe8c0eecbd0>) ketika saya mencoba ini
moger777
118

Anda juga dapat menemukan pembersih pendekatan berikut daripada memasukkan setiap metode:

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

  def url
    url_helpers.thing_path(self)
  end
end
matthuhiggins
sumber
7
Dokumentasi tentang ini sepertinya sulit ditemukan, jadi di sini ada tautan yang layak terkait penggunaan delegasi di ActiveSupport. simonecarletti.com/blog/2009/12/inside-ruby-on-rails-delegate
tlbrack
2
Saya mendapatkan undefined local variable or method 'url_helpers' for Event:Classkesalahan ... :(
Augustin Riedinger
Saya mengerti undefined method url_helpers. Apa yang akan saya lakukan?
asiniy
@ asiniy, apakah Anda yakin Anda telah menggunakan opsi delegasi untuk url_helpers yang ditulis setelah deklarasi kelas?
Krzysztof Witczak
Tidak berfungsi, tetapi mungkin ini hanya berfungsi jika Anda membuatnya sendiri class, seperti yang ditunjukkan dalam jawaban. Jika kelas Model Anda sudah diperluas, < ApplicationRecordini tidak akan berhasil?
skplunkerin
13

Logika apa pun yang berkaitan dengan apa yang ditampilkan dalam tampilan harus didelegasikan ke metode penolong, karena metode dalam model ini semata-mata untuk menangani data.

Inilah yang dapat Anda lakukan:

# 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
sumber
1
Apa yang saya tidak suka tentang metode pembantu dalam hal ini: Ketika saya melihat link_to_thing (), saya harus memutuskan apakah itu pembantu hal-spesifik atau aplikasi-lebar (dengan mudah bisa berupa salah satu). Saya perlu mempertimbangkan memeriksa 2 file untuk sumbernya. thing.link tidak meninggalkan pertanyaan tentang file sumber.
Aaron Longwell
Juga, bagaimana jika saya perlu menggunakan fungsi ini dalam tugas Rake (untuk mengekspor file CSV dari URL hal mungkin) ... langsung ke model akan jauh lebih baik dalam hal itu juga.
Aaron Longwell
Tetapi perbedaannya adalah bahwa link_to tidak akan tersedia dalam model, karena itu adalah pembantu ActionView. Jadi, ini tidak akan berhasil. Anda bisa meretas beberapa atribut bawaan dalam model, jadi jika tidak disetel, itu default untuk sesuatu, tetapi itu terserah Anda.
Josh Delsman
12
Dengan API layanan web tumbuh, sering kali model perlu menghubungi sumber daya eksternal dengan data mereka sendiri dan menyediakan URL panggilan balik. Misalnya, objek foto perlu memposting dirinya ke Socialmod, yang akan memanggil kembali ke URL foto itu ketika moderasi dilakukan. Kait after_create () masuk akal untuk dikirim ke Socialmod, tetapi bagaimana Anda tahu URL panggilan balik? Saya pikir jawaban penulis sendiri masuk akal.
JasonSmith
3
Meskipun Anda benar dalam pengertian teknis, ada kalanya orang hanya perlu sesuatu untuk bekerja tanpa harus mencukur setiap yak ada untuk menghindari melanggar beberapa pola desain suci atau apa pun, mungkin mereka perlu mendapatkan url, dan benar-benar menyimpan itu dalam database, akan berguna kemudian bagi model untuk hanya tahu bagaimana melakukan itu daripada melewati bolak-balik, kadang-kadang aturan bisa dilanggar.
nitecoder
1

Meskipun mungkin ada cara saya cenderung menjaga logika seperti itu dari Model. Saya setuju bahwa Anda tidak harus menempatkan itu dalam tampilan ( tetap kurus ) tetapi kecuali model mengembalikan url sebagai sepotong data ke controller, hal-hal routing harus berada di controller.

Ryan Montgomery
sumber
4
Re: "Kecuali jika model mengembalikan URL sebagai bagian dari data". Itulah yang terjadi di sini ... Model "memiliki" data ini, yang merupakan tautan ke URL di tempat atau di luar situs. Dalam beberapa kasus, URL dihasilkan dari Rails Routing. Dalam kasus lain, URL adalah data yang diberikan pengguna.
Aaron Longwell
0

(Sunting: Lupakan ocehan saya sebelumnya ...)

Ok, mungkin ada situasi di mana Anda akan pergi ke model atau ke url lain ... Tapi saya tidak benar-benar berpikir ini termasuk dalam model, tampilan (atau mungkin model) terdengar lebih tepat.

Tentang rute, sejauh yang saya tahu rute adalah untuk tindakan dalam pengontrol (yang biasanya "ajaib" menggunakan tampilan), tidak langsung ke tampilan. Pengontrol harus menangani semua permintaan, tampilan harus menyajikan hasil dan model harus menangani data dan menyajikannya ke tampilan atau pengontrol. Saya sudah mendengar banyak orang di sini berbicara tentang rute ke model (ke titik saya paling mulai percaya itu), tetapi seperti yang saya mengerti: rute pergi ke controller. Tentu saja banyak pengontrol adalah pengontrol untuk satu model dan sering disebut <modelname>sController(mis. "UsersController" adalah pengontrol model "User").

Jika Anda mendapati diri Anda menulis sejumlah logika yang tidak menyenangkan dalam sebuah tampilan, cobalah untuk memindahkan logika tersebut ke tempat yang lebih tepat; permintaan dan logika komunikasi internal mungkin termasuk dalam pengontrol, logika terkait data dapat ditempatkan dalam model (tetapi tidak menampilkan logika, yang mencakup tag tautan dll.) dan logika yang murni terkait tampilan akan ditempatkan dalam helper.

Stein G. Strindhaug
sumber
Katakanlah Anda memiliki model gambar. Jika Gambar memiliki URL luar yang terkait dengannya, arahkan ke URL itu. Jika tidak, arahkan ke halaman pertunjukan Gambar untuk mengunggah gambar? Hanya sebuah ide.
Josh Delsman
Bagaimana dengan contoh yang kurang umum ini: link_to "Gambar Ukuran Penuh", image.link Metode tautan dalam model akan menautkan ke URL di tempat (image_path), atau, katakanlah, URL Flickr jika pengguna menyediakannya dan .url diatur pada gambar.
Aaron Longwell