Build2Spec 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ == This is a Really Cool Ruby Thing
2
+ === ( That deserves better documentation than this. )
@@ -0,0 +1,36 @@
1
+
2
+ The Builder class
3
+ - should produce 0 arg method text
4
+ - should produce 1 arg method text
5
+ - should produce 1..3 arg method text, with a block
6
+
7
+ The Builder class
8
+ - should create a standin for Kernel
9
+
10
+ A covered class
11
+ - should return instances of itself from ::new
12
+ - should have an associated StandIn
13
+ - should collect methods from explicit calls
14
+ - should collect methods from #should calls
15
+ - should return false from query? methods
16
+
17
+ An UnknownType
18
+ - should return instances of itself from ::new
19
+ - should guess nil without method calls
20
+ - should not guess nil with method calls
21
+
22
+ The Kernel module
23
+ - should be a legitimate target of method creation
24
+
25
+ An UnknownStandIn
26
+ - should not produce module body even without methods
27
+ - should not produce module body if type guessed
28
+
29
+ A StandIn
30
+ - should not produce module body without methods
31
+ - should produce module body when instance method recorded
32
+ - should produce module body when class method recorded
33
+
34
+ Finished in 0.01738 seconds
35
+
36
+ 18 specifications, 0 failures
@@ -0,0 +1,585 @@
1
+ require "spec"
2
+
3
+ END { Build2Spec::Builder.build unless $UNDER_SPEC }
4
+
5
+ unless $UNDER_SPEC
6
+ require "interpose/lib/spec/expectations"
7
+ require "interpose/lib/spec/runner/specification"
8
+ require "interpose/lib/spec/runner/context_eval"
9
+ module Kernel
10
+ alias b2s_original_require require
11
+ def require(path)
12
+ Build2Spec::Builder::register_file(nil, path)
13
+ Build2Spec::Builder::cover_for_modules(path)
14
+ end
15
+
16
+ alias b2s_original_autoload autoload
17
+ def autoload(mod, filename)
18
+ Build2Spec::Builder::register_file(mod, filename)
19
+ b2s_original_autoload(mod, filename)
20
+ Build2Spec::Builder::cover_for_modules(filename)
21
+ end
22
+
23
+ def create_files_in(path)
24
+ Build2Spec::Builder.create_files_in(path)
25
+ end
26
+ end
27
+
28
+
29
+ class Module
30
+ def const_missing(name)
31
+ Build2Spec::Builder.create_covered_module(name, self)
32
+ end
33
+ end
34
+
35
+ class Object
36
+ def self.const_missing(name)
37
+ Build2Spec::Builder.create_covered_module(name, self)
38
+ end
39
+ end
40
+ end
41
+
42
+ module Build2Spec
43
+ class Builder
44
+ @module_homes = Hash.new {|h,k| h.has_key?(nil) ? h[nil] : nil }
45
+ @create_files_in = nil
46
+
47
+ class UnresolvedPath
48
+ def initialize(path)
49
+ @path = path
50
+ end
51
+
52
+ def resolve(directory)
53
+ unless /.rb$/ =~ @path
54
+ @path += ".rb"
55
+ end
56
+ return File::join(directory, @path)
57
+ end
58
+ end
59
+
60
+ @standins = {}
61
+
62
+ class << self
63
+ attr_accessor :module_homes
64
+ attr_accessor :standins
65
+
66
+ def create_files_in(path)
67
+ @create_files_in = path
68
+ end
69
+
70
+ def cover_for_modules(path)
71
+ existing_modules = all_modules
72
+ begin
73
+ b2s_original_require(path)
74
+ rescue LoadError
75
+ return
76
+ end
77
+
78
+ new_modules = all_modules - existing_modules
79
+ new_modules,contexts = new_modules.partition{|m| m.include?(Kernel)}
80
+ new_modules.sort!{|l, r| l.name.delete("^:").length <=> r.name.delete("^:").length}
81
+
82
+ new_modules.each do |mod|
83
+ cover_for_module(mod)
84
+ end
85
+ end
86
+
87
+ def all_modules
88
+ list = []
89
+ ObjectSpace.each_object(Module){|m| list << m}
90
+ list
91
+ end
92
+
93
+ def register_file(mod, file)
94
+ module_homes[mod] = resolve_path(file)
95
+ end
96
+
97
+ def resolve_path(file)
98
+ $:.each do |lib_path|
99
+ path = File::expand_path(File::join(lib_path, file))
100
+ unless /.rb$/ =~ path
101
+ path += ".rb"
102
+ end
103
+ if File::exists?(path) and File::file?(path)
104
+ return path
105
+ end
106
+ end
107
+
108
+ return UnresolvedPath.new(file)
109
+ end
110
+
111
+ def build
112
+ create_in = @create_files_in || $:.first
113
+
114
+ FakingMethods::unknown_types.each do |type|
115
+ type.guess!
116
+ end
117
+
118
+ @module_homes.each_pair do |mod, path|
119
+ if path.respond_to? :resolve
120
+ @module_homes[mod] = path.resolve(create_in)
121
+ end
122
+ end
123
+
124
+ if @module_homes.values.empty?
125
+ raise "No files designated to generate code into. Use require or autoload to designate code destinations!"
126
+ end
127
+
128
+ @standins.each_value do |standin|
129
+ unless File::exists?(@module_homes[standin.name])
130
+ puts "Creating new code file: #{@module_homes[standin.name]}"
131
+ end
132
+ File::open(@module_homes[standin.name], "a") do |file|
133
+ file.write(standin.mod_text + "\n")
134
+ end
135
+ end
136
+ end
137
+
138
+ def add_standin(nesting_path, name, standin)
139
+ nesting_path = nesting_path.name.to_s if Module === nesting_path
140
+ list = standin_location(nesting_path)
141
+ list[name] = standin
142
+ end
143
+
144
+ def standin_for(nesting_path)
145
+ nesting_path = nesting_path.name.to_s if Module === nesting_path
146
+ m = /(?:(.*)::)?(.*)/.match nesting_path
147
+ list = standin_location(m[1])
148
+ list[m[2]]
149
+ end
150
+
151
+ def standin_location(nesting_path)
152
+ list = @standins
153
+ return list if nesting_path.nil?
154
+ classpath = nesting_path.split("::")
155
+ create_missing_under = ::Object
156
+ classpath.each do |klass|
157
+ unless list.has_key?(klass)
158
+ unless create_missing_under.const_defined?(klass.intern)
159
+ raise RuntimeError, "Missing #{klass} in #{classpath}"
160
+ end
161
+ list[klass] = StandIn.new(create_missing_under.const_get(klass.intern))
162
+ end
163
+ create_missing_under = list[klass].covering_for
164
+ list = list[klass].nested
165
+ end
166
+ return list
167
+ end
168
+
169
+ def cover_for_module(klass)
170
+ m = /(?:(.*)::)?(.*)/.match klass.name.to_s
171
+ nesting = m[1] || ""
172
+ class_name = m[2]
173
+ klass.class_eval { include CoveredModule }
174
+ class_name = klass.name.to_s.sub(/.*::/, "")
175
+ Build2Spec::Builder::add_standin(nesting, class_name, StandIn.new(klass))
176
+ end
177
+
178
+ def create_covered_module(name, nesting, superclass=::Object)
179
+ classes = name.to_s.split("::")
180
+ class_name = classes.pop
181
+ classes.each do |nest|
182
+ unless nesting.const_defined?(nest)
183
+ create_covered_module(nest, nesting)
184
+ end
185
+ nesting = nesting.const_get(nest)
186
+ end
187
+ klass = Class.new(superclass)
188
+ nesting.const_set(class_name, klass)
189
+ UnknownType::add_guess_entry(klass, "#{nesting}::#{name}.new")
190
+ cover_for_module(klass)
191
+ return klass
192
+ end
193
+ end
194
+ end
195
+
196
+ class StandIn
197
+ def initialize(klass)
198
+ @covering_for = klass
199
+ @faked_instance_methods = {}
200
+ @faked_module_methods = {}
201
+ @nested = {}
202
+ @i_am_module = nil
203
+ end
204
+
205
+ attr_reader :covering_for, :faked_instance_methods, :faked_module_methods, :nested
206
+
207
+ Indent=" " * 2
208
+
209
+ def method_text(name, spec, indent)
210
+ args = argument_text(spec)
211
+
212
+ result = spec[2].inspect
213
+ if spec[2].respond_to?(:guess)
214
+ result = spec[2].guess
215
+ end
216
+
217
+ return "def #{name}#{args}\n#{indent}return #{result}\nend\n"
218
+ end
219
+
220
+ def argument_text(spec)
221
+ required = spec[0].begin > 0 ? Range.new("a",(?a + spec[0].begin - 1).chr).to_a : []
222
+ optional = spec[0].end - spec[0].begin > 0 ?
223
+ Range.new((?a + spec[0].begin).chr,(?a + spec[0].end - 1).chr).to_a.map{|a| "#{a}=nil"} : []
224
+
225
+ args = required + optional
226
+ if spec[1]
227
+ args << "&block"
228
+ end
229
+ return args.length > 0 ? "( #{args.join(", ")} )" : ""
230
+ end
231
+
232
+ def is_module
233
+ @i_am_module = "module"
234
+ end
235
+
236
+ def is_class
237
+ @i_am_module = "class"
238
+ end
239
+
240
+ def mod_text(nesting="")
241
+ return "" if empty?
242
+ body = body_text(nesting)
243
+
244
+ text = mod_opener(nesting)
245
+ text << body
246
+ text << "end\n"
247
+ end
248
+
249
+ def nest_standin(other)
250
+ @nested[other] = nil
251
+ end
252
+
253
+ def name
254
+ covering_for.name
255
+ end
256
+
257
+ def empty?
258
+ return faked_instance_methods.values.empty? &&
259
+ faked_module_methods.values.empty? &&
260
+ nested.empty?
261
+ end
262
+
263
+ protected
264
+
265
+ def body_text(nesting)
266
+ spacer( mod_methods_text,
267
+ nested_module_text(nesting_at(nesting), nested),
268
+ instance_methods_text)
269
+ end
270
+
271
+ def spacer(*args)
272
+ args.delete_if{|a| /\A\s*\Z/ =~ a}
273
+ return args.join("\n")
274
+ end
275
+
276
+ def indent
277
+ string = yield
278
+ string = string.split("\n").map do |line|
279
+ Indent + line
280
+ end.join("\n")
281
+ unless string.empty?
282
+ string << "\n"
283
+ end
284
+ string
285
+ end
286
+
287
+ def name_at(nesting)
288
+ name = @covering_for.name
289
+ if not nesting.empty?
290
+ if /^#{nesting}::/ =~ name
291
+ name.sub!(/^#{nesting}::/,"")
292
+ else
293
+ name = "::" + name
294
+ end
295
+ end
296
+ return name
297
+ end
298
+
299
+ def nesting_at(nesting)
300
+ name = name_at(nesting)
301
+ return name if %r"^::" =~ name or nesting.empty?
302
+ return [nesting, name].join("::")
303
+ end
304
+
305
+ def mod_opener(nesting)
306
+ supertext = ""
307
+
308
+ if @covering_for.class != Module and @covering_for.superclass != Object
309
+ supertext = " < #{@covering_for.superclass.name}"
310
+ end
311
+
312
+ is_module if @covering_for.class == Module
313
+
314
+ type = @i_am_module || "class"
315
+ return "#{type} #{name_at(nesting)}#{supertext}\n"
316
+ end
317
+
318
+ def mod_methods_text
319
+ return "" if @faked_module_methods.empty?
320
+ indent do
321
+ "class << self\n" + indent do
322
+ @faked_module_methods.map do |name, spec|
323
+ method_text(name, spec, Indent)
324
+ end.delete_if{|m| m == ""}.join("\n") + "\n"
325
+ end + "end"
326
+ end
327
+ end
328
+
329
+ def instance_methods_text
330
+ return "" if @faked_instance_methods.empty?
331
+ indent do
332
+ @faked_instance_methods.map do |name, spec|
333
+ method_text(name, spec, Indent)
334
+ end.delete_if{|m| m == ""}.join("\n")
335
+ end
336
+ end
337
+
338
+ def nested_module_text(nesting, nested)
339
+ return "" if nested.empty?
340
+ indent do
341
+ mods = nested.values.map do |mod|
342
+ mod.mod_text(nesting)
343
+ end.delete_if{|m| m == ""}.join("\n")
344
+ end
345
+ end
346
+ end
347
+
348
+ class UnknownType; end
349
+
350
+ module FakingMethods
351
+ def initialize(*args, &block)
352
+ return FakingMethods::record_method_spec(return_type_nesting, faked_methods, FakingMethods.current_method, args, block)
353
+ end
354
+
355
+ @@unknown_types = []
356
+ def self.unknown_types
357
+ return @@unknown_types
358
+ end
359
+
360
+ def self.current_method
361
+ called_from = caller.grep(/in `/)[1]
362
+ if (m = /in `([^']*)/.match called_from)
363
+ return m[1].intern
364
+ else
365
+ raise RuntimeError, called_from
366
+ end
367
+ end
368
+
369
+ def self.record_method_spec(nesting, faked_methods, name, args, block)
370
+ method_spec = []
371
+ if (method_spec = faked_methods[name]).nil?
372
+ arity = args.length
373
+
374
+ result = UnknownType.create(nesting)
375
+ if name == :initialize
376
+ result = nil
377
+ elsif /\?$/ =~ name.to_s
378
+ result = false
379
+ end
380
+
381
+ if UnknownType === result
382
+ @@unknown_types << result
383
+ end
384
+
385
+ method_spec = [arity..arity, (not block.nil?), result]
386
+ faked_methods[name] = method_spec
387
+ else
388
+ unless (method_spec[0].include? args.length)
389
+ if(method_spec[0].begin > args.length)
390
+ method_spec[0] = args.length..method_spec[0].end
391
+ else
392
+ method_spec[0] = method_spec[0].begin..args.length
393
+ end
394
+ end
395
+
396
+ method_spec[1] ||= !(block.nil?)
397
+ end
398
+
399
+ #p [:rms, name, method_spec]
400
+
401
+ return method_spec[2]
402
+ end
403
+
404
+ def method_missing(name, *args, &block)
405
+ retval = FakingMethods::record_method_spec(return_type_nesting, faked_methods, name, args, block)
406
+
407
+ if Module === self
408
+ define_method(name, instance_method(:initialize))
409
+ else
410
+ self.class.class_eval do
411
+ define_method(name, self.instance_method(:initialize))
412
+ end
413
+ end
414
+
415
+ return retval
416
+ end
417
+ end
418
+
419
+ module FakingModules
420
+ def const_missing(name)
421
+ Build2Spec::Builder::create_covered_module(name, self)
422
+ end
423
+ end
424
+
425
+ module CoveredModule
426
+ def self.included(sub)
427
+ sub.module_eval do
428
+ def self.faked_methods
429
+ Build2Spec::Builder::standin_for(self).faked_module_methods
430
+ end
431
+
432
+ def self.return_type_nesting
433
+ self.name.to_s
434
+ end
435
+
436
+ include FakingMethods
437
+ end
438
+
439
+ sub.extend FakingMethods
440
+ sub.extend FakingModules
441
+ end
442
+
443
+ def faked_methods
444
+ Build2Spec::Builder::standin_for(self.class).faked_instance_methods
445
+ end
446
+
447
+ def return_type_nesting
448
+ self.class.name.to_s.sub(/(::)?[^:]*$/, "")
449
+ end
450
+ end
451
+
452
+ class UnknownStandIn < StandIn
453
+ def name_at(nesting)
454
+ @covering_for.name || "Build2Spec::UnknownType"
455
+ end
456
+
457
+ def mod_text(nesting="")
458
+ return "" if @covering_for.guessed?
459
+ super
460
+ end
461
+ end
462
+
463
+ class UnknownType
464
+ include FakingMethods
465
+
466
+ @@name_suffix = "A"
467
+
468
+ @@guessing_table = [
469
+ [Fixnum, '42'],
470
+ [String, '"wrong answer"'],
471
+ [Array, '[]'],
472
+ [Hash, '{}'],
473
+ [Object, "Object.new"],
474
+ [Class, @name],
475
+ ]
476
+
477
+ def self.add_guess_entry(klass, exemplar)
478
+ @@guessing_table.unshift [klass, exemplar]
479
+ end
480
+
481
+ def self.create(nesting, name = nil)
482
+ type = self.allocate
483
+ type.setup(nesting, name)
484
+ type
485
+ end
486
+
487
+ def superclass
488
+ Object
489
+ end
490
+
491
+ def setup(nesting, name)
492
+ @standin = UnknownStandIn.new(self)
493
+ @guess = nil
494
+ @name = name || "UnknownType#{@@name_suffix}"
495
+ @nesting = nesting
496
+ @@name_suffix.succ!
497
+ Builder::add_standin(nesting, @name, @standin)
498
+ end
499
+
500
+ def inspect
501
+ "<Build2Spec Dummy Return Value>"
502
+ end
503
+
504
+ attr_reader :standin, :name
505
+
506
+ def guess!
507
+ return if faked_methods.empty?
508
+ return if guessed?
509
+
510
+ @@guessing_table.each do |klass, exemplar|
511
+ faked = faked_methods.map{|f| f[0].to_s}
512
+ klass.instance_methods.each do |real|
513
+ faked.delete(real)
514
+ end
515
+ if(faked.empty?)
516
+ @guess = exemplar
517
+ return
518
+ end
519
+ end
520
+ end
521
+
522
+ def guessed?
523
+ not @guess.nil?
524
+ end
525
+
526
+ def guess
527
+ if @guess.nil?
528
+ if standin.empty?
529
+ return "nil"
530
+ else
531
+ init_spec = @standin.faked_instance_methods[:initialize]
532
+ if init_spec.nil?
533
+ return "#@name.new"
534
+ else
535
+ return "#@name.new#{@standin.argument_text(init_spec)}"
536
+ end
537
+ end
538
+ else
539
+ return @guess
540
+ end
541
+ end
542
+
543
+ def new(*args, &block)
544
+ instance = im_a_module
545
+ instance.initialize(*args, &block)
546
+ return instance
547
+ end
548
+
549
+ def im_a_module
550
+ return @instance_type unless @instance_type.nil?
551
+ module_standin = UnknownStandIn.new(self)
552
+ @standin.faked_instance_methods.each_pair do |k,v|
553
+ module_standin.faked_module_methods[k] = v
554
+ end
555
+ @standin = module_standin
556
+ class << self
557
+ def faked_methods
558
+ @standin.faked_module_methods
559
+ end
560
+
561
+ def guess!
562
+ end
563
+ end
564
+
565
+ @instance_type = UnknownType.create(@nesting, @name)
566
+
567
+ @name += "Class"
568
+ @guess = nil
569
+
570
+ return @instance_type
571
+ end
572
+
573
+ def faked_methods
574
+ @standin.faked_instance_methods
575
+ end
576
+
577
+ def return_type_nesting
578
+ @nesting
579
+ end
580
+ end
581
+
582
+ Builder::add_standin(nil, "Kernel", StandIn.new(Kernel))
583
+ end
584
+
585
+
@@ -0,0 +1,28 @@
1
+
2
+
3
+ module Spec
4
+ module Expectations
5
+ @errors = []
6
+
7
+ class << self
8
+
9
+ def clear_errors
10
+ @errors.clear
11
+ end
12
+
13
+ def first_error
14
+ return if @errors.empty?
15
+ raise @errors.first
16
+ end
17
+
18
+ alias b2s_fail_with fail_with
19
+ def fail_with(message, expected=nil, target=nil)
20
+ begin
21
+ b2s_fail_with(message, expected, target)
22
+ rescue Spec::Expectations::ExpectationNotMetError => seenme
23
+ @errors << seenme
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ module Spec
2
+ module Runner
3
+ module ContextEval
4
+ module ModuleMethods
5
+
6
+ def derive_execution_context_class_from_context_superclass
7
+ @execution_context_class = Class.new(context_superclass)
8
+ @execution_context_class.class_eval do
9
+ def const_missing
10
+ ::Build2Spec::Builder.create_covered_module(name, Object)
11
+ end
12
+
13
+ def self.faked_methods
14
+ ::Build2Spec::Builder::standin_for(Kernel).faked_module_methods
15
+ end
16
+
17
+ def faked_methods
18
+ ::Build2Spec::Builder::standin_for(Kernel).faked_instance_methods
19
+ end
20
+
21
+ def self.return_type_nesting
22
+ ""
23
+ end
24
+
25
+ def return_type_nesting
26
+ ""
27
+ end
28
+
29
+ include ::Build2Spec::FakingMethods
30
+ include ::Spec::Runner::ExecutionContext::InstanceMethods
31
+
32
+ extend ::Build2Spec::FakingMethods
33
+ end
34
+ end
35
+
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,17 @@
1
+ module Spec
2
+ module Runner
3
+ class Specification
4
+ def execute_spec(execution_context, errors)
5
+ begin
6
+ Spec::Expectations::clear_errors
7
+ execution_context.instance_eval(&spec_block)
8
+ Spec::Expectations::first_error
9
+ return true
10
+ rescue Exception => e
11
+ errors << e
12
+ return false
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,51 @@
1
+ class SpecXExample
2
+ class Generator
3
+ class << self
4
+ def instance
5
+ return UnknownTypeA.new
6
+ end
7
+ end
8
+
9
+ class UnknownTypeA
10
+ def initialize
11
+ return nil
12
+ end
13
+
14
+ def contexts
15
+ return nil
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ module Kernel
22
+ def eg( &block )
23
+ return nil
24
+ end
25
+ end
26
+
27
+ class SpecXExample
28
+ class Generator
29
+ end
30
+ end
31
+
32
+ class SpecXExample
33
+ class Generator
34
+ end
35
+ end
36
+
37
+ class SpecXExample
38
+ class Generator
39
+ end
40
+ end
41
+
42
+ class SpecXExample
43
+ class Generator
44
+ end
45
+ end
46
+
47
+ class SpecXExample
48
+ class Generator
49
+ end
50
+ end
51
+
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.2
3
+ specification_version: 1
4
+ name: Build2Spec
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.0
7
+ date: 2007-04-03 00:00:00 -07:00
8
+ summary: Rapidly produce code skeletons from rspec specifications
9
+ require_paths:
10
+ - lib
11
+ email: judson@redfivellc.com
12
+ homepage:
13
+ rubyforge_project:
14
+ description: Any automated testing should save you effort. Use the work you did writing your specs first to produce the skeleton of new projects or even just new features. Makes BDD discipline effortless to maintain.
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message: Another tidy package brought to you by the cheerful folks at Red Five
29
+ authors:
30
+ - Judson Lester
31
+ files:
32
+ - lib/build2spec.rb
33
+ - lib/spec_x_example.rb
34
+ - lib/interpose
35
+ - lib/interpose/lib
36
+ - lib/interpose/lib/spec
37
+ - lib/interpose/lib/spec/runner
38
+ - lib/interpose/lib/spec/expectations.rb
39
+ - lib/interpose/lib/spec/runner/specification.rb
40
+ - lib/interpose/lib/spec/runner/context_eval.rb
41
+ - doc/README
42
+ - doc/Specifications
43
+ test_files: []
44
+
45
+ rdoc_options:
46
+ - --diagram
47
+ - --inline-source
48
+ - --main
49
+ - Build2Spec
50
+ - --title
51
+ - Build2Spec
52
+ extra_rdoc_files:
53
+ - doc/README
54
+ - doc/Specifications
55
+ executables: []
56
+
57
+ extensions: []
58
+
59
+ requirements: []
60
+
61
+ dependencies:
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ version_requirement:
65
+ version_requirements: !ruby/object:Gem::Version::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 0.8.0
70
+ version: