cauldron 0.1.5 → 0.1.6

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 (89) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -1
  3. data/.rspec +1 -0
  4. data/Gemfile +16 -9
  5. data/Gemfile.lock +134 -64
  6. data/README.md +26 -0
  7. data/Rakefile +24 -99
  8. data/build_sandbox.rb +41 -0
  9. data/cucumber.yml +8 -0
  10. data/features/chop.feature +23 -0
  11. data/features/create_dynamic_statements.feature +14 -0
  12. data/features/generate_known_solution.feature +42 -0
  13. data/features/generate_new_statement.feature +20 -0
  14. data/features/step_definitions/cauldron_steps.rb +47 -0
  15. data/features/support/env.rb +1 -1
  16. data/features/use_existing_statements.feature +23 -0
  17. data/lib/cauldron.rb +41 -5
  18. data/lib/cauldron/actualized_composite.rb +35 -0
  19. data/lib/cauldron/array_collect_template/default.rb +95 -0
  20. data/lib/cauldron/array_collect_template/template.rb +57 -0
  21. data/lib/cauldron/builder.rb +60 -0
  22. data/lib/cauldron/caret.rb +50 -0
  23. data/lib/cauldron/dynamic_operator.rb +90 -0
  24. data/lib/cauldron/dynamic_operator_module.rb +140 -0
  25. data/lib/cauldron/example.rb +18 -0
  26. data/lib/cauldron/example_set.rb +36 -0
  27. data/lib/cauldron/histories.rb +53 -0
  28. data/lib/cauldron/history.rb +34 -0
  29. data/lib/cauldron/if_relationship.rb +4 -6
  30. data/lib/cauldron/number_addition_template/add_five.rb +71 -0
  31. data/lib/cauldron/number_addition_template/template.rb +56 -0
  32. data/lib/cauldron/operator.rb +62 -0
  33. data/lib/cauldron/operator/array_reverse_operator.rb +160 -0
  34. data/lib/cauldron/operator/concat_operator.rb +72 -0
  35. data/lib/cauldron/operator/hash_key_value_operator.rb +74 -0
  36. data/lib/cauldron/operator/numeric_operator.rb +115 -0
  37. data/lib/cauldron/operator/string_asterisk_operator.rb +131 -0
  38. data/lib/cauldron/operator/to_s_operator.rb +18 -0
  39. data/lib/cauldron/operator/var_collect_operator.rb +29 -0
  40. data/lib/cauldron/pot.rb +136 -26
  41. data/lib/cauldron/scope.rb +24 -0
  42. data/lib/cauldron/solution/composite.rb +236 -0
  43. data/lib/cauldron/solution/one.rb +49 -0
  44. data/lib/cauldron/statement_generator.rb +298 -0
  45. data/lib/cauldron/template_base.rb +14 -0
  46. data/lib/cauldron/tracer.rb +55 -0
  47. data/lib/cauldron/version.rb +1 -1
  48. data/lib/pry_tester.rb +76 -0
  49. data/ruby_to_sexp.rb +74 -0
  50. data/sandbox.rb +7 -0
  51. data/sexp_to_ruby.rb +150 -0
  52. data/spec/cauldron/actualized_composite_spec.rb +140 -0
  53. data/spec/cauldron/array_collect_template/default_spec.rb +41 -0
  54. data/spec/cauldron/builder_spec.rb +186 -0
  55. data/spec/cauldron/dynamic/add_number_template_spec.rb +30 -0
  56. data/spec/cauldron/dynamic_operator_spec.rb +416 -0
  57. data/spec/cauldron/example_set_spec.rb +49 -0
  58. data/spec/cauldron/example_spec.rb +33 -0
  59. data/spec/cauldron/histories_spec.rb +135 -0
  60. data/spec/cauldron/history_spec.rb +118 -0
  61. data/spec/cauldron/if_relationship_spec.rb +1 -1
  62. data/spec/cauldron/operator/array_reverse_operator_spec.rb +73 -0
  63. data/spec/cauldron/{concat_operator_spec.rb → operator/concat_operator_spec.rb} +30 -12
  64. data/spec/cauldron/operator/hash_key_value_operator_spec.rb +98 -0
  65. data/spec/cauldron/operator/numeric_operator_spec.rb +110 -0
  66. data/spec/cauldron/operator/string_asterisk_operator_spec.rb +196 -0
  67. data/spec/cauldron/operator/var_collect_operator_spec.rb +38 -0
  68. data/spec/cauldron/pot_spec.rb +176 -14
  69. data/spec/cauldron/solution/composite_spec.rb +421 -0
  70. data/spec/cauldron/solution/one_spec.rb +24 -0
  71. data/spec/cauldron/statement_generator_spec.rb +211 -0
  72. data/spec/cauldron/terminal_spec.rb +2 -2
  73. data/spec/spec_helper.rb +5 -1
  74. data/spec/support/code_matcher.rb +55 -0
  75. data/spec/support/include_instance_of_matcher.rb +9 -0
  76. data/spec/support/shared_examples_for_leaf_operators.rb +22 -0
  77. data/spec/support/shared_examples_for_operators.rb +23 -0
  78. data/syntax_spec.txt +2 -0
  79. metadata +104 -41
  80. data/README +0 -1
  81. data/README.rdoc +0 -19
  82. data/VERSION +0 -1
  83. data/features/cauldron_new_approach.feature +0 -46
  84. data/lib/cauldron/array_reverse_operator.rb +0 -39
  85. data/lib/cauldron/concat_operator.rb +0 -34
  86. data/lib/cauldron/numeric_operator.rb +0 -45
  87. data/lib/cauldron/relationship.rb +0 -5
  88. data/spec/cauldron/array_reverse_operator_spec.rb +0 -59
  89. data/spec/cauldron/numeric_operator_spec.rb +0 -70
@@ -0,0 +1,24 @@
1
+ module Cauldron
2
+
3
+ class Scope
4
+ include Enumerable
5
+
6
+ attr_reader :variables
7
+
8
+ def initialize(variables)
9
+ @variables = variables
10
+ end
11
+
12
+ def new_variable!
13
+ #@variables << "var#{variables.length+1}"
14
+ @variables << "var#{variables.length}"
15
+ @variables.last
16
+ end
17
+
18
+ def [](index)
19
+ variables[index]
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,236 @@
1
+ module Cauldron::Solution
2
+
3
+ class Composite
4
+
5
+ attr_reader :operators
6
+
7
+ def initialize(children)
8
+ raise StandardError.new('inital value should be an array') unless children.kind_of?(Array)
9
+ @operators = children
10
+ end
11
+
12
+ def record(example)
13
+ # TODO params passed twice - and example not used at all
14
+ insert_tracking(example.params).process(example)
15
+ end
16
+
17
+ def end_points
18
+ results = []
19
+ operators.each do |x|
20
+ if x.content.branch?
21
+ results << [0,x.children.length]
22
+ end
23
+ end
24
+ results << [operators.length]
25
+ end
26
+
27
+ def clone_solution
28
+ #self.clone
29
+ tree_operators = operators.collect do |node|
30
+ Tree::TreeNode.new('x', node.content.clone_statement)
31
+ end
32
+ Composite.new(tree_operators)
33
+ end
34
+
35
+ def add_statement_at(statement, point)
36
+ if point.length == 2
37
+ container = self.operators[0]
38
+ #return self if container.length > 1 # TODO: Quick hack to get it working
39
+ container << Tree::TreeNode.new('SASA', statement)
40
+ elsif point.length == 1
41
+ operators << Tree::TreeNode.new('SASA', statement)
42
+ else
43
+ raise StandardError.new('Have not written code: '+point.inspect)
44
+ end
45
+ self
46
+ end
47
+
48
+ def insert_tracking(params)
49
+ scope = Cauldron::Scope.new(params.clone)
50
+
51
+ # TODO Might be useful
52
+ # trace = TracePoint.new(:call) do |tp|
53
+ # p [tp.lineno, tp.event, tp.raised_exception]
54
+ # end
55
+
56
+
57
+ # NEW: Implementation
58
+ m = %Q{
59
+ def function(#{params.join(',')})
60
+ #{to_ruby(Cauldron::Scope.new(params.clone))}
61
+ end
62
+ }
63
+
64
+ sexp = Ripper::SexpBuilder.new(m).parse
65
+ rendered_code = Sorcerer.source(sexp, indent: true)
66
+ caret = Cauldron::Caret.new
67
+
68
+ rendered_code = Sorcerer.source(sexp, indent: true).gsub(/end/,"\nend").split("\n").reject(&:empty?).join("\n")
69
+
70
+ # Generate tracking code with pending substitutions
71
+ tracked_code = []
72
+ rendered_code.each_line do |line|
73
+ #if line.match /end\s+/
74
+ if line.match /end/
75
+ tracked_code << Sorcerer.source(Ripper::SexpBuilder.new(Cauldron::Tracer.substitue_tracking).parse) #Sorcerer.source(Cauldron::Tracer.substitue_tracking)
76
+ end
77
+ tracked_code << line
78
+ end
79
+ sexp = Ripper::SexpBuilder.new(tracked_code.join("\n")).parse
80
+ code_tracking = Sorcerer.source(sexp, indent: true)
81
+ code_tracking.split("\n")
82
+
83
+ current_line = -1
84
+ total_lines = 0
85
+ new_tracked_code = []
86
+ last_line = nil
87
+ relative_line = 0
88
+ placeholder = nil
89
+ point = [0,0]
90
+ current_depth = 0
91
+ caret = Cauldron::Caret.new
92
+
93
+ points = end_points
94
+
95
+ code_tracking.split("\n").each do |line|
96
+
97
+ if line.match /record/
98
+ depth = (line.match(/^(\s+)/)[0].length / 2) -1
99
+ if depth > current_depth
100
+ relative_line = 0
101
+ end
102
+ current_depth = depth
103
+
104
+ new_tracked_code << last_line
105
+ new_tracked_code << Sorcerer.source(
106
+ Cauldron::Tracer.tracking(relative_line, depth, total_lines, points.shift)
107
+ )
108
+ new_tracked_code << placeholder
109
+ else
110
+ total_lines += 1
111
+
112
+ unless line['=']
113
+ placeholder = "#{'placeholder_'+rand(10000000000).to_s}"
114
+ last_line = "#{placeholder} = "+line
115
+ end
116
+
117
+ if last_line
118
+ if !last_line.match(/\s+end/).nil? || !last_line.match(/function/).nil? # || last_line.match /function/
119
+ last_line = nil
120
+ placeholder = nil
121
+ end
122
+ end
123
+
124
+ if line.match /end$/
125
+ unless line.strip == 'end'
126
+ line = line.gsub(/end$/,'')
127
+ end
128
+ end
129
+
130
+ new_tracked_code << line
131
+ current_line += 1
132
+ end
133
+ #total_lines += 1
134
+ end
135
+
136
+ # NOTE: Keep this to debug before conversion of S-EXP
137
+ sexp = Ripper::SexpBuilder.new(new_tracked_code.join("\n")).parse
138
+
139
+ Cauldron::Tracer.new(sexp)
140
+
141
+ end
142
+
143
+ def reset_and_track(caret)
144
+ caret.return_depth(0)
145
+ Cauldron::Tracer.tracking(caret.line, caret.current_depth, caret.total_lines)
146
+ end
147
+
148
+ def to_sexp(scope=Cauldron::Scope.new)
149
+
150
+ res = operators.collect do |operator|
151
+ #begin
152
+ operator.content.to_ruby(scope, operator.children)
153
+ # rescue NoMethodError => e
154
+ # binding.pry
155
+ # end
156
+
157
+ end.join("\n")
158
+
159
+ sexp = Ripper::SexpBuilder.new(res).parse
160
+ return sexp
161
+ end
162
+
163
+ def to_ruby(scope)
164
+ return '' if operators.empty?
165
+ Sorcerer.source(to_sexp(scope))
166
+ end
167
+
168
+ def add_first_statement(statement)
169
+ [:stmts_add, [:stmts_new], statement]
170
+ end
171
+
172
+ def add_statement(statement, inner)
173
+ [:stmts_add, inner, statement]
174
+ end
175
+
176
+ def solution?(problems)
177
+ o = Object.new
178
+ m = %Q{
179
+ def function(#{problems.variables.join(',')})
180
+ #{to_ruby(problems.scope)}
181
+ end
182
+ }
183
+ o.instance_eval(m)
184
+
185
+ #o.function *problems.examples.first.arguments
186
+ problems.all? do |example|
187
+ o.function(*example.arguments) == example.response
188
+ end
189
+
190
+ # TODO: Remove this resque - it is just a temp
191
+ rescue NoMethodError => e
192
+ return false
193
+ rescue NameError => e
194
+ return false
195
+ rescue TypeError => e
196
+ return false
197
+ end
198
+
199
+ # TODO Drop this method
200
+ def successful?(problem)
201
+
202
+ # # TODO track the parameters of the operator
203
+ # operators.trace(problem)
204
+
205
+ # # TODO For now just evalute the code
206
+ # return true if problem[:arguments].first == problem[:response]
207
+ # false
208
+
209
+ pt = PryTester.new
210
+
211
+ args = problem.arguments
212
+ variables = problem.params #(0...args.length).collect {|x| 'var'+x.to_s}
213
+ a = [
214
+ 'def function('+variables.join(',')+');'+self.to_ruby(variables)+"; end",
215
+ 'function('+problem.arguments.collect {|x| to_programme(x) }.join(',')+')'
216
+ ]
217
+
218
+ res = pt.eval(
219
+ ['def function('+variables.join(',')+');'+self.to_ruby(variables)+"; end", 'function('+problem.arguments.collect {|x| to_programme(x) }.join(',')+')']
220
+ )
221
+
222
+ problem.response == res
223
+ end
224
+
225
+ def to_programme(value)
226
+ if value.kind_of?(String)
227
+ return %Q{'#{value}'}
228
+ end
229
+ value.to_s
230
+ end
231
+
232
+ # TODO Add a safety evalutor
233
+
234
+ end
235
+
236
+ end
@@ -0,0 +1,49 @@
1
+ # def function(var0)
2
+ # var0.collect { |x| x * 2 }
3
+ # end
4
+
5
+ module Cauldron::Solution
6
+
7
+ class One
8
+
9
+ def initialize
10
+
11
+ end
12
+
13
+ def successful?(problem)
14
+ return false unless problem.arguments.first.kind_of?(Array)
15
+ return false unless problem.arguments.first[0] * 2 == problem.response[0]
16
+ return false unless problem.arguments.first[1] * 2 == problem.response[1]
17
+ true
18
+ end
19
+
20
+ def to_ruby(variables)
21
+ sexp =
22
+ [:method_add_block,
23
+ [:call,
24
+ [:vcall,
25
+ [:@ident, variables[0]]
26
+ ],
27
+ :".",
28
+ [:@ident, "collect"]
29
+ ],
30
+ [:brace_block,
31
+ [:block_var,
32
+ [:params,
33
+ [[:@ident, "x"]]
34
+ ]
35
+ ],
36
+ [
37
+ :binary,
38
+ [:var_ref, [:@ident, "x"]],
39
+ :*,
40
+ [:@int, "2"]
41
+ ]
42
+ ]
43
+ ]
44
+ Sorcerer.source(sexp)
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,298 @@
1
+ module Cauldron
2
+
3
+ class StatementGenerator
4
+
5
+ # Build appropriate classes that can build appropriate instances - instances
6
+ # must have the constants
7
+ def build(instance,dynamic_methods,declare_variable=false)
8
+ dynamic_methods.collect do |x|
9
+ build_template(instance,x)
10
+ end
11
+ end
12
+
13
+ # TODO Change to build_blue_print
14
+ def build_template(instance, dynamic_method)
15
+ build_class(instance, dynamic_method)
16
+ end
17
+
18
+ def sexp_method_to_ruby(instance, dynamic_method)
19
+ %Q{
20
+ def to_ruby(scope, operators)
21
+ Sorcerer.source self.to_sexp(scope, operators)
22
+ end
23
+ }
24
+ end
25
+
26
+ def sexp_method_to_desc
27
+ %Q{
28
+ def to_desc
29
+ to_ruby( Cauldron::Scope.new(['var0']), [] )
30
+ end
31
+ }
32
+ end
33
+
34
+ def method_to_sexp(instance, dynamic_method)
35
+
36
+ # Does it expect arguments?
37
+ begin
38
+ instance.send(dynamic_method)
39
+ rescue ArgumentError => e
40
+
41
+ number_of_arguments = e.message.match(/(\d+)\)/)[1].to_i
42
+
43
+ #statement = "scope[@indexes[0]] #{dynamic_method}"
44
+ statement = "scope[@indexes[0]]"
45
+
46
+ to_sexp_method = %Q^
47
+ def to_sexp(scope, operators)
48
+ Ripper::SexpBuilder.new(#{statement}).parse
49
+ end
50
+ ^
51
+ # to_sexp_method = %Q^
52
+ # def to_sexp(scope, operators)
53
+ # Ripper::SexpBuilder.new("\#{scope[@indexes[0]]} + \#{constant}").parse
54
+ # end
55
+ # ^
56
+ return to_sexp_method
57
+ end
58
+
59
+ if instance.send(dynamic_method).class == Enumerator
60
+ %Q^
61
+ def to_sexp(scope, operators)
62
+ scope_var = scope.new_variable!
63
+ scope_var_two = scope.new_variable!
64
+ dynamic_method = '#{dynamic_method}'
65
+
66
+ first_variable = 'var'+@indexes[0].to_s
67
+
68
+ a = "\#{scope_var} = \#{first_variable}.\#{dynamic_method} do |\#{scope_var_two}|"+"\n"
69
+ a += operators.collect {|x| x.content.to_ruby(scope, x.children) }.join("\n")
70
+ a += "\n"+"end"+"\n"
71
+ puts a
72
+ Ripper::SexpBuilder.new(a).parse
73
+
74
+ end
75
+ ^
76
+ else
77
+ %Q{
78
+ def to_sexp(scope, operators)
79
+ first_variable = 'var'+@indexes[0].to_s
80
+ [:call,
81
+ [:vcall, [:@ident, first_variable ]],
82
+ :".",
83
+ [:@ident, "#{dynamic_method}"]
84
+ ]
85
+ end
86
+ }
87
+ end
88
+ end
89
+
90
+ def requires_arguments?(instance, dynamic_method)
91
+ instance.send(dynamic_method)
92
+ false
93
+ rescue ArgumentError => e
94
+ true
95
+ end
96
+
97
+ def expects_block?(instance, dynamic_method)
98
+ instance.send(dynamic_method).class == Enumerator
99
+ end
100
+
101
+ def branch_method(instance, dynamic_method)
102
+ if requires_arguments?(instance, dynamic_method)
103
+ return %q{
104
+ def branch?
105
+ false
106
+ end
107
+ }
108
+ end
109
+
110
+ if expects_block?(instance, dynamic_method)
111
+ return %q{
112
+ def branch?
113
+ true
114
+ end
115
+ }
116
+ end
117
+ %q{
118
+ def branch?
119
+ false
120
+ end
121
+ }
122
+ end
123
+
124
+ def template_sexp(instance, dynamic_method)
125
+
126
+ res = %Q{
127
+
128
+ #{sexp_method_to_ruby(instance, dynamic_method)}
129
+
130
+ #{method_to_sexp(instance, dynamic_method)}
131
+
132
+ #{sexp_method_to_desc}
133
+
134
+ #{branch_method(instance, dynamic_method)}
135
+
136
+ def self.instances(histories, composite, examples, insert_points)
137
+
138
+ # TEMP
139
+ unless examples.class == ExampleSet
140
+ raise StandardError.new('Examples should be an example')
141
+ end
142
+
143
+ # Print out each insertable statements
144
+ scope = examples.scope
145
+
146
+ # self.init([0]).to_ruby(scope)
147
+ # - this will print out "var0.chop"
148
+
149
+ # Get the variables available at each point
150
+ results = []
151
+
152
+ insert_points.each do |point|
153
+
154
+ # Find the variables at a particular point
155
+ # TODO Change to test
156
+ contexts = histories.contexts_at(point)
157
+ composites = context_instances(contexts)
158
+
159
+ composites.each do |x|
160
+ if contexts.all? do |context|
161
+ x.context_realizable?(context)
162
+ end
163
+ results << extend_actualized_composite(x, composite, examples, point)
164
+ end
165
+ end
166
+
167
+ end
168
+
169
+ results
170
+ end
171
+
172
+ }
173
+ Ripper::SexpBuilder.new(res).parse
174
+ end
175
+
176
+ def dynamic_template_name(instance, dynamic_method)
177
+ dynamic_method_name = dynamic_method.to_s.gsub(/\+/,'Add')
178
+ dynamic_name = ('Dynamic'+'_'+instance.class.to_s+'_'+dynamic_method_name.to_s).camelize
179
+ dynamic_name+'Template'
180
+ end
181
+
182
+ def default_template(instance, dynamic_method)
183
+ blue_print = build_class(instance, dynamic_method)
184
+ blue_print.statement_classes.first
185
+ end
186
+
187
+ def build_class(instance, dynamic_method)
188
+ sexp = template_sexp(instance, dynamic_method)
189
+ information = { constants: false }
190
+
191
+ template_name = dynamic_template_name(instance, dynamic_method)
192
+
193
+ # http://ruby-doc.org/core-2.3.0/Class.html
194
+ # http://stackoverflow.com/questions/4113479/dynamic-class-definition-with-a-class-name
195
+ unless Object.const_defined? template_name
196
+ c = Object.const_set(
197
+ template_name,
198
+ Class.new do
199
+
200
+ attr_reader :indexes, :dynamic_name, :sexp_methods
201
+ attr_accessor :failed_uses
202
+
203
+ def initialize(information, sexp_methods)
204
+ @information, @sexp_methods = information, sexp_methods
205
+ @failed_uses = []
206
+ end
207
+
208
+ # NOTE: These theses classes define the constants
209
+ def statement_classes(examples = nil)
210
+
211
+ #binding.pry
212
+
213
+ # Find the constants
214
+ b = Object.const_set(
215
+ self.class.to_s+rand(4000000).to_s,
216
+ Class.new do
217
+
218
+ include Cauldron::Operator
219
+ include Cauldron::DynamicOperatorModule
220
+
221
+ attr_reader :indexes
222
+ attr_accessor :failed_uses
223
+
224
+ def initialize(indexes)
225
+ @indexes = indexes
226
+ @failed_uses = []
227
+ end
228
+
229
+ def self.context_instances(contexts)
230
+ temp = []
231
+ contexts.each do |context|
232
+ temp << context.keys.collect(&:to_s).select {|x| x.match(/var\d/) }
233
+ end
234
+ results = temp.flatten.uniq
235
+
236
+ variable_numbers = results.collect { |x| x.match(/var(\d+)/)[1] }
237
+ # TODO Presumes that only one variable is passed
238
+ variable_numbers.collect { |x| new([x.to_i])}
239
+ end
240
+
241
+ def self.extend_actualized_composite(x, container, examples, point)
242
+ cloned_container = container.clone_solution
243
+ cloned_container.add_statement_at(x, point)
244
+ cloned_container
245
+ Cauldron::ActualizedComposite.new(cloned_container, examples)
246
+ end
247
+
248
+ def rip2
249
+ %Q{
250
+ def function(var0)
251
+ #{Sorcerer.source(to_sexp(Cauldron::Scope.new(['var0']),[]), indent: true)}
252
+ end
253
+ }
254
+ end
255
+
256
+ end
257
+ )
258
+ b.class_eval(Sorcerer.source(sexp_methods, indent: true))
259
+ [b]
260
+ end
261
+
262
+ end
263
+ )
264
+
265
+
266
+ #a = c.new(information, sexp.clone)
267
+
268
+ #return a.statement_classes.first
269
+ return c.new(information, sexp.clone)
270
+ else
271
+ #a = eval(template_name).new(information, sexp.clone)
272
+ return eval(template_name).new(information, sexp.clone) #a.statement_classes.first
273
+ end
274
+
275
+ raise StandardError.new('Should not get here')
276
+ end
277
+
278
+ end
279
+
280
+ end
281
+
282
+ # TODO stacking mulitple rescues does not work
283
+ # realizable?
284
+
285
+ # def realizable?(composite, examples)
286
+ # o = Object.new
287
+ # composite.to_ruby(examples.scope)
288
+ # sexp = rip(composite,examples)
289
+ # o.instance_eval(Sorcerer.source(sexp, indent: true))
290
+ # begin
291
+ # o.function(examples.examples.first.arguments.first)
292
+ # rescue NoMethodError
293
+ # # TODO Need to record failing tests here
294
+ # failed_uses << { composite:composite, examples: examples}
295
+ # return false
296
+ # end
297
+ # true
298
+ # end