class KBSecret::Record::Abstract

Represents an abstract {KBSecret} record that can be subclassed to produce more useful records. @abstract

Attributes

data[R]

@return [Hash] the record's data

label[R]

@return [String] the record's label

path[R]

@return [String] the fully qualified path to the record in KBFS

session[RW]

@return [Session] the session associated with the record

timestamp[R]

@return [Integer] the UNIX timestamp marking the record's last modification

type[R]

@return [Symbol] the record's type

Public Class Methods

data_field(field, sensitive: true, internal: false) click to toggle source

Add a field to the record's data. @param field [Symbol] the new field's name @param sensitive [Boolean] whether the field is sensitive (e.g., a password) @param internal [Boolean] whether the field should be populated by the user @return [void]

# File lib/kbsecret/record/abstract.rb, line 38
def data_field(field, sensitive: true, internal: false)
  @fields    ||= []
  @sensitive ||= {}
  @internal  ||= {}

  @fields << field
  @sensitive[field] = sensitive
  @internal[field]  = internal

  gen_methods field
end
data_fields() click to toggle source

@return [Array<Symbol>] all data fields for the record class @note This includes internal fields, which are generated. See {external_fields}

for the list of exclusively external fields.
# File lib/kbsecret/record/abstract.rb, line 85
def data_fields
  @fields
end
external_fields() click to toggle source

@return [Array<Symbol>] all external data fields for the record class

# File lib/kbsecret/record/abstract.rb, line 90
def external_fields
  @fields.reject { |f| internal? f }
end
gen_methods(field) click to toggle source

Generate the methods used to access a given field. @param field [Symbol] the new field's name @return [void]

# File lib/kbsecret/record/abstract.rb, line 53
def gen_methods(field)
  class_eval %[
    def #{field}
      @data[self.class.type][:"#{field}"]
    end

    def #{field}=(val)
      @data[self.class.type][:"#{field}"] = val
      @timestamp = Time.now.to_i
      sync!
    end
  ], __FILE__, __LINE__ - 10
end
internal?(field) click to toggle source

@param field [Symbol] the field's name @return [Boolean] whether the field is internal @note Fields that are marked as “internal” should not be presented to the user

for population. Instead, it is up to the record type itself to define a reasonable
default (and subsequent values) for these fields.
# File lib/kbsecret/record/abstract.rb, line 78
def internal?(field)
  !!@internal[field]
end
load!(session, hsh) click to toggle source

Load the given hash-representation into a record. @param session [Session] the session to associate with @param hsh [Hash] the record's hash-representation @return [Record::AbstractRecord] the created record @api private

# File lib/kbsecret/record/abstract.rb, line 110
def load!(session, hsh)
  instance         = allocate
  instance.session = session
  instance.initialize_from_hash(hsh)

  instance
end
new(session, label, **body) click to toggle source

Create a brand new record, associated with a session. @param session [Session] the session to associate with @param label [String, Symbol] the new record's label @param body [Hash<Symbol, String>] a mapping of the record's data fields @note Creation does not sync the new record; see {#sync!} for that.

# File lib/kbsecret/record/abstract.rb, line 124
def initialize(session, label, **body)
  @session    = session
  @timestamp  = Time.now.to_i
  @label      = label.to_s
  @type       = self.class.type
  @data       = { @type => body }
  @path       = File.join(session.path, "#{label}.json")
  @defer_sync = false

  populate_internal_fields
end
sensitive?(field) click to toggle source

@param field [Symbol] the field's name @return [Boolean] whether the field is sensitive

# File lib/kbsecret/record/abstract.rb, line 69
def sensitive?(field)
  !!@sensitive[field]
end
type() click to toggle source

@return [Symbol] the record's type @example

KBSecret::Record::Abstract.type # => :abstract
# File lib/kbsecret/record/abstract.rb, line 97
def type
  name.split("::")
      .last
      .gsub(/([^A-Z])([A-Z]+)/, '\1_\2')
      .downcase
      .to_sym
end

Public Instance Methods

defer_sync(implicit: true, &block) click to toggle source

Evaluate the given block within the current instance, deferring any

synchronizations caused by method calls (e.g., field changes).

@param implicit [Boolean] whether or not to call {#sync!} at the end of the block @return [void] @note This is useful for decreasing the number of writes performed, especially if multiple

fields within the record are modified simultaneously.
# File lib/kbsecret/record/abstract.rb, line 202
def defer_sync(implicit: true, &block)
  @defer_sync = true
  instance_eval(&block)
  @defer_sync = false
  sync! if implicit
end
initialize_from_hash(hsh) click to toggle source

Fill in instance fields from a record's hash-representation. @param hsh [Hash] the record's hash-representation. @return [void] @api private

# File lib/kbsecret/record/abstract.rb, line 140
def initialize_from_hash(hsh)
  @timestamp  = hsh[:timestamp]
  @label      = hsh[:label]
  @type       = hsh[:type].to_sym
  @data       = hsh[:data]
  @path       = File.join(session.path, "#{label}.json")
  @defer_sync = false
end
populate_internal_fields() click to toggle source

Fill in any internal fields that require a default value. @return [void] @note This gets called at the end of {#initialize}, and should be overridden by children

of {Abstract} if they need to modify their internal fields during initialization.
# File lib/kbsecret/record/abstract.rb, line 192
def populate_internal_fields
  nil # stub
end
sync!() click to toggle source

Write the record's in-memory state to disk. @note Every sync updates the record's timestamp. @return [void]

# File lib/kbsecret/record/abstract.rb, line 179
def sync!
  return if @defer_sync

  # bump the timestamp every time we sync
  @timestamp = Time.now.to_i

  File.write(path, JSON.pretty_generate(to_h))
end
to_h() click to toggle source

Create a hash-representation of the current record. @return [Hash] the hash-representation

# File lib/kbsecret/record/abstract.rb, line 167
def to_h
  {
    timestamp: timestamp,
    label: label,
    type: type,
    data: data,
  }
end
to_s() click to toggle source

Create a string representation of the current record. @return [String] the string representation

# File lib/kbsecret/record/abstract.rb, line 161
def to_s
  @label
end