Add validation to check translations amount on updates

In order to not allow users to remove all persited
translations from any resource. A few exceptions were
added:

* Does not apply to globalizable models without
   translatable attributes required
* Make a copy of main model error on current translations to be more realistic
This commit is contained in:
Senén Rodero Rodríguez
2019-06-07 16:45:14 +02:00
parent 12d20b481d
commit d3422acbb7
4 changed files with 55 additions and 0 deletions

View File

@@ -1,4 +1,5 @@
module Globalizable module Globalizable
MIN_TRANSLATIONS = 1
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
@@ -8,11 +9,18 @@ module Globalizable
def locales_not_marked_for_destruction def locales_not_marked_for_destruction
translations.reject(&:_destroy).map(&:locale) translations.reject(&:_destroy).map(&:locale)
end end
validate :check_translations_number, on: :update, if: :translations_required?
after_validation :copy_error_to_current_translation, on: :update
def description def description
self.read_attribute(:description).try :html_safe self.read_attribute(:description).try :html_safe
end end
def translations_required?
translated_attribute_names.any?{|attr| required_attribute?(attr)}
end
if self.paranoid? && translation_class.attribute_names.include?("hidden_at") if self.paranoid? && translation_class.attribute_names.include?("hidden_at")
translation_class.send :acts_as_paranoid, column: :hidden_at translation_class.send :acts_as_paranoid, column: :hidden_at
end end
@@ -21,6 +29,38 @@ module Globalizable
private private
def required_attribute?(attribute)
presence_validators = [ActiveModel::Validations::PresenceValidator,
ActiveRecord::Validations::PresenceValidator]
attribute_validators(attribute).any?{|validator| presence_validators.include? validator }
end
def attribute_validators(attribute)
self.class.validators_on(attribute).map(&:class)
end
def check_translations_number
errors.add(:base, :translations_too_short) unless traslations_count_valid?
end
def traslations_count_valid?
translations.reject(&:marked_for_destruction?).count >= MIN_TRANSLATIONS
end
def copy_error_to_current_translation
return unless errors.added?(:base, :translations_too_short)
if locales_persisted_and_marked_for_destruction.include?(I18n.locale)
locale = I18n.locale
else
locale = locales_persisted_and_marked_for_destruction.first
end
translation = translation_for(locale)
translation.errors.add(:base, :translations_too_short)
end
def searchable_globalized_values def searchable_globalized_values
values = {} values = {}
translations.each do |translation| translations.each do |translation|

View File

@@ -448,6 +448,7 @@ en:
valuation: valuation:
cannot_comment_valuation: "You cannot comment a valuation" cannot_comment_valuation: "You cannot comment a valuation"
messages: messages:
translations_too_short: Is mandatory to provide one translation at least
record_invalid: "Validation failed: %{errors}" record_invalid: "Validation failed: %{errors}"
another_poll_active: There is another poll active for the given period another_poll_active: There is another poll active for the given period
restrict_dependent_destroy: restrict_dependent_destroy:

View File

@@ -450,6 +450,7 @@ es:
valuation: valuation:
cannot_comment_valuation: "No puedes comentar una evaluación" cannot_comment_valuation: "No puedes comentar una evaluación"
messages: messages:
translations_too_short: El obligatorio proporcionar una traducción como mínimo
record_invalid: "Error de validación: %{errors}" record_invalid: "Error de validación: %{errors}"
another_poll_active: Hay otra encuesta activa para este periodo. another_poll_active: Hay otra encuesta activa para este periodo.
restrict_dependent_destroy: restrict_dependent_destroy:

View File

@@ -236,6 +236,19 @@ shared_examples "edit_translatable" do |factory_name, path_name, input_fields, t
expect_not_to_have_language "Español" expect_not_to_have_language "Español"
end end
scenario "Remove all translations should show an error message", :js do
skip("can't have invalid translations") if required_fields.empty?
visit path
click_link "Remove language"
click_link "Remove language"
click_button update_button_text
expect(page).to have_content "Is mandatory to provide one translation at least"
end
scenario "Remove a translation with invalid data", :js do scenario "Remove a translation with invalid data", :js do
skip("can't have invalid translations") if required_fields.empty? skip("can't have invalid translations") if required_fields.empty?