jfornoff
jfornoff

Reputation: 1368

Best practices regarding per-user settings and predefining options

I want to save settings for my users and some of them would be one out of a predefined list! Using https://github.com/ledermann/rails-settings ATM.

The setting for f.e. weight_unit would be out of [:kg, :lb].

I don't really want to hardcode that stuff into controller or view code.

It's kind of a common functionality, so I was wondering: Did anyone come up with some way of abstracting that business into class constants or the database in a DRY fashion?

Upvotes: 0

Views: 544

Answers (3)

BGuimberteau
BGuimberteau

Reputation: 249

If you are using PostgreSQL, I think you can watch to HStore with Rails 4 + this gem https://github.com/devmynd/hstore_accessor

Upvotes: 0

CDub
CDub

Reputation: 13354

Based on your question, it sounds like these are more configuration options that a particular user will choose from that may be quite static, rather than dynamic in nature in that the options can change over time. For example, I doubt you'll be adding various other weight_units other than :kg and :lb, but it's possible I'm misreading your question.

If I am reading this correctly, I would recommend (and have used) a yml file in the config/ directory for values such as this. The yml file is accessible app wide and all your "settings" could live in one file. These could then be loaded into your models as constants, and serialized as @SDp suggests. However, I tend to err on the side of caution, especially when thinking that perhaps these "common values" may want to be queried some day, so I would prefer to have each of these as a column on a table rather than a single serialized value. The overhead isn't that much more, and you would gain a lot of additional built-in benefits from Rails having them be individual columns.

That said, I have personally used hstore with Postgres with great success, doing just what you are describing. However, the reason I chose to use an hstore over individual columns was because I was storing multiple different demographics, in which all of the demographics could change over time (e.g. some keys could be added, and more importantly, some keys could be removed.) It sounds like in your case it's highly unlikely you'll be removing keys as these are basic traits, but again, I could be wrong.

TL;DR - I feel that unless you have a compelling reason (such as regularly adding and/or removing keys/settings), these should be individual columns on a database table. If you strongly feel these should be stored in the database serialized, and you're using Postgres, check out hstore.

Upvotes: 1

SDp
SDp

Reputation: 329

Usually, when I have to store some not important information which I don't care to query individually, I store them on a serialized column.

In your case you could create a new column in your users table (for example call it "settings").

After that you add to user model

serialize :settings, Hash

from this moment you can put whatever you like into settings, for example

user.settings = {:weight_unit => :kg, :other_setting1 => 'foo', :other_setting2 => 'bar'}

and saving with user.save you will get, in settings column, the serialized data.

Rails does also de-serialize it so after fetching a user's record, calling user.settings, you will get all saved settings for the user.

To get more information on serialize() refer to docs: http://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html#method-i-serialize

UPDATE1 To ensure that settings are in the predefined list you can use validations on your user model.

UPDATE2 Usually, if there are some pre-defined values it's a good habit to store them in a constant inside the related model, in this way you have access to them from model (inside and outside). Acceptable values does not change by instance so it makes sense to share them between all. An example which is more valuable than any word. Defining in your User model:

ALLOWED_SETTINGS = {:weight_unit => [:kg, :lb],
                  :eyes_color => [:green, :blue, :brows, :black],
                  :hair_length => [:short, :long]}

you can use it BOTH

  • outside the model itself, doing

    User::ALLOWED_SETTINGS

  • inside your model (in validations, instance methods or wherever you want) using:

    ALLOWED_SETTINGS

Upvotes: 1

Related Questions