flows 0.2.0 → 0.6.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.
Files changed (166) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/{build.yml → test.yml} +5 -10
  3. data/.gitignore +9 -1
  4. data/.mdlrc +1 -1
  5. data/.reek.yml +54 -0
  6. data/.rubocop.yml +26 -7
  7. data/.rubocop_todo.yml +27 -0
  8. data/.ruby-version +1 -1
  9. data/.yardopts +1 -0
  10. data/CHANGELOG.md +81 -0
  11. data/Gemfile +0 -6
  12. data/README.md +167 -363
  13. data/Rakefile +35 -1
  14. data/bin/.rubocop.yml +5 -0
  15. data/bin/all_the_errors +55 -0
  16. data/bin/benchmark +73 -105
  17. data/bin/benchmark_cli/compare.rb +118 -0
  18. data/bin/benchmark_cli/compare/a_plus_b.rb +22 -0
  19. data/bin/benchmark_cli/compare/base.rb +45 -0
  20. data/bin/benchmark_cli/compare/command.rb +47 -0
  21. data/bin/benchmark_cli/compare/ten_steps.rb +22 -0
  22. data/bin/benchmark_cli/examples.rb +23 -0
  23. data/bin/benchmark_cli/examples/.rubocop.yml +22 -0
  24. data/bin/benchmark_cli/examples/a_plus_b/dry_do.rb +23 -0
  25. data/bin/benchmark_cli/examples/a_plus_b/dry_transaction.rb +17 -0
  26. data/bin/benchmark_cli/examples/a_plus_b/flows_do.rb +22 -0
  27. data/bin/benchmark_cli/examples/a_plus_b/flows_railway.rb +13 -0
  28. data/bin/benchmark_cli/examples/a_plus_b/flows_scp.rb +13 -0
  29. data/bin/benchmark_cli/examples/a_plus_b/flows_scp_mut.rb +13 -0
  30. data/bin/benchmark_cli/examples/a_plus_b/flows_scp_oc.rb +21 -0
  31. data/bin/benchmark_cli/examples/a_plus_b/trailblazer.rb +15 -0
  32. data/bin/benchmark_cli/examples/ten_steps/dry_do.rb +70 -0
  33. data/bin/benchmark_cli/examples/ten_steps/dry_transaction.rb +64 -0
  34. data/bin/benchmark_cli/examples/ten_steps/flows_do.rb +69 -0
  35. data/bin/benchmark_cli/examples/ten_steps/flows_railway.rb +58 -0
  36. data/bin/benchmark_cli/examples/ten_steps/flows_scp.rb +58 -0
  37. data/bin/benchmark_cli/examples/ten_steps/flows_scp_mut.rb +58 -0
  38. data/bin/benchmark_cli/examples/ten_steps/flows_scp_oc.rb +66 -0
  39. data/bin/benchmark_cli/examples/ten_steps/trailblazer.rb +60 -0
  40. data/bin/benchmark_cli/helpers.rb +12 -0
  41. data/bin/benchmark_cli/ruby.rb +15 -0
  42. data/bin/benchmark_cli/ruby/command.rb +38 -0
  43. data/bin/benchmark_cli/ruby/method_exec.rb +71 -0
  44. data/bin/benchmark_cli/ruby/self_class.rb +69 -0
  45. data/bin/benchmark_cli/ruby/structs.rb +90 -0
  46. data/bin/console +1 -0
  47. data/bin/docserver +7 -0
  48. data/bin/errors +138 -0
  49. data/bin/errors_cli/contract_error_demo.rb +49 -0
  50. data/bin/errors_cli/di_error_demo.rb +38 -0
  51. data/bin/errors_cli/flow_error_demo.rb +22 -0
  52. data/bin/errors_cli/flows_router_error_demo.rb +15 -0
  53. data/bin/errors_cli/interface_error_demo.rb +17 -0
  54. data/bin/errors_cli/oc_error_demo.rb +40 -0
  55. data/bin/errors_cli/railway_error_demo.rb +10 -0
  56. data/bin/errors_cli/result_error_demo.rb +13 -0
  57. data/bin/errors_cli/scp_error_demo.rb +17 -0
  58. data/docs/README.md +3 -187
  59. data/docs/_sidebar.md +0 -24
  60. data/docs/index.html +1 -1
  61. data/flows.gemspec +27 -2
  62. data/forspell.dict +9 -0
  63. data/lefthook.yml +9 -0
  64. data/lib/flows.rb +11 -5
  65. data/lib/flows/contract.rb +402 -0
  66. data/lib/flows/contract/array.rb +55 -0
  67. data/lib/flows/contract/case_eq.rb +43 -0
  68. data/lib/flows/contract/compose.rb +77 -0
  69. data/lib/flows/contract/either.rb +53 -0
  70. data/lib/flows/contract/error.rb +24 -0
  71. data/lib/flows/contract/hash.rb +75 -0
  72. data/lib/flows/contract/hash_of.rb +70 -0
  73. data/lib/flows/contract/helpers.rb +22 -0
  74. data/lib/flows/contract/predicate.rb +34 -0
  75. data/lib/flows/contract/transformer.rb +50 -0
  76. data/lib/flows/contract/tuple.rb +70 -0
  77. data/lib/flows/flow.rb +96 -7
  78. data/lib/flows/flow/errors.rb +29 -0
  79. data/lib/flows/flow/node.rb +132 -0
  80. data/lib/flows/flow/router.rb +29 -0
  81. data/lib/flows/flow/router/custom.rb +59 -0
  82. data/lib/flows/flow/router/errors.rb +11 -0
  83. data/lib/flows/flow/router/simple.rb +25 -0
  84. data/lib/flows/plugin.rb +15 -0
  85. data/lib/flows/plugin/dependency_injector.rb +170 -0
  86. data/lib/flows/plugin/dependency_injector/dependency.rb +24 -0
  87. data/lib/flows/plugin/dependency_injector/dependency_definition.rb +16 -0
  88. data/lib/flows/plugin/dependency_injector/dependency_list.rb +55 -0
  89. data/lib/flows/plugin/dependency_injector/errors.rb +58 -0
  90. data/lib/flows/plugin/implicit_init.rb +45 -0
  91. data/lib/flows/plugin/interface.rb +84 -0
  92. data/lib/flows/plugin/output_contract.rb +85 -0
  93. data/lib/flows/plugin/output_contract/dsl.rb +48 -0
  94. data/lib/flows/plugin/output_contract/errors.rb +74 -0
  95. data/lib/flows/plugin/output_contract/wrapper.rb +55 -0
  96. data/lib/flows/plugin/profiler.rb +114 -0
  97. data/lib/flows/plugin/profiler/injector.rb +35 -0
  98. data/lib/flows/plugin/profiler/report.rb +48 -0
  99. data/lib/flows/plugin/profiler/report/events.rb +43 -0
  100. data/lib/flows/plugin/profiler/report/flat.rb +41 -0
  101. data/lib/flows/plugin/profiler/report/flat/method_report.rb +80 -0
  102. data/lib/flows/plugin/profiler/report/raw.rb +15 -0
  103. data/lib/flows/plugin/profiler/report/tree.rb +98 -0
  104. data/lib/flows/plugin/profiler/report/tree/calculated_node.rb +116 -0
  105. data/lib/flows/plugin/profiler/report/tree/node.rb +34 -0
  106. data/lib/flows/plugin/profiler/wrapper.rb +53 -0
  107. data/lib/flows/railway.rb +140 -34
  108. data/lib/flows/railway/dsl.rb +8 -18
  109. data/lib/flows/railway/errors.rb +8 -12
  110. data/lib/flows/railway/step.rb +24 -0
  111. data/lib/flows/railway/step_list.rb +38 -0
  112. data/lib/flows/result.rb +188 -2
  113. data/lib/flows/result/do.rb +158 -16
  114. data/lib/flows/result/err.rb +12 -6
  115. data/lib/flows/result/errors.rb +29 -17
  116. data/lib/flows/result/helpers.rb +25 -3
  117. data/lib/flows/result/ok.rb +12 -6
  118. data/lib/flows/shared_context_pipeline.rb +342 -0
  119. data/lib/flows/shared_context_pipeline/dsl.rb +12 -0
  120. data/lib/flows/shared_context_pipeline/dsl/callbacks.rb +35 -0
  121. data/lib/flows/shared_context_pipeline/dsl/tracks.rb +52 -0
  122. data/lib/flows/shared_context_pipeline/errors.rb +17 -0
  123. data/lib/flows/shared_context_pipeline/mutation_step.rb +30 -0
  124. data/lib/flows/shared_context_pipeline/router_definition.rb +21 -0
  125. data/lib/flows/shared_context_pipeline/step.rb +55 -0
  126. data/lib/flows/shared_context_pipeline/track.rb +54 -0
  127. data/lib/flows/shared_context_pipeline/track_list.rb +51 -0
  128. data/lib/flows/shared_context_pipeline/wrap.rb +73 -0
  129. data/lib/flows/util.rb +17 -0
  130. data/lib/flows/util/inheritable_singleton_vars.rb +86 -0
  131. data/lib/flows/util/inheritable_singleton_vars/dup_strategy.rb +100 -0
  132. data/lib/flows/util/inheritable_singleton_vars/isolation_strategy.rb +91 -0
  133. data/lib/flows/util/prepend_to_class.rb +191 -0
  134. data/lib/flows/version.rb +1 -1
  135. metadata +253 -38
  136. data/Gemfile.lock +0 -174
  137. data/bin/demo +0 -66
  138. data/bin/examples.rb +0 -195
  139. data/bin/profile_10steps +0 -106
  140. data/bin/ruby_benchmarks +0 -26
  141. data/docs/CNAME +0 -1
  142. data/docs/contributing/benchmarks_profiling.md +0 -3
  143. data/docs/contributing/local_development.md +0 -3
  144. data/docs/flow/direct_usage.md +0 -3
  145. data/docs/flow/general_idea.md +0 -3
  146. data/docs/operation/basic_usage.md +0 -1
  147. data/docs/operation/inject_steps.md +0 -3
  148. data/docs/operation/lambda_steps.md +0 -3
  149. data/docs/operation/result_shapes.md +0 -3
  150. data/docs/operation/routing_tracks.md +0 -3
  151. data/docs/operation/wrapping_steps.md +0 -3
  152. data/docs/overview/performance.md +0 -336
  153. data/docs/railway/basic_usage.md +0 -232
  154. data/docs/result_objects/basic_usage.md +0 -196
  155. data/docs/result_objects/do_notation.md +0 -139
  156. data/lib/flows/node.rb +0 -27
  157. data/lib/flows/operation.rb +0 -52
  158. data/lib/flows/operation/builder.rb +0 -130
  159. data/lib/flows/operation/builder/build_router.rb +0 -37
  160. data/lib/flows/operation/dsl.rb +0 -93
  161. data/lib/flows/operation/errors.rb +0 -75
  162. data/lib/flows/operation/executor.rb +0 -78
  163. data/lib/flows/railway/builder.rb +0 -68
  164. data/lib/flows/railway/executor.rb +0 -23
  165. data/lib/flows/result_router.rb +0 -14
  166. 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,15 @@
1
+ module Flows
2
+ module Plugin
3
+ module Profiler
4
+ class Report
5
+ # Raw report. Preserves events as is.
6
+ class Raw < Report
7
+ # @see Report#to_s
8
+ def to_s
9
+ raw_data.map(&:to_s).join("\n")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ 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