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).
Related
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.
I'm working on an app with controller that have lots of before_actions. Most of them are connected with each other by instance variables that they set. For example:
def first_action
#first_variable = Something.new
end
def second_action
if #first_variable
#second_variable = Other.new
end
end
Controller looks like this:
class ExampleController < ApplicationController
before_action :first_action, only: [:index, :show, :create]
before_action :second_action, only: [:index, :show, :create]
before_action :third_action, only: [:index, :show, :create]
before_action :fourth_action, only: [:index, :show, :create]
before_action :fifth_action, only: [:index, :show, :create]
before_action :sixth_action, only: [:index, :show, :create]
before_action :seventh_action, only: [:index, :show, :create]
def index
# some code
end
def show
# some code
end
def create
# some code
end
private
# all of the before_action methods
end
It's really hard to understand from mine point of view. Each of those method has lots of code. Additionaly there are controllers that inherits from this one and also use part or all of those actions.
I heard that it's better to be explicit about loaded variables in each method but this:
class ExampleController < ApplicationController
def index
first_action
second_action
third_action
fourth_action
fifth_action
sixth_action
seventh_action
# some code
end
def show
first_action
second_action
third_action
fourth_action
fifth_action
sixth_action
seventh_action
# some code
end
def create
first_action
second_action
third_action
fourth_action
fifth_action
sixth_action
seventh_action
# some code
end
private
# all of the before_action methods
end
doesn't look much better. Is there a way to refactor it for more readability or should I stick with current solution?
Your current solution is okay. You can use like to avoid multiple method calls
before_action :first_action, :second_action, :third_action, :fourth_action, :fifth_action, :sixth_action, :seventh_action, only: [:index, :show, :create]
There is nothing wrong with having multiple before_actions - but it looks more like you have a case where they could be collected into one action?
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]
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
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.