15

Because of how the different gems interact in my system, I have an engine mounted onto a rails application. I recently started working on a new gem that provides some middleware functionality.

Sort of like this:

BaseApp
\
  Engine
  \
   NewMiddlewareEngine

# BaseApp/Gemfile
gem 'Engine'

# Engine/Gemfile
gem 'NewMiddlewareEngine'

# rake middleware output:
user@laptop[BaseApp]$ bundle exec rake middleware
use Rack::Sendfile
use ActionDispatch::Static
use Rack::Lock
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x6ebf30e1>
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::RemoteIp
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use CatchJsonParseErrors
use ActionDispatch::ParamsParser
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
run BaseApp::Application.routes

However, I can't seem to get the NewMiddlewareEngine to show up in middleware. I've tested mounting this:

BaseApp
\
 NewMiddlewareEngine

# BaseApp/Gemfile
gem 'NewMiddlewareEngine'

# rake middleware output:
user@laptop[BaseApp]$ bundle exec rake middleware
use Rack::Sendfile
use ActionDispatch::Static
use Rack::Lock
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x2f9795d8>
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::RemoteIp
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use CatchJsonParseErrors
use ActionDispatch::ParamsParser
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
use NewMiddlewareEngine    # Notice it mounts fine on it's own
run BaseApp::Application.routes    

And:

BaseApp
\
 Engine

# BaseApp/Gemfile
gem 'Engine'

# rake middleware output:
user@laptop[BaseApp]$ bundle exec rake middleware
use Rack::Sendfile
use ActionDispatch::Static
use Rack::Lock
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x6ebf30e1>
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::RemoteIp
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use CatchJsonParseErrors
use ActionDispatch::ParamsParser
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
run BaseApp::Application.routes

Which both work fine. The issue is when I try to mount the NewMiddlewareEngine 'through' the Engine.

Does anyone know how to configure something like this?

Here's MyMiddlewareEngine mounting:

module MyMiddlewareEngine
  class Railtie < Rails::Railtie
    initializer "add_my_middleware_engine_route_middleware" do |app|
      app.middleware.use 'MyMiddlewareEngine'
    end
  end
end
SortingHat
  • 727
  • 3
  • 15
  • 30
  • Can you provide the output of `rake middleware` – Tawan Jan 29 '16 at 19:43
  • Yeah, for sure. I updated to include middleware output for all 3 variations. – SortingHat Jan 29 '16 at 22:25
  • Have you tried `config.middleware.use NewMiddlewareEngine` in the other engine's initializer? That engine's initializer is definitely being run, so that might be where you need to put the code for it to work. This shouldn't require any changes to the application code. – Isaac Betesh Feb 09 '16 at 01:51

1 Answers1

3

From Rails::Engine Edge API documentation:

 class MyEngine < Rails::Engine
   # Add a load path for this specific Engine
   config.autoload_paths << File.expand_path("../lib/some/path", __FILE__)

   initializer "my_engine.add_middleware" do |app|
     app.middleware.use MyEngine::Middleware
   end
 end

I tend to configure rails middleware to run at the application level and not in an engine (as a separation of responsibilities). I used this approach to separate authentication from a mounted rails application (see example). The configuration for this is in application.rb:

 module MyApp
   class Application < Rails::Application
     ..
     config.middleware.use NewMiddlewareEngine

Do you see any exceptions when you run rails console? Did you try using the constant name like the above instead of a String?

  • Is that application.rb from your main application? I wanted to be able to just have the 1 line in the Gemfile to have my engine included as opposed to any other edits. And I tried to use a railtie for the reason you mentioned. I think a railtie will allow configuration across the application. The weird thing is I'm putting an Engine in an Engine which finally runs on an application (I tried to show that with the diagram). – SortingHat Feb 03 '16 at 16:38
  • I think you're right in the sense that this needs to be configured on the application level. Because what seems to be happening is the railtie initializes what it's mounted on, but when it's a second layer deep the railtie initialization is never called. MyMiddlewareEngine needs to somehow be configured for the main Application and not the Engine it's mounted into. – SortingHat Feb 03 '16 at 16:58
  • So if I leave my structure of 'App with Engine with MiddlewareEngine' and in App/config/application.rb add 'config.middleware.use NewMiddleWareEngine' I get 'NameError: uninitialized constant App::Application::MiddlewareEngine'. So the setup isn't propagating up to the main App. I'm going to try messing with the includes. – SortingHat Feb 03 '16 at 17:22
  • It sounds like you are missing one or more `require`s – Nick Aschenbach Feb 05 '16 at 05:28