# frozen_string_literal: true require 'mobility/backends/active_record/key_value' module Mobility # - module Backends # # Implements the {Mobility::Backends::KeyValue} backend for ActionText. # # @example # class Post < ApplicationRecord # extend Mobility # translates :content, backend: :action_text # end # # post = Post.create(content: "

My text is rich

") # post.rich_text_translations # #=> # # post.rich_text_translations.first.to_s # #=> "
\n

My text is rich

\n
\n" # post.content # #=> "
\n

My text is rich

\n
\n" # post.rich_text_translations.first.class # #=> Mobility::Backends::ActionText::RichTextTranslation # class ActionText < ActiveRecord::KeyValue # override to return record instead of value def read(locale, **options) return super if self.options[:plain] translation_for(locale, **options) end class << self def valid_keys super.tap { |keys| keys.delete(:type) } << :plain end # @!group Backend Configuration # @option (see Mobility::Backends::KeyValue::ClassMethods#configure) def configure(options) options[:plain] = false unless options.has_key?(:plain) if options[:plain] options[:association_name] ||= 'plain_text_translations' options[:class_name] ||= PlainTextTranslation else options[:association_name] ||= 'rich_text_translations' options[:class_name] ||= RichTextTranslation end options[:key_column] ||= :name options[:value_column] ||= :body options[:belongs_to] ||= :record super end # @!endgroup # override destroy logic because we are not using the db tables from # the subclassed KeyValue implementation def define_after_destroy_callback(klass) # Ensure we only call after destroy hook once per translations class b = self translation_classes = [class_name, RichTextTranslation, PlainTextTranslation].uniq klass.after_destroy do @mobility_after_destroy_translation_classes = [] unless defined?(@mobility_after_destroy_translation_classes) (translation_classes - @mobility_after_destroy_translation_classes).each do |translation_class| translation_class.where(b.belongs_to => self).destroy_all end @mobility_after_destroy_translation_classes += translation_classes end end end setup do |attributes, options| next if options[:plain] attributes.each do |name| has_one :"rich_text_#{name}", -> { where(name: name, locale: Mobility.locale) }, class_name: 'Mobility::Backends::ActionText::RichTextTranslation', as: :record, inverse_of: :record, autosave: true, dependent: :destroy scope :"with_rich_text_#{name}", -> { includes("rich_text_#{name}") } scope :"with_rich_text_#{name}_and_embeds", -> { includes("rich_text_#{name}": { embeds_attachments: :blob }) } end singleton_class.prepend(WithAllRichText) unless singleton_class < WithAllRichText end module WithAllRichText def with_all_rich_text super.eager_load(:rich_text_translations) end end module ActionTextValidations extend ActiveSupport::Concern included do validates :name, presence: true, uniqueness: { scope: %i[record_id record_type locale], case_sensitive: true } validates :record, presence: true validates :locale, presence: true end end # Model for translated rich text class RichTextTranslation < ::ActionText::RichText extend ActionTextValidations end # Model for translated plain text class PlainTextTranslation < ::ActiveRecord::Base self.table_name = "action_text_rich_texts" belongs_to :record, polymorphic: true, touch: true extend ActionTextValidations end end register_backend(:action_text, ActionText) end end