How can I pass variable from one controller to another? (rails) - ruby-on-rails

I have controller with action (welcome#index):
class WelcomeController < ApplicationController
def index
#card = current_user.cards.review_before(Date.today).first
#my_test_variable #this variable from another controller
end
end
and I have another controller:
class CardsController < ApplicationController
def review
if #card.check = true
#my_test_variable = 1
else
#my_test_variable = 2
end
redirect_to root_path #redirect to welcome#index
end
end
How can I put #my_test_variable value to action index controller Welcome to use it in view index?

I think what you're asking is how to make them available to the next request. Put them in the session:
class CardsController < ApplicationController
def review
if #card.check = true
session[:my_test_variable] = 1
else
session[:my_test_variable] = 2
end
redirect_to root_path #redirect to welcome#index
end
end
class WelcomeController < ApplicationController
def index
#card = current_user.cards.review_before(Date.today).first
session[:my_test_variable]
end
end

I will not question why you would want to do this but one solution is redirect to root_path with a parameter and then grab it in the other controller:
class CardsController < ApplicationController
def review
if #card.check == true
#my_test_variable = 1
else
#my_test_variable = 2
end
redirect_to root_path(my_test_variable: #my_test_variable)
end
end
class WelcomeController < ApplicationController
def index
#card = current_user.cards.review_before(Date.today).first
#my_test_variable = params[:my_test_variable] # will be a string
end
end
(and btw, you have a typo in the if statement. should be ==, not =)

The easy way would be making the variable global so that it can be assessed from any controller and later making it null.
class ModelController < ApplicationController
def index
$count = 125
end
end
class PaymentController < ApplicationController
def price
puts $count
end
end

The login in the cards controller should probably be in the model, then you don't have to worry about the approach you have taken.
It is highly uncommon to have 2 controller instances involved with processing one request.

Related

Pundit policy for personalized routes

I'm working on a rails app where I wrote a personalized route called "all_designs"; with the corresponding method on the controller and the view, before I add pundit to my project it was working fine.
Now I'm having this error:
Pundit::AuthorizationNotPerformedError in DesignsController#all_designs
I understand that I'm missing a policy for this action, but the way I'm trying is not working.
How can I add a policy for this method?
Controller:
class DesignsController < ApplicationController
before_action :set_design, only: [:show,:edit,:update,:destroy]
def index
#designs = policy_scope(Design.where(user: current_user, status: 'activo'))
#user = current_user
end
def all_designs
#designs = Design.where(user: current_user)
#user = current_user
end
...
end
Policy:
class DesignPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.all
end
end
def create?
true
end
def show?
true
end
def destroy?
user == record.user
end
def update?
# If the user is the owner of the design
user == record.user
end
def all_designs?
true
end
end
I would consider separate controller and policy for this as what you're doing is really just a nested route (designs belonging to a singleton resource).
scope 'user', module: :users do
resources :designs, only: :index
end
module Users
class DesignsPolicy
class Scope < Scope
def resolve
#user.designs # make sure user has a `has_many :designs` assocation.
end
end
end
def index?
true
end
end
# Represents designs belonging to the current user
module Users
class DesignsController < ApplicationController
# GET /user/designs
def index
#designs = policy_scope
end
end
end
This lets you separate the logic of displaying the the current users designs from /designs which would display everything in a clean way.
Every method on the controller which needs to be authorized, needs to contains an explicit declaration like this:
def all_designs
#designs = Design.where(user: current_user)
#user = current_user
authorize #designs
end
The reason it wasn't working was: I missed the authorize line

How does implicit Rails rendering work with super in controller methods?

In the below, will the implicit render still get called in the SpecialArticlesController? Or will it get skipped to the implicit render in ArticlesController?
class SpecialArticlesController < ArticlesController
def index
...
super
end
end
class ArticlesController
def index
...
end
end
You will not get a double render error, unless you are explicitly rendering twice. This code will cause an error:
class ParentController < ApplicationController
def index
render
end
end
class ChildController < ParentController
def index
render
super
end
end
Whereas the code below will not cause an error:
class ParentController < ApplicationController
def index
end
end
class ChildController < ParentController
def index
super
render
end
end

Rails pass value/object/orm from parent controller to child

To keep to restfull protocol, I need to do /api/backup_jobs/777/errors.
In rails, the parent controller- I have:
module Api
class BackupJobsController < ApplicationController
respond_to :json
def show
#backup_job = #backup_jobs.find(params[:id])
respond_with data: #backup_job
end
end
end
in the child controller:
module Api
class ErrorsController < BackupJobsController
respond_to :json
def index
respond_with data: #backup_jobs.find(params[:id]).backup_events.errors
end
end
end
But obvisouley this isn't going to work because params[] doesn't exist for /api/backup_jobs/777/errors
How can I pass the #backup_job = #backup_jobs.find(params[:id]) from the parent controller's def show to the child controller and have it accessible in the child's def index?
You cannot do that because when an ErrorsController is created and used, you will not have a BackupsJobsController that ran before it.
This comes down to the nature of HTTP being a request-response protocol.
Instead, you can extract the line of code you wrote into a method that will be inherited by the ErrorsController.
backup_jobs_controller.rb:
module Api
class BackupJobsController < ApplicationController
def show
find_backup_job
respond_with data: #backup_job
end
protected
def find_backup_job
#backup_job = #backup_jobs.find(params[:id])
# or maybe #backup_job = BackupJob.find(params[:id])
end
end
end
errors_controller.rb:
module Api
class ErrorsController < BackupJobsController
respond_to :json
def index
respond_with data: find_backup_job.backup_events.errors
end
protected
def find_backup_job
#backup_job = BackupJob.find(params[:backup_job_id])
end
end
end

Rails API - keeping an application controller method DRY

I have a method in my Rails application controller that I call when I am creating a new Post. I have also created an API to create a new Post. However, it seems that I need to repeat the code for my application controller method in my API BaseController. Where is the best place to put the application controller method in my Rails app so that I do not have to repeat the code for the API? Is there a way that the API base controller can inherit from the ApplicationController?
Rails app
class PostsController < ApplicationController
def create
#post = Post.new(post_params)
#post.text = foo_action(#post.text)
if #post.save
redirect_to posts_path
else
render :new
end
end
end
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
def foo_action(string)
return string
end
end
Rails API
class Api::V1::PostsController < Api::V1::BaseController
def create
#post = Post.new(post_params)
#post.text = foo_action(#post.text)
if #post.save
respond_with(#post)
end
end
end
class Api::V1::BaseController < ActionController::Base
respond_to :json
def foo_action(string)
return string
end
end
Based on #phoet's recommendation in the comments above, I moved the foo_action method to the Post model:
class Post < ActiveRecord::Base
def foo_action
string = self.text
return string
end
end
class PostsController < ApplicationController
def create
#post = Post.new(post_params)
#post.text = #post.foo_action
if #post.save
redirect_to posts_path
else
render :new
end
end
end
class Api::V1::PostsController < Api::V1::BaseController
def create
#post = Post.new(post_params)
#post.text = #post.foo_action
if #post.save
respond_with(#post)
end
end
end

inheritance in controllers

I use inheritance in my model. An event has different types:
Event < activity
Event < training
Event < game
I want to set session data to every event type like
game.user_id = session[:user_id]
training.user_id = session[:user_id]
activity.user_id = session[:user_id]
I want to avoid writing #game.user_id = session[:user_id] , ..., ... in every create method in the controller of activity, game and training
Someone knows how to approach this best.
Thanks
Perhaps you are looking for a before_filter that resides in your ApplicationController? Then in each controller, you can set the before_filter to run on create actions.
ApplicationController
def set_user_ids
game.user_id = session[:user_id]
training.user_id = session[:user_id]
activity.user_id = session[:user_id]
end
...
end
OneController < ApplicationController
before_filter :set_user_ids, :only => [:create]
...
end
TwoController < ApplicationController
before_filter :set_user_ids, :only => [:create]
...
end
Don't use game.user_id, instead you can do this:
game = current_user.games.build(params[:game])
if game.save
# do something
else
# do something else
end
Repeat for your other controllers too!
The associations guide may be helpful too.
Generally you'll want to use the built-in scoping that Rails provides. Just to flesh out what #Radar already posted:
class ApplicationController < ActionController::Base
before_filter :find_current_user
private
def find_current_user
#current_user = User.find( session[:user_id] )
end
end
class EventsController < ApplicationController
def create
#event = #current_user.events.build( params[:event] )
#event.save!
end
end
This assumes that you have setup the associations in your model:
class User
has_many :events
end
class Event
belongs_to :user
end
This is also a rather handy mechanism if you need to restrict what a user can see or edit:
class EventsController < ApplicationController
def index
#events = #current_user.events # only fetch current users events
end
def update
#event = #current_user.events.find( params[:id] ) # can't update other user's events
#event.update_attributes!( params[:event] )
end
end

Resources