scryglass 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|