问题
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.
回答1:
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
回答2:
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).
回答3:
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.
回答4:
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
回答5:
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
回答6:
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
回答7:
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 }
来源:https://stackoverflow.com/questions/9490985/permanent-variable-in-rails