Пространство имен Rails против вложенных ресурсов

Скажем, мое приложение имеет две модели: Foo и Bar.

Foo необязательно принадлежит_бар.

Прямо сейчас я могу посмотреть на один Foo или искать конкретный Foo, и FoosController обрабатывает все это. Мои URL-адреса: foos/1 и foos/new

Иногда я хочу посмотреть бар. BarsController обрабатывает это, и я получаю к нему, как: bars/1 или bars/1/edit.

Если я смотрю на панель, я, возможно, захочу просмотреть все фоны, которые являются частью этого бара. Итак, я хотел бы использовать bars/1/foos/ для просмотра этих Foos.

Это довольно просто с вложенными ресурсами, и выглядит так:

resources :foo
resources :bar do
  resources :foo
end

Тем не менее, Foos, которые являются частью бара, являются особыми, отличными от обычных Foos. Так, например, если я загружаю foos/1 или bars/1/foos/1, я бы посмотрел на тот же Foo, но в каждом случае я сосредоточен на различной информации.

Итак, я думал о том, что BarFoos Controller обрабатывает Foos, когда они находятся в контексте Bar. Однако, если я вставлю BarFoos под Bar, тогда мои помощники будут похожи на bar_bar_foos_path и new_bar_bar_foo_path. Это кажется излишним.

Итак, теперь я думаю об пространствах имен, чего я раньше никогда не рассматривал. Я вижу в направляющих рельсов, которые я мог бы определить:

namespace "bar" do
  resources :foos
end

Если я это сделаю, я могу сделать второй FoosController под app/bar/ и что FoosController может обрабатывать Foos внутри бара с помощью приятных помощников, таких как bar_foo_path(:id) вместо bar_bar_foo_path(:id).

Но если я это сделаю, что произойдет с моим BarsController? Как запросы перенаправляются на BarsController, если вместо resources :bars у меня есть namespace "bar"?

И, наконец, есть ли что-то особенное в моем второстепенном FoosController, чтобы убедиться, что имя не конфликтует с FoosController верхнего уровня? Я понимаю, что маршрутизация говорит "пространство имен", но как остальные коды ruby ​​знают, что app/bar/foos_controller и app/foos_controller не являются одним и тем же классом?

Спасибо!

Ответ 1

Я думаю, что вы пытаетесь достичь:

  • В баре есть много Foos
  • Просмотр фонов, принадлежащих Bar
  • Посмотреть все Foos, независимо от родителя.

Вы можете добиться этого с помощью: routes.rb:

resources :foos
resources :bars do
  resources :foos, :controller => 'bars/foos'
end

Помощники маршрута, с которыми вы закончили:

  • bar_path
  • foos_path
  • bars_foos_path
  • и т.д., "рейк-маршруты" для остальных =)

В сущности, вы получаете:

  • приложение /BarsController (рельсы g панели управления)
  • приложение /FoosController (rails g controller foos)
  • приложение/бары/FoosController (rails g control bars/foos)

В FoosController вы получите доступ к foos, как обычно, с помощью

@foos = Foos.all

а в барах /FoosController вы получите доступ к панели с помощью:

@foos = @bar.foos

где бар может быть предварительно извлечен в контроллере bars/foos с помощью:

before_filter :get_client

private
def get_client
  @bar = Bar.find(params[:bar_id])
end

Надеюсь, это поможет. =)

Edit: Что касается маршрутов с именами, я лично использовал их, когда некоторые из моих ресурсов извлекались из подкаталога. Например, если у меня есть раздел администратора моего сайта, то у меня может быть следующее:

routes.rb:

namespace :admin do
  resources :foos
end

и я создаю свой контроллер с помощью:

rails g controller admin/foos

Это устанавливает мой ресурс foos, так что я могу получить к нему доступ на "url сайта" /admin/foos, а также получить помощники, такие как admin_foos_path.

Ответ 2

Есть минусы этого подхода.

Если вы объявляете константу, например. CONST_NAME, в вложенном ресурсе foos, рельсы будут вызывать исключение "uninitialized constant:: Foo:: CONST_NAME" из-за его алгоритма области видимости.

Чтобы избежать такого поведения, используйте:

resources :foos
resources :bars do
  scope :module => "bar" do
    resources :foos #, :controller => 'bar/foos' no need to use this now because route will be searched there by default
  end
end

Теперь вы не получите исключение при использовании:

Foo::CONST_NAME

или

Bar::Foo::CONST_NAME