Where do you put your Rack middleware files and requires?

蓝咒 提交于 2019-11-28 17:26:05
Mike Jarema

As of Rails 3.2, Rack middleware belongs in the app/middleware directory.

It works "out-of-the-box" without any explicit require statements.

Quick example:

I'm using a middleware class called CanonicalHost which is implemented in app/middleware/canonical_host.rb. I've added the following line to production.rb (note that the middleware class is explicitly given, rather than as a quoted string, which works for any environment-specific config files):

config.middleware.use CanonicalHost, "example.com"

If you're adding middleware to application.rb, you'll need to include quotes, as per @mltsy's comment.

config.middleware.use "CanonicalHost", "example.com"
Chris Heald

You can put it in lib/tableized/file_name.rb. As long as the class you're trying to load is discoverable by its filename, Rails will automatically load the file necessary. So, for example:

config.middleware.use "MyApp::TotallyAwesomeMiddleware"

You would keep in:

lib/my_app/totally_awesome_middleware.rb

Rails catches const_missing and attemts to load files corresponding to the missing constants automatically. Just make sure your names match and you're gravy. Rails even provides nifty helpers that'll help you identify the path for a file easily:

>> ChrisHeald::StdLib.to_s.tableize.singularize
=> "chris_heald/std_lib"

So my stdlib lives in lib/chris_heald/std_lib.rb, and is autoloaded when I reference it in code.

In my Rails 3.2 app, I was able to get my middleware TrafficCop loading by putting it at app/middleware/traffic_cop.rb, just as @MikeJarema described. I then added this line to my config/application.rb, as instructed:

config.middleware.use TrafficCop

However, upon application start, I kept getting this error:

uninitialized constant MyApp::Application::TrafficCop

Explicitly specifying the root namespace didn't help either:

config.middleware.use ::TrafficCop
# uninitialized constant TrafficCop

For some reason (which I've yet to discover), at this point in the Rails lifecycle, app/middleware wasn't included in the load paths. If I removed the config.middleware.use line, and ran the console, I could access the TrafficCop constant without any issue. But it couldn't find it in app/middleware at config time.

I fixed this by enclosing the middleware class name in quotes, like so:

config.middleware.use "TrafficCop"

This way, I would avoid the uninitialized constant error, since Rails isn't trying to find the TrafficCop class just yet. But, when it starts to build the middleware stack, it will constantize the string. By this time, app/middleware is in the load paths, and so the class will load correctly.

For Rails 3:

#config/application.rb
require 'lib/rack/my_adapter.rb'
module MyApp
  class Application < Rails::Application
    config.middleware.use Rack::MyAdapter
  end
end

I'm not aware of a convention, but why not put it in the /lib directory? Files in there get automatically loaded by Rails.

You could create an initializer which requires the necessary files and then leave the files wherever you want.

According to this the initializers are executed before the rack middleware is loaded.

The working solution I have so far is moving the middleware requires to config/middleware.rb and requiring that file in environment.rb, reducing it to a single require which I can live with.

I'd still like to hear how other people have solved this seemingly basic problem of adding middleware to Rails.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!