Where to define Rails select/radio options and how to store - ruby-on-rails

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.

Related

How do I properly store multiple checkboxed values using SimpleForm?

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)

rails 3 best practice for handling attribute with fixed possibilities

For example if I have an attribute that is limited to a short list of values like:
ways = {:way_1 => 1, :way_2 => 2, :way_3 => 3}
What is the best practice for handling this attribute, for both cases:
User can choose only one value (radio button)
User can choose multiple values (checkbox)
For the first case I would use hash defined in an initializer and save the integer value, for second case I thought of bit manipulation to represent all possible combinations.
Is there a best practice for handling both cases in rails 3?
Edit:
I found this gem BitmaskAttributes which handles the bit manipulation nicely, but I still want to know if this is the best practice?
You can use array and serialize this array in a text attribute in your model
class Expense < ActiveRecord::Base
serialize :ways
# other model code
end
then when saving your model, you can do it like this
Expense.new(:ways => [1, 2])

Rails 3: What is the correct Rails way of doing custom types?

I'm doing a fairly complicated model on Ruby on Rails right now, and I was wondering what the correct "Rails" way of doing "custom types" for attributes was. For example, I have a table businesses which has a string attribute region. But region can only be one of a predefined list of possibilities (that could later be expanded). My question is: where do I define this Region type?
I know I could make a specific regions table (i.e. a Region model) which could house all the options, and then I could make an association between the models that have regions to that table. The problem is that I have many of these types on my model, so I would end up with more than half the tables in my database being "custom type tables" that only store the possible values for these types. Is that practical?
I also read that you could do this through validations (i.e. validate when saving a record, that the variables were within the possible values). This seems very impractical, since I want to make this model expandable, and form views would need to load the possible values of types into select boxes, etc. If I used this method, every time I needed to add a new possible value for a type, I'd have to change the validation and the views.
Is there a standard way of doing something like this? Something like defining types (maybe models without DB backing?) where I could list all the possible values easily?
Thank you for any help or suggestions on this. It's been bothering me for a long time while doing RoR apps, and I'm tired of hacking around it.
I guess there are many different ways to do it. Personally I would keep things very simple and DRY.
In an initializer, set arrays in the global scope:
REGIONS = ["region A", "region B", "region C"]
In the models, use validations as you wrote. Check that the value is in the REGIONS array.
In the views, use Rails helpers to populate selects, radios etc. from the REGIONS array. If you always have the same select, write your own helper region_select for instance.

Using multiple dropdowns instead of a text field in Rails 3

I'm currently building my first Rails 3 app, but can't quite figure out how to get one piece of functionality.
I have a temperature field, but rather than using <%= f.text_field :temp %>, I'd like to have two dropdowns. The first dropdown would allow choosing 97-99, while the second dropdown would allow choosing 0-9. After the user has made their selection and saved, the results should be concatenated into e.g. 98.2
What is the best way to achieve this in Rails 3? Thanks!
I suggest creating two virtual attributes, one for each part of the temperature (97-99 and 0-9). Then define a before_validation method on the model that takes those two values (that aren't persisted to the database) and sets the temp attribute accordingly (that is persisted to the database). This way you can validate the temp value as you normally would and query it as you would any other regular value in the database. It also keeps your view code easy on the eyes. There are other ways to accomplish this though.
Ryan Bates has a great screencast on virtual attributes.
http://railscasts.com/episodes/167-more-on-virtual-attributes
You could make two attributes in whatever model you use. One for each field you want to use. Then when you hit save you have code in the model and uses these attributes to form the temperature attribute.

Rails - Should I use boolean fields or relational table?

User model has three keys: is_master, is_standard, is_guest because I originally wanted to use activerecord's boolean methods like is_master? or is_power?.
However, if it would be better to create a UserType relationship and create my own methods like this:
def master?
return true if self.user_type = 1
end
If the master/standard/guest relationship is mutually exclusive (that is, you can only ever be one of them) then a field that stores the type (in a human-readable form, please -- no opaque numbers) is better. You can always reimplement is_foo? trivially.
On the other hand, if you could have an account that be more than one of master/standard/guest/whatever at once, then stick with the separate boolean fields.
As a DBA I always hate when people are using columns as flags, it would be a lot of extra columns.
If it is all the same type (like account type), I would do as the first anwer suggests (including using text, not numbers).
If you on the other hand needs it for separate flags or multiple types (but having a restricted number) I would actually go for a binary calculation.
That is, have one columns in the table representing all the flags and then assign each flag a number.
ex.
FLAGS = {:master => 1, :standard => 2, :guest => 4, :power => 8,
:another_flag => 32, :yet_another_flag => 64}
def is_master?
self.flags & FLAGS[:master]
end
def is_standard?
self.flags & FLAGS[:standard]
end
It requires a bit more work when setting the values, but doesn't clutter up the table with a lot of columns used only for flags.
If you look at how the restful authentication plugin does it (which includes user roles) they use a join table.
I think a join is much more readable.Using a join allows you to be more flexible with your role-system.
If you require that a user can only have a single role I would put that logic in your model.
I'd start with the simplest thing that solves your current need. Refactor and add complexity from there as needed. Sounds like a set of boolean columns is just what you need.

Resources