class KBSecret::Record::Abstract
Represents an abstract {KBSecret} record that can be subclassed to produce more useful records. @abstract
Attributes
@return [Hash] the record's data
@return [String] the record's label
@return [String] the fully qualified path to the record in KBFS
@return [Session] the session associated with the record
@return [Integer] the UNIX timestamp marking the record's last modification
@return [Symbol] the record's type
Public Class Methods
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
@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
@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
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
@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 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
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
@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
@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
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
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
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
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
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
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