duke-libraries/ddr-models

View on GitHub
lib/ddr/datastreams/datastream_behavior.rb

Summary

Maintainability
A
1 hr
Test Coverage
module Ddr
  module Datastreams
    module DatastreamBehavior
      extend ActiveSupport::Concern

      DEFAULT_FILE_EXTENSION = "bin"

      STRFTIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%LZ"

      included do
        around_save :notify_save
        around_destroy :notify_delete
      end

      def validate_checksum! checksum, checksum_type=nil
        raise Ddr::Models::Error, "Checksum cannot be validated on new datastream." if new?
        raise Ddr::Models::Error, "Checksum cannot be validated on unpersisted content." if content_changed?
        raise Ddr::Models::ChecksumInvalid, "The repository internal checksum validation failed." unless dsChecksumValid
        algorithm = checksum_type || self.checksumType
        ds_checksum = if !external? && algorithm == self.checksumType
                        self.checksum
                      else
                        content_digest(algorithm)
                      end
        if checksum == ds_checksum
          "The checksum [#{algorithm}]#{checksum} is valid for datastream #{version_info}."
        else
          raise Ddr::Models::ChecksumInvalid, "The checksum [#{algorithm}]#{checksum} is not valid for datastream #{version_info}."
        end
      end

      def version_history
        versions.map(&:profile)
      end

      def version_uri
        # E.g., info:fedora/duke:1/content/content.0
        ["info:fedora", pid, dsid, dsVersionID].join("/") unless new?
      end

      def version_info
        # E.g., info:fedora/duke:1/content/content.0 [2013-09-26T20:00:03.357Z]
        "#{version_uri} [#{Ddr::Utils.ds_as_of_date_time(self)}]" unless new?
      end

      def create_date_string
        dsCreateDate.strftime(STRFTIME_FORMAT) if dsCreateDate
      end

      def content_digest algorithm
        Ddr::Utils.digest(self.content, algorithm)
      end

      def file_paths
        if new?
          return Array(file_path)
        else
          versions.map(&:file_path).compact
        end
      end

      def file_path
        if external? && dsLocation.present? && dsLocation.start_with?("file:")
          Ddr::Utils.path_from_uri(dsLocation)
        end
      end

      def file_name
        if path = file_path
          File.basename(path)
        end
      end

      # Returns the size of the external file for the datastream.
      def file_size
        if external?
          if path = file_path
            File.size(path)
          end
        else
          dsSize
        end
      end

      # Return default file extension for datastream based on MIME type
      def default_file_extension
        mimetypes = MIME::Types[mimeType]
        return mimetypes.first.extensions.first unless mimetypes.empty?
        case mimeType
        when 'application/n-triples'
          'txt'
        else
          DEFAULT_FILE_EXTENSION
        end
      end

      # Return default file name prefix based on object PID
      def default_file_prefix
        [pid.sub(/:/, '_'), dsid].join("_")
      end

      # Return default file name
      def default_file_name
        [default_file_prefix, default_file_extension].join(".")
      end

      def tempfile(prefix: nil, suffix: nil)
        if empty?
          raise Ddr::Models::Error, "Refusing to create tempfile for empty datastream!"
        end
        prefix ||= default_file_prefix + "--"
        suffix ||= "." + default_file_extension
        Tempfile.open [prefix, suffix], encoding: Encoding::ASCII_8BIT do |f|
          f.write(content)
          f.close
          yield f
        end
      end

      private

      def default_notification_payload
        { pid: pid, file_id: dsid, control_group: controlGroup }
      end

      def delete_notification_payload
        default_notification_payload.merge(
          profile: profile.dup,
          version_history: version_history
        )
      end

      def notify_save
        ActiveSupport::Notifications.instrument(
          Ddr::Datastreams::SAVE,
          default_notification_payload.merge(attributes_changed: changes)
        ) do |payload|
          yield
          payload[:profile] = profile.dup
        end
      end

      def notify_delete
        ActiveSupport::Notifications.instrument(
          Ddr::Datastreams::DELETE, delete_notification_payload
        ) do |payload|
          yield
        end
      end

    end
  end
end