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?
Related
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).
I have a controller with the default RESTful route actions (index new create show edit update destroy) and also several other actions. I want to set a before_action that only runs on the default routes.
I know I can add before_action :set_x, only: [:index, :new, :create, :show, :edit, :update, :destroy] to the top of the controller but is there a quicker way to do this? I'd like to do this for multiple controllers, so I cannot do before_action :set_x, except: [:foo, :bar, :baz] because the actions change in each controller and new actions are added all the time.
Thanks!
Options:
define the before_action in the ApplicationController
write skip_before_action in those controllers that do not need it
define another "controller"-class, that derives from ApplicationController, add the before_action and only those controllers that need it, will inherit from that class
create a module with only the before_action and include it
I have two layout:
Dashboard (Where logged user can Add/EDIT/Delete new Post)
Public view (where anybody can read Posts)
I would like to get urls like:
dashboard_post_path and
post_path
but I don't want to have two different controllers (eg. PostController and DashboardPostController), because I want to share the same code and switch only the layout ('dashboard' layout OR 'default' layout).
I've tried this
namespace :dashboard do
resources :posts
end
but I get
because it looks for dashboard/posts#index controller
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
before_filter :authenticate_user!, only: [:edit, :update, :destroy, :new]
layout 'dashboard'
//...
end
You can write conditional layout like below
Conditional Layouts
Layouts specified at the controller level support the :only and :except options. These options take either a method name, or an array of method names, corresponding to method names within the controller:
class EventsController < ApplicationController
layout :resolve_layout
def resolve_layout
case action_name
when "show"
"post_layout"
when "index"
"dashboard_layout"
else
"default_layout"
end
end
end
Are you looking something like this?
So, I have this code in a controller:
before_filter :require_login, :only => :new, :edit, :destroy
My controller has these methods: index, new, edit, create, update, show, destroy.
What I want to do is to protect with login_required (:require_login in the code) the methods: new, edit, destroy, but the above code doesn't work, I can protect one method if i have, for example:
before_filter :require_login, :only => :new
But I want to protect the three of them, How can I do it?
You're missing square brackets around the only option's value:
before_filter :require_login, :only => [:new, :edit, :destroy]
It's not working because the Ruby interpreter doesn't know where the options for only start and the arguments for before_filter continue. This is case where you need to be explicit about the container.
Use an array:
before_filter :require_login, :only => [:new, :edit, :destroy]
Oh I was a FOOL!!! it was just matter of putting them in array form -.- like this:
before_filter :require_login, :only => [:new, :edit, :destroy]
Sorry for the obvious question.
I'm using Authologic and some written methods for managing users and what they can do in my app. I don't really need the ability to have multiple roles, I just need the ability for me to destroy things. I am wondering if I can hack this together.
In my "QuestionsController" I am using the following filters:
before_filter :require_user, :only => [:edit, :update, :destroy] # all actions require user to be logged in
before_filter :init_data # create a member variable called #post, initialized based on the action
before_filter :require_owner, :only => [:edit, :update, :destroy] #edit, update, and destroy actions require ownership
I am trying to figure out if I can wrap those filters with a condition that specifies if the current_user's username is "bgadoci" then don't do the filters. Is this even possible and if so what syntax should I use (kind of new to ruby and rails).
Here is what I have now which is giving a syntax error in the first line (obviously).
if :current_user :username => "bgadoci"
before_filter :require_user, :only => [:edit, :update, :destroy] # all actions require user to be logged in
before_filter :init_data # create a member variable called #post, initialized based on the action
before_filter :require_owner, :only => [:edit, :update, :destroy] #edit, update, and destroy actions require ownership
end
Maybe you can do a before filter that wraps those other filters?
before_filter :check_for_me
def check_for_me
unless current_user.username == 'me'
before_filter :require_user
..
end
end