picocandy/rubytus

View on GitHub
lib/rubytus/storage.rb

Summary

Maintainability
A
45 mins
Test Coverage
require 'json'
require 'rubytus/info'
require 'rubytus/error'
require 'pathname'

module Rubytus
  module StorageHelper
    def validates_data_dir(data_dir)
      if Pathname.new(data_dir).relative?
        data_dir = File.join(ENV['PWD'], data_dir)
      end

      begin
        unless File.directory?(data_dir)
          Dir.mkdir(data_dir)
        end
      rescue SystemCallError => _
        raise PermissionError, "Couldn't create `data_dir` in #{data_dir}"
      end

      unless File.world_writable?(data_dir)
        begin
          File.chmod(0777, data_dir)
        rescue Errno::EPERM
          raise PermissionError, "Couldn't make `data_dir` in #{data_dir} writable"
        end
      end

      data_dir
    end

    def file_path(uid)
      File.join(@options[:data_dir], "#{uid}.bin")
    end

    def info_path(uid)
      File.join(@options[:data_dir], "#{uid}.info")
    end
  end

  class Storage
    include StorageHelper

    def initialize(options)
      @options = options
    end

    def create_file(uid, entity_length, metadata = {})
      fpath = file_path(uid)
      ipath = info_path(uid)
      info  = Rubytus::Info.new(metadata)
      info.entity_length = entity_length

      begin
        File.open(fpath, 'w') {}
        File.open(ipath, 'w') do |f|
          f.write(info.to_json)
        end
      rescue SystemCallError => e
        raise(PermissionError, e.message) if e.class.name.start_with?('Errno::')
      end
    end

    def read_file(uid)
      fpath = file_path(uid)

      begin
        f = File.open(fpath, 'rb')
        f.read
      rescue SystemCallError => e
        raise(PermissionError, e.message) if e.class.name.start_with?('Errno::')
      ensure
        f.close unless f.nil?
      end
    end

    def patch_file(uid, data, offset = nil)
      fpath = file_path(uid)
      begin
        f = File.open(fpath, 'r+b')
        f.sync = true
        f.seek(offset) unless offset.nil?
        f.write(data)
        size = f.size
        f.close
        update_info(uid, size)
      rescue SystemCallError => e
        raise(PermissionError, e.message) if e.class.name.start_with?('Errno::')
      end
    end

    def read_info(uid)
      ipath = info_path(uid)

      begin
        data = File.open(ipath, 'r') { |f| f.read }
        JSON.parse(data, :object_class => Rubytus::Info)
      rescue SystemCallError => e
        raise(PermissionError, e.message) if e.class.name.start_with?('Errno::')
      end
    end

    def update_info(uid, offset)
      ipath = info_path(uid)
      info = read_info(uid)
      info.offset = offset

      begin
        File.open(ipath, 'w') do |f|
          f.write(info.to_json)
        end
      rescue SystemCallError => e
        raise(PermissionError, e.message) if e.class.name.start_with?('Errno::')
      end
    end
  end
end