Rails controller using wrong method - ruby-on-rails

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

Related

New action not being reached in my Ruby app

I'm just learning Ruby on Rails and building a fairly simple app that stores info to a rake generated db. I'm trying to add a new action to the controller but I can't trigger it. There's a before_action getting triggered which shouldn't be.
class GamesController < ApplicationController
before_action :set_entry, only: %i[ show edit update destroy ]
before_action :authenticate_user!
def index
end
def analyse
#The function here doesnt matter, its not reaching it because its hitting the set_entry instead
puts 'howdy'
end
private
def set_entry
#entry = Entry.find(params[:id])
end
end
The error I'm hitting is Couldn't find Game with 'id'=analyse", highlighting the set_entry action, which to my understanding should only be running with the show, edit, update, and destroy actions (which are also in the full controller and running fine). There are other actions in the controller (such as create and new) which don't seem to trigger that set_entry and are running just fine as expected. For example, linking to the new path takes me to /entry/new, while linking to an edit path takes me to /entry/:id/edit, which is all fine. But it keeps linking my new analyse action trying for entry/:id/analyse when I want it to go to entry/analyse.
My button to trigger it is simply:
<%= link_to "Analyse", analyse_path %>
Which is in a navbar in my application.html.erb
And here's my routes.rb:
Rails.application.routes.draw do
resources :entry
devise_for :users
resources :users
root to: "entry#index"
get 'entry/index'
get 'entry/analyse', as: 'analyse'
end
The path /entry/analyse is matched two routes:
entry#show action in resources :entry
entry#analyse action in get 'entry/analyse', as: 'analyse'
Because the route matching is interpreted through config/routes.rb in order, and resources :entry is the first route the path is matched. At the result, entry#show action will handle the requests from /entry/analyse.
The solution is simple, just switch the order of resources :entry and get 'entry/analyse', as: 'analyse' in config/routes.rb. For example:
Rails.application.routes.draw do
get 'entry/analyse', as: 'analyse' # <- to here
resources :entry
devise_for :users
resources :users
root to: "entry#index"
get 'entry/index'
# from here ...
end
Move all of your resources to the end of your route configuration. I am not sure why its getting tripped up but it seems something within your routes is matching analyse to your entry resource and routes within rails are matched in order. All custom routes really should come first to prevent something like a generic route catching your expected action.
Best practices also state your root to: should be at the top of the configuration since it is your most popular route generally in an application.

How are resources connected to controllers without a Model

I am writing a basic application that scrapes data. I have the following in my routes.rb.
Rails.application.routes.draw do
constraints subdomain: 'api' do
namespace :api, path: '/' do
resources :apps, :only => :show
end
end
In controllers I have something like this although I am not sure how are resources connected to Controller.
class AppsController < ApplicationController
def show
puts "this works"
respond_to do |format|
format.json { render json: #user }
end
end
def apps
puts "my app"
end
end
Also, I dont have a Model. Does that mean that in my resources :apps calls a method in AppsController called apps?
If I wanted it t call apps then how's it possible ?
how does a controller in rails know what route does it belong to
I am trying to add a GET /apps?filter=5 that returns my scraped data in the form of JSON and with filter as parameter to that it means that return 5 JSON objects to me
#config/routes.rb
constraints subdomain: 'api' do
namespace :api, path: '/' do
get :apps, to: "apps#apps", on: :collection #-> api.url.com/apps
end
end
A much more coherent way to do it would be...
#config/routes.rb
constraints subdomain: 'api' do
namespace :api, path: '/' do
resources :apps #-> api.url.com/apps -> apps#index
end
end
I think you're getting confused with how Rails works, especially with your data.
I post this all the time, maybe it will help you:
As you can see, your request is not tied to a specific "model", nor is a controller bound to it either. I'll explain the importance of the MVC (Model View Controller) aspect of rails in a minute.
Your thought process is that each request / resource has to have a corresponding model / dataset to pull from. Strictly, this is not the case, although many would believe it to be.
Remember that Rails is just an application framework - it has to work with all the same protocols & restrictions as the other frameworks & languages out there.
When you send a request to Rails (through your browser URL), it takes that request, and matches it to the appropriate controller. This controller action will then pull data from your model (if you've set it up like that), render the view with that data, and return the processed HTML to the browser.
Thus, you don't have to have a model bound to a particular controller action, or anything. You just need to make sure your controllers & views are mapped accordingly.
OOP
I think the part you're getting hooked up on is the object orientated nature of Ruby / Rails.
Although every part of the Rails framework is meant to work with objects, this only applies on a request-basis.
For example, whilst it's typically recommended to keep your controllers resourceful, you don't have to adhere to that methodology if you don't want to. Many newbies don't know the difference.
Thus, when you use the following:
#config/routes.rb
constraints subdomain: 'api' do
namespace :api, path: '/' do
resources :apps, only: :show #-> api.url.com/:id -> apps#show
end
end
... what you're denoting is a controller bound by its resourceful nature. This would typically be expected to use model data, but it's not essential...
In controllers I have something like this although I am not sure how
are resources connected to Controller.
Rails.application.routes.draw provides a DSL which hooks into Rack (the interface between the HTTP server and Rails). It generates rules for where to route the response from Rack.
The DSL is provides has a lot of ways to do the same things. In this example, the resources :apps, :only => :show line basically says you want to generate all of the REST verbs for the AppsController, but you only want the :show verb, so the router will only generate a route to AppsController#show. Note that you can run rake routes to get a list of your routes.
Also, I dont have a Model. Does that mean that in my resources :apps
calls a method in AppsController called apps? If I wanted it t call
apps then how's it possible ?
Models are totally separate abstractions. Once the code reaches your controller you are in plain Ruby land until you return out of that controller action. Models are simply plain Ruby objects with the ability to talk to the database.
In your code if you wanted to call apps from the show method (or action) then you can just call it from there since it's in the same scope.
how does a controller in rails know that ok that is my route. In this case, apps
I'm not sure I understand this question, could you elaborate?
I am trying to add a GET /apps?filter=5 that returns my scraped data in the form of JSON and with filter as parameter to that it means
that return 5 JSON objects to me
For one, you'll need to add a route for /apps. There are several ways you can do this. Here's one approach. I'm going to call it index instead of apps since that's more conventional:
# config/routes.rb
get '/apps' => 'apps#index'
# app/controllers/apps_controller.rb
class AppsController < ApplicationController
respond_to :json
def index
limit = params[:filter].to_i
#users = User.first(limit) # Implement this however you wish
respond_with(#users)
end
end
My syntax might be a little off here with the respond_to and respond_with, but it should explain how the controller routes
Routing simply maps URLs to a controller/action, the existence of a model with the same name does not matter.
To get to the apps action that you defined in the AppsController you need to define a route that maps to apps#apps < This syntax means AppsController, apps action.
An example of a route that would map to the AppsController apps action:
get '/apps', to: "apps#apps"
This is a weird example. It's not conventional to have a def apps action inside AppsController, what exactly are you trying to accomplish with this action?
If you want a rest call to /apps that returns a JSON list of apps, then this all you need to do.
router
resources :apps, only: [:index]
controller
class AppsController < ActionController::Base
def index
puts "This is the index route in AppsController"
end
end
In the router, when you specify resource :apps, only: [:index]. This routes the request GET /apps to AppsController#index

Index being redirected to Show View

When I go to /relationships/index it displays the show page even if it is not mentioned in the controller? Then when I try to just go to an index view without a show page created I get the following error: Unknown action: The action 'show' could not be found for relationships controller, even with no mention of it in the controller or a view file for the action.
routes.rb
Mymanual::Application.routes.draw do
resources :validation_rules
resources :validations
resources :product_types
resources :products
resources :connections
resources :relationships
root :to => 'products#index'
end
relationships_controller.rb
class RelationshipsController < ApplicationController
def index
end
def new
#relationship = Relationship.new
end
end
Then just HTML in a index.html.erb, show.html.erb and new.html.erb file.
It's because /relationship path is for relationships#index. If you go to /relationships/index, Rails router assumes you want to go to relationships#show path with params[:id] == 'index'.
Even though you want the index action, you don't enter index in the url. Otherwise, it thinks you're trying to find a Relationship with an id of index.
So just go to /relationships instead.
Go to a console and then run:
rake routes
It will give you all routes your app understand
In some place of this routes you will have something like:
relationships relationships#index /relationships
relationship relationships#show /relationships/:id
The issue that you have is that the app understand /relationships/index as relationships with id=index
The fix? use /relationships url

How do I re-write this route to be shorter?

I have resources :tags in my routes.rb.
So when I go to /tags/ios, it shows me the correct Tag#Show view.
What I would like to happen is when the user goes to /tags/iosit shows it as /ios and likewise I want that generated path to always be /ios (and not have the leading /tags).
Here is an example of how I am rendering that link, within an each block:
<%= link_to "#{tag.name}", url_for(tag) %>
In your routes.rb
resources :tags
match '/:tag_name' => 'tags#show'
Then in your tags_controller#show action you have access to the tag name via:
params[:tag_name]
Be sure to put that at the end of your routes file, as it will catch everything. Also, since it will catch everything you should render a 404 if it's not a valid tag name:
def show
unless #tag = Tag.where(name: params[:tag_name]).first
raise ActionController::RoutingError.new('Not Found')
end
end
To redirect /tags/ruby to /ruby:
match "/tags/:tag_name" => redirect("/%{tag_name}")
http://guides.rubyonrails.org/routing.html
well the problem here is namespace
if you define a route like
get "/(:slug)" => "Tags#show"
that will basically match anything where its a valid tag or not. The way I would do it would be
in your routes file:
begin
Tag.all.each do |t|
begin
get "#{t.slug}" => "Tags#show"
rescue
end
end
rescue
end
Then in your tags controller you can
def show
slug = request.env['PATH_INFO']
#tag = Tag.find_by_slug(slug)
end
My answer comes from another answer I wrote that you may be interested How to create app-wide slug routing for Rails app?
So even though I got lots of nice suggestions from these guys, I found what I think is the best solution for me.
All I am trying to do is basically make /tags/ruby be rendered as /ruby both in the actual URL on the links of all the tags, and in the URL bar.
I don't want to do anything that will add load to my app.
This is the solution that works for me:
resources :tags, path: "", except: [:index, :new, :create]
Using path:, you can specify what you want the path to display as. i.e. what do you want to appear before your resource. So if you wanted your URLs to look like myhotness/tags/ruby, then you would simply do path: "myhotness".
I didn't want anything in my path, so I just left it blank.
For what it's worth, you can even add a constraint to that route, like so:
resources :tags, path: "", except: [:index, :new, :create], constraints: { :id => /.*/ }

How to fix this up? " Unknown action The action 'index' could not be found for UsersController"

When I go to my localhost:3000/users page, I get:
Unknown action
The action 'index' could not be found for UsersController.
If you were following Hartl's tutorial,then accessing localhost:3000/users will cause this error. Try localhost:3000/signup instead.
You need not define the default actions (assuming appropriate http method), all you need to do is add the following to your config/routes.rb
resources :users
First you need to make sure that your controller actually has an index action, so
class UsersController < ApplicationController has to include the def index ... end in it.
Also, make sure your routes are set up correctly using
resources :users
and check it by typing
rake routes
in the terminal to check that the routes are right.
You might also want to check that the root is set up correctly in the config/routes.rb file
if you got the same problem with me (I cant access the localhost:3000/users but I can access my localhost:3000/signup), it might be works for u.
First, in your users_controller.rb (Controller for Users) , add
def index
end
Then , make a file "index/html/erb" in your app/views/users/index.html.erb
and put this code
<% controller.redirect_to "/signup" %>
You might have to rerun your server, and it works on my problem.
Remember, if you've included
get 'signup' => 'users#new
resources :users
…in your routes.rb file, then you need to use localhost:3000/signup instead. I believe if you removed get 'signup' => 'users#new and left only resources :users then using localhost:3000/users would take you to the new user signup form.
Remove the debugger line. Also, make sure you have exit the rails console.
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
debugger
end
def new
end
end

Resources