flows 0.2.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/{build.yml → test.yml} +5 -10
- data/.gitignore +9 -1
- data/.mdlrc +1 -1
- data/.reek.yml +54 -0
- data/.rubocop.yml +26 -7
- data/.rubocop_todo.yml +27 -0
- data/.ruby-version +1 -1
- data/.yardopts +1 -0
- data/CHANGELOG.md +81 -0
- data/Gemfile +0 -6
- data/README.md +167 -363
- data/Rakefile +35 -1
- data/bin/.rubocop.yml +5 -0
- data/bin/all_the_errors +55 -0
- data/bin/benchmark +73 -105
- data/bin/benchmark_cli/compare.rb +118 -0
- data/bin/benchmark_cli/compare/a_plus_b.rb +22 -0
- data/bin/benchmark_cli/compare/base.rb +45 -0
- data/bin/benchmark_cli/compare/command.rb +47 -0
- data/bin/benchmark_cli/compare/ten_steps.rb +22 -0
- data/bin/benchmark_cli/examples.rb +23 -0
- data/bin/benchmark_cli/examples/.rubocop.yml +22 -0
- data/bin/benchmark_cli/examples/a_plus_b/dry_do.rb +23 -0
- data/bin/benchmark_cli/examples/a_plus_b/dry_transaction.rb +17 -0
- data/bin/benchmark_cli/examples/a_plus_b/flows_do.rb +22 -0
- data/bin/benchmark_cli/examples/a_plus_b/flows_railway.rb +13 -0
- data/bin/benchmark_cli/examples/a_plus_b/flows_scp.rb +13 -0
- data/bin/benchmark_cli/examples/a_plus_b/flows_scp_mut.rb +13 -0
- data/bin/benchmark_cli/examples/a_plus_b/flows_scp_oc.rb +21 -0
- data/bin/benchmark_cli/examples/a_plus_b/trailblazer.rb +15 -0
- data/bin/benchmark_cli/examples/ten_steps/dry_do.rb +70 -0
- data/bin/benchmark_cli/examples/ten_steps/dry_transaction.rb +64 -0
- data/bin/benchmark_cli/examples/ten_steps/flows_do.rb +69 -0
- data/bin/benchmark_cli/examples/ten_steps/flows_railway.rb +58 -0
- data/bin/benchmark_cli/examples/ten_steps/flows_scp.rb +58 -0
- data/bin/benchmark_cli/examples/ten_steps/flows_scp_mut.rb +58 -0
- data/bin/benchmark_cli/examples/ten_steps/flows_scp_oc.rb +66 -0
- data/bin/benchmark_cli/examples/ten_steps/trailblazer.rb +60 -0
- data/bin/benchmark_cli/helpers.rb +12 -0
- data/bin/benchmark_cli/ruby.rb +15 -0
- data/bin/benchmark_cli/ruby/command.rb +38 -0
- data/bin/benchmark_cli/ruby/method_exec.rb +71 -0
- data/bin/benchmark_cli/ruby/self_class.rb +69 -0
- data/bin/benchmark_cli/ruby/structs.rb +90 -0
- data/bin/console +1 -0
- data/bin/docserver +7 -0
- data/bin/errors +138 -0
- data/bin/errors_cli/contract_error_demo.rb +49 -0
- data/bin/errors_cli/di_error_demo.rb +38 -0
- data/bin/errors_cli/flow_error_demo.rb +22 -0
- data/bin/errors_cli/flows_router_error_demo.rb +15 -0
- data/bin/errors_cli/interface_error_demo.rb +17 -0
- data/bin/errors_cli/oc_error_demo.rb +40 -0
- data/bin/errors_cli/railway_error_demo.rb +10 -0
- data/bin/errors_cli/result_error_demo.rb +13 -0
- data/bin/errors_cli/scp_error_demo.rb +17 -0
- data/docs/README.md +3 -187
- data/docs/_sidebar.md +0 -24
- data/docs/index.html +1 -1
- data/flows.gemspec +27 -2
- data/forspell.dict +9 -0
- data/lefthook.yml +9 -0
- data/lib/flows.rb +11 -5
- data/lib/flows/contract.rb +402 -0
- data/lib/flows/contract/array.rb +55 -0
- data/lib/flows/contract/case_eq.rb +43 -0
- data/lib/flows/contract/compose.rb +77 -0
- data/lib/flows/contract/either.rb +53 -0
- data/lib/flows/contract/error.rb +24 -0
- data/lib/flows/contract/hash.rb +75 -0
- data/lib/flows/contract/hash_of.rb +70 -0
- data/lib/flows/contract/helpers.rb +22 -0
- data/lib/flows/contract/predicate.rb +34 -0
- data/lib/flows/contract/transformer.rb +50 -0
- data/lib/flows/contract/tuple.rb +70 -0
- data/lib/flows/flow.rb +96 -7
- data/lib/flows/flow/errors.rb +29 -0
- data/lib/flows/flow/node.rb +132 -0
- data/lib/flows/flow/router.rb +29 -0
- data/lib/flows/flow/router/custom.rb +59 -0
- data/lib/flows/flow/router/errors.rb +11 -0
- data/lib/flows/flow/router/simple.rb +25 -0
- data/lib/flows/plugin.rb +15 -0
- data/lib/flows/plugin/dependency_injector.rb +170 -0
- data/lib/flows/plugin/dependency_injector/dependency.rb +24 -0
- data/lib/flows/plugin/dependency_injector/dependency_definition.rb +16 -0
- data/lib/flows/plugin/dependency_injector/dependency_list.rb +55 -0
- data/lib/flows/plugin/dependency_injector/errors.rb +58 -0
- data/lib/flows/plugin/implicit_init.rb +45 -0
- data/lib/flows/plugin/interface.rb +84 -0
- data/lib/flows/plugin/output_contract.rb +85 -0
- data/lib/flows/plugin/output_contract/dsl.rb +48 -0
- data/lib/flows/plugin/output_contract/errors.rb +74 -0
- data/lib/flows/plugin/output_contract/wrapper.rb +55 -0
- data/lib/flows/plugin/profiler.rb +114 -0
- data/lib/flows/plugin/profiler/injector.rb +35 -0
- data/lib/flows/plugin/profiler/report.rb +48 -0
- data/lib/flows/plugin/profiler/report/events.rb +43 -0
- data/lib/flows/plugin/profiler/report/flat.rb +41 -0
- data/lib/flows/plugin/profiler/report/flat/method_report.rb +80 -0
- data/lib/flows/plugin/profiler/report/raw.rb +15 -0
- data/lib/flows/plugin/profiler/report/tree.rb +98 -0
- data/lib/flows/plugin/profiler/report/tree/calculated_node.rb +116 -0
- data/lib/flows/plugin/profiler/report/tree/node.rb +34 -0
- data/lib/flows/plugin/profiler/wrapper.rb +53 -0
- data/lib/flows/railway.rb +140 -34
- data/lib/flows/railway/dsl.rb +8 -18
- data/lib/flows/railway/errors.rb +8 -12
- data/lib/flows/railway/step.rb +24 -0
- data/lib/flows/railway/step_list.rb +38 -0
- data/lib/flows/result.rb +188 -2
- data/lib/flows/result/do.rb +158 -16
- data/lib/flows/result/err.rb +12 -6
- data/lib/flows/result/errors.rb +29 -17
- data/lib/flows/result/helpers.rb +25 -3
- data/lib/flows/result/ok.rb +12 -6
- data/lib/flows/shared_context_pipeline.rb +342 -0
- data/lib/flows/shared_context_pipeline/dsl.rb +12 -0
- data/lib/flows/shared_context_pipeline/dsl/callbacks.rb +35 -0
- data/lib/flows/shared_context_pipeline/dsl/tracks.rb +52 -0
- data/lib/flows/shared_context_pipeline/errors.rb +17 -0
- data/lib/flows/shared_context_pipeline/mutation_step.rb +30 -0
- data/lib/flows/shared_context_pipeline/router_definition.rb +21 -0
- data/lib/flows/shared_context_pipeline/step.rb +55 -0
- data/lib/flows/shared_context_pipeline/track.rb +54 -0
- data/lib/flows/shared_context_pipeline/track_list.rb +51 -0
- data/lib/flows/shared_context_pipeline/wrap.rb +73 -0
- data/lib/flows/util.rb +17 -0
- data/lib/flows/util/inheritable_singleton_vars.rb +86 -0
- data/lib/flows/util/inheritable_singleton_vars/dup_strategy.rb +100 -0
- data/lib/flows/util/inheritable_singleton_vars/isolation_strategy.rb +91 -0
- data/lib/flows/util/prepend_to_class.rb +191 -0
- data/lib/flows/version.rb +1 -1
- metadata +253 -38
- data/Gemfile.lock +0 -174
- data/bin/demo +0 -66
- data/bin/examples.rb +0 -195
- data/bin/profile_10steps +0 -106
- data/bin/ruby_benchmarks +0 -26
- data/docs/CNAME +0 -1
- data/docs/contributing/benchmarks_profiling.md +0 -3
- data/docs/contributing/local_development.md +0 -3
- data/docs/flow/direct_usage.md +0 -3
- data/docs/flow/general_idea.md +0 -3
- data/docs/operation/basic_usage.md +0 -1
- data/docs/operation/inject_steps.md +0 -3
- data/docs/operation/lambda_steps.md +0 -3
- data/docs/operation/result_shapes.md +0 -3
- data/docs/operation/routing_tracks.md +0 -3
- data/docs/operation/wrapping_steps.md +0 -3
- data/docs/overview/performance.md +0 -336
- data/docs/railway/basic_usage.md +0 -232
- data/docs/result_objects/basic_usage.md +0 -196
- data/docs/result_objects/do_notation.md +0 -139
- data/lib/flows/node.rb +0 -27
- data/lib/flows/operation.rb +0 -52
- data/lib/flows/operation/builder.rb +0 -130
- data/lib/flows/operation/builder/build_router.rb +0 -37
- data/lib/flows/operation/dsl.rb +0 -93
- data/lib/flows/operation/errors.rb +0 -75
- data/lib/flows/operation/executor.rb +0 -78
- data/lib/flows/railway/builder.rb +0 -68
- data/lib/flows/railway/executor.rb +0 -23
- data/lib/flows/result_router.rb +0 -14
- data/lib/flows/router.rb +0 -22
@@ -0,0 +1,43 @@
|
|
1
|
+
module Flows
|
2
|
+
module Plugin
|
3
|
+
module Profiler
|
4
|
+
class Report
|
5
|
+
# @api private
|
6
|
+
Event = Struct.new(:method_class, :method_type, :method_name, :data) do
|
7
|
+
def subject
|
8
|
+
"#{method_class}#{delimeter}#{method_name}"
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def delimeter
|
14
|
+
case method_type
|
15
|
+
when :instance then '#'
|
16
|
+
when :singleton then '.'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @api private
|
22
|
+
#
|
23
|
+
# Method execution start event.
|
24
|
+
class StartEvent < Event
|
25
|
+
def to_s
|
26
|
+
"start: #{subject}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# @api private
|
31
|
+
#
|
32
|
+
# Method execution finish event.
|
33
|
+
#
|
34
|
+
# Data is an execution time in microseconds.
|
35
|
+
class FinishEvent < Event
|
36
|
+
def to_s
|
37
|
+
"finish(#{data} microseconds): #{subject}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require_relative 'flat/method_report'
|
2
|
+
|
3
|
+
module Flows
|
4
|
+
module Plugin
|
5
|
+
module Profiler
|
6
|
+
class Report
|
7
|
+
# Flat report. Merges similar calls, hides execution structure.
|
8
|
+
#
|
9
|
+
# It's a variation of a {Rport::Tree} where all calls of the same method
|
10
|
+
# are combined into a one first-level entry.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# Flows::Plugin::Profiler.profile(:flat) do
|
14
|
+
# # some code here
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# puts Flows::Plugin::Profiler.last_report
|
18
|
+
class Flat < Tree
|
19
|
+
def to_a
|
20
|
+
method_reports.map(&:to_h)
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
method_reports.map(&:to_s).join("\n")
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def method_reports
|
30
|
+
@method_reports ||= root_calculated_node
|
31
|
+
.group_by_subject
|
32
|
+
.values
|
33
|
+
.map { |nodes| MethodReport.new(root_calculated_node, *nodes) }
|
34
|
+
.sort_by(&:total_self_ms)
|
35
|
+
.reverse
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Flows
|
2
|
+
module Plugin
|
3
|
+
module Profiler
|
4
|
+
class Report
|
5
|
+
class Flat < Tree
|
6
|
+
# @api private
|
7
|
+
class MethodReport
|
8
|
+
attr_reader :root_node, :calculated_nodes
|
9
|
+
|
10
|
+
def initialize(root_node, *calculated_nodes)
|
11
|
+
@root_node = root_node
|
12
|
+
@calculated_nodes = calculated_nodes
|
13
|
+
|
14
|
+
raise 'no single node provided' if calculated_nodes.empty?
|
15
|
+
raise 'calculated_nodes must be about the same subject' unless nodes_have_same_subject
|
16
|
+
end
|
17
|
+
|
18
|
+
def subject
|
19
|
+
@subject ||= calculated_nodes.first.subject
|
20
|
+
end
|
21
|
+
|
22
|
+
def count
|
23
|
+
@count ||= calculated_nodes.map(&:count).sum
|
24
|
+
end
|
25
|
+
|
26
|
+
def total_self_ms
|
27
|
+
@total_self_ms ||= calculated_nodes.map(&:total_self_ms).sort.sum
|
28
|
+
end
|
29
|
+
|
30
|
+
def total_self_percentage
|
31
|
+
@total_self_percentage ||= calculated_nodes
|
32
|
+
.map { |node| node.total_self_percentage(root_node) }
|
33
|
+
.sort
|
34
|
+
.sum
|
35
|
+
end
|
36
|
+
|
37
|
+
def avg_self_ms
|
38
|
+
@avg_self_ms ||= total_self_ms / count
|
39
|
+
end
|
40
|
+
|
41
|
+
def direct_subcalls
|
42
|
+
@direct_subcalls ||= calculated_nodes
|
43
|
+
.flat_map { |node| node.children.map(&:subject) }
|
44
|
+
.uniq
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_h
|
48
|
+
@to_h ||= {
|
49
|
+
subject: subject,
|
50
|
+
count: count,
|
51
|
+
total_self_ms: total_self_ms,
|
52
|
+
total_self_percentage: total_self_percentage,
|
53
|
+
avg_self_ms: avg_self_ms,
|
54
|
+
direct_subcalls: direct_subcalls
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_s
|
59
|
+
[
|
60
|
+
'',
|
61
|
+
"- #{subject} -",
|
62
|
+
"called: #{count} time(s)",
|
63
|
+
"total self execution time: #{total_self_ms.truncate(2)}ms",
|
64
|
+
"total self percentage: #{total_self_percentage.truncate(2)}%",
|
65
|
+
"average self execution time: #{avg_self_ms.truncate(2)}ms",
|
66
|
+
"direct subcalls: #{direct_subcalls.join(', ')}"
|
67
|
+
]
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def nodes_have_same_subject
|
73
|
+
calculated_nodes.all? { |node| node.subject == subject }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require_relative 'tree/node'
|
2
|
+
require_relative 'tree/calculated_node'
|
3
|
+
|
4
|
+
module Flows
|
5
|
+
module Plugin
|
6
|
+
module Profiler
|
7
|
+
class Report
|
8
|
+
# Tree report. Merges similar calls, saves execution structure (who called whom).
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# Flows::Plugin::Profiler.profile(:tree) do
|
12
|
+
# # some code here
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# puts Flows::Plugin::Profiler.last_report
|
16
|
+
class Tree < Report
|
17
|
+
# Returns tree report as Ruby data structs.
|
18
|
+
#
|
19
|
+
# @return [Array<Hash>] tree report.
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# [
|
23
|
+
# {
|
24
|
+
# subject: 'MyClass#call',
|
25
|
+
# count: 2,
|
26
|
+
# total_ms: 100.0,
|
27
|
+
# total_self_ms: 80.0,
|
28
|
+
# total_self_percentage: 80.0,
|
29
|
+
# avg_ms: 50.0,
|
30
|
+
# avg_self_ms: 40.0,
|
31
|
+
# nested: [
|
32
|
+
# {
|
33
|
+
# subject: 'MyClass#another_method',
|
34
|
+
# count: 1,
|
35
|
+
# total_ms: 20.0,
|
36
|
+
# total_self_ms: 20.0,
|
37
|
+
# total_self_percentage: 20.0,
|
38
|
+
# avg_ms: 20.0,
|
39
|
+
# avg_self_ms: 20.0,
|
40
|
+
# nested: []
|
41
|
+
# }
|
42
|
+
# ]
|
43
|
+
# }
|
44
|
+
# ]
|
45
|
+
def to_a
|
46
|
+
root_calculated_node.children.map { |node| node.to_h(root_calculated_node) }
|
47
|
+
end
|
48
|
+
|
49
|
+
def add(*)
|
50
|
+
forget_memoized_values
|
51
|
+
super
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_s
|
55
|
+
root_calculated_node.children.map { |node| node.to_s(root_calculated_node) }.join
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def forget_memoized_values
|
61
|
+
@root_node = nil
|
62
|
+
@root_calculated_node = nil
|
63
|
+
end
|
64
|
+
|
65
|
+
def root_calculated_node
|
66
|
+
@root_calculated_node ||= CalculatedNode.new(root_node)
|
67
|
+
end
|
68
|
+
|
69
|
+
def root_node
|
70
|
+
@root_node ||= Node.new(subject: :ROOT).tap do |root_node|
|
71
|
+
events.each_with_object([root_node]) do |event, node_path|
|
72
|
+
current_node = node_path.last
|
73
|
+
|
74
|
+
case event
|
75
|
+
when StartEvent then process_start_event(node_path, current_node, event)
|
76
|
+
when FinishEvent then process_finish_event(node_path, current_node, event)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# :reek:UtilityFunction
|
83
|
+
def process_start_event(node_path, current_node, event)
|
84
|
+
node_path << current_node[event.subject]
|
85
|
+
end
|
86
|
+
|
87
|
+
# :reek:UtilityFunction :reek:FeatureEnvy
|
88
|
+
def process_finish_event(node_path, current_node, event)
|
89
|
+
raise 'Invalid profiling events detected' if event.subject != current_node.subject
|
90
|
+
|
91
|
+
current_node.register_execution(event.data)
|
92
|
+
node_path.pop
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module Flows
|
2
|
+
module Plugin
|
3
|
+
module Profiler
|
4
|
+
class Report
|
5
|
+
class Tree < Report
|
6
|
+
# @api private
|
7
|
+
class CalculatedNode
|
8
|
+
MICROSECONDS_IN_MILLISECOND = 1000.0
|
9
|
+
|
10
|
+
attr_reader :children
|
11
|
+
|
12
|
+
def initialize(node)
|
13
|
+
@node = node
|
14
|
+
@children = node.children
|
15
|
+
.map { |child| self.class.new(child) }
|
16
|
+
.sort_by(&:total_ms)
|
17
|
+
.reverse
|
18
|
+
end
|
19
|
+
|
20
|
+
def subject
|
21
|
+
@node.subject
|
22
|
+
end
|
23
|
+
|
24
|
+
def count
|
25
|
+
@count ||= @node.executions.count
|
26
|
+
end
|
27
|
+
|
28
|
+
def total_ms
|
29
|
+
@total_ms ||= @node.executions.sort.sum / MICROSECONDS_IN_MILLISECOND
|
30
|
+
end
|
31
|
+
|
32
|
+
def avg_ms
|
33
|
+
@avg_ms ||= total_ms / count
|
34
|
+
end
|
35
|
+
|
36
|
+
def children_ms
|
37
|
+
@children_ms ||= children.map(&:total_ms).sort.sum
|
38
|
+
end
|
39
|
+
|
40
|
+
def total_self_ms
|
41
|
+
@total_self_ms ||= total_ms - children_ms
|
42
|
+
end
|
43
|
+
|
44
|
+
def total_self_percentage(root_node = self)
|
45
|
+
@total_self_percentage ||= total_self_ms / root_node.children_ms * 100.0
|
46
|
+
end
|
47
|
+
|
48
|
+
def total_percentage(root_node = self)
|
49
|
+
@total_percentage ||= total_ms / root_node.children_ms * 100.0
|
50
|
+
end
|
51
|
+
|
52
|
+
def avg_self_ms
|
53
|
+
@avg_self_ms ||= total_self_ms / count
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_h(root_node = self) # rubocop:disable Metrics/MethodLength
|
57
|
+
@to_h ||= {
|
58
|
+
subject: subject,
|
59
|
+
count: count,
|
60
|
+
total_ms: total_ms,
|
61
|
+
total_percentage: total_percentage(root_node),
|
62
|
+
total_self_ms: total_self_ms,
|
63
|
+
total_self_percentage: total_self_percentage(root_node),
|
64
|
+
avg_ms: avg_ms,
|
65
|
+
avg_self_ms: avg_self_ms,
|
66
|
+
nested: children.map { |node| node.to_h(root_node) }
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_s(root_node = self)
|
71
|
+
@to_s ||= (base_text_list(root_node) + childeren_text_list(root_node)).join("\n")
|
72
|
+
end
|
73
|
+
|
74
|
+
# :reek:DuplicateMethodCall
|
75
|
+
# :reek:NestedIterators
|
76
|
+
def group_by_subject
|
77
|
+
@group_by_subject ||= (
|
78
|
+
[children.group_by(&:subject)] + children.map(&:group_by_subject)
|
79
|
+
).each_with_object({}) do |group, result|
|
80
|
+
group.each do |subject, nodes|
|
81
|
+
result[subject] ||= []
|
82
|
+
result[subject] += nodes
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def base_text_list(root_node) # rubocop:disable Metrics/MethodLength
|
90
|
+
[
|
91
|
+
'',
|
92
|
+
"- #{subject} -",
|
93
|
+
"called: #{count} time(s)",
|
94
|
+
"total execution time: #{total_ms.truncate(2)}ms",
|
95
|
+
"total percentage: #{total_percentage(root_node).truncate(2)}%",
|
96
|
+
"total self execution time: #{total_self_ms.truncate(2)}ms",
|
97
|
+
"total self percentage: #{total_self_percentage(root_node).truncate(2)}%",
|
98
|
+
"average execution time: #{avg_ms.truncate(2)}ms",
|
99
|
+
"average self execution time: #{avg_self_ms.truncate(2)}ms"
|
100
|
+
]
|
101
|
+
end
|
102
|
+
|
103
|
+
def childeren_text_list(root_node)
|
104
|
+
return [] if @children.empty?
|
105
|
+
|
106
|
+
children.map { |node| node.to_s(root_node) }
|
107
|
+
.join("\n")
|
108
|
+
.split("\n")
|
109
|
+
.map { |str| "| #{str}" }
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Flows
|
2
|
+
module Plugin
|
3
|
+
module Profiler
|
4
|
+
class Report
|
5
|
+
class Tree < Report
|
6
|
+
# @api private
|
7
|
+
class Node
|
8
|
+
attr_reader :subject, :executions
|
9
|
+
|
10
|
+
def initialize(subject:)
|
11
|
+
@subject = subject
|
12
|
+
@children = {}
|
13
|
+
@cache = {}
|
14
|
+
|
15
|
+
@executions = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def [](subject)
|
19
|
+
@children[subject] ||= Node.new(subject: subject)
|
20
|
+
end
|
21
|
+
|
22
|
+
def children
|
23
|
+
@children.values
|
24
|
+
end
|
25
|
+
|
26
|
+
def register_execution(microseconds)
|
27
|
+
@executions << microseconds
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Flows
|
2
|
+
module Plugin
|
3
|
+
module Profiler
|
4
|
+
# @api private
|
5
|
+
module Wrapper
|
6
|
+
class << self
|
7
|
+
def make_instance_wrapper(method_name) # rubocop:disable Metrics/MethodLength
|
8
|
+
Module.new.tap do |mod|
|
9
|
+
mod.define_method(method_name) do |*args, &block| # rubocop:disable Metrics/MethodLength
|
10
|
+
thread = Thread.current
|
11
|
+
klass = self.class
|
12
|
+
|
13
|
+
return super(*args, &block) unless thread[THREAD_VAR_FLAG]
|
14
|
+
|
15
|
+
report = thread[THREAD_VAR_REPORT]
|
16
|
+
report.add(:started, klass, :instance, method_name, nil)
|
17
|
+
|
18
|
+
before = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_microsecond)
|
19
|
+
super(*args, &block)
|
20
|
+
ensure
|
21
|
+
if before
|
22
|
+
after = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_microsecond)
|
23
|
+
report.add(:finished, klass, :instance, method_name, after - before)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def make_singleton_wrapper(method_name) # rubocop:disable Metrics/MethodLength
|
30
|
+
Module.new.tap do |mod|
|
31
|
+
mod.define_method(method_name) do |*args, &block| # rubocop:disable Metrics/MethodLength
|
32
|
+
thread = Thread.current
|
33
|
+
|
34
|
+
return super(*args, &block) unless thread[THREAD_VAR_FLAG]
|
35
|
+
|
36
|
+
report = thread[THREAD_VAR_REPORT]
|
37
|
+
report.add(:started, self, :singleton, method_name, nil)
|
38
|
+
|
39
|
+
before = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_microsecond)
|
40
|
+
super(*args, &block)
|
41
|
+
ensure
|
42
|
+
if before
|
43
|
+
after = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_microsecond)
|
44
|
+
report.add(:finished, self, :singleton, method_name, after - before)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|