jsduck 4.0.1 → 4.1.1

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 (91) hide show
  1. data/Rakefile +14 -0
  2. data/esprima/esprima.js +210 -85
  3. data/jsduck.gemspec +3 -3
  4. data/lib/jsduck/accessors.rb +10 -8
  5. data/lib/jsduck/aggregator.rb +7 -7
  6. data/lib/jsduck/app.rb +24 -164
  7. data/lib/jsduck/app_data.rb +2 -4
  8. data/lib/jsduck/assets.rb +5 -2
  9. data/lib/jsduck/ast.rb +9 -4
  10. data/lib/jsduck/batch_formatter.rb +54 -0
  11. data/lib/jsduck/batch_parser.rb +106 -0
  12. data/lib/jsduck/categories.rb +5 -6
  13. data/lib/jsduck/class.rb +77 -239
  14. data/lib/jsduck/class_doc_expander.rb +0 -5
  15. data/lib/jsduck/class_formatter.rb +14 -10
  16. data/lib/jsduck/class_name.rb +23 -0
  17. data/lib/jsduck/class_writer.rb +9 -8
  18. data/lib/jsduck/doc_ast.rb +5 -6
  19. data/lib/jsduck/doc_formatter.rb +61 -272
  20. data/lib/jsduck/enum.rb +4 -4
  21. data/lib/jsduck/esprima.rb +10 -4
  22. data/lib/jsduck/examples.rb +5 -5
  23. data/lib/jsduck/export_writer.rb +62 -0
  24. data/lib/jsduck/exporter/app.rb +62 -0
  25. data/lib/jsduck/exporter/examples.rb +58 -0
  26. data/lib/jsduck/exporter/full.rb +60 -0
  27. data/lib/jsduck/file_categories.rb +7 -4
  28. data/lib/jsduck/function_ast.rb +99 -0
  29. data/lib/jsduck/grouped_asset.rb +27 -16
  30. data/lib/jsduck/guide_writer.rb +8 -7
  31. data/lib/jsduck/guides.rb +31 -29
  32. data/lib/jsduck/icons.rb +12 -1
  33. data/lib/jsduck/images.rb +3 -3
  34. data/lib/jsduck/importer.rb +7 -7
  35. data/lib/jsduck/index_html.rb +20 -6
  36. data/lib/jsduck/inherit_doc.rb +9 -12
  37. data/lib/jsduck/inline/example.rb +42 -0
  38. data/lib/jsduck/inline/img.rb +55 -0
  39. data/lib/jsduck/inline/link.rb +227 -0
  40. data/lib/jsduck/inline/video.rb +67 -0
  41. data/lib/jsduck/inline_examples.rb +7 -7
  42. data/lib/jsduck/js_parser.rb +5 -4
  43. data/lib/jsduck/lint.rb +14 -2
  44. data/lib/jsduck/logger.rb +5 -6
  45. data/lib/jsduck/members_index.rb +132 -0
  46. data/lib/jsduck/merger.rb +3 -9
  47. data/lib/jsduck/options.rb +39 -41
  48. data/lib/jsduck/override.rb +3 -7
  49. data/lib/jsduck/relations.rb +9 -9
  50. data/lib/jsduck/renderer.rb +3 -3
  51. data/lib/jsduck/return_values.rb +72 -0
  52. data/lib/jsduck/search_data.rb +16 -20
  53. data/lib/jsduck/shortener.rb +58 -0
  54. data/lib/jsduck/signature_renderer.rb +1 -2
  55. data/lib/jsduck/source/file.rb +98 -0
  56. data/lib/jsduck/source/file_parser.rb +72 -0
  57. data/lib/jsduck/source/writer.rb +89 -0
  58. data/lib/jsduck/tag/aside.rb +1 -1
  59. data/lib/jsduck/tag/since.rb +1 -1
  60. data/lib/jsduck/template_dir.rb +2 -2
  61. data/lib/jsduck/util/html.rb +27 -0
  62. data/lib/jsduck/util/io.rb +32 -0
  63. data/lib/jsduck/util/json.rb +60 -0
  64. data/lib/jsduck/util/null_object.rb +23 -0
  65. data/lib/jsduck/util/os.rb +14 -0
  66. data/lib/jsduck/util/parallel.rb +34 -0
  67. data/lib/jsduck/util/singleton.rb +35 -0
  68. data/lib/jsduck/util/stdout.rb +33 -0
  69. data/lib/jsduck/videos.rb +5 -5
  70. data/lib/jsduck/web_writer.rb +79 -0
  71. data/lib/jsduck/welcome.rb +6 -6
  72. data/spec/class_factory.rb +20 -0
  73. metadata +32 -20
  74. data/lib/jsduck/api_exporter.rb +0 -48
  75. data/lib/jsduck/app_exporter.rb +0 -60
  76. data/lib/jsduck/examples_exporter.rb +0 -56
  77. data/lib/jsduck/full_exporter.rb +0 -35
  78. data/lib/jsduck/html.rb +0 -25
  79. data/lib/jsduck/inline_img.rb +0 -53
  80. data/lib/jsduck/inline_video.rb +0 -58
  81. data/lib/jsduck/io.rb +0 -30
  82. data/lib/jsduck/json_duck.rb +0 -52
  83. data/lib/jsduck/lexer.rb +0 -251
  84. data/lib/jsduck/null_object.rb +0 -19
  85. data/lib/jsduck/os.rb +0 -11
  86. data/lib/jsduck/parallel_wrap.rb +0 -32
  87. data/lib/jsduck/source_file.rb +0 -97
  88. data/lib/jsduck/source_file_parser.rb +0 -70
  89. data/lib/jsduck/source_writer.rb +0 -87
  90. data/lib/jsduck/stats.rb +0 -103
  91. data/lib/jsduck/stdout.rb +0 -31
@@ -2,8 +2,8 @@ Gem::Specification.new do |s|
2
2
  s.required_rubygems_version = ">= 1.3.5"
3
3
 
4
4
  s.name = 'jsduck'
5
- s.version = '4.0.1'
6
- s.date = '2012-08-21'
5
+ s.version = '4.1.1'
6
+ s.date = '2012-09-14'
7
7
  s.summary = "Simple JavaScript Duckumentation generator"
8
8
  s.description = "Documentation generator for Sencha JS frameworks"
9
9
  s.homepage = "https://github.com/senchalabs/jsduck"
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
12
12
  s.rubyforge_project = s.name
13
13
 
14
14
  s.files = `git ls-files`.split("\n").find_all do |file|
15
- file !~ /spec.rb$/ && file !~ /benchmark/ && file !~ /template\// && file !~ /opt\//
15
+ file !~ /spec.rb$/ && file !~ /benchmark/ && file !~ /template\// && file !~ /opt\// && file !~ /comments\//
16
16
  end
17
17
  # Add files not in git
18
18
  s.files += Dir['template-min/**/*']
@@ -9,37 +9,39 @@ module JsDuck
9
9
  # not added.
10
10
  def create(cls)
11
11
  # Grab all configs tagged as @accessor
12
- accessors = cls[:members][:cfg].find_all {|cfg| cfg[:accessor] }
12
+ accessors = cls[:members].find_all {|m| m[:tagname] == :cfg && m[:accessor] }
13
13
 
14
14
  # Build lookup tables of method and event names
15
- methods = build_lookup_table(cls[:members][:method])
16
- events = build_lookup_table(cls[:members][:event])
15
+ methods = build_lookup_table(cls[:members], :method)
16
+ events = build_lookup_table(cls[:members], :event)
17
17
 
18
18
  accessors.each do |cfg|
19
19
  # add getter if no method with same name exists
20
20
  get = create_getter(cfg)
21
21
  if !methods[get[:name]]
22
- cls[:members][:method] << get
22
+ cls[:members] << get
23
23
  end
24
24
  # add setter if no method with same name exists
25
25
  set = create_setter(cfg)
26
26
  if !methods[set[:name]]
27
- cls[:members][:method] << set
27
+ cls[:members] << set
28
28
  end
29
29
  # for evented accessors
30
30
  if cfg[:evented]
31
31
  # add event if no event with same name exists
32
32
  ev = create_event(cfg)
33
33
  if !events[ev[:name]]
34
- cls[:members][:event] << ev
34
+ cls[:members] << ev
35
35
  end
36
36
  end
37
37
  end
38
38
  end
39
39
 
40
- def build_lookup_table(members)
40
+ def build_lookup_table(members, tagname)
41
41
  map = {}
42
- members.each {|m| map[m[:name]] = m }
42
+ members.each do |m|
43
+ map[m[:name]] = m if m[:tagname] == tagname
44
+ end
43
45
  map
44
46
  end
45
47
 
@@ -81,7 +81,7 @@ module JsDuck
81
81
  def warn_alt_name(cls)
82
82
  file = cls[:files][0][:filename]
83
83
  line = cls[:files][0][:linenr]
84
- Logger.instance.warn(:alt_name, "Name #{cls[:name]} used as both classname and alternate classname", file, line)
84
+ Logger.warn(:alt_name, "Name #{cls[:name]} used as both classname and alternate classname", file, line)
85
85
  end
86
86
 
87
87
  # Merges new class-doc into old one.
@@ -106,8 +106,7 @@ module JsDuck
106
106
  end
107
107
  old[:doc] = old[:doc].length > 0 ? old[:doc] : new[:doc]
108
108
  # Additionally the doc-comment can contain configs and constructor
109
- old[:members][:cfg] += new[:members][:cfg]
110
- old[:members][:method] += new[:members][:method]
109
+ old[:members] += new[:members]
111
110
  end
112
111
 
113
112
  # Tries to place members into classes where they belong.
@@ -138,7 +137,7 @@ module JsDuck
138
137
  end
139
138
 
140
139
  def add_to_class(cls, member)
141
- cls[member[:meta][:static] ? :statics : :members][member[:tagname]] << member
140
+ cls[:members] << member
142
141
  end
143
142
 
144
143
  def add_orphan(node)
@@ -188,8 +187,7 @@ module JsDuck
188
187
  :doc => doc,
189
188
  :mixins => [],
190
189
  :alternateClassNames => [],
191
- :members => Class.default_members_hash,
192
- :statics => Class.default_members_hash,
190
+ :members => [],
193
191
  :aliases => {},
194
192
  :meta => {},
195
193
  :files => [{:filename => "", :linenr => 0, :href => ""}],
@@ -215,7 +213,9 @@ module JsDuck
215
213
  :doc => "The options object passed to {@link Ext.util.Observable#addListener}."
216
214
  }
217
215
  @classes.each_value do |cls|
218
- cls[:members][:event].each {|e| e[:params] << options }
216
+ cls[:members].each do |m|
217
+ m[:params] << options if m[:tagname] == :event
218
+ end
219
219
  end
220
220
  end
221
221
 
@@ -1,31 +1,8 @@
1
- require 'rubygems'
2
- require 'jsduck/aggregator'
3
- require 'jsduck/source_file'
4
- require 'jsduck/doc_formatter'
5
- require 'jsduck/class_formatter'
6
- require 'jsduck/class'
7
- require 'jsduck/relations'
8
- require 'jsduck/inherit_doc'
9
- require 'jsduck/parallel_wrap'
10
- require 'jsduck/logger'
1
+ require 'jsduck/batch_parser'
11
2
  require 'jsduck/assets'
12
- require 'jsduck/json_duck'
13
- require 'jsduck/io'
14
- require 'jsduck/importer'
15
- require 'jsduck/lint'
16
- require 'jsduck/template_dir'
17
- require 'jsduck/class_writer'
18
- require 'jsduck/source_writer'
19
- require 'jsduck/app_data'
20
- require 'jsduck/index_html'
21
- require 'jsduck/api_exporter'
22
- require 'jsduck/full_exporter'
23
- require 'jsduck/app_exporter'
24
- require 'jsduck/examples_exporter'
25
- require 'jsduck/inline_examples'
26
- require 'jsduck/guide_writer'
27
- require 'jsduck/stdout'
28
- require 'fileutils'
3
+ require 'jsduck/meta_tag_registry'
4
+ require 'jsduck/export_writer'
5
+ require 'jsduck/web_writer'
29
6
 
30
7
  module JsDuck
31
8
 
@@ -34,159 +11,42 @@ module JsDuck
34
11
  # Initializes app with JsDuck::Options object
35
12
  def initialize(opts)
36
13
  @opts = opts
37
- # Sets the nr of parallel processes to use.
38
- # Set to 0 to disable parallelization completely.
39
- ParallelWrap.in_processes = @opts.processes
40
- # Turn JSON pretty-printing on/off
41
- JsonDuck.pretty = @opts.pretty_json
42
14
  end
43
15
 
44
- # Call this after input parameters set
16
+ # Main App logic.
45
17
  def run
46
- parsed_files = parallel_parse(@opts.input_files)
47
- result = aggregate(parsed_files)
48
- @relations = filter_classes(result)
49
- InheritDoc.new(@relations).resolve_all
50
- Importer.import(@opts.imports, @relations, @opts.new_since)
51
- Lint.new(@relations).run
18
+ parse
52
19
 
53
- # Initialize guides, videos, examples, ...
54
- @assets = Assets.new(@relations, @opts)
55
-
56
- # Give access to assets from all meta-tags
57
- MetaTagRegistry.instance.assets = @assets
20
+ init_assets
58
21
 
59
22
  if @opts.export
60
- format_classes
61
- FileUtils.rm_rf(@opts.output_dir) unless @opts.output_dir == :stdout
62
- exporters = {
63
- :full => FullExporter,
64
- :api => ApiExporter,
65
- :examples => ExamplesExporter,
66
- }
67
- cw = ClassWriter.new(exporters[@opts.export], @relations, @opts)
68
- cw.write(@opts.output_dir, ".json")
69
- if @opts.export == :examples
70
- gw = GuideWriter.new(exporters[@opts.export], @assets.guides, @opts)
71
- gw.write(@opts.output_dir, ".json")
72
- end
73
- Stdout.instance.flush
23
+ generate_export
74
24
  else
75
- FileUtils.rm_rf(@opts.output_dir)
76
- TemplateDir.new(@opts).write
77
-
78
- IndexHtml.new(@assets, @opts).write
79
-
80
- AppData.new(@relations, @assets, @opts).write(@opts.output_dir+"/data.js")
81
-
82
- # class-formatting is done in parallel which breaks the links
83
- # between source files and classes. Therefore it MUST to be done
84
- # after writing sources which needs the links to work.
85
- if @opts.source
86
- source_writer = SourceWriter.new(parsed_files)
87
- source_writer.write(@opts.output_dir + "/source")
88
- end
89
- format_classes
90
-
91
- if @opts.tests
92
- examples = InlineExamples.new
93
- examples.add_classes(@relations)
94
- examples.add_guides(@assets.guides)
95
- examples.write(@opts.output_dir+"/inline-examples.js")
96
- end
97
-
98
- cw = ClassWriter.new(AppExporter, @relations, @opts)
99
- cw.write(@opts.output_dir+"/output", ".js")
100
-
101
- @assets.write
25
+ generate_web_page
102
26
  end
103
27
  end
104
28
 
105
- # Parses the files in parallel using as many processes as available CPU-s
106
- def parallel_parse(filenames)
107
- ParallelWrap.map(filenames) do |fname|
108
- Logger.instance.log("Parsing", fname)
109
- begin
110
- SourceFile.new(JsDuck::IO.read(fname), fname, @opts)
111
- rescue
112
- Logger.instance.fatal_backtrace("Error while parsing #{fname}", $!)
113
- exit(1)
114
- end
115
- end
116
- end
29
+ private
117
30
 
118
- # Aggregates parsing results sequencially
119
- def aggregate(parsed_files)
120
- agr = Aggregator.new
121
- parsed_files.each do |file|
122
- Logger.instance.log("Aggregating", file.filename)
123
- agr.aggregate(file)
124
- end
125
- agr.classify_orphans
126
- agr.create_global_class
127
- agr.remove_ignored_classes
128
- agr.create_accessors
129
- if @opts.ext4_events == true || (@opts.ext4_events == nil && agr.ext4?)
130
- agr.append_ext4_event_options
131
- end
132
- agr.process_enums
133
- # Ignore override classes after applying them to actual classes
134
- @opts.external_classes += agr.process_overrides.map {|o| o[:name] }
135
- agr.result
31
+ def parse
32
+ @batch_parser = BatchParser.new(@opts)
33
+ @relations = @batch_parser.run
136
34
  end
137
35
 
138
- # Turns all aggregated data into Class objects.
139
- # Depending on --ignore-global either keeps or discards the global class.
140
- # Warnings for global members are printed regardless of that setting,
141
- # but of course can be turned off using --warnings=-global
142
- def filter_classes(docs)
143
- classes = []
144
- docs.each do |d|
145
- cls = Class.new(d)
146
- if d[:name] != "global"
147
- classes << cls
148
- else
149
- # add global class only if --ignore-global not specified
150
- classes << cls unless @opts.ignore_global
36
+ def init_assets
37
+ # Initialize guides, videos, examples, ...
38
+ @assets = Assets.new(@relations, @opts)
151
39
 
152
- # Print warning for each global member
153
- cls.all_local_members.each do |m|
154
- type = m[:tagname].to_s
155
- name = m[:name]
156
- file = m[:files][0]
157
- Logger.instance.warn(:global, "Global #{type}: #{name}", file[:filename], file[:linenr])
158
- end
159
- end
160
- end
161
- Relations.new(classes, @opts.external_classes)
40
+ # Give access to assets from all meta-tags
41
+ MetaTagRegistry.instance.assets = @assets
162
42
  end
163
43
 
164
- # Formats each class
165
- def format_classes
166
- doc_formatter = DocFormatter.new(@relations, @opts)
167
- doc_formatter.img_path = "images"
168
- class_formatter = ClassFormatter.new(@relations, doc_formatter)
169
- # Don't format types when exporting
170
- class_formatter.include_types = !@opts.export
171
- # Format all doc-objects in parallel
172
- formatted_classes = ParallelWrap.map(@relations.classes) do |cls|
173
- files = cls[:files].map {|f| f[:filename] }.join(" ")
174
- Logger.instance.log("Markdown formatting #{cls[:name]}", files)
175
- begin
176
- {
177
- :doc => class_formatter.format(cls.internal_doc),
178
- :images => doc_formatter.images
179
- }
180
- rescue
181
- Logger.instance.fatal_backtrace("Error while formatting #{cls[:name]} #{files}", $!)
182
- exit(1)
183
- end
184
- end
185
- # Then merge the data back to classes sequentially
186
- formatted_classes.each do |cls|
187
- @relations[cls[:doc][:name]].internal_doc = cls[:doc]
188
- cls[:images].each {|img| @assets.images.add(img) }
189
- end
44
+ def generate_export
45
+ ExportWriter.new(@relations, @assets, @opts).write
46
+ end
47
+
48
+ def generate_web_page
49
+ WebWriter.new(@relations, @assets, @batch_parser.parsed_files, @opts).write
190
50
  end
191
51
 
192
52
  end
@@ -1,7 +1,6 @@
1
- require 'jsduck/json_duck'
1
+ require 'jsduck/util/json'
2
2
  require 'jsduck/icons'
3
3
  require 'jsduck/search_data'
4
- require 'jsduck/stats'
5
4
  require 'jsduck/meta_tag_registry'
6
5
 
7
6
  module JsDuck
@@ -16,14 +15,13 @@ module JsDuck
16
15
 
17
16
  # Writes classes, guides, videos, and search data to one big .js file
18
17
  def write(filename)
19
- js = "Docs = " + JsonDuck.generate({
18
+ js = "Docs = " + Util::Json.generate({
20
19
  :data => {
21
20
  :classes => Icons.new.create(@relations.classes),
22
21
  :guides => @assets.guides.to_array,
23
22
  :videos => @assets.videos.to_array,
24
23
  :examples => @assets.examples.to_array,
25
24
  :search => SearchData.new.create(@relations.classes, @assets),
26
- :stats => @opts.stats ? Stats.new.create(@relations.classes) : [],
27
25
  :tests => @opts.tests,
28
26
  :signatures => MetaTagRegistry.instance.signatures,
29
27
  :localStorageDb => @opts.local_storage_db,
@@ -26,12 +26,15 @@ module JsDuck
26
26
  @relations = relations
27
27
  @opts = opts
28
28
 
29
+ doc_formatter = DocFormatter.new(@opts)
30
+ doc_formatter.relations = @relations
31
+
29
32
  @images = Images.new(@opts.images)
30
33
  @welcome = Welcome.create(@opts.welcome)
31
- @guides = Guides.create(@opts.guides, DocFormatter.new(@relations, @opts), @opts)
34
+ @guides = Guides.create(@opts.guides, doc_formatter, @opts)
32
35
  @videos = Videos.create(@opts.videos)
33
36
  @examples = Examples.create(@opts.examples, @opts)
34
- @categories = Categories.create(@opts.categories_path, DocFormatter.new(@relations, @opts), @relations)
37
+ @categories = Categories.create(@opts.categories_path, doc_formatter, @relations)
35
38
  end
36
39
 
37
40
  # Writes out the assets that can be written out separately:
@@ -1,5 +1,6 @@
1
1
  require "jsduck/serializer"
2
2
  require "jsduck/evaluator"
3
+ require "jsduck/function_ast"
3
4
 
4
5
  module JsDuck
5
6
 
@@ -224,7 +225,6 @@ module JsDuck
224
225
  cls[:mixins] = []
225
226
  cls[:aliases] = []
226
227
  cls[:members] = []
227
- cls[:statics] = []
228
228
  cls[:code_type] = :ext_define
229
229
 
230
230
  each_pair_in_object_expression(ast["arguments"][1]) do |key, value, pair|
@@ -254,9 +254,9 @@ module JsDuck
254
254
  when "eventedConfig"
255
255
  cls[:members] += make_configs(value, {:accessor => true, :evented => true})
256
256
  when "statics"
257
- cls[:statics] += make_statics(value)
257
+ cls[:members] += make_statics(value)
258
258
  when "inheritableStatics"
259
- cls[:statics] += make_statics(value, {:inheritable => true})
259
+ cls[:members] += make_statics(value, {:inheritable => true})
260
260
  else
261
261
  detect_method_or_property(cls, key, value, pair)
262
262
  end
@@ -393,7 +393,8 @@ module JsDuck
393
393
  return {
394
394
  :tagname => :method,
395
395
  :name => name,
396
- :params => make_params(ast)
396
+ :params => make_params(ast),
397
+ :chainable => chainable?(ast) && name != "constructor",
397
398
  }
398
399
  end
399
400
 
@@ -405,6 +406,10 @@ module JsDuck
405
406
  end
406
407
  end
407
408
 
409
+ def chainable?(ast)
410
+ FunctionAst.chainable?(ast)
411
+ end
412
+
408
413
  def make_property(name=nil, ast=nil, tagname=:property)
409
414
  return {
410
415
  :tagname => tagname,
@@ -0,0 +1,54 @@
1
+ require 'jsduck/util/parallel'
2
+ require 'jsduck/class_formatter'
3
+ require 'jsduck/doc_formatter'
4
+ require 'jsduck/logger'
5
+
6
+ module JsDuck
7
+
8
+ # Performs the formatting of the doc-object of all classes.
9
+ class BatchFormatter
10
+
11
+ # Formats all classes.
12
+ # Also registers found images in assets.
13
+ def self.format_all!(relations, assets, opts)
14
+ # Format all doc-objects in parallel
15
+ formatted_classes = Util::Parallel.map(relations.classes) do |cls|
16
+
17
+ files = cls[:files].map {|f| f[:filename] }.join(" ")
18
+ Logger.log("Markdown formatting #{cls[:name]}", files)
19
+
20
+ formatter = create_class_formatter(relations, opts)
21
+ begin
22
+ {
23
+ :doc => formatter.format(cls.internal_doc),
24
+ :images => formatter.images
25
+ }
26
+ rescue
27
+ Logger.fatal_backtrace("Error while formatting #{cls[:name]} #{files}", $!)
28
+ exit(1)
29
+ end
30
+ end
31
+
32
+ # Then merge the data back to classes sequentially
33
+ formatted_classes.each do |cls|
34
+ relations[cls[:doc][:name]].internal_doc = cls[:doc]
35
+ cls[:images].each {|img| assets.images.add(img) }
36
+ end
37
+ end
38
+
39
+ # Factory method to create new ClassFormatter instances.
40
+ def self.create_class_formatter(relations, opts)
41
+ doc_formatter = DocFormatter.new(opts)
42
+ doc_formatter.relations = relations
43
+ doc_formatter.img_path = "images"
44
+
45
+ class_formatter = ClassFormatter.new(relations, doc_formatter)
46
+ # Don't format types when exporting
47
+ class_formatter.include_types = !opts.export
48
+
49
+ class_formatter
50
+ end
51
+
52
+ end
53
+
54
+ end