What's the best way to store global application settings in a Rails application?
I want to handle two kinds of global configuration settings:
- Settings which can be altered by the user, like if notification mails for certain events are sent or not.
- Settings which are tied to a specific product edition, like disabling a feature开发者_Go百科 in a free version, which is only available in the commercial version.
What's the best way to store these settings? Database, configuration file, hardcoded in the source, ...?
For both cases database. You're going to be using the same structures for multiple people/products so it makes sense. Also it allows you to change things without restarting the server.
I've handled it this way in the past: For settings specific to the user, I've created a UserSettings model/table, that has a one-to-one to relationship with a user. The reasoning for this is that the majority of my operations involving users do no not require these settings to be loaded, so they're only included on user loads from the database when I need them.
When I do this, I'll usually group my column names, so that I can write helpers that dynamically create based on the names. Meaning that I won't have to modify my views to incorporate new settings unless I add one with a different naming scheme.
For the settings specific to a product, well that depends on how you are doing things. And there are a couple of ways to interpret your question.
The way I read it is that you want to decide on a product level. What settings users can overriding or disabling a user's setting. And possibly define some product specific settings.
I would use a one-to-many product to setting relationship. The setting table would be something simplistic (product_id, setting_name, setting_default_value, allow_user_change)
This does a number of things. It lets you have a variable list of settings for different products (Perfect for the case where you're offering many different products instead of varying tiers of access to services). It also lets you define what settings a user can/can't change and give values for that product type. That can be changed from an administrator view without restarting the application. It's also not tied to user settings, to the point where if a user doesn't have a setting listed in the product_settings there will be no problems.
The downside is you will have multiple common settings in this table. I would move settings that every product will have a different value to a field in the product table.
You will also have to write validations to ensure that a user does not change a setting their product says they can't. You will also have to write helper methods to merge settings from the product and user sides.
class Flag < ActiveRecord::Base
# id, user_id, name, value (serialized probably)
belongs_to :user
DEFAULTS = {
"newsletter" => false
}
def self.lookup(user, flag)
# Please involve memcached here
case flag
when "ssl_enabled"
# Check if user has paid for sufficient access to SSL
return false
else
stored_flag = self.find_by_user_id_and_name(user.id, flag)
if stored_flag
return stored_flag.value
else
return DEFAULTS[flag]
end
end
end
end
class User < ActiveRecord::Base
has_many :flags
def flag(name)
return Flag.lookup(self, name)
end
end
For stuff that's product edition based, you probably can't really store things in the database, because the flag is going to be based on some piece of authorization code, rather than static data.
Here's my experience with this kind of stuff: don't override behavior.
You see, your first thought is going to be something like this:
Hmm.... There are system-wide settings that may or may not be overridden by users (or products). Hey! I know this! It's composition!
And technically, you'd be correct. So, you'll make a Settings table and put all your settings in there. And then you'll have a user_settings table, where you will override those settings if the user so decides. And it'll work fine.
Until you add a setting to one table and not the other.
Or you get a bug that Setting X can't be overridden at the user or product level and it takes more than 5 seconds to figure out exactly where that setting is set.
And then you'll realize:
Hey, I'm keeping track of all these settings in at least two different places. That seems kinda dumb.
And you'd be right.
So, yes. Go ahead and keep the settings in the DB, but save them distinctly for each user or product. Use smart default values on row creation and it'll keep things nice and simple.
For the first kind of settings, I would keep them in the User model (Users table).
The second kind of settings, would go to the database again. For example if a user had a free account, that would be somehow saved in the database. I would have some helpers in Application, for example "free?" or "commercial?". These helpers could find out if they are true or false, asking the currently connected User/Account model. You could then use these helpers across different parts in your application to decide if you show or hide certain functionality.
精彩评论