Rails and class variables: will this persist across users and server calls? - ruby-on-rails

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.

Related

Best practice for a big array manipulation with values that never change and will be used in more than one view

What would be the best and more efficient way in Rails if I want to use a hash of about 300-500 integers (but it will never be modified) and use it in more than one view in the application?
Should I save the data in the database?, create the hash in each action that is used? (this is what I do now, but the code looks ugly and inefficient), or is there another option?
Why don't you put it in a constant? You said it will never change, so it fits either configuration or constant.
Using the cache has the downside that it can be dropped out of cache, triggering a reload, which seems quite useless in this case.
The overhead of having it always in memory is none, 500 integers are 4KB or something like that at most, you are safe.
You can write the hash manually or load a YAML file (or whatever) if you prefer, your choice.
My suggestion is create a file app/models/whatever.rb and:
module Whatever
MY_HASH = {
1 => 241
}.freeze
end
This will be preloaded by rails on startup (in production) and kept in memory all the time.
You can access those valus in view with Whatever::MY_HASH[1], or you can write a wrapper method like
module Whatever
MY_HASH = {
1 => 241
}.freeze
def self.get(id)
MY_HASH.fetch(id)
end
end
And use that Whatever.get(1)
If the data will never be changed, why not just calculate the values before hand and write them directly into the view?
Another option would be to put the values into a singleton and cache them there.
require 'singleton'
class MyHashValues
include Singleton
def initialize
#
#results = calculation
end
def result_key_1
#results[:result_key_1]
end
def calculation
Hash.new
end
end
MyHashValues.instance.result_key_1
Cache it, it'll do exactly what you want and it's a standard Rails component. If you're not caching yet, check out the Rails docs on caching. If you use the memory store, your data will essentially be in RAM.
You will then be able to do this sort of thing
# The block contains the value to cache, if there's a miss
# Setting the value is done initially and after the cache
# expires or is cleared.
# put this in application controller and make it a helper method
def integer_hash
cache.fetch('integer_hash') { ... }
end
helper_method :integer_hash

Permanent variable in Rails

Lets say that on top of my Rails app there is a bar with piece of text displayed - latest hot deal, scheduled downtime notfication, something like that. It's a single, on of a kind information that needs to be accessed on basically every request, and may be updated from time to time. What is the best way to achieve this?
What I'd like to do is some kind of permanent global variable (accessible from controllers).
It will be updated very rarely, so there's no problem if for some time after update there will be an inconsistency between workers.
On the other hand, it should be persistent in case of server fault (periodic backup is enough).
It will be accessed really often, so it should be as fast as possible - preferably stay in memory.
Also, it's only one of a kind, so I'd really prefer not to bloat the app with a dedicated database model.
Something like that is damn easy in Node.js for example, but I couldn't find a single way to achieve this in Rails. What shall I do?
EDIT
Thanks for the answers so far, but while they're inspiring, I think that I should stress out one key functionality that they're all missing. The variable should be editable inside the app and persistent. While it's possible to edit your variables, in case of server restart I'm back to the default - which is bad.
It really depends on what you are looking for. You could do something very simply by putting it in your application_controller.rb
class ApplicationController < ActionController::Base
def system_message
"Come buy our amazing .99 iphone chocolate bar apps, with 100% more gamification!"
end
end
That function (and string) is then accessible from any controller in your application. You could also specify something in the after_initialize block in your application.rb file.
config.after_initialize do
::MYTEXT = "MY SUPER AMAZING TEXT"
end
You could also create your own file under the initializers directory, which is preloaded in rails.
so siteAnnounce.rb
MYANNOUNCEMENT = "NOW LISTEN TO ME!"
You may also want to check out this Railscast video about site wide announcements
I would store it in the database and let caching take care of it.
I feel that global variables are fine, when appropriate, for code that needs to share that common value in many places but that is the code, not the the user view.
This is clearly true in this case as the OP has bolded 'editable by the app'. So I would have a view that lets the users enter it, it gets stored in a db table and then recalled as needed (as cached once used once).
Well I had faced a similar problem.
My problem was I needed a global variable in all the levels (MVC).
We went to use Memcache to store the variable.
May be you can go for a similar solution.
And as an added bonus you can change it throughout the program.
You could declare it as a constant in an initializer:
config/initialzers/foo.rb:
MYVARIABLE = 'some string'
Accessible from anywhere in your application as MYVARIABLE
Ok, so here's what I did. Instead of just putting the value to an initializer, I've made there a simple class that handles it. The variable itself is stored in a predefined file. Besides of reading the file upon the initialization, the class updates file when the value is changed, and also re-read the file periodically to maintain consistency across workers. I've also put there some basic JSON handling and backup functionality to make life easier.
For anyone interested, here's the important code:
class Pomegranate
def initialize
#delay = 30.minutes
#path = "db/pomegranate.json"
#valid = Time.now - 1
validate
end
def get(*p)
validate
p.inject(#data) {|object,key| object[key] if object}
end
def set(*p, q, v)
hash = p.inject(#data) {|object,key| object[key]||={}}
hash[q] = v
end
def save
#valid = Time.now + #delay
File.open(#path,"w") {|f| f.write(#data.to_json)}
end
private
def validate
if #valid < Time.now
#data = ActiveSupport::JSON.decode(File.read(#path)) rescue {}
#valid = Time.now + #delay
#valid = Time.now - 1 if #data.empty?
end
end
end
$pom = Pomegranate.new
Source:
Where to put Global variables in Rails 3
Try putting it in your applicaton.rb like this:
module MyAppName
class Application < Rails::Application
YOUR_GLOBAL_VAR = "test"
end
end
Then you can call it with the namespace in your controllers, views or whatever..
MyAppName::Application::YOUR_GLOBAL_VAR
Another alternative would be using something like settingslogic. With settingslogic, you just create a yml config file and a model (Settings.rb) that points to the config file. Then you can access these settings anywhere in your rails app with:
Settings.my_setting
I've started putting constants and variables like this in the configuration object, e.g.
TestApp::Application.config.foo = 'bar'
TestApp::Application.config.something = { :a => 1, :b => 2 }

Ruby and Rails: Metaprogramming variables to become class methods

I'm creating a model called Configuration and I have the following code and I want to make it more dynamic by using metaprogramming.
In a table on the database for Configuration model I have the following data.
---------------------------------------------------------
variable_name as string | value in text
|
company_name | MyCompany
welcome_text | Welcome to MyCompany's App!
email_order_text | You've just created an account with MyCompany.
year_since | 2012
----------------------------------------------------------
class Configuration < ActiveRecord::Base
#nothing here yet
end
----------------------------------------------------------
Currently, the only way to access the company_name is to do the following in rails console:
configuration_company_name = Configuration.find_by_variable_name("company_name")
configuration_company_name.company_name
> "MyCompany"
I think this is an unacceptable way to do things. First, it will access the database everytime someone checks for the company's name. I think if I could load it when the app starts and doesn't have to access it again because it's in the memory, then it would be better. How can I do something more dynamic so I could access the value "MyCompany" like this.
Configuration.company_name
> "MyCompany"
The reason to do this is to give allow fast customization of the application.
class Configuration < ActiveRecord::Base
# loads all the configuration variables to an in-memory
# static hash during the first access.
def self.[](n)
#config ||= {}.tap { |h| Configuration.all.each{ h[variable_name] = c.value}}
#config[n]
end
end
Now you can access your configuration as :
Configuration["company_name"]
If you a large number of configuration parameters, it might be beneficial to pre-load the cache by accessing a configuration parameter in an initializer file. If you have 1000s of configuration variables you might have to consider migrating the cache to memcached etc.
If you want to access the configuration parameter as a class method:
class Configuration < ActiveRecord::Base
klass = class << self; self; end
Configuration.all.each{|c| klass.send(:define_method, c.variable_name){c.value}}
end
Now you can access the parameter as follows:
Configuration.company_name
One thing you are getting wrong here,it will be never be Configuration.company_name , thats like access a Class property instead of a Object/Instance property,
It should be a instance of the Configuration Class. It would still be somewhat acceptable to use #KandagaBoggu's method in the other answer, but database access still almost everytime or from the Active Record’s query cache.But AR's query cache lives for the duration of a particular action (i.e. request ). You may want use something like Memcached for the objects to survive longer.
We can move these constant values into a yml file, when the server starts load them into a variable and access it whenever needed.

Class variables instance variables in Ruby

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

How to persist Ruby class variables across page loads in Rails?

I have a class variable that I would like to set from an initilizer and have the value kept from then on. The example below works for only the first page load. Is there a better way to do this?
app/models/token.rb
class Token
class << self
attr_accessor :salt
end
end
config/initilizers/token.rb
Token.salt = "savory hash"
In development mode, your class is going to get reloaded with every request, so the value that's set in an initializer at app startup will not persist when the class is reloaded after the first request. (The result of "config.cache_classes = false" in your development.rb).
However, if you want to set a value in an initializer and have it persist in development mode, you can either add it as a constant:
initializers.rb
SALT='savory_hash'
OR as an application config variable:
application.rb
module YourAppsName
class Application < Rails::Application
config.token_salt = "savory_hash"
end
end
which would be accessible anywhere in the app with:
Rails.application.config.token_salt
Of course, if you enable class caching in your environment, you should find that your variable's value will persist without doing anything of the above.
You can try storing them in session variables, cache, or even within its own table (a reference table).

Resources