How to use validations with mobility gem?

446 Views Asked by At

I'm trying to add validations to my mobility-powered application and i'm confused a little.Earlier I've used code like this

I18n.available_locales.each do |locale|
    validates :"name_#{locale}", presence: true, uniqueness: {scope: :animal_type}
end

And it worked fine. But in my last project it doesn't work at all. Any ideas how to perform validations? My config is below:

Mobility.configure do
  plugins do
    backend :container
    active_record
    reader
    writer
    backend_reader
    query
    cache
    presence
    locale_accessors
  end
end

UPD: I've identified my problem - it is because of , uniqueness: {scope: :animal_type}. Is it possible to use mobility with similar type of validations?

2

There are 2 best solutions below

0
On BEST ANSWER

When you use uniqueness validator it uses query to the database to ensure that record haven't already taken and you get this query:

Let's assume you have Animal model

SELECT 1 AS one FROM "animals" WHERE "animals"."name_en" = "Cat" AND "animals"."animal_type" = "some_type" LIMIT 1

And of course there is no name_en field in the animals table that is why you have an error

To archive this you have to write your own validator

class CustomValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    if translation_exists?(record, attribute)
      record.errors.add(attribute, options[:message] || :taken)
    end
  end

  private

  def translation_exists?(record, attribute)
    attribute, locale = attribute.to_s.split('_')
    record.class.joins(:string_translations).exists?(
      mobility_string_translations: { locale: locale, key: attribute },
      animals: { animal_type: record.animal_type }
    )
  end
end

And then in your model do next:

I18n.available_locales.each do |locale|
  validates :"name_#{locale}", presence: true, custom: true
end
0
On

@Yurii Stefaniuk's answer was very helpful. But if you are using Action Text with Mobility (https://github.com/sedubois/mobility-actiontext/) then the attribute names are different and you may need to use something like this instead:

  def translation_exists?(record, attribute)
    attribute, locale = attribute.to_s.split('_')
    record.class.joins(:plain_text_translations).exists?(
      { action_text_rich_texts: {
          locale: locale,
          body: record.send(attribute)
        } }
    )
  end