sirens 0.0.1
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/lib/components/component.rb +46 -0
- data/lib/components/component_behaviour.rb +160 -0
- data/lib/components/containers/splitter.rb +98 -0
- data/lib/components/containers/stack.rb +42 -0
- data/lib/components/containers/tabs.rb +24 -0
- data/lib/components/containers/window.rb +20 -0
- data/lib/components/primitive_component.rb +155 -0
- data/lib/components/widgets/button.rb +16 -0
- data/lib/components/widgets/checkbox.rb +39 -0
- data/lib/components/widgets/column_props.rb +23 -0
- data/lib/components/widgets/input_text.rb +27 -0
- data/lib/components/widgets/list.rb +68 -0
- data/lib/components/widgets/list_choice.rb +89 -0
- data/lib/components/widgets/radio_button.rb +33 -0
- data/lib/components/widgets/text.rb +31 -0
- data/lib/components/widgets/tree_choice.rb +82 -0
- data/lib/layouts/columns_builder.rb +63 -0
- data/lib/layouts/layout_builder.rb +203 -0
- data/lib/layouts/radio_button_group_builder.rb +28 -0
- data/lib/models/choice_model.rb +41 -0
- data/lib/models/list_model.rb +173 -0
- data/lib/models/tree_choice_model.rb +102 -0
- data/lib/models/value_model.rb +56 -0
- data/lib/models/virtual_tree_model.rb +150 -0
- data/lib/sirens/browsers/module_browser.rb +67 -0
- data/lib/sirens/browsers/object_browser.rb +103 -0
- data/lib/sirens/components/ancestors_list.rb +22 -0
- data/lib/sirens/components/class_browser.rb +38 -0
- data/lib/sirens/components/constants_list.rb +22 -0
- data/lib/sirens/components/method_source_code.rb +22 -0
- data/lib/sirens/components/methods_list.rb +63 -0
- data/lib/sirens/components/modules_list.rb +23 -0
- data/lib/sirens/components/namespaces_list.rb +21 -0
- data/lib/sirens/models/constant_model.rb +21 -0
- data/lib/sirens/models/method_model.rb +111 -0
- data/lib/sirens/models/module_browser_model.rb +225 -0
- data/lib/sirens/models/object_browser_model.rb +125 -0
- data/lib/sirens.rb +90 -0
- data/lib/views/button_view.rb +56 -0
- data/lib/views/check_button_view.rb +65 -0
- data/lib/views/entry_view.rb +25 -0
- data/lib/views/list_view.rb +230 -0
- data/lib/views/menu_view.rb +46 -0
- data/lib/views/notebook_view.rb +27 -0
- data/lib/views/paned_view.rb +26 -0
- data/lib/views/radio_button_view.rb +67 -0
- data/lib/views/stack_view.rb +26 -0
- data/lib/views/text_view.rb +112 -0
- data/lib/views/tree_view.rb +314 -0
- data/lib/views/view.rb +206 -0
- data/lib/views/window_view.rb +51 -0
- metadata +110 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
require 'observer'
|
|
2
|
+
|
|
3
|
+
module Sirens
|
|
4
|
+
class ListModel
|
|
5
|
+
include Observable
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
def with(item)
|
|
9
|
+
self.on([items])
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def with_all(items)
|
|
13
|
+
self.on(items.clone)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def on(list)
|
|
17
|
+
self.new(list)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Initializing
|
|
22
|
+
|
|
23
|
+
def initialize(list = [])
|
|
24
|
+
super()
|
|
25
|
+
|
|
26
|
+
@list = list
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Accessing
|
|
30
|
+
|
|
31
|
+
def list()
|
|
32
|
+
@list
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def set_list(new_list)
|
|
36
|
+
return if @list == new_list
|
|
37
|
+
|
|
38
|
+
old_list = @list
|
|
39
|
+
|
|
40
|
+
@list = new_list
|
|
41
|
+
|
|
42
|
+
changed
|
|
43
|
+
|
|
44
|
+
notify_observers(
|
|
45
|
+
ListChanged.new(new_list: new_list, old_list: old_list)
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def value()
|
|
50
|
+
list
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def set_value(new_list)
|
|
54
|
+
set_list(new_list)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Adding
|
|
58
|
+
|
|
59
|
+
##
|
|
60
|
+
# Adds the given item at the end of the list.
|
|
61
|
+
#
|
|
62
|
+
def <<(item)
|
|
63
|
+
add_at(items: [item], index: -1)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
##
|
|
67
|
+
# Adds the given items at the end of the list.
|
|
68
|
+
#
|
|
69
|
+
def add_at(index:, items:)
|
|
70
|
+
list.insert(index, *items)
|
|
71
|
+
|
|
72
|
+
changed
|
|
73
|
+
|
|
74
|
+
notify_observers(
|
|
75
|
+
ItemsAdded.new(list: list, index: index, items: items)
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Updating
|
|
80
|
+
|
|
81
|
+
##
|
|
82
|
+
# Updates the items at the given indices in the list.
|
|
83
|
+
#
|
|
84
|
+
def update_at(indices:, items:)
|
|
85
|
+
(0 ... items.size).each do |i|
|
|
86
|
+
list[ indices[i] ] = items[i]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
changed
|
|
90
|
+
|
|
91
|
+
notify_observers(
|
|
92
|
+
ItemsUpdated.new(list: list, indices: indices, items: items)
|
|
93
|
+
)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Removing
|
|
97
|
+
|
|
98
|
+
##
|
|
99
|
+
# Removes the items at the given indices in the list.
|
|
100
|
+
#
|
|
101
|
+
def remove_at(indices:)
|
|
102
|
+
indices = indices.sort.reverse
|
|
103
|
+
items = []
|
|
104
|
+
|
|
105
|
+
indices.sort.reverse.each do |i|
|
|
106
|
+
items << list.delete_at(i)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
changed
|
|
110
|
+
|
|
111
|
+
notify_observers(
|
|
112
|
+
ItemsRemoved.new(list: list, indices: indices, items: items)
|
|
113
|
+
)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Iterating
|
|
117
|
+
|
|
118
|
+
include Enumerable
|
|
119
|
+
|
|
120
|
+
def each(&block)
|
|
121
|
+
@list.each(&block)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def [](index)
|
|
125
|
+
@list[index]
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def []=(index, value)
|
|
129
|
+
@list[index] = value
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Announcements
|
|
134
|
+
|
|
135
|
+
class ListChanged
|
|
136
|
+
def initialize(new_list:, old_list:)
|
|
137
|
+
@new_list = new_list
|
|
138
|
+
@old_list = old_list
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
attr_reader :new_list, :old_list
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
class ItemsAdded
|
|
145
|
+
def initialize(list:, index:, items:)
|
|
146
|
+
@list = list
|
|
147
|
+
@index = index
|
|
148
|
+
@items = items
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
attr_reader :list, :index, :items
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
class ItemsUpdated
|
|
155
|
+
def initialize(list:, indices:, items:)
|
|
156
|
+
@list = list
|
|
157
|
+
@indices = indices
|
|
158
|
+
@items = items
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
attr_reader :list, :indices, :items
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
class ItemsRemoved
|
|
165
|
+
def initialize(list:, indices:, items:)
|
|
166
|
+
@list = list
|
|
167
|
+
@indices = indices
|
|
168
|
+
@items = items
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
attr_reader :list, :indices, :items
|
|
172
|
+
end
|
|
173
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
module Sirens
|
|
2
|
+
class TreeChoiceModel
|
|
3
|
+
|
|
4
|
+
# Initializing
|
|
5
|
+
|
|
6
|
+
def initialize(selection: nil, roots: [], get_children_block:)
|
|
7
|
+
super()
|
|
8
|
+
|
|
9
|
+
@selection = ValueModel.on(selection)
|
|
10
|
+
@tree = VirtualTreeModel.on(
|
|
11
|
+
roots: roots,
|
|
12
|
+
get_children_block: get_children_block
|
|
13
|
+
)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Accessing
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# Returns the selection model, not the selected item.
|
|
20
|
+
# This model can be observed for changes.
|
|
21
|
+
# This model value is an array with the objects path from the tree root object to the selected item.
|
|
22
|
+
#
|
|
23
|
+
def selection()
|
|
24
|
+
@selection
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
##
|
|
28
|
+
# Sets the item currently selected.
|
|
29
|
+
# The objects_hierarchy is an array with the objects path from the tree root object to the
|
|
30
|
+
# selected item.
|
|
31
|
+
# The selection model triggers an announcement that its value changed.
|
|
32
|
+
#
|
|
33
|
+
def set_selection(objects_hierarchy)
|
|
34
|
+
@selection.set_value(objects_hierarchy)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# Sets the item currently selected.
|
|
39
|
+
# The objects_hierarchy is an array with the objects path from the tree root object to the
|
|
40
|
+
# selected item.
|
|
41
|
+
# The selection model triggers an announcement that its value changed.
|
|
42
|
+
#
|
|
43
|
+
def set_selection_from_path(path:)
|
|
44
|
+
objects_hierarchies = objects_hierarchy_at(path: path)
|
|
45
|
+
|
|
46
|
+
set_selection(objects_hierarchies)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
##
|
|
50
|
+
# Returns the tree model.
|
|
51
|
+
# The tree model holds the tree items and announces changes on it.
|
|
52
|
+
#
|
|
53
|
+
def tree()
|
|
54
|
+
@tree
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
##
|
|
58
|
+
# Sets the tree model roots.
|
|
59
|
+
# Announces that the tree changed.
|
|
60
|
+
#
|
|
61
|
+
def set_tree_roots(items)
|
|
62
|
+
@tree.set_roots(items)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
##
|
|
66
|
+
# Returns the item in the tree at the given path.
|
|
67
|
+
# The path is an array of Integers, each Integer being the index in each level of the tree.
|
|
68
|
+
# Example:
|
|
69
|
+
# [1, 0, 3] returns the item taken from the second root, its first child and its fourth child.
|
|
70
|
+
#
|
|
71
|
+
def item_at(path:)
|
|
72
|
+
@tree.item_at(path: path)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
##
|
|
76
|
+
# Returns an array with the children of the item in the tree at the given path.
|
|
77
|
+
#
|
|
78
|
+
def children_at(path:)
|
|
79
|
+
@tree.children_at(path: path)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
##
|
|
83
|
+
# Given a hierarchy of objects in the tree, returns an array with the path indices.
|
|
84
|
+
#
|
|
85
|
+
def path_of(objects_hierarchy)
|
|
86
|
+
@tree.path_of(objects_hierarchy)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
##
|
|
90
|
+
# Given a path returns an array with the objects on each tree level corresponding to each index in the path.
|
|
91
|
+
#
|
|
92
|
+
def objects_hierarchy_at(path:)
|
|
93
|
+
@tree.objects_hierarchy_at(path: path)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Asking
|
|
97
|
+
|
|
98
|
+
def has_selection()
|
|
99
|
+
! @selection.value.nil? && ! @selection.value.empty?
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require 'observer'
|
|
2
|
+
|
|
3
|
+
module Sirens
|
|
4
|
+
class ValueModel
|
|
5
|
+
include Observable
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
def on(value)
|
|
9
|
+
self.new(value)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Initializing
|
|
14
|
+
|
|
15
|
+
def initialize(value = nil)
|
|
16
|
+
super()
|
|
17
|
+
|
|
18
|
+
@value = value
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Accessing
|
|
22
|
+
|
|
23
|
+
def value()
|
|
24
|
+
@value
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def set_value(new_value)
|
|
28
|
+
return if value == new_value
|
|
29
|
+
|
|
30
|
+
old_value = value
|
|
31
|
+
|
|
32
|
+
@value = new_value
|
|
33
|
+
|
|
34
|
+
announce_value_changed(new_value: new_value, old_value: old_value)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def announce_value_changed(new_value:, old_value:)
|
|
38
|
+
changed
|
|
39
|
+
|
|
40
|
+
notify_observers(
|
|
41
|
+
ValueChanged.new(new_value: new_value, old_value: old_value)
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Events
|
|
47
|
+
|
|
48
|
+
class ValueChanged
|
|
49
|
+
def initialize(new_value:, old_value:)
|
|
50
|
+
@new_value = new_value
|
|
51
|
+
@old_value = old_value
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
attr_reader :new_value, :old_value
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
require 'observer'
|
|
2
|
+
|
|
3
|
+
module Sirens
|
|
4
|
+
class VirtualTreeModel
|
|
5
|
+
include Observable
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
def with(root_item)
|
|
9
|
+
self.on([root_item])
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def with_all(root_items)
|
|
13
|
+
self.on(root_items.clone)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def on(root_items)
|
|
17
|
+
self.new(root_items)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Initializing
|
|
22
|
+
|
|
23
|
+
def initialize(roots: [], get_children_block:)
|
|
24
|
+
super()
|
|
25
|
+
|
|
26
|
+
@get_children_block = get_children_block
|
|
27
|
+
|
|
28
|
+
@roots = roots.collect{ |item| new_tree_node_on(item) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def new_tree_node_on(item)
|
|
32
|
+
TreeNode.new(value: item, get_children_block: @get_children_block)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Accessing
|
|
36
|
+
|
|
37
|
+
def roots()
|
|
38
|
+
@roots.collect{ |node| node.value }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def set_roots(new_roots)
|
|
42
|
+
old_roots = roots
|
|
43
|
+
|
|
44
|
+
@roots = new_roots.collect{ |item| new_tree_node_on(item) }
|
|
45
|
+
|
|
46
|
+
changed
|
|
47
|
+
|
|
48
|
+
notify_observers(
|
|
49
|
+
TreeChanged.new(new_roots: new_roots, old_roots: old_roots)
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def node_at(path:)
|
|
54
|
+
node = @roots[path.first]
|
|
55
|
+
|
|
56
|
+
path[1..-1].each do |child_index|
|
|
57
|
+
node = node.child_at(index: child_index)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
node
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def item_at(path:)
|
|
64
|
+
node_at(path: path).value
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def children_at(path:)
|
|
68
|
+
node_at(path: path).children.collect{ |node| node.value }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
##
|
|
72
|
+
# Given a hierarchy of objects in the tree, returns an array with the path indices.
|
|
73
|
+
#
|
|
74
|
+
def path_of(objects_hierarchy)
|
|
75
|
+
objects = @roots
|
|
76
|
+
|
|
77
|
+
objects_hierarchy.inject([]) { |path, each_object|
|
|
78
|
+
index = objects.index { |node| node.value == each_object }
|
|
79
|
+
|
|
80
|
+
path << index
|
|
81
|
+
|
|
82
|
+
objects = objects[index].children
|
|
83
|
+
|
|
84
|
+
path
|
|
85
|
+
}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
##
|
|
89
|
+
# Given a path returns an array with the objects on each tree level corresponding to each index in the path.
|
|
90
|
+
#
|
|
91
|
+
def objects_hierarchy_at(path:)
|
|
92
|
+
return [] if path.nil?
|
|
93
|
+
|
|
94
|
+
nodes = @roots
|
|
95
|
+
|
|
96
|
+
path.inject([]) { |hierarchy, index|
|
|
97
|
+
node = nodes[index]
|
|
98
|
+
|
|
99
|
+
hierarchy << node.value
|
|
100
|
+
|
|
101
|
+
nodes = node.children
|
|
102
|
+
|
|
103
|
+
hierarchy
|
|
104
|
+
}
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Announcements
|
|
108
|
+
|
|
109
|
+
class TreeChanged
|
|
110
|
+
def initialize(new_roots:, old_roots:)
|
|
111
|
+
@new_roots = new_roots
|
|
112
|
+
@old_roots = old_roots
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
attr_reader :new_roots, :old_roots
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Tree node class
|
|
119
|
+
|
|
120
|
+
class TreeNode
|
|
121
|
+
def initialize(value:, get_children_block:)
|
|
122
|
+
@value = value
|
|
123
|
+
@children = nil
|
|
124
|
+
@get_children_block = get_children_block
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def value()
|
|
128
|
+
@value
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def children()
|
|
132
|
+
if @children.nil?
|
|
133
|
+
@children = get_children.collect{ |item|
|
|
134
|
+
self.class.new(value: item, get_children_block: @get_children_block)
|
|
135
|
+
}
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
@children
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def child_at(index:)
|
|
142
|
+
children[index]
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def get_children()
|
|
146
|
+
@get_children_block.call(value)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module Sirens
|
|
2
|
+
|
|
3
|
+
class ModuleBrowser < Sirens::Component
|
|
4
|
+
|
|
5
|
+
def self.open_on(klass: nil)
|
|
6
|
+
self.open.tap { |browser|
|
|
7
|
+
if klass.kind_of?(Class)
|
|
8
|
+
browser.model.select_class(klass)
|
|
9
|
+
else
|
|
10
|
+
browser.model.select_namespace(namespace)
|
|
11
|
+
end
|
|
12
|
+
}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Model
|
|
16
|
+
|
|
17
|
+
def default_model()
|
|
18
|
+
ModuleBrowserModel.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Building
|
|
22
|
+
|
|
23
|
+
def renderWith(layout)
|
|
24
|
+
browser_model = model
|
|
25
|
+
|
|
26
|
+
layout.render do |component|
|
|
27
|
+
|
|
28
|
+
window do
|
|
29
|
+
|
|
30
|
+
styles title: 'Namespace Browser',
|
|
31
|
+
width: 900,
|
|
32
|
+
height: 400
|
|
33
|
+
|
|
34
|
+
vertical_splitter do
|
|
35
|
+
|
|
36
|
+
horizontal_splitter do
|
|
37
|
+
|
|
38
|
+
styles splitter_proportion: 1.0/2.0
|
|
39
|
+
|
|
40
|
+
component NamespacesList.new(
|
|
41
|
+
model: browser_model.namespaces,
|
|
42
|
+
splitter_proportion: 1.0/4.0
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
component ModulesList.new(
|
|
46
|
+
model: browser_model.modules,
|
|
47
|
+
splitter_proportion: 1.0/4.0,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
component ClassBrowser.new(
|
|
51
|
+
model: browser_model,
|
|
52
|
+
splitter_proportion: 2.0/4.0
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
component MethodSourceCode.new(
|
|
57
|
+
model: browser_model.method_source_code,
|
|
58
|
+
splitter_proportion: 1.0/2.0
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
module Sirens
|
|
2
|
+
|
|
3
|
+
class ObjectBrowser < Sirens::Component
|
|
4
|
+
|
|
5
|
+
def self.open_on(object:)
|
|
6
|
+
self.open.tap { |browser|
|
|
7
|
+
browser.model.object.set_value(object)
|
|
8
|
+
}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Model
|
|
12
|
+
|
|
13
|
+
def default_model()
|
|
14
|
+
ObjectBrowserModel.new
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Building
|
|
18
|
+
|
|
19
|
+
def renderWith(layout)
|
|
20
|
+
browser_model = model
|
|
21
|
+
|
|
22
|
+
layout.render do |component|
|
|
23
|
+
|
|
24
|
+
window do
|
|
25
|
+
|
|
26
|
+
styles title: 'Object Browser',
|
|
27
|
+
width: 400,
|
|
28
|
+
height: 400
|
|
29
|
+
|
|
30
|
+
vertical_splitter do
|
|
31
|
+
|
|
32
|
+
choices_tree do
|
|
33
|
+
model browser_model.object_instance_variables
|
|
34
|
+
|
|
35
|
+
styles splitter_proportion: 2.0/3.0
|
|
36
|
+
|
|
37
|
+
handlers on_selection_action: proc { |index_path:, item:|
|
|
38
|
+
component.browse(object: item.value)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
column label: 'Instance variables',
|
|
42
|
+
get_text_block: proc { |inst_var| inst_var.display_string }
|
|
43
|
+
|
|
44
|
+
popup_menu { |menu:, menu_owner:|
|
|
45
|
+
selected_object = component.model.selected_inst_var_value
|
|
46
|
+
|
|
47
|
+
menu.item label: 'Browse it', enabled: ! selected_object.nil?,
|
|
48
|
+
action: proc{ component.browse(object: selected_object) }
|
|
49
|
+
|
|
50
|
+
menu.separator
|
|
51
|
+
|
|
52
|
+
menu.item label: 'Browse class', enabled: ! selected_object.nil?,
|
|
53
|
+
action: proc{ component.browse_class_of(object: selected_object) }
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
text do
|
|
58
|
+
model browser_model.text
|
|
59
|
+
|
|
60
|
+
styles(
|
|
61
|
+
splitter_proportion: 1.0/3.0,
|
|
62
|
+
wrap_mode: :char
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
popup_menu { |menu:, menu_owner:|
|
|
66
|
+
selected_text = menu_owner.selected_text
|
|
67
|
+
|
|
68
|
+
menu.separator
|
|
69
|
+
|
|
70
|
+
menu.item label: 'Browse it', enabled: ! selected_text.nil?,
|
|
71
|
+
action: proc{ component.browse_text_selection(text: selected_text) }
|
|
72
|
+
}
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Actions
|
|
81
|
+
|
|
82
|
+
def browse(object:)
|
|
83
|
+
Sirens.browse(object: object)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def browse_class_of(object:)
|
|
87
|
+
Sirens.browse(klass: object.class)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def browse_text_selection(text:)
|
|
91
|
+
selected_object = model.selected_inst_var_value
|
|
92
|
+
|
|
93
|
+
evaluation_result =
|
|
94
|
+
begin
|
|
95
|
+
selected_object.instance_exec { eval(text) }
|
|
96
|
+
rescue Exception => e
|
|
97
|
+
e
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
browse(object: evaluation_result)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Sirens
|
|
2
|
+
class AncestorsList < Sirens::Component
|
|
3
|
+
|
|
4
|
+
# Building
|
|
5
|
+
|
|
6
|
+
def renderWith(layout)
|
|
7
|
+
|
|
8
|
+
layout.render do |component|
|
|
9
|
+
choices_list do
|
|
10
|
+
model component.model
|
|
11
|
+
|
|
12
|
+
styles(
|
|
13
|
+
show_headers: true
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
column label: 'Module ancestors',
|
|
17
|
+
get_text_block: proc{ |a_module| a_module.name }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Sirens
|
|
2
|
+
class ClassBrowser < Sirens::Component
|
|
3
|
+
|
|
4
|
+
# Building
|
|
5
|
+
|
|
6
|
+
def renderWith(layout)
|
|
7
|
+
browser_model = model
|
|
8
|
+
|
|
9
|
+
layout.render do |component|
|
|
10
|
+
horizontal_splitter do
|
|
11
|
+
component AncestorsList.new(
|
|
12
|
+
model: browser_model.module_ancestors,
|
|
13
|
+
splitter_proportion: 1.0/3.0
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
tabs do
|
|
17
|
+
styles splitter_proportion: 2.0/3.0
|
|
18
|
+
|
|
19
|
+
component MethodsList.new(
|
|
20
|
+
model: browser_model.methods,
|
|
21
|
+
instance_or_class_methods_chooser: browser_model.instance_or_class_methods_chooser,
|
|
22
|
+
show_inherit_methods: browser_model.show_inherit_methods,
|
|
23
|
+
show_public_methods: browser_model.show_public_methods,
|
|
24
|
+
show_protected_methods: browser_model.show_protected_methods,
|
|
25
|
+
show_private_methods: browser_model.show_private_methods,
|
|
26
|
+
tab_label: 'Methods'
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
component ConstantsList.new(
|
|
30
|
+
model: browser_model.constants,
|
|
31
|
+
tab_label: 'Constants'
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|