lib/pione/location/data-location.rb

Summary

Maintainability
B
5 hrs
Test Coverage
module Pione
  module Location
    class DataLocation < BasicLocation
      location_type :data

      KNOWN_ATTRS = [:need_caching, :real_appendable, :writable]

      class << self
        # @return [String]
        #   location's scheme name
        attr_reader :scheme

        # Declare the name as location scheme.
        #
        # @param name [String]
        #   scheme name
        def set_scheme(name)
          @scheme = name
          SCHEMES[name] = self
        end


        # Define a location's attribute.
        #
        # - need_caching : whether the location needs to be cached or not
        # - real_appendable : whether the location can appendable or not
        # - writable : whether the location is writable or not
        def define(name, val)
          if DataLocation::KNOWN_ATTRS.include?(name)
            (@attr ||= Hash.new)[name] = val
          else
            raise ArgumentError.new(name)
          end
        end

        # Return true if the location needs caching.
        def need_caching?
          @attr[:need_caching]
        end

        def real_appendable?
          @attr[:real_appendable]
        end

        def writable?
          @writable
        end
      end

      forward! :class, :scheme, :real_appendable?, :writable?, :need_caching?
      forward :@uri, :host

      # @return [URI]
      #   URI of the location
      attr_reader :uri

      # @return [Pathname]
      #   path of the location
      attr_reader :path

      # Create a location with the URI.
      #
      # @param uri [URI]
      #   location URI
      def initialize(uri)
        @address = uri.to_s
        @uri = uri.kind_of?(URI::Generic) ? uri : URI.parse(uri)
        @path = Pathname.new(uri.path)
        raise ArgumentError.new(uri) unless @uri.scheme = scheme
      end

      # Copy the content to temporary local location and return the location. If
      # the scheme is local, return itself.
      def local
        if scheme == "local"
          self
        else
          Location[Temppath.create].tap {|tmp| copy(tmp) if exist?}
        end
      end

      # Return true if scheme of the location is local.
      def local?
        scheme == "local"
      end

      # Create new location appended the name.
      #
      # @param name [String]
      #   filename or directory name
      # @return [BasicLocation]
      #   new location
      def +(name)
        self.class.new(@uri.as_directory + name.to_s)
      end

      # Create new location that has URI as a directory.
      #
      # @return [BasicLocation]
      #   new location
      def as_directory
        self.class.new(@uri.as_directory)
      end

      # Return the basename of the location.
      #
      # @param suffix [String]
      #   suffix name
      # @return [String]
      #   basename
      def basename(suffix="")
        File.basename(@path, suffix)
      end

      # Return the extension name of location.
      #
      # @return
      #    the extension name of location
      def extname
        File.extname(basename)
      end

      # Return the dirname of location. This method returns it as a location.
      def dirname
        rebuild(@path.dirname).as_directory
      end

      # Rebuild location with the path.
      #
      # @param path [Pathname]
      #   new path
      # @return [Location]
      #   location with new path
      def rebuild(path)
        scheme = @uri.scheme
        auth = @uri.user and @uri.password ? "%s:%s@" % [@uri.user, @uri.password] : ""
        host = @uri.host
        port = @uri.port ? ":%i" % @uri.port : ""
        path = path.expand_path("/").to_s
        Location["%s://%s%s%s%s" % [scheme, auth, host, port, path]]
      end

      # Return true if the location is cached.
      #
      # @return [Boolean]
      #   true if the location is cached
      def cached?
        System::FileCache.cached?(self)
      end

      # Write a data into the location.
      #
      # @param data [String]
      #   data content
      # @return [void]
      def write(data)
        if exist?
          update(data)
        else
          create(data)
        end
      end

      # Creates a file at the location. If a file exists at the location aleady,
      # it raises an exception.
      #
      # @param data [String]
      #   data content
      # @return [void]
      def create(data)
        raise NotImplementedError
      end

      # Append data to the location data.
      #
      # @param data [String]
      #   data content
      # @return [void]
      def append(data)
        if real_appendable?
          raise NotImplmentedError
        else
          _local = local
          _local.append(data)
          _local.copy(self)
        end
      end

      # Read location data.
      #
      # @return [String]
      #   data content
      def read
        raise NotImplementedError
      end

      # Update with the data.
      #
      # @param data [String]
      #   new data content
      # @return [void]
      def update(data)
        raise NotImplementedError
      end

      # Delete data of the location.
      #
      # @return [void]
      def delete
        raise NotImplementedError
      end

      # Return ctime of the location.
      #
      # @return [Time]
      #   ctime
      def ctime
        raise NotImplementedError
      end

      # Return mtime of the location.
      #
      # @return [Time]
      #   mtime
      def mtime
        raise NotImplementedError
      end

      # Set mtime of the location.
      #
      # @return [void]
      def mtime=(time)
        raise NotImplementedError
      end

      # Return byte size of data in the location.
      #
      # @return [Integer]
      #   byte size of data
      def size
        raise NotImplementedError
      end

      # Return entries of the location.
      #
      # @return [Array<Location>]
      #   entries of the location
      def entries(&b)
        raise NotImplementedError
      end

      # Return relative entries of the location.
      #
      # @return [Array<String>]
      #   entries of the location
      def rel_entries(option)
        raise NotImplementedError
      end

      # Return file entries of the location.
      #
      # @return [Array<Location>]
      #    file entries of the location
      def file_entries
        entries.select{|entry| entry.file?}
      end

      # Return directory entries of the location.
      #
      # @return [Array<Location>]
      #    directory entries of the location
      def directory_entries
        entries.select do |entry|
          entry.directory? and not(entry.path.basename == "." or entry.path.basename == "..")
        end
      end

      # Return true if there is data in the location.
      #
      # @return [Boolean]
      #   if there is data in the location
      def exist?
        raise NotImplementedError
      end

      # Return true if data at the location is a file. When there exists no
      # files and no directories, then return false.
      #
      # @return [Boolean]
      #   true if data at the location is a file
      def file?
        raise NotImplementedError
      end

      # Return true if data at the location is a directory. When there exists no
      # files and no direcotries, then return false.
      #
      # @return [Boolean]
      #   true if data at the location is a directory
      def directory?
        raise NotImplementedError
      end

      # Make the path a directory.
      #
      # @return [void]
      def mkdir
        raise NotImplementedError
      end

      # Move to the destination.
      #
      # @param dest [BasicLocation]
      #   destination
      # @return [void]
      def move(dest)
        raise NotImplementedError
      end

      # Copy location's content to the destination.
      #
      # @param dest [BasicLocation]
      #   destination
      # @return [void]
      def copy(dest)
        raise NotImplementedError
      end

      # Link to the destination. If the location scheme is same to destination,
      # create link by a symbolic link or lightweight copy method. If not, copy
      # it simply.
      #
      # @param dest [BasicLocation]
      #   destination
      # @return [void]
      def link(dest)
        raise NotImplementedError
      end

      # Move data to the destination and link self to it.
      #
      # @param dest [BasicLocation]
      #   destination
      # @return [void]
      def turn(dest)
        raise NotImplementedError
      end

      # Return the digest string by SHA1.
      #
      # @return [String]
      #   hex-string of the file
      def sha1
        if file?
          Digest::SHA1.file(local.path)
        else
          raise InvalidFileOperation.new(self)
        end
      end

      # @api private
      def inspect
        "#<%s %s:%s>" % [self.class, scheme, @path.to_s]
      end
      alias :to_s :inspect

      # @api private
      def ==(other)
        return false unless other.kind_of?(self.class)
        @uri == other.uri
      end
      alias :eql? :"=="

      # @api private
      def hash
        @uri.hash
      end
    end
  end
end