Rails - best practice to store key/value hash - ruby-on-rails

Here's my challenge. I have a key/value set that I want to tie to a model. These are my specific requirements:
I want the hash to be stored as a serialized JSON object in the model's table instead of in a separate table
I want to be able to pre-define the valid keys within the model itself
I want to be able to set a strong type for each key and automatically perform validations. I don't want to have to write validation functions for each individual attribute unless it needs a validation out of the basic data type scope.
I would LOVE to be able to magically access the attributes inside a form generator (f.input :my_key) and have the form generator recognize that :my_key is of type :boolean and create a checkbox instead of a generic text input. The same for other data types.
There are a few different ways to solve this problem, and lots of opinions for both. I read over this answer from 5 years ago:
Best approach to save user preferences?
It seems that many/most of those plugins have been abandoned. Anything else come out in the last 5 years that matches my criteria?

Your question is a bit open-ended, but as far as I can see your needs, they should be met with using Hashie gem.

Related

Can fields in different but associated tables have the same name is Rails 4

Can fields in different but associated tables have the same names is Rails 4 and be distinct. For example, if I have a class Shipping and a class Receiving, where each has the field EnterTrackingNo, and they are associated via a one to one association on the field shipping_id, will there be any issues with this setup / the fields somehow overlap / interfere with one another?
Thanks
There will not be any issue as Rails will automatically add the table name to the SQL queries it builds when it needs to. You'll be able to access the attribute easily as either shipping.EnterTrackingNo, receiving.EnterTrackingNo, shipping.receiving.EnterTrackingNo, receiving.shipping.EnterTrackingNo, etc. and Rails knows which table you're talking about due to the way they're written.
Even when you search for an object, say you want to search for all Shippings with a Receiving item that has EnterTrackingNo == 3 you'd do
Shipping.includes(:receiving).where(receiving: { EnterTrackingNo: 3 })
The only thing to keep in mind is that if you use SQL fragments (writing a where as a String, for example) you MUST write it as table_name.attribute, otherwise you'll get a SQLException: ambiguous column name. For example:
Shipping.includes(:receiving).where("EnterTrackingNo = 3").references(:receivings)
would not work as Rails, and your DB, have no way of knowing WHICH EnterTrackingNo you're talking about. You'd have to write it as:
Shipping.includes(:receiving).where("receivings.EnterTrackingNo = 3").references(:receivings)
So they know you want the Receiving model's attribute.
You'll also notice I add references(:table_name) to the ones with SQL fragments. This is necesary as well since Rails can't tell it needs a join when you just give it a String.

Load model partly in Ruby On rails

guys! I'm trying to make right architecture decision:
I need to hide from user of my site some fields of other users by default. So not to bother with filtering the fields in views I want to not load those fields at all, like:
default_scope -> { select(column_names - FILTERED_PARAMS) }
the rest of fields should be loaded explicitly in special cases.
The problem is that once code refers to the missing fields nomethod error shows up. I tried to meta-program those fields but fruitless this far. It seems to me that this approach doesn't fit to the AR object life-cycle.
Have you ever implemented such functionality if so what pattern have you chosen?
From my experience the best decision would be not to filter these params on the query with select, but to filter what parameters are actually sent to the user. my_model.as_json (with given param filtering options) is a simple solution for that, but for more advanced uses I would advise Rabl gem
https://github.com/nesquena/rabl
That way you have more control over which params are returned even in very advanced cases in a model-view-controller manner.

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?
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
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.
If you are using PostgreSQL, I think you can watch to HStore with Rails 4 + this gem https://github.com/devmynd/hstore_accessor

Rails 3 only update serialized attribute when changed

Using Rails 3 I have a number of model containing serialized attributes. To perform the serialization I'm currently using 3 different techniques - the serialize method; activerecord store; and store configurable gem.
In all cases, when I save a model instance the serialized attribute is updated even if the content is unchanged. This was a surprising discovery particularly when using the store configurable gem as the readme states:
"StoreConfigurable is smart enough to let your parent object know when it changes. It is not dumb either. It will only trigger changes if the values you set are different, are new, or change the configs state."
Am I missing a trick here or if this is the expected behaviour is there a way to override it?
Model with serialized columns being saved all the time is the expected behaviour according to this answer.
I haven't tried "store configurable gem" but it sounds like it should be smart enough to detect that.
I have similar issue so before updating i am resetting all the values and let new values to be entered.

Get a list of validation rules in Rails 3?

I need to get a list of validation rules out of a Model in my Rails application. I have searched around and looked through the source of a few client-side validation gems, but am still scratching my head about how to do this. Is there an easy way to just pull a list of validation rules out of a Rails model?
My specific use case is creating an API where the entry form for new items will be auto-generated from the Model definition, and I need to be able to express which fields are required, max length, etc. I already have fields, types, and length from the columns method, but there does not seem to be any type of similar validations method that returns what I need (mainly, required fields as enforced with validates and validates_presence_of, etc.).
Check out the #validators and #validators_on methods:
http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html
You will have to write some custom code that operates on the returned objects and determines which attributes are required, i.e. which ones have validates_presence_of.

Resources