I use Devise for authentication in my Rails app.
In my registrations_controller I set an instance variable like this:
class RegistrationsController < Devise::RegistrationsController
def create
#foo = "bar"
super
end
end
In my customized mailer I then try to access the #foo instance variable, but it just returns nil:
class CustomMailer < Devise::Mailer
helper :application
include Devise::Controllers::UrlHelpers
def confirmation_instructions(record, token, opts={})
Rails.logger.error #foo.inspect # => nil
super
end
end
Anyone who could help?
I have looked through the posts How do I add instance variables to Devise email templates?, How to pass additional data to devise mailer?, How to pass instance variable to devise custom mailer in Rails?. But none of them seem to deal with this exact problem.
First let me explain you have instance variables work in ruby classes.
In ruby, instance variable(in your case #foo) in a class(in your case RegistrationsController) do not pass down to other classes(in your case CustomMailer) even if inheritance is ON. For Example consider:
class Abc
#first = "First"
puts "Abc: #{#first}"
end
class Def < Abc
puts "Def: #{#first}"
end
# => Abc: First instance variable
# => Def:
As you can see the class Def cannot have access to #first instance variable. Thus, instance variables don't get passed down to other classes automatically.
If you want these variables to pass down the class, you should consider using Class Instance Variables which starts with ##. Ex:
class Abc
##first = "First instance variable"
puts "Abc: #{##first}"
end
class Def < Abc
puts "Def: #{##first}"
end
# => Abc: First instance variable
# => Def: First instance variable
Now ##first will pass down to inherited class automatically.
Now, relate the above senario to your question. So you're creating a instance variable #foo in RegistrationsController and it will not be passed to other inherited classes. Under the hood both Devise::RegistrationsController and DeviseMailer are inherited from Devise.parent_controller.
So, better way to work with it is to send the #foo as a parameter, for example:
class RegistrationsController < Devise::RegistrationsController
def create
#foo = "bar"
CustomMailer.confirmation_instructions(user, token, opts={foo: #foo})
...
end
end
Then you can access this in your CustomMailer:
def confirmation_instructions(record, token, opts={})
puts opts[:foo]
super
end
Related
In my Rails application I have a class that I want to initialize and then access it throughout my controllers. So the idea is that I set it via the application controller if it's not already been defined:
class ApplicationController < ActionController::Base
before_action :set_custom_class
# create an instance of customclass if doesn't exist
def set_custom_class
#custom_class ||= CustomClass.new
end
end
An example of the class:
class CustomClass
def initialize; end
def custom_method
#custom_method
end
def custom_method=(content)
#custom_method = content
end
end
If I then have a controller like:
class MyController < ApplicationController
def method_1
# set the custom_method value on my instance
#custom_class.custom_method('Some content')
# return the value I set above
#variable = #custom_class.custom_method
redirect_to :method_2
end
def method_2
# I should be able to retrieve the same value from that same instance
#variable = #custom_class.custom_method
end
end
What I'm finding is that when calling method_1 the #variable will return my content fine, but when calling method_2 AFTER method_1 (so the custom_method for the app wide #custom_class has been set) it's returning nil.
Why isn't the instance being retained? The #custom_class shouldn't be creating a new instance as it's already been set. So I can't understand why the value I have set gets lost when requesting it.
You witnessing such behaviour, because state of a controller is not preserved between requests. For example, imagine that current_user method sets #current_user for one request and returns the same user for another one.
Please, consider an option of using cookies or database for sharing state between requests.
Otherwise, a workaround would be setting a class variable of CustomClass, but I don't recommend to do it.
Looks like your before_action will re-instantiate the new object on every request. That means that since you aren't passing anything through to the class in Method2, it will come out as NULL.
Since you said app-wide, why not make it app-wide?
In config/application.rb,
module App
class Application < Rails::Application
def custom_class
#custom_class ||= CustomClass.new
end
end
end
in your application code,
Rails.application.custom_class
I am trying to access helper method in my controller using helpers like below:
class MyController < ApplicationController
def index
#foo = 'bar'
helpers.my_helper_method
end
end
Inside Helper method, I am trying to access an instance variable of controller
module MyHelper
def my_helper_method
#some manipulation on foo
#foo.to_i
end
end
But in above scenario #foo is nil. When I call the same method from view, #foo is available. So the instance variable can be passed to helper method only through UI or some other way is there?
Thanks in advance.
UPDATE:
view_context
seems like reasonable solution https://apidock.com/rails/AbstractController/Rendering/view_context
class MyController < ApplicationController
def index
#foo = 'bar'
helpers.my_helper_method(#foo)
end
end
module MyHelper
def my_helper_method(foo)
#some manipulation on foo
foo.to_i
end
end
pass it as argument.
You can access instance variables that you set in a controller in your helpers. If the value is nil, then you need to deal with it in your helper:
module SomeHelper
def do_something
return 0 if !#value
value * 3
end
end
class SomeController
def index
#value = 1
helpers.do_something
end
def show
#value = nil
helpers.do_something
end
end
How can I read a class variable? In my case I can't get value from variable.
class MediaContentsController < ApplicationController
#randomUsrId = rand(100)
def create
puts #randomUsrId
end
end
First of all, #randomUsrId refers to an instance variable, not a class variable. You can access it through an instance of the class, not direct on the class. For a class variable, you should use ##randomUsrId.
What you are actually looking for is attr_accessor :randomUsrId, through this, you can read it on an instance method, and even can set it through an instance of the class.
Here's how:
class MediaContentsController < ApplicationController
attr_accessor :randomUsrId
#randomUsrId = rand(100)
def create
puts #randomUsrId
end
end
But #randomUsrId = rand(100) won't set #randomUsrId to a random number, at least it is not the recommend way. You should use before_action here.
class MediaContentsController < ApplicationController
attr_accessor :randomUsrId
before_action :set_user_id_to_a_random_number, only: :create
def create
puts #randomUsrId
end
private
def set_user_id_to_a_random_number
#randomUsrId = rand(100)
end
end
Edit:
Each time you call the set_user_id_to_a_random_number function, it will generate a different number based on rand(100) and store it inside #randomUsrId. If that's what you do not want, and you want to persist the same value, you can do something like following:
def set_user_id_to_a_random_number
#randomUsrId = rand(100) unless defined? #randomUsrId
end
Edit:
What I have stated works only for one request, if you have multiple request, it won't work. As Ryan Bates says here:
An instance variable only sticks around for a single request, so using the technique described will only benefit you if you need to call a method multiple times per request.
That leaves you with two options if you want to store something between multiple requests. Either you can go with Databases, or you can use something called memcached.
You probably doing something wrong in your code, since this isn't how Rails controller logic should be usually implemented, but let's get down to your question anyway. As I mentioned it isn't class variable, it's instance variable in class scope, so in order to access it, you should first get it from that scope:
class MediaContentsController
#randomUsrId = rand(100)
def create
puts self.class.get_random_usr_id
end
def self.get_random_usr_id
#randomUsrId
end
end
MediaContentsController.new.create
# => 44
I am trying to do a custom active record macro. But it right now seems impossible set an instance variable from within it's block.. here is what i am trying to do.
module ActiveRecord
class Base
def self.included(base)
base.class.send(:define_method, :my_macro) do |args|
# instance_variable_set for the model instance that has called this
# macro using args
end
end
end
end
i have tried class_eval, instance_eval.. but nothing seems to work or i don't how to use them.
Thanks in advance.
Edit: Let me try to explain better. I have a class method. An instance of the class calls this method. Now, this class method should instruct the instance to set an instance variable for itself.
Edit- this is how i want o use the macro
class MyModel < ActiveRecord::Base
my_macro(*args)
def after_initialize
# use the value set in the macro as #instance variable
end
end
Is this what you are thinking of:
class DynamicAdd
def add_method
self.class_eval do
attr_accessor :some_method
end
end
end
You can then do the following:
k = DynamicAdd.new
k.some_method = "hi"
should result in an undefined method error.
But,
k = DynamicAdd.new
k.add_method
k.some_method = "hi"
should work.
You can use this same format to define other types of methods besides attr_accessors as well:
class DynamicAdd
def add_method
self.class_eval do
def some_method
return "hi"
end
end
end
end
Hm.. Isn't included() a Module method? I don't think you can use that in a class like you have written. If you want to create a class method you can do
class Base
def self.my_method
end
or
class Base
class << self
def my_method
end
end
If all you want to do is to add an instance variable to an existing object, then you can use #instance_variable_set
class Base
class << self
def my_method(instance_of_base, value)
instance_of_base.instance_variable_set "#x", value
end
end
end
a = Base.new
a.class.send(:my_method, *[a,4])
I'm using the facebooker gem which creates a variable called facebook_session in the controller scope (meaning when I can call facebook_session.user.name from the userscontroller section its okay). However when I'm rewriting the full_name function (located in my model) i can't access the facebook_session variable.
You'll have to pass the value into your model at some point, then store it if you need to access it regularly.
Models aren't allowed to pull data from controllers -- it would break things in console view, unit testing and in a few other situations.
The simplest answer is something like this:
class User
attr_accessor :facebook_name
before_create :update_full_name
def calculated_full_name
facebook_name || "not sure"
end
def update_full_name
full_name ||= calculated_full_name
end
end
class UsersController
def create
#user = User.new params[:user]
#user.facebook_name = facebook_session.user.name
#user.save
end
end