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
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Scryglass
|
4
|
+
class Config
|
5
|
+
using ClipStringRefinement
|
6
|
+
|
7
|
+
attr_accessor :tab_length
|
8
|
+
attr_accessor :include_empty_associations,
|
9
|
+
:include_through_associations, :include_scoped_associations,
|
10
|
+
:show_association_types
|
11
|
+
attr_accessor :cursor_tracking
|
12
|
+
attr_accessor :lenses
|
13
|
+
attr_accessor :tree_view_key_string_clip_length,
|
14
|
+
:tree_view_value_string_clip_length
|
15
|
+
attr_accessor :dot_coloring
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
## Display
|
19
|
+
self.tab_length = 2 # You can make it 0, but please don't make it 0.
|
20
|
+
self.tree_view_key_string_clip_length = 200
|
21
|
+
self.tree_view_value_string_clip_length = 500
|
22
|
+
self.dot_coloring = true
|
23
|
+
|
24
|
+
## UX
|
25
|
+
self.cursor_tracking = [:flexible_range, :dead_center][0] # One or the other
|
26
|
+
self.lenses = [ # Custom lenses can easily be added as name+lambda hashes! Or comment some out to turn them off.
|
27
|
+
{ name: 'Pretty Print (`pp`)',
|
28
|
+
lambda: ->(o) { Hexes.capture_io(char_limit: 20_000) { pp o } } },
|
29
|
+
{ name: 'Inspect (`.inspect`)',
|
30
|
+
lambda: ->(o) { Hexes.capture_io(char_limit: 20_000) { puts o.inspect } } },
|
31
|
+
{ name: 'Yaml Print (`y`)',
|
32
|
+
lambda: ->(o) { Hexes.capture_io(char_limit: 20_000) { require 'yaml' ; y o } } }, # OR: `puts o.to_yaml`
|
33
|
+
{ name: 'Puts (`puts`)',
|
34
|
+
lambda: ->(o) { Hexes.capture_io(char_limit: 20_000) { puts o } } },
|
35
|
+
# { name: 'Method Showcase',
|
36
|
+
# lambda: ->(o) { Scryglass::LensHelper.method_showcase_for(o) } },
|
37
|
+
]
|
38
|
+
|
39
|
+
## Building ActiveRecord association sub-rows:
|
40
|
+
self.include_empty_associations = true
|
41
|
+
self.include_through_associations = false
|
42
|
+
self.include_scoped_associations = false
|
43
|
+
self.show_association_types = true
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate!
|
47
|
+
validate_boolean_attrs!
|
48
|
+
validate_positive_integer_attrs!
|
49
|
+
validate_cursor_tracking_options!
|
50
|
+
validate_lenses!
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def validate_boolean_attrs!
|
56
|
+
bool_attrs = [
|
57
|
+
:include_empty_associations,
|
58
|
+
:include_through_associations,
|
59
|
+
:include_scoped_associations,
|
60
|
+
:dot_coloring,
|
61
|
+
]
|
62
|
+
bool_attrs.each do |bool_attr|
|
63
|
+
value = send(bool_attr)
|
64
|
+
unless [true, false].include?(value)
|
65
|
+
raise ArgumentError, "#{bool_attr} must be true or false."
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def validate_positive_integer_attrs!
|
71
|
+
positive_integer_attrs = [
|
72
|
+
:tab_length,
|
73
|
+
:tree_view_key_string_clip_length,
|
74
|
+
:tree_view_value_string_clip_length,
|
75
|
+
]
|
76
|
+
positive_integer_attrs.each do |int_attr|
|
77
|
+
value = send(int_attr)
|
78
|
+
unless value.integer? && value.positive?
|
79
|
+
raise ArgumentError, "#{value} is not a positive integer."
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def validate_cursor_tracking_options!
|
85
|
+
cursor_tracking_options = [:flexible_range, :dead_center]
|
86
|
+
unless cursor_tracking_options.include?(cursor_tracking)
|
87
|
+
raise ArgumentError, "#{cursor_tracking.inspect} not in " \
|
88
|
+
"[#{cursor_tracking_options.map(&:inspect).join(', ')}]."
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def validate_lenses!
|
93
|
+
raise ArgumentError, 'lenses cannot be empty' unless lenses.any?
|
94
|
+
|
95
|
+
lenses.each do |lens|
|
96
|
+
unless lens.is_a?(Hash) && lens[:name].is_a?(String) && lens[:lambda].lambda?
|
97
|
+
raise ArgumentError, "Lens #{lens.inspect} must be a hash of the form:" \
|
98
|
+
'{ name: String, lambda: lambda }'
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Scryglass
|
4
|
+
module LensHelper
|
5
|
+
def method_showcase_for(object)
|
6
|
+
method_list = object.methods - Object.methods
|
7
|
+
label_space = [method_list.map(&:length).max, 45].min
|
8
|
+
method_list.sort.map do |method_name|
|
9
|
+
label = method_name.to_s.ljust(label_space, ' ')
|
10
|
+
begin
|
11
|
+
method = object.method(method_name)
|
12
|
+
label + ' : ' +
|
13
|
+
method.source_location.to_a.join(':') + "\n" +
|
14
|
+
Hexes.capture_io { puts method.source }
|
15
|
+
rescue => e
|
16
|
+
label + ' : Error: ' +
|
17
|
+
e.message + "\n"
|
18
|
+
end
|
19
|
+
end.join("\n")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Scryglass
|
4
|
+
class LensPanel < Scryglass::ViewPanel
|
5
|
+
using ClipStringRefinement
|
6
|
+
using AnsilessStringRefinement
|
7
|
+
using ArrayFitToRefinement
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def self.lenses
|
12
|
+
Scryglass.config.lenses
|
13
|
+
end
|
14
|
+
|
15
|
+
def uncut_body_string
|
16
|
+
current_lens = scry_session.current_lens
|
17
|
+
current_subject_type = scry_session.current_subject_type
|
18
|
+
current_subject = scry_session.current_ro.current_subject
|
19
|
+
ro_has_no_key = !scry_session.current_ro.key_value_pair?
|
20
|
+
|
21
|
+
return '' if ro_has_no_key && current_subject_type == :key
|
22
|
+
|
23
|
+
lens_id = current_lens % LensPanel.lenses.count
|
24
|
+
lens = LensPanel.lenses[lens_id]
|
25
|
+
|
26
|
+
scry_session.current_ro.lens_strings[current_subject_type][lens_id] ||=
|
27
|
+
begin
|
28
|
+
lens[:lambda].call(current_subject)
|
29
|
+
rescue => e
|
30
|
+
[e.message, *e.backtrace].join("\n")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def uncut_header_string
|
35
|
+
_screen_height, screen_width = $stdout.winsize
|
36
|
+
dotted_line = '·' * screen_width
|
37
|
+
|
38
|
+
[
|
39
|
+
current_ro_subheader,
|
40
|
+
dotted_line,
|
41
|
+
lens_param_subheader,
|
42
|
+
dotted_line
|
43
|
+
].join("\n")
|
44
|
+
end
|
45
|
+
|
46
|
+
def visible_body_slice(uncut_body_string)
|
47
|
+
screen_height, screen_width = $stdout.winsize
|
48
|
+
non_header_view_size = screen_height - visible_header_string.split("\n").count
|
49
|
+
|
50
|
+
split_lines = uncut_body_string.split("\n")
|
51
|
+
|
52
|
+
## Here we cut down the (rectangular) display array in both dimensions (into a smaller rectangle), as needed, to fit the view.
|
53
|
+
sliced_lines = split_lines.map do |string|
|
54
|
+
ansi_length = string.length - string.ansiless_length # Escape codes make `length` different from display length!
|
55
|
+
slice_length = screen_width + ansi_length
|
56
|
+
string[current_view_coords[:x], slice_length] || '' # If I don't want to
|
57
|
+
# opacify here, I need to account for nils when the view is fully
|
58
|
+
# beyond the shorter lines.
|
59
|
+
end
|
60
|
+
sliced_list = sliced_lines[current_view_coords[:y], non_header_view_size]
|
61
|
+
|
62
|
+
sliced_list.join("\n")
|
63
|
+
end
|
64
|
+
|
65
|
+
def recalculate_y_boundaries
|
66
|
+
self.y_boundaries = 0...(uncut_body_string.count("\n") + 1)
|
67
|
+
end
|
68
|
+
|
69
|
+
def recalculate_x_boundaries
|
70
|
+
_screen_height, screen_width = $stdout.winsize
|
71
|
+
|
72
|
+
split_lines = uncut_body_string.split("\n")
|
73
|
+
length_of_longest_line = split_lines.map(&:length).max || 0
|
74
|
+
max_line_length = [length_of_longest_line, screen_width].max
|
75
|
+
|
76
|
+
self.x_boundaries = 0...max_line_length
|
77
|
+
end
|
78
|
+
|
79
|
+
def current_ro_subheader
|
80
|
+
current_ro = scry_session.current_ro
|
81
|
+
user_input = scry_session.user_input
|
82
|
+
|
83
|
+
row_above_string =
|
84
|
+
current_ro.next_visible_ro_up.to_s if current_ro.next_visible_ro_up
|
85
|
+
row_below_string =
|
86
|
+
current_ro.next_visible_ro_down.to_s if current_ro.next_visible_ro_down
|
87
|
+
|
88
|
+
tree_preview_related_commands = ['A', 'B', 'C', 'D',
|
89
|
+
'@', '.', '(', '*', '|', '-']
|
90
|
+
ro_view_label =
|
91
|
+
if tree_preview_related_commands.include?(user_input)
|
92
|
+
"\e[7mVIEWING:\e[00m" # Color reversed
|
93
|
+
else
|
94
|
+
'VIEWING:'
|
95
|
+
end
|
96
|
+
|
97
|
+
current_ro_window =
|
98
|
+
" #{row_above_string}\n" \
|
99
|
+
"#{ro_view_label} #{current_ro}\n" \
|
100
|
+
" #{row_below_string}"
|
101
|
+
|
102
|
+
current_ro_window
|
103
|
+
end
|
104
|
+
|
105
|
+
def lens_param_subheader
|
106
|
+
_screen_height, screen_width = $stdout.winsize
|
107
|
+
|
108
|
+
current_lens = scry_session.current_lens
|
109
|
+
current_subject_type = scry_session.current_subject_type
|
110
|
+
current_subject = scry_session.current_ro.current_subject
|
111
|
+
user_input = scry_session.user_input
|
112
|
+
|
113
|
+
lens_count = LensPanel.lenses.count
|
114
|
+
lens_id = current_lens % lens_count
|
115
|
+
lens = LensPanel.lenses[lens_id]
|
116
|
+
|
117
|
+
longest_lens_name_length = LensPanel.lenses.map do |lens|
|
118
|
+
lens[:name].length
|
119
|
+
end.max
|
120
|
+
lens_type_header_length = 9 + (lens_count.to_s.length * 2)
|
121
|
+
+ longest_lens_name_length
|
122
|
+
subject_type_header = "SUBJECT: #{current_subject_type}".ljust(14, ' ')
|
123
|
+
subject_class_header = " CLASS: #{current_subject.class}"
|
124
|
+
lens_type_header = " LENS #{lens_id + 1}/#{lens_count}: #{lens[:name]}"
|
125
|
+
.ljust(lens_type_header_length, ' ')
|
126
|
+
|
127
|
+
fit_lens_header = [
|
128
|
+
subject_type_header, subject_class_header, lens_type_header
|
129
|
+
].fit_to(screen_width)
|
130
|
+
|
131
|
+
if user_input == 'l'
|
132
|
+
fit_lens_header[4] = "\e[7m#{fit_lens_header[4]}" # Format to be ended by Hexes.opacify_screen_string() (using \e[00m)
|
133
|
+
elsif user_input == 'L'
|
134
|
+
fit_lens_header[0] = "\e[7m#{fit_lens_header[0]}\e[00m"
|
135
|
+
end
|
136
|
+
|
137
|
+
fit_lens_header.join('')
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
data/lib/scryglass/ro.rb
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Scryglass
|
4
|
+
# A ro is essentially a complex wrapper for an object that deals with how it is
|
5
|
+
# nested and displayed relative to other ros/objects.
|
6
|
+
class Ro
|
7
|
+
using ClipStringRefinement
|
8
|
+
|
9
|
+
attr_accessor :key, :value, :val_type,
|
10
|
+
:key_string, :value_string, :lens_strings,
|
11
|
+
:key_value_relationship_indicator, :special_sub_ro_type,
|
12
|
+
:wrappers
|
13
|
+
|
14
|
+
attr_accessor :has_cursor, :expanded,
|
15
|
+
:parent_ro, :sub_ros,
|
16
|
+
:depth, :index, :scry_session
|
17
|
+
|
18
|
+
WRAPPER_TYPES = {
|
19
|
+
'Hash' => '{}',
|
20
|
+
'Array' => '[]',
|
21
|
+
'ActiveRecord_Relation' => '<>',
|
22
|
+
'ActiveRecord_Associations_CollectionProxy' => '‹›',
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
def initialize(scry_session:,
|
26
|
+
val:,
|
27
|
+
val_type:,
|
28
|
+
parent_ro:,
|
29
|
+
key:,
|
30
|
+
depth:,
|
31
|
+
key_value_relationship_indicator: false,
|
32
|
+
special_sub_ro_type: nil)
|
33
|
+
key_clip_length = Scryglass.config.tree_view_key_string_clip_length
|
34
|
+
value_clip_length = Scryglass.config.tree_view_value_string_clip_length
|
35
|
+
|
36
|
+
self.has_cursor = false
|
37
|
+
self.expanded = false
|
38
|
+
|
39
|
+
self.key_value_relationship_indicator = key_value_relationship_indicator
|
40
|
+
|
41
|
+
## Open up ViewWrappers and grab their objects and their custom strings
|
42
|
+
if key.class == Scryglass::ViewWrapper
|
43
|
+
self.key_string = key.to_s.clip_at(key_clip_length)
|
44
|
+
self.key = key.model
|
45
|
+
else
|
46
|
+
self.key_string = key.inspect.clip_at(key_clip_length)
|
47
|
+
self.key = key
|
48
|
+
end
|
49
|
+
if val.class == Scryglass::ViewWrapper
|
50
|
+
self.value_string = val.to_s.clip_at(value_clip_length)
|
51
|
+
self.value = val.model
|
52
|
+
else
|
53
|
+
self.value_string = val.inspect.clip_at(value_clip_length)
|
54
|
+
self.value = val
|
55
|
+
end
|
56
|
+
|
57
|
+
self.sub_ros = []
|
58
|
+
self.parent_ro = parent_ro
|
59
|
+
self.val_type = val_type
|
60
|
+
self.special_sub_ro_type = special_sub_ro_type
|
61
|
+
self.depth = depth
|
62
|
+
self.wrappers = WRAPPER_TYPES[value.class.to_s.split('::').last] || '?¿'
|
63
|
+
|
64
|
+
self.lens_strings = { key: {}, value: {} }
|
65
|
+
|
66
|
+
self.index = scry_session.all_ros.count
|
67
|
+
scry_session.all_ros << self
|
68
|
+
self.scry_session = scry_session
|
69
|
+
end
|
70
|
+
|
71
|
+
def top_ro?
|
72
|
+
parent_ro.nil?
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_s
|
76
|
+
value_indicator =
|
77
|
+
bucket? ? bucket_indicator : value_string
|
78
|
+
|
79
|
+
key_value_spacer =
|
80
|
+
key_value_pair? ? key_string + key_value_relationship_indicator : ''
|
81
|
+
|
82
|
+
special_sub_ro_expansion_indicator =
|
83
|
+
special_sub_ros.any? && !expanded ? '•' : ' '
|
84
|
+
|
85
|
+
left_fill_string + special_sub_ro_expansion_indicator +
|
86
|
+
key_value_spacer + value_indicator
|
87
|
+
end
|
88
|
+
|
89
|
+
def next_visible_ro_down
|
90
|
+
subsequent_ros = scry_session.all_ros[(index + 1)..-1]
|
91
|
+
subsequent_ros.find(&:visible?)
|
92
|
+
end
|
93
|
+
|
94
|
+
def next_visible_ro_up
|
95
|
+
preceding_ros = scry_session.all_ros[0...index]
|
96
|
+
preceding_ros.reverse.find(&:visible?)
|
97
|
+
end
|
98
|
+
|
99
|
+
def current_subject
|
100
|
+
send(scry_session.current_subject_type)
|
101
|
+
end
|
102
|
+
|
103
|
+
def iv_sub_ros
|
104
|
+
sub_ros.select { |sub_ro| sub_ro.special_sub_ro_type == :iv }
|
105
|
+
end
|
106
|
+
|
107
|
+
def ar_sub_ros
|
108
|
+
sub_ros.select { |sub_ro| sub_ro.special_sub_ro_type == :ar }
|
109
|
+
end
|
110
|
+
|
111
|
+
def enum_sub_ros
|
112
|
+
sub_ros.select { |sub_ro| sub_ro.special_sub_ro_type == :enum }
|
113
|
+
end
|
114
|
+
|
115
|
+
# (Used for recalculate_indeces after new Ros have been injected)
|
116
|
+
def next_ro_without_using_index
|
117
|
+
if sub_ros.any?
|
118
|
+
sub_ros.first
|
119
|
+
elsif top_ro?
|
120
|
+
nil
|
121
|
+
elsif sibling_down.present?
|
122
|
+
sibling_down
|
123
|
+
else
|
124
|
+
upward_feeler_ro = self
|
125
|
+
until upward_feeler_ro.sibling_down.present? || upward_feeler_ro.top_ro?
|
126
|
+
upward_feeler_ro = upward_feeler_ro.parent_ro
|
127
|
+
end
|
128
|
+
upward_feeler_ro.sibling_down
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def sibling_down
|
133
|
+
return nil if top_ro?
|
134
|
+
|
135
|
+
siblings = parent_ro.sub_ros
|
136
|
+
self_index = siblings.index(self)
|
137
|
+
return nil if self == siblings.last
|
138
|
+
|
139
|
+
siblings[self_index + 1]
|
140
|
+
end
|
141
|
+
|
142
|
+
## This exists so that an easy *unordered array match* can occur elsewhere.
|
143
|
+
def <=>(other)
|
144
|
+
unless self.class == other.class
|
145
|
+
raise ArgumentError, "Comparison of #{self.class} with #{other.class}"
|
146
|
+
end
|
147
|
+
|
148
|
+
object_id <=> other.object_id
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
def visible?
|
153
|
+
return true if top_ro?
|
154
|
+
|
155
|
+
scanning_ro = parent_ro
|
156
|
+
until scanning_ro.top_ro? || !scanning_ro.expanded
|
157
|
+
scanning_ro = scanning_ro.parent_ro
|
158
|
+
end
|
159
|
+
|
160
|
+
scanning_ro.expanded
|
161
|
+
end
|
162
|
+
|
163
|
+
def bucket?
|
164
|
+
val_type == :bucket
|
165
|
+
end
|
166
|
+
|
167
|
+
def nugget?
|
168
|
+
val_type == :nugget
|
169
|
+
end
|
170
|
+
|
171
|
+
def key_value_pair?
|
172
|
+
!!key_value_relationship_indicator
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
|
177
|
+
def special_sub_ros
|
178
|
+
sub_ros.select(&:special_sub_ro_type)
|
179
|
+
end
|
180
|
+
|
181
|
+
def normal_sub_ros
|
182
|
+
sub_ros.reject(&:special_sub_ro_type)
|
183
|
+
end
|
184
|
+
|
185
|
+
def bucket_indicator
|
186
|
+
if expanded && normal_sub_ros.any?
|
187
|
+
wrappers[0]
|
188
|
+
elsif normal_sub_ros.any?
|
189
|
+
# Number of dots indicating order of magnitude for Enumerable's count:
|
190
|
+
# Turning this off (the consistent three dots is more like an ellipsis,
|
191
|
+
# communicating with a solid preexisting symbol), but keeping the idea here:
|
192
|
+
# sub_ros_order_of_magnitude = normal_sub_ros.count.to_s.length
|
193
|
+
# wrappers.dup.insert(1, '•' * sub_ros_order_of_magnitude)
|
194
|
+
wrappers.dup.insert(1, '•••')
|
195
|
+
else
|
196
|
+
wrappers
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def left_fill_string
|
201
|
+
left_fill = if has_cursor
|
202
|
+
cursor_string
|
203
|
+
else
|
204
|
+
' ' * cursor_length
|
205
|
+
end
|
206
|
+
|
207
|
+
if scry_session.special_command_targets.any? && scry_session.special_command_targets.map(&:index).include?(index)
|
208
|
+
left_fill[-2..-1] = '->'
|
209
|
+
end
|
210
|
+
left_fill
|
211
|
+
end
|
212
|
+
|
213
|
+
def cursor_length
|
214
|
+
tab_length = Scryglass.config.tab_length
|
215
|
+
|
216
|
+
consistent_margin = [4 - tab_length, 0].max
|
217
|
+
|
218
|
+
(tab_length * depth) + consistent_margin
|
219
|
+
end
|
220
|
+
|
221
|
+
def cursor_string
|
222
|
+
cursor = Scryglass::Session::CURSOR_CHARACTER * cursor_length
|
223
|
+
|
224
|
+
if nugget? && has_cursor && value.is_a?(Enumerable) &&
|
225
|
+
value.any? &&
|
226
|
+
enum_sub_ros.empty?
|
227
|
+
cursor[0] = '('
|
228
|
+
end
|
229
|
+
|
230
|
+
if value.instance_variables.any? && iv_sub_ros.empty?
|
231
|
+
cursor[1] = '@'
|
232
|
+
end
|
233
|
+
|
234
|
+
cursor
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|