RubyOnRails: url_for application root - ruby-on-rails

i know that doing
url_for(:only_path => false, :controller => 'home')
I will get, for example, http://localhost/home
But how do i handle to genereate http://localhost

This is an old question, but it still ranks high in searches. Currently, use root_url.
e.g.
<%= link_to "fully qualified root", root_url %>
will generate
fully qualified root

to get http://localhost, you'll simply:
<%= link_to "Home", root_path %>
That'll generate: Home which will effectively link to http://localhost

Depending on what your goals are, there are a few ways to use the server name or base URL. For the general case of, "I just need a reliable base URL that I can use anywhere," I use the config method.
# via routes.rb
map.root :controller => "foo", :action => "bar"
# view/controller:
root_url # inflexible. root_url will only ever be one URL
# via request object
url_for("http://"+request.host) # not available in models
# via config file (see railscast 85)
# environment.rb
APP_CONFIG = YAML.load_file("#{RAILS_ROOT}/config/config.yml")[RAILS_ENV]
# config/config.yml
development:
server_name: localhost:3000
production:
server_name: foo.com
# view/controller:
url_for(APP_CONFIG('server_name'))

You can also use:
ActionController::Base.relative_url_root
i.e. #{ActionController::Base.relative_url_root}/images/my_img.jpg

Related

Rails 4 url_for with host constraint

We created a multi tenancy application with multiple sites and each site is able to modify the routes from a backend.
in routes.rb we load the dynamic routes for all sites and put them into a host constraint like this
routes.rb
Frontend::Application.routes.draw do
DynamicRoutes.load
end
app/models/dynamic_routes.rb
class DynamicRoutes
# dynamically loads the routes from settings into the routes.rb file
# and adds a host constraint to just match with the current sites host
# http://codeconnoisseur.org/ramblings/creating-dynamic-routes-at-runtime-in-rails-4
def self.load
if Site.table_exists?
Frontend::Application.routes.draw do
Site.includes(:setting).each do |site|
site.routes.each do |route|
# write the route with the host constraint
constraints(:host => site.hostname) do
case route[0]
when :shop_show
match "#{route[1]}", to: 'shops#show', via: [:get], as: "shop_show_#{site.id}"
end
end
end
end
end
end
end
# allows to reload the routing
# e.g. when changes in route settings where made
#
def self.reload
Rails.application.reload_routes!
end
end
So we create all routes for each site and match them with a host constraint. This works fine unless we use the url_for helper
#site = Site.find_by(hostname: request.host)
url_for controller: 'shop', action: 'show', host: #site.hostname
url_for returns the first matching url, doesnt matter from which host it should belong to. so the host constraint is not used, even if I put a host: param
Do you have any idea, of how its possible to use url_for with host constraints?
I have had the same task in my application. url_for ignores host param. But we could create additional path helpers in our ApplicationController in the following way:
ApplicationController.rb
%w( shop_show ).each do |helper|
helper_name = "#{helper}_path".to_sym
helper_method helper_name
define_method(helper_name) { |*args| send "#{helper}_#{site.id}_path", *args }
end
After that you are able to use universal path shop_show_path in your views. Of course, you should dynamically assign site variable depending on your host/domain.

Rails routes: different domains to different places

I've got a Rails app up running on a server. It's a big project so there are lots of routes involved, and two domains point to the root at the moment. I'd like to somehow design my routes.rb to interpret one domain to take it to a certain part of the app as if it was the root, and use the other for everywhere else.
Something like this (very pseudocode, hope you get the idea):
whole_app.com
whole_app.com/documents
whole_app.com/share
whole_app.com/users
partial_app.com, :points_to => 'whole_app.com/share'
Can Rails handle this? Thank-you!
You can achieve this by overriding default url_options method in application controller. This will override host url for every request.
class ApplicationController < ActionController::Base
....
def default_url_options
if some_condition
{:host => "partial_app.com"}
else
{:host => "whole_app.com"}
end
end
....
end
And for pointing a route to some specific url, you may use:
match "/my_url" => redirect("http://google.com/"), :as => :my_url_path
The better way is to do settings on server to redirect some url to a specific location.
is it going to /share based on some kind of criteria? if so you can do this:
routes.rb
root :to => 'pages#home'
pages_controller.rb
def home
if (some condition is met)
redirect_to this_path
else
render :layout => 'that'
end
end

Change ActionMailer Email URL Host Dynamically

In my application.html.erb I have <%= render #objects %>, that renders a bunch of _object.html.erb partials, each with a <%= link_to(object) %>. I render the same single partial <%= render #object %> in the email that is being sent when somebody leaves a comment but I want link to start with a server url.
I have tried everything:
link_to(object)
url_for(object)
before_filter :set_mailer_host
def set_mailer_host
ActionMailer::Base.default_url_options[:host] = request.host_with_port
end
default_url_options[:host] = "example.com"
def default_url_options
{ host: 'example.com' }
end
...Nothing works. With or without :only_links
In the end, I just built a dumb helper that prepends hostname to links:
# application_controller.rb
before_filter { App::request=request }
# application_helper.rb
def hostify obj
"http://#{App::request.host_with_port}#{url_for obj}"
end
# _object.html.erb:
<%= link_to obj.title, hostify(object) %>
Is there a normal way to do this?
I can't really tell what you're trying to link to or what 'obj' is referring to, but you can do something like:
link_to obj.title, {:host => 'example.com'}
or
link_to obj.title, {:controller => 'store', :action => 'view_cart', :host => 'example.com'}
Okay after spending some time with it, I think I am starting to figure it out.
You got to use restful routes then you can set default_url_options[:host] = host in the Mailer action and :only_path to true in default_url_options for other controllers. Plus :locale.
And in the view I have: objects_url(object)
Since I have different hostnames, I pass request.host_with_port as a parameter from a controller to the Mailer when sending mail.
link_to doesn't accept parameters and url_for() can only build url from parts of it.
def url_options
{ domain: 'example.com' }
end
Or better to merge in case you use other options:
def url_options
{ domain: 'example.com' }.merge(super)
end

Multiple robots.txt for subdomains in rails

I have a site with multiple subdomains and I want the named subdomains robots.txt to be different from the www one.
I tried to use .htaccess, but the FastCGI doesn't look at it.
So, I was trying to set up routes, but it doesn't seem that you can't do a direct rewrite since every routes needs a controller:
map.connect '/robots.txt', :controller => ?, :path => '/robots.www.txt', :conditions => { :subdomain => 'www' }
map.connect '/robots.txt', :controller => ?, :path => '/robots.club.txt'
What would be the best way to approach this problem?
(I am using the request_routing plugin for subdomains)
Actually, you probably want to set a mime type in mime_types.rb and do it in a respond_to block so it doesn't return it as 'text/html':
Mime::Type.register "text/plain", :txt
Then, your routes would look like this:
map.robots '/robots.txt', :controller => 'robots', :action => 'robots'
For rails3:
match '/robots.txt' => 'robots#robots'
and the controller something like this (put the file(s) where ever you like):
class RobotsController < ApplicationController
def robots
subdomain = # get subdomain, escape
robots = File.read(RAILS_ROOT + "/config/robots.#{subdomain}.txt")
respond_to do |format|
format.txt { render :text => robots, :layout => false }
end
end
end
at the risk of overengineering it, I might even be tempted to cache the file read operation...
Oh, yeah, you'll almost certainly have to remove/move the existing 'public/robots.txt' file.
Astute readers will notice that you can easily substitute RAILS_ENV for subdomain...
Why not to use rails built in views?
In your controller add this method:
class StaticPagesController < ApplicationController
def robots
render :layout => false, :content_type => "text/plain", :formats => :txt
end
end
In the view create a file: app/views/static_pages/robots.txt.erb with robots.txt content
In routes.rb place:
get '/robots.txt' => 'static_pages#robots'
Delete the file /public/robots.txt
You can add a specific business logic as needed, but this way we don't read any custom files.
As of Rails 6.0 this has been greatly simplified.
By default, if you use the :plain option, the text is rendered without
using the current layout. If you want Rails to put the text into the
current layout, you need to add the layout: true option and use the
.text.erb extension for the layout file. Source
class RobotsController < ApplicationController
def robots
subdomain = request.subdomain # Whatever logic you need
robots = File.read( "#{Rails.root}/config/robots.#{subdomain}.txt")
render plain: robots
end
end
In routes.rb
get '/robots.txt', to: 'robots#robots'
For Rails 3:
Create a controller RobotsController:
class RobotsController < ApplicationController
#This controller will render the correct 'robots' view depending on your subdomain.
def robots
subdomain = request.subdomain # you should also check for emptyness
render "robots.#{request.subdomain}"
end
end
Create robots views (1 per subdomain):
views/robots/robots.subdomain1.txt
views/robots/robots.subdomain2.txt
etc...
Add a new route in config/routes.rb: (note the :txt format option)
match '/robots.txt' => 'robots#robots', :format => :txt
And of course, you should declare the :txt format in config/initializers/Mime_types.rb:
Mime::Type.register "text/plain", :txt
Hope it helps.
If you can't configure your http server to do this before the request is sent to rails, I would just setup a 'robots' controller that renders a template like:
def show_robot
subdomain = # get subdomain, escape
render :text => open('robots.#{subdomain}.txt').read, :layout => false
end
Depending on what you're trying to accomplish you could also use a single template instead of a bunch of different files.
I liked TA Tyree's solution but it is very Rails 2.x centric so here is what I came up with for Rail 3.1.x
mime_types.rb
Mime::Type.register "text/plain", :txt
By adding the format in the routes you don't have to worry about using a respond_to block in the controller.
routes.rb
match '/robots.txt' => 'robots#robots', :format => "text"
I added a little something extra on this one. The SEO people were complaining about duplicated content both in subdomains and in SSL pages so I created a two robot files one for production and one for not production which is also going to be served with any SSL/HTTPS requests in production.
robots_controller.rb
class RobotsController < ApplicationController
def robots
site = request.host
protocol = request.protocol
(site.eql?("mysite.com") || site.eql?("www.mysite.com")) && protocol.eql?("http://") ? domain = "production" : domain = "nonproduction"
robots = File.read( "#{Rails.root}/config/robots-#{domain}.txt")
render :text => robots, :layout => false
end
end

Rails redirect_to "www.somewebsite.com" with GET/POST parameters?

I'm trying to issue a redirect_to in one of my controllers to a fully qualified URL + I want to pass in some parameters
In the controller for site A I do:
redirect_to: "www.siteB.com/my_controller/my_action?my_parameter=123"
Is there a nicer way to do this in rails?
If SiteB is running the same app (i.e. the routes are the same for that server), then you can build the redirect you describe with:
redirect_to :host => "www.siteB.com",
:controller => "my_controller",
:action => "my_action",
:my_parameter => 123
Notice that any keys not handled by url_for are automatically encoded as parameters.
You can apparently pass a host:
redirect_to { :host => "www.siteB.com", :controller => "my_controller", :action => "my_action", :id => 123 }
Check out the documentation for url_for.
Along the lines of the other responses. If you set up a controller defining the path in your routes.rb of site A, you can use the generated url helpers. Just override the :host as an argument.
Example:
Site A Routes.rb:
...
map.resource whatever
...
Site A Controller:
...
redirect_to edit_whatever_url(:host => "www.siteB.com", :my_parameter => 123)
...
So long as SiteB's web server (rails or otherwise) recognizes the http://www.siteB.com/whaterver/edit?my_parameter=123 you're good.
Caveat: Keep in mind that redirecting a Post with 302 has specific consequences as defined in RFC 2616. In a nutshell it means that a user will be asked to reconfirm their post to the new URL, before the redirected post can succeed.

Resources