scryglass 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.tool-versions +1 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +33 -0
- data/LICENSE.txt +21 -0
- data/README.md +252 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/example_config.rb +30 -0
- data/lib/example_material.rb +97 -0
- data/lib/hexes.rb +135 -0
- data/lib/prog.rb +89 -0
- data/lib/refinements/ansiless_string_refinement.rb +11 -0
- data/lib/refinements/array_fit_to_refinement.rb +67 -0
- data/lib/refinements/clip_string_refinement.rb +27 -0
- data/lib/refinements/constant_defined_string_refinement.rb +11 -0
- data/lib/scryglass.rb +177 -0
- data/lib/scryglass/config.rb +103 -0
- data/lib/scryglass/lens_helper.rb +22 -0
- data/lib/scryglass/lens_panel.rb +140 -0
- data/lib/scryglass/ro.rb +237 -0
- data/lib/scryglass/ro_builder.rb +402 -0
- data/lib/scryglass/session.rb +514 -0
- data/lib/scryglass/tree_panel.rb +122 -0
- data/lib/scryglass/version.rb +3 -0
- data/lib/scryglass/view_panel.rb +91 -0
- data/lib/scryglass/view_wrapper.rb +23 -0
- data/scryglass.gemspec +46 -0
- metadata +117 -0
data/lib/hexes.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
## Hexes takes care of some console/view/IO work for Scryglass
|
4
|
+
module Hexes
|
5
|
+
using ClipStringRefinement
|
6
|
+
using AnsilessStringRefinement
|
7
|
+
using ConstantDefinedStringRefinement
|
8
|
+
|
9
|
+
def self.simple_screen_slice(screen_string)
|
10
|
+
screen_height, screen_width = $stdout.winsize
|
11
|
+
|
12
|
+
split_lines = screen_string.split("\n")
|
13
|
+
|
14
|
+
## Here we cut down the (rectangular if opacified) display array in both
|
15
|
+
## dimensions (into a smaller rectangle), as needed, to fit the view.
|
16
|
+
sliced_lines = split_lines.map do |string|
|
17
|
+
ansi_length = string.length - string.ansiless_length
|
18
|
+
slice_length = screen_width + ansi_length
|
19
|
+
string[0, slice_length]
|
20
|
+
end
|
21
|
+
sliced_list = sliced_lines[0, screen_height]
|
22
|
+
|
23
|
+
sliced_list.join("\n")
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.opacify_screen_string(screen_string)
|
27
|
+
screen_height, screen_width = $stdout.winsize
|
28
|
+
|
29
|
+
split_lines = screen_string.split("\n")
|
30
|
+
rows_filled = split_lines.count
|
31
|
+
|
32
|
+
blank_rows_at_bottom = [screen_height - rows_filled, 0].max
|
33
|
+
|
34
|
+
# This takes all the unfilled spaces left after a newline, and makes them
|
35
|
+
# real spaces, so they'll overwrite whatever was there a second ago. Thus
|
36
|
+
# I don't have to worry about clearing the screen all the time, which was
|
37
|
+
# seemingly causing console chaff and some flickering.
|
38
|
+
side_filled_string = split_lines.map do |line|
|
39
|
+
margin_to_fill = screen_width - line.ansiless.length
|
40
|
+
line + (' ' * margin_to_fill)
|
41
|
+
end.join("\e[00m\n") # Also turns off ANSI text formatting at the end of
|
42
|
+
# each line, in case a formatted string had its "turn off formatting" code
|
43
|
+
# cut off from the end. (Reducing the need to end with one at all).
|
44
|
+
|
45
|
+
blank_line = "\n" + (' ' * screen_width)
|
46
|
+
|
47
|
+
side_filled_string + (blank_line * blank_rows_at_bottom)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.stdout_rescue
|
51
|
+
@preserved_stdout_dup = $stdout.dup
|
52
|
+
|
53
|
+
begin
|
54
|
+
yielded_return = yield
|
55
|
+
rescue => e
|
56
|
+
# `e` is raised again in the `ensure` block after stdout is safely reset.
|
57
|
+
ensure
|
58
|
+
$stdout = @preserved_stdout_dup
|
59
|
+
raise e if e
|
60
|
+
end
|
61
|
+
|
62
|
+
yielded_return
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.capture_io(char_limit: nil)
|
66
|
+
stdout_rescue do # Ensures that $stdout is reset no matter what
|
67
|
+
temporary_io_channel = StringIO.new
|
68
|
+
$stdout = temporary_io_channel
|
69
|
+
Thread.abort_on_exception = true # So threads can return error text at all
|
70
|
+
|
71
|
+
if char_limit
|
72
|
+
background_output_thread = Thread.new { yield } # It's assumed that the
|
73
|
+
# yielded block will be printing something somewhat promptly.
|
74
|
+
|
75
|
+
sleep 0.05 # Give it a head start (Sometimes makes a difference!)
|
76
|
+
|
77
|
+
while temporary_io_channel.size < char_limit
|
78
|
+
io_size = temporary_io_channel.size
|
79
|
+
sleep 0.05
|
80
|
+
new_io_size = temporary_io_channel.size
|
81
|
+
break if new_io_size == io_size
|
82
|
+
end
|
83
|
+
background_output_thread.terminate
|
84
|
+
else
|
85
|
+
yield
|
86
|
+
end
|
87
|
+
|
88
|
+
temporary_io_channel.rewind
|
89
|
+
captured_output = temporary_io_channel.read
|
90
|
+
captured_output = captured_output.clip_at(char_limit) if char_limit
|
91
|
+
|
92
|
+
captured_output
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.overwrite_screen(screen_string)
|
97
|
+
csi = "\e["
|
98
|
+
$stdout.write "#{csi}s" # Saves terminal cursor position
|
99
|
+
$stdout.write "#{csi}1;1H" # Moves terminal cursor to top left corner
|
100
|
+
|
101
|
+
$stdout.print "\r#{screen_string}"
|
102
|
+
$stdout.write "#{csi}u" # Restores saved terminal cursor position
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.hide_db_outputs
|
106
|
+
necessary_constants = ['Logger', 'ActiveRecord::Base']
|
107
|
+
necessary_constants_defined = necessary_constants.all?(&:constant_defined?)
|
108
|
+
return yield unless necessary_constants_defined
|
109
|
+
|
110
|
+
rails_logger_defined = 'Rails'.constant_defined? && Rails.try(:logger).present?
|
111
|
+
|
112
|
+
## These are purposefully preserved as global variables so retrieval, in
|
113
|
+
## debugging or errored usage, is as easy as possible.
|
114
|
+
$preserved_ar_base_logger = ActiveRecord::Base.logger.dup
|
115
|
+
$preserved_rails_logger = Rails.logger.dup if rails_logger_defined
|
116
|
+
|
117
|
+
begin
|
118
|
+
## Now we create an unused dump string to serve as the output
|
119
|
+
ignored_output = StringIO.new
|
120
|
+
ignored_log = Logger.new(ignored_output)
|
121
|
+
ActiveRecord::Base.logger = ignored_log
|
122
|
+
Rails.logger = ignored_log if rails_logger_defined
|
123
|
+
|
124
|
+
yielded_return = yield
|
125
|
+
rescue => e
|
126
|
+
# `e` is raised again in the `ensure` after displays are safely reset.
|
127
|
+
ensure
|
128
|
+
ActiveRecord::Base.logger = $preserved_ar_base_logger
|
129
|
+
Rails.logger = $preserved_rails_logger if rails_logger_defined
|
130
|
+
raise e if e
|
131
|
+
end
|
132
|
+
|
133
|
+
yielded_return
|
134
|
+
end
|
135
|
+
end
|
data/lib/prog.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
## Prog is a simple progress bar for tracking one or more nested or simultaneous
|
4
|
+
## processes. Tasks fit evenly and dynamically into a Pipe, which can then
|
5
|
+
## be displayed at a chosen width.
|
6
|
+
module Prog
|
7
|
+
class Pipe
|
8
|
+
attr_accessor :tasks
|
9
|
+
attr_accessor :highest_count
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
self.tasks = []
|
13
|
+
self.highest_count = 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s(length: $stdout.winsize[1])
|
17
|
+
return ' ' * length if tasks.count.zero?
|
18
|
+
|
19
|
+
unused_length = length
|
20
|
+
self.highest_count = [highest_count, tasks.count].max
|
21
|
+
|
22
|
+
## Set up the first barrier
|
23
|
+
display_string = +'|'
|
24
|
+
unused_length -= 1
|
25
|
+
|
26
|
+
## Get a first pass at equal task length
|
27
|
+
# first_pass_task_length = unused_length/working_tasks.count
|
28
|
+
first_pass_task_length = unused_length / highest_count
|
29
|
+
if first_pass_task_length < 2
|
30
|
+
raise "Prog::Pipe length (#{length}) too small to " \
|
31
|
+
"fit all tasks (#{tasks.count})"
|
32
|
+
end
|
33
|
+
tasks.each do |task|
|
34
|
+
task.working_length = first_pass_task_length
|
35
|
+
end
|
36
|
+
|
37
|
+
## Distribute the remaining space evenly among the first n tasks
|
38
|
+
remaining_space = unused_length - (first_pass_task_length * highest_count)
|
39
|
+
tasks[0...remaining_space].each { |task| task.working_length += 1 }
|
40
|
+
|
41
|
+
tasks.each do |task|
|
42
|
+
display_string << task.to_s
|
43
|
+
end
|
44
|
+
display_string.ljust(length, ' ')
|
45
|
+
end
|
46
|
+
|
47
|
+
def <<(task)
|
48
|
+
tasks << task
|
49
|
+
task.pipe = self
|
50
|
+
task.force_finish if task.max_count.zero?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Task
|
55
|
+
attr_accessor :pipe, :max_count, :current_count
|
56
|
+
attr_accessor :working_length
|
57
|
+
|
58
|
+
def initialize(max_count:)
|
59
|
+
self.max_count = max_count
|
60
|
+
self.current_count = 0
|
61
|
+
self.working_length = nil # (Only set by Prog::Pipe)
|
62
|
+
end
|
63
|
+
|
64
|
+
def tick(number_of_ticks = 1)
|
65
|
+
self.current_count += number_of_ticks
|
66
|
+
|
67
|
+
force_finish if current_count >= max_count
|
68
|
+
end
|
69
|
+
|
70
|
+
def force_finish
|
71
|
+
pipe.tasks.delete(self)
|
72
|
+
pipe.highest_count = 0 if pipe.tasks.empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_s
|
76
|
+
progress_ratio = current_count / max_count.to_f
|
77
|
+
|
78
|
+
unused_length = working_length
|
79
|
+
display_string = +'|' # (not frozen)
|
80
|
+
unused_length -= 1
|
81
|
+
|
82
|
+
filled_cells = (unused_length * progress_ratio).floor
|
83
|
+
fill_bar = ('=' * filled_cells).ljust(unused_length, ' ')
|
84
|
+
display_string.prepend(fill_bar)
|
85
|
+
|
86
|
+
display_string
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module ArrayFitToRefinement
|
2
|
+
refine Array do
|
3
|
+
using ClipStringRefinement
|
4
|
+
using AnsilessStringRefinement
|
5
|
+
# Warning: Still not going to work nicely if a string ends in an ansi code!
|
6
|
+
def fit_to(string_length_goal, fill: ' ', ignore_ansi_codes: true)
|
7
|
+
string_array = self.map(&:to_s) # This also acts to dup
|
8
|
+
length_method = ignore_ansi_codes ? :ansiless_length : :length
|
9
|
+
length_result = string_array.join('').send(length_method)
|
10
|
+
|
11
|
+
|
12
|
+
if length_result > string_length_goal
|
13
|
+
string_array.compress_to(string_length_goal, ignore_ansi_codes: ignore_ansi_codes)
|
14
|
+
elsif length_result < string_length_goal
|
15
|
+
string_array.expand_to(string_length_goal, ignore_ansi_codes: ignore_ansi_codes, fill: fill)
|
16
|
+
else # If it joins to the right length already, we still want to return the expected number of strings.
|
17
|
+
spacers = [''] * (string_array.count - 1)
|
18
|
+
string_array.zip(spacers).flatten.compact
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Warning: Still not going to work nicely if a string ends in an ansi code!
|
23
|
+
def compress_to(string_length_goal, ignore_ansi_codes:)
|
24
|
+
working_array = self.map(&:to_s)
|
25
|
+
original_string_count = self.count
|
26
|
+
spacers = [''] * (original_string_count - 1)
|
27
|
+
length_method = ignore_ansi_codes ? :ansiless_length : :length
|
28
|
+
|
29
|
+
## Ensure the strings are short enough to fit:
|
30
|
+
slider = 0
|
31
|
+
while working_array.join('').send(length_method) > string_length_goal
|
32
|
+
longest_string_length = working_array.map { |s| s.send(length_method) }.max
|
33
|
+
slider_index = slider % working_array.count
|
34
|
+
if working_array[slider_index].send(length_method) >= longest_string_length
|
35
|
+
working_array[slider_index] =
|
36
|
+
working_array[slider_index].clip_at(working_array[slider_index].send(length_method) - 1,
|
37
|
+
ignore_ansi_codes: ignore_ansi_codes)
|
38
|
+
end
|
39
|
+
slider += 1
|
40
|
+
end
|
41
|
+
|
42
|
+
working_array.zip(spacers).flatten.compact
|
43
|
+
end
|
44
|
+
|
45
|
+
def expand_to(string_length_goal, ignore_ansi_codes:, fill:)
|
46
|
+
original_string_count = self.count
|
47
|
+
working_array = self.map(&:to_s)
|
48
|
+
spacers = [''] * (original_string_count - 1)
|
49
|
+
length_method = ignore_ansi_codes ? :ansiless_length : :length
|
50
|
+
|
51
|
+
## Ensure the spacers are large enough to fill out to string_length_goal
|
52
|
+
space_to_fill = string_length_goal - working_array.join('').send(length_method)
|
53
|
+
first_pass_spacer_length = space_to_fill / spacers.count
|
54
|
+
spacers.map! { fill * first_pass_spacer_length }
|
55
|
+
|
56
|
+
## Distribute the remaining space evenly among the last n spacers
|
57
|
+
remaining_space = space_to_fill - spacers.join('').send(length_method)
|
58
|
+
if remaining_space.positive?
|
59
|
+
spacers =
|
60
|
+
spacers[0...-remaining_space] +
|
61
|
+
spacers[-remaining_space..-1].map! { |spacer| spacer + ' ' } #each { |task| task.working_length += 1 }
|
62
|
+
end
|
63
|
+
|
64
|
+
working_array.zip(spacers).flatten.compact
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ClipStringRefinement
|
2
|
+
refine String do
|
3
|
+
using AnsilessStringRefinement
|
4
|
+
|
5
|
+
# Warning: Still not going to work nicely if a string ends in an ansi code!
|
6
|
+
def clip_at(clip_length, ignore_ansi_codes: false)
|
7
|
+
length_method = ignore_ansi_codes ? :ansiless_length : :length
|
8
|
+
original_length = send(length_method)
|
9
|
+
ansi_length = ignore_ansi_codes ? length - ansiless_length : 0
|
10
|
+
slice_length = clip_length + ansi_length
|
11
|
+
clipped_string = self[0...slice_length]
|
12
|
+
if clipped_string.send(length_method) < original_length
|
13
|
+
clipped_string = clipped_string.mark_as_abbreviated
|
14
|
+
end
|
15
|
+
|
16
|
+
clipped_string
|
17
|
+
end
|
18
|
+
|
19
|
+
# Warning: Still not going to work nicely if a string ends in an ansi code!
|
20
|
+
def mark_as_abbreviated
|
21
|
+
self_dup = dup
|
22
|
+
self_dup[-1] = '…' if self_dup[-1]
|
23
|
+
self_dup[-2] = '…' if self_dup[-2]
|
24
|
+
self_dup
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/scryglass.rb
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
|
4
|
+
## Bookkeeping and external tools:
|
5
|
+
require "scryglass/version"
|
6
|
+
require 'active_support/core_ext/object/blank' # This gives us `.present?` and `.blank?` # https://stackoverflow.com/questions/4648684/how-to-use-present-in-ruby-projects
|
7
|
+
require 'io/console'
|
8
|
+
require 'pp'
|
9
|
+
|
10
|
+
## Refinements and sub-tools:
|
11
|
+
require 'refinements/ansiless_string_refinement'
|
12
|
+
require 'refinements/clip_string_refinement'
|
13
|
+
require 'refinements/constant_defined_string_refinement'
|
14
|
+
require 'refinements/array_fit_to_refinement'
|
15
|
+
require 'hexes'
|
16
|
+
require 'prog'
|
17
|
+
|
18
|
+
## Core gem components:
|
19
|
+
require 'scryglass/config'
|
20
|
+
require 'scryglass/ro'
|
21
|
+
require 'scryglass/ro_builder'
|
22
|
+
require 'scryglass/session'
|
23
|
+
require 'scryglass/view_wrapper'
|
24
|
+
require 'scryglass/view_panel'
|
25
|
+
require 'scryglass/tree_panel'
|
26
|
+
require 'scryglass/lens_panel'
|
27
|
+
|
28
|
+
## Testing and Demoing:
|
29
|
+
require 'example_material.rb'
|
30
|
+
|
31
|
+
module Scryglass
|
32
|
+
HELP_SCREEN = <<~'HELPSCREENPAGE'
|
33
|
+
q : Quit Scry ? : Cycle help panels (1/2)
|
34
|
+
|
35
|
+
BASIC NAVIGATION: · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
|
36
|
+
· ·
|
37
|
+
· UP / DOWN : Navigate (You can type a number first) ·
|
38
|
+
· RIGHT : Expand current or selected row(s) ·
|
39
|
+
· LEFT : Collapse current or selected row(s) ·
|
40
|
+
· ·
|
41
|
+
· ENTER : Close Scry, returning current or selected object(s) (Key or Value) ·
|
42
|
+
· ·
|
43
|
+
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
|
44
|
+
|
45
|
+
INSPECTING WITH LENS VIEW: · · · · · · · · · · · · · ·
|
46
|
+
· ·
|
47
|
+
· SPACEBAR : Toggle Lens View ·
|
48
|
+
· l : Cycle through lens types ·
|
49
|
+
· L : Toggle subject (Key/Value of row) ·
|
50
|
+
· ·
|
51
|
+
· · · · · · · · · · · · · · · · · · · · · · · · · · ·
|
52
|
+
|
53
|
+
MORE NAVIGATION: · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
|
54
|
+
· ·
|
55
|
+
· [w] : Move view window 0 : Reset view location ·
|
56
|
+
· [a][s][d] (ALT increases speed) (Press again: reset cursor) ·
|
57
|
+
· ·
|
58
|
+
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
|
59
|
+
HELPSCREENPAGE
|
60
|
+
|
61
|
+
HELP_SCREEN_ADVANCED = <<~'HELPSCREENADVANCEDPAGE'
|
62
|
+
q : Quit Scry ? : Cycle help panels (2/2)
|
63
|
+
|
64
|
+
ADVANCED: · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
|
65
|
+
· DIGGING DEEPER: ·
|
66
|
+
· For current or selected row(s)... ·
|
67
|
+
· @ : Build instance variable sub-rows ·
|
68
|
+
· . : Build ActiveRecord association sub-rows ·
|
69
|
+
· ( : Attempt to smart-build sub-rows, if Enumerable. Usually '@' is preferable. ·
|
70
|
+
· ·
|
71
|
+
· SELECTING ROWS: ·
|
72
|
+
· * : Select/Deselect ALL rows ·
|
73
|
+
· | : Select/Deselect every sibling row under the same parent row ·
|
74
|
+
· - : Select/Deselect current row ·
|
75
|
+
· ·
|
76
|
+
· TEXT SEARCH: ·
|
77
|
+
· / : Begin a text search (in tree view) ·
|
78
|
+
· n : Move to next search result ·
|
79
|
+
· ·
|
80
|
+
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
|
81
|
+
HELPSCREENADVANCEDPAGE
|
82
|
+
|
83
|
+
def self.config
|
84
|
+
@config ||= Config.new
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.reset_config
|
88
|
+
@config = Config.new
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.configure
|
92
|
+
yield(config)
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.load_silently
|
96
|
+
begin
|
97
|
+
add_kernel_methods
|
98
|
+
{ success: true, error: nil }
|
99
|
+
rescue => e
|
100
|
+
{ success: false, error: e }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.load
|
105
|
+
caller_path = caller_locations.first.path
|
106
|
+
|
107
|
+
silent_load_result = Scryglass.load_silently
|
108
|
+
|
109
|
+
if silent_load_result[:success]
|
110
|
+
puts "(Scryglass is loaded, from `#{caller_path}`. Use `Scryglass.help` for help getting started)"
|
111
|
+
else
|
112
|
+
puts "(Scryglass failed to load, from `#{caller_path}` " \
|
113
|
+
"getting `#{silent_load_result[:error].message}`)"
|
114
|
+
end
|
115
|
+
|
116
|
+
silent_load_result
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.help
|
120
|
+
console_help = <<~"CONSOLE_HELP" # Bolded with \e[1m
|
121
|
+
\e[1m
|
122
|
+
| To prep Scryglass, call `Scryglass.load`
|
123
|
+
| (Or add it to .irbrc & .pryrc)
|
124
|
+
|
|
125
|
+
| To start a Scry Session, call:
|
126
|
+
| > scry my_object OR
|
127
|
+
| > my_object.scry
|
128
|
+
|
|
129
|
+
| To resume the previous session: (in same console session)
|
130
|
+
| > scry OR
|
131
|
+
| > scry_resume (if you're in a breakpoint pry)
|
132
|
+
\e[0m
|
133
|
+
CONSOLE_HELP
|
134
|
+
|
135
|
+
puts console_help
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def self.add_kernel_methods
|
141
|
+
Kernel.module_eval do
|
142
|
+
def scry(arg = nil, actions = nil) # `actions` can't be a keyword arg due
|
143
|
+
# to this ruby issue: https://bugs.ruby-lang.org/issues/8316
|
144
|
+
|
145
|
+
receiver = self unless to_s == 'main'
|
146
|
+
# As in: `receiver.scry`,
|
147
|
+
# and no receiver means scry was called on 'main', (unless self is
|
148
|
+
# different in the because you've pry'd into something!)
|
149
|
+
|
150
|
+
seed_object = arg || receiver
|
151
|
+
|
152
|
+
$scry_session = Scryglass::Session.new(seed_object) if seed_object
|
153
|
+
# If it's been given an arg or receiver, create new session!
|
154
|
+
# The global variable is purposeful, and not accessible outside of
|
155
|
+
# the one particular console instance.
|
156
|
+
|
157
|
+
scry_resume(actions) # Pick up the new or previous session
|
158
|
+
end
|
159
|
+
|
160
|
+
# For the user, this is mainly just for pry sessions where `self` isn't `main`
|
161
|
+
def scry_resume(actions = nil)
|
162
|
+
Scryglass.config.validate!
|
163
|
+
|
164
|
+
no_previous_session = $scry_session.nil?
|
165
|
+
if no_previous_session
|
166
|
+
raise ArgumentError,
|
167
|
+
'`scry` requires either an argument, a receiver, or a past' \
|
168
|
+
'session to reopen. try `Scryglass.help`'
|
169
|
+
end
|
170
|
+
|
171
|
+
Hexes.stdout_rescue do
|
172
|
+
$scry_session.run_scry_ui(actions: actions)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|