rdoc 6.17.0 → 8.1.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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +193 -0
  3. data/LEGAL.rdoc +6 -0
  4. data/LICENSE.rdoc +4 -0
  5. data/README.md +132 -8
  6. data/RI.md +75 -75
  7. data/doc/markup_reference/markdown.md +659 -0
  8. data/doc/markup_reference/rdoc.rdoc +1169 -0
  9. data/exe/rdoc +2 -2
  10. data/lib/rdoc/code_object/alias.rb +70 -74
  11. data/lib/rdoc/code_object/any_method.rb +308 -293
  12. data/lib/rdoc/code_object/attr.rb +150 -142
  13. data/lib/rdoc/code_object/class_module.rb +802 -715
  14. data/lib/rdoc/code_object/constant.rb +181 -144
  15. data/lib/rdoc/code_object/context/section.rb +140 -130
  16. data/lib/rdoc/code_object/context.rb +925 -941
  17. data/lib/rdoc/code_object/extend.rb +7 -5
  18. data/lib/rdoc/code_object/include.rb +7 -5
  19. data/lib/rdoc/code_object/method_attr.rb +328 -315
  20. data/lib/rdoc/code_object/mixin.rb +97 -92
  21. data/lib/rdoc/code_object/normal_class.rb +77 -78
  22. data/lib/rdoc/code_object/normal_module.rb +61 -59
  23. data/lib/rdoc/code_object/require.rb +23 -39
  24. data/lib/rdoc/code_object/single_class.rb +21 -19
  25. data/lib/rdoc/code_object/top_level.rb +215 -202
  26. data/lib/rdoc/code_object.rb +305 -305
  27. data/lib/rdoc/comment.rb +274 -337
  28. data/lib/rdoc/cross_reference.rb +195 -206
  29. data/lib/rdoc/encoding.rb +105 -103
  30. data/lib/rdoc/erb_partial.rb +13 -11
  31. data/lib/rdoc/erbio.rb +29 -27
  32. data/lib/rdoc/generator/aliki.rb +196 -30
  33. data/lib/rdoc/generator/darkfish.rb +647 -629
  34. data/lib/rdoc/generator/json_index.rb +233 -229
  35. data/lib/rdoc/generator/markup.rb +165 -122
  36. data/lib/rdoc/generator/pot/message_extractor.rb +57 -51
  37. data/lib/rdoc/generator/pot/po.rb +52 -51
  38. data/lib/rdoc/generator/pot/po_entry.rb +138 -132
  39. data/lib/rdoc/generator/pot.rb +85 -81
  40. data/lib/rdoc/generator/ri.rb +23 -19
  41. data/lib/rdoc/generator/template/aliki/DESIGN.md +538 -0
  42. data/lib/rdoc/generator/template/aliki/_aside_toc.rhtml +1 -1
  43. data/lib/rdoc/generator/template/aliki/_footer.rhtml +2 -2
  44. data/lib/rdoc/generator/template/aliki/_head.rhtml +15 -10
  45. data/lib/rdoc/generator/template/aliki/_header.rhtml +29 -44
  46. data/lib/rdoc/generator/template/aliki/_sidebar_extends.rhtml +8 -6
  47. data/lib/rdoc/generator/template/aliki/_sidebar_includes.rhtml +8 -6
  48. data/lib/rdoc/generator/template/aliki/_sidebar_installed.rhtml +1 -1
  49. data/lib/rdoc/generator/template/aliki/_sidebar_pages.rhtml +3 -3
  50. data/lib/rdoc/generator/template/aliki/_sidebar_search.rhtml +3 -3
  51. data/lib/rdoc/generator/template/aliki/_sidebar_sections.rhtml +1 -1
  52. data/lib/rdoc/generator/template/aliki/_sidebar_toggle.rhtml +1 -1
  53. data/lib/rdoc/generator/template/aliki/class.rhtml +71 -61
  54. data/lib/rdoc/generator/template/aliki/css/rdoc.css +796 -323
  55. data/lib/rdoc/generator/template/aliki/index.rhtml +1 -1
  56. data/lib/rdoc/generator/template/aliki/js/aliki.js +119 -113
  57. data/lib/rdoc/generator/template/aliki/js/bash_highlighter.js +167 -0
  58. data/lib/rdoc/generator/template/aliki/js/c_highlighter.js +1 -1
  59. data/lib/rdoc/generator/template/aliki/js/{search.js → search_controller.js} +16 -7
  60. data/lib/rdoc/generator/template/aliki/js/search_navigation.js +105 -0
  61. data/lib/rdoc/generator/template/aliki/js/search_ranker.js +239 -0
  62. data/lib/rdoc/generator/template/aliki/page.rhtml +1 -1
  63. data/lib/rdoc/generator/template/aliki/servlet_not_found.rhtml +1 -1
  64. data/lib/rdoc/generator/template/aliki/servlet_root.rhtml +2 -2
  65. data/lib/rdoc/generator/template/darkfish/_footer.rhtml +1 -1
  66. data/lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml +8 -6
  67. data/lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml +8 -6
  68. data/lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml +1 -1
  69. data/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml +2 -2
  70. data/lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml +1 -1
  71. data/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml +5 -5
  72. data/lib/rdoc/generator/template/darkfish/class.rhtml +29 -32
  73. data/lib/rdoc/generator/template/darkfish/css/rdoc.css +19 -1
  74. data/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml +3 -3
  75. data/lib/rdoc/generator.rb +48 -46
  76. data/lib/rdoc/i18n/locale.rb +99 -95
  77. data/lib/rdoc/i18n/text.rb +109 -105
  78. data/lib/rdoc/i18n.rb +7 -5
  79. data/lib/rdoc/markdown/byte_runtime.rb +80 -0
  80. data/lib/rdoc/markdown.kpeg +52 -33
  81. data/lib/rdoc/markdown.rb +365 -177
  82. data/lib/rdoc/markup/blank_line.rb +25 -23
  83. data/lib/rdoc/markup/block_quote.rb +12 -8
  84. data/lib/rdoc/markup/document.rb +127 -123
  85. data/lib/rdoc/markup/element.rb +21 -0
  86. data/lib/rdoc/markup/formatter.rb +220 -203
  87. data/lib/rdoc/markup/hard_break.rb +30 -27
  88. data/lib/rdoc/markup/heading.rb +163 -77
  89. data/lib/rdoc/markup/include.rb +33 -29
  90. data/lib/rdoc/markup/indented_paragraph.rb +37 -33
  91. data/lib/rdoc/markup/inline_parser.rb +316 -0
  92. data/lib/rdoc/markup/list.rb +80 -88
  93. data/lib/rdoc/markup/list_item.rb +73 -85
  94. data/lib/rdoc/markup/paragraph.rb +23 -19
  95. data/lib/rdoc/markup/parser.rb +501 -497
  96. data/lib/rdoc/markup/pre_process.rb +284 -305
  97. data/lib/rdoc/markup/raw.rb +52 -55
  98. data/lib/rdoc/markup/rule.rb +16 -12
  99. data/lib/rdoc/markup/table.rb +48 -40
  100. data/lib/rdoc/markup/to_ansi.rb +143 -92
  101. data/lib/rdoc/markup/to_bs.rb +86 -102
  102. data/lib/rdoc/markup/to_html.rb +608 -506
  103. data/lib/rdoc/markup/to_html_crossref.rb +219 -210
  104. data/lib/rdoc/markup/to_html_snippet.rb +210 -205
  105. data/lib/rdoc/markup/to_joined_paragraph.rb +40 -41
  106. data/lib/rdoc/markup/to_label.rb +64 -51
  107. data/lib/rdoc/markup/to_markdown.rb +212 -184
  108. data/lib/rdoc/markup/to_rdoc.rb +341 -277
  109. data/lib/rdoc/markup/to_table_of_contents.rb +66 -62
  110. data/lib/rdoc/markup/to_test.rb +62 -50
  111. data/lib/rdoc/markup/to_tt_only.rb +83 -92
  112. data/lib/rdoc/markup/verbatim.rb +62 -58
  113. data/lib/rdoc/markup.rb +196 -215
  114. data/lib/rdoc/options.rb +1063 -1076
  115. data/lib/rdoc/parser/c.rb +1039 -1036
  116. data/lib/rdoc/parser/changelog.rb +320 -287
  117. data/lib/rdoc/parser/markdown.rb +17 -13
  118. data/lib/rdoc/parser/rbs.rb +279 -0
  119. data/lib/rdoc/parser/rd.rb +17 -13
  120. data/lib/rdoc/parser/ruby.rb +1231 -2222
  121. data/lib/rdoc/parser/ruby_colorizer.rb +303 -0
  122. data/lib/rdoc/parser/simple.rb +31 -27
  123. data/lib/rdoc/parser/text.rb +12 -8
  124. data/lib/rdoc/parser.rb +230 -221
  125. data/lib/rdoc/rbs_helper.rb +186 -0
  126. data/lib/rdoc/rd/inline.rb +57 -53
  127. data/lib/rdoc/rd.rb +90 -88
  128. data/lib/rdoc/rdoc.rb +547 -366
  129. data/lib/rdoc/ri/driver.rb +1141 -1130
  130. data/lib/rdoc/ri/formatter.rb +7 -3
  131. data/lib/rdoc/ri/paths.rb +140 -136
  132. data/lib/rdoc/ri/servlet.rb +456 -0
  133. data/lib/rdoc/ri/store.rb +4 -2
  134. data/lib/rdoc/ri/task.rb +55 -51
  135. data/lib/rdoc/ri.rb +14 -11
  136. data/lib/rdoc/rubygems_hook.rb +194 -192
  137. data/lib/rdoc/server.rb +462 -0
  138. data/lib/rdoc/stats/normal.rb +46 -42
  139. data/lib/rdoc/stats/quiet.rb +39 -35
  140. data/lib/rdoc/stats/verbose.rb +35 -31
  141. data/lib/rdoc/stats.rb +363 -338
  142. data/lib/rdoc/store.rb +919 -725
  143. data/lib/rdoc/task.rb +260 -255
  144. data/lib/rdoc/text.rb +164 -240
  145. data/lib/rdoc/token_stream.rb +88 -106
  146. data/lib/rdoc/tom_doc.rb +203 -201
  147. data/lib/rdoc/version.rb +1 -1
  148. data/lib/rdoc.rb +35 -7
  149. data/lib/rubygems_plugin.rb +2 -11
  150. data/rdoc-logo.svg +43 -0
  151. data/rdoc.gemspec +9 -7
  152. metadata +46 -32
  153. data/CONTRIBUTING.rdoc +0 -219
  154. data/ExampleMarkdown.md +0 -39
  155. data/ExampleRDoc.rdoc +0 -210
  156. data/lib/rdoc/code_object/anon_class.rb +0 -10
  157. data/lib/rdoc/code_object/ghost_method.rb +0 -6
  158. data/lib/rdoc/code_object/meta_method.rb +0 -6
  159. data/lib/rdoc/markdown/literals.kpeg +0 -21
  160. data/lib/rdoc/markdown/literals.rb +0 -454
  161. data/lib/rdoc/markup/attr_changer.rb +0 -22
  162. data/lib/rdoc/markup/attr_span.rb +0 -35
  163. data/lib/rdoc/markup/attribute_manager.rb +0 -405
  164. data/lib/rdoc/markup/attributes.rb +0 -70
  165. data/lib/rdoc/markup/regexp_handling.rb +0 -40
  166. data/lib/rdoc/parser/prism_ruby.rb +0 -1100
  167. data/lib/rdoc/parser/ripper_state_lex.rb +0 -302
  168. data/lib/rdoc/parser/ruby_tools.rb +0 -163
  169. data/lib/rdoc/servlet.rb +0 -452
@@ -1,84 +1,170 @@
1
1
  # frozen_string_literal: true
2
- ##
3
- # A heading with a level (1-6) and text
4
2
 
5
- RDoc::Markup::Heading =
6
- Struct.new :level, :text do
7
-
8
- @to_html = nil
9
- @to_label = nil
10
-
11
- ##
12
- # A singleton RDoc::Markup::ToLabel formatter for headings.
13
-
14
- def self.to_label
15
- @to_label ||= RDoc::Markup::ToLabel.new
16
- end
17
-
18
- ##
19
- # A singleton plain HTML formatter for headings. Used for creating labels
20
- # for the Table of Contents
21
-
22
- def self.to_html
23
- return @to_html if @to_html
24
-
25
- markup = RDoc::Markup.new
26
- markup.add_regexp_handling RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF
27
-
28
- @to_html = RDoc::Markup::ToHtml.new nil
29
-
30
- def @to_html.handle_regexp_CROSSREF(target)
31
- target.text.sub(/^\\/, '')
3
+ module RDoc
4
+ class Markup
5
+ # IMPORTANT! This weird workaround is required to ensure that RDoc can correctly deserializing Marshal data from
6
+ # older rubies. Older rubies have `Heading` as a struct, so if we change it to a class, deserialization fails
7
+ if RUBY_VERSION.start_with?("4.")
8
+ class Heading < Element
9
+ #: String
10
+ attr_reader :text
11
+
12
+ #: Integer
13
+ attr_accessor :level
14
+
15
+ #: (Integer, String) -> void
16
+ def initialize(level, text)
17
+ super()
18
+
19
+ @level = level
20
+ @text = text
21
+ end
22
+
23
+ #: (Object) -> bool
24
+ def ==(other)
25
+ other.is_a?(Heading) && other.level == @level && other.text == @text
26
+ end
27
+ end
28
+ else
29
+ Heading = Struct.new(:level, :text)
32
30
  end
33
31
 
34
- @to_html
35
- end
36
-
37
- ##
38
- # Calls #accept_heading on +visitor+
39
-
40
- def accept(visitor)
41
- visitor.accept_heading self
42
- end
43
-
44
- ##
45
- # An HTML-safe anchor reference for this header.
46
-
47
- def aref
48
- "label-#{self.class.to_label.convert text.dup}"
49
- end
50
-
51
- ##
52
- # Creates a fully-qualified label which will include the label from
53
- # +context+. This helps keep ids unique in HTML.
54
-
55
- def label(context = nil)
56
- label = aref
57
-
58
- label = [context.aref, label].compact.join '-' if
59
- context and context.respond_to? :aref
60
-
61
- label
62
- end
63
-
64
- ##
65
- # HTML markup of the text of this label without the surrounding header
66
- # element.
67
-
68
- def plain_html
69
- text = self.text.dup
70
-
71
- if matched = text.match(/rdoc-image:[^:]+:(.*)/)
72
- text = matched[1]
32
+ # A heading with a level (1-6) and text
33
+ #
34
+ # RDoc syntax:
35
+ # = Heading 1
36
+ # == Heading 2
37
+ # === Heading 3
38
+ #
39
+ # Markdown syntax:
40
+ # # Heading 1
41
+ # ## Heading 2
42
+ # ### Heading 3
43
+ #
44
+ class Heading
45
+ # A singleton RDoc::Markup::ToLabel formatter for headings.
46
+ #: () -> RDoc::Markup::ToLabel
47
+ def self.to_label
48
+ @to_label ||= Markup::ToLabel.new
49
+ end
50
+
51
+ # A singleton plain HTML formatter for headings. Used for creating labels for the Table of Contents
52
+ #: () -> RDoc::Markup::ToHtml
53
+ def self.to_html
54
+ @to_html ||= begin
55
+ to_html = Markup::ToHtml.new
56
+
57
+ def to_html.handle_regexp_CROSSREF(text)
58
+ text.sub(/^\\/, '')
59
+ end
60
+
61
+ to_html
62
+ end
63
+ end
64
+
65
+ # @override
66
+ #: (untyped) -> void
67
+ def accept(visitor)
68
+ visitor.accept_heading(self)
69
+ end
70
+
71
+ # An HTML-safe anchor reference for this header using GitHub-style formatting:
72
+ # - Lowercase
73
+ # - Spaces converted to hyphens
74
+ # - Special characters removed (except hyphens)
75
+ #
76
+ # Examples:
77
+ # "Hello" -> "hello"
78
+ # "Hello World" -> "hello-world"
79
+ # "Foo Bar Baz" -> "foo-bar-baz"
80
+ #
81
+ #: () -> String
82
+ def aref
83
+ self.class.to_label.convert text.dup
84
+ end
85
+
86
+ # An HTML-safe anchor reference using legacy RDoc formatting:
87
+ # - Prefixed with "label-"
88
+ # - Original case preserved
89
+ # - Spaces converted to + (URL encoding style)
90
+ # - Special characters percent-encoded
91
+ #
92
+ # Returns nil if it would be the same as the GitHub-style aref (no alias needed).
93
+ #
94
+ # Examples:
95
+ # "hello" -> "label-hello" (different due to label- prefix)
96
+ # "Hello" -> "label-Hello"
97
+ # "Hello World" -> "label-Hello+World"
98
+ # "Foo Bar Baz" -> "label-Foo+Bar+Baz"
99
+ #
100
+ #: () -> String?
101
+ def legacy_aref
102
+ "label-#{self.class.to_label.convert_legacy text.dup}"
103
+ end
104
+
105
+ # Creates a fully-qualified label (GitHub-style) which includes the context's aref prefix.
106
+ # This helps keep IDs unique in HTML when headings appear within class/method documentation.
107
+ #
108
+ # Examples (without context):
109
+ # "Hello World" -> "hello-world"
110
+ #
111
+ # Examples (with context being class Foo):
112
+ # "Hello World" -> "class-foo-hello-world"
113
+ #
114
+ # Examples (with context being method #bar):
115
+ # "Hello World" -> "method-i-bar-hello-world"
116
+ #
117
+ #: (RDoc::Context?) -> String
118
+ def label(context = nil)
119
+ result = +""
120
+ result << "#{context.aref}-" if context&.respond_to?(:aref)
121
+ result << aref
122
+ result
123
+ end
124
+
125
+ # Creates a fully-qualified legacy label for backward compatibility.
126
+ # This is used to generate a secondary ID attribute on the heading's inner anchor,
127
+ # allowing old-style links (e.g., #label-Hello+World) to continue working.
128
+ #
129
+ # Examples (without context):
130
+ # "hello" -> "label-hello"
131
+ # "Hello World" -> "label-Hello+World"
132
+ #
133
+ # Examples (with context being class Foo):
134
+ # "hello" -> "class-Foo-label-hello"
135
+ # "Hello World" -> "class-Foo-label-Hello+World"
136
+ #
137
+ #: (RDoc::Context?) -> String
138
+ def legacy_label(context = nil)
139
+ result = +""
140
+ if context&.respond_to?(:legacy_aref)
141
+ result << "#{context.legacy_aref}-"
142
+ elsif context&.respond_to?(:aref)
143
+ result << "#{context.aref}-"
144
+ end
145
+ result << legacy_aref
146
+ result
147
+ end
148
+
149
+ # HTML markup of the text of this label without the surrounding header element.
150
+ #: () -> String
151
+ def plain_html
152
+ no_image_text = text
153
+
154
+ if matched = no_image_text.match(/rdoc-image:[^:]+:(.*)/)
155
+ no_image_text = matched[1]
156
+ end
157
+
158
+ self.class.to_html.to_html(no_image_text)
159
+ end
160
+
161
+ # @override
162
+ #: (PP) -> void
163
+ def pretty_print(q)
164
+ q.group 2, "[head: #{level} ", ']' do
165
+ q.pp text
166
+ end
167
+ end
73
168
  end
74
-
75
- self.class.to_html.to_html(text)
76
169
  end
77
-
78
- def pretty_print(q) # :nodoc:
79
- q.group 2, "[head: #{level} ", ']' do
80
- q.pp text
81
- end
82
- end
83
-
84
170
  end
@@ -1,42 +1,46 @@
1
1
  # frozen_string_literal: true
2
- ##
3
- # A file included at generation time. Objects of this class are created by
4
- # RDoc::RD for an extension-less include.
5
- #
6
- # This implementation in incomplete.
2
+ module RDoc
3
+ class Markup
4
+ ##
5
+ # A file included at generation time. Objects of this class are created by
6
+ # RDoc::RD for an extension-less include.
7
+ #
8
+ # This implementation in incomplete.
7
9
 
8
- class RDoc::Markup::Include
10
+ class Include
9
11
 
10
- ##
11
- # The filename to be included, without extension
12
+ ##
13
+ # The filename to be included, without extension
12
14
 
13
- attr_reader :file
15
+ attr_reader :file
14
16
 
15
- ##
16
- # Directories to search for #file
17
+ ##
18
+ # Directories to search for #file
17
19
 
18
- attr_reader :include_path
20
+ attr_reader :include_path
19
21
 
20
- ##
21
- # Creates a new include that will import +file+ from +include_path+
22
+ ##
23
+ # Creates a new include that will import +file+ from +include_path+
22
24
 
23
- def initialize(file, include_path)
24
- @file = file
25
- @include_path = include_path
26
- end
25
+ def initialize(file, include_path)
26
+ @file = file
27
+ @include_path = include_path
28
+ end
27
29
 
28
- def ==(other) # :nodoc:
29
- self.class === other and
30
- @file == other.file and @include_path == other.include_path
31
- end
30
+ def ==(other) # :nodoc:
31
+ self.class === other and
32
+ @file == other.file and @include_path == other.include_path
33
+ end
34
+
35
+ def pretty_print(q) # :nodoc:
36
+ q.group 2, '[incl ', ']' do
37
+ q.text file
38
+ q.breakable
39
+ q.text 'from '
40
+ q.pp include_path
41
+ end
42
+ end
32
43
 
33
- def pretty_print(q) # :nodoc:
34
- q.group 2, '[incl ', ']' do
35
- q.text file
36
- q.breakable
37
- q.text 'from '
38
- q.pp include_path
39
44
  end
40
45
  end
41
-
42
46
  end
@@ -1,47 +1,51 @@
1
1
  # frozen_string_literal: true
2
- ##
3
- # An Indented Paragraph of text
2
+ module RDoc
3
+ class Markup
4
+ ##
5
+ # An Indented Paragraph of text
4
6
 
5
- class RDoc::Markup::IndentedParagraph < RDoc::Markup::Raw
7
+ class IndentedParagraph < Markup::Raw
6
8
 
7
- ##
8
- # The indent in number of spaces
9
+ ##
10
+ # The indent in number of spaces
9
11
 
10
- attr_reader :indent
12
+ attr_reader :indent
11
13
 
12
- ##
13
- # Creates a new IndentedParagraph containing +parts+ indented with +indent+
14
- # spaces
14
+ ##
15
+ # Creates a new IndentedParagraph containing +parts+ indented with +indent+
16
+ # spaces
15
17
 
16
- def initialize indent, *parts
17
- @indent = indent
18
+ def initialize(indent, *parts)
19
+ @indent = indent
18
20
 
19
- super(*parts)
20
- end
21
+ super(*parts)
22
+ end
21
23
 
22
- def ==(other) # :nodoc:
23
- super and indent == other.indent
24
- end
24
+ def ==(other) # :nodoc:
25
+ super and indent == other.indent
26
+ end
25
27
 
26
- ##
27
- # Calls #accept_indented_paragraph on +visitor+
28
+ ##
29
+ # Calls #accept_indented_paragraph on +visitor+
28
30
 
29
- def accept(visitor)
30
- visitor.accept_indented_paragraph self
31
- end
32
-
33
- ##
34
- # Joins the raw paragraph text and converts inline HardBreaks to the
35
- # +hard_break+ text followed by the indent.
31
+ def accept(visitor)
32
+ visitor.accept_indented_paragraph self
33
+ end
36
34
 
37
- def text(hard_break = nil)
38
- @parts.map do |part|
39
- if RDoc::Markup::HardBreak === part then
40
- '%1$s%3$*2$s' % [hard_break, @indent, ' '] if hard_break
41
- else
42
- part
35
+ ##
36
+ # Joins the raw paragraph text and converts inline HardBreaks to the
37
+ # +hard_break+ text followed by the indent.
38
+
39
+ def text(hard_break = nil)
40
+ @parts.map do |part|
41
+ if Markup::HardBreak === part
42
+ '%1$s%3$*2$s' % [hard_break, @indent, ' '] if hard_break
43
+ else
44
+ part
45
+ end
46
+ end.join
43
47
  end
44
- end.join
45
- end
46
48
 
49
+ end
50
+ end
47
51
  end