How do I properly store multiple checkboxed values using SimpleForm? - ruby-on-rails

Basically I want to create an option in my form that accepts checkboxes (0 to all values accepted).
I love the structure of enums, because I get the performance speed of storing the integer in the DB, but I can reference the symbol in my code. However, I doubt I can use enum when I am storing multiple values like a checkbox.
So the way I imagine it working best is to just store it as a string that is also an array. So something like this:
# refactor_rule :string default([]), is an Array
Then my form looks like this:
<%= f.input :refactor_rule, collection: ["dry", "concise", "performant"], as: :check_boxes, class: "form-control" %>
The issue with this approach is when I store just 1 or 2 of the options (i.e. not all), this is what the attribute looks like:
q.refactor_rule
=> ["", "dry", "concise"]
Which I hate, because of the empty value at [0].
So my questions are as follows:
What's the most performant way to achieve this? Note that the options in my checkbox are static, but the field needs to accept multiple not 1?
How do I only store the values checked and not empty values?
Is there any way to take advantage of Rails built-in enum functionality even though I am storing multiple values?

Instead of an enum (performant but allows only a single value to be stored) or a serialized array (slow and storage space demanding) you should consider storing the boolean values as a bit field. This approach converts multiple boolean values (flags) to a single integer column where each flag has its own bit position in the integer. This of course has a massive positive impact on performance - you can search for any combination of flags using the & operator (in MySQL) instead of trying to search for substrings in a text column.
Take a look at the flag_shih_tzu or the bitfields gems. Both gems enhance Active Record so that all flags act like separate "virtual" attributes of a model object and they provide convenient methods for searching through such attributes. For this to work you probably would have to rewrite the simple_form form to use 3 separate check boxes instead of a single one though.

You can always have something like this to "clean" your attributes :
q.refactor_rule.reject!(&:empty?)
Which is gonna reject all empty elements from you array. Mind the !. reject! replaces it without the empty elements, reject just returns it. Your call !
If you really need to store an array in database, you can do it like so, in your migration :
create_table :products do |t|
t.text :tags, array: true, default: []
end
(This is from this blog post from Plataformatec)

Related

Rails Migration Column Type Polymorphic

I've got a little situation that I'd like to take care of, but I don't even know if it's possible.
In my app, I have one model that can take a parameter value, this parameter can be either a string or an integer or a boolean.
Instead of doing two string column (in my migration) with one labeled as "type" (the type - really !) and the other labeled as "value" (for the... value ! Yes !) i was wondering if it was possible that there is only the column value remaining, with polymorphics types in there.
The Model represent a property on a taks. Theses properties can be : is this task's open (type "boolean" and value "true" for exemple) ? Does this task has a percentage (type "integer", value "20") ? Note that the table also have a column name.
Polymorphism in Rails is more about associations where the class of the related item is unknown - not database columns with a dynamic type.
While is possible to implement a dynamic column in a relational database using two string columns value and value_type and use a custom getter to typecast value its going to get really messy. Another possibility is to use a something like a HSTORE or JSON column type but again do you really need it? Is there a better way to structure your business logic than trying to jam everything into a single column?
I'm guessing that what you may be looking for is something like an enum to denote the state of your model.
class Ticket < ActiveRecord::Base
enum status: [ :open, :closed, :pending, :active ]
end
This would use an integer column named status.
If you wanted to store additional information it would be prudent to use an additional database column. Like if you want to monitor the progress you would create an integer database column named tickets.progress.
For your answer, yes it is possible, but don't do it that way.
It is possible to use string for all different cases:
"integer:100", "integer:23", etc.
"boolean:true", "boolean:false", etc.
you will need to identify each data type(use concat)and convert to a right type and only then you can use the value.
But it is completely wrong and its implementation is going to be very complicated. I strongly recommend not to use this example or something similar.
I think that this is a wrong database structure.
In general, if you have a case in which you have a column that you think can contain more than one data type - something is wrong in your database and you should make a 'redesign' of your database.
Usually the best practice is to separate to different columns. In your case, your column can get different types, each type represents different information. I think your table should have these columns:
status_id - will reference to another table, Status that will contain all possible statuses(if there are more than 2 options).
progress/percentage - integer.
My answer isn't relevant for relational databases.

Where to define Rails select/radio options and how to store

Where is the best place to define, and how should I store select/radio options for rails (and where to put translations)?
Right now I am defining a Hash within the model and storing the integer keys in the record. I've also placed the translations as 'attributes' within the model translations as it seems to group them together well. ie
PHYSICAL_CONDITIONS = {
1 => "activerecord.attributes.building.condition_excellent",
2 => "activerecord.attributes.building.condition_good",
3 => "activerecord.attributes.building.condition_average_for_age",
4 => "activerecord.attributes.building.condition_fair",
5 => "activerecord.attributes.building.condition_poor"
}.freeze
Is there a better way to do this? I have dozens of fields with options and do not want to create separate tables for each either.
My solution is:
use varchar to store the answer ie 'excellent', 'good' from above. This is actual meaningful data I can see in raw form vs numeric values.
in my model have the options array. The order is maintained and if I re-order them numbers don't matter:
PHYSICAL_CONDITIONS = [
:excellent,
:good
]
under the active record model translation have an options group for each set of options. In the above case I call it :physical_condition_options.
have each translation as a subset of that ie excellent: "Excellent"
If I ever need to convert this to allow multiple selections (checkbox) on the model (happened a number of times), I just remove this subset and make it part of the model. Migration is simpler instead of having to translate numeric values.
have a helper translate those options when passing it to a field
localized_options(Building::PHYSICAL_CONDITIONS,
"activerecord.attributes.building.physical_condition_options.")
This seemed to be the best way to store the data and allow it to be easily translated.

Issue of using field of type array in rails

There might be a group of records in a table where only two fields vary record by record, and rest fields remains same. This calls for a normalization by splitting by a table through a foreign key association. But, in Ruby-on-Rails, it would mean the creation of a model. So, is it still possible to lessen use of disk space?
May be, it is, because it would be reasonable that storing multiple values of one column in a record would require the column to be an array of any type. But declaring the field to be :array type results in an error. So, is there a way to work around it?
After generating a model, open the model's file. Insert one line for each field.
serialize :field_name
But ensure that the fields for which you are serializing, should be of type
:text
or
:string
If they aren't of such primitive data types, i.e. of another type like
:datetime
then it would return an error.
This step is not complete as a whole. You need to do one complementing step: de-serialize, because in the model-level storage, it is stored as a string starting with "---\n-", which is not suitable for array-type operations.
While reading data from the model, you need to perform the following step:
YAML.load(field_name)
where field_name refers to the field that was serialized.
The above step would return an array, on which you can perform normal array operations.

efficiently storing "answers"

I'm building out an Answer ActiveRecord class that can have different types of answers. That is, the answer could be an integer, float, date, string... whatever...
The way I see it there are two ways to store the answers
1)
Have an attribute named "value" that is serialized.
This is nice since you can always access the answer from the same place. It probably sucks for searching answers since the data has to be de-serialized in order to be used (is this a correct assumption?)
2)
have several attributes integerValue, floatValue, etc...
This is easiest to search (if you know what type you're searching (which you probably do))
This sucks since the value isn't in the same place and need some hokey method like:
def value
stringValue||floatValue||integerValue
end
and some validations to ensure that only the correct one is set
What would you do??
I'd do one of two things:
Use single-table inheritance. Your table does contain those integerValue, floatValue, etc. fields; but it also contains a "type" field, and you'll subclass the Answer model to make IntegerAnswer, FloatAnswer, StringAnswer, etc. And then you can simply map the value accessor to one of those fields in each subclass. Look in the Agile book or Google on single-table inheritance in Rails for more on how to implement this.
Use a non-relational database. Something like MongoDB or CouchDB would render this problem moot; you could make value anything you wanted, or have multiple values per answer, or skip it entirely. I personally like Mongo with the MongoMapper gem, and if I had to address your use case that's the direction I'd go. It is getting away from ActiveRecord and "mainstream" Rails though, so you'd have to be comfortable with living at least a little on the edge.
What converting the value to a string in the database.
And using single table inheritance to define the type and retrieve the appropriate value.
You'd have one model Answer with two fields : one "value", which is a string.
And one "type", which is a string too and gets the type of the value.
Then for the float, you' have the following :
class FloatAnswer < Answer
def value
attributes['value'].to_f
end
end
When you do a Answer.find(:all), if the element has FloatAnswer as value, you will have a FloatAnswer object. Not an Answer one.
So you keep only one database field and always have the datas in the appropriate format.

Best way to store constants referenced in the DB?

In my database, I have a model which has a field which should be selected from one of a list of options. As an example, consider a model which needs to store a measurement, such as 5ft or 13cm or 12.24m3. The obvious way to achieve this is to have a decimal field and then some other field to store the unit of measurement.
So what is the best way to store the unit of measurement? I've used a couple of approaches in the past:
1) Storing the various options in another DB table (and associated model), and linking the two with a standard foreign key (and usually eager loading the associated model). This seems like overkill, as you are forcing the DB to perform a join on every query.
2) Storing the options as a constant Hash, loaded in one of the initializers, where the key into the Hash is stored in the unit of measurement field. This way, you effectively do the join in Ruby (which may or may not be a performance increase), but you lose the ability to query from the "unit of measurement" side. This wouldn't be a problem provided it's unlikely you'd need to do queries like "find me all measurements with units of cm".
Neither of these feel particularly elegant to me.. can anyone suggest something better?
Have you seen constant_cache? It's sort of the combination of the best of 1 and 2 - lookup data is stored in the DB, but it's exposed as class constants on the lookup model and only loaded at application start, so you don't suffer the join penalties constantly. The following example comes from the README:
migration:
create_table :account_statuses do |t|
t.string :name, :description
end
AccountStatus.create!(:name => 'Active', :description => 'Active user account')
AccountStatus.create!(:name => 'Pending', :description => 'Pending user account')
AccountStatus.create!(:name => 'Disabled', :description => 'Disabled user account')
model:
class AccountStatus < ActiveRecord::Base
caches_constants
end
using it:
Account.new(:username => 'preagan', :status => AccountStatus::PENDING)
I would go with option one. How large will it be the UnitOfMeasurement table? And, if using an integer primary key, why do you worry so much about speed?
Option 1 is the way to go for design reasons. Just declare it with an integer (even smallint) primary key and a field for the unit description.
Has ActiveRecord gotten support for natural keys, yet? If it has, you can just make the name (or whatever) column of the UnitOfMeasure table the PK, that way the value of the FK column has all the info you need, and you still have a fully normalized DB with a canonical set of UnitOfMeasurement values.
Do you need to perform lookups on these values? If not, you could as well store them as a string and parse the string later on in the application that reads the values. While you risk storing unparseable data, you gain speed and reduce DB complexity. Sometimes normalizing a database is not helpful. In the end /something/ within your system needs to know that "cm" is a length measure and "m3" is a room measure and comparing "3cm" to "1m3" doesn't make any sense anyway. So you just as well can put all that knowledge in code.
Let's say you are only going to display that data anyway, what is normalizing good for here?

Resources