Rails configure application to have url prefix - ruby-on-rails

I feel like this should not be all that difficult, but I cannot find any solutions that seem to work with Rails4
My setup
I have a proxy server (Kong) that directs to various services behind it based on path.
myapp.com/app1/ is redirected to http://app1_address:PORT/ (notice /app1 is stripped)
same for myapp.com/app2
app2 is a Rails 4 application and it works just fine when browsing to specific, but its relative routing is completely off. For example, link_to or url_for links to controllers or actions all redirect to the wrong links.
For example, I have a logout link that has a path of /logout on app2, but redirecting the user to /logout is incorrect. They need to be routed to /app2/logout. How can I configure the Rails app to add a prefix to all routes?
I have tried:
config.action_controller.relative_url_root = '/app2'
As well as this:
config.relative_url_root = '/app2'
And this in config.ru
map <MyAppName>::Application.config.relative_url_root || "/" do
run Rails.application
end
Any ideas for how to make this work?

You should use a scope in your routes:
Rails.application.routes.draw do
scope ENV["PROXY_PATH"] do
# your routes..
end
end
This way you'll have to set the ENV variable:
$ export PROXY_PATH=app2
$ bin/rails s

Related

Vue.js router with Ruby on Rails routes

I'm currently working on a application with a Ruby on Rails backend and Vue.js frontend. It's a single page application. I'm using the webpacker gem to build my main JS file.
I've added vue-router and and a couple frontend routes. I'm able to navigate using <router-link> which correctly renders the appropriate component. What I can't figure out is how do I setup my frontend routes so that someone can directly navigate to a URL without hitting my Rails routes.
For example if I type /sample-route I'd like to hit my Vue.js route and not my Rails route. I still want to be able to make API calls to my Rails routes as well. For example /api/users.
My problem was solved by adding <router-view></router-vew> into my main Vue.js component.
Here's my rails routes file as well:
Rails.application.routes.draw do
namespace :api do
# api routes here
end
root 'application#index'
get '/*path', to: 'application#index'
end
Depending on how many routes you have, you can add your Vue routes to routes.rb and send them to your root Vue application route. e.g. Webpacker is rendering your js pack with controller#action vue_controller#app. Your Vue app router uses /user/profile. In routes.rb, add a route:
get "/user/profile" => "vue_controller#app" # <- The controller action rendering your Vue pack
If it seems unmaintainable to redefine every Vue route in routes.rb, you may want to look into a universal fallback route that sends them to the vue app controller action. As long as you don't change the route, but just respond with the Vue controller action, the Vue router will take care of rendering the right components for that route when the page loads.
In Rails 4, you can use something like the answers in this SO question to help you out with setting up a "catch-all" route Rails catch-all route.
EDIT: Using a catch all route does lead to some problems with 404's. If the user requests a route that doesn't exist, the Rails app will still send them the Vue pack. To fix this, you would need to add some unknown route handling in your Vue router to render something like a 404 page.
There is one more approach which will be useful and handles sub-routes as well (having separate vue apps per page in a single rails app):
routes.rb
Rails.application.routes.draw do
# this route entry will take care of forwarding all the page/* urls to the index
get 'page_controller/*path', to: 'page_controller#index', format: false
end
Additionally, please handle the api and index routes separately based on the design.
vue-routes.js
const router = new VueRouter({
mode: 'history',
base: '/page_url',
routes: [
...
]
});

Ember-cli-rails No route match for ember's paths

In a Rails and Ember project, I decided to use EmberCLI Rails because I want to do integration tests with Capybara and all my favorite testing gems.
I installed it and it works when I go to the home page.
I added routes on ember like this :
import Ember from 'ember'
import config from './config/environment'
Router = Ember.Router.extend(location: config.locationType)
Router.map ->
#resource 'users'
export default Router
When I go on http://localhost:3000/users, I have a no route matches error. I understand why this is happening, Rails does not load routes of embers. Is there a solution to do it or is it just impossible with EmberCli-Rails for now?
Your Rails app needs a wildcard route so it knows to handle those requests through your Ember app controller. Can you try adding a route in routes.rb:
get '/:all', to: "ember#index"
substituting ember#index with whatever you have set up as the controller and action for your Ember app.

Routing a folder to another URL

I Recently migrated a website from PHP to rails. NEwt to the website they was a wordpress that is staying. the urls were as follow :
www.thedomain.com / #former PHP now rails
www.thedomain.com/blog/ #the wordpress.
now that the website is in Rails, i cannot just put a php wordpress in the blog folder.
I tried to go to a blog.thedomain.com solution using thoses route (actual domain names i use in dev as i am trying to make it work) :
namespace :blog do
match'', to: redirect {|params,request| "http://blog.fakefake1111/#{params[:path]}#{request.path}"}, via: :all
match '*path', to: redirect {|params,request| "http://blog.fakefake2222/#{params[:path]}"}, via: :all
end
It doesnt quite work, as the params are ignored and the first one add /blog/ at the end of the URL.
How can i make it work?
I will use the alias capability of apache to do it. I do not really need to use rails for that.

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.

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