Skip to content

Commit 7a19801

Browse files
Merge pull request #25 from patrick204nqh/develop
[Update] Mermaid diagram generator
2 parents 5314a5c + 1ce22ad commit 7a19801

39 files changed

+603
-275
lines changed

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
trace_viz (1.0.0)
4+
trace_viz (1.0.1)
55

66
GEM
77
remote: https://rubygems.org/

README.md

Lines changed: 63 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99

1010
TraceViz is a Ruby library designed to trace and visualize events executed in a block of code. It is useful for logging, debugging, and generating diagrams to understand code execution and flow.
1111

12-
> **Note:** The diagram generation feature is currently under development.
13-
1412
The gem allows you to customize how much detail you want to see, such as method calls, parameters, return values, and execution times.
1513

1614
## Demo
@@ -143,12 +141,14 @@ TraceViz.trace(
143141
},
144142
filters: [
145143
:exclude_internal_call,
146-
include_classes: [Example]
144+
include_classes: {
145+
classes: [Example]
146+
}
147147
],
148148
export: {
149149
enabled: true,
150150
path: "tmp",
151-
format: :txt,
151+
format: :txt, # Select :mermaid to export a mermaid diagram
152152
overwrite: false,
153153
}
154154
) do
@@ -159,8 +159,6 @@ end
159159

160160
</details>
161161

162-
**Output**
163-
164162
<details open>
165163
<summary>Sample Output</summary>
166164

@@ -178,45 +176,69 @@ Final result: 24
178176

179177
</details>
180178

179+
<details open>
180+
<summary>Sample Diagram</summary>
181+
182+
```mermaid
183+
sequenceDiagram
184+
box rgb(224, 236, 221) Example
185+
participant E as Example
186+
end
187+
E ->> E: perform_task(x: 5, y: 7)
188+
Note over E: 24
189+
activate E
190+
E ->> E: add_numbers(a: 5, b: 7)
191+
Note over E: 24
192+
activate E
193+
E ->> E: multiply_by_factor(value: 12, factor: 2)
194+
Note over E: 24
195+
deactivate E
196+
E ->> E: log_result(result: 24)
197+
Note over E: nil
198+
deactivate E
199+
```
200+
201+
</details>
202+
181203
### Configuration Options
182204

183205
TraceViz provides extensive configuration options to customize tracing behavior.
184206

185-
| Group | Option | Type | Default Value | Description |
186-
| ----------------- | -------------------------- | ---------------- | --------------------------- | -------------------------------------------------------------------------- |
187-
| `general` | `tab_size` | Integer | 2 | Number of spaces for indentation. |
188-
| | `mode` | Symbol | `:summary` | Display mode (`:summary` or `:verbose`). |
189-
| | `group_keys` | Array of Symbols | `[:event, :klass, :action]` | Keys to group similar outputs. |
190-
| | `show_indent` | Boolean | true | Enables visual indentation for nested calls. |
191-
| | `show_depth` | Boolean | true | Displays the depth level of the method call. |
192-
| | `max_display_depth` | Integer | 3 | Maximum depth of calls to display. |
193-
| | `show_method_name` | Boolean | true | Logs the name of the method being executed. |
194-
| `source_location` | `show` | Boolean | false | Logs the source file and line number for methods. |
195-
| | `truncate_length` | Integer | 100 | Maximum length of displayed source location information. |
196-
| `params` | `show` | Boolean | true | Logs method parameters. |
197-
| | `mode` | Symbol | `:name_and_value` | Parameter display mode (`:name`, `:value`, or `:name_and_value`). |
198-
| | `truncate_values` | Integer | 50 | Maximum length of parameter values to display. |
199-
| `result` | `show` | Boolean | true | Logs method return values. |
200-
| | `truncate_length` | Integer | 50 | Maximum length of return value logs. |
201-
| `execution` | `show_time` | Boolean | true | Logs execution time for methods. |
202-
| | `show_trace_events` | Array of Symbols | `[:call, :return]` | Specifies the trace events to log (e.g., `:call`, `:return`). |
203-
| `filters` | `:exclude_internal_call` | Symbol | N/A | Exclude internal Ruby calls. |
204-
| | `:exclude_default_classes` | Symbol | N/A | Skip logging standard library classes. |
205-
| | `:exclude_rails_framework` | Symbol | N/A | Ignore Rails framework classes and methods. |
206-
| | `include_classes` | Hash | N/A | Specify classes to include in tracing. |
207-
| | - `classes` | Array | [] | List of class names to include (e.g., `["ClassName"]`). |
208-
| | `exclude_classes` | Hash | N/A | Specify classes to exclude from tracing. |
209-
| | - `classes` | Array | [] | List of class names to exclude (e.g., `["ClassName"]`). |
210-
| | `include_gems` | Hash | N/A | Include specific gems or runtime application gems. |
211-
| | - `app_running` | Boolean | true | Include the code of the application. |
212-
| | - `app_path` | String | `Dir.pwd` | Path to the application (e.g., `Dir.pwd`). |
213-
| | - `gems` | Array | [] | List of gems to include (e.g., `["gem1", "gem2"]`). |
214-
| | `exclude_gems` | Hash | N/A | Exclude specified gems. |
215-
| | - `gems` | Array | [] | List of gems to exclude (e.g., `["excluded_gem"]`). |
216-
| `export` | `enabled` | Boolean | true | Enables or disables exporting of trace logs. |
217-
| | `path` | String | `"tmp"` | Directory for exported trace logs. |
218-
| | `format` | Symbol | `:txt` | Format for trace logs (`:txt`). `.json` and `.yaml` are under development. |
219-
| | `overwrite` | Boolean | false | Prevents overwriting of existing exported files. |
207+
| Group | Option | Type | Default Value | Description |
208+
| ----------------- | -------------------------- | ---------------- | --------------------------- | -------------------------------------------------------------------------------- |
209+
| `general` | `tab_size` | Integer | 2 | Number of spaces for indentation. |
210+
| | `mode` | Symbol | `:summary` | Display mode (`:summary` or `:verbose`). |
211+
| | `group_keys` | Array of Symbols | `[:event, :klass, :action]` | Keys to group similar outputs. |
212+
| | `show_indent` | Boolean | true | Enables visual indentation for nested calls. |
213+
| | `show_depth` | Boolean | true | Displays the depth level of the method call. |
214+
| | `max_display_depth` | Integer | 3 | Maximum depth of calls to display. |
215+
| | `show_method_name` | Boolean | true | Logs the name of the method being executed. |
216+
| `source_location` | `show` | Boolean | false | Logs the source file and line number for methods. |
217+
| | `truncate_length` | Integer | 100 | Maximum length of displayed source location information. |
218+
| `params` | `show` | Boolean | true | Logs method parameters. |
219+
| | `mode` | Symbol | `:name_and_value` | Parameter display mode (`:name`, `:value`, or `:name_and_value`). |
220+
| | `truncate_values` | Integer | 50 | Maximum length of parameter values to display. |
221+
| `result` | `show` | Boolean | true | Logs method return values. |
222+
| | `truncate_length` | Integer | 50 | Maximum length of return value logs. |
223+
| `execution` | `show_time` | Boolean | true | Logs execution time for methods. |
224+
| | `show_trace_events` | Array of Symbols | `[:call, :return]` | Specifies the trace events to log (e.g., `:call`, `:return`). |
225+
| `filters` | `:exclude_internal_call` | Symbol | N/A | Exclude internal Ruby calls. |
226+
| | `:exclude_default_classes` | Symbol | N/A | Skip logging standard library classes. |
227+
| | `:exclude_rails_framework` | Symbol | N/A | Ignore Rails framework classes and methods. |
228+
| | `include_classes` | Hash | N/A | Specify classes to include in tracing. |
229+
| | - `classes` | Array | [] | List of class names to include (e.g., `["ClassName"]`). |
230+
| | `exclude_classes` | Hash | N/A | Specify classes to exclude from tracing. |
231+
| | - `classes` | Array | [] | List of class names to exclude (e.g., `["ClassName"]`). |
232+
| | `include_gems` | Hash | N/A | Include specific gems or runtime application gems. |
233+
| | - `app_running` | Boolean | true | Include the code of the application. |
234+
| | - `app_path` | String | `Dir.pwd` | Path to the application (e.g., `Dir.pwd`). |
235+
| | - `gems` | Array | [] | List of gems to include (e.g., `["gem1", "gem2"]`). |
236+
| | `exclude_gems` | Hash | N/A | Exclude specified gems. |
237+
| | - `gems` | Array | [] | List of gems to exclude (e.g., `["excluded_gem"]`). |
238+
| `export` | `enabled` | Boolean | true | Enables or disables exporting of trace logs. |
239+
| | `path` | String | `"tmp"` | Directory for exported trace logs. |
240+
| | `format` | Symbol | `:txt` | Format for trace logs. Use :txt for plain text or :mermaid for mermaid diagrams. |
241+
| | `overwrite` | Boolean | false | Prevents overwriting of existing exported files. |
220242

221243
### Notes
222244

lib/trace_viz/builders/diagram/message_builder.rb

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# frozen_string_literal: true
22

33
require "trace_viz/managers/diagram/participant_manager"
4-
require "trace_viz/models/message"
4+
require "trace_viz/models"
55
require_relative "base_builder"
66

77
module TraceViz
@@ -14,60 +14,82 @@ def initialize(formatter, participants)
1414
@participants_manager = Managers::Diagram::ParticipantsManager.new(participants)
1515
end
1616

17-
def build(type, from, to, content)
18-
Models::Message.new(type: type, from: from, to: to, content: content)
19-
end
20-
2117
def build_call_message(from_trace, to_trace)
22-
return if from_trace.klass == to_trace.klass
18+
return if same_class?(from_trace, to_trace)
2319

2420
build(
2521
:call,
26-
participants_manager.find(from_trace.klass),
27-
participants_manager.find(to_trace.klass),
28-
formatter.format_call,
22+
from: participant_for(from_trace),
23+
to: participant_for(to_trace),
24+
content: formatter.format_call,
2925
)
3026
end
3127

3228
def build_return_message(from_trace, to_trace)
33-
return if from_trace.klass == to_trace.klass
29+
return if same_class?(from_trace, to_trace)
3430

3531
build(
3632
:return,
37-
participants_manager.find(from_trace.klass),
38-
participants_manager.find(to_trace.klass),
39-
formatter.format_return,
33+
from: participant_for(from_trace),
34+
to: participant_for(to_trace),
35+
content: formatter.format_return,
4036
)
4137
end
4238

4339
def build_loop_start_message(trace)
44-
build(:loop_start, nil, nil, "#{trace.count} calls")
40+
build(:loop_start, content: "#{trace.count} calls")
4541
end
4642

4743
def build_loop_end_message
48-
build(:loop_end, nil, nil, "")
44+
build(:loop_end)
4945
end
5046

5147
def build_activate_message(trace)
52-
build(:activate, nil, participants_manager.find(trace.klass), "")
48+
build(:activate, to: participant_for(trace))
5349
end
5450

5551
def build_deactivate_message(trace)
56-
build(:deactivate, nil, participants_manager.find(trace.klass), "")
52+
build(:deactivate, to: participant_for(trace))
5753
end
5854

5955
def build_internal_message(trace)
6056
build(
6157
:call,
62-
participants_manager.find(trace.klass),
63-
participants_manager.find(trace.klass),
64-
trace.action,
58+
from: participant_for(trace),
59+
to: participant_for(trace),
60+
content: formatter.format_internal_message(trace),
61+
)
62+
end
63+
64+
def build_note(trace)
65+
build(
66+
:note,
67+
from: participant_for(trace),
68+
to: participant_for(trace),
69+
content: formatter.format_result(trace),
6570
)
6671
end
6772

6873
private
6974

7075
attr_reader :formatter, :participants_manager
76+
77+
def build(type, from: nil, to: nil, content: "")
78+
Models::Message.new(
79+
type: type,
80+
from: from,
81+
to: to,
82+
content: content,
83+
)
84+
end
85+
86+
def same_class?(from_trace, to_trace)
87+
from_trace.klass == to_trace.klass
88+
end
89+
90+
def participant_for(trace)
91+
participants_manager.find(trace.klass)
92+
end
7193
end
7294
end
7395
end

lib/trace_viz/builders/diagram/sequence_builder.rb

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# frozen_string_literal: true
22

33
require "trace_viz/models/diagram"
4-
require "trace_viz/extractors/diagram/participant_extractor"
4+
require "trace_viz/extractors/diagram/box_extractor"
55
require "trace_viz/extractors/diagram/message_extractor"
66
require_relative "base_builder"
77

@@ -15,20 +15,35 @@ def initialize(collector)
1515
end
1616

1717
def build
18-
diagram = Models::Diagram.new
18+
Models::Diagram.new.tap do |diagram|
19+
add_boxes_to(diagram)
20+
add_messages_to(diagram)
21+
end
22+
end
1923

20-
participants = Extractors::Diagram::ParticipantExtractor.new(collector).extract
21-
messages = Extractors::Diagram::MessageExtractor.new(collector, participants).extract
24+
private
2225

23-
participants.each { |p| diagram.add_participant(p) }
24-
messages.each { |m| diagram.add_message(m) }
26+
attr_reader :collector
2527

26-
diagram
28+
def add_boxes_to(diagram)
29+
boxes.each { |box| diagram.add_box(box) }
2730
end
2831

29-
private
32+
def add_messages_to(diagram)
33+
messages.each { |message| diagram.add_message(message) }
34+
end
3035

31-
attr_reader :collector
36+
def boxes
37+
@boxes ||= Extractors::Diagram::BoxExtractor.new(collector).extract
38+
end
39+
40+
def participants
41+
@participants ||= boxes.flat_map(&:participants)
42+
end
43+
44+
def messages
45+
@messages ||= Extractors::Diagram::MessageExtractor.new(collector, participants).extract
46+
end
3247
end
3348
end
3449
end

lib/trace_viz/collectors/filters/base_class_filter.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ def initialize(classes:)
1717

1818
def normalize_class_name(klass)
1919
# Normalize class/module names to handle dynamic/nested representations
20-
klass.to_s.gsub(/^#<Class:/, "").gsub(/>$/, "").strip
20+
klass.to_s
21+
.gsub(/^#<Class/, "<Class")
22+
.strip
2123
end
2224

2325
def matches_hierarchy?(klass_name, target_name)

lib/trace_viz/config/validator.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def validate_params(value)
3838
end
3939

4040
def validate_result(value)
41-
if value[:truncate_length] && !value[:truncate_length].is_a?(Integer)
41+
if value[:truncate_value] && !value[:truncate_value].is_a?(Integer)
4242
raise ArgumentError, "Truncate values must be a positive integer."
4343
end
4444
end

0 commit comments

Comments
 (0)