Class: Gemstash::Resource

Inherits:
Object
  • Object
show all
Includes:
Logging
Defined in:
lib/gemstash/storage.rb

Overview

A resource within the storage engine. The resource may have 1 or more files associated with it along with a metadata Hash that is stored in a YAML file.

Defined Under Namespace

Classes: VersionTooNew

Constant Summary collapse

VERSION =
1

Constants included from Logging

Logging::LEVELS

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Logging

#log, #log_error, logger, reset, setup_logger

Constructor Details

#initialize(folder, name) ⇒ Resource

This object should not be constructed directly, but instead via Storage#resource.



104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/gemstash/storage.rb', line 104

def initialize(folder, name)
  @base_path = folder
  @name = name
  # Avoid odd characters in paths, in case of issues with the file system
  safe_name = sanitize(@name)
  # Use a trie structure to avoid file system limits causing too many files in 1 folder
  # Downcase to avoid issues with case insensitive file systems
  trie_parents = safe_name[0...3].downcase.split("")
  # The digest is included in case the name differs only by case
  # Some file systems are case insensitive, so such collisions will be a problem
  digest = Digest::MD5.hexdigest(@name)
  child_folder = "#{safe_name}-#{digest}"
  @folder = File.join(@base_path, *trie_parents, child_folder)
end

Instance Attribute Details

#folderObject (readonly)

Returns the value of attribute folder.



89
90
91
# File 'lib/gemstash/storage.rb', line 89

def folder
  @folder
end

#nameObject (readonly)

Returns the value of attribute name.



89
90
91
# File 'lib/gemstash/storage.rb', line 89

def name
  @name
end

Instance Method Details

#content(key) ⇒ String

Fetch the content for the given key. This will load and cache the properties and the content of the key. The key corresponds to the content key provided to #save.

Parameters:

  • key (Symbol)

    the key of the content to load

Returns:

  • (String)

    the content stored in the key



168
169
170
171
172
# File 'lib/gemstash/storage.rb', line 168

def content(key)
  @content ||= {}
  load(key) unless @content.include?(key)
  @content[key]
end

#delete(key) ⇒ Gemstash::Resource

Delete the content for the given key. If the key is the last one for this resource, the metadata properties will be deleted as well. The key corresponds to the content key provided to #save.

The resource will be reset afterwards, clearing any cached content or properties.

Does nothing if the key doesn’t #exist?.

Parameters:

  • key (Symbol)

    the key of the content to delete

Returns:



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/gemstash/storage.rb', line 241

def delete(key)
  return self unless exist?(key)

  begin
    File.delete(content_filename(key))
  rescue => e
    log_error "Failed to delete stored content at #{content_filename(key)}", e, level: :warn
  end

  begin
    File.delete(properties_filename) unless content?
  rescue => e
    log_error "Failed to delete stored properties at #{properties_filename}", e, level: :warn
  end

  return self
ensure
  reset
end

#exist?(key = nil) ⇒ Boolean

When key is nil, this will test if this resource exists with any content. If a key is provided, this will test that the resource exists with at least the given key file. The key corresponds to the content key provided to #save.

Parameters:

  • key (Symbol, nil) (defaults to: nil)

    the key of the content to check existence

Returns:

  • (Boolean)

    true if the indicated content exists



126
127
128
129
130
131
132
# File 'lib/gemstash/storage.rb', line 126

def exist?(key = nil)
  if key
    File.exist?(properties_filename) && File.exist?(content_filename(key))
  else
    File.exist?(properties_filename) && content?
  end
end

#propertiesHash

Fetch the metadata properties for this resource. The properties will be cached for future calls.

Returns:

  • (Hash)

    the metadata properties for this resource



178
179
180
181
# File 'lib/gemstash/storage.rb', line 178

def properties
  load_properties
  @properties || {}
end

#property?(*keys) ⇒ Boolean

Check if the metadata properties includes the keys. The keys represent a nested path in the properties to check.

Examples:

resource = Gemstash::Storage.for("x").resource("y")
resource.save({ file: "content" }, foo: "one", bar: { baz: "qux" })
resource.has_property?(:foo)       # true
resource.has_property?(:bar, :baz) # true
resource.has_property?(:missing)   # false
resource.has_property?(:foo, :bar) # false

Parameters:

  • keys (Array<Object>)

    one or more keys pointing to a property

Returns:

  • (Boolean)

    whether the nested keys points to a valid property



219
220
221
222
223
224
225
226
227
228
# File 'lib/gemstash/storage.rb', line 219

def property?(*keys)
  keys.inject(node: properties, result: true) do |memo, key|
    if memo[:result]
      memo[:result] = memo[:node].is_a?(Hash) && memo[:node].include?(key)
      memo[:node] = memo[:node][key] if memo[:result]
    end

    memo
  end[:result]
end

#save(content, properties = nil) ⇒ Gemstash::Resource

Save one or more files for this resource given by the content hash. Metadata properties about the file(s) may be provided in the optional properties parameter. The keys in the content hash correspond to the file name for this resource, while the values will be the content stored for that key.

Separate calls to save for the same resource will replace existing files, and add new ones. Properties on additional calls will be merged with existing properties. Nested hashes in properties will also be merged.

Examples:

Gemstash::Storage.for("foo").resource("bar").save(baz: "qux")
Gemstash::Storage.for("foo").resource("bar").save(baz: "one", qux: "two")
Gemstash::Storage.for("foo").resource("bar").save({ baz: "qux" }, meta: true)

Parameters:

  • content (Hash{Symbol => String})

    files to save, *must not be nil*

  • properties (Hash, nil) (defaults to: nil)

    metadata properties related to this resource

Returns:



153
154
155
156
157
158
159
160
# File 'lib/gemstash/storage.rb', line 153

def save(content, properties = nil)
  content.each do |key, value|
    save_content(key, value)
  end

  update_properties(properties)
  self
end

#update_properties(props) ⇒ Gemstash::Resource

Update the metadata properties of this resource. The props will be merged with any existing properties. Nested hashes in the properties will also be merged.

Parameters:

  • props (Hash)

    the properties to add

Returns:



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/gemstash/storage.rb', line 189

def update_properties(props)
  load_properties(true)

  deep_merge = proc do |_, old_value, new_value|
    if old_value.is_a?(Hash) && new_value.is_a?(Hash)
      old_value.merge(new_value, &deep_merge)
    else
      new_value
    end
  end

  props = properties.merge(props || {}, &deep_merge)
  save_properties(properties.merge(props || {}))
  self
end