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,69 @@
1
+ class BenchmarkCLI
2
+ module Ruby
3
+ class SelfClass
4
+ include Helpers
5
+
6
+ def call
7
+ header 'Check if repeatative `self.class` impacts performance'
8
+
9
+ Benchmark.ips do |benchmark|
10
+ benchmark.config(stats: :bootstrap, confidence: 95)
11
+
12
+ run_benchmarks(benchmark)
13
+
14
+ benchmark.compare!
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def run_benchmarks(benchmark)
21
+ report_self_class(benchmark)
22
+ report_klass(benchmark)
23
+ end
24
+
25
+ def report_self_class(benchmark)
26
+ benchmark.report '[self.class, ... 10 times]' do
27
+ self_class_10_times
28
+ end
29
+ end
30
+
31
+ def self_class_10_times # rubocop:disable Metrics/MethodLength
32
+ [
33
+ self.class,
34
+ self.class,
35
+ self.class,
36
+ self.class,
37
+ self.class,
38
+ self.class,
39
+ self.class,
40
+ self.class,
41
+ self.class,
42
+ self.class
43
+ ]
44
+ end
45
+
46
+ def report_klass(benchmark)
47
+ benchmark.report 'klass = self.class; [klass, ... 10 times]' do
48
+ klass_10_times
49
+ end
50
+ end
51
+
52
+ def klass_10_times # rubocop:disable Metrics/MethodLength
53
+ klass = self.class
54
+ [
55
+ klass,
56
+ klass,
57
+ klass,
58
+ klass,
59
+ klass,
60
+ klass,
61
+ klass,
62
+ klass,
63
+ klass,
64
+ klass
65
+ ]
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,90 @@
1
+ require 'ostruct'
2
+
3
+ class BenchmarkCLI
4
+ module Ruby
5
+ class Structs
6
+ include Helpers
7
+
8
+ def call
9
+ header 'Creates a struct with 3 random number fields and calculates the sum of the fields'
10
+
11
+ Benchmark.ips do |benchmark|
12
+ benchmark.config(stats: :bootstrap, confidence: 95)
13
+
14
+ run_benchmarks(benchmark)
15
+
16
+ benchmark.compare!
17
+ end
18
+ end
19
+
20
+ RubyStruct = Struct.new(:a, :b, :c, keyword_init: true)
21
+
22
+ class CustomStruct
23
+ attr_reader :a, :b, :c
24
+
25
+ def initialize(a:, b:, c:) # rubocop:disable Naming/MethodParameterName
26
+ @a = a
27
+ @b = b
28
+ @c = c
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def run_benchmarks(benchmark)
35
+ report_hash(benchmark)
36
+ report_ruby_struct(benchmark)
37
+ report_custom_class(benchmark)
38
+ report_ostruct(benchmark)
39
+ end
40
+
41
+ def report_hash(benchmark)
42
+ benchmark.report 'Hash' do
43
+ hash = {
44
+ a: rand(10),
45
+ b: rand(20),
46
+ c: rand(30)
47
+ }
48
+
49
+ hash[:a] + hash[:b] + hash[:c]
50
+ end
51
+ end
52
+
53
+ def report_ruby_struct(benchmark)
54
+ benchmark.report 'Ruby Struct' do
55
+ rstruct = RubyStruct.new(
56
+ a: rand(10),
57
+ b: rand(20),
58
+ c: rand(30)
59
+ )
60
+
61
+ rstruct.a + rstruct.b + rstruct.c
62
+ end
63
+ end
64
+
65
+ def report_custom_class(benchmark)
66
+ benchmark.report 'Custom Class' do
67
+ custom_struct = CustomStruct.new(
68
+ a: rand(10),
69
+ b: rand(10),
70
+ c: rand(10)
71
+ )
72
+
73
+ custom_struct.a + custom_struct.b + custom_struct.c
74
+ end
75
+ end
76
+
77
+ def report_ostruct(benchmark)
78
+ benchmark.report 'Open Struct' do
79
+ ostruct = OpenStruct.new(
80
+ a: rand(10),
81
+ b: rand(10),
82
+ c: rand(10)
83
+ )
84
+
85
+ ostruct.a + ostruct.b + ostruct.c
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -6,5 +6,6 @@ require 'flows'
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
8
8
 
9
+ require 'awesome_print'
9
10
  require 'pry'
10
11
  Pry.start
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ rm -rf .yardoc/ && bundle exec yard server --reload
7
+ rm -rf .yardoc/
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'flows'
5
+
6
+ require 'pry'
7
+ require 'gli'
8
+
9
+ require 'rainbow/refinement'
10
+ using Rainbow
11
+
12
+ require_relative 'errors_cli/contract_error_demo'
13
+ require_relative 'errors_cli/flows_router_error_demo'
14
+ require_relative 'errors_cli/di_error_demo'
15
+ require_relative 'errors_cli/oc_error_demo'
16
+ require_relative 'errors_cli/railway_error_demo'
17
+ require_relative 'errors_cli/result_error_demo'
18
+ require_relative 'errors_cli/scp_error_demo'
19
+ require_relative 'errors_cli/flow_error_demo'
20
+ require_relative 'errors_cli/interface_error_demo'
21
+
22
+ class ErrorsCLI
23
+ extend GLI::App
24
+
25
+ program_desc 'Error reporting demo'
26
+
27
+ use_openstruct true
28
+
29
+ def self.make_cmd(ctx, title, name) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
30
+ ctx.desc title
31
+ ctx.command name do |cmd|
32
+ cmd.action do |_, _, _|
33
+ puts title.green
34
+ puts("BEGIN#{'-' * (title.length - 5)}".color(:darkgray))
35
+ yield
36
+ rescue StandardError => err
37
+ puts err.message
38
+ puts("END#{'-' * (title.length - 3)}".color(:darkgray))
39
+ puts
40
+ end
41
+ end
42
+ end
43
+
44
+ make_cmd self, 'Flows::Contract error', :contract do
45
+ ContractErrorDemo.call
46
+ end
47
+
48
+ make_cmd self, 'Flows::Flow::Router::NoRouteError', :router do
49
+ FlowsRouterErrorDemo.call
50
+ end
51
+
52
+ desc 'Flows::Plugin::DependencyInjector errors'
53
+ command :di do |cmd|
54
+ make_cmd cmd, 'Missing Dependency', :missing do
55
+ DIErrorDemo.missing_dependency
56
+ end
57
+
58
+ make_cmd cmd, 'Unexpected Dependency', :unexpected do
59
+ DIErrorDemo.unexpected_dependency
60
+ end
61
+
62
+ make_cmd cmd, 'Invalid Type', :invalid_type do
63
+ DIErrorDemo.invalid_type_dependency
64
+ end
65
+
66
+ make_cmd cmd, 'Missing Default', :missing_default do
67
+ DIErrorDemo.missing_default
68
+ end
69
+ end
70
+
71
+ desc 'OutputContract plugin errors'
72
+ command :output_contract do |cmd|
73
+ make_cmd cmd, 'No Contract error', :no_contract do
74
+ OCErrorDemo.no_contract
75
+ end
76
+
77
+ make_cmd cmd, 'Contract error', :contract do
78
+ OCErrorDemo.contract_error
79
+ end
80
+
81
+ make_cmd cmd, 'Status error', :status do
82
+ OCErrorDemo.status_error
83
+ end
84
+
85
+ make_cmd cmd, 'Result type error', :result_type do
86
+ OCErrorDemo.result_type_error
87
+ end
88
+ end
89
+
90
+ desc 'Railway errors'
91
+ command :railway do |cmd|
92
+ make_cmd cmd, 'No Steps error', :no_steps do
93
+ RailwayErrorDemo.call
94
+ end
95
+ end
96
+
97
+ desc 'Result errors'
98
+ command :result do |cmd|
99
+ make_cmd cmd, 'Access error for successful result', :ok_access do
100
+ ResultErrorDemo.success_access_error
101
+ end
102
+
103
+ make_cmd cmd, 'Access error for failure result', :err_access do
104
+ ResultErrorDemo.failure_access_error
105
+ end
106
+ end
107
+
108
+ desc 'Shared Context Pipeline errors'
109
+ command :scp do |cmd|
110
+ make_cmd cmd, 'No Steps error', :no_steps do
111
+ SCPErrorDemo.no_steps
112
+ end
113
+
114
+ make_cmd cmd, 'Missing Step Implementation error', :no_step_impl do
115
+ SCPErrorDemo.no_step_impl
116
+ end
117
+ end
118
+
119
+ desc 'Flow errors'
120
+ command :flow do |cmd|
121
+ make_cmd cmd, 'No first node', :no_first_node do
122
+ FlowErrorDemo.no_first_node
123
+ end
124
+
125
+ make_cmd cmd, 'Invalid Node route', :invalid_node_route do
126
+ FlowErrorDemo.invalid_node_route
127
+ end
128
+ end
129
+
130
+ desc 'Interface errors'
131
+ command :interface do |cmd|
132
+ make_cmd cmd, 'Missing Implementation', :missing_implementation do
133
+ InterfaceErrorDemo.missing_implementation
134
+ end
135
+ end
136
+ end
137
+
138
+ exit ErrorsCLI.run(ARGV)
@@ -0,0 +1,49 @@
1
+ module ContractErrorDemo
2
+ class << self
3
+ def call
4
+ contract.transform!(invalid_data)
5
+ end
6
+
7
+ private
8
+
9
+ def invalid_data # rubocop:disable Metrics/MethodLength
10
+ {
11
+ str_array_field: ['aaa', 'bbb', :ccc],
12
+ hash_array_field: [
13
+ {
14
+ x: 1,
15
+ y: 2
16
+ },
17
+ {
18
+ 'x' => 1,
19
+ 'y' => 2
20
+ }
21
+ ],
22
+ array_compose_predicate: ['aaaa', 'bbbb', 'a', :xxx],
23
+ int_field: '10',
24
+ array_either: ['a', :a, 1],
25
+ tuple: [1, 1]
26
+ }
27
+ end
28
+
29
+ def contract # rubocop:disable Metrics/MethodLength
30
+ Flows::Contract.make do
31
+ hash_of(
32
+ str_array_field: array(String),
33
+ hash_array_field: array(
34
+ hash(Symbol, String)
35
+ ),
36
+ array_compose_predicate: array(
37
+ compose(String, predicate('must be longer that 3') { |str| str.size > 3 })
38
+ ),
39
+ int_field: Integer,
40
+ array_either: array(
41
+ either(String, Symbol)
42
+ ),
43
+ tuple: tuple(Float, Float),
44
+ field_to_be_missed: String
45
+ )
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,38 @@
1
+ module DIErrorDemo
2
+ class WithDI
3
+ include Flows::Plugin::DependencyInjector
4
+
5
+ dependency :req_str_dep, required: true, type: String
6
+
7
+ def call
8
+ 'Hi!'
9
+ end
10
+ end
11
+
12
+ class WithEmptyDI
13
+ include Flows::Plugin::DependencyInjector
14
+ end
15
+
16
+ class << self
17
+ def missing_dependency
18
+ WithDI.new
19
+ end
20
+
21
+ def unexpected_dependency
22
+ WithDI.new(dependencies: {
23
+ req_str_dep: 'AAA',
24
+ my_extra_dependency: 'III'
25
+ })
26
+ end
27
+
28
+ def invalid_type_dependency
29
+ WithDI.new(dependencies: {
30
+ req_str_dep: :AAA
31
+ })
32
+ end
33
+
34
+ def missing_default
35
+ WithEmptyDI.dependency :my_opt_dep
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+ module FlowErrorDemo
2
+ class << self
3
+ def no_first_node
4
+ Flows::Flow.new(
5
+ start_node: :first,
6
+ node_map: {}
7
+ )
8
+ end
9
+
10
+ def invalid_node_route
11
+ Flows::Flow.new(
12
+ start_node: :first,
13
+ node_map: {
14
+ first: Flows::Flow::Node.new(
15
+ body: ->(_) {},
16
+ router: Flows::Flow::Router::Custom.new(a: :b)
17
+ )
18
+ }
19
+ )
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ module FlowsRouterErrorDemo
2
+ class << self
3
+ include Flows::Result::Helpers
4
+
5
+ def call
6
+ router.call(ok(some: :data))
7
+ end
8
+
9
+ private
10
+
11
+ def router
12
+ Flows::Flow::Router::Custom.new(match_err => :end)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ module InterfaceErrorDemo
2
+ class Parent
3
+ extend Flows::Plugin::Interface
4
+
5
+ defmethod :execute
6
+ defmethod :debug
7
+ end
8
+
9
+ class Child < Parent
10
+ end
11
+
12
+ class << self
13
+ def missing_implementation
14
+ Child.new
15
+ end
16
+ end
17
+ end