flows 0.2.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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,85 @@
1
+ require_relative 'output_contract/errors'
2
+ require_relative 'output_contract/dsl'
3
+ require_relative 'output_contract/wrapper'
4
+
5
+ module Flows
6
+ module Plugin
7
+ # Allows to make a contract check and transformation for `#call` method execution in any class.
8
+ #
9
+ # Plugin applies a wrapper to a `#call` instance method.
10
+ # This wrapper will do the following:
11
+ #
12
+ # * check that {Result} instance is returned by `#call`
13
+ # * check that returned {Result#status} is expected
14
+ # * check that returned result data conforms {Contract} assigned
15
+ # to a particular result type and status
16
+ # * applies contract transform to the returned data
17
+ # * returns {Result} with the same status and type,
18
+ # wraps transformed data inside.
19
+ #
20
+ # Plugin provides DSL to express expected result statuses and assigned contracts.
21
+ # Contracts definition reuses {Contract.make} to execute block and get a contract.
22
+ #
23
+ # * `success_with(status, &block)` - defines contract for a successful result with status `status`.
24
+ # * `failure_with(status, &block)` - defines contract for a failure result with status `status`.
25
+ # * `skip_output_contract` - disables contract check and transformation for current class and children.
26
+ #
27
+ # @example with one possible output contract
28
+ # class DoJob
29
+ # include Flows::Result::Helpers
30
+ # include Flows::Plugin::OutputContract
31
+ #
32
+ # success_with :ok do
33
+ # Integer
34
+ # end
35
+ #
36
+ # def call(a, b)
37
+ # ok_data(a + b)
38
+ # end
39
+ # end
40
+ #
41
+ # DoJob.new.call(1, 2).unwrap
42
+ # # => 3
43
+ #
44
+ # DoJob.new.call('a', 'b')
45
+ # # Flows::Contract::Error exception raised
46
+ #
47
+ # @example with multiple contracts
48
+ # class DoJob
49
+ # include Flows::Result::Helpers
50
+ # include Flows::Plugin::OutputContract
51
+ #
52
+ # success_with :int_sum do
53
+ # Integer
54
+ # end
55
+ #
56
+ # success_with :float_sum do
57
+ # Float
58
+ # end
59
+ #
60
+ # failure_with :err do
61
+ # hash_of(
62
+ # key: Symbol,
63
+ # msg: String
64
+ # )
65
+ # end
66
+ #
67
+ # def call(a, b)
68
+ # if a.is_a?(Float) || b.is_a?(Float)
69
+ # ok_data(a + b, status: :float_sum)
70
+ # elsif a.is_a?(Integer) && b.is_a?(Integer)
71
+ # ok_data(a + b, status: :int_sum)
72
+ # else
73
+ # err(key: :unexpected_type, msg: "Unexpected argument types")
74
+ # end
75
+ # end
76
+ # end
77
+ module OutputContract
78
+ # @api private
79
+ def self.included(mod)
80
+ mod.extend(DSL)
81
+ mod.prepend(Wrapper)
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,48 @@
1
+ module Flows
2
+ module Plugin
3
+ module OutputContract
4
+ # DSL for OutputContract plugin.
5
+ module DSL
6
+ # Hash of contracts for successful results.
7
+ attr_reader :success_contracts
8
+
9
+ # Hash of contracts for failure results.
10
+ attr_reader :failure_contracts
11
+
12
+ # Is contract check and transformation disabled
13
+ attr_reader :skip_output_contract_flag
14
+
15
+ SingletonVarsSetup = Flows::Util::InheritableSingletonVars::DupStrategy.make_module(
16
+ '@success_contracts' => {},
17
+ '@failure_contracts' => {},
18
+ '@skip_output_contract_flag' => false
19
+ )
20
+
21
+ include SingletonVarsSetup
22
+
23
+ # Defines a contract for a successful result with specific status.
24
+ #
25
+ # @param status [Symbol] Corresponding result status.
26
+ # @param contract_block [Proc] This block will be passed to {Contract.make} to get a contract.
27
+ def success_with(status, &contract_block)
28
+ success_contracts[status] = Flows::Contract.make(&contract_block)
29
+ end
30
+
31
+ # Defines a contract for a failure result with specific status.
32
+ #
33
+ # @param status [Symbol] Corresponding result status.
34
+ # @param contract_block [Proc] This block will be passed to {Contract.make} to get a contract.
35
+ def failure_with(status, &contract_block)
36
+ failure_contracts[status] = Flows::Contract.make(&contract_block)
37
+ end
38
+
39
+ # Disables contract check and transformation for current class and children.
40
+ #
41
+ # @param enable [Boolean] if true - contracts are disabled
42
+ def skip_output_contract(enable: true)
43
+ @skip_output_contract_flag = enable
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,74 @@
1
+ module Flows
2
+ module Plugin
3
+ module OutputContract
4
+ # Base error class for output contract errors.
5
+ class Error < StandardError; end
6
+
7
+ # Raised when no single contract for successful results is defined
8
+ class NoContractError < Error
9
+ def initialize(klass)
10
+ @klass = klass
11
+ end
12
+
13
+ def message
14
+ "No single success contract defined for #{@klass}"
15
+ end
16
+ end
17
+
18
+ # Raised when result's data violates contract
19
+ class ContractError < Error
20
+ def initialize(klass, result, error)
21
+ @klass = klass
22
+ @result = result
23
+ @error = error
24
+ end
25
+
26
+ def message
27
+ shifted_error = @error.split("\n").map { |str| " #{str}" }.join("\n")
28
+
29
+ "Output contract for #{@klass} is violated.\n" \
30
+ "Result:\n" \
31
+ " `#{@result.inspect}`\n" \
32
+ "Contract Error:\n" \
33
+ "#{shifted_error}"
34
+ end
35
+ end
36
+
37
+ # Raised when no contract found for result
38
+ class StatusError < Error
39
+ def initialize(klass, result, allowed_statuses)
40
+ @klass = klass
41
+ @result = result
42
+ @allowed_statuses = allowed_statuses
43
+ end
44
+
45
+ def message
46
+ allowed_statuses_str = @allowed_statuses.map { |st| "`#{st.inspect}`" }.join(', ')
47
+
48
+ "Output contract for #{@klass} is violated.\n" \
49
+ "Result:\n" \
50
+ " `#{@result.inspect}`\n" \
51
+ "Contract Error:\n" \
52
+ " has unexpected status `#{@result.status.inspect}`\n" \
53
+ " allowed statuses for `#{@result.class}` are: #{allowed_statuses_str}"
54
+ end
55
+ end
56
+
57
+ # Raised when not a result object returned
58
+ class ResultTypeError < Error
59
+ def initialize(klass, result)
60
+ @klass = klass
61
+ @result = result
62
+ end
63
+
64
+ def message
65
+ "Output contract for #{@klass} is violated.\n" \
66
+ "Result:\n" \
67
+ " `#{@result.inspect}`\n" \
68
+ "Contract Error:\n" \
69
+ ' result must be instance of `Flows::Result`'
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,55 @@
1
+ module Flows
2
+ module Plugin
3
+ module OutputContract
4
+ # Contains wrappers for initializer and `#call` methods.
5
+ #
6
+ # @api private
7
+ module Wrapper
8
+ def initialize(*args, &block)
9
+ super(*args, &block)
10
+ klass = self.class
11
+ raise NoContractError, klass if klass.success_contracts.empty? && !klass.skip_output_contract_flag
12
+ end
13
+
14
+ def call(*args, &block)
15
+ result = super(*args, &block)
16
+ klass = self.class
17
+
18
+ return result if klass.skip_output_contract_flag
19
+
20
+ Util.transform_result(klass, result)
21
+
22
+ result
23
+ end
24
+
25
+ # Helper methods for {Wrapper} are extracted to this
26
+ # module as singleton methods to not pollute user classes.
27
+ #
28
+ # @api private
29
+ module Util
30
+ class << self
31
+ def transform_result(klass, result)
32
+ contract = Util.contract_for(klass, result)
33
+
34
+ data = result.send(:data)
35
+
36
+ transformed_result = contract.transform(data)
37
+ raise ContractError.new(klass, result, transformed_result.error) if transformed_result.err?
38
+
39
+ result.send(:'data=', transformed_result.unwrap)
40
+ end
41
+
42
+ def contract_for(klass, result)
43
+ raise ResultTypeError.new(klass, result) unless result.is_a?(Flows::Result)
44
+
45
+ status = result.status
46
+ contracts = result.ok? ? klass.success_contracts : klass.failure_contracts
47
+
48
+ contracts[status] || raise(StatusError.new(klass, result, contracts.keys))
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,114 @@
1
+ require_relative 'profiler/report'
2
+ require_relative 'profiler/injector'
3
+ require_relative 'profiler/wrapper'
4
+
5
+ module Flows
6
+ module Plugin
7
+ # Allows to record execution count and time of particular method on class or singleton class.
8
+ #
9
+ # Recorded data can be displayed in a different ways.
10
+ # See {Profiler::Report} implementations for possible options.
11
+ #
12
+ # To do a measurement you have call your classes inside {.profile} block.
13
+ #
14
+ # @note even without calling {.profile} using this module has some performance
15
+ # impact. Don't left this module used in production environments.
16
+ #
17
+ # @example
18
+ # class MyClass
19
+ # CallProfiler = Flows::Plugin::Profiler.for_method(:call)
20
+ # include CallProfiler
21
+ #
22
+ # def call(a, b)
23
+ # # some work here
24
+ # end
25
+ # end
26
+ #
27
+ # class AnotherClass
28
+ # CallProfiler = Flows::Plugin::Profiler.for_method(:perform)
29
+ # extend CallProfiler
30
+ #
31
+ # def self.perform(x)
32
+ # MyClass.new.call(x, x)
33
+ # end
34
+ # end
35
+ #
36
+ # last_result = Flows::Plugin::Profiler.profile do
37
+ # AnotherClass.perform(2)
38
+ # AnotherClass.perform(6)
39
+ # end
40
+ #
41
+ # Profiler.last_report.to_a
42
+ # # => [
43
+ # # [:started, AnotherClass, :singleton, :perform, nil],
44
+ # # [:started, MyClass, :instance, :call, nil],
45
+ # # [:finished, MyClass, :instance, :call, 7.3],
46
+ # # [:finished, AnotherClass, :singleton, :perform, 10.5],
47
+ # # [:started, AnotherClass, :singleton, :perform, nil],
48
+ # # [:started, MyClass, :instance, :call, nil],
49
+ # # [:finished, MyClass, :instance, :call, 8.8],
50
+ # # [:finished, AnotherClass, :singleton, :perform, 14.2]
51
+ # # ]
52
+ module Profiler
53
+ THREAD_VAR_FLAG = :flows_profiler_flag
54
+ THREAD_VAR_REPORT = :flows_profiler_report
55
+
56
+ class << self
57
+ # Generates profiler module for a particular method.
58
+ #
59
+ # Use `include` for instance methods and `extend` for singleton ones.
60
+ #
61
+ # @param method_name [Symbol] method to wrap with profiling.
62
+ # @return [Module] module to include or extend.
63
+ def for_method(method_name)
64
+ Module.new.tap do |mod|
65
+ injector_mod = Injector.make_module(method_name)
66
+ mod.const_set(:Injector, injector_mod)
67
+ mod.extend injector_mod
68
+ end
69
+ end
70
+
71
+ # Profiles a block execution.
72
+ #
73
+ # @param report [Report, Symbol]
74
+ # desired {Report} to be used.
75
+ # In case of symbol `:some_name` the `Flows::Plugin::Profiler::Report::SomeName.new` will be used.
76
+ # @yield code to profile
77
+ # @return block result
78
+ def profile(report = :raw)
79
+ thread = Thread.current
80
+
81
+ thread[THREAD_VAR_FLAG] = true
82
+ thread[THREAD_VAR_REPORT] = make_report(report)
83
+
84
+ yield
85
+ ensure
86
+ thread[THREAD_VAR_FLAG] = false
87
+ end
88
+
89
+ # Resets thread-local variables used for reporting.
90
+ def reset
91
+ thread = Thread.current
92
+ thread[THREAD_VAR_FLAG] = false
93
+ thread[THREAD_VAR_REPORT] = nil
94
+ end
95
+
96
+ # @return [Report, nil] last generated report if some.
97
+ def last_report
98
+ Thread.current[THREAD_VAR_REPORT]
99
+ end
100
+
101
+ private
102
+
103
+ def make_report(report_or_sym)
104
+ case report_or_sym
105
+ when Report then report_or_sym
106
+ when Symbol
107
+ const_name = report_or_sym.to_s.split('_').map(&:capitalize).join
108
+ Report.const_get(const_name).new
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,35 @@
1
+ module Flows
2
+ module Plugin
3
+ module Profiler
4
+ # @api private
5
+ module Injector
6
+ class << self
7
+ def make_module(method_name)
8
+ Module.new.tap do |mod|
9
+ add_included(mod, method_name)
10
+ add_extended(mod, method_name)
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def add_included(mod, method_name)
17
+ mod.define_method(:included) do |target|
18
+ raise 'must be included into class' unless target.is_a?(Class)
19
+
20
+ target.prepend Wrapper.make_instance_wrapper(method_name)
21
+ end
22
+ end
23
+
24
+ def add_extended(mod, method_name)
25
+ mod.define_method(:extended) do |target|
26
+ raise 'must be extended into class' unless target.is_a?(Class)
27
+
28
+ target.singleton_class.prepend(Wrapper.make_singleton_wrapper(method_name))
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,48 @@
1
+ require_relative 'report/events'
2
+ require_relative 'report/raw'
3
+ require_relative 'report/tree'
4
+ require_relative 'report/flat'
5
+
6
+ module Flows
7
+ module Plugin
8
+ module Profiler
9
+ # Base class for {Profiler} reports.
10
+ #
11
+ # @!method to_s
12
+ # @abstract
13
+ # @return [String] human-readable representation.
14
+ class Report
15
+ # @return [Array<Array>] raw profiler events
16
+ attr_reader :raw_data
17
+
18
+ def initialize
19
+ @raw_data = []
20
+ end
21
+
22
+ # Add event to profile report.
23
+ #
24
+ # @param event_type [:started, :finished] event type
25
+ # @param klass [Class] class where called method is placed
26
+ # @param method_type [:instance, :singleton] method type
27
+ # @param method_name [Symbol] name of the called method
28
+ # @param data [nil, Float] event data, time represented as
29
+ # a Float microseconds value.
30
+ def add(*args)
31
+ raw_data << args
32
+ end
33
+
34
+ # @return [Array<Event>] array of events
35
+ def events
36
+ raw_data.map do |raw_event|
37
+ klass = case raw_event.first
38
+ when :started then StartEvent
39
+ when :finished then FinishEvent
40
+ end
41
+
42
+ klass.new(*raw_event[1..-1])
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end