Rails router: Marking path as static (public) - ruby-on-rails

Is there any way (middleware, etc) to tell the rails router that any route beginning with /photos should be considered as static data and should only be served from the /public/photos folder?
What i mean is that if the resource is not found, a 404 should be returned directly, without loading all rails stack.
This is for my dev env (single threaded mongrel), I don't want to use a front-end server.

You can define a Rack::Middleware Rack::Static (http://rack.rubyforge.org/doc/classes/Rack/Static.html)
use Rack::Static, :urls => ["/photo"]

Related

How do I prevent my routes.rb from intercepting requests coming to /blog?

My /blog directory is just a bunch of static HTML files. That's good.
When I go to localhost/blog it works fine - it renders the index.html for my middleman generated blog. Great.
But when I click on any of the posts, it gives me a routing error:
No route matches [GET] "/blog/2015/03/11/hello_world"
I am pretty sure the reason this is happening is because of one of these rules in my routes.rb:
get '/:friendly_id', to: 'posts#show'
get '/rbt/:name', to: redirect {|path_params, _| "/#{path_params[:name].gsub(/^\d+\-/, '')}" }
get ':name', to: 'posts#show'
I need all of these routes, but I don't want an HTML request to hit my Rack middleware unnecessarily....or worse yet, do a DB query which this error seems to suggest is happening.
How do I confine all requests to /blog/ to just resolve to my public/blog/ directory?
Edit 1
I realize the above description may not be clear. My Rails App isn't a blog, and so the posts you see referenced above, are not posts to the blog. They are posts of another kind, separately managed by the Rails app with a DB and all. I have since added a real /blog which will just be a collection of HTML articles generated by MiddleMan that will sit in my Rails /public/blog folder. The idea being that the HTML files in my /blog directory, should not hit my Rack middleware at all.
You can force rack to serve certain folder as static and routes-ignoring by adding config.middleware.use Rack::Static, urls: ['/blog'], root: 'public' to config/application.rb but imho it's better to setup a web server to intercept and serve /blog earlier than your app does.
And also in your case /blog/2015/03/11/hello_world seems to be a directory name, if you add index.html to the link it should work as you expect, without changing any configuration.
When your app is run by webserver, its webroot will be the public directory. So, if you have your blog directory inside public directory this should work. localhost/blog
Try running with nginx with following config :
root /root/path/to/your_app/public;
Couldn't you create a static_url controller and configure your routes so that
get '/blog', to: 'static_url#show'
Then in your controller have
def show
render file: request.fullpath
end
Sorry I can't test right now but I'm sure you get the idea. Also, I guess you would need to detect non-existent pages.

How to override public folder path in Rails 4?

I would like to use a different public folder from a parent directory called client which contains the entire AngularJS app. Essentially I want to tell Rails to load AngularJS app and the only job that Rails has to do is serve JSON.
Is that possible in Ruby on Rails?
As others have mentioned, it may or may not be a great idea to override the existing paths['public'] folder. But you can do the following safely in somewhere like application.rb:
Rails.application.config.middleware.insert_after(
ActionDispatch::Static,
ActionDispatch::Static,
Rails.root.join("client").to_s,
Rails.application.config.static_cache_control
)
The public folder is exposed to the web server through the Rack middleware ActionDispatch::Static. There's nothing else special about it, and the above code simply adds another instance of the middleware that points to the directory client. So in the above case, the browser would be able to access everything in public as well as client.
Just had to do it myself for an Angular app.
Put this in your application.rb:
config.serve_static_files = true
paths['public'] = File.join 'client', 'app'
Or if you still use asset pipeline (config.assets.enabled = true):
paths['public/javascripts'] = File.join 'client', 'app', 'scripts'
paths['public/stylesheets'] = File.join 'client', 'app', 'styles'
Would be interesting to know if there are any consequences with the second bit as my front-end is served completely separately thus I keep asset pipeline switched off and use grunt instead.
You can define another path like
# config/application.rb
paths['my_website'] = 'website'
Then you can use this path in your routes like
# routes.rb
get '/my_website', to: redirect('my_website/index.html')

Prepend path prefix to all rails routes

I have a setup where nginx serves a rails application inside a specific subfolder
eg. http://myserver/railsapp/ and everything inside gets proxied to rails, if the first subfolder is different, it servers static files from another folder.
I haven't been able to find how to specify this behaviour in rails in an intelligent way. I mean, what I want is to specify an option like Rails.server_prefix = /railsapp so that all the routes get prepended automagically, both on the incoming requests and on the generated links.
You probably want to use the router's scope method with the :path argument:
Rails.application.routes do
scope(:path => '/railsapp') do
# the rest of your routes go here
end
end
See the docs for more info.

Render static files in /doc in Rails

So far I have in config/routes.rb:
match 'doc/:path' => 'doc#show'
And in app/controllers/doc_controller.rb:
class DocController < ApplicationController
layout false
def show
render File.join( RAILS_ROOT, 'doc', params[:path] )
end
end
This works find for index.html and other .html files. But it doesn't serve up .css and .js files. It also doesn't serve nested files and directories such as /doc/metrics/output/index.html
How can I get Rails to serve up all static files in /doc but without simply putting a link to them in /public (so that I can autheticate the user in the controller first)?
I would recommend not serving the files through Rails at all. Serve them through your server (Nginx, Apache). You can use the X-Accel-Redirect and the X-Sendfile headers to tell Nginx and Apache to send the static file instead. The benefit of this approach is that you can still authenticate a user before allowing them access to the file. Here's the Nginx tutorial:
http://ablogaboutcode.com/2010/11/19/serving-static-files-passenger-nginx/
Another option is to setup your routes like this:
match 'doc' => 'doc#show'
And pass your path as a parameter so you don't have to do nested URL matching in your routes, or handle special cases (.css, .js, .html, ...)
/doc?path=/path/to/my/document.css

What is the replacement for ActionController::Base.relative_url_root?

I am porting a 2.x rails app to rails3; we'll call it foo-app. Foo-app is one section of a larger rails app and lives at main_rails_app.com/foo-app. Previously we just set up the following in our foo-app production config to ensure that our foo-app routes worked properly:
ActionController::Base.relative_url_root = "/foo-app"
However, with rails3, I now get:
DEPRECATION WARNING: ActionController::Base.relative_url_root is ineffective. Please stop using it.
I have since changed the config entry to the following:
config.action_controller.relative_url_root = "/foo-app"
This mostly works in that all calls to external resources (javascript/css/images) will use /foo-app. However, none of my routes change appropriately, or put another way, foo-app root_path gives me '/' when I would expect '/foo-app'.
Two questions:
What is the replacement for ActionController::Base.relative_url_root
if it is config.action_controller.relative_url_root, then why are my routes not reflecting the relative_url_root value I set?
You should be able to handle all that within the routes.rb file. Wrap all your current routes in scope; for instance.
scope "/context_root" do
resources :controller
resources :another_controller
match 'welcome/', :to => "welcome#index"
root :to => "welcome#index"
end
You can then verify your routing via the rake routes they should show your routes accordingly, including your context root(relative_url_root)
If you deploy via Passenger, use the RackBaseURI directive: http://www.modrails.com/documentation/Users%20guide%20Apache.html#RackBaseURI
Otherwise, you can wrap the run statement in your config.ru with the following block:
map ActionController::Base.config.relative_url_root || "/" do
run FooApp::Application
end
Then you only have to set the environment variable RAILS_RELATIVE_URL_ROOT to "/foo-app". This will even apply to routes set in gems or plugins.
Warning: do not mix these two solutions.
I feel like I must be over-complicating this and/or missing something, but this issue has been frustrating me for a while now, and here are my notes.
Summary
There are two separate issues with two points each for dynamic and static routes:
how to get routing to correctly match an incoming URL
for routes
for static files
how to generate URLs that include the relative_root
via url helpers
for static assets
One way to solve all four points:
Configure Nginx to strip the relative_root portion
This solves route matching; just write routes expecting URLs at / like development
Also static files are served as in development
Set RAILS_RELATIVE_URL_ROOT environment variable
This solves generated static asset helpers
Use the ScriptName middleware below (modify it to use the value from the environment)
This solves generated url helpers, e.g. users_path
Wrapping the Rails application in Rack::URLMap in config.ru (Christoph's answer)
# config.ru
map '/relative_root' do
run Myapp::Application
end
requires incoming URL contain the relative_url_root (Nginx can be configured to remove or retain this; see below)
Rack appends the relative_url_root to the Rack env SCRIPT_NAME rack/urlmap.rb:62
Rails adds the current request's SCRIPT_NAME to url_for options metal/url_for.rb:41
Rails' url_for prepends the script name when generating paths routing/url_for.rb:133
So that covers URLs generated by the url helpers, e.g. given UserController, users_path will be prefixed by the relative url root.
Set SCRIPT_NAME in middleware
# config.ru
class ScriptName
def initialize(app, name)
#app = app
#name = name
end
def call(env)
env['SCRIPT_NAME'] += #name
#app.call(env)
end
end
use ScriptName, '/relative_root'
run Rails.application
Has same effect as above, but
Requires that incoming URL NOT contain the relative_url_root
Setting RAILS_RELATIVE_URL_ROOT
value is saved in app.config.relative_url_root configuration.rb:41
which in turn affects asset paths asset_url_helper.rb:137
but that's it as far as I see
notably does not affect url helpers
Setting config.action_controller.relative_url_root
?? May affect assets compilation?
Overrides RAILS_RELATIVE_URL_ROOT env var?
Explicitly defining all routes under /relative_root (rizzah's answer)
# config/routes.rb
Myapp::Application.routes.draw do
scope '/relative_root' do
...
end
end
Url helpers will generate correct urls
Incoming URL must contain the relative url root (sensitive to Nginx configuration, see below), else "no route matches" exceptions
URLs requesting static assets, e.g. /relative_root/images/logo.png will result in "no route matches" exceptions. This may not be an issue if nginx is serving static assets anyway.
Nginx config
Given a config like this:
upstream myapp {
server localhost:3000;
}
server {
...
location /relative_root {
proxy_pass http://myapp/;
}
}
Nginx will strip out the /relative_root, and the Rails app will not see it. If you need the Rails app so see it, one way is to change the proxy_pass line:
...
proxy_pass http://myapp/relative_root/;
...

Resources