динамически перехватывать и вызывать Ruby on Rails Routes в Rails Engine

В настоящее время я работаю над движком Rails, который будет дублировать маршрутизацию хост-приложения в определенной области. Таким образом, если маршрут get '/posts', to: 'posts#index', as: 'posts' существует в исходном приложении, этот маршрут также должен быть доступен под креплением двигателя get '/mount/posts', но должен указывать на то же действие Contoller # в хост-приложении. Однако у меня нет доступа к окончательному приложению, поэтому маршруты нужно генерировать динамически.

Вот мой нынешний подход, но, может быть, он работает лучше по-другому?

# lib/embed_me.rb
require "embed_me/engine"
module EmbedMe
  # defines a scoped route under which the embedded content can be found
  mattr_accessor :scope_name, default: :embed
end


# lib/embed_me/engine.rb
module EmbedMe
  class Engine < ::Rails::Engine
    isolate_namespace EmbedMe

    initializer "embed_me", before: :load_config_initializers do |app|
      Rails.application.routes.append do
        mount EmbedMe::Engine, at: EmbedMe.scope_name
      end
    end
  end
end


# config/routes.rb
EmbedMe::Engine.routes.draw do
  match '/*path', to: 'application#index', via: :all
end


# app/controllers/embed_me/application_controller.rb
module EmbedMe
  class ApplicationController < ActionController::Base
    protect_from_forgery with: :exception

    def index
      # will validate existance of route, or raise ActionController::RoutingError
      path = Rails.application.routes.recognize_path(params[:path])
      controller = path[:controller]
      action = path[:action]

      # how to call original action?
    end
  end
end

Я не могу использовать redirect_to("/#{params[:path]}"), потому что это изменит URL-адрес на URL-адрес без области действия. Я не могу использовать render(action: action, controller: controller), потому что будут отображаться только соответствующие представления, но логика в контроллере будет пропущена. (или я сделал что-то в корне неправильно?) Я не могу использовать main_app.resource_path, потому что маршруты не статичны и варьируются от приложения к приложению.

Я также просмотрел ссылку и Ссылка , но тоже не мог заставить это работать


person tobiasbhn    schedule 14.12.2020    source источник
comment
Это не жизнеспособный путь вперед. Rails не предназначен для внутренней пересылки от одного контроллера к другому, и это превратило бы отладку в настоящий ад. Я предполагаю, что вы могли бы сделать так, чтобы ваш движок прочитал файл маршрутов, как это делают рельсы, и создал измененную коллекцию маршрутов и каким-то образом вставил ее обратно в хост-приложение. Вероятно, это потребует огромного количества работы и углубления во внутренности. Я бы порекомендовал вам начать с чтения medium.com/rubyinside/.   -  person max    schedule 15.12.2020
comment
Спасибо. Посмотрю по ссылке   -  person tobiasbhn    schedule 15.12.2020


Ответы (1)


Так что у меня получилось более-менее работать. Я не нашел решения именно этой проблемы, поэтому пошел на небольшие компромиссы. А именно, я решил, что администратор хост-приложения должен внести небольшое изменение в маршруты. По сути, теперь я использую область видимости, как в следующем примере.

scope path: "/#{EmbedMe.scope_name}", as: EmbedMe.scope_name, is_embedded: true

Чтобы максимально упростить для пользователя движка, я интегрировал функцию настраиваемого маршрута, которая обрабатывает область видимости маршрутов.

# lib/embed_me/rails/routes.rb
module ActionDispatch::Routing
  class Mapper
    def embeddable
      # note that I call the yield function twice, see describtion below
      yield
      scope path: "/#{EmbedMe.scope_name}", as: EmbedMe.scope_name, is_embedded: true do
        yield
      end
    end
  end
end


# [Host Application]/config/routes.rb
Rails.application.routes.draw do
  # not embeddable
  get '/private', to: 'application#private'

  # is embeddable
  embeddable do
    get '/embeddable', to: 'application#embeddable'
  end
end


# output of $rails routes
...
private           GET   /private(.:format)            application#private
embeddable        GET   /embeddable(.:format)         application#embeddable
embed_embeddable  GET   /embed/embeddable(.:format)   application#embeddable {:is_embedded=>true}
...

Обратите внимание, что я дважды вызываю функцию yield: один раз для нормального получения маршрутов и один раз в области видимости. Таким образом, я также получаю помощники по нескольким путям для обычной и встроенной ссылки.

Создание пользовательского метода маршрута выполняется аналогично Devise. См. исходный код

person tobiasbhn    schedule 15.12.2020