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.
Related
I have the Comment model, which is polymorphic associated to commentable models like Project, User, Update etc. And I have a page where a user can see every User's comment. I want a link near each comment with an address of an object this comment is associated with.
I could write something like that:
link_to 'show on page', Object.const_get(c.commentable_type).find(c.commentable_id)
But this will work only for not nested routes (like User). Here's how my routes look like:
resources :users do
resources :projects, only: [:show, :edit, :update, :destroy]
end
So when I need a link to a Project page, I will get an error, because I need a link like user_project_path.
How can I make Rails to generate a proper link? Somehow I have to find out if this object's route is nested or not and find a parent route for nested ones
You could use a bit of polymophic routing magic.
module CommentsHelper
def path_to_commentable(commentable)
resources = [commentable]
resources.unshift(commentable.parent) if commentable.respond_to?(:parent)
polymorphic_path(resources)
end
def link_to_commentable(commentable)
link_to(
"Show # {commentable.class.model_name.human}",
path_to_commentable(commentable)
)
end
end
class Project < ActiveRecord::Base
# ...
def parent
user
end
end
link_to_commentable(c.commentable)
But it feels dirty. Your model should not be aware of routing concerns.
But a better way to solve this may be to de-nest the routes.
Unless a resource is purely nested and does not make sense outside its parent context it is often better to employ a minimum of nesting and consider that resources may have different representations.
/users/:id/projects may show the projects belonging to a user. While /projects would display all the projects in the app.
Since each project has a unique identifier on its own we can route the individual routes without nesting:
GET /projects/:id - projects#show
PATCH /projects/:id - projects#update
DELETE /projects/:id - projects#destroy
This lets us use polymorphic routing without any knowledge of the "parent" resource and ofter leads to better API design.
Consider this example:
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
resources :projects
resources :users do
# will route to User::ProjectsController#index
resources :projects, module: 'user', only: [:index]
end
end
class ProjectsController < ApplicationController
def index
#projects = Project.all
end
# show, edit, etc
end
class User::ProjectsController < ApplicationController
def index
#user = User.joins(:projects).find(params[:user_id])
#projects = #user.comments
end
end
This would let us link to any project from a comment by:
link_to 'show on page', c.commentable
And any users projects by:
link_to "#{#user.name}'s projects", polymorphic_path(#user, :projects)
I'm trying to build a basic Rails/React app which takes in a series of user inputs, then displays them on the screen within a letter template (think MadLibs.) I've followed several React tutorials and I have a decent amount of experience with Rails but I've gotten amazingly hung up with an API I've created. For context I'm using this tutorial here as a guide, but am building my own app.
This is my routes file:
Rails.application.routes.draw do
root to: 'site#index'
namespace :api do
namespace :v1 do
resources :letters, only: [:index, :create]
end
end
end
This is my base_controller.rb file:
class Api::V1::BaseController < ApplicationController
respond_to :json
end
and this is my letters_controller.rb file:
class Api::V1::LettersController < Api::V1::BaseController
def index
respond_with Letter.all
end
def create
respond_with :api, :v1, Letter.create(letter_params)
end
private
def letter_params
params.require(:letter).permit(:id, :param_one, :param_two)
end
end
The controller files are both stored in controllers/api/v1 and this is reflected in my routes file. I'm able to use the index method no problem and get all Letter objects to display. I'm able to use the create method somewhat, in that a new Letter is created and saved to the database (sqlite btw.) Problem comes with the response, as in there isn't one. Instead of a response coming back to my AJAX call I'm getting the following:
NoMethodError (undefined method `api_v1_letter_url' for #<Api::V1::LettersController:0x007f3085f1d300>):
app/controllers/api/v1/letters_controller.rb:7:in `create'
I have no earthy idea why this isn't working. From everything that I've read feeding the respond_with call the parameters I have should cause the returned url to work and come back with a response. Instead I get this strange 500.
I've never had so much trouble with a program that it's given me a migraine before. If anyone can help it will be greatly appreciated.
If you run rake routes, you will see that there is no declaration for api_v1_letter_url.
This is because you do not declare a show method on routes.rb (you explicitly only use index and create). If you want the call in your controller to work as-is, you need a show method:
Rails.application.routes.draw do
root to: 'site#index'
namespace :api do
namespace :v1 do
resources :letters, only: [:index, :create, :show]
end
end
end
Now, run rake routes, you'll see it's there.
To implement show in your controller:
def show
respond_with Letter.find(params[:id])
end
I'm getting this error :
"The action 'create' could not be found for ObjectController"
I know it should be obvious but I'm missing something, that's my controller :
class ObjectController < ApplicationController
def index
end
def create
end
end
And that is my routes :
Rails.application.routes.draw do
get 'object/index'
get 'object/create'
match ':controller(/:action(/:id))', :via => :get
resources :objets
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
# You can have the root of your site routed with "root"
root 'object#index'
You probably want to scrap those routes and try something simpler like
resources :objects, only: [:get, :create, :show]
Then use
$ rake routes
To make sure your routes are as the should be. You will want a POST route to /objects to create a new object etc..
Ok that one was dumb, actually I had two directories and I wasn't modifying the right one, sorry about that...
Your routes could be greatly improved:
#config/routes.rb
Rails.application.routes.draw do
root 'objects#index'
resources :objects
--
Next, the "standard" way to achieve what you're looking for is to use the new action; IE not the "create" action. If you wanted to use the create path name (instead of new), you'll be able to define it in the path_names argument:
#config/routes
resources :objects, path_names: { new: "create", create: "create" } #-> url.com/objects/create
To understand why you should be using new instead of create, you should look up resourceful routing, and how it pertains to object orientated programming.
Finally, your controller should be named in the plural:
#app/controllers/objects_controller.rb
class ObjectsController < ApplicationController
...
end
Whilst you can call it whatever you like, Rails defaults to plural controller names, singular model names.
i am rewritting a rails app I did some 5 years ago using rails 1.something.
When I try to browse localhost/companies/search_updates/ I get this error... I know this is a routing error because when I remove the resources :companies from router.rb the thing works fine... How can this be fixed? And do I need to manually add routes for every action I create?
Error when I try to access localhost/companies/search_updates/
The action 'show' could not be found for CompaniesController
Controler
class CompaniesController < ApplicationController
def index
#companies = Company.all
end
def search_updates
# Execute code to search for updates
# Redirect to results
end
end
Routes
resources :accounts
resources :companies
get 'companies/search_updates' => 'companies#search_updates'
search_updates.html.erb
Hello Updates!
The action 'show' could not be found for CompaniesController
Rails routes are matched in the order they are specified, so if you
have a resources :companies above a get 'companies/search_updates' the show action's
route for the resources line will be matched before the get line. To
fix this, move the get line above the resources line so that it is
matched first.
resources :accounts
get 'companies/search_updates' => 'companies#search_updates'
resources :companies
And
Resource routing allows you to quickly declare all of the common
routes for a given resourceful controller. Instead of declaring
separate routes for your index, show, new, edit, create, update and
destroy actions, a resourceful route declares them in a single line of
code.
If you have only index method (default CURD rails) on your controller, you can specific routes for it.
resources :accounts
get 'companies/search_updates' => 'companies#search_updates'
resources :companies, :only => [index]
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