locomotivecms/steam

View on GitHub
lib/locomotive/steam/services/content_entry_service.rb

Summary

Maintainability
A
35 mins
Test Coverage
require 'sanitize'

module Locomotive
  module Steam

    class ContentEntryService

      include Locomotive::Steam::Services::Concerns::Decorator

      attr_accessor_initialize :content_type_repository, :repository, :locale

      def all(type_slug, conditions = {}, as_json = false)
        with_repository(type_slug) do |_repository|
          _repository.all(conditions).map do |entry|
            _decorate(entry, as_json)
          end
        end
      end

      def find(type_slug, id_or_slug, as_json = false)
        with_repository(type_slug) do |_repository|
          entry = _repository.by_slug(id_or_slug) || _repository.find(id_or_slug)
          _decorate(entry, as_json)
        end
      end

      def build(type_slug, attributes)
        with_repository(type_slug) do |_repository|
          _attributes = prepare_attributes(_repository.content_type, attributes)

          entry = _repository.build(_attributes)

          i18n_decorate { entry }
        end
      end

      # Warning: do not work with localized and file fields
      def create(type_slug, attributes, as_json = false)
        with_repository(type_slug) do |_repository|
          _attributes = prepare_attributes(_repository.content_type, attributes)

          entry = _repository.build(_attributes)

          yield(entry) if block_given?

          decorated_entry = i18n_decorate { entry }

          if validate(_repository, decorated_entry)
            _repository.create(entry)
          end

          logEntryOperation(type_slug, decorated_entry)

          _json_decorate(decorated_entry, as_json)
        end
      end

      # Warning: do not work with localized and file fields
      def update(type_slug, id_or_slug, attributes, as_json = false)
        with_repository(type_slug) do |_repository|
          entry       = _repository.by_slug(id_or_slug) || _repository.find(id_or_slug)
          _attributes = prepare_attributes(_repository.content_type, attributes)

          decorated_entry = i18n_decorate { entry.change(_attributes) }

          if validate(_repository, decorated_entry)
            _repository.update(entry)
          end

          logEntryOperation(type_slug, decorated_entry)

          _json_decorate(decorated_entry, as_json)
        end
      end

      # Warning: do not work with localized and file fields
      def update_decorated_entry(decorated_entry, attributes)
        with_repository(decorated_entry.content_type) do |_repository|
          entry       = decorated_entry.__getobj__
          _attributes = prepare_attributes(_repository.content_type, attributes)

          entry.change(_attributes)

          # remove the proxy select fields because we don't need them at this point
          # and MongoDB is going to complain when persisting it.
          _repository.content_type.select_fields.each do |field|
            entry.attributes.delete(field.name)
          end

          # remove any association field
          _repository.content_type.association_fields.each do |field|
            entry.attributes.delete(field.name)
          end

          _repository.update(entry)

          logEntryOperation(decorated_entry.content_type.slug, decorated_entry)

          decorated_entry
        end
      end

      def delete(type_slug, id_or_slug)
        with_repository(type_slug) do |_repository|
          entry = _repository.by_slug(id_or_slug) || _repository.find(id_or_slug)
          _repository.delete(entry)
        end
      end

      def get_type(slug)
        return nil if slug.blank?

        content_type_repository.by_slug(slug)
      end

      def logger
        Locomotive::Common::Logger
      end

      private

      def logEntryOperation(type_slug, entry)
        if (json = entry.as_json)['errors'].blank?
          logger.info "[#{type_slug}] Entry persisted with success. #{json}"
        else
          logger.error "[#{type_slug}] Failed to persist entry. #{json}"
        end
      end

      def with_repository(type_or_slug)
        type = type_or_slug.respond_to?(:fields) ? type_or_slug : get_type(type_or_slug)

        return if type.nil?

        yield(repository.with(type))
      end

      def _decorate(entry, as_json)
        decorated_entry = i18n_decorate { entry }
        _json_decorate(decorated_entry, as_json)
      end

      def _json_decorate(entry, as_json)
        as_json ? entry.as_json : entry
      end

      def prepare_attributes(content_type, attributes)
        select_field_names = content_type.fields.selects.map(&:name)

        attributes.each do |key, value|
          next unless value.is_a?(String)
          attributes[key] = Sanitize.clean(value, Sanitize::Config::BASIC)
        end

        # special case: select fields. <name> becomes <name>_id
        attributes.map do |key, value|
          _key = select_field_names.include?(key.to_s) ? "#{key}_id" : key
          [_key, value]
        end.to_h
      end

      def validate(_repository, entry)
        # simple validations (existence of values) first
        entry.valid?

        # check if the entry has unique values for its
        # fields marked as unique
        content_type_repository.look_for_unique_fields(entry.content_type).each do |name, _|
          if _repository.exists?(name => entry.send(name), :"_id.ne" => entry._id)
            entry.errors.add(name, :taken)
          end
        end

        entry.errors.empty?
      end

    end

  end
end