I have a Rails app that has a COUNTRIES list with full country names and abbreviations created inside the Company model. The array for the COUNTRIES list is used for a select tag on the input form to store abbreviations in the DB. See below. VALID_COUNTRIES is used for validations of abbreviations in the DB. FULL_COUNTRIES is used to display the full country name from the abbreviation.
class Company < ActiveRecord::Base
COUNTRIES = [["Afghanistan","AF"],["Aland Islands","AX"],["Albania","AL"],...]
COUNTRIES_TRANSFORM = COUNTRIES.transpose
VALID_COUNTRIES = COUNTRIES_TRANSPOSE[1]
FULL_COUNTRIES = COUNTRIES_TRANSPOSE[0]
validates :country, inclusion: { in: VALID_COUNTRIES, message: "enter a valid country" }
...
end
On the form:
<%= select_tag(:country, options_for_select(Company::COUNTRIES, 'US')) %>
And to convert back the the full country name:
full_country = FULL_COUNTRIES[VALID_COUNTRIES.index(:country)]
This seems like an excellent application for a hash, except the key/value order is wrong. For the select I need:
COUNTRIES = {"Afghanistan" => "AF", "Aland Islands" => "AX", "Albania" => "AL",...}
While to take the abbreviation from the DB and display the full country name I need:
COUNTRIES = {"AF" => "Afghanistan", "AX" => "Aland Islands", "AL" => "Albania",...}
Which is a shame, because COUNTRIES.keys or COUNTRIES.values would give me the validation list (depending on which hash layout is used).
I’m relatively new to Ruby/Rails and am looking for the more Ruby-like way to solve the problem. Here are the questions:
- Does the transpose occur only once, and if so, when is it executed?
- Is there a way to specify the FULL_ and VALID_ lists that do not require the transpose?
- Is there a better or reasonable alternate way to do this? For instance, VALID_COUNTRIES is COUNTRIES[x][1] and FULL_COUNTRIES is COUNTRIES[x][0], but VALID_ must work with the validation.
- Is there a way to make a hash work with just one hash rather then one for the
select_tagand one for converting the abbreviations in the DB back to full names for display?
Yes at compile time because you are assigning to constants if you want it to be evaluated every time use a lambda
Yes use a map or collect (they are the same thing)
See Above
Yes I am not sure why a hash isn’t working as the rails docs say options_for_select will use
hash.to_a.map &:firstfor the options text andhash.to_a.map &:lastfor the options value so the first hash you give should be working if you can clarify why it is not I can help you more.