Ruby on Rails variable visibility - ruby-on-rails

I have many controllers where I set variables so that the appropriate views gain visibility to these variables. For example,
class UsersController < ApplicationController
...
def index
#users = User.all
end
...
This works. So how come this doesn't work:
class PlacesController < ApplicationController
...
def show
#params = params
end
...
If I byebug in the show-method, I can access params. If I byebug in the view (places/show.html.erb), then "params" and "#params" return nil.
Does this have something to do with the fact that "User" is an ActiveRecord and "Place" is not? How can I make arbitrary data accessible to the view?

You can use your controller instance variable #params in your /places views. But because you did not pass any params, params returns nil.
So, if this variables was out visibility, you would get NameError.
Try to set #params directly like #params = { foo: "bar" } and you will see it.
More about params in Rails here (#4 Parameters).

Related

Are arguments passed into UserMailer available to before_actions?

I want to assign the same set of instance variables for multiple emails based on the argument passed into mailer. Something like this:
class UserMailer < ActionMailer::Base
before_action -> { set_defaults(order) }, only: [:email1, :email2]
def email1(order)
...
end
def email2(order)
...
end
private
set_defaults(order)
#order = order
#customer = order.customer
#price = order.price
end
end
I see that you can pass params, strings, and the like to before_actions in controllers: Rails 4 before_action, pass parameters to invoked method
And it looks like I can use process_action as workaround: Rails before_action for ActionMailer that would use mailer arguments
But is there a way to access arguments? Are they out of scope? Help.
This ?
DO_ME_BEFORE = [:email1, :email2]
def process_action(*args)
return super unless DO_ME_BEFORE.include?(args[0].to_sym)
#order = args[1]
#customer = #order.customer
#price = #order.price
super
end
Edit:
I don't think you can hook exactly in the same way as with before_action, but you can simulate it, since your args[0] will be the name of the method. If you write the array of allowed methods as strings, you don't need to call .to_sym on args[0] on the .include?()

Why is my params hash nil?

In my rails controller, in a pry session, my params hash is nil. request.params has the expected hash.
If I comment out the params = … line, params returns to normal.
class UsersController < Clearance::UsersController
skip_before_filter :verify_authenticity_token
def update
binding.pry
params = params.require(:user).allow(:foo)
end
end
What could be causing this?
To begin with, params is a method. http://api.rubyonrails.org/classes/ActionController/StrongParameters.html#method-i-params
In your code, when you write
params = params.require(:user).allow(:foo)
you initialize a variable called params.
When you hit your pry, you have already initialized (gets initialized upon loading of code) but not yet set the value of your variable, params. Therefore, when you call it within your pry, it is set to nil. The method params is overwritten by your variable within the scope of your update method.
The params hash is what you get when the user request the page. For example:
https://www.example.com/index.html?username=john&email=john#example.com
The params hash would be
{username: 'john', email: 'john#example.com'}
And you can assess then like params[:username].
Looks like you are trying to use strong parameters to set what the user can or cannot update. In that case, what you should do is
def update
user = User.find(params[:id])
user.update_attributes(params.require(:user).permit(:foo))
end
This will only allow the user to update the foo attribute and nothing else.
Because this is so common, it is standard to write a private method called user_params and just call that method when you call save.
def update
user = User.find(params[:id])
user.update_attributes(user_params)
end
private
def user_params
params.require(:user).permit(:foo)
end

Rails - Controller Object Scope vs Controller Function Scope

I have seen local variables as well as object variables being used in Rails controller actions. An example of both is given below:
# Local variable
class MyController < ApplicationController
def some_action
local_variable = Model.find(<some-condition>).delete
end
end
# Object variable
class MyController < ApplicationController
def some_action
#object_variable = Model.find(<some-condition>).delete
end
end
I want to know what is the difference between both of them and the scenarios that they are both suited to be used in.
Rails exports the controller's instance variables into what is called the view context:
class UserController < ApplicationController
def new
#user = User.new
end
end
# the view gets any # variables from the controller.
# views/users/new.html.haml
= form_for(#user) do
Rails also offers another mechanism called locals as well:
class UserController < ApplicationController
def new
render :new, locals: { user: User.new }
end
end
# locals are lexical variables in the view context.
# views/users/new.html.haml
= form_for(user) do
Which exports a local variable to the view context.
So when do you use what?
Use lexical (local) variables (some_variable) for anything you do not want to implicitly export to the view. Use the locals option when rendering when you need to pass data between views and partials or things that are not quite part of the "public api" of your controller.
Use instance variables (#foo) only for important exports from your controller and treat them as part of a public API. Make sure you test them:
describe UserController do
describe "#new" do
before { get :new }
it "assigns as new user as #user" do
expect(assigns(:user)).to be_a_new_record
end
end
end
In your provided code, local_variable would only be available to the current method in the controller. Your #object_variable would be available to the method, but also to the view (to be accesses directly as #object_variable)
Therefore, you should reserve using #object_variable for only when you want to use the variable in your views.

Rails: How to get the model class name based on the controller class name?

class HouseBuyersController < ...
def my_method
# How could I get here the relevant model name, i.e. "HouseBuyer" ?
end
end
This will do it:
class HouseBuyersController < ApplicationController
def index
#model_name = controller_name.classify
end
end
This is often needed when abstracting controller actions:
class HouseBuyersController < ApplicationController
def index
# Equivalent of #house_buyers = HouseBuyer.find(:all)
objects = controller_name.classify.constantize.find(:all)
instance_variable_set("##{controller_name}", objects)
end
end
If your controller and model are in the same namespace, then what you want is
controller_path.classify
controller_path gives you the namespace; controller_name doesn't.
For example, if your controller is
Admin::RolesController
then:
controller_path.classify # "Admin::Role" # CORRECT
controller_name.classify # "Role" # INCORRECT
It's a bit of a hack, but if your model is named after your controller name then:
class HouseBuyersController < ApplicationController
def my_method
#model_name = self.class.name.sub("Controller", "").singularize
end
end
... would give you "HouseBuyer" in your #model_name instance variable.
Again, this makes a huge assumption that "HouseBuyersController" only deals with "HouseBuyer" models.
For namespaces working:
def resource_class
controller_path.classify.constantize
end
The accepted solution did not work for me as my controller and model was namespaced. Instead, I came up with the following method:
def controllers_model
(self.class.name.split('::')[0..-2] << controller_name.classify).join('::')
end
This is not possible if you are using the default MVC, which your code doesn't seem to follow. Your controller seems to be a model but maybe you just got a type there. Anyway, controllers and models are fundamentally separated in Rails MVC so controllers cannot know which model they are associated with.
For example you could have a model named post. This can have a controller posts_controller or could have a controller like articles_controller. Rails only knows about models when you def the actual code in the controller such as
def index
#posts = Post.all
#posts = Article.all
end
In rails standard controllers there is no way to know what the model is.

How to pass values between controller methods

Is there any way to share an array between controller methods and store it until page reloads or calling method of another controller? Some methods should change the array.
you can use rails cache.
Rails.cache.write("list",[1,2,3])
Rails.cache.read("list")
If you want to share the value across the methods of a same controller instance then,
declare an instance variable:
class BarsController < UsersController
before_filter :init_foo_list
def method1
render :method2
end
def method2
#foo_list.each do | item|
# do something
end
end
def init_foo_list
#foo_list ||= ['Money', 'Animals', 'Ummagumma']
end
end
If you want to share the value across two controllers withn a session, then:
class BarsController < UsersController
before_filter :init_foo_list
def method1
render :controller => "FoosController", :action => "method2"
end
def init_foo_list
params[:shared_param__] ||= ['Money', 'Animals', 'Ummagumma']
end
end
class FoosController < UsersController
def method2
params[:shared_param__].each do | item|
# do something
end
end
end
Give an unique name to the shared parameter key so as to avoid collision with existing keys.
Other option is to store the shared array in the session ad delete it before the final render.
I am not sure whether my answer is close to your requirement, but this is what I do if I want to get the value of an object/model which is fetched in one controller action and basis on that value I need to fetch other values in another controller action.
I make use of class variables and use it throughout my controller action
for eg:
#pages=Post.find.all`
##my_value=#pages.(any manipulations)
now ##my_vales can be used in any actions of that controller..
hope it helps...

Resources