Reputation: 1925
I'm curious if there's a gem or way of doing per-model settings. For example, user preferences.
I'd like to have defaults per model (say a class attribute), but definable (on another table! I don't want a serializable field on my model).
For example:
user = User.find(1)
user.settings.newsletter # => true
There would be a model for UserSetting that has a schema of key => string, value => string, type => string (Boolean, Date, String, etc...)
UPDATE:
Here is my solution to this in the end. Supports value types for settings (Boolean, Time, etc)
def setting(key, whiny=true)
s = user_settings.where(:key => key).first
if s
case s.value_type
when 'Boolean'
s.value.to_i == 1
when 'Time'
Time.parse(s.value)
else
s.value
end
else
if whiny
raise NameError, "Setting key #{key} does not exist for #{name}."
else
nil
end
end
end
Upvotes: 0
Views: 163
Reputation: 106037
The has_settings
gem appears to do this, but alas doesn't appear to be maintained. This fork has Rails 3 support, at least.
If you can't find a gem that suits your needs it's pretty straightforward to implement on your own with a simple has_many relation and a little association extension magic, e.g. (untested):
class User < ActiveRecord::Base
has_many :settings do
# association extension
def method_missing name, *args
key = name.to_s
# setter
if key.ends_with? '='
key.chop!
new_val = args.first
setting = find_or_create_by_key key
setting.update_attribute(:value, new_val) &&
@settings[key] = new_val && # KISS cache
true # to mirror the semantics of update_attribute
# getter
elsif @settings.has_key? name # is it cached?
@settings[name]
elsif setting = where(:key => name).select(:value).first
@settings[name] = setting.value # KISS cache again
else
super
end
end
end
end
class Setting < ActiveRecord::Base
belongs_to :user
serialize :value # so we don't have to bother with an extra :type attribute
end
class CreateSettings < ActiveRecord::Migration
def up
create_table :settings do |t|
t.references :user
t.string :key
t.string :value
end
end
def down
remove_table :settings
end
end
Obviously this gets the job done but obviously isn't very robust so you may encounter gotchas.
Upvotes: 0
Reputation: 9700
I would implement this with User has_many UserSettings, and UserSetting is user_id, key, and value. Maybe have a convenience method for accessing them, like so:
class User < ActiveRecord::Base
has_many :user_settings
def setting(key)
user_settings.where(:key => key).first.try(&:value)
end
end
class UserSetting < ActiveRecord::Base
belongs_to :user
end
Then, you can go
user = User.find(1)
user.setting('newsletter') # => true/false (or nil if that setting doesn't exist)
Upvotes: 1