asciidoctor-bibtex 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # StringUtils.rb
5
+ #
6
+ # Some utilities for strings.
7
+ #
8
+ # Copyright (c) Peter Lane, 2012-13.
9
+ # Copyright (c) Zhang Yang, 2019.
10
+ #
11
+ # Released under Open Works License, 0.9.2
12
+ #
13
+
14
+ module AsciidoctorBibtex
15
+ module StringUtils
16
+ # Converts html output produced by citeproc to asciidoc markup
17
+ def self.html_to_asciidoc(s)
18
+ s = s.gsub(%r{</?i>}, '_')
19
+ s = s.gsub(%r{</?b>}, '*')
20
+ s = s.gsub(%r{</?span.*?>}, '')
21
+ s = s.gsub(/\{|\}/, '')
22
+ s
23
+ end
24
+
25
+ # Provides a check that a string is in integer
26
+ def self.is_i?(s)
27
+ !!(s =~ /^[-+]?[0-9]+$/)
28
+ end
29
+
30
+ # Merge consecutive number so that "1,2,3,5" becomes "1-3,5"
31
+ def self.combine_consecutive_numbers(str)
32
+ nums = str.split(',').collect(&:strip)
33
+ res = ''
34
+ # Loop through ranges
35
+ start_range = 0
36
+ while start_range < nums.length
37
+ end_range = start_range
38
+ while (end_range < nums.length - 1) &&
39
+ is_i?(nums[end_range]) &&
40
+ is_i?(nums[end_range + 1]) &&
41
+ (nums[end_range + 1].to_i == nums[end_range].to_i + 1)
42
+ end_range += 1
43
+ end
44
+ if end_range - start_range >= 2
45
+ res += "#{nums[start_range]}-#{nums[end_range]}, "
46
+ else
47
+ start_range.upto(end_range) do |i|
48
+ res += "#{nums[i]}, "
49
+ end
50
+ end
51
+ start_range = end_range + 1
52
+ end
53
+ # finish by removing last comma
54
+ res.gsub(/, $/, '')
55
+ end
56
+ end
57
+ end
@@ -3,25 +3,25 @@
3
3
  # Simple checks on available styles through CSL
4
4
  #
5
5
 
6
- module AsciidoctorBibtex
7
-
8
- module Styles
6
+ require 'citeproc'
7
+ require 'csl/styles'
9
8
 
10
- def Styles.available
9
+ module AsciidoctorBibtex
10
+ module StyleUtils
11
+ def self.available
11
12
  CSL::Style.ls
12
13
  end
13
14
 
14
- def Styles.default_style
15
+ def self.default_style
15
16
  'apa'
16
17
  end
17
18
 
18
- def Styles.valid? style
19
+ def self.valid?(style)
19
20
  Styles.available.include? style
20
21
  end
21
22
 
22
- def Styles.is_numeric? style
23
+ def self.is_numeric?(style)
23
24
  CSL::Style.load(style).citation_format == :numeric
24
25
  end
25
26
  end
26
27
  end
27
-
@@ -1,64 +1,174 @@
1
- # Some extension and helper methods.
2
1
  #
3
- # Copyright (c) Peter Lane, 2012-13.
4
- # Released under Open Works License, 0.9.2
2
+ # Treeprocessor extension for asciidoctor
3
+ #
5
4
 
6
- module AsciidoctorBibtexArrayExtensions
5
+ require 'asciidoctor'
6
+ require 'asciidoctor/extensions'
7
+ require 'asciidoctor/reader'
8
+ require 'asciidoctor/parser'
7
9
 
8
- # Retrieve the third item of an array
9
- # Note: no checks for validity
10
- def third
11
- self[2]
12
- end
10
+ require_relative 'PathUtils'
11
+ require_relative 'Processor'
13
12
 
14
- # Join items in array using commas and 'and' on last item
15
- def comma_and_join
16
- if size < 2
17
- return self.join("")
18
- end
19
- result = ""
20
- self.each_with_index do |item, index|
21
- if index.zero?
22
- result << item
23
- elsif index == size-1
24
- result << " and #{item}"
25
- else
26
- result << ", #{item}"
13
+ module AsciidoctorBibtex
14
+ module Asciidoctor
15
+ # Placeholder paragraph for the bibliography paragraph. Choose a uuid so
16
+ # that it is a special word unlikeky to conflict with normal texts.
17
+ BibliographyBlockMacroPlaceholder = %(a5d42deb-3cfc-4293-b96a-fcb47316ce56)
18
+
19
+ # BibliographyBlockMacro
20
+ #
21
+ # The `bibliography::[] block macro` processor.
22
+ #
23
+ # This macro processor does only half the work. It just parse the macro
24
+ # and set bibtex file and style if found in the macro. The macro is then
25
+ # expanded to a special paragraph, which is then replaced with generated
26
+ # references by the treeprocessor.
27
+ #
28
+ class BibliographyBlockMacro < ::Asciidoctor::Extensions::BlockMacroProcessor
29
+ use_dsl
30
+ named :bibliography
31
+ name_positional_attributes :style, :locale
32
+
33
+ def process(parent, target, attrs)
34
+ # NOTE: bibtex-file and bibtex-style set by this macro shall be
35
+ # overridable by document attributes and commandline arguments. So we
36
+ # respect the convention here.
37
+ if target && (!parent.document.attr? 'bibtex-file')
38
+ parent.document.set_attribute 'bibtex-file', target
39
+ end
40
+ if attrs.key?(:style) && (!parent.document.attr? 'bibtex-style')
41
+ parent.document.set_attribute 'bibtex-style', attrs[:style]
42
+ end
43
+ if attrs.key?(:locale) && (!parent.document.attr? 'bibtex-locale')
44
+ parent.document.set_attribute 'bibtex-locale', attrs[:locale]
45
+ end
46
+ create_paragraph parent, BibliographyBlockMacroPlaceholder, {}
27
47
  end
28
48
  end
29
49
 
30
- return result
31
- end
32
- end
50
+ # CitationProcessor
51
+ #
52
+ # A tree processor to replace all citations and bibliography.
53
+ #
54
+ # This processor scans the document, generates a list of citations, replaces
55
+ # each citation with citation text and the reference block macro placeholder
56
+ # with the final bibliography list. It relys on the block macro processor to
57
+ # generate the place holder.
58
+ #
59
+ # NOTE: According to the asiidoctor extension policy, the tree processor can
60
+ # only produce texts with inline macros.
61
+ #
62
+ class CitationProcessor < ::Asciidoctor::Extensions::Treeprocessor
63
+ def process(document)
64
+ bibtex_file = (document.attr 'bibtex-file').to_s
65
+ bibtex_style = ((document.attr 'bibtex-style') || 'ieee').to_s
66
+ bibtex_locale = ((document.attr 'bibtex-locale') || 'en-US').to_s
67
+ bibtex_order = ((document.attr 'bibtex-order') || 'appearance').to_sym
68
+ bibtex_format = ((document.attr 'bibtex-format') || 'asciidoc').to_sym
69
+ bibtex_throw = ((document.attr 'bibtex-throw') || 'false').to_s.downcase
33
70
 
34
- # monkey patch the extension methods to Array
35
- class Array
36
- include AsciidoctorBibtexArrayExtensions
37
- end
71
+ # Fild bibtex file automatically if not supplied.
72
+ if bibtex_file.empty?
73
+ bibtex_file = AsciidoctorBibtex::PathUtils.fild_bibfile '.'
74
+ end
75
+ if bibtex_file.empty?
76
+ bibtex_file = AsciidoctorBibtex::PathUtils.find_bibfile "#{ENV['HOME']}/Documents"
77
+ end
78
+ if bibtex_file.empty?
79
+ puts 'Error: bibtex-file is not set and automatic search failed'
80
+ exit
81
+ end
38
82
 
39
- # Converts html output produced by citeproc to asciidoc markup
40
- module StringHtmlToAsciiDoc
41
- def html_to_asciidoc
42
- r = self.gsub(/<\/?i>/, '_')
43
- r = r.gsub(/<\/?b>/, '*')
44
- r = r.gsub(/<\/?span.*?>/, '')
45
- r = r.gsub(/\{|\}/, '')
46
- r
47
- end
48
- end
83
+ # Extract all AST nodes that can contain citations.
84
+ prose_blocks = document.find_by do |b|
85
+ (b.content_model == :simple) ||
86
+ (b.context == :list_item) ||
87
+ (b.context == :table_cell)
88
+ end
89
+ return nil if prose_blocks.nil?
49
90
 
50
- # Provides a check that a string is in integer
51
- # Taken from:
52
- # http://stackoverflow.com/questions/1235863/test-if-a-string-is-basically-an-integer-in-quotes-using-ruby
53
- module IntegerCheck
54
- def is_i?
55
- !!(self =~ /^[-+]?[0-9]+$/)
91
+ processor = Processor.new bibtex_file, true, bibtex_style, bibtex_locale,
92
+ bibtex_order == :appearance, bibtex_format,
93
+ bibtex_throw == 'true'
94
+
95
+ # First pass: extract all citations.
96
+ prose_blocks.each do |block|
97
+ if block.context == :list_item || block.context == :table_cell
98
+ line = block.text
99
+ processor.process_citation_macros line
100
+ else
101
+ block.lines.each do |line|
102
+ processor.process_citation_macros line
103
+ end
104
+ end
105
+ end
106
+ # Make processor finalize macro processing as required.
107
+ processor.finalize_macro_processing
108
+
109
+ # Second pass: replace citations with citation texts.
110
+ prose_blocks.each do |block|
111
+ if block.context == :list_item || block.context == :table_cell
112
+ line = block.text
113
+ line = processor.replace_citation_macros(line)
114
+ block.text = line
115
+ else
116
+ block.lines.each_with_index do |line, index|
117
+ line = processor.replace_citation_macros(line)
118
+ block.lines[index] = line
119
+ end
120
+ end
121
+ end
122
+
123
+ references_asciidoc = []
124
+ if (bibtex_format == :latex) || (bibtex_format == :bibtex)
125
+ references_asciidoc << %(+++\\bibliography{#{bibtex_file}}{}+++)
126
+ references_asciidoc << %(+++\\bibliographystyle{#{bibtex_style}}+++)
127
+ elsif bibtex_format == :biblatex
128
+ references_asciidoc << %(+++\\printbibliography+++)
129
+ else
130
+ references_asciidoc = processor.build_bibliography_list
131
+ end
132
+
133
+ # Third pass: replace the bibliography paragraph with the bibliography
134
+ # list.
135
+ biblio_blocks = document.find_by do |b|
136
+ # for fast search (since most searches shall fail)
137
+ (b.content_model == :simple) && (b.lines.size == 1) \
138
+ && (b.lines[0] == BibliographyBlockMacroPlaceholder)
139
+ end
140
+ biblio_blocks.each do |block|
141
+ block_index = block.parent.blocks.index do |b|
142
+ b == block
143
+ end
144
+ reference_blocks = parse_asciidoc block.parent, references_asciidoc
145
+ reference_blocks.reverse.each do |b|
146
+ block.parent.blocks.insert block_index, b
147
+ end
148
+ block.parent.blocks.delete_at block_index + reference_blocks.size
149
+ end
150
+
151
+ nil
152
+ end
153
+
154
+ # This is an adapted version of Asciidoctor::Extension::parse_content,
155
+ # where resultant blocks are returned as a list instead of attached to
156
+ # the parent.
157
+ def parse_asciidoc(parent, content, attributes = {})
158
+ result = []
159
+ reader = ::Asciidoctor::Reader.new content
160
+ while reader.has_more_lines?
161
+ block = ::Asciidoctor::Parser.next_block reader, parent, attributes
162
+ result << block if block
163
+ end
164
+ result
165
+ end
166
+ end
56
167
  end
57
168
  end
58
169
 
59
- # monkey patch the extension methods into String
60
- class String
61
- include StringHtmlToAsciiDoc
62
- include IntegerCheck
170
+ # Register the extensions to asciidoctor
171
+ Asciidoctor::Extensions.register do
172
+ block_macro AsciidoctorBibtex::Asciidoctor::BibliographyBlockMacro
173
+ treeprocessor AsciidoctorBibtex::Asciidoctor::CitationProcessor
63
174
  end
64
-
@@ -1,3 +1,3 @@
1
1
  module AsciidoctorBibtex
2
- VERSION = '0.4.1'
2
+ VERSION = '0.5.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asciidoctor-bibtex
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zhang YANG
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-20 00:00:00.000000000 Z
11
+ date: 2019-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: asciidoctor
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 1.5.0
19
+ version: 2.0.0
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: 3.0.0
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 1.5.0
29
+ version: 2.0.0
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: 3.0.0
@@ -86,6 +86,48 @@ dependencies:
86
86
  - - "~>"
87
87
  - !ruby/object:Gem::Version
88
88
  version: '0.2'
89
+ - !ruby/object:Gem::Dependency
90
+ name: minitest
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '5'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '5'
103
+ - !ruby/object:Gem::Dependency
104
+ name: bundler
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '2'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '2'
117
+ - !ruby/object:Gem::Dependency
118
+ name: rake
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '12'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '12'
89
131
  description: |
90
132
  asciidoctor-bibtex adds bibtex support for asciidoc documents by introducing
91
133
  two new macros: `cite:[KEY]` and `bibliography::[]`. Citations are parsed and
@@ -100,19 +142,14 @@ files:
100
142
  - LICENSE.txt
101
143
  - README.md
102
144
  - lib/asciidoctor-bibtex.rb
103
- - lib/asciidoctor-bibtex/asciidoctor.rb
104
- - lib/asciidoctor-bibtex/bibextension.rb
105
- - lib/asciidoctor-bibtex/citation.rb
106
- - lib/asciidoctor-bibtex/citationdata.rb
107
- - lib/asciidoctor-bibtex/citations.rb
108
- - lib/asciidoctor-bibtex/citationutils.rb
145
+ - lib/asciidoctor-bibtex/CitationMacro.rb
146
+ - lib/asciidoctor-bibtex/CitationUtils.rb
147
+ - lib/asciidoctor-bibtex/PathUtils.rb
148
+ - lib/asciidoctor-bibtex/Processor.rb
149
+ - lib/asciidoctor-bibtex/StringUtils.rb
150
+ - lib/asciidoctor-bibtex/StyleUtils.rb
109
151
  - lib/asciidoctor-bibtex/extensions.rb
110
- - lib/asciidoctor-bibtex/filehandlers.rb
111
- - lib/asciidoctor-bibtex/processor.rb
112
- - lib/asciidoctor-bibtex/processorutils.rb
113
- - lib/asciidoctor-bibtex/styles.rb
114
152
  - lib/asciidoctor-bibtex/version.rb
115
- - lib/utils.rb
116
153
  homepage: https://github.com/asciidoctor/asciidoctor-bibtex
117
154
  licenses:
118
155
  - OWL
@@ -1,7 +0,0 @@
1
- require 'asciidoctor/extensions'
2
- require_relative 'bibextension'
3
-
4
- Asciidoctor::Extensions.register do
5
- block_macro AsciidoctorBibtex::Asciidoctor::BibliographyBlockMacro
6
- treeprocessor AsciidoctorBibtex::Asciidoctor::CitationProcessor
7
- end
@@ -1,177 +0,0 @@
1
- #
2
- # Treeprocessor extension for asciidoctor
3
- #
4
-
5
- require 'asciidoctor'
6
- require 'asciidoctor/extensions'
7
- require 'asciidoctor/reader'
8
- require 'asciidoctor/parser'
9
- require 'bibtex/filters'
10
- require 'latex/decode/base'
11
- require 'latex/decode/maths'
12
- require 'latex/decode/accents'
13
- require 'latex/decode/diacritics'
14
- require 'latex/decode/punctuation'
15
- require 'latex/decode/symbols'
16
- require 'latex/decode/greek'
17
- require_relative 'styles'
18
- require_relative 'filehandlers'
19
-
20
- module AsciidoctorBibtex
21
- module Asciidoctor
22
-
23
- # This filter extends the original latex filter in bibtex-ruby to handle
24
- # unknown latex macros more gracefully. We could have used latex-decode
25
- # gem together with our custom replacement rules, but latex-decode eats up
26
- # all braces after it finishes all decoding. So we hack over the
27
- # LaTeX.decode function and insert our rules before `strip_braces`.
28
- class LatexFilter < ::BibTeX::Filter
29
- def apply(value)
30
- text = value.to_s
31
- LaTeX::Decode::Base.normalize(text)
32
- LaTeX::Decode::Maths.decode!(text)
33
- LaTeX::Decode::Accents.decode!(text)
34
- LaTeX::Decode::Diacritics.decode!(text)
35
- LaTeX::Decode::Punctuation.decode!(text)
36
- LaTeX::Decode::Symbols.decode!(text)
37
- LaTeX::Decode::Greek.decode!(text)
38
- text = text.gsub(/\\url\{(.+?)\}/, " \\1 ").gsub(/\\\w+(?=\s+\w)/, "").gsub(/\\\w+(?:\[.+?\])?\s*\{(.+?)\}/, "\\1")
39
- LaTeX::Decode::Base.strip_braces(text)
40
- LaTeX.normalize_C(text)
41
- end
42
- end
43
-
44
- BibliographyBlockMacroPlaceholder = %(BIBLIOGRAPHY BLOCK MACRO PLACEHOLDER)
45
-
46
- # This macro processor does only half the work. It just parse the macro
47
- # and set bibtex file and style if found in the macro. The macro is then
48
- # expanded to a special paragraph, which is then replaced with generated
49
- # references by the treeprocessor.
50
- class BibliographyBlockMacro < ::Asciidoctor::Extensions::BlockMacroProcessor
51
- use_dsl
52
- named :bibliography
53
- name_positional_attributes :style
54
-
55
- def process parent, target, attrs
56
- # NOTE: bibtex-file and bibtex-style set by this macro shall be
57
- # overridable by document attributes and commandline arguments. So we
58
- # respect the convention here.
59
- if target and not parent.document.attr? 'bibtex-file'
60
- parent.document.set_attribute 'bibtex-file', target
61
- end
62
- if attrs.key? :style and not parent.document.attr? 'bibtex-style'
63
- parent.document.set_attribute 'bibtex-style', attrs[:style]
64
- end
65
- if attrs.key? :locale and not parent.document.attr? 'bibtex-locale'
66
- parent.document.set_attribute 'bibtex-locale', attrs[:locale]
67
- end
68
- create_paragraph parent, BibliographyBlockMacroPlaceholder, {}
69
- end
70
- end
71
-
72
- # This processor scans the document, generates a list of citations,
73
- # replace each citation with correct text and the reference block macro
74
- # placeholder with the final reference list. It relys on the block macro
75
- # processor to generate the place holder.
76
- class CitationProcessor < ::Asciidoctor::Extensions::Treeprocessor
77
-
78
- def process document
79
- bibtex_file = (document.attr 'bibtex-file').to_s
80
- bibtex_style = ((document.attr 'bibtex-style') || 'ieee').to_s
81
- bibtex_locale = ((document.attr 'bibtex-locale') || 'en-US').to_s
82
- bibtex_order = ((document.attr 'bibtex-order') || 'appearance').to_sym
83
- bibtex_format = ((document.attr 'bibtex-format') || 'asciidoc').to_sym
84
-
85
- if bibtex_file.empty?
86
- bibtex_file = AsciidoctorBibtex::FileHandlers.find_bibliography "."
87
- end
88
- if bibtex_file.empty?
89
- bibtex_file = AsciidoctorBibtex::FileHandlers.find_bibliography "#{ENV['HOME']}/Documents"
90
- end
91
- if bibtex_file.empty?
92
- puts "Error: bibtex-file is not set and automatic search failed"
93
- exit
94
- end
95
-
96
- bibtex = BibTeX.open bibtex_file, :filter => [LatexFilter]
97
- processor = Processor.new bibtex, true, bibtex_style, bibtex_locale, bibtex_order == :appearance, bibtex_format
98
-
99
- prose_blocks = document.find_by {|b| b.content_model == :simple or b.context == :list_item}
100
- prose_blocks.each do |block|
101
- if block.context == :list_item
102
- line = block.instance_variable_get :@text
103
- processor.citations.add_from_line line
104
- else
105
- block.lines.each do |line|
106
- processor.citations.add_from_line line
107
- end
108
- end
109
- end
110
-
111
- prose_blocks.each do |block|
112
- if block.context == :list_item
113
- line = block.instance_variable_get :@text
114
- processor.citations.retrieve_citations(line).each do |citation|
115
- line = line.gsub(citation.original, processor.complete_citation(citation))
116
- end
117
- block.instance_variable_set :@text, line
118
- else
119
- block.lines.each do |line|
120
- tmp = line.clone
121
- processor.citations.retrieve_citations(line).each do |citation|
122
- tmp = tmp.gsub(citation.original, processor.complete_citation(citation))
123
- end
124
- line.replace tmp
125
- end
126
- end
127
- end
128
-
129
- references_asciidoc = []
130
- if bibtex_format == :latex or bibtex_format == :bibtex
131
- references_asciidoc << %(+++\\bibliography{#{bibtex_file}}{}+++)
132
- references_asciidoc << %(+++\\bibliographystyle{#{bibtex_style}}+++)
133
- elsif bibtex_format == :biblatex
134
- references_asciidoc << %(+++\\printbibliography+++)
135
- else
136
- processor.cites.each do |ref|
137
- references_asciidoc << processor.get_reference(ref)
138
- references_asciidoc << ''
139
- end
140
- end
141
-
142
- biblio_blocks = document.find_by do |b|
143
- # for fast search (since most searches shall fail)
144
- b.content_model == :simple and b.lines.size == 1 \
145
- and b.lines[0] == BibliographyBlockMacroPlaceholder
146
- end
147
- biblio_blocks.each do |block|
148
- block_index = block.parent.blocks.index do |b|
149
- b == block
150
- end
151
- reference_blocks = parse_asciidoc block.parent, references_asciidoc
152
- reference_blocks.reverse.each do |b|
153
- block.parent.blocks.insert block_index, b
154
- end
155
- block.parent.blocks.delete_at block_index + reference_blocks.size
156
- end
157
-
158
- nil
159
- end
160
-
161
- # This is an adapted version of Asciidoctor::Extension::parse_content,
162
- # where resultant blocks are returned as a list instead of attached to
163
- # the parent.
164
- def parse_asciidoc parent, content, attributes = {}
165
- result = []
166
- reader = ::Asciidoctor::Reader.new content
167
- while reader.has_more_lines?
168
- block = ::Asciidoctor::Parser.next_block reader, parent, attributes
169
- result << block if block
170
- end
171
- result
172
- end
173
-
174
- end
175
-
176
- end
177
- end