Можно ли использовать в моделях помощники маршрутизации Rails (т.е. mymodel_path (model))?

335

Скажем, у меня есть модель Rails под названием Thing. Вещь имеет атрибут url, который может произвольно устанавливать URL-адрес где-то в Интернете. В коде зрения мне нужна логика, которая делает следующее:

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

Эта условная логика в представлении является уродливой. Конечно, я мог бы создать вспомогательную функцию, которая изменила бы представление на это:

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

Это решает проблему многословности, но я бы предпочел иметь функциональность в самой модели. В этом случае код представления:

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

Это, очевидно, потребует метода ссылок на модель. Здесь он должен содержать:

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

В вопросе вопроса thing_path() является методом undefined внутри кода модели. Я предполагаю, что можно "втягивать" некоторые вспомогательные методы в модель, но как? И есть ли реальная причина, что маршрутизация работает только на контроллере и просматривает уровни приложения? Я могу придумать множество случаев, когда код модели может потребоваться для обработки URL-адресов (интеграция с внешними системами и т.д.).

  • 0
    Вариант использования: создать сокращенный URL из goo.gl после сохранения,
  • 2
    Вы, вероятно, должны обернуть свою модель в презентатор, если вы хотите добавить логику представления, это будет держать слои MVC разделенными. Смотрите Дрейпер ( github.com/jcasimir/draper ).
Показать ещё 1 комментарий
Теги:
rails-routing

7 ответов

642

В Rails 3, 4 и 5 вы можете использовать:

Rails.application.routes.url_helpers

например

Rails.application.routes.url_helpers.posts_path
Rails.application.routes.url_helpers.posts_url(:host => "example.com")
  • 14
    Ypu может включить это в любой модуль, и после него вы можете получить доступ к url-помощникам там. Спасибо
  • 37
    Спасите себя от копирования вашей опции :host везде и установите ее один раз в конфигурационных файлах вашей среды: Rails.application.routes.default_url_options[:host] = 'localhost:3000'
Показать ещё 10 комментариев
175

Я нашел ответ о том, как это сделать сам. Внутри кода модели просто поставьте:

Для Rails <= 2:

include ActionController::UrlWriter

Для Rails 3:

include Rails.application.routes.url_helpers

Это волшебным образом возвращает thing_path(self) URL-адрес текущей вещи или other_model_path(self.association_to_other_model) возвращает другой URL-адрес.

  • 27
    Просто обновление, поскольку вышесказанное устарело. Это текущий include: include Rails.application.routes.url_helpers
  • 2
    Я получаю NoMethodError (неопределенный метод optimize_routes_generation? Для # <ActionView :: Base: 0x007fe8c0eecbd0>), когда я пытаюсь это сделать
116

Вы также можете найти следующий подход более чистым, чем включать все методы:

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

  def url
    url_helpers.thing_path(self)
  end
end
  • 7
    Документация по этому вопросу казалась трудной для поиска, так что здесь есть достойная ссылка на использование делегата в ActiveSupport. simonecarletti.com/blog/2009/12/inside-ruby-on-rails-delegate
  • 2
    Я получаю undefined local variable or method 'url_helpers' for Event:Class error ... :(
Показать ещё 3 комментария
13

Любая логика, связанная с тем, что отображается в представлении, должна быть делегирована вспомогательному методу, так как методы в модели строго предназначены для обработки данных.

Вот что вы могли бы сделать:

# 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) %>
  • 1
    Что мне не нравится в вспомогательных методах в этом случае: когда я смотрю на link_to_thing (), я должен решить, является ли это вспомогательным средством для конкретной вещи или для всего приложения (это легко может быть либо). Мне нужно рассмотреть возможность проверки 2 файлов на источник. thing.link не оставляет вопросов о исходном файле.
  • 0
    Кроме того, что если мне понадобится использовать эту функциональность в задаче Rake (возможно, для экспорта файла CSV с URL-адресами вещей) ... в этом случае было бы гораздо лучше перейти непосредственно к модели.
Показать ещё 4 комментария
1

Несмотря на то, что может быть способ, я хотел бы оставить такую ​​модель из Модели. Я согласен, что вы не должны помещать это в представление (держать его тощим), но если модель не возвращает URL-адрес в виде части данных к контроллеру, материал маршрутизации должен находиться в контроллере.

  • 4
    Re: «Если модель не возвращает URL как часть данных». Это именно то, что здесь происходит ... Модель «владеет» этим фрагментом данных, который является либо ссылкой на локальный, либо на внешний URL-адрес. В некоторых случаях URL генерируется из Rails Routing. В других случаях URL является предоставленными пользователем данными.
0

Мне очень нравится следовать чистому решению.

class Router
  include Rails.application.routes.url_helpers

  def self.default_url_options
    ActionMailer::Base.default_url_options
  end
end

router = Router.new
router.posts_url  # http://localhost:3000/posts
router.posts_path # /posts

Это основано на http://hawkins.io/2012/03/generating_urls_whenever_and_wherever_you_want/

0

(Edit: Забудьте о моем предыдущем болтовне...)

Хорошо, могут быть ситуации, когда вы переходите либо к модели, либо к другому URL-адресу... Но я действительно не думаю, что это принадлежит модели, представление (или, может быть, модель) звучит более подходящим.

О маршрутах, насколько я знаю, маршруты предназначены для действий в контроллерах (обычно "магически" использует представление), а не непосредственно к представлениям. Контроллер должен обрабатывать все запросы, представление должно представлять результаты, и модель должна обрабатывать данные и подавать их на просмотр или контроллер. Я слышал, как много людей говорили о маршрутах к моделям (до такой степени, что я начинаю все это начинать), но, насколько я понимаю, маршруты идут к контроллерам. Конечно, многие контроллеры являются контроллерами для одной модели и часто называются <modelname>sController (например, "UsersController" является контроллером модели "Пользователь" ).

Если вы обнаружите, что пишете довольно неприятные количества логики в представлении, попробуйте переместить логику где-то более уместно; логика запроса и внутренней связи, вероятно, принадлежит контроллеру, логика, связанная с данными, может быть помещена в модель (но не логика отображения, которая включает в себя теги ссылок и т.д.), а логика, которая является чисто связанной с отображением, будет помещена в помощник.

  • 0
    Скажем, у вас есть модель изображения. Если с изображением связан внешний URL-адрес, укажите этот URL-адрес. В противном случае, указать страницу показа изображения, чтобы загрузить изображение? Просто идея.
  • 0
    Как насчет этого менее общего примера: link_to "Изображение полного размера", image.link Метод ссылки в модели будет либо ссылаться на локальный URL (image_path), либо, скажем, на Flickr URL, если пользователь предоставил один и .url был установлен на изображении.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню