Class variables instance variables in Ruby - ruby-on-rails

I was trying to get a dynamic array for in RUBY which will be changed dynamically. I could not able to push to class variable. Can any one help how can i do that please see the below code.
class SampleController < ApplicationController
##array = []
##x = 0
def ajax_data
y = (rand()*100).round()
##array << [##x,y]
##x += 1
end
end
My Question is that the class variable ##array should increase the size of the array whenever we call to the method ajax_data but it always gives the output of one value like this [ [0, y] ] . I want to increase the ##array and ##x values .
How can we do that ?

Ruby on Rails, in development mode, by default reloads your source files on each request. Since you're saving your "program"'s state in class variables, the changes get wiped out by the reloading of your classes.
By the way, class variables are normally used with much caution, as they are essentially global. Especially in a Rails web application. Save any state in a database, not in your classes' context.
Update:
Remember that web server processes are usually supposed to be stateless. Also, you usually have multiple processes running in production, which means that your counter would be different between requests depending on which process would answer a request. Also, processes can be restarted, meaning you counter would be lost.
In Rails, if something is this tricky, it usually means you're trying to do something that you shouldn't do :)
If you really don't want to use a DB, and if the counter is not supposed to be global for all visitors of your page, you could try storing the counter in a cookie: http://api.rubyonrails.org/classes/ActionDispatch/Cookies.html

Related

changing the value of a global variable in rails

I'm prototyping an app and want to have a global variable that I can hit an endpoint that toggles the global variable $current_status. I have:
def toggle_status
$current_status=false if $current_status.nil?
$current_status=!$current_status
r={}
r[:current_status]=$current_status
render json:r.to_json
end
and in application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
$current_status
end
but hitting /toggle_status always returns false. Why isn't assigning a bool to what it isn't changing this value? I'm aware something like this should be in db but just prototyping
edit 1
I just created this in lib/
class Jt
#cur
def self.cur
#cur
end
def self.cur=val
#cur=val
end
end
and updated the controller to:
def toggle_status
Jt.cur=!Jt.cur
r={}
r[:current_status]=Jt.cur
render json:r.to_json
end
Your toggle code doesn't actually toggle anything. It appears you expect this line to "toggle" the contents of the $current_status variable.
$current_status!=$current_status
However, the != operator doesn't assign anything but it is a comparison operator. In your case, it returns always false based on your query whether $current_status is equal to not $current_status.
What you want to use instead is probably
$current_status = !$current_status
As for your software design, global variables are generally frowned upon in Ruby (and Rails) as are all other kinds of globally mutable state. Use proper classes and objects instead to encapsulate your state and behaviour into more manageable structures. Using global variables, you will shoot yourself in the foot some day and you will have a very hard time to find out what is actually happening. You should try to avoid this :)
You can't use a global variable in this way in such app and there are several reasons. I'll give you just one: depending on the webserver you use, the server may start different processes to handle the incoming web requests, and global variables can't be shared between these processes.
And in fact, it's not even a good idea to use a global variable at all.
If you want to persist a state, use a proper storage. Depending on how long the value should be persisted and who should be able to access it, you have plenty of choices:
database
file system
memory
cookie
Your first snipper does not work because != is a comparison operator, not assignment
Second may not work due to code reloading (Jt class instance is not guaranteed to be the same for other request unless cache_classes is on, but in development you usually always want it off, because otherwise any code changes require server restart to take effect), simple way to have a non-reloaded class - put it in a initializer
For prototyping you also may try thread-local storage for this task: Thread.current[:foo] = 1234

Instance variable in Rails for several request

I have a Ruby on Rails server for a web application.
I am wondering if instance variables in Ruby are shared on different requests. Here is my problem:
Bob makes a Get request to my ruby server; it first initiates a instance variable #x and then its request is processed for a while.
In the meantime, Alice makes another Get request that initiates a instance variable #x.
At the end of Bob's process, the variable #x is looked at. Will it have the value given by Bob or Alice?
What if Bob and Alice are the same person that quickly makes the same request twice in a row? Will the first request see the value initiated by the second request at the end of its process?
Edit: this happens when config.cache_classes is set to true and from my experiments, it doesn't concern controllers' instance variables but it do concern librairies. Here is what I have done. When an http request arrives, I set an instance variable #x to a random value and save it in a variable x. I sleep for a while and then I compare #x and x. When I launch several requests at the same time, I sometime find different values when doing the comparison. However, this mismatch doesn't happen in controllers but it does in librairies. Does it means that the controllers are not cashed but librairies are when config.cache_classes = true ?
#x is not a global variable, it's an instance variable.
No, controller instance variables are not shared between requests.
Rails 4 is the first version to be multi-threaded by default - you shouldn't run into variable concurrency issues with earlier versions unless you explicitly turned threading on in the config (config.threadsafe!).
You can still have database-related concurrency issues in single-threaded Rails: If, for example, you read from a field at the start of a request, perform some calculation on that value and then write it back.
In multi-threaded Rails you can have concurrency problems with global or class variables too, as these are stored in shared memory space. Global variables are defined with the dollar symbol $x and class variables are defined with two at symbols ##x - both have their uses but there are usually better ways to pass data around.

Rails and class variables: will this persist across users and server calls?

We're on Rails 3.0.6.
Will the following code persist across page loads and users in Rails? Or does it get redefined with every request? The code lives at the top of a controller.
##list = []
hero = {}
hero['name'] = 'so'
hero['superpowers'] = ['coding', 'qa', 'spec_writing']
##list.push hero
hero2 = {}
hero2['name'] = 'so2'
hero2['superpowers'] = ['coding']
##list.push hero2
... more Hashes pushed into ##list
The list only changes once a month, and we would like to efficiently and quickly make this data available to page requests. We will eventually use Rails.cache to read from the database, but we're using the following approach for now (assuming it works).
In development, the default behavior is for the class to be reloaded on each request, thereby resetting your class variable. In production, however, the class is initialized once and so the class variable will persist across multiple requests and multiple sessions.
You should move to a proper caching technique as soon as you can. You cannot, obviously, persist the value beyond the reloading of the class when the application is restarted. Furthermore, if the web server is multi-threaded (as it is likely to be), it may be running multiple instances of the application on different threads which do not share the class variables, potentially creating inconsistencies between requests.
I've just came across the same issue and found this post. I know it's an old question, but posting my answer just in case someone else faces similar issue...
I think for cases where database or Rails.cache can not be used for some reason then the best place to put those values is Application class. Just define some attribute and initialize it. Then it's easy to access it just like Rails.application.heros. Quick and dirty sample below:
in config/application.rb
module YourRailsApp
class Application < Rails::Application
attr_reader :heros
initializer "init heros" do
#heros = []
hero = {}
hero['name'] = 'so'
hero['superpowers'] = ['coding', 'qa', 'spec_writing']
#heros.push hero
hero2 = {}
hero2['name'] = 'so2'
hero2['superpowers'] = ['coding']
#heros.push hero2
end
#Other application sutff...
end
end
I sometimes used what I called the cache of the poor.
It worked as follow:
in an initializer, add: MY_CACHE_HASH = {}
wherever you want: MY_CACHE_HASH[:foo] = :bar
wherever you need MY_CACHE_HASH[:foo] #=> :bar
I wouldn't recommend it though: what would you do if your server crashes?
If you have data that won't be changed while rails is running then it's fine to use a ##class_variable.
keep in mind that (in the default development configuration) controller classes are re-loaded at every request, so if you need to say, read the data from a file, consider putting the code to initialize the data into config/application.rb in a $global or CONSTANT_VAR.
So what you're describing is a datastructure that is not a database. I think it's perfectly reasonable to have items that "never" change in your code and not in your database.
for example, You could have:
In app/models/hero.rb
class Hero
##all_heros = []
def self.all_heros
##all_heros
end
def self.add_hero(hero)
##all_heros << hero
end
def initialize(name, superpowers=[])
#name = name
#superpowers = superpowers
end
end
# this will get executed on load
Hero.add_hero( Hero.new("so", ['coding', 'qa', 'spec_writing']))
Then later in your code, you'll access:
#heros = Hero.all_heros
And later you can swap out for a database backed store if you need on.

Why is my class variable being re-initialized using cattr_accessor (JRuby, Rails)

OK, I know this is a bit of a frankenstack, but I am running JRuby on Rails, and I'm relatively new to both. I'm seeing some behavior that I can't understand, and I'd like to know if I'm doing something wrong or if its a problem with my stack. The basic issue is that it appears that my class attributes are being reinitialized, something that I wouldn't expect to ever happen.
Here's essentially my code
class MyController < ActionController::Base
cattr_accessor :an_attr
before_filter :init_an_attr
def init_an_attr
if ##an_attr.nil?
##an_attr = {}
end
# do some other stuff here
end
end
The problem lies in the fact that every time init_an_attr is called, the condition on the if evaluates to true, and I end up reassigning ##an_attr.
Is that the expected behavior? If so can you explain why, because to me, the assignment should only happen once.
In Rails, when running in development mode, classes are not cached. MyController, and all other classes are reloaded on each request. When running in production, this is not the case - classes are cached.
However, even in production, this variable will be local to a particular application instance - if you're running with two Mongrels, for example, each will have a different version of this variable.
If you want state to be set across multiple requests, consider either using the session, or storing values in your database. Class variables are really not appropriate for cross-request storage.

why class variable of Application Controller in Rails is re-initialized in different request

I have my Application Controller called McController which extends ApplicationController, and i set a class variable in McController called ##scheduler_map like below:
class McController < ApplicationController
##scheduler_map = {}
def action
...
end
private
def get_scheduler(host, port)
scheduler = ##scheduler_map[host+"_"+port]
unless scheduler
scheduler = Scheduler.create(host, port)
##scheduler_map[host+"_"+port] = scheduler
end
scheduler
end
end
but i found that from second request start on ##scheduler_map is always an empty hash, i run it in development env, could someone know the reason? is that related to the running env?
Thank you in advance.
You answered your own question :-)
Yes this is caused by the development environment (i tested it) and to be more precise the config option "config.cache_classes = false" in config/environments/development.rb
This flag will cause all classes to be reloaded at every request.
This is done because then you dont have to restart the whole server when you make a small change to your controllers.
You might want to take in consideration that what you are trying can cause HUGE memory leaks when later run in production with a lot of visits.
Every time a user visits your site it will create a new entree in that hash and never gets cleaned.
Imagine what will happen if 10.000 users have visited your site? or what about 1.000.000?
All this data is kept in the systems memory so this can take a lot of space the longer the server is online.
Also, i'm not really sure this solution will work on a production server.
The server will create multiple threats to handle a lot of visitors on the same time.
I think (not sure) that every threat will have his own instances of the classes.
This means that in treat 1 the schedule map for ip xx might exist but in treat 2 it doesn't.
If you give me some more information about what this scheduler is i might be able to give a suggestion for a different solution.

Resources