vistual_call 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 254b90d7ec0cbe6c4c202202beaf927acab31c4a8ebdf38c566affefde719513
4
+ data.tar.gz: bb2dabaf7f79a194341c6757a0e94e48e66695460b6f47333595d6ce6aa47f2a
5
+ SHA512:
6
+ metadata.gz: 7ddce952a828984266dd861a20bce14942ec483b9e5a2edd864fe93fb47c3e04cdad8f835cd59a5b11f4bb0d84fffe925be8b77e3de5d7c63261cd31e3584669
7
+ data.tar.gz: 11179df1c70d10ab271c4754e1986e09d4c7cc49887bca333ccc10a9880f83c808db525d6462418d0c83e57240960a821b691f5a15a080c44ee8b9b11d0fc6e5
data/.DS_Store ADDED
Binary file
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in vistual_call.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
data/Gemfile.lock ADDED
@@ -0,0 +1,19 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ vistual_call (0.2.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ rake (13.0.6)
10
+
11
+ PLATFORMS
12
+ arm64-darwin-21
13
+
14
+ DEPENDENCIES
15
+ rake (~> 13.0)
16
+ vistual_call!
17
+
18
+ BUNDLED WITH
19
+ 2.4.12
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Mark24
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # VistualCall
2
+
3
+ VistualCall is a gem to help you trace your code and export beautiful vistual call graph.
4
+
5
+ # Introduction
6
+
7
+ ## Dependency
8
+
9
+ 1. Graphviz
10
+
11
+ You need to install [Graphviz](https://graphviz.org/) by yourself.
12
+
13
+ Go to install [graphviz](https://graphviz.org/download/).
14
+
15
+ ## Usage
16
+
17
+ ### 1. Install gem
18
+
19
+ `gem install vistual_call`
20
+
21
+ ### 2. Only the method needs to be wrapped.
22
+
23
+
24
+ ```ruby
25
+ require 'vistual_call'
26
+
27
+ def call_c
28
+ end
29
+
30
+ def call_b
31
+ call_c
32
+ end
33
+
34
+ def call_a
35
+ call_b
36
+ end
37
+
38
+ VistualCall.trace do
39
+ call_a # enter call
40
+ end
41
+ ```
42
+
43
+ ![example](./example/example.png)
44
+
45
+ The method after each node is call order number. This will help your understand the order of the function call.
46
+
47
+ ## 3. More information
48
+
49
+ ## configuration
50
+
51
+ ```ruby
52
+ # you can pass options
53
+ VistualCall.trace(options) do
54
+ # run your code here...
55
+ end
56
+ ```
57
+
58
+ Options:
59
+
60
+ | name | type | required | explain | example |
61
+ | ---- | ---- | ---- | ---- | ---- |
62
+ | label | String | true | 标题 | Hello |
63
+ | labelloc | Symbol | false | 标题位置: :top :bottom :center | :top |
64
+ | labeljust | Symbol | false | 标题对齐位置 :left, :center, :right | :center |
65
+ | direction | Symbol| false | 绘制方向,依次是 :TB(从上到下),:LR(从左到右,默认方式),:BT(从下到上),:RL(从右到左) | :LR |
66
+ | format | String | false | 输出图片格式,查看 [graphviz 支持输出格式](https://graphviz.org/docs/outputs/) 'png'、'svg' | 默认 'png' |
67
+ | output | String | false | 导出图片绝对路径 | 默认家目录下 `vistual_call_result.png` |
68
+ | theme | Symbol | false | 配色主题 :sky, :lemon | 默认 :sky |
69
+ | show_dot | boolean | false | 展示 dot 内容 | 默认 false |
70
+ | show_order_number | boolean | false | 输出调用序号 | 默认 true |
71
+ | jump_list | Array(String) | false | 跳过节点,默认 ["Kernel#class", "Kernel#frozen?"] | - |
72
+ | heightlight_match | Regex | false | 默认高亮匹配 label, 默认 /method_missing/ | /method_missing/ |
73
+
74
+ ## LICENSE
75
+
76
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ task default: %i[]
Binary file
data/example/sample.rb ADDED
@@ -0,0 +1,16 @@
1
+ require_relative "../lib/vistual_call"
2
+
3
+ def call_c
4
+ end
5
+
6
+ def call_b
7
+ call_c
8
+ end
9
+
10
+ def call_a
11
+ call_b
12
+ end
13
+
14
+ VistualCall.trace(title: "Hellow", show_dot: true) do
15
+ call_a # enter call
16
+ end
Binary file
@@ -0,0 +1,8 @@
1
+ require "sinatra"
2
+ require_relative "../lib/vistual_call"
3
+
4
+ VistualCall.trace do
5
+ get "/" do
6
+ "hello"
7
+ end
8
+ end
@@ -0,0 +1,298 @@
1
+ require "set"
2
+ require "tempfile"
3
+ require "yaml"
4
+ require_relative "./tracer"
5
+
6
+ module VistualCall
7
+ # Output config
8
+ DEFAULT_OUTPUT_FORMAT = "png"
9
+ DEFAULT_OUTPUT = "vistual_call_result.png"
10
+ DEFAULT_OUTPUT_PATH = File.join(Dir.home, DEFAULT_OUTPUT)
11
+
12
+ # Jump Node
13
+ DEFAULT_JUMP_NODE = %w[Kernel#class Kernel#frozen?]
14
+
15
+ # Hightlight Label
16
+ HIGHT_LIGHT_REGEX = /method_missing/
17
+
18
+ # Core
19
+ DIRECTIONS = %w[TB LR BT RL]
20
+ LABEL_LOC = %w[top bottom center]
21
+ LABEL_JUST = %w[left right center]
22
+
23
+ class Graph
24
+ @@custer_count = 0
25
+ attr_accessor :call_tree_root
26
+
27
+ def self.root
28
+ File.expand_path("../../", __dir__)
29
+ end
30
+
31
+ def initialize(options = {})
32
+ @label = options.fetch(:label, nil)
33
+ @labelloc = options.fetch(:labelloc, :top).to_s
34
+ if !LABEL_LOC.include?(@labelloc)
35
+ raise VistualCallError("labelloc must in #{LABEL_LOC}")
36
+ end
37
+ @labeljust = options.fetch(:labeljust, :center).to_s
38
+ if !LABEL_JUST.include?(@labeljust)
39
+ raise VistualCallError("labeljust must in #{LABEL_JUST}")
40
+ end
41
+ @margin = options[:margin] || "5"
42
+ # display config
43
+ @direction = options.fetch(:direction, :LR).to_s
44
+ if !DIRECTIONS.include?(@direction)
45
+ raise VistualCallError("direction must in #{DIRECTIONS}")
46
+ end
47
+ @format = options.fetch(:format, DEFAULT_OUTPUT_FORMAT)
48
+ @output = File.expand_path(options.fetch(:output, DEFAULT_OUTPUT_PATH))
49
+
50
+ @show_dot = options.fetch(:show_dot, false)
51
+ @show_order_number = options.fetch(:show_order_number, true)
52
+
53
+ # node graph config
54
+ @jump_list = options.fetch(:jump_list, DEFAULT_JUMP_NODE)
55
+ @heightlight_match = options.fetch(:heightlight_match, HIGHT_LIGHT_REGEX)
56
+
57
+ # theme
58
+ @theme_name = options.fetch(:theme, :sky).to_s
59
+ @theme_config =
60
+ YAML.load_file(File.join(self.class.root, "theme.yml"), aliases: true)
61
+ @theme =
62
+ @theme_config[@theme_name] || @theme_config[@theme_config["use_theme"]]
63
+ @node_attrs = @theme["node_attrs"]
64
+ @edge_attrs = @theme["edge_attrs"]
65
+ @node_waring_attrs = @theme["node_warn_attrs"]
66
+
67
+ # working cache
68
+ @tracer = Tracer.new
69
+
70
+ @call_tree_root = nil
71
+ @call_tree_hashmap = nil
72
+
73
+ @label_hashmap = {}
74
+ @cache_graph_nodes_set = Set.new
75
+ @cache_graph_edges = []
76
+ end
77
+
78
+ def get_call_tree_root
79
+ @call_tree_root = @tracer.call_tree_root
80
+ end
81
+
82
+ def get_call_tree_hashmap
83
+ @call_tree_hashmap = @tracer.call_tree_hashmap
84
+ end
85
+
86
+ def track(&block)
87
+ @tracer.track(&block)
88
+ end
89
+
90
+ def get_graph_node_id(node)
91
+ label_name = node.method_name
92
+ if !@label_hashmap.key?(label_name)
93
+ @label_hashmap[label_name] = node.node_id
94
+ end
95
+ return @label_hashmap[label_name]
96
+ end
97
+
98
+ def collect_graph_nodes_edges(node)
99
+ return if node == nil
100
+ return if @jump_list.include?(node.method_name)
101
+
102
+ graph_node_id = get_graph_node_id(node)
103
+
104
+ # build node
105
+ @cache_graph_nodes_set.add(graph_node_id)
106
+
107
+ # buid edges
108
+ if node.parent_node_id
109
+ parent_node = @call_tree_hashmap[node.parent_node_id]
110
+ parent_graph_node_id = get_graph_node_id(parent_node)
111
+ @cache_graph_edges.push([parent_graph_node_id, graph_node_id])
112
+ end
113
+
114
+ # Make recurse call
115
+ if node.children.size > 0
116
+ node.children.each do |one_child_node|
117
+ collect_graph_nodes_edges(one_child_node)
118
+ end
119
+ end
120
+ end
121
+
122
+ def create_or_set(obj, key, value)
123
+ obj[key] = [] if !obj.key?(key)
124
+ obj[key].push(value)
125
+ end
126
+
127
+ def collect_group_cluster
128
+ @cluster_group = {}
129
+ @label_hashmap.keys.each do |label_name|
130
+ if label_name.count("::") == 0
131
+ create_or_set(@cluster_group, "_single", @label_hashmap[label_name])
132
+ else
133
+ module_name = label_name.split("#")
134
+ module_name.pop
135
+ module_name = module_name.join("#")
136
+ create_or_set(@cluster_group, module_name, @label_hashmap[label_name])
137
+ end
138
+ end
139
+ end
140
+
141
+ def get_dot_config_string(hashmap = nil)
142
+ return if !hashmap
143
+ config = hashmap.keys.map { |key| "#{key}=\"#{hashmap[key]}\"" }.join(",")
144
+ return "[#{config}]"
145
+ end
146
+
147
+ def merge_config(*configs)
148
+ new_config = {}
149
+ configs.each { |conf| new_config = new_config.merge(conf) }
150
+ return new_config
151
+ end
152
+
153
+ def get_label_text(node)
154
+ result = "#{node.defined_class}##{node.method_id}"
155
+ result << " (#{node.node_id})" if @show_order_number
156
+ return result
157
+ end
158
+
159
+ def dot_node_format(node_id)
160
+ if node_id == StartNodeID
161
+ return("node#{node_id}#{get_dot_config_string({ label: "Start" })}")
162
+ end
163
+
164
+ node = @call_tree_hashmap[node_id]
165
+
166
+ config = { label: get_label_text(node) }
167
+ config = merge_config(config, @node_waring_attrs) if @heightlight_match =~
168
+ node.method_name
169
+
170
+ return("node#{node_id}#{get_dot_config_string(config)}")
171
+ end
172
+
173
+ def generate_node_text(graph_node_id)
174
+ return dot_node_format(graph_node_id) + ";\n"
175
+ end
176
+
177
+ def generate_cluster(module_name, graph_ids)
178
+ @@custer_count += 1
179
+
180
+ cluster_style_config = @theme.dig("cluster", "style") || nil
181
+ cluster_style_config_text =
182
+ cluster_style_config && "style=\"#{cluster_style_config}\";"
183
+
184
+ cluster_color_config = @theme.dig("cluster", "color") || nil
185
+ cluster_color_config_text =
186
+ cluster_color_config && "color=\"#{cluster_color_config}\";"
187
+
188
+ cluster_node_config = @theme.dig("cluster_node") || nil
189
+ cluster_node_config_text =
190
+ cluster_node_config &&
191
+ "node#{get_dot_config_string(cluster_node_config)};"
192
+
193
+ template = <<-CLUSTER
194
+ subgraph cluster_#{@@custer_count} {
195
+ label="#{module_name}";
196
+ #{cluster_style_config_text}
197
+ #{cluster_color_config_text}
198
+ #{cluster_node_config_text}
199
+
200
+ #{graph_ids.map { |graph_node_id| generate_node_text(graph_node_id) }.join("")}
201
+ }
202
+ CLUSTER
203
+
204
+ return template
205
+ end
206
+
207
+ def render_nodes_and_clusters()
208
+ content = ""
209
+ @cluster_group.keys.each do |key|
210
+ if key == "_single"
211
+ graph_node_ids = @cluster_group[key]
212
+
213
+ graph_node_ids.each do |graph_node_id|
214
+ content << generate_node_text(graph_node_id)
215
+ end
216
+ else
217
+ module_name = key
218
+ module_graph_node_ids = @cluster_group[key]
219
+
220
+ content << generate_cluster(module_name, module_graph_node_ids)
221
+ end
222
+ end
223
+
224
+ return content
225
+ end
226
+
227
+ def dot_edge_format(edge)
228
+ parent_id, child_id = edge
229
+ return("node#{parent_id} -> node#{child_id}")
230
+ end
231
+
232
+ def render_edges
233
+ @cache_graph_edges.map { |edge| dot_edge_format(edge) }.join("\n")
234
+ end
235
+
236
+ def render_graph_config
237
+ @theme.dig("graph") &&
238
+ "graph #{get_dot_config_string(@theme.dig("graph"))}"
239
+ end
240
+
241
+ def render_node_config
242
+ @node_attrs && "node #{get_dot_config_string(@node_attrs)};"
243
+ end
244
+
245
+ def render_edge_config
246
+ @edge_attrs && "edge #{get_dot_config_string(@edge_attrs)};"
247
+ end
248
+
249
+ def render_meta_info
250
+ meta_info = ["rankdir=#{@direction};"]
251
+ meta_info << "margin=#{@margin};" if @margin
252
+ meta_info << "label=\"#{@label}\";" if @label
253
+ meta_info << "labelloc=\"#{@labelloc}\";" if @labelloc
254
+ meta_info << "labeljust=\"#{@labeljust}\";" if @labeljust
255
+
256
+ meta_info.join("\n")
257
+ end
258
+ def generate_dot_template
259
+ dot_template = <<-DOT
260
+ digraph "virtual_call_graph"{
261
+
262
+ #{render_meta_info}
263
+ #{render_graph_config}
264
+ #{render_node_config}
265
+ #{render_edge_config}
266
+
267
+ #{render_nodes_and_clusters}
268
+
269
+ #{render_edges}
270
+
271
+ }
272
+ DOT
273
+
274
+ return dot_template
275
+ end
276
+
277
+ def create_dot_file(content)
278
+ dot_file_path = nil
279
+ dot_file = Tempfile.new("vistual_call")
280
+ dot_file_path = dot_file.path
281
+ dot_file.write content
282
+ dot_file.close
283
+ return dot_file_path
284
+ end
285
+
286
+ def output
287
+ get_call_tree_root()
288
+ get_call_tree_hashmap()
289
+
290
+ collect_graph_nodes_edges(@call_tree_root)
291
+ collect_group_cluster()
292
+ content = generate_dot_template()
293
+ dot_file_path = create_dot_file(content)
294
+ system("cat #{dot_file_path}") if @show_dot
295
+ system("dot #{dot_file_path} -T #{@format} -o '#{@output}'")
296
+ end
297
+ end
298
+ end
@@ -0,0 +1,39 @@
1
+ require_relative "./method_node"
2
+
3
+ module VistualCall
4
+ class MethodCallTree
5
+ attr_accessor :call_stack, :memo
6
+ def initialize
7
+ @call_stack = []
8
+ @memo = {}
9
+
10
+ root = BasicNode.new
11
+ @call_stack.push(root)
12
+ memo_it(root.node_id, root)
13
+ end
14
+
15
+ def memo_it(node_id, payload)
16
+ @memo[node_id] = payload unless @memo.key?(node_id)
17
+ return @memo[node_id]
18
+ end
19
+
20
+ def dispatch_call(method_info)
21
+ # Memo: 构建树的call、return 不应该被跳过、修改,来保持对应关系,可以还原起调用树。过滤工作应该在消费数据的层面
22
+ node = MethodNode.new(method_info)
23
+ node_id = node.node_id
24
+ memo_it(node_id, node)
25
+
26
+ @call_stack.push(node)
27
+ end
28
+
29
+ def dispatch_return(method_info = {})
30
+ match_call_method = @call_stack.pop
31
+
32
+ match_call_method_parent = @call_stack.last
33
+ if match_call_method_parent
34
+ match_call_method.parent_node_id = match_call_method_parent.node_id
35
+ match_call_method_parent.add_child(match_call_method)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,48 @@
1
+ module VistualCall
2
+ StartNodeID = 1
3
+
4
+ class BasicNode
5
+ attr_accessor :node_id, :method_name, :parent_node_id, :children
6
+ def initialize(method_name = nil)
7
+ @node_id = StartNodeID
8
+ @method_name = method_name || "Start"
9
+ @parent_node_id = nil
10
+ @children = []
11
+ end
12
+
13
+ def add_child(child)
14
+ @children << child
15
+ end
16
+ end
17
+
18
+ class MethodNode
19
+ @@instance_count = StartNodeID
20
+
21
+ def self.instance_count
22
+ @@instance_count
23
+ end
24
+
25
+ def self.get_method_name(node_info)
26
+ "#{node_info.defined_class}##{node_info.method_id}"
27
+ end
28
+
29
+ attr_accessor :node_id, :method_name, :parent_node_id, :children
30
+ def initialize(method_info)
31
+ @@instance_count += 1
32
+ @node_id = @@instance_count
33
+ @parent_node_id = nil
34
+
35
+ # Must copy TracePointer information, because TracePointer cannot access outside.
36
+ %i[path event lineno method_id defined_class parameters].each do |attr|
37
+ instance_variable_set("@#{attr}", method_info.send(attr))
38
+ self.class.send(:attr_accessor, attr)
39
+ end
40
+ @method_name = self.class.get_method_name(self)
41
+ @children = []
42
+ end
43
+
44
+ def add_child(child)
45
+ @children << child
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,29 @@
1
+ require_relative "./method_call_tree"
2
+
3
+ module VistualCall
4
+ class Tracer
5
+ attr_accessor :call_tree
6
+ def initialize(options = {})
7
+ @events = %i[call return]
8
+ @call_tree = MethodCallTree.new
9
+
10
+ @trace_point =
11
+ TracePoint.new(*@events) do |tp|
12
+ # TODO: stop & disable situations
13
+ @call_tree.send("dispatch_#{tp.event}", tp)
14
+ end
15
+ end
16
+
17
+ def track(&block)
18
+ @trace_point.enable { block.call }
19
+ end
20
+
21
+ def call_tree_root
22
+ @call_tree.call_stack.first || nil
23
+ end
24
+
25
+ def call_tree_hashmap
26
+ @call_tree.memo || {}
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VistualCall
4
+ VERSION = "1.0.0"
5
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "vistual_call/version"
4
+ require_relative "./vistual_call/graph"
5
+
6
+ module VistualCall
7
+ class VistualCallError < StandardError
8
+ end
9
+
10
+ class << self
11
+ def trace(options = {})
12
+ unless block_given?
13
+ puts "Block required!"
14
+ return
15
+ end
16
+
17
+ proxy = ::VistualCall::Graph.new(options)
18
+ proxy.track { yield }
19
+ proxy.output
20
+ end
21
+ end
22
+ end
data/read_yaml.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "yaml"
2
+ require "json"
3
+
4
+ data = YAML.load_file("./theme.yml", aliases: true)
5
+
6
+ puts JSON.pretty_generate(data)
@@ -0,0 +1,4 @@
1
+ module VistualCall
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
data/theme.yml ADDED
@@ -0,0 +1,86 @@
1
+ # Setting your theme
2
+ use_theme: "sky"
3
+
4
+
5
+ # Theme basic settings
6
+ basic_node: &basic_node
7
+ fontname: "wqy-microhei"
8
+ shape: "box"
9
+ peripheries: 1
10
+ style: "rounded, filled"
11
+
12
+ # Theme list
13
+ lemon:
14
+ name: "柠檬黄主题"
15
+
16
+ # 说明
17
+ # color 非填充是边框色、填充就是节点背景色
18
+ # fontcolor 是文字颜色
19
+
20
+ # 画布属性
21
+ graph:
22
+ bgcolor: "#eff1f3" # 全局背景
23
+
24
+ # 节点
25
+ node_attrs: &lemon_node
26
+ <<: *basic_node
27
+ color: "#696773"
28
+ fontcolor: "#fcfcfc"
29
+
30
+ # 高亮节点
31
+ node_warn_attrs:
32
+ <<: *basic_node
33
+ fontcolor: "#ffffff"
34
+ color: "#f19b97"
35
+
36
+ # 边
37
+ edge_attrs:
38
+ color: "#696773"
39
+
40
+ # 子图容器
41
+ cluster:
42
+ style: "filled"
43
+ color: "#e3efd7"
44
+
45
+ # 子图节点
46
+ cluster_node:
47
+ color: "#f4b184"
48
+ fontcolor: "#ffffff"
49
+
50
+
51
+ sky:
52
+ name: "天真蓝主题"
53
+
54
+ # 说明
55
+ # color 非填充是边框色、填充就是节点背景色
56
+ # fontcolor 是文字颜色
57
+
58
+ # 画布背景
59
+ graph:
60
+ bgcolor: "#ffffff" # 全局背景
61
+
62
+ # 节点
63
+ node_attrs:
64
+ <<: *basic_node
65
+ color: "#deeaf6"
66
+ fontcolor: "#000000"
67
+
68
+ # 高亮节点
69
+ node_warn_attrs:
70
+ <<: *basic_node
71
+ fontcolor: "#000000"
72
+ color: "#ffda65"
73
+
74
+ # 边
75
+ edge_attrs:
76
+ color: "#021d57"
77
+
78
+ # 子图
79
+ cluster:
80
+ style: "filled"
81
+ color: "#deeaf6"
82
+
83
+ # 子图节点
84
+ cluster_node:
85
+ color: "#5a9ad7"
86
+ fontcolor: "#ffffff"
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/vistual_call/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "vistual_call"
7
+ spec.version = VistualCall::VERSION
8
+ spec.authors = ["Mark24"]
9
+ spec.email = ["mark.zhangyoung@qq.com"]
10
+
11
+ spec.summary = "Export vistual call graph"
12
+ spec.description = "Make export vistual call graph easy to use."
13
+ spec.homepage = "https://github.com/Mark24Code/vistual_call"
14
+ spec.required_ruby_version = ">= 2.6.0"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata[
18
+ "source_code_uri"
19
+ ] = "https://github.com/Mark24Code/vistual_call"
20
+ spec.metadata["changelog_uri"] = "https://github.com/Mark24Code/vistual_call"
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files =
25
+ Dir.chdir(__dir__) do
26
+ `git ls-files -z`.split("\x0")
27
+ .reject do |f|
28
+ (File.expand_path(f) == __FILE__) ||
29
+ f.start_with?(
30
+ *%w[bin/ test/ spec/ features/ .git .circleci appveyor]
31
+ )
32
+ end
33
+ end
34
+ spec.bindir = "exe"
35
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
36
+ spec.require_paths = ["lib"]
37
+ spec.licenses = ["MIT"]
38
+
39
+ # Uncomment to register a new dependency of your gem
40
+ # spec.add_dependency "example-gem", "~> 1.0"
41
+
42
+ # For more information and examples about making a new gem, check out our
43
+ # guide at: https://bundler.io/guides/creating_gem.html
44
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vistual_call
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Mark24
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-04-24 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Make export vistual call graph easy to use.
14
+ email:
15
+ - mark.zhangyoung@qq.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".DS_Store"
21
+ - Gemfile
22
+ - Gemfile.lock
23
+ - LICENSE.txt
24
+ - README.md
25
+ - Rakefile
26
+ - example/example.png
27
+ - example/sample.rb
28
+ - example/sinatra.png
29
+ - example/sinatra.rb
30
+ - lib/vistual_call.rb
31
+ - lib/vistual_call/graph.rb
32
+ - lib/vistual_call/method_call_tree.rb
33
+ - lib/vistual_call/method_node.rb
34
+ - lib/vistual_call/tracer.rb
35
+ - lib/vistual_call/version.rb
36
+ - read_yaml.rb
37
+ - sig/vistual_call.rbs
38
+ - theme.yml
39
+ - vistual_call.gemspec
40
+ homepage: https://github.com/Mark24Code/vistual_call
41
+ licenses:
42
+ - MIT
43
+ metadata:
44
+ homepage_uri: https://github.com/Mark24Code/vistual_call
45
+ source_code_uri: https://github.com/Mark24Code/vistual_call
46
+ changelog_uri: https://github.com/Mark24Code/vistual_call
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: 2.6.0
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ requirements: []
62
+ rubygems_version: 3.4.12
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: Export vistual call graph
66
+ test_files: []