Class: RubyJard::Session

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/ruby_jard/session.rb

Overview

Centralized flow control and data storage to feed into screens. Each process supposes to have only one instance of this class. TODO: This class is created to store data, but byebug data structures are leaked, and accessible from outside and this doesn’t work if screens stay in other processes. Therefore, an internal, jard-specific data mapping should be built.

Constant Summary collapse

OUTPUT_BUFFER_LENGTH =

10k lines

10_000

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Session

Returns a new instance of Session.



46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/ruby_jard/session.rb', line 46

def initialize(options = {})
  @options = options
  @started = false
  @session_lock = Mutex.new
  @output_buffer = []

  @current_frame = nil
  @current_backtrace = []
  @threads = []
  @current_thread = nil

  @path_filter = RubyJard::PathFilter.new
end

Instance Attribute Details

#output_bufferObject

Returns the value of attribute output_buffer.



44
45
46
# File 'lib/ruby_jard/session.rb', line 44

def output_buffer
  @output_buffer
end

Class Method Details

.attachObject



26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/ruby_jard/session.rb', line 26

def attach
  unless RubyJard::Console.attachable?
    $stdout.puts 'Failed to attach. Jard could not detect a valid tty device.'
    $stdout.puts 'This bug occurs when the process Jard trying to access is a non-interactive environment '\
      ' such as docker, daemon, sub-processes, etc.'
    $stdout.puts 'If you are confused, please submit an issue in https://github.com/nguyenquangminh0711/ruby_jard/issues.'
    return
  end

  instance.start unless instance.started?

  Byebug.attach
  Byebug.current_context.step_out(3, true)
end

.instanceObject



22
23
24
# File 'lib/ruby_jard/session.rb', line 22

def instance
  @instance ||= new
end

Instance Method Details

#append_output_buffer(string) ⇒ Object



98
99
100
101
# File 'lib/ruby_jard/session.rb', line 98

def append_output_buffer(string)
  @output_buffer.shift if @output_buffer.length > OUTPUT_BUFFER_LENGTH
  @output_buffer << string
end

#current_backtraceObject



139
140
141
# File 'lib/ruby_jard/session.rb', line 139

def current_backtrace
  @current_backtrace ||= generate_backtrace
end

#current_frameObject



126
127
128
129
130
131
132
133
# File 'lib/ruby_jard/session.rb', line 126

def current_frame
  @current_frame ||=
    begin
      frame = RubyJard::Frame.new(@current_context, @current_context.frame.pos)
      frame.visible = @path_filter.match?(frame.frame_file)
      frame
    end
end

#current_threadObject



135
136
137
# File 'lib/ruby_jard/session.rb', line 135

def current_thread
  @current_thread ||= RubyJard::ThreadInfo.new(@current_context.thread)
end

#frame=(real_pos) ⇒ Object



152
153
154
155
# File 'lib/ruby_jard/session.rb', line 152

def frame=(real_pos)
  @current_context.frame = @current_backtrace[real_pos].real_pos
  @current_frame = @current_backtrace[real_pos]
end

#lockObject

Raises:



165
166
167
168
169
170
171
172
173
# File 'lib/ruby_jard/session.rb', line 165

def lock
  raise RubyJard::Error, 'This method requires a block' unless block_given?

  # TODO: This doesn't solve anything. However, debugging a multi-threaded process is hard.
  # Let's deal with that later.
  @session_lock.synchronize do
    yield
  end
end

#should_stop?Boolean

Returns:

  • (Boolean)


113
114
115
# File 'lib/ruby_jard/session.rb', line 113

def should_stop?
  @path_filter.match?(@current_context.frame_file)
end

#startObject



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/ruby_jard/session.rb', line 60

def start
  return if started?

  ##
  # Globally configure Byebug. Byebug doesn't allow configuration by instance.
  # So, I have no choice.
  # TODO: Byebug autoloaded configuration may override those values.
  Byebug::Setting[:autolist] = false
  Byebug::Setting[:autoirb] = false
  Byebug::Setting[:autopry] = false

  require 'ruby_jard/repl_processor'
  Byebug::Context.processor = RubyJard::ReplProcessor
  # Exclude all files in Ruby Jard source code from the stacktrace.
  Byebug::Context.ignored_files = Byebug::Context.all_files + RubyJard.all_files

  $stdout.send(:instance_eval, <<-CODE)
    def write(*string, from_jard: false)
      # NOTE: `RubyJard::ScreenManager.instance` is a must. Jard doesn't work well with delegator
      # TODO: Debug and fix the issues permanently
      if from_jard
        super(*string)
        return
      end

      unless RubyJard::ScreenManager.instance.updating?
        RubyJard::Session.instance.append_output_buffer(string)
      end

      super(*string)
    end
  CODE

  at_exit { stop }

  @started = true
end

#started?Boolean

Returns:

  • (Boolean)


109
110
111
# File 'lib/ruby_jard/session.rb', line 109

def started?
  @started == true
end

#step_into(times) ⇒ Object



157
158
159
# File 'lib/ruby_jard/session.rb', line 157

def step_into(times)
  @current_context.step_into(times, current_frame.real_pos)
end

#step_over(times) ⇒ Object



161
162
163
# File 'lib/ruby_jard/session.rb', line 161

def step_over(times)
  @current_context.step_over(times, current_frame.real_pos)
end

#stopObject



103
104
105
106
107
# File 'lib/ruby_jard/session.rb', line 103

def stop
  return unless started?

  RubyJard::ScreenManager.stop
end

#sync(context) ⇒ Object



117
118
119
120
121
122
123
124
# File 'lib/ruby_jard/session.rb', line 117

def sync(context)
  @current_context = context
  # Remove cache
  @current_frame = nil
  @current_thread = nil
  @current_backtrace = nil
  @threads = nil
end

#threadsObject



143
144
145
146
147
148
149
150
# File 'lib/ruby_jard/session.rb', line 143

def threads
  @threads ||=
    Thread
    .list
    .select(&:alive?)
    .reject { |t| t.name.to_s =~ /<<Jard:.*>>/ }
    .map { |t| RubyJard::ThreadInfo.new(t) }
end