class Moltrio::Config::FileStorage

Attributes

path[R]

Public Class Methods

new(path) click to toggle source
# File lib/moltrio/config/storage/file_storage.rb, line 16
def initialize(path)
  @path = path
end

Public Instance Methods

[](key) click to toggle source
# File lib/moltrio/config/storage/file_storage.rb, line 20
def [](key)
  if has_key?(key)
    *path, leaf = splitted_key(key)
    hamster_to_ruby(traverse(path).fetch(leaf))
  else
    nil
  end
end
[]=(dotted_key, value) click to toggle source
# File lib/moltrio/config/storage/file_storage.rb, line 29
def []=(dotted_key, value)
  ancestor_keys = splitted_key(dotted_key)
  current_value = value
  base_hash = hash

  while ancestor_keys.any?
    *ancestor_keys, current_key = ancestor_keys

    old_value = traverse(ancestor_keys, base_hash: base_hash)
    current_value = old_value.put(current_key, current_value)
  end

  unless @hash.equal?(base_hash)
    # Retry the change. The hash was updated by other thread while we
    # were computing the new hash.
    return self[dotted_key] = value
  end

  @hash = current_value

  save
end
has_key?(key) click to toggle source
# File lib/moltrio/config/storage/file_storage.rb, line 52
def has_key?(key)
  not_found = Object.new
  value = splitted_key(key).inject(hash) do |current, part|
    if current.respond_to?(:has_key?) && current.has_key?(part)
      current[part]
    else
      break not_found
    end
  end

  !value.equal?(not_found)
end
on_namespace(namespace) click to toggle source
# File lib/moltrio/config/storage/file_storage.rb, line 65
def on_namespace(namespace)
  scoped(namespace)
end

Private Instance Methods

hamster_to_ruby(object) click to toggle source
# File lib/moltrio/config/storage/file_storage.rb, line 119
def hamster_to_ruby(object)
  return object unless object.kind_of?(Hamster::Hash)

  object.inject({}) { |hash, (key, value)|
    hash[key] = hamster_to_ruby(value)
    hash
  }
end
hash() click to toggle source
# File lib/moltrio/config/storage/file_storage.rb, line 71
def hash
  @hash ||= load_from_file
end
load_from_file() click to toggle source
# File lib/moltrio/config/storage/file_storage.rb, line 89
def load_from_file
  ruby_hash = begin
    file = File.open(path, "r")
    file.flock(File::LOCK_SH)

    YAML.load_file(path)
  rescue Errno::ENOENT
  ensure
    file && file.flock(File::LOCK_UN)
  end

  ruby_to_hamster(ruby_hash || {})
rescue Errno::ENOENT
  Hamster.hash
end
ruby_to_hamster(object) click to toggle source

Not concerned about circular structures here, since this is just for internal usage.

# File lib/moltrio/config/storage/file_storage.rb, line 130
def ruby_to_hamster(object)
  return object unless object.kind_of?(Hash)

  object.inject(Hamster.hash) { |hash, (key, value)|
    hash.put(key, ruby_to_hamster(value))
  }
end
save() click to toggle source
# File lib/moltrio/config/storage/file_storage.rb, line 105
def save
  unless File.exist?(path)
    FileUtils.mkdir_p(File.dirname(path))
  end

  file = File.open(path, "w")
  file.flock(File::LOCK_EX)

  file.write YAML.dump(hamster_to_ruby(hash))
ensure
  file.flock(File::LOCK_UN)
  file.close
end
splitted_key(key) click to toggle source
# File lib/moltrio/config/storage/file_storage.rb, line 75
def splitted_key(key)
  key.to_s.split('.')
end
traverse(splitted_key, base_hash: hash) click to toggle source
# File lib/moltrio/config/storage/file_storage.rb, line 79
def traverse(splitted_key, base_hash: hash)
  splitted_key.inject(base_hash) { |object, current_key|
    if object.respond_to?(:has_key?) && object.has_key?(current_key)
      object.fetch(current_key)
    else
      break Hamster.hash
    end
  }
end