My Rails route is always going to show action - ruby-on-rails

I have following route in my Rails project
resources :custom_urls, :path => '', :only => [:show, :new, :create]
My route is working fine for show, new and create action. But when ever I go to other paths e.g
localhost:3000/index
it always goes to show page. Due to this I am getting error because my instance variable in show action is not set. How can I avoid this problem? I want to get 404 when I try to go to other routes. I only want my application to route to show, new and create action.
Thanks in advance.
Upate
Below are my routes
Prefix Verb URI Pattern Controller#Action
root GET / custom_urls#new
custom_urls POST / custom_urls#create
new_custom_url GET /new(.:format) custom_urls#new
custom_url GET /:id(.:format) custom_urls#show

There is nothing wrong with your solution, you are just getting a little bit tricked by Rails behaviour. What is happening is, since you changed the default route path for :custom_urls to "", when you access the following url:
localhost:3000/index
The server thinks of it as
localhost:3000/custom_urls/index
So it first look for a action called "index" on your controller. Since you didn't declare it and specified that this route doesn't really exists in your route.rb, The next logical step, for rails, is to think "index" as an ID for an object of the class "Custom_url", and because of that it triggers the show action causing the error.
Basically this logic will happen for whatever you type like this:
localhost:3000/XXXX
or this, if you change the routes.rb
localhost:3000/custom_urls/XXXX
I dont recommend the following step, but if you really want to redirect the /index to 404 you would need to create the action in routes
resources :custom_urls, :path => '', :only => [:show, :new, :create, :index]
Then inside the CustumUrlsController, create the action "index" to redirect to a 404 error
def index
raise ActionController::RoutingError.new('Not Found')
end

Are you getting ActiveRecord::RecordNotFound exception? If so, it is working as expected. In dev, it shows the webconsole and in production it will display the public/404.html.
Btw, how does the user go to localhost:3000/index page? Does your app has a menu item with /index?
If you observe rake routes carefully, localhost:3000/blah (when blah is not new) is same as telling your app find me custom_url with id blah. In this case the blah is index. It could be any thing and if it is a valid id it will fetch the record. localhost:3000/new takes precedence over localhost:3000/notnew.

Related

Rails controller using wrong method

I'm working on a Rails project that is giving me some problems. I've got a controller characters_controller.rb that has two methods.
class CharactersController < ApplicationController
before_action :authenticate_player!
def view
#character = Character.find(params[:id])
unless #character.player_id == current_player.id
redirect_to :root
end
end
def new
end
end
I've got routes set up for each of those.
get 'characters/:id', to: 'characters#view'
get 'characters/new', to: 'characters#new'
The first route works fine. I can get go to /characters/1 and I'm shown the appropriate view and the requested information. If I visit /characters/new I'm shown an error that references characters#view.
raise RecordNotFound, "Couldn't find #{name} with '#{primary_key}'=#{id}"
and
app/controllers/characters_controller.rb:6:in `view'
So /characters/new is trying to get a Character from the database with an id of "new" but that doesn't work well. Any idea what I may be doing wrong?
Order matters in routes.rb, the router will find the first route that matches.
In your case, it would never go to characters#new, because the line above it will always match.
A simple solution would be to swap the two lines.
A better solution might be to use resource routing as documented in the Rails routing guide.
Rails parses routes sequentially and therefore it is considering 'new' as the :id for characters/:id route (which encountered first).
Just swap the order of routes as follow:
get 'characters/new', to: 'characters#new'
get 'characters/:id', to: 'characters#view'
If using this order in your routes.rb, for /character/new request, rails will understand that request is handled by view action with paramas[:id] = 'new'
Let characters/new before the other will resolve your problem:
get 'characters/new', to: 'characters#new'
get 'characters/:id', to: 'characters#view'
Try to use resourceful routes, much cleaner:
resources :characters, only: [:new, :show]
Also I suggest rename def view to def show to follow rails convention
Just use
resources :characters, :path => "characters"
in your routes.rb

Rails redirect_to creates wrong URL

Haven't been able to find anything specific to this issue (other searches deal with forms and such). It's probably a simple oversight on my part. But what on earth am I missing?
GOAL: I'm simply trying to redirect from the /login page URL to the /dashboard URL if a session exists.
EXPECTED OUTCOME: Calling redirect_to dashboard_index_url or redirect_to '/dashboard' should go to https://mydomain/dashboard
CURRENT OUTCOME: if I go to https://mydomain after creating a session it redirects me to https://mydomaindashboard, note the missing slash
ATTEMPTED SOLUTIONS:
Manually type the URL https://mydomain/dashboard after creating a session, RESULT: works, so the proper route seems to exist
Make manual route in routes.rb, RESULT: behavior is exactly the same as resource routing with the missing slash
Clear browser cache, use different browswers RESULT: all exhibit same behavior
Here's what I have (abbreviated to relevant parts):
class LoginController < ApplicationController
def index
redirect_to dashboard_index_url if session[:user_id]
end
#...
end
class DashboardController < ApplicationController
before_action :require_login # calls redirect_to root_url unless session[:user_id]
def index
#...
end
end
# In routes.rb:
resources :login
resources :dashboard
# have also tried things like (removed the above line for these)
get 'dashboard' => "dashboard#index"
#Ryan Here is the current output for the routes:
$ rake routes
Prefix Verb URI Pattern Controller#Action
login_index GET /login(.:format) login#index
POST /login(.:format) login#create
new_login GET /login/new(.:format) login#new
edit_login GET /login/:id/edit(.:format) login#edit
login GET /login/:id(.:format) login#show
PATCH /login/:id(.:format) login#update
PUT /login/:id(.:format) login#update
DELETE /login/:id(.:format) login#destroy
dashboard GET /dashboard(.:format) dashboard#index
dashboard_index GET /dashboard(.:format) dashboard#index
POST /dashboard(.:format) dashboard#create
new_dashboard GET /dashboard/new(.:format) dashboard#new
edit_dashboard GET /dashboard/:id/edit(.:format) dashboard#edit
GET /dashboard/:id(.:format) dashboard#show
PATCH /dashboard/:id(.:format) dashboard#update
PUT /dashboard/:id(.:format) dashboard#update
DELETE /dashboard/:id(.:format) dashboard#destroy
root GET / login#index
SOLVED:
Well, I found the problem and it was not actually Rails, it was Apache-Passenger. There was a config with a redirect (so that all HTTP get redirected to HTTPS) at the apache level that didn't have the trailing slash floating around and causing trouble. (smacks forehead)
Gotta love those red herrings. Thanks so much guys for the quick help!
for use dashboard_index_url, you have to write it in your routes file. However as you've created in routes.rb a dashboard resource, a dashboards_url is available to you and it leads to /dashboards/index
Other way to nail this task is create a mapping
get 'dashboard' => "dashboard#index" as: :dashboard_index
and dashboard_index_url and dashboard_index_path will be available for you
Update 1
Please, look on this SO question
the problem is that path give you relative route and url - absolute. that's why you don't have additional slash in your path. Try path instead of url and it should work.
Update 2
try dashboard_index_path
How about changing your routes to
get '/login', to: redirect('/dashboard')
You can even tailor the resulting link to something more specific to your user by
get '/login', to: redirect('/dashboard'), as: '/dashboard/user[:username]' if you need to.
On a side-note, you should try cleaning up your routes as you go. There are a lot there that you are not using. Change it to
resources :dashboard, only: [:index] and add to it as you need. E.g only: [:index, :show] etc. If another developer was needed to modify your app one day we'd look at your routes and perhaps make incorrect assumptions about what we can do.
Justin

Understanding constraints in Rails

I have been putting tests together to ensure that some CRUD actions are not routable in my application based upon this resource declaration
resources :posts, only: [:index, :show] do
resources :categories
end
My Rspec Controller test
it 'does not route to #new' do
expect(get: '/posts/new').to_not be_routable
end
When I run this test I get the following output
expected {:get=>"/posts/new"} not to be routable, but it routes to {:controller=>"posts", :action=>"show", :id=>"new"}
So after some research I came across this post on SO, having read it an implementing the solution using constraints.
resources :posts, only: [:index, :show], except: :new, constraints: { id: /\d+/ } do
resources :categories
end
Now my tests pass, but I don't understand why and can't leave it without understanding it. So I have two questions
Why was the test failing in the beginning?
How did adding the constraints argument fix it?
According the the post you linked, and the answer you implemented,
The difference between
'/posts/new' # which is the route to the new action of post controller
and
'/posts/:id' #which is the route to the show action of the same controller
is almost nothing, apart from the fact that one is /new and the other is /:id
Now, this difference is almost nothing, given that having a route like /posts/:id means that the only thing really needed here is the /posts/ part. any other thing in front of this will be seen as an id for /posts/:id.
To this effect, /posts/5, /posts/10, /posts/you, /posts/me, and even /posts/new all matches /posts/:id. with :id equaling => 5, 10, you, me, new respectively.
The above is the reason why your test failed because going to /posts/new is routable for the show action, which essentially is /posts/:id
On the other hand, when you added the constraint ( id: /\d+/ ), you are telling the route that id should be strictly digits. This will prevent any character that is not a digit from being interpreted as an id, so, from the example above, ( /posts/5, /posts/10, /posts/you, /posts/me, and even /posts/new ), only posts/5 and /posts/10 will match the show action, while the rest will not.
Hope this is well explanatory ...
1) Your test was failing initially because of the :show route for :posts. It would look something like this:
GET /posts/:id(.:format) posts/#show
This will match any GET request to /posts/xxxx and will assign whatever you put in the second part of the path (xxxx in this case) to the :id element in your params hash.
If you inspect the params[:id] value in your show action after browsing to /posts/new you'll see that params[:id] == 'new'
So why did your test fail?
In your test, you are checking that /posts/new doesn't route anywhere. But without any constraints on what is an acceptable value for :id, it will route to your show action.
2) The constraint you added specifies that the :id parameter must match the regular expression /\d+/ in order to be accepted. This is one or more digits (0-9).
So, GET /posts/123 is accepted, but GET /posts/new is not. Incidentally, this constraint will prohibit you from using friendly ids to make your URLs nicer. eg GET /posts/my-new-post-about-something will not be acceptable due to the constraint specifying that any argument be an integer.
Alright, this is what I think happened.
If you put back your original code:
resources :posts, only: [:index, :show] do
resources :categories
end
and run rake routes, you will see that get posts/new can be matched in term to one of the /posts/:id route with :id being matched to the string "new".
Adding the contraint that id must be numeric does not allow for this match to happen anymore.
Note that if you were using mongoid instead of activerecord for instance, your contraint would not work since ids are string.

No route Match then redirect to home page

In my routes I added this two routes
match '/:controller(/:action(/:id))', :controller => /admin\/[^\/]+/
match '/:controller(/:action(/:id))'
When I goes to invalid route then giving me
The action 'dashboard1' could not be found for Admin::AdminController
I added below line at the end of the routes.rb file
match "*path" => redirect("/")
But it's not working.
I am using rails 3.2.x
Please Help me, Thanks In Advance
wouldn't the two routes catch everything that is in controller/action format?
I guess you are doing everything in admin controller or redirecting to proper controllers and actions, by parsing the params in the admin, redirection to home page should be done in the controller where you are doing that, perhaps a begin rescue block?
begin
....rerouting logic
rescue
redirect_to home_page
end
of course you should check if the exception is the correct one and so forth.

How to redirect when ActionNotFound in Rails

I have a controller SubscriptionsController with only actions new and create. How would I redirect to new if say someone tries to visit GET /subscriptions which would normally trigger the index action?
config/routes.rb
resource :subscriptions, :only => [:new, :create]
Using rails3 you can do it from a route, something like:
match "/subscriptions", :to => redirect("/subscriptions/new")
Edit:
From the comments it was made clear you want to capture more than that, using a wild card you can make it more generic. You may need to combine this form with the previous to deal with the non-slash form (or try the below form without a slash, I havent tried that). Also make sure to put these "catch all" routes below your other ones since routes are matched from top to bottom.
match "/subscriptions/*other", :to => redirect("/subscriptions/new")

Resources