Two routes for the same action Rails - ruby-on-rails

I have an application that lists all the subaccounts under a main account. However when I click the subaccount, instead of going to do accounts/1/subaccounts/1 I want it to go to subaccounts/1. When I use the for_each statement I get the following error. How can I click on a nested route and have it go to just subaccounts/1 instead of accounts/1/subaccounts/1?
<% #subaccounts.each do |sa| %>
<%= link_to "#{sa.name}", subaccount_path(sa) %>
<% end %>
Routes.rb
resources :subaccounts
resources :accounts do
resources :subaccounts
end
Subaccounts controller
before_action :set_account, only: [:show, :edit, :new, :create]
before_action :set_subaccount, only: [:show, :edit, :update, :destroy]
def show
end
private
def set_account
#account ||= Account.find(params[:account_id])
end
def set_subaccount
#subaccount ||= #account.subaccounts.find(params[:id])
end
def subaccount_params
params.require(:subaccount).permit(:name, :state)
end

The issue you are facing is due to same controller action for 2 different routes. You can fix it by either adding another controller for nested route
resources :accounts do
resources :subaccounts, controller: 'accounts_subaccounts'
end
Or handle exception and check for #account (least preferred way / bad practice)
#account ||= Account.find(params[:account_id]) rescue nil
With handling with rescue you need to handle #account everywhere.
You can also trigger set_account method when params[:account_id].present? which will also work for you.

Related

RAILS: undefined method `budget' for nil:NilClass

I'm new in rails, the thing here is that I'm tryin to create a budget with an specific user ID but when I try to create a budget with postman:
{
"name": "Segundo", "description": "segundo"
}
it sends me that message error:
undefined method `budget' for nil:NilClass
Extracted source (around line #14):
def create
#budget = current_user.budget.build(budget_params)
#budget.save
end
My controller:
class BudgetsController < ApplicationController
before_action :find_id, only: [:show, :edit, :update, :destroy]
def budget_params
params.require(:budget).permit(:name, :description)
end
def new
#budget = current_user.budgets.build
render layout: false
end
def create
#budget = current_user.budgets.build(budget_params)
#budget.save
end
def show
render json: #budget
end
def edit
render layout: false
end
def update
#budget.update(budget_params)
end
def destroy
#budget.destroy
end
def find_id
#budget = Budget.find(params[:id])
end
end
My routes:
Rails.application.routes.draw do
resources :sessions, only: [:create]
resources :registrations, only: [:create]
delete :logout, to: "sessions#logout"
get :logged_in, to: "sessions#logged_in"
root to: "static#home"
#budgets
resources :budgets
#expenses
resources :expenses, only: [:create, :show, :edit, :update, :destroy]
#incomes
resources :incomes, only: [:create, :show, :edit, :update, :destroy]
#investments
resources :investments, only: [:create, :show, :edit, :update, :destroy]
end
can anyone help me with this?
If you use postman and want to save something in the database that in normal flow only registered users can save, you need to send user session cookies value with your request.
https://learning.postman.com/docs/sending-requests/cookies/.
 If you don't send session cookies with your request that means that request is sent by a guest, not logged user. The current_user is nil, so you get undefined method `budget' for nil:NilClass error.
You don't define current_user anywhere. You should probably set a before_action where you save the current user, or a base controller your budget controller can inherit from (if you are going to have more controllers that utilize current_user).

Processing button on a Show view in Ruby on Rails

I have an application entity called Source on my app. This entity has a property called url.
I want to make a processing on my SHOW view. So I added a button on the show view to make a call to my controller and do this processing.
This is my routes.rb
get '/process', to: 'sources#read', as: 'read'
This is my controller method:
class SourcesController < ApplicationController
before_action :set_source, only: [:show, :edit, :update, :destroy, :read]
access all: [:index, :show, :new, :edit, :create, :update, :destroy, :read], user: :all
def read
require 'rss'
require 'open-uri'
url = #source.url
open(url) do |rss|
feed = RSS::Parser.parse(rss)
puts "Title: #{feed.channel.title}"
feed.items.each do |item|
puts "Item: #{item.title}"
puts "Link: #{item.link}"
puts "Description: #{item.description}"
end
end
render template: 'rss_reader/home'
end
And of course. My show.html.erb:
<%= button_to 'Process Source', read_path(#source), method: :get %>
<%= link_to 'Edit', edit_source_path(#source) %> |
<%= link_to 'Back', sources_path %>
</div>
When I press the "Process Source" button, it goes to the right controller method, but the object #source its not being found because of:
Couldn't find Source with 'id'=
# Use callbacks to share common setup or constraints between actions.
def set_source
#source = Source.find(params[:id])
end
What Im doing wrong here?
You are visiting the route with read_path(#source) which is expected to set the param id to the value #source.id, but you did not define your routes to support any parameter in the path.
I believe read is an action that belongs to a single instance of Source. So, you should define the route on member. This way you will be able to access params[:id] in your controller and your before_action set_source will work fine.
Change your route definition to:
resources :sources, only: [...] do # Keep the `only` array just as you have now.
get '/process', to: 'sources#read', as: 'read', on: :member
end

Rails - devise_token_auth - Accessing current user in constraint

I'm using this gem, in my rails-api project.
I'm trying to restrict some routes base on user roles. So I create a constraint
class BackendConstraint
def self.matches?(request)
current_user = request.env['warden'].user
return false if current_user.blank?
current_user.role?(:admin)
end
end
But request.env['warden'].user is always null. Am I'm missing something?
Thanks
I had the same problem with flipper's suggested filter, but found devise has a one liner solution: https://github.com/plataformatec/devise/wiki/How-To:-Define-resource-actions-that-require-authentication-using-routes.rb
eg
authenticate :user do
resources :fjords, only: [:new, :create, :edit, :update, :destroy]
end
resources :fjords, only: [:index, :show]

Rails - route index to first module's object

Say I have a module name Server that was created with a scaffold. I want the url 'www.example.com/server/' to be redirected to the first Server object that exists. So for example to be redirected to 'www.example.com/server/2'.
How could this be done with routes.rb (or any other way)?
route.rb:
Rails.application.routes.draw do
resources :servers
end
Server controller:
class ServersController < ApplicationController
before_action :set_server, only: [:show, :edit, :update, :destroy]
# GET /servers
# GET /servers.json
def index
#servers = Server.all
end
....
your can put
redirect_to server_path(Server.first) and return
inside your index method it'll redirect you when ever index action is called.
and just to extent #richfisher's answer (which might be a more appropriate way to do it.)
resources :servers, except: [:index] # this won't generate redundant routes
get '/servers/' => 'servers#first' #note this is now accessible via "server_path" instead of "servers_path" helper.
For what it's worth, I'd do this:
#config/routes.rb
resources :servers, except: :index do
get "", action: :show, id: Server.first.id, on: :collection
end
This will allow you to use the show action in place of index in a super efficient setup:
#app/controllers/servers_controller.rb
class ServersController < ApplicationController
def show
#server = Server.find params[:id]
end
end

ActionController::UrlGenerationError no route matches

I am still very much new to rails, but cant seem to get a grasp on this route
show.html.erb
<%= link_to "up", vote_movie_review_path(#review, type: "up"), method: "post" %>
rake route
vote_movie_review POST /movies/:movie_id/reviews/:id/vote(.:format) reviews#vote
routes.rb
Rails.application.routes.draw do
devise_for :users
resources :movies do
resources :reviews do
member { post :vote }
end
end
reviews_controller.rb
class ReviewsController < ApplicationController
before_action :set_reviews, only: [:show, :edit, :update, :destroy]
before_action :set_movie
before_action :authenticate_user!
respond_to :html
def index
#reviews = Review.all
respond_with(#reviews)
end
def show
end
def vote
value - params[:type] == "up" ? 1 : -1
#review = Review.find(params[:id])
#review.add_evaluation(:votes, value, current_user)
redirect_to :back, notice: "thanks for the vote"
end
You are using nested routes, so you need to pass movie object also.use like this vote_movie_review_path(#movies, #review, type: "up").
Check your routes, it showing /movies/:movie_id/reviews/:id/vote while the way you are calling it will generate like /reviews/id with method post and for it you have not defined any routes.

Resources