Normally when I create a selection box in form it is linked to a fact-table model, a model that exists purely to represent the options in the select, but doesn’t have properties of its own. A contrived example might be the colours: you create a model called Colour with an attribute of name, or color or some-such. If another model then has some kind of color property you simply add a “belongs_to :color
” relationship to it (along with the usual migration). This has a number of advantages over hard-coding that list in the form and using a text field. It makes it super easy to reuse the concept in other forms, it takes advantage of all the clever things that you can do with AR collections, and adding a new colour is very easy, you can just add a new entry to the fact table without having to update every location where you use it.
However, from time to time you really do have a set of options for a selection field that are definitely not going to change, and are pretty short.
Generating the select tag is easy, but you still need to validate it. You can do this using the :inclusions option of the AR validate method, e.g.:
validates :response, inclusion: %w(yes no maybe)
This will simply validate that the value of response is in the list given.
You can also use an integer range, for example if you have a select tage with a list of numbers in it…
validates :your_age, inclusion: { in: 18..80 }
This would of couse also work for a simple text form, as an alternative to the :numericality
option.
If you want to generate the form and validation from the same list, making it easier to keep up-to-date, simply use a model constant…
class Answer OPTIONS = %w(yes no maybe) validates :would_you_come_again, inclusion: OPTIONS end
…and your form might look something like this (using simple_form)…
<%= simple_form_for(@answer) do |f| %> <%= f.input :would_you_come_again, as: :select, collection: Answer::OPTIONS %> <% end %>
The :exclusion option is (you’ll be unsurprised to hear) the inverse of :inclusion. This is particularly useful for where you have a blacklist of things you want to exclude. For example, we are building an application that uses account subdomains, so we need to thinks about restricting the account names people might register with, either because we might want to use those for special purposes (admin, static, media, etc). validates :exclude
lets us restrict those account names in one simple line.