AlchemyCMS/alchemy_cms

View on GitHub
app/decorators/alchemy/ingredient_editor.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
# frozen_string_literal: true

module Alchemy
  class IngredientEditor < SimpleDelegator
    alias_method :ingredient, :__getobj__

    def to_partial_path
      "alchemy/ingredients/#{partial_name}_editor"
    end

    # Returns the translated role for displaying in labels
    #
    # Translate it in your locale yml file:
    #
    #   alchemy:
    #     ingredient_roles:
    #       foo: Bar
    #
    # Optionally you can scope your ingredient role to an element:
    #
    #   alchemy:
    #     ingredient_roles:
    #       article:
    #         foo: Baz
    #
    def translated_role
      Alchemy.t(
        role,
        scope: "ingredient_roles.#{element.name}",
        default: Alchemy.t("ingredient_roles.#{role}", default: role.humanize)
      )
    end

    def css_classes
      [
        "ingredient-editor",
        partial_name,
        deprecated? ? "deprecated" : nil,
        (respond_to?(:level_options) && level_options.any?) ? "with-level-select" : nil,
        (respond_to?(:size_options) && size_options.many?) ? "with-size-select" : nil,
        settings[:linkable] ? "linkable" : nil,
        settings[:anchor] ? "with-anchor" : nil
      ].compact
    end

    def data_attributes
      {
        ingredient_id: id,
        ingredient_role: role
      }
    end

    # Returns a string to be passed to Rails form field tags to ensure it can be used with Rails' nested attributes.
    #
    # === Example:
    #
    #   <%= text_field_tag text_editor.form_field_name, text_editor.value %>
    #
    # === Options:
    #
    # You can pass an Ingredient column_name. Default is 'value'
    #
    # ==== Example:
    #
    #   <%= text_field_tag text_editor.form_field_name(:link), text_editor.value %>
    #
    def form_field_name(column = "value")
      "element[ingredients_attributes][#{form_field_counter}][#{column}]"
    end

    # Returns a unique string to be passed to a form field id.
    #
    # @param column [String] A Ingredient column_name. Default is 'value'
    #
    def form_field_id(column = "value")
      "element_#{element.id}_ingredient_#{id}_#{column}"
    end

    # Fixes Rails partial renderer calling to_model on the object
    # which reveals the delegated ingredient instead of this decorator.
    def respond_to?(method_name)
      return false if method_name == :to_model

      super
    end

    def has_warnings?
      definition.blank? || deprecated?
    end

    def linked?
      link.try(:present?)
    end

    def warnings
      return unless has_warnings?

      if definition.blank?
        Logger.warn("ingredient #{role} is missing its definition", caller(1..1))
        Alchemy.t(:ingredient_definition_missing)
      else
        deprecation_notice
      end
    end

    # Returns a deprecation notice for ingredients marked deprecated
    #
    # You can either use localizations or pass a String as notice
    # in the ingredient definition.
    #
    # == Custom deprecation notices
    #
    # Use general ingredient deprecation notice
    #
    #     - name: element_name
    #       ingredients:
    #         - role: old_ingredient
    #           type: Text
    #           deprecated: true
    #
    # Add a translation to your locale file for a per ingredient notice.
    #
    #     en:
    #       alchemy:
    #         ingredient_deprecation_notices:
    #           element_name:
    #             old_ingredient: Foo baz widget is deprecated
    #
    # or use the global translation that apply to all deprecated ingredients.
    #
    #     en:
    #       alchemy:
    #         ingredient_deprecation_notice: Foo baz widget is deprecated
    #
    # or pass string as deprecation notice.
    #
    #     - name: element_name
    #       ingredients:
    #         - role: old_ingredient
    #           type: Text
    #           deprecated: This ingredient will be removed soon.
    #
    def deprecation_notice
      case definition[:deprecated]
      when String
        definition[:deprecated]
      when TrueClass
        Alchemy.t(
          role,
          scope: [:ingredient_deprecation_notices, element.name],
          default: Alchemy.t(:ingredient_deprecated)
        )
      end
    end

    private

    def form_field_counter
      element.definition.fetch(:ingredients, []).index { |i| i[:role] == role }
    end
  end
end