Access to class instance variable - ruby-on-rails

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

Related

Set an instance variable app wide in Rails

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

How can I create an instance variable to all instances of the class in ruby, using metaprogramming?

I'm new to ruby/rails world, and I'm facing and issue that appears to be something trivial, but it isn't being that easy for me.
What I want to do, is to write a simple helper method, to help keep the controllers clean. This method will receive a symbol/string, and create an instance variable with the camelized received string, which will evaluate to a constant/class.
My attempts resume themselves to this:
class ApplicationController < ActionController::Base
...
protected
def self.service(name)
instance_eval do
instance_variable_set("##{name.to_s.pluralize}", name.to_s.camelize.constantize)
end
end
And, in my controller:
class UserController < ApplicationController
service :user
But when I do this, it create the instance variables in UserController, not UserController.new. So, I know what is wrong, but I couldn't find how can I do this right.
Can anyone help me with that?
The trouble is that this method is in the class scope:
def self.service(name)
instance_eval do
instance_variable_set("##{name.to_s.pluralize}", name.to_s.camelize.constantize)
end
end
This means it'll be setting class level instance variables - or class variables (kinda).
If you want to follow this approach, you'll need to store these all in an array or similar at the class level, which can be read out and turned into instance variables when the class is instantiated.
However, I don't like to mess with the initialize method on controllers. It's not the way to do things, so could change the class behaviour, break things or become fragile when changing versions.
In your ApplicationController, something like:
class << self
def service(name)
#services ||= []
return #services += name.map(&:to_sym) if name.is_a?(Array)
#services << name.to_sym
end
def service_names
#services
end
end
before_action :services
def services
self.class.service_names.each do |name|
instance_eval do
instance_variable_set("##{name.to_s.pluralize}", name.to_s.camelize.constantize)
end
end
end
This is untested.

RoR: instances variables within controller methods

My question is about controller methods (possibly included from an outside class) that work with instance variables. I frequently use a before_filter in controllers to set up certain variables, e.g.:
class DocumentController < ApplicationController
before_filter :fetch_document
def action
#document.do_something
end
private
def fetch_document
#document = Document.find(params[:id])
end
end
I've been working on a project in which a few controllers will share some functionality, say, document editing. My first thought was to extract the relevant methods, and get them from application_controller.rb or a separate module. But then I noticed I was writing code that looks like this:
def fetch_document
#document = Document.find(params[:id])
end
def do_something_to_document
#document.do_something
end
This sets off warning bells: do_something_to_document is essentially assuming the existence of #document, rather than taking it as an argument. Is this, in your sage opinions, a bad coding practice? Or am I being paranoid?
Assuming it is an issue, I see two general approaches to deal with it:
Check for the instance var and bail unless it's set:
def do_something_to_document
raise "no doc!" unless #document
[...]
end
Call the action with the instance var as an argument:
def do_something_to_document(document)
[...]
end
2 looks better, because it hides the context of the calling object. But do_something_to_doc will only be called by controllers that have already set up #document, and taking #document as a method argument incurs the overhead of object creation. (Right?) 1 seems hackish, but should cover all of the cases.
I'm inclined to go with 1 (assuming I'm right about the performance issue), even though seeing a list of methods referencing mysterious instance vars gives me hives. Thoughts? Let me know if I can be more clear. (And of course, if this is answered somewhere I didn't see it, just point me in the right direction...)
Thanks,
-Erik
If you really need document in different controllers, I'd do something like this:
class ApplicationController < ActionController::Base
private
def document
#document ||= Document.find(params[:document_id])
end
end
class FooController < ApplicationController
before_filter :ensure_document, :only => [:foo]
def foo
document.do_something
end
private
# TODO: not sure if controller_name/action_name still exists
def ensure_document
raise "#{controller_name}##{action_name} needs a document" unless document
end
end
As #variable are session/instance variable you will get a Nil exception in do_something_to_document method.
The first code is fine, because before_filter will always load your #document.
I suggest you to write something like that
def fetch_document(doc_id)
#document ||= Document.find(doc_id)
end
def do_something_to_document
my_doc = fetch_document(params[:id])
end
where do_something_to_document is in the controller (if not, dont use params[:id], even if you know you can access this global, use another explicit parameter). The ||= thing, will asssure that you call the base only once by request.

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...

attributes and constructors in rails

I'm new to rails and don't even know if this is the correct way of solving my situation.
I have a "Club" ActiveRecords model which has a "has_many" association to a "Member" model. I want the logged in "Club" to only be able to administrate it's own "Member" so in the beginning of each action in the "Member" model I did something similar to the following:
def index
#members = Club.find(session[:club_id]).members
to access the right members. This did not however turn out very DRY as I did the same in every action. So I thought of using something equivalent to what would be called a constructor in other languages. The initialize method as I've understood it. This was however not working, this told me why, and proposed an alternative. The after_initialize.
def after_initialize
#club = Club.find(session[:club_id])
end
def index
#members = #club.members
....
does not seem to work anyway. Any pointers to why?
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.members
Makes me think that the #club var isn't set at all.
Also, is this solution really a good one? This makes it hard to implement any kind of "super admin" who can manage the members in all of the clubs. Any ideas on where I am missing something?
You can use a before_filter.
Define the filter in your ApplicationController (so that you can access it from any controller).
class ApplicationController < ActionController::Base
# ..
protected
def load_members
#members = if session[:club_id]
Club.find(session[:club_id]).members
else
[]
end
end
end
Then, load the filter before any action where you need it.
For example
class ClubController < ApplicationController
before_filter :load_members, :only => %w( index )
def index
# here #members is set
end
end
Otherwise, use lazy loading. You can use the same load_members and call it whenever you need it.
class ClubController < ApplicationController
def index
# do something with members
load_members.each { ... }
end
end
Of course, you can customize load_member to raise an exception, redirect the client if #members.empty? or do whatever you want.
You want to use a before_filter for this.
class MembersController < ApplicationController
before_filter :find_club
def index
#members = #club.members
end
private
def find_club
#club = Club.find(session[:club_id])
end
end
I'm a fan of a plugin called Rolerequirement. It allows you to make custom roles and apply them by controller: http://code.google.com/p/rolerequirement/

Resources