I have a problem, recently the name of a controller changed.
I changed the routes file to accept calls using the old controller name, for people with bookmarks referencing the old name:
get '/old/about', to: redirect('/new/about')
get '/old/report/:client', to: redirect('/new/report/%{client}')
get '/old/:sub_path', to: redirect('/new/%{sub_path}')
that works fine. But for calls with query string it blocks it to /report/200. for example:
/old/report/200?c_id=257&end=2013-10-19&num_results=294540&start=2013-10-13
it cuts the url to:
old/report/200
and shows me an error because of the lack of parameters. Do you know what can I do? (I thought the :sub_path line in the routes would help but not) :(
Modify redirect to use the path: option to preserve the querystring:
- get '/old/about', to: redirect('/new/about')
+ get '/old/about', to: redirect(path: '/new/about')
This is demonstrated in the API docs for redirect, see http://api.rubyonrails.org/classes/ActionDispatch/Routing/Redirection.html#method-i-redirect
The question mention by Matt helped me to figure out my answer (thanks a lot!). It was a slightly different for my specific case. Im leaving the answer that worked for me for future reference.
match "/old/report/:client" => redirect{ |params, request| "/new/report/#{params[:client]}?#{request.query_string}" }
Building on Alejandra's answer, more verbose but without the ? if there's no query string:
get "/old/report/:client", to: redirect{ |params, request| ["/new/report/#{params[:client]}", request.query_string.presence].compact.join('?') }
So /old/report/:client?with=param will become /new/report/:client?with=param,
and /old/report/:client will become /new/report/:client.
The existing answers work perfectly, but are not quite suitable for keeping things DRY — there is a lot of duplicate code once you need to redirect more than one route.
In this case, a custom redirector is an elegant approach:
class QueryRedirector
def call(params, request)
uri = URI.parse(request.original_url)
if uri.query
"#{#destination}?#{uri.query}"
else
#destination
end
end
def initialize(destination)
#destination = destination
end
end
Now you can provide the redirect method with a new instance of this class:
get "/old/report/:client", to: redirect(QueryRedirector.new("/new/report/#{params[:client]}"))
I have a written an article with a more detailed explanation.
Related
For a specific URL, we want to redirect ONLY users in a certain state (defined by session[:state_code] to be directed to a new URL.
All other users should see and redirected to the old URL. How can we do this effectively in Rails? We can't access the session from a route constraint. For complex issues I won't get into, we need to do this redirect in routes.rb and not perform this in the controller.
You could access session throw request parameter - request.session.
Example:
Rails.application.routes.draw do
root to: redirect('<specific URL>'), constraints: lambda { |request|
request.session[:state_code] == <specific value>
}
root to: redirect('<old URL>'), as: 'root_old'
end
For different possibilities to declare advanced constraints look in Rails official doc - Advanced Constraints.
The same could be achieved by providing a block to redirect (for more details see Redirection):
Example:
Rails.application.routes.draw do
root to: redirect{ |path_params, request|
request.session[:state_code] == <specific value> ? '<specific URL>' : '<old URL>'
}
end
I've been working on some something similar. As far as I understand, what you are specifically wanting to do is not possible, or at least easily. The issue is that the routes.rb has not knowledge of the sessions 'hash' so there is no way to look it up.
http://bjedrocha.com/rails/2015/03/18/role-based-routing-in-rails/
This blog helped me, and the alternative is to persist the user state in the database. hope this helps!
Just use request.session[:state_code] as #ych suggests
get('/your_route', constraints: AuthenticatedConstraint.new, to: redirect do |_p, req|
req.flash[:error] = 'not_authorized
'/'
end)
and define AuthenticatedConstraint in app/constraints with def matches?(request) that returns true/false
In my routes file we originally had this rout set up:
match '/search/*tag' => 'search#search'
We now want to remove the word 'search' from the url. So I added a new route:
match '/*tag' => 'search#search'
That all works beautifully. We wanted to update the old route to redirect to the new one to keep seo and bookmarks working.
match '/search/*tag' => redirect {|params| "/#{params[:tag]}"}
However this is pluralizing the term.
Input url: www.fubar.com/search/work
Becomes: www.fubar.com/works
What is causing this and how do I stop it from pluralizing the tag?
Might be relevant: We need to use /*tag instead of /:tag because we sometimes have a list of tags. I.e. www.fubar.com/work/web/video
Turns out everything worked once I cleared my cache. Browsers remember 301 redirects (and I forgot that and was apparently kept serving a previous broken redirect.
match '/search/*tag' => redirect { |params| "/#{params[:tag]}" }
match '/*tag' => 'search#search'
All I can recommend is stuffing a Rails.logger.debug{ params.inspect } inside the block to redirect. That'd at least give you a start on debugging it. Rails routing can be tricky and brittle when you stray from the happy path, and knowing whether the issue is in the recognition (i.e. that the match for *tags is being pluralized) or in routing (that the result from the redirection is being pluralized) would be the first place to look.
We recently changed the product name on our website from 'bracelets' to 'wristbands' and need to keep the old routes around for SEO purposes.
Essentially, these routes
www.hostname.com/products/bracelets/series-1/
www.hostname.com/products/bracelets/series-1/small-purple
should route to
www.hostname.com/products/wristbands/series-1/
www.hostname.com/products/wristbands/series-1/small-purple
I am reading the tutorial here http://guides.rubyonrails.org/routing.html#redirection and looks like i'll be using the block syntax, but am still not sure how to do the route properly. I'm looking to learn more about the routes file as well, so any information would be great. Thanks in advance
match "/bracelets/:name" => redirect {|params| "/wristbands/#{params[:name]}" }
EDIT:
OK i've been playing with it for a bit, and here is how it is working with what I have tried
match "/products/bracelets/:name/:name2" => redirect {|params| "/products/wristbands/#{params[:name].pluralize}/#{params[:name2].pluralize}" }
Input URL:
localhost:3000/products/bracelets/series-1/small-purple
Output URL: localhost:3000/products/wristbands
Error Message: Invalid Product: series-1
(So the match worked, but the redirect didn't)
If I change the match to inspect params like this:
match "/products/balance-bracelets/:name/:name2" => redirect {|params| "/products/wristbands/#{params.inspect}" }
I get the following:
/products/wristbands/{:name=>"series-1", :name2=>"small-purple"}
So it appears it isn't recognizing the second slash '/' or something. Any Ideas?
I'm was at the same situation and that works for me:
match "/products/bracelets/:name/:name2" => redirect {|params, request| "/products/wristbands/#{params[:name].pluralize}/#{params[:name2].pluralize}" }
I've passed two agruments into the block.
You can do this in Rails 5.
match '/products/bracelets/:name/:name2', to: "/products/wristbands/%{name}/%{name2}"
I am trying to do something for hours and I'm stuck with rails routes.
So.. the idea is to have some even more user-friendly urls like for example /Laptops for a category and /Laptops/Apple-MacBook-Air-and-so-on. I should also use such links for simple pages like /MyDummyPage etc.
So my idea was to get the request_url and check if i can find the page myself. But it seems rails is initialising this request class after defining routes and right before calling the controller.
As you can see I am stuck and can't see any possible solution for my problem.
I will be glad if someone can help me.
Thank you in advance.
All the best!
(Whole thing revised)
If you want to allow dynamic matches along with normal restful routes, there are a couple options- (put it at the end of your routes or it will match everything)
match '*raw' => 'dynamic#show'
And in dynamic_controller.rb
def show
parts = params[:raw].split '/'
# do logic here to set all variables used in views
render #resource_or_page
end
You could also use the input in a search function and redirect to the first result of that search. Or return a 404 if there are no results.
def show
results = search_method_here params[:raw].sub('/', ' ')
if results.any?
redirect_to results.first
else
raise ActionController::RoutingError.new 'Not Found'
end
end
Also, for freindlier urls within restful routes, try out this: https://github.com/norman/friendly_id
I think its important to realize that people generally do not manipulate URLs by hand, and its nice to have readable urls, but its more important for them to be clear on what/where they are doing/going.
In response to your comment, I think you are mislead about routing. If you make 2 routes :category and :page, they match the exact same url, except one of them stores it in params[:category] and the other in params[:page]. To differentiate it, you would need to have a different amount of arguments matched like :category/:product or a namespace, or, perhaps, a restful route which specifies the MVC the route routes to.
How can I use dynamic root route if it depends on... weather, or current time, or whatever?
I thought about two ways: ApplicationController level and Rack redirect.
With first solution I will check my dynamic state and redirect to particular page.
Second solution is little more native as far as it uses routes level
For example
root :to => proc { |env| [ 302, {'Location'=> some_code }, [] ] }
But what I hope to see is how can I use simple lambda for route option like:
root :to => "mycontroller#myaction", :some_param => proc{ DateTime.now.hour }
It doesn't work but it shows my expectation
I'm not sure why you'd need to initialize a parameter in the routing table when the same thing could be done in the controller:
params[:some_param] = DateTime.now.hour
You can also do the redirection inside the controller as required instead of leaning so heavily on the routing table using the redirect_to method.
Has anyone figured this out? Even though this question is a decade old now I still don't see a clean way of doing this in Rails. I have a route constraint for the root path. My need is once they get to that action then a few conditions would be checked and based on that the request should be ultimately processed by another controller action. I could do redirect_to but then the URL shown in the browser also changes, which is not what I want. If they are hitting abc.com they should still be kept at abc.com and not redirected to abc.com/home or something else. Anyone figured this out?