class Scryglass::Ro

A ro is essentially a complex wrapper for an object that deals with how it is

nested and displayed relative to other ros/objects.

Constants

WRAPPER_TYPES

Attributes

depth[RW]
expanded[RW]
has_cursor[RW]
index[RW]
key[RW]
key_string[RW]
key_value_relationship_indicator[RW]
lens_strings[RW]
parent_ro[RW]
scry_session[RW]
special_sub_ro_type[RW]
sub_ros[RW]
val_type[RW]
value[RW]
value_string[RW]
wrappers[RW]

Public Class Methods

new(scry_session:, val:, val_type:, parent_ro:, key:, depth:, key_value_relationship_indicator: false, special_sub_ro_type: nil) click to toggle source
# File lib/scryglass/ro.rb, line 25
def initialize(scry_session:,
               val:,
               val_type:,
               parent_ro:,
               key:,
               depth:,
               key_value_relationship_indicator: false,
               special_sub_ro_type: nil)
  key_clip_length = Scryglass.config.tree_view_key_string_clip_length
  value_clip_length = Scryglass.config.tree_view_value_string_clip_length

  self.has_cursor = false
  self.expanded = false

  self.key_value_relationship_indicator = key_value_relationship_indicator

  ## Open up ViewWrappers and grab their objects and their custom strings
  if key.class == Scryglass::ViewWrapper
    self.key_string = key.to_s.clip_at(key_clip_length)
    self.key = key.model
  else
    # Note: `.inspect` may return *true newlines* for objects with a custom
    #   `.inspect`, which will sabotage scry's display, so we gsub thusly:
    self.key_string = key.inspect
                         .gsub("\n", "\\n")
                         .clip_at(key_clip_length)
    self.key = key
  end
  if val.class == Scryglass::ViewWrapper
    self.value_string = val.to_s.clip_at(value_clip_length)
    self.value = val.model
  else
    # Note: `.inspect` may return *true newlines* for objects with a custom
    #   `.inspect`, which will sabotage scry's display, so we gsub thusly:
    self.value_string = val.inspect
                           .gsub("\n", "\\n")
                           .clip_at(value_clip_length)
    self.value = val
  end

  self.sub_ros = []
  self.parent_ro = parent_ro
  self.val_type = val_type
  self.special_sub_ro_type = special_sub_ro_type
  self.depth = depth
  self.wrappers = WRAPPER_TYPES[value.class.to_s.split('::').last] || '?¿'

  self.lens_strings = { key: {}, value: {} }

  self.index = scry_session.all_ros.count
  scry_session.all_ros << self
  self.scry_session = scry_session
end

Private Class Methods

safe_quick_check() { || ... } click to toggle source
# File lib/scryglass/ro.rb, line 338
def safe_quick_check
  begin
    Timeout.timeout(0.05) do
      yield
    end
  rescue
    nil
  end
end

Public Instance Methods

<=>(other) click to toggle source

This exists so that an easy *unordered array match* can occur elsewhere.

# File lib/scryglass/ro.rb, line 187
def <=>(other)
  unless self.class == other.class
    raise ArgumentError, "Comparison of #{self.class} with #{other.class}"
  end

  object_id <=> other.object_id
end
ar_sub_ros() click to toggle source
# File lib/scryglass/ro.rb, line 145
def ar_sub_ros
  sub_ros.select { |sub_ro| sub_ro.special_sub_ro_type == :ar }
end
bucket?() click to toggle source
# File lib/scryglass/ro.rb, line 207
def bucket?
  val_type == :bucket
end
current_subject() click to toggle source
# File lib/scryglass/ro.rb, line 137
def current_subject
  send(scry_session.current_subject_type)
end
enum_sub_ros() click to toggle source
# File lib/scryglass/ro.rb, line 149
def enum_sub_ros
  sub_ros.select { |sub_ro| sub_ro.special_sub_ro_type == :enum }
end
iv_sub_ros() click to toggle source
# File lib/scryglass/ro.rb, line 141
def iv_sub_ros
  sub_ros.select { |sub_ro| sub_ro.special_sub_ro_type == :iv }
end
key_value_pair?() click to toggle source
# File lib/scryglass/ro.rb, line 215
def key_value_pair?
  !!key_value_relationship_indicator
end
next_ro_without_using_index() click to toggle source

(Used for recalculate_indeces after new Ros have been injected)

# File lib/scryglass/ro.rb, line 154
def next_ro_without_using_index
  return sub_ros.first if sub_ros.first
  return sibling_down if sibling_down
  return nil if top_ro?

  upward_feeler_ro = self
  until upward_feeler_ro.sibling_down || upward_feeler_ro.top_ro?
    upward_feeler_ro = upward_feeler_ro.parent_ro
  end
  upward_feeler_ro.sibling_down
end
next_visible_ro_down() click to toggle source
# File lib/scryglass/ro.rb, line 98
def next_visible_ro_down
  raise '(Must be called on a "visible" row)' unless visible?

  first_sub_ro = sub_ros.first
  return first_sub_ro if expanded && first_sub_ro
  return nil if top_ro?

  next_sibling = sibling_down
  return next_sibling if next_sibling

  # Note: since this ro is known to be visible, all its parents are, too.
  upward_feeler_ro = self.parent_ro
  parents_lower_sibling = upward_feeler_ro.sibling_down
  until parents_lower_sibling || upward_feeler_ro.top_ro?
    upward_feeler_ro = upward_feeler_ro.parent_ro
    parents_lower_sibling = upward_feeler_ro.sibling_down
  end

  parents_lower_sibling
end
next_visible_ro_up() click to toggle source
# File lib/scryglass/ro.rb, line 119
def next_visible_ro_up
  raise '(Must be called on a "visible" row)' unless visible?

  return nil if top_ro?

  next_sibling_up = sibling_up

  # Note: since this ro is known to be visible, all its parents are, too.
  return parent_ro unless next_sibling_up

  downward_feeler_ro = next_sibling_up
  while downward_feeler_ro.expanded && downward_feeler_ro.sub_ros.any?
    downward_feeler_ro = downward_feeler_ro.sub_ros.last
  end

  downward_feeler_ro
end
nugget?() click to toggle source
# File lib/scryglass/ro.rb, line 211
def nugget?
  val_type == :nugget
end
sibling_down() click to toggle source
# File lib/scryglass/ro.rb, line 166
def sibling_down
  return nil if top_ro?

  siblings = parent_ro.sub_ros
  self_index = siblings.index(self)
  return nil if self == siblings.last

  siblings[self_index + 1]
end
sibling_up() click to toggle source
# File lib/scryglass/ro.rb, line 176
def sibling_up
  return nil if top_ro?

  siblings = parent_ro.sub_ros
  self_index = siblings.index(self)
  return nil if self_index.zero?

  siblings[self_index - 1]
end
special_sub_ros() click to toggle source
# File lib/scryglass/ro.rb, line 219
def special_sub_ros
  sub_ros.select(&:special_sub_ro_type)
end
to_s() click to toggle source
# File lib/scryglass/ro.rb, line 83
def to_s
  value_indicator =
    bucket? ? bucket_indicator : value_string

  key_value_spacer =
    key_value_pair? ? key_string + key_value_relationship_indicator : ''
    dot = '•'
    dot = "\e[36m#{dot}\e[00m" if Scryglass.config.dot_coloring # cyan then back to *default*
  special_sub_ro_expansion_indicator =
    any_special_sub_ros? && !expanded ? dot : ' '

  left_fill_string + special_sub_ro_expansion_indicator +
    key_value_spacer + value_indicator
end
top_ro?() click to toggle source
# File lib/scryglass/ro.rb, line 79
def top_ro?
  parent_ro.nil?
end
visible?() click to toggle source
# File lib/scryglass/ro.rb, line 196
def visible?
  return true if top_ro?

  scanning_ro = parent_ro
  until scanning_ro.top_ro? || !scanning_ro.expanded
    scanning_ro = scanning_ro.parent_ro
  end

  scanning_ro.expanded
end

Private Instance Methods

any_normal_sub_ros?() click to toggle source
# File lib/scryglass/ro.rb, line 229
def any_normal_sub_ros?
  !!sub_ros.find { |ro| !ro.special_sub_ro_type }
end
any_special_sub_ros?() click to toggle source
# File lib/scryglass/ro.rb, line 233
def any_special_sub_ros?
  !!sub_ros.last&.special_sub_ro_type
end
ar_status_char() click to toggle source
# File lib/scryglass/ro.rb, line 321
def ar_status_char
  return cursor_char unless ar_sub_ros.empty?

  iv_check = Scryglass::Ro.safe_quick_check do
    # Currently, this will always indicate hidden secrets if the object, with
    #   the given Scryglass config, doesn't yield any ar_sub_ros upon trying '.'
    value.class.respond_to?(:reflections) # TODO: maybe dig more here?
  end

  return 'X' if iv_check.nil?

  return '·' if iv_check

  cursor_char
end
bucket_indicator() click to toggle source
# File lib/scryglass/ro.rb, line 237
def bucket_indicator
  return wrappers unless any_normal_sub_ros?

  if expanded
    wrappers[0]
  else
    # Number of dots indicating order of magnitude for Enumerable's count:
    #   Turning this off (the consistent three dots is more like an ellipsis,
    #   communicating with a solid preexisting symbol), but keeping the idea here:
    #     sub_ros_order_of_magnitude = normal_sub_ros.count.to_s.length
    #     wrappers.dup.insert(1, '•' * sub_ros_order_of_magnitude)
    dots = '•••'
    dots = "\e[36m#{dots}\e[00m" if Scryglass.config.dot_coloring # cyan then back to *default*
    wrappers.dup.insert(1, dots)
  end
end
cursor_char() click to toggle source
# File lib/scryglass/ro.rb, line 275
def cursor_char
  Scryglass::Session::CURSOR_CHARACTER
end
cursor_length() click to toggle source
# File lib/scryglass/ro.rb, line 267
def cursor_length
  tab_length = Scryglass.config.tab_length

  consistent_margin = [4 - tab_length, 0].max

  (tab_length * depth) + consistent_margin
end
cursor_string() click to toggle source
# File lib/scryglass/ro.rb, line 279
def cursor_string
  cursor = cursor_char * cursor_length

  cursor[0] = enum_status_char
  cursor[1] = iv_status_char
  cursor[2] = ar_status_char

  cursor
end
enum_status_char() click to toggle source
# File lib/scryglass/ro.rb, line 289
def enum_status_char
  enum_worth_checking = nugget? && value.is_a?(Enumerable)
  return cursor_char unless enum_worth_checking

  enum_check = Scryglass::Ro.safe_quick_check do
    # value.any? Can take an eternity for a few specific objects, breaking
    #   the session when the cursor passes over them. Also breaks on read-
    #   locked IO objects.
    enum_sub_ros.empty? && value.any?
  end

  return 'X' if enum_check.nil?

  return '(' if enum_check

  cursor_char
end
iv_status_char() click to toggle source
# File lib/scryglass/ro.rb, line 307
def iv_status_char
  return cursor_char unless iv_sub_ros.empty?

  iv_check = Scryglass::Ro.safe_quick_check do
    value.instance_variables.any?
  end

  return 'X' if iv_check.nil?

  return '@' if iv_check

  cursor_char
end
left_fill_string() click to toggle source
# File lib/scryglass/ro.rb, line 254
def left_fill_string
  left_fill = if has_cursor
                cursor_string
              else
                ' ' * cursor_length
              end

  if scry_session.special_command_targets.any? && scry_session.special_command_targets.map(&:index).include?(index)
    left_fill[-2..-1] = '->'
  end
  left_fill
end
normal_sub_ros() click to toggle source
# File lib/scryglass/ro.rb, line 225
def normal_sub_ros
  sub_ros.reject(&:special_sub_ro_type)
end