Class: Gemstash::Resource

Inherits:
Object
  • Object
show all
Includes:
Env::Helper, 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.



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/gemstash/storage.rb', line 112

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)
  @properties = nil
end

Instance Attribute Details

#folderObject (readonly)

Returns the value of attribute folder.



96
97
98
# File 'lib/gemstash/storage.rb', line 96

def folder
  @folder
end

#nameObject (readonly)

Returns the value of attribute name.



96
97
98
# File 'lib/gemstash/storage.rb', line 96

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



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

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:



250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/gemstash/storage.rb', line 250

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

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

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

  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



135
136
137
138
139
140
141
# File 'lib/gemstash/storage.rb', line 135

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



187
188
189
190
# File 'lib/gemstash/storage.rb', line 187

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



228
229
230
231
232
233
234
235
236
237
# File 'lib/gemstash/storage.rb', line 228

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:



162
163
164
165
166
167
168
169
# File 'lib/gemstash/storage.rb', line 162

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:



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/gemstash/storage.rb', line 198

def update_properties(props)
  load_properties(force: 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