hyperlayer 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/MIT-LICENSE +20 -0
- data/README.md +38 -0
- data/Rakefile +8 -0
- data/app/assets/config/manifest.js +7 -0
- data/app/assets/stylesheets/application.bootstrap.scss +2 -0
- data/app/assets/stylesheets/hyperlayer/app.css +47 -0
- data/app/assets/stylesheets/hyperlayer/application.css +18774 -0
- data/app/assets/stylesheets/hyperlayer/prism.css +3 -0
- data/app/controllers/hyperlayer/application_controller.rb +4 -0
- data/app/controllers/hyperlayer/events_controller.rb +95 -0
- data/app/controllers/hyperlayer/example_groups_controller.rb +9 -0
- data/app/controllers/hyperlayer/paths_controller.rb +18 -0
- data/app/controllers/hyperlayer/runs_controller.rb +7 -0
- data/app/controllers/hyperlayer/specs_controller.rb +8 -0
- data/app/javascript/controllers/application.js +9 -0
- data/app/javascript/controllers/index.js +11 -0
- data/app/javascript/hyperlayer/application.js +7 -0
- data/app/javascript/hyperlayer/prism.js +8 -0
- data/app/models/application_record.rb +3 -0
- data/app/models/hyperlayer/event.rb +28 -0
- data/app/models/hyperlayer/example_group.rb +6 -0
- data/app/models/hyperlayer/path.rb +35 -0
- data/app/models/hyperlayer/run.rb +5 -0
- data/app/models/hyperlayer/spec.rb +8 -0
- data/app/services/build_file_overlay.rb +26 -0
- data/app/services/callable.rb +129 -0
- data/app/services/file_builder.rb +260 -0
- data/app/services/find_method_from_event.rb +65 -0
- data/app/services/find_method_in_path.rb +98 -0
- data/app/services/generate_buffer.rb +116 -0
- data/app/services/import_events.rb +56 -0
- data/app/services/split_methods.rb +50 -0
- data/app/services/test_one.rb +34 -0
- data/app/services/update_code.rb +111 -0
- data/app/views/hyperlayer/events/_code.html.erb +36 -0
- data/app/views/hyperlayer/events/_event.html.erb +15 -0
- data/app/views/hyperlayer/events/_tree-text.html.erb +22 -0
- data/app/views/hyperlayer/events/_tree.html.erb +15 -0
- data/app/views/hyperlayer/events/index.html.erb +41 -0
- data/app/views/hyperlayer/example_groups/index.html.erb +25 -0
- data/app/views/hyperlayer/paths/_path.html.erb +52 -0
- data/app/views/hyperlayer/paths/index.html.erb +5 -0
- data/app/views/hyperlayer/paths/show.html.erb +3 -0
- data/app/views/hyperlayer/runs/index.html.erb +23 -0
- data/app/views/hyperlayer/specs/index.html.erb +24 -0
- data/app/views/layouts/_nav.html.erb +8 -0
- data/app/views/layouts/hyperlayer/application.html.erb +30 -0
- data/app/views/layouts/hyperlayer/mailer.html.erb +13 -0
- data/app/views/layouts/hyperlayer/mailer.text.erb +1 -0
- data/config/initializers/engine_migrations.rb +3 -0
- data/config/routes.rb +11 -0
- data/db/migrate/20231009165755_create_hyperlayer_events.rb +19 -0
- data/db/migrate/20231009165919_create_hyperlayer_example_groups.rb +12 -0
- data/db/migrate/20231009165952_create_hyperlayer_runs.rb +9 -0
- data/db/migrate/20231009170101_create_hyperlayer_paths.rb +10 -0
- data/db/migrate/20231009170121_create_hyperlayer_specs.rb +12 -0
- data/lib/hyperlayer/engine.rb +22 -0
- data/lib/hyperlayer/method_tracer.rb +12 -0
- data/lib/hyperlayer/tracer.rb +78 -0
- data/lib/hyperlayer/version.rb +3 -0
- data/lib/hyperlayer.rb +15 -0
- data/lib/tasks/listen.rake +8 -0
- metadata +207 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
# require 'fast'
|
2
|
+
|
3
|
+
class UpdateCode < Parser::AST::Processor
|
4
|
+
include RuboCop::AST::Traversal
|
5
|
+
|
6
|
+
def self.call(id, code, method)
|
7
|
+
new.call(id, code, method)
|
8
|
+
end
|
9
|
+
|
10
|
+
LOCATION_MAP = {
|
11
|
+
Send: :expression,
|
12
|
+
MethodDefinition: :name
|
13
|
+
}
|
14
|
+
|
15
|
+
def call(id, code, method)
|
16
|
+
path = Path.find(id)
|
17
|
+
code = code.presence || File.read(path.path)
|
18
|
+
|
19
|
+
method = method.to_sym
|
20
|
+
|
21
|
+
# First find all instances
|
22
|
+
|
23
|
+
source = RuboCop::ProcessedSource.new(code, 2.7)
|
24
|
+
nodes = FindMethod.call(
|
25
|
+
source: source,
|
26
|
+
name: method,
|
27
|
+
method_types: [:def, :send]
|
28
|
+
)
|
29
|
+
|
30
|
+
nodes.each do |node|
|
31
|
+
# Then look again and change them all one by one
|
32
|
+
source = RuboCop::ProcessedSource.new(code, 2.7)
|
33
|
+
node = FindMethod.call(
|
34
|
+
source: source,
|
35
|
+
name: method,
|
36
|
+
method_types: [:def, :send]
|
37
|
+
).first
|
38
|
+
|
39
|
+
type = node.loc.class.to_s.split("::").last.to_sym
|
40
|
+
|
41
|
+
ReplaceCode.call(
|
42
|
+
code: code,
|
43
|
+
node: node,
|
44
|
+
location: LOCATION_MAP[type],
|
45
|
+
with: 'geoff',
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
code
|
50
|
+
|
51
|
+
# Add "after/before" which allows to position
|
52
|
+
# AddMethod.call(code: code, pos: 150)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
class FindMethod
|
58
|
+
include Callable
|
59
|
+
expects :source, :name, :method_types
|
60
|
+
|
61
|
+
def call
|
62
|
+
source.ast
|
63
|
+
.each_node
|
64
|
+
.map { |n| n if method_types.include?(n.type) && n.method_name == name }
|
65
|
+
.compact
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class AddMethod
|
70
|
+
include Callable
|
71
|
+
expects :code, :pos
|
72
|
+
|
73
|
+
def call
|
74
|
+
code.insert(pos, "def code \n inserted.here! \n end")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class ShowCode
|
79
|
+
include Callable
|
80
|
+
expects :code, :node, :location, :with
|
81
|
+
|
82
|
+
def call
|
83
|
+
code[begin_pos...end_pos]
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
delegate :begin_pos, :end_pos, to: :expression
|
89
|
+
|
90
|
+
def expression
|
91
|
+
node.loc.send(location)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class ReplaceCode
|
96
|
+
include Callable
|
97
|
+
expects :code, :node, :location, :with
|
98
|
+
|
99
|
+
def call
|
100
|
+
code[begin_pos...end_pos] = with
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
delegate :begin_pos, :end_pos, to: :expression
|
106
|
+
|
107
|
+
def expression
|
108
|
+
node.loc.send(location)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
<div class="card node">
|
2
|
+
<div class="card-header">
|
3
|
+
<div class="font-monospace text-body-secondary text-uppercase small">
|
4
|
+
<%= event.defined_class %>
|
5
|
+
</div>
|
6
|
+
<div>
|
7
|
+
#<%= event.method %>
|
8
|
+
</div>
|
9
|
+
<hr />
|
10
|
+
<div class="font-monospace text-body-secondary text-uppercase small">
|
11
|
+
<%= event.arguments %>
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<div class="card-body">
|
16
|
+
<pre>
|
17
|
+
<code class="language-ruby">
|
18
|
+
<%= SplitMethods.call(event) %>
|
19
|
+
</code>
|
20
|
+
</pre>
|
21
|
+
|
22
|
+
<% if false %>
|
23
|
+
<pre style='text-wrap: wrap;'>
|
24
|
+
<code>
|
25
|
+
<%= event.variables %>
|
26
|
+
</code>
|
27
|
+
</pre>
|
28
|
+
<% end %>
|
29
|
+
|
30
|
+
<pre style='text-wrap: wrap;'>
|
31
|
+
<code>
|
32
|
+
<%= event.return_value_events&.return_value.presence || 'nil' %>
|
33
|
+
</code>
|
34
|
+
</pre>
|
35
|
+
</div>
|
36
|
+
</div>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<div class="card node">
|
2
|
+
<div class="card-body">
|
3
|
+
<pre>
|
4
|
+
<code class="language-ruby">
|
5
|
+
<%= SplitMethods.call(event) %>
|
6
|
+
</code>
|
7
|
+
</pre>
|
8
|
+
|
9
|
+
<pre style='text-wrap: wrap;'>
|
10
|
+
<code>
|
11
|
+
<%= event.return_value_events&.return_value.presence || 'nil' %>
|
12
|
+
</code>
|
13
|
+
</pre>
|
14
|
+
</div>
|
15
|
+
</div>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<% branches.each do |branch| %>
|
2
|
+
<ul>
|
3
|
+
<% branch.each do |event, sub_branches| %>
|
4
|
+
<li>
|
5
|
+
<div class='text-muted' style='font-size: 12px'><%= event.defined_class %></div>
|
6
|
+
|
7
|
+
<% if true %>
|
8
|
+
<a href="#" data-bs-toggle="modal" data-bs-target="#staticBackdrop" data-bs-anchor="<%= "#{event.defined_class}::#{event.method}" %>" class="noStyle">#<%= event.method %></a>
|
9
|
+
<% else %>
|
10
|
+
<a href="#" data-bs-toggle="offcanvas" data-bs-target="#offcanvasRight" data-bs-anchor="<%= "#{event.defined_class}::#{event.method}" %>" class="noStyle">#<%= event.method %></a>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<code><%= event.arguments %></code><br />
|
14
|
+
<code>→ <%= event.return_value_events&.return_value.presence %></code>
|
15
|
+
</li>
|
16
|
+
|
17
|
+
<% if sub_branches.present? %>
|
18
|
+
<%= render partial: 'tree-text', locals: { branches: sub_branches } %>
|
19
|
+
<% end %>
|
20
|
+
<% end %>
|
21
|
+
</ul>
|
22
|
+
<% end %>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<% branches.each do |branch| %>
|
2
|
+
<div class="tree-node">
|
3
|
+
<% branch.each do |event, sub_branches| %>
|
4
|
+
<div class="node-content" id="<%= "#{event.defined_class}::#{event.method}" %>">
|
5
|
+
<div class="node-code">
|
6
|
+
<%= render "code", event: event %>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<% if sub_branches.present? %>
|
11
|
+
<%= render partial: 'tree', locals: { branches: sub_branches } %>
|
12
|
+
<% end %>
|
13
|
+
<% end %>
|
14
|
+
</div>
|
15
|
+
<% end %>
|
@@ -0,0 +1,41 @@
|
|
1
|
+
<div class="container">
|
2
|
+
<div class='row'>
|
3
|
+
<div class="col">
|
4
|
+
<%= render 'tree-text', branches: @tree %>
|
5
|
+
</div>
|
6
|
+
</div>
|
7
|
+
</div>
|
8
|
+
|
9
|
+
<!-- Modal -->
|
10
|
+
<div class="modal fade" id="staticBackdrop" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
11
|
+
<div class="modal-dialog modal-xl">
|
12
|
+
<div class="modal-content">
|
13
|
+
<div class="modal-header">
|
14
|
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
15
|
+
</div>
|
16
|
+
<div class="modal-body">
|
17
|
+
<%= render 'tree', branches: @tree %>
|
18
|
+
</div>
|
19
|
+
<div class="modal-footer">
|
20
|
+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
21
|
+
<button type="button" class="btn btn-primary">Understood</button>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
|
27
|
+
<script>
|
28
|
+
document.addEventListener('DOMContentLoaded', (event) => {
|
29
|
+
document.getElementById('staticBackdrop').addEventListener('shown.bs.modal', function (event) {
|
30
|
+
var triggerElement = event.relatedTarget; // Element that triggered the modal
|
31
|
+
var anchorId = triggerElement.getAttribute('data-bs-anchor'); // Get the data-anchor attribute
|
32
|
+
var modalBody = this.querySelector('.modal-body');
|
33
|
+
var anchor = document.getElementById(anchorId);
|
34
|
+
if (anchor) {
|
35
|
+
setTimeout(() => {
|
36
|
+
anchor.scrollIntoView({ behavior: 'smooth' });
|
37
|
+
}, 100);
|
38
|
+
}
|
39
|
+
});
|
40
|
+
});
|
41
|
+
</script>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<div class="container-fluid">
|
2
|
+
<div class="row mt-2">
|
3
|
+
<div class="col-12">
|
4
|
+
<h5><%= @spec.location %></h5>
|
5
|
+
|
6
|
+
<table class="table">
|
7
|
+
<thead>
|
8
|
+
<tr>
|
9
|
+
<th scope="col">Example Groups</th>
|
10
|
+
</tr>
|
11
|
+
</thead>
|
12
|
+
<tbody>
|
13
|
+
<% @example_groups.each do |example_group| %>
|
14
|
+
<tr>
|
15
|
+
<td>
|
16
|
+
<%= link_to(example_group.description, run_spec_example_group_events_path(@run, params[:spec_id], example_group)) %><br />
|
17
|
+
<%= example_group.location %>
|
18
|
+
</td>
|
19
|
+
</tr>
|
20
|
+
<% end %>
|
21
|
+
</tbody>
|
22
|
+
</table>
|
23
|
+
</div>
|
24
|
+
</div>
|
25
|
+
</div>
|
@@ -0,0 +1,52 @@
|
|
1
|
+
<div class="row mt-2">
|
2
|
+
<div class="col-4">
|
3
|
+
<table class="table">
|
4
|
+
<thead>
|
5
|
+
<tr>
|
6
|
+
<th scope="col">Method</th>
|
7
|
+
<th scope="col">Type</th>
|
8
|
+
<th scope="col">Value</th>
|
9
|
+
<th scope="col">Line #</th>
|
10
|
+
</tr>
|
11
|
+
</thead>
|
12
|
+
<tbody>
|
13
|
+
<% path.events.distinct.order(line_number: :asc).each do |event| %>
|
14
|
+
<tr>
|
15
|
+
<td><%= event.method %></td>
|
16
|
+
<td><%= event.event_type %></td>
|
17
|
+
<td>
|
18
|
+
<code class='small text-wrap'>
|
19
|
+
<%= event.return_value %>
|
20
|
+
</code>
|
21
|
+
</td>
|
22
|
+
<td><%= event.line_number %></td>
|
23
|
+
</tr>
|
24
|
+
<% end %>
|
25
|
+
</tbody>
|
26
|
+
</table>
|
27
|
+
</div>
|
28
|
+
<div class="col-4">
|
29
|
+
<div class="card mb-4">
|
30
|
+
<div class="card-header">
|
31
|
+
<%= path.path.split('/')[5..-1].join('/') %> (<%= path.id %>)
|
32
|
+
</div>
|
33
|
+
|
34
|
+
<div class="card-body">
|
35
|
+
<pre><code class='language-ruby'><%= path.overlay.html_safe %></code></pre>
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
</div>
|
39
|
+
<div class="col-4">
|
40
|
+
<% if false %>
|
41
|
+
<div class="card mb-4">
|
42
|
+
<div class="card-header">
|
43
|
+
<%= path.path.split('/')[5..-1].join('/') %> (<%= path.id %>)
|
44
|
+
</div>
|
45
|
+
|
46
|
+
<div class="card-body">
|
47
|
+
<pre><code class='language-ruby'><%= path.overlay_code.html_safe %></code></pre>
|
48
|
+
</div>
|
49
|
+
</div>
|
50
|
+
<% end %>
|
51
|
+
</div>
|
52
|
+
</div>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<div class="container-fluid">
|
2
|
+
<div class="row mt-2">
|
3
|
+
<div class="col-8">
|
4
|
+
<table class="table">
|
5
|
+
<thead>
|
6
|
+
<tr>
|
7
|
+
<th scope="col">Spec</th>
|
8
|
+
</tr>
|
9
|
+
</thead>
|
10
|
+
<tbody>
|
11
|
+
<% @runs.each do |run| %>
|
12
|
+
<tr>
|
13
|
+
<td>
|
14
|
+
<%= link_to(run.process, run_specs_path(run)) %><br />
|
15
|
+
<%= run.specs.count %> specs
|
16
|
+
</td>
|
17
|
+
</tr>
|
18
|
+
<% end %>
|
19
|
+
</tbody>
|
20
|
+
</table>
|
21
|
+
</div>
|
22
|
+
</div>
|
23
|
+
</div>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<div class="container-fluid">
|
2
|
+
<div class="row mt-2">
|
3
|
+
<div class="col-8">
|
4
|
+
<table class="table">
|
5
|
+
<thead>
|
6
|
+
<tr>
|
7
|
+
<th scope="col">Spec</th>
|
8
|
+
</tr>
|
9
|
+
</thead>
|
10
|
+
<tbody>
|
11
|
+
<% @specs.each do |spec| %>
|
12
|
+
<tr>
|
13
|
+
<td>
|
14
|
+
<%= link_to(spec.description, run_spec_example_groups_path(@run, spec)) %><br />
|
15
|
+
<%= spec.location %><br />
|
16
|
+
<%= spec.example_groups.count %> example groups
|
17
|
+
</td>
|
18
|
+
</tr>
|
19
|
+
<% end %>
|
20
|
+
</tbody>
|
21
|
+
</table>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
</div>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<nav class="navbar navbar-expand-lg bg-light">
|
2
|
+
<div class="container-fluid">
|
3
|
+
<a class="navbar-brand" href="#">⚡️ Hyperlayer</a>
|
4
|
+
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
5
|
+
<span class="navbar-toggler-icon"></span>
|
6
|
+
</button>
|
7
|
+
</div>
|
8
|
+
</nav>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Hyperlayer</title>
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
|
+
<%= csrf_meta_tags %>
|
7
|
+
<%= csp_meta_tag %>
|
8
|
+
|
9
|
+
<%= stylesheet_link_tag 'hyperlayer/application', media: 'all', 'data-turbolinks-track': 'reload' %>
|
10
|
+
<%= stylesheet_link_tag 'hyperlayer/app', media: 'all', 'data-turbolinks-track': 'reload' %>
|
11
|
+
|
12
|
+
<link href="https://unpkg.com/prismjs@1.29.0/themes/prism.css" rel="stylesheet" />
|
13
|
+
<link href="https://unpkg.com/prismjs@1.29.0/plugins/line-highlight/prism-line-highlight.css" rel="stylesheet" />
|
14
|
+
</head>
|
15
|
+
|
16
|
+
<body>
|
17
|
+
<%= render 'layouts/nav' %>
|
18
|
+
|
19
|
+
<div class="container-fluid mt-4">
|
20
|
+
<div class="row align-items-start">
|
21
|
+
<%= yield %>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
|
25
|
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/prism.min.js"></script>
|
26
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
27
|
+
<script src="https://unpkg.com/prismjs@1.29.0/plugins/autoloader/prism-autoloader.js"></script>
|
28
|
+
<script src="https://unpkg.com/prismjs@1.29.0/plugins/line-highlight/prism-line-highlight.js"></script>
|
29
|
+
</body>
|
30
|
+
</html>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= yield %>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateHyperlayerEvents < ActiveRecord::Migration[7.0]
|
2
|
+
def change
|
3
|
+
create_table :hyperlayer_events do |t|
|
4
|
+
t.string :location
|
5
|
+
t.string :defined_class
|
6
|
+
t.string :event_type
|
7
|
+
t.string :method
|
8
|
+
t.text :return_value
|
9
|
+
t.integer :path_id
|
10
|
+
t.integer :line_number
|
11
|
+
t.jsonb :spec
|
12
|
+
t.integer :example_group_id
|
13
|
+
t.jsonb :arguments
|
14
|
+
t.jsonb :variables
|
15
|
+
|
16
|
+
t.timestamps
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Hyperlayer
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace Hyperlayer
|
4
|
+
|
5
|
+
initializer "hyperlayer.use_parent_app_database" do
|
6
|
+
database_config = Rails.configuration.database_configuration[Rails.env]
|
7
|
+
ActiveRecord::Base.establish_connection(database_config)
|
8
|
+
end
|
9
|
+
|
10
|
+
initializer "hyperlayer.load_migrations" do |app|
|
11
|
+
unless app.root.to_s.match?(config.root.to_s)
|
12
|
+
config.paths["db/migrate"].expanded.each do |expanded_path|
|
13
|
+
app.config.paths["db/migrate"] << expanded_path
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
initializer "hyperlayer.assets.precompile" do |app|
|
19
|
+
app.config.assets.precompile += %w( hyperlayer/application.css hyperlayer/app.css hyperlayer/prism.css hyperlayer/prism.js hyperlayer/application.js )
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# lib/hyperlayer/method_tracer.rb
|
2
|
+
|
3
|
+
class MethodTracer
|
4
|
+
def self.arguments_for(tp)
|
5
|
+
method_params = tp.self.method(tp.method_id).parameters
|
6
|
+
method_params.map do |type, name|
|
7
|
+
[name, tp.binding.local_variable_get(name)]
|
8
|
+
end.to_h
|
9
|
+
rescue => e
|
10
|
+
{} # Return an empty hash in case of errors
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# lib/hyperlayer/tracer.rb
|
2
|
+
|
3
|
+
module Hyperlayer
|
4
|
+
module Tracer
|
5
|
+
def self.trace_rspec!
|
6
|
+
trace = setup_trace
|
7
|
+
trace.enable
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.after(:suite) { trace.disable }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def self.setup_trace
|
17
|
+
TracePoint.new(:call, :return) do |tp|
|
18
|
+
next unless relevant_path?(tp.path)
|
19
|
+
|
20
|
+
event_data = extract_event_data(tp)
|
21
|
+
|
22
|
+
Redis.new(url: 'redis://localhost:6379').publish('events', event_data.to_json)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.relevant_path?(path)
|
27
|
+
path.include?('tembo')
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.extract_event_data(tp)
|
31
|
+
args = MethodTracer.arguments_for(tp)
|
32
|
+
|
33
|
+
event = {
|
34
|
+
path: tp.path,
|
35
|
+
line_number: tp.lineno,
|
36
|
+
defined_class: tp.defined_class.to_s,
|
37
|
+
event: tp.event,
|
38
|
+
method_id: tp.method_id,
|
39
|
+
arguments: args,
|
40
|
+
return_value: (tp.event == :return ? tp.return_value.to_s : nil)
|
41
|
+
}
|
42
|
+
|
43
|
+
# RSpec metadata extraction (as per your original code)
|
44
|
+
if defined?(RSpec) && RSpec.respond_to?(:current_example) && RSpec.current_example
|
45
|
+
metadata = Hashie::Mash.new(RSpec.current_example.metadata)
|
46
|
+
|
47
|
+
tree = []
|
48
|
+
tree << metadata.except(:example_group, :block)
|
49
|
+
tree << metadata.example_group.except(:parent_example_group, :block)
|
50
|
+
|
51
|
+
parent = metadata.example_group.parent_example_group
|
52
|
+
|
53
|
+
while parent
|
54
|
+
tree << parent.except(:parent_example_group, :block)
|
55
|
+
parent = parent.parent_example_group
|
56
|
+
end
|
57
|
+
|
58
|
+
tree.each do |branch|
|
59
|
+
branch['described_class'] = branch['described_class'].to_s
|
60
|
+
branch['description_args'] = [branch['description_args'].first.to_s]
|
61
|
+
|
62
|
+
if branch['execution_result']
|
63
|
+
branch['execution_result'] = branch['execution_result'].started_at.to_f
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
event[:spec] = {
|
68
|
+
process: Process.pid,
|
69
|
+
run: metadata.execution_result.started_at.to_f.to_s,
|
70
|
+
tree: tree
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
event[:return_value] = nil if event[:return_value]&.class != String
|
75
|
+
event
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/hyperlayer.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "hyperlayer/version"
|
2
|
+
require "hyperlayer/engine"
|
3
|
+
|
4
|
+
require 'json'
|
5
|
+
require 'redis'
|
6
|
+
require 'hashie'
|
7
|
+
require 'pry'
|
8
|
+
require 'hyperlayer/method_tracer'
|
9
|
+
require 'hyperlayer/tracer'
|
10
|
+
|
11
|
+
module Hyperlayer
|
12
|
+
def self.trace_rspec!
|
13
|
+
Tracer.trace_rspec!
|
14
|
+
end
|
15
|
+
end
|