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!

200_success
146k22 gold badges190 silver badges479 bronze badges
asked May 31, 2016 at 15:43
\$\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.

answered Jun 20, 2016 at 18:24
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.