3
\$\begingroup\$

The Ruby on Rails framework doesn't allow you to validate enums submitted through a form in any sane manner. Evidently, enums are meant to be used to maintain internal application state, and are not supposed to be user-facing. Well, in my case it doesn't matter too much, but I still want validations *just in case*.

Here is a widely-used enum:

class Language < ActiveRecord::Base self.abstract_class = true enum language: { arabic: 0, chinese: 1, english: 2, french: 3, german: 4, italian: 5, japanese: 6, russian: 7, spanish: 8, other: 9 } end 

In my schema, the field using this enum is a text array: t.text "audio_languages", default: [], allowing multiple languages to be selected through the form.

In the form, the values are added as a list of check boxes:

HTML form

In the controller, the values for that field come in as:

"audio_languages"=>["English", ""]

Notice the last array entry, "". That's just how it works I guess, nothing I've tried gets rid of that, so I have to handle it in the validation.

In the validation, a language needs to be chosen, and the chosen language has to be present in the language enum:

def validate_audio_languages # Remove the empty string, which is an artifact of form submission of an array # field, during validation audio_languages.reject! { |l| l.empty? } if !audio_languages.any? errors.add(:audio_languages, "need to select a language") end # If any of the audio languages is invalid, add the error and break audio_languages.each do |al| valid = false Language.languages.each do |l, i| valid = true if (al.downcase == l.downcase) end if !valid errors.add(:audio_languages, "not a valid language selection") break end end end 

I'm concerned about the length of the function more than anything. Surely there's an easier way to do this kind of simple validation, especially to validate whether or not a value is actually in the enum. Any help in dealing with this issue is much appreciated!

\$\endgroup\$

    1 Answer 1

    2
    \$\begingroup\$

    You can use Ruby's array intersection and check for matches. The empty string passed in your params is irrelevant.

    def validate_audio_languages if audio_languages.any? enums = Language.languages.map(&:first) matches = enums & audio_languages.map!(&:downcase) matches.present? ? matches : errors.add(:audio_languages, "not a valid language selection") else errors.add(:audio_languages, "need to select a language") end end 

    You can shave off a couple more lines at the expense of readability, but it pays to keep things simple and readable.

    \$\endgroup\$

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.