Instance variable in model can not be accessed from within method - ruby-on-rails

I'm working on a Rails app, and I've run into a small issue, but I can't seem to find any information pertaining to my certain case.
I'm getting an undefined method error '[]' for NilClass when calling the add_user method in an instance of the class below.
class Group < ActiveRecord::Base
has_and_belongs_to_many :users
#user_info = Hash.new
def add_user(user_id)
new_info = OpenStruct.new
new_info.score1 = 0
new_info.score2 = 0
#user_info[user_id] = new_info
end
...
end
Why is this happening and how can I fix it? In a C++, Java program the equivalent would work fine but I guess Ruby works a bit differently.

When you use #user_info = Hash.new in the body of Group's class definition, the scoping rules of Ruby dictate that #user_info is an instance variable of the Group instance of the class Class. It is accessible only when self is Group, not when self is an instance of Group.
You can give yourself access to the class-level instance variable by defining a singleton method on Group.
class Group < ActiveRecord::Base
has_and_belongs_to_many :users
#user_info = Hash.new
def self.user_info
#user_info
end
def add_user(user_id)
new_info = OpenStruct.new
new_info.score1 = 0
new_info.score2 = 0
self.class.user_info[user_id] = new_info
# OR: Group.user_info[user_id] = new_info
end
end

Since your class is a subclass of ActiveRecord::Base, you probably should not override the constructor. You can do the initialization in the after_initialize hook:
class Group < ActiveRecord::Base
after_initialize do
#user_info = {}
end
# Other stuff here
end
BTW, there's a huge difference between ruby and other languages: The instance variables defined directly in the class belong to the class itself, not its instances. Only those instance variables defined in the instance methods belong to that class's instances.

Related

Rails model definition using related field

I have a Rails app that is multi-tenant. In the Tenant record, I store codes that are particular to that Tenant.
The following works well (PS - scope doesn't work):
class Worequest < ActiveRecord::Base
acts_as_tenant(:tenant)
def self.closed
where("statuscode_id = ?", ActsAsTenant.current_tenant.request_closed)
end
What I really need is not worequest.statuscode_id but instead worequest.statuscode.position.
I tried this:
def self.closed
self.statuscode.position = ActsAsTenant.current_tenant.request_closed
end
But, that gives this error:
undefined method `statuscode'
Thanks for the help!
Your method is a class method. It means that your code is referring to the class via self object.
def self.closed
self.statuscode.position = ActsAsTenant.current_tenant.request_closed
end
self here is class Worequest and it doesn't have an attribute statuscode.
What are you trying to with self.statuscode.position = ActsAsTenant.current_tenant.request_closed?

Can't access class variable in ruby/rails?

I have a class like so:
Railsapp/lib/five9_providers/record_provider.rb:
class Five9Providers::RecordProvider < Five9Providers::BaseProvider
def add_record_to_list
variable = 'test'
end
end
Then, in a controller I have this:
Railsapp/app/controllers/five9_controller.rb:
class Five9Controller < ApplicationController
def import
record_provider = Five9Providers::RecordProvider.new()
record_provider.add_record_to_list
puts Five9Providers::RecordProvider::variable
end
end
However, calling my controller method import just returns:
NoMethodError (undefined method 'variable' for Five9Providers::RecordProvider:Class)
How can I access variable from the recover_provider.rb class in my five9_controller.rb class?
EDIT:
Even when using ##variable in both my record_provider and my five9_controller, I still can't access that variable. I am calling it like so: puts ##variable.
As written, you cannot. variable is local to the instance method and can't be accessed by any Ruby expression from outside the method.
On a related point, the term "class variable" is typically used to refer to variables of the form ##variable.
Update: In response to your "Edit" statement, if you change variable to ##variable in your class, then there are techniques available to access that variable from outside the class, but a naked reference to ##variable isn't one of them. Carefully read the answers to the question you cited in your comment for more information.
Best way is to set and get the value using methods. Below is a sample code
class Planet
##planets_count = 0
def initialize(name)
#name = name
##planets_count += 1
end
def self.planets_count
##planets_count
end
def self.add_planet
##planets_count += 1
end
def add_planet_from_obj
##planets_count += 1
end
end
Planet.new("uranus")
Plant.add_planet
obj = Planet.new("earth")
obj.add_planet_from_obj

What is the proper way to access class variables in Ruby 1.9?

I'm trying to set some class variables to store paths in a Rails application (but I think this more a ruby question)
Basically my class looks like this
class Image < ActiveRecord::Base
##path_to_folder = "app/assets"
##images_folder = "upimages"
##path_to_images = File.join(##path_to_folder, ##images_folder)
end
But when I try to access ##path_to_images from my controller by doing Image.path_to_images, I get a NoMethodError
When I try with Image.class_eval( ##path_to_images ), I get uninitialized class variable ##path_to_images in ImagesController
I've searched around and all I've seen says those would work, so I'm very confused about this
What's more, I tried defining simple classes with the ruby console like so
class Bidule
##foo = "foo"
Bar = "bar"
end
And so I tried, I think, all the ways possible (previous 2 included) to access them but no way I always get an exception raised
Rails provides class level attribute accessor for this functionality
Try
class Image < ActiveRecord::Base
cattr_accessor :path_to_folder
##path_to_folder = "app/assets"
end
Then to access path_to_folder class variable just use
Image.path_to_folder
But people always suggest to avoid class variables due to its behavior in inheritance.So you can use constants like
class Image < ActiveRecord::Base
PATH_TO_FOLDER = "app/assets"
end
Then you can access the constant like
Image::PATH_TO_FOLDER
Although I wouldn't in general recommend it, you can access class variables by passing a string to class_eval as in:
Image.class_eval('##path_to_folder')
at least in later versions of Ruby.
Note, however, that class variables are associated with the uppermost class in a subclassed hierarchy. In the case of ActiveRecord subclasses like this one, this means that these class variables really exist in the namespace of ActiveRecord::Base.
If you can't or don't want to extend class use:
Image.class_variable_get(:##path_to_images)
Best way is to set and get the value using methods. Below is a sample code
class Planet
##planets_count = 0
def initialize(name)
#name = name
##planets_count += 1
end
def self.planets_count
##planets_count
end
def self.add_planet
##planets_count += 1
end
def add_planet_from_obj
##planets_count += 1
end
end
Planet.new("uranus")
Plant.add_planet
obj = Planet.new("earth")
obj.add_planet_from_obj
Class variables are rarely used in Ruby applications because they have a lot of limitations and also tend to run against the grain of proper Object-Oriented design.
In nearly every case a class variable can be replaced with a proper constant, a class method, or both.
Your example is probably better described as:
class Image < ActiveRecord::Base
PATH_TO_FOLDER = "app/assets"
IMAGES_FOLDER = "upimages"
PATH_TO_IMAGES = File.join(PATH_TO_FOLDER, IMAGES_FOLDER)
end
Class variables are private to the class in question and don't trickle down to sub-classes and are difficult to access from an external context. Using constants allows the use of things like:
image_path = Image::PATH_TO_FOLDER
There are some circumstances under which a class variable is more reasonable than the alternative, but these are usually very rare.
You can do that by wrapping it in a class method, like this:
def self.path_to_images
##path_to_images
end
but I should mention that you should try to avoid using class variables in rails
I would modify it like this:
class Image < ActiveRecord::Base
##path_to_folder = "app/assets"
##images_folder = "upimages"
##path_to_images = File.join(##path_to_folder, ##images_folder)
def initialize
end
...
def self.path_to_folder
##path_to_folder
end
end
What you've done here is make the class variable into a method, so you can now access is using a .method name call. Since this is a class variable though, you can only call this on the class itself, not on the instance of a class.
You are getting a 'NoMethodError' because you're calling the class variable using a method which has not exist. The code above defines this method on the lines where you say:
def self.path_to_folder
##path_to_folder
end
This will now work:
Image.path_to_folder

Instance Variables in Rails Model

I want to initialize an instance variable within my Rails model that will hold an array and I want to access this variable in other methods within my model. I tried this:
class Participant < ActiveRecord::Base
#possible_statuses = [
'exists',
'paired',
'quiz_finished',
'quiz_results_seen',
'money_sent'
]
def statuses
#possible_statuses
end
But when I tried the following using rails console:
Participant.first.statuses
I am returned nil :(
Why does this happen?
Is there a way to accomplish what I am trying to accomplish?
I would recommend using a constant for this kind of cases:
class Participant < ActiveRecord::Base
STATUSES = [
'exists',
'paired',
'quiz_finished',
'quiz_results_seen',
'money_sent'
]
If you want to access that array from the inside class, just do STATUSES, and from the outside class use Participant::STATUSES
In your example, #possible_statuses is a variable on the class rather than on each instance of the object. Here is a rather verbose example of how you might accomplish this:
class Participant < ActiveRecord::Base
#possible_statuses = [
'exists',
'paired',
'quiz_finished',
'quiz_results_seen',
'money_sent'
]
def self.possible_statuses
#possible_statuses
end
def possible_statuses
self.class.possible_statuses
end
def statuses
possible_statuses
end
end
As mentioned in other answers, if that list of statuses never changes, you should use a constant rather than a variable.
The best answer for me was to create a class variable, not an instance variable:
##possible_statuses = [
'exists',
'paired',
'chat1_ready',
'chat1_complete'
]
I could then freely access it in methods of the class:
def status=(new_status)
self.status_data = ##possible_statuses.index(new_status)
end
The instance variable will only exist when you create the model, since it is not being stored in a database anywhere. You will want to make it a constant as per Nobita's suggestion. Alternatively, you could make it a module like...
module Status
EXISTS = "exists"
PAIRED = "paired"
QUIZ_FINISHED = "quiz_finished"
QUIZ_RESULTS_SEEN = "quiz_results_seen"
MONEY_SENT = "money_sent"
def self.all
[EXISTS, PAIRED, QUIZ_FINISHED, QUIZ_RESULTS_SEEN, MONEY_SENT]
end
end
This gives you a bit more control and flexibility, IMO. You would include this code nested in your model.

Rails instance variable for record

Is there any built-in way to attach an instance variable to a record? For example, suppose I have a User class with a foo attr_accessor:
class User < ActiveRecord::Base
...
attr_accessor :foo
end
If I do
u = User.first
u.foo = "bar"
u.foo # -> "bar"
User.find(1).foo # => nil
This is the correct behavior and I understand why: the instance variable exists for the instance (in memory) and not for the record. What I want is something like a record variable, so that in the above example u.foo and User.find(1).foo return the same value within the same instance of my application. I don't want persistence: it's not appropriate for foo to be a column in the table, it's just appropriate for foo to return the same value for the same record during the life cycle of e.g., a controller action, console session, etc. Nor do I want a class variable via cattr_accessor, because there's no reason that User.find(1).foo should be the same as User.find(2).foo.
The best bet I can come up with is to fake it with a class variable array and instance methods to get/set the appropriate element of the array:
class User < ActiveRecord::Base
cattr_accessor :all_the_foos
self.all_the_foos = Array.new
def foo
self.all_the_foos[id]
end
def foo= new_foo
self.all_the_foos[id] = new_foo
end
end
This ALMOST works, but it doesn't work with un-saved records, e.g. User.new.foo = "bar" fails.
You can use Thread.current to set variables that are active within the context of a controller action invocation. Your current implementation doesn't guarantee context reset across calls.
class User < ActiveRecord::Base
after_create :set_thread_var
def foo
new_record? ? #foo : Thread.current["User-foo-#{id}"]
end
def foo=(val)
new_record? ? (#foo = val) : (Thread.current["User-foo-#{id}"] = val)
end
private
def set_thread_var
Thread.current["User-foo-#{id}"] = #foo if defined?(#foo)
end
end
I can't think of a better idea than your class variable solution. To solve your "new" problem I'd use after_initialize.
class User < ActiveRecord::Base
after_initialize :init_foos
cattr_accessor :all_the_foos
def foo
self.all_the_foos[id]
end
def foo= new_foo
self.all_the_foos[id] = new_foo
end
private
def init_foos
##all_the_foos ||= []
end
end

Resources