<commit_before>module Locomotive
  module Mounter
    module Models

      class ContentEntry < Base

        ## fields ##
        field :_slug,               localized: true
        field :_position,           default: 0
        field :_visible,            default: true
        field :seo_title,           localized: true
        field :meta_keywords,       localized: true
        field :meta_description,    localized: true

        field :content_type,        association: true

        attr_accessor :dynamic_attributes, :main_locale, :errors

        alias :_permalink :_slug
        alias :_permalink= :_slug=

        ## callbacks ##
        set_callback :initialize, :after, :set_default_main_locale
        set_callback :initialize, :after, :set_default_dynamic_attributes

        ## methods ##

        # By definition, if the label field defined in the content type is
        # localized, then the content entry will be considered as localized.
        #
        # @return [ Boolean ] True if the label field is localized.
        #
        def localized?
          field = self.content_type.label_field
          !!field.try(:localized)
        end

        # Return the internal label used to identify a content entry
        # in a YAML file for instance. It is based on the first field
        # of the related content type.
        #
        # @return [ String ] The internal label
        #
        def _label
          field = self.content_type.label_field
          value = self.dynamic_getter(field.name)

          if field.type == :belongs_to
            value.try(:_label)
          elsif field.type == :file
            value['url']
          else
            value
          end
        end

        # Process a minimal validation by checking if the required fields
        # are filled in or not.
        #
        # @return [ Boolean ] False if one of the required fields is missing.
        #
        def valid?
          self.errors = []
          self.content_type.fields.each do |field|
            if field.required
              if self.dynamic_getter(field.name).blank?
                self.errors << field.name
              end
            end
          end
          self.errors.blank?
        end

        # Return the list of the fields defined in the content type
        # for which there is a value assigned.
        #
        # @return [ Array ] The list of fields
        #
        def dynamic_fields
          self.dynamic_attributes.keys.map do |name|
            self.content_type.find_field(name)
          end
        end

        # Loop over the list of dynamic fields defined in
        # the content type for which there is a value assigned.
        #
        # @example: each_dynamic_field { |field, value| .... }
        #
        def each_dynamic_field(&block)
          return unless block_given?

          self.dynamic_fields.each do |field|
            value = self.localized_dynamic_attribute_value(field)
            block.call(field, value)
          end
        end

        # Determine if field passed in parameter is one of the dynamic fields.
        #
        # @param [ String/Symbol ] name Name of the dynamic field
        #
        # @return [ Boolean ] True if it is a dynamic field
        #
        def is_dynamic_field?(name)
          name = find_dynamic_name(name)
          !self.content_type.try(:find_field, name).nil?
        end

        # Find the name of a dynamic field from a String
        #
        # Examples:
        #   "name" references the name field
        #   "name=" references the name field
        #   "author_id" references the author field (belongs_to)
        #   "article_ids" references the articles field (many_to_many)
        #
        def find_dynamic_name(name)
          name = name.to_s.gsub(/\=$/, '')

          # _id or _ids (belongs_to or many_to_many)
          if name =~ /(.+)_ids\Z/
            name = $1.pluralize
          end

          name.to_sym
        end

        # Return the value of a dynamic field and cast it depending
        # on the type of the field (string, date, belongs_to, ...etc).
        #
        # @param [ String/Symbol ] name Name of the dynamic field
        #
        # @return [ Object ] The casted value (String, Date, ContentEntry, ...etc)
        #
        def dynamic_getter(name)
          field = self.content_type.find_field(name)
          value = self.localized_dynamic_attribute_value(field)

          case field.type
          when :date, :date_time
            value.is_a?(String) ? Chronic.parse(value) : value
          when :file
            value.present? ? { 'url' => value, 'filename' => File.basename(value) } : nil
          when :belongs_to
            field.klass.find_entry(value)
          when :has_many
            field.klass.find_entries_by(field.inverse_of, [self._label, self._permalink])
          when :many_to_many
            field.klass.find_entries_among(value)
          else
            # :string, :text, :select, :boolean, :email, :integer, :float, :tags
            value
          end
        end

        # Set the value of a dynamic field. If the value is a hash,
        # it assumes that it represents the translations.
        #
        # @param [ String/Symbol ] name Name of the dynamic field
        # @param [ Object ] value Value to set
        #
        def dynamic_setter(name, value)
          self.dynamic_attributes ||= {}
          self.dynamic_attributes[name.to_sym] ||= {}

          field = self.content_type.find_field(name)

          if value.is_a?(Hash) # already localized
            value.keys.each { |locale| self.add_locale(locale) }
            self.dynamic_attributes[name.to_sym].merge!(value.symbolize_keys)
          else
            if field.is_relationship? || !field.localized
              self.dynamic_attributes[name.to_sym] = value
            else
              self.add_locale(Locomotive::Mounter.locale)
              self.dynamic_attributes[name.to_sym][Locomotive::Mounter.locale] = value
            end
          end
        end

        # We also have to deal with dynamic attributes so that
        # it does not raise an exception when calling the attributes=
        # method.
        #
        # @param [ Hash ] attributes The new attributes
        #
        def write_attributes(attributes)
          _attributes = attributes.select do |name, value|
            if self.is_dynamic_field?(name)
              self.dynamic_setter(name, value)
              false
            else<commit_msg>forgot to the use the find_dynamic_name method declared in the previous commit in the write_attribute method<commit_after>module Locomotive
  module Mounter
    module Models

      class ContentEntry < Base

        ## fields ##
        field :_slug,               localized: true
        field :_position,           default: 0
        field :_visible,            default: true
        field :seo_title,           localized: true
        field :meta_keywords,       localized: true
        field :meta_description,    localized: true

        field :content_type,        association: true

        attr_accessor :dynamic_attributes, :main_locale, :errors

        alias :_permalink :_slug
        alias :_permalink= :_slug=

        ## callbacks ##
        set_callback :initialize, :after, :set_default_main_locale
        set_callback :initialize, :after, :set_default_dynamic_attributes

        ## methods ##

        # By definition, if the label field defined in the content type is
        # localized, then the content entry will be considered as localized.
        #
        # @return [ Boolean ] True if the label field is localized.
        #
        def localized?
          field = self.content_type.label_field
          !!field.try(:localized)
        end

        # Return the internal label used to identify a content entry
        # in a YAML file for instance. It is based on the first field
        # of the related content type.
        #
        # @return [ String ] The internal label
        #
        def _label
          field = self.content_type.label_field
          value = self.dynamic_getter(field.name)

          if field.type == :belongs_to
            value.try(:_label)
          elsif field.type == :file
            value['url']
          else
            value
          end
        end

        # Process a minimal validation by checking if the required fields
        # are filled in or not.
        #
        # @return [ Boolean ] False if one of the required fields is missing.
        #
        def valid?
          self.errors = []
          self.content_type.fields.each do |field|
            if field.required
              if self.dynamic_getter(field.name).blank?
                self.errors << field.name
              end
            end
          end
          self.errors.blank?
        end

        # Return the list of the fields defined in the content type
        # for which there is a value assigned.
        #
        # @return [ Array ] The list of fields
        #
        def dynamic_fields
          self.dynamic_attributes.keys.map do |name|
            self.content_type.find_field(name)
          end
        end

        # Loop over the list of dynamic fields defined in
        # the content type for which there is a value assigned.
        #
        # @example: each_dynamic_field { |field, value| .... }
        #
        def each_dynamic_field(&block)
          return unless block_given?

          self.dynamic_fields.each do |field|
            value = self.localized_dynamic_attribute_value(field)
            block.call(field, value)
          end
        end

        # Determine if field passed in parameter is one of the dynamic fields.
        #
        # @param [ String/Symbol ] name Name of the dynamic field
        #
        # @return [ Boolean ] True if it is a dynamic field
        #
        def is_dynamic_field?(name)
          name = find_dynamic_name(name)
          !self.content_type.try(:find_field, name).nil?
        end

        # Find the name of a dynamic field from a String
        #
        # Examples:
        #   "name" references the name field
        #   "name=" references the name field
        #   "author_id" references the author field (belongs_to)
        #   "article_ids" references the articles field (many_to_many)
        #
        def find_dynamic_name(name)
          name = name.to_s.gsub(/\=$/, '')

          # _id or _ids (belongs_to or many_to_many)
          if name =~ /(.+)_ids\Z/
            name = $1.pluralize
          end

          name.to_sym
        end

        # Return the value of a dynamic field and cast it depending
        # on the type of the field (string, date, belongs_to, ...etc).
        #
        # @param [ String/Symbol ] name Name of the dynamic field
        #
        # @return [ Object ] The casted value (String, Date, ContentEntry, ...etc)
        #
        def dynamic_getter(name)
          field = self.content_type.find_field(name)
          value = self.localized_dynamic_attribute_value(field)

          case field.type
          when :date, :date_time
            value.is_a?(String) ? Chronic.parse(value) : value
          when :file
            value.present? ? { 'url' => value, 'filename' => File.basename(value) } : nil
          when :belongs_to
            field.klass.find_entry(value)
          when :has_many
            field.klass.find_entries_by(field.inverse_of, [self._label, self._permalink])
          when :many_to_many
            field.klass.find_entries_among(value)
          else
            # :string, :text, :select, :boolean, :email, :integer, :float, :tags
            value
          end
        end

        # Set the value of a dynamic field. If the value is a hash,
        # it assumes that it represents the translations.
        #
        # @param [ String/Symbol ] name Name of the dynamic field
        # @param [ Object ] value Value to set
        #
        def dynamic_setter(name, value)
          self.dynamic_attributes ||= {}
          self.dynamic_attributes[name.to_sym] ||= {}

          field = self.content_type.find_field(name)

          if value.is_a?(Hash) # already localized
            value.keys.each { |locale| self.add_locale(locale) }
            self.dynamic_attributes[name.to_sym].merge!(value.symbolize_keys)
          else
            if field.is_relationship? || !field.localized
              self.dynamic_attributes[name.to_sym] = value
            else
              self.add_locale(Locomotive::Mounter.locale)
              self.dynamic_attributes[name.to_sym][Locomotive::Mounter.locale] = value
            end
          end
        end

        # We also have to deal with dynamic attributes so that
        # it does not raise an exception when calling the attributes=
        # method.
        #
        # @param [ Hash ] attributes The new attributes
        #
        def write_attributes(attributes)
          _attributes = attributes.select do |name, value|
            name = find_dynamic_name(name)
            if self.is_dynamic_field?(name)
              self.dynamic_setter(name, value)
              false
            else
{'fraction_non_alphanumeric': 0.07723146747352497, 'fraction_numerical': 0.000302571860816944, 'mean_word_length': 1.714227058098953, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5822624', 'n_tokens_mistral': 3537, 'n_tokens_neox': 3470, 'n_words': 1076}