Class: KBSecret::Record::Abstract Abstract

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/kbsecret/record/abstract.rb

Overview

This class is abstract.

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

Direct Known Subclasses

Environment, Login, Snippet, Todo, Unstructured

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(session, label, **body) ⇒ Abstract

Note:

Creation does not sync the new record; see #sync! for that.

Create a brand new record, associated with a session.

Parameters:

  • session (Session)

    the session to associate with

  • label (String, Symbol)

    the new record's label

  • body (Hash<Symbol, String>)

    a mapping of the record's data fields



124
125
126
127
128
129
130
131
132
133
134
# 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

Instance Attribute Details

#dataHash (readonly)

Returns the record's data.

Returns:

  • (Hash)

    the record's data



27
28
29
# File 'lib/kbsecret/record/abstract.rb', line 27

def data
  @data
end

#labelString (readonly)

Returns the record's label.

Returns:

  • (String)

    the record's label



21
22
23
# File 'lib/kbsecret/record/abstract.rb', line 21

def label
  @label
end

#pathString (readonly)

Returns the fully qualified path to the record in KBFS.

Returns:

  • (String)

    the fully qualified path to the record in KBFS



30
31
32
# File 'lib/kbsecret/record/abstract.rb', line 30

def path
  @path
end

#sessionSession

Returns the session associated with the record.

Returns:

  • (Session)

    the session associated with the record



15
16
17
# File 'lib/kbsecret/record/abstract.rb', line 15

def session
  @session
end

#timestampInteger (readonly)

Returns the UNIX timestamp marking the record's last modification.

Returns:

  • (Integer)

    the UNIX timestamp marking the record's last modification



18
19
20
# File 'lib/kbsecret/record/abstract.rb', line 18

def timestamp
  @timestamp
end

#typeSymbol (readonly)

Returns the record's type.

Returns:

  • (Symbol)

    the record's type



24
25
26
# File 'lib/kbsecret/record/abstract.rb', line 24

def type
  @type
end

Class Method Details

.data_field(field, sensitive: true, internal: false) ⇒ void

This method returns an undefined value.

Add a field to the record's data.

Parameters:

  • field (Symbol)

    the new field's name

  • sensitive (Boolean) (defaults to: true)

    whether the field is sensitive (e.g., a password)

  • internal (Boolean) (defaults to: false)

    whether the field should be populated by the user



38
39
40
41
42
43
44
45
46
47
48
# 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_fieldsArray<Symbol>

Note:

This includes internal fields, which are generated. See external_fields for the list of exclusively external fields.

Returns all data fields for the record class.

Returns:

  • (Array<Symbol>)

    all data fields for the record class



85
86
87
# File 'lib/kbsecret/record/abstract.rb', line 85

def data_fields
  @fields
end

.external_fieldsArray<Symbol>

Returns all external data fields for the record class.

Returns:

  • (Array<Symbol>)

    all external data fields for the record class



90
91
92
# File 'lib/kbsecret/record/abstract.rb', line 90

def external_fields
  @fields.reject { |f| internal? f }
end

.gen_methods(field) ⇒ void

This method returns an undefined value.

Generate the methods used to access a given field.

Parameters:

  • field (Symbol)

    the new field's name



53
54
55
56
57
58
59
60
61
62
63
64
65
# 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) ⇒ Boolean

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.

Returns whether the field is internal.

Parameters:

  • field (Symbol)

    the field's name

Returns:

  • (Boolean)

    whether the field is internal



78
79
80
# File 'lib/kbsecret/record/abstract.rb', line 78

def internal?(field)
  !!@internal[field]
end

.load!(session, hsh) ⇒ Record::AbstractRecord

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Load the given hash-representation into a record.

Parameters:

  • session (Session)

    the session to associate with

  • hsh (Hash)

    the record's hash-representation

Returns:

  • (Record::AbstractRecord)

    the created record



110
111
112
113
114
115
116
# File 'lib/kbsecret/record/abstract.rb', line 110

def load!(session, hsh)
  instance         = allocate
  instance.session = session
  instance.initialize_from_hash(hsh)

  instance
end

.sensitive?(field) ⇒ Boolean

Returns whether the field is sensitive.

Parameters:

  • field (Symbol)

    the field's name

Returns:

  • (Boolean)

    whether the field is sensitive



69
70
71
# File 'lib/kbsecret/record/abstract.rb', line 69

def sensitive?(field)
  !!@sensitive[field]
end

.typeSymbol

Returns the record's type.

Examples:

KBSecret::Record::Abstract.type # => :abstract

Returns:

  • (Symbol)

    the record's type



97
98
99
100
101
102
103
# File 'lib/kbsecret/record/abstract.rb', line 97

def type
  name.split("::")
      .last
      .gsub(/([^A-Z])([A-Z]+)/, '\1_\2')
      .downcase
      .to_sym
end

Instance Method Details

#data_fieldsArray<Symbol>

Returns all data fields for the record class.

Returns:

  • (Array<Symbol>)

    all data fields for the record class



157
# File 'lib/kbsecret/record/abstract.rb', line 157

def_delegators :"self.class", :data_fields, :external_fields, :sensitive?, :internal?

#defer_sync(implicit: true, &block) ⇒ void

Note:

This is useful for decreasing the number of writes performed, especially if multiple fields within the record are modified simultaneously.

This method returns an undefined value.

Evaluate the given block within the current instance, deferring any synchronizations caused by method calls (e.g., field changes).

Parameters:

  • implicit (Boolean) (defaults to: true)

    whether or not to call #sync! at the end of the block



202
203
204
205
206
207
# 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

#external_fieldsArray<Symbol>

Returns all external data fields for the record class.

Returns:

  • (Array<Symbol>)

    all external data fields for the record class



157
# File 'lib/kbsecret/record/abstract.rb', line 157

def_delegators :"self.class", :data_fields, :external_fields, :sensitive?, :internal?

#initialize_from_hash(hsh) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Fill in instance fields from a record's hash-representation.

Parameters:

  • hsh (Hash)

    the record's hash-representation.



140
141
142
143
144
145
146
147
# 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

#internal?Boolean

Returns whether the field is internal.

Returns:

  • (Boolean)

    whether the field is internal



157
# File 'lib/kbsecret/record/abstract.rb', line 157

def_delegators :"self.class", :data_fields, :external_fields, :sensitive?, :internal?

#populate_internal_fieldsvoid

Note:

This gets called at the end of #initialize, and should be overridden by children of KBSecret::Record::Abstract if they need to modify their internal fields during initialization.

This method returns an undefined value.

Fill in any internal fields that require a default value.



192
193
194
# File 'lib/kbsecret/record/abstract.rb', line 192

def populate_internal_fields
  nil # stub
end

#sensitive?Boolean

Returns whether the field is sensitive.

Returns:

  • (Boolean)

    whether the field is sensitive



157
# File 'lib/kbsecret/record/abstract.rb', line 157

def_delegators :"self.class", :data_fields, :external_fields, :sensitive?, :internal?

#sync!void

Note:

Every sync updates the record's timestamp.

This method returns an undefined value.

Write the record's in-memory state to disk.



179
180
181
182
183
184
185
186
# 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_hHash

Create a hash-representation of the current record.

Returns:

  • (Hash)

    the hash-representation



167
168
169
170
171
172
173
174
# File 'lib/kbsecret/record/abstract.rb', line 167

def to_h
  {
    timestamp: timestamp,
    label: label,
    type: type,
    data: data,
  }
end

#to_sString

Create a string representation of the current record.

Returns:

  • (String)

    the string representation



161
162
163
# File 'lib/kbsecret/record/abstract.rb', line 161

def to_s
  @label
end