Dynamic root path route - ruby-on-rails

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?

Related

Rails Routing Based on Session Parameter

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

Rails routing issue, can't figure out the link path

Let me fair from the outset, and tell you that I've 'solved' the problem I'm describing. But a solution that you don't understand is not really a solution, now is it?
I have a resource, Newsbites. I have an index page for Newsbites. All my CRUD actions work fine.
I created a separate index (frontindex.html.erb) that acts as the front page of my website to show the newest Newsbites. The formatting is different from my normal index so readers get a larger photo, more of the text of the article(more ads too:).
In my routing table, I have the following statements:
resources :newsbites
get 'newsbites/frontindex'
root 'newsbites#frontindex'
Rake routes show the following:
newsbites_frontindex GET /newsbites/frontindex(.:format) newsbites#frontindex
If I load my website from the root (localhost:3000), it works great. There is a separate menu page that is rendered at the top, and it loads fine. I can click on all links, except the 'Home' link, and they work fine.
The 'Home' link is:
<%= link_to 'Home', newsbites_frontindex_path %>
When I click on the linked, I get the following error:
Couldn't find Newsbite with 'id'=frontindex
The error points to the 'show' action of my Newbites controller. Here are the frontindex and show def from the controller. They appear exactly as I'm posting them:
def frontindex
#newsbites = Newsbite.all
end
def show
#newsbite = Newsbite.find(params[:id])
end
I don't get it. Why is the show action being called by newbites_frontindex_path when there is both a def and views that match? Now, I can get around this by simply pointing home to root_path. But that doesn't help me understand. What if this was not the root of the site?
Any help would be greatly appreciated.
Actually I'm very surprised your code worked at all. A route must define two things
Some sort of regex against which the URL of the user is matched (newsbites/frontindex is different than newsbites/backindex)
What do you want to do for a given URL ? You want to point to a controller action
Rails won't usually "guess" what that action is. Or maybe, he was still able to "guess" that you wanted to use the newsbites controller, but it didn't guess the action right this time :(.
You should declare the root like this, which is what you did
root 'controller#action'
For the rest, there are two ways you can declare it. I prefer the second one
resources :newsbites
get 'newsbites/frontindex', to: 'newsbites#frontindex'
resources :newsbites do
# stuff added here will have the context of the `newsbites` controller already
get 'frontindex', on: :collection # the name of the action is inferred to be `frontindex`
end
The on: :collection, means that 'frontindex' is an action that concerns ALL the newsbites, so the URL generated will be newsbites/frontindex.
On the other hand get 'custom_action', on: :member, means that the custom_action targets a specific item, the URL generated would look like newsbites/:id/custom_action
EDIT : Rails also generate path_helpers based on the route declaration
get 'test', to: 'newsbites#frontindex'
get 'test/something', to: 'newsbites#frontindex'
resources :newsbites do
get 'frontindex', on: :collection
get 'custom_action', on: :member
Will generate path helpers
test_path
test_something_path
# CRUD helpers : new/edit/show/delete, etc. helpers
frontindex_newsbites_path
custom_actions_newsbite_path(ID) # without s !
You can always override this by adding an as: option
get 'custom_action', on: :member, as: 'something_cool'
# => something_cool_newsbites_path
Rails routes thinks that frontindex is an id. That's what the error message says. So it goes to GET newsbite/:id which maps to show.
You need to find a way let Rails routes know that frontindex is not an id.
On a side note: The order in which you define routes matters. The first one matched will be used. If you have GET newsbite/:id and GET newsbite/frontindex then the one that appears first will be matched. In your case this is the first one.
Maybe try to change the order.

How to change URL after route match in Ruby on Rails

I have been programming in Ruby on Rails for a while now, but never really dug deep into routing until recently. After reading a fair amount of documentation and googling, I haven't been able to answer this question.
How do you change a URL after a route is matched? To better explain this, let me set a scenario I'm trying to solve. The root of my website while testing is localhost:3000. My login page is localhost:3000/login. Once logged in though, I want the URL to read localhost:3000 again with no extension. The actual page name is dashboard and my route is as follows currently.
get 'dashboard' => 'user#dashboard'
This only matches when the URL is localhost:3000/dashboard, but I wan't to have cleaner URL like a lot of sites have. How is this achieved with Ruby On Rails? I want to avoid a javascript solutions or anything that is a workaround.
Any help or tips is greatly appreciated. Many thanks in advance.
I've provided the solution below, but I agree with max that your wanting to make a RESTful URL less meaningful is backwards. You should strive to alias a URL to make it more meaningful (e.g. from site.com/posts/34239482069472/ to site.com/posts/my-post-title).
The URL that appears in the address bar is an instruction to an app. When a user puts "site.com/dashboard" into the address bar, they're instructing the app to make an HTTP request get 'dashboard'. The Controller#action is a set of instructions the app executes when it receives that request. If you're following Rails naming convention then Users#dashboard will retrieve data and then by default render the view template at views/users/dashboard.html.erb. Understand this: you're not changing the URL for a given view, you're changing which view template is rendered by the Controller#action that is set for that url.
This means the Controller#action for your root_url (i.e. your root to: 'controller#action' in config/routes.rb) should render one view template if user is logged in and a different view template if a user is not logged in. Assuming root to: welcome#index, your controller action would look something like this:
app/controllers/welcome_controller.rb
def index
# db queries, logic, set #variables
if session[:user_id]
render "users/dashboard" # app/views/users/dashboard.html.erb
else
render "index" # app/views/welcome/index.html.erb
end
end
Note that if the view template you want to render corresponds to the controller, e.g. users_controller.rb action is rendering a view in views/users, then you only need to give the view name, otherwise you need to give a path (relative to app/views).
Why? /dashboard is a proper RESTful definition of a resource. In REST a route should have the same response independent of state. So having a radically different root page for a logged in user violates REST.
Also your users may want to access the index page as well the dashboard and you would be denying them that possibility.
These kind of URL micro-optimizations do not warrant hacking a bunch of state into your routes definitions.

rails 3.0 routing - redirect globbed route

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.

Rails 3 routing and multiple domains

My app allows people to create portfolios. I would like for them to be able to connect their domain to their portfolio.
So somedomain.com would show /portfolio/12, someotherdomain.com would show /portfolio/13 and so on. But I don't want the redirect. I want the user to see somedomain.com in the browser url.
How do I do that?
Ok, I've found this solution:
match "/" => "portfolio#show",
:constraints => { :domain => "somedomain.com" },
:defaults => { :id => '1' }
As I don't have many custom domains, this is fine for now but the question is - how to make this dynamic, to read domain and id data from db?
Ok, let's assume you own yourdomain.com and use it as your home page for your application. And any other domain name like somedomain.net is mapped to a portfolio page.
First of all, in your routes.rb you need to catch yourdomain.com and map it to wherever your home page is, so that it stands out from the rest of the crowd.
root :to => "static#home", :constraints => { :domain => "yourdomain.com" }
Then you need to catch any other root on any domain and forward it to your PortfoliosController
root :to => "portfolios#show"
Keep in mind that this line will only be checked if the previous line fails to match.
Then in your PortfoliosController find the requested portfolio by its domain rather than id.
def show
#portfolio = Portfolio.find_by_domain(request.host)
…
end
Of course you may want to rescue from an ActiveRecord::RecordNotFound exception in case the domain is not in your database, but let's leave that for another discussion.
Hope this helps.
First, you should add a field to the portfolio model to hold the user's domain. Make sure this field is unique. Adding an index to the field in your database would also be wise.
Second, set your root to route to the portfolios#show action, as you already did, but without the constraints.
Then, in the PortfoliosController#show method, do the following check:
if params[:id]
#portfolio = Portfolio.find(params[:id])
else
#portfolio = Portfolio.find_by_domain(request.host)
end
After this, the only thing left to do is to make sure your own domain does not trigger the portfolio#show action. This can be done with the constraint you used before, but now with your own domain. Be sure to put this line in routes.rb above the line for the portfolio#show action, since the priority is based upon order of creation.
The request object seems not to be available to the routes.rb file w/o some patching.
There are some plugins that make it available, but most of them seem to be outdated. This one here request_routing seems to be with the latest commit dates so it would be most up to date. Though I doubt it will work with Rails 3.0 out of the box, it is a start and might not be that hard to port.
Your users can set DNS CNAME redirects so that requests for theirdomain.com land on your_app.com/portfolio/12.

Resources