review 1.0.0 → 1.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.
- data/.travis.yml +1 -2
- data/ChangeLog +65 -0
- data/Gemfile +5 -0
- data/README.rdoc +6 -1
- data/Rakefile +14 -28
- data/bin/review-check +9 -21
- data/bin/review-compile +45 -12
- data/bin/review-epubmaker +477 -220
- data/bin/review-epubmaker-ng +5 -14
- data/bin/review-index +4 -2
- data/bin/review-init +94 -0
- data/bin/review-pdfmaker +70 -287
- data/bin/review-preproc +5 -2
- data/bin/review-vol +4 -11
- data/debian/changelog +3 -3
- data/debian/copyright +5 -3
- data/debian/docs +1 -0
- data/doc/format.rdoc +57 -11
- data/doc/quickstart.rdoc +4 -4
- data/doc/sample.yaml +11 -7
- data/lib/epubmaker.rb +1 -1
- data/lib/epubmaker/epubv2.rb +26 -27
- data/lib/epubmaker/epubv3.rb +13 -13
- data/lib/epubmaker/producer.rb +1 -1
- data/lib/review.rb +3 -4
- data/lib/review/book.rb +17 -581
- data/lib/review/book/base.rb +247 -0
- data/lib/review/book/chapter.rb +81 -0
- data/lib/review/book/compilable.rb +163 -0
- data/lib/review/book/index.rb +339 -0
- data/lib/review/book/page_metric.rb +38 -0
- data/lib/review/book/parameters.rb +97 -0
- data/lib/review/book/part.rb +44 -0
- data/lib/review/book/volume.rb +65 -0
- data/lib/review/builder.rb +34 -40
- data/lib/review/compiler.rb +32 -13
- data/lib/review/configure.rb +38 -0
- data/lib/review/ewbbuilder.rb +23 -21
- data/lib/review/extentions.rb +3 -0
- data/lib/review/extentions/object.rb +9 -0
- data/lib/review/{compat.rb → extentions/string.rb} +11 -0
- data/lib/review/htmlbuilder.rb +162 -74
- data/lib/review/htmlutils.rb +28 -1
- data/lib/review/i18n.yaml +11 -0
- data/lib/review/idgxmlbuilder.rb +119 -35
- data/lib/review/inaobuilder.rb +355 -0
- data/lib/review/latexbuilder.rb +133 -59
- data/lib/review/latexutils.rb +15 -6
- data/lib/review/makerhelper.rb +59 -0
- data/lib/review/markdownbuilder.rb +129 -0
- data/lib/review/preprocessor.rb +20 -7
- data/lib/review/review.tex.erb +250 -0
- data/lib/review/textutils.rb +28 -3
- data/lib/review/tocparser.rb +20 -12
- data/lib/review/tocprinter.rb +7 -3
- data/lib/review/topbuilder.rb +44 -27
- data/lib/review/version.rb +3 -0
- data/review.gemspec +20 -117
- data/test/book_test_helper.rb +35 -0
- data/test/sample-book/README.md +5 -0
- data/test/sample-book/src/CHAPS +2 -0
- data/test/sample-book/src/PREDEF +1 -0
- data/test/sample-book/src/Rakefile +26 -0
- data/test/sample-book/src/_cover.html +3 -0
- data/test/sample-book/src/ch01.re +71 -0
- data/test/sample-book/src/ch02.re +3 -0
- data/test/sample-book/src/config.yml +55 -0
- data/test/sample-book/src/images/ch01-imgsample.jpg +0 -0
- data/test/sample-book/src/images/cover.jpg +0 -0
- data/test/sample-book/src/main.css +251 -0
- data/test/sample-book/src/preface.re +15 -0
- data/test/sample-book/src/sty/jumoline.sty +310 -0
- data/test/sample-book/src/sty/samplemacro.sty +21 -0
- data/test/sample-book/src/vendor/jumoline/README +29 -0
- data/test/sample-book/src/vendor/jumoline/jumoline.dtx +2988 -0
- data/test/sample-book/src/vendor/jumoline/jumoline.ins +6 -0
- data/test/test_book.rb +26 -646
- data/test/test_book_chapter.rb +418 -0
- data/test/test_book_parameter.rb +42 -0
- data/test/test_book_part.rb +53 -0
- data/test/test_builder.rb +17 -10
- data/test/test_compiler.rb +46 -0
- data/test/test_epubmaker.rb +1 -1
- data/test/test_epubmaker_cmd.rb +36 -0
- data/test/test_helper.rb +17 -0
- data/test/test_htmlbuilder.rb +156 -24
- data/test/test_htmlutils.rb +4 -0
- data/test/test_i18n.rb +6 -2
- data/test/test_idgxmlbuilder.rb +33 -11
- data/test/test_inaobuilder.rb +247 -0
- data/test/test_index.rb +31 -0
- data/test/test_latexbuilder.rb +115 -35
- data/test/test_makerhelper.rb +50 -0
- data/test/test_pdfmaker_cmd.rb +36 -0
- data/test/test_preprocessor.rb +7 -7
- data/test/test_topbuilder.rb +6 -7
- data/test/test_uuid.rb +1 -0
- metadata +117 -12
- data/lib/review/index.rb +0 -323
- data/lib/review/volume.rb +0 -66
@@ -0,0 +1,247 @@
|
|
1
|
+
#
|
2
|
+
# $Id: book.rb 4315 2009-09-02 04:15:24Z kmuto $
|
3
|
+
#
|
4
|
+
# Copyright (c) 2002-2008 Minero Aoki
|
5
|
+
# 2009 Minero Aoki, Kenshi Muto
|
6
|
+
#
|
7
|
+
# This program is free software.
|
8
|
+
# You can distribute or modify this program under the terms of
|
9
|
+
# the GNU LGPL, Lesser General Public License version 2.1.
|
10
|
+
# For details of the GNU LGPL, see the file "COPYING".
|
11
|
+
#
|
12
|
+
module ReVIEW
|
13
|
+
module Book
|
14
|
+
class Base
|
15
|
+
|
16
|
+
attr_accessor :param
|
17
|
+
|
18
|
+
def self.load_default
|
19
|
+
%w( . .. ../.. ).each do |basedir|
|
20
|
+
if File.file?("#{basedir}/CHAPS")
|
21
|
+
book = load(basedir)
|
22
|
+
if File.file?("#{basedir}/config.rb")
|
23
|
+
book.instance_variable_set("@parameters", Parameters.load("#{basedir}/config.rb"))
|
24
|
+
end
|
25
|
+
return book
|
26
|
+
end
|
27
|
+
end
|
28
|
+
new('.')
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.load(dir)
|
32
|
+
update_rubyenv dir
|
33
|
+
new(dir)
|
34
|
+
end
|
35
|
+
|
36
|
+
@basedir_seen = {}
|
37
|
+
|
38
|
+
def self.update_rubyenv(dir)
|
39
|
+
return if @basedir_seen.key?(dir)
|
40
|
+
if File.directory?("#{dir}/lib/review")
|
41
|
+
$LOAD_PATH.unshift "#{dir}/lib"
|
42
|
+
end
|
43
|
+
if File.file?("#{dir}/review-ext.rb")
|
44
|
+
Kernel.load File.expand_path("#{dir}/review-ext.rb")
|
45
|
+
end
|
46
|
+
@basedir_seen[dir] = true
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize(basedir, parameters = Parameters.default)
|
50
|
+
@basedir = basedir
|
51
|
+
@parameters = parameters
|
52
|
+
@parts = nil
|
53
|
+
@chapter_index = nil
|
54
|
+
end
|
55
|
+
|
56
|
+
extend Forwardable
|
57
|
+
def_delegators '@parameters',
|
58
|
+
:chapter_file,
|
59
|
+
:part_file,
|
60
|
+
:bib_file,
|
61
|
+
:reject_file,
|
62
|
+
:predef_file,
|
63
|
+
:postdef_file,
|
64
|
+
:ext,
|
65
|
+
:image_dir,
|
66
|
+
:image_types,
|
67
|
+
:page_metric
|
68
|
+
|
69
|
+
def parts
|
70
|
+
@parts ||= read_parts()
|
71
|
+
end
|
72
|
+
|
73
|
+
def parts_in_file
|
74
|
+
parts.find_all{|part|
|
75
|
+
part if part.present? and part.file?
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
def part(n)
|
80
|
+
parts.detect {|part| part.number == n }
|
81
|
+
end
|
82
|
+
|
83
|
+
def each_part(&block)
|
84
|
+
parts.each(&block)
|
85
|
+
end
|
86
|
+
|
87
|
+
def chapters
|
88
|
+
parts().map {|p| p.chapters }.flatten
|
89
|
+
end
|
90
|
+
|
91
|
+
def each_chapter(&block)
|
92
|
+
chapters.each(&block)
|
93
|
+
end
|
94
|
+
|
95
|
+
def chapter_index
|
96
|
+
@chapter_index ||= ChapterIndex.new(chapters())
|
97
|
+
@chapter_index
|
98
|
+
end
|
99
|
+
|
100
|
+
def chapter(id)
|
101
|
+
chapter_index()[id]
|
102
|
+
end
|
103
|
+
|
104
|
+
def volume
|
105
|
+
Volume.sum(chapters.map {|chap| chap.volume })
|
106
|
+
end
|
107
|
+
|
108
|
+
def read_CHAPS
|
109
|
+
read_FILE("chapter_file")
|
110
|
+
end
|
111
|
+
|
112
|
+
def read_PREDEF
|
113
|
+
read_FILE("predef_file")
|
114
|
+
end
|
115
|
+
|
116
|
+
def read_POSTDEF
|
117
|
+
read_FILE("postdef_file")
|
118
|
+
end
|
119
|
+
|
120
|
+
def read_PART
|
121
|
+
@read_PART ||= File.read("#{@basedir}/#{part_file}")
|
122
|
+
end
|
123
|
+
|
124
|
+
def part_exist?
|
125
|
+
File.exist?("#{@basedir}/#{part_file}")
|
126
|
+
end
|
127
|
+
|
128
|
+
def read_bib
|
129
|
+
File.read("#{@basedir}/#{bib_file}")
|
130
|
+
end
|
131
|
+
|
132
|
+
def bib_exist?
|
133
|
+
File.exist?("#{@basedir}/#{bib_file}")
|
134
|
+
end
|
135
|
+
|
136
|
+
def prefaces
|
137
|
+
if File.file?("#{@basedir}/#{predef_file}")
|
138
|
+
begin
|
139
|
+
return mkpart_from_namelistfile("#{@basedir}/#{predef_file}")
|
140
|
+
rescue FileNotFound => err
|
141
|
+
raise FileNotFound, "preface #{err.message}"
|
142
|
+
end
|
143
|
+
else
|
144
|
+
mkpart_from_namelist(%w(preface))
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def postscripts
|
149
|
+
if File.file?("#{@basedir}/#{postdef_file}")
|
150
|
+
begin
|
151
|
+
return mkpart_from_namelistfile("#{@basedir}/#{postdef_file}")
|
152
|
+
rescue FileNotFound => err
|
153
|
+
raise FileNotFound, "postscript #{err.message}"
|
154
|
+
end
|
155
|
+
else
|
156
|
+
mkpart_from_namelist(%w(appendix postscript))
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def basedir
|
161
|
+
@basedir
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
def read_parts
|
167
|
+
list = parse_chapters
|
168
|
+
if pre = prefaces
|
169
|
+
list.unshift pre
|
170
|
+
end
|
171
|
+
if post = postscripts
|
172
|
+
list.push post
|
173
|
+
end
|
174
|
+
list
|
175
|
+
end
|
176
|
+
|
177
|
+
def parse_chapters
|
178
|
+
part = 0
|
179
|
+
num = 0
|
180
|
+
chap = read_CHAPS()\
|
181
|
+
.strip.lines.map {|line| line.strip }.join("\n").split(/\n{2,}/)\
|
182
|
+
.map {|part_chunk|
|
183
|
+
chaps = part_chunk.split.map {|chapid|
|
184
|
+
Chapter.new(self, (num += 1), chapid, "#{@basedir}/#{chapid}")
|
185
|
+
}
|
186
|
+
if part_exist? && read_PART.split("\n").size >= part
|
187
|
+
Part.new(self, (part += 1), chaps, read_PART.split("\n")[part-1])
|
188
|
+
else
|
189
|
+
Part.new(self, (part += 1), chaps)
|
190
|
+
end
|
191
|
+
}
|
192
|
+
return chap
|
193
|
+
end
|
194
|
+
|
195
|
+
def mkpart_from_namelistfile(path)
|
196
|
+
chaps = []
|
197
|
+
File.read(path).split.each_with_index do |name, idx|
|
198
|
+
name.sub!(/\A\xEF\xBB\xBF/u, '') # remove BOM
|
199
|
+
if path =~ /PREDEF/
|
200
|
+
chaps << mkchap(name)
|
201
|
+
else
|
202
|
+
chaps << mkchap(name, idx + 1)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
mkpart(chaps)
|
206
|
+
end
|
207
|
+
|
208
|
+
def mkpart_from_namelist(names)
|
209
|
+
mkpart(names.map {|n| mkchap_ifexist(n) }.compact)
|
210
|
+
end
|
211
|
+
|
212
|
+
def mkpart(chaps)
|
213
|
+
chaps.empty? ? nil : Part.new(self, nil, chaps)
|
214
|
+
end
|
215
|
+
|
216
|
+
def mkchap(name, number = nil)
|
217
|
+
name += ext if File.extname(name) == ""
|
218
|
+
path = "#{@basedir}/#{name}"
|
219
|
+
raise FileNotFound, "file not exist: #{path}" unless File.file?(path)
|
220
|
+
Chapter.new(self, number, name, path)
|
221
|
+
end
|
222
|
+
|
223
|
+
def mkchap_ifexist(id)
|
224
|
+
name = "#{id}#{ext()}"
|
225
|
+
path = "#{@basedir}/#{name}"
|
226
|
+
File.file?(path) ? Chapter.new(self, nil, name, path) : nil
|
227
|
+
end
|
228
|
+
|
229
|
+
def read_FILE(filename)
|
230
|
+
res = ""
|
231
|
+
File.open("#{@basedir}/#{eval(filename)}") do |f|
|
232
|
+
while line = f.gets
|
233
|
+
line.sub!(/\A\xEF\xBB\xBF/u, '') # remove BOM
|
234
|
+
if /\A#/ =~ line
|
235
|
+
next
|
236
|
+
end
|
237
|
+
line.gsub!(/#.*$/, "")
|
238
|
+
res << line
|
239
|
+
end
|
240
|
+
end
|
241
|
+
res
|
242
|
+
rescue Errno::ENOENT
|
243
|
+
Dir.glob("#{@basedir}/*#{ext()}").sort.join("\n")
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
#
|
2
|
+
# $Id: book.rb 4315 2009-09-02 04:15:24Z kmuto $
|
3
|
+
#
|
4
|
+
# Copyright (c) 2002-2008 Minero Aoki
|
5
|
+
# 2009 Minero Aoki, Kenshi Muto
|
6
|
+
#
|
7
|
+
# This program is free software.
|
8
|
+
# You can distribute or modify this program under the terms of
|
9
|
+
# the GNU LGPL, Lesser General Public License version 2.1.
|
10
|
+
# For details of the GNU LGPL, see the file "COPYING".
|
11
|
+
#
|
12
|
+
require 'review/book/compilable'
|
13
|
+
module ReVIEW
|
14
|
+
module Book
|
15
|
+
class Chapter
|
16
|
+
include Compilable
|
17
|
+
|
18
|
+
def Chapter.intern_pathes(pathes)
|
19
|
+
books = {}
|
20
|
+
pathes.map {|path|
|
21
|
+
basedir = File.dirname(path)
|
22
|
+
book = (books[File.expand_path(basedir)] ||= Book.load(basedir))
|
23
|
+
begin
|
24
|
+
book.chapter(File.basename(path, '.*'))
|
25
|
+
rescue KeyError => err
|
26
|
+
raise FileNotFound, "no such file: #{path}"
|
27
|
+
end
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def Chapter.for_stdin
|
32
|
+
new(nil, nil, '-', nil, $stdin)
|
33
|
+
end
|
34
|
+
|
35
|
+
def Chapter.for_path(number, path)
|
36
|
+
new(nil, number, File.basename(path), path)
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :number
|
40
|
+
|
41
|
+
def initialize(book, number, name, path, io = nil)
|
42
|
+
@book = book
|
43
|
+
@number = number
|
44
|
+
@name = name
|
45
|
+
@path = path
|
46
|
+
@io = io
|
47
|
+
@title = nil
|
48
|
+
@content = nil
|
49
|
+
@list_index = nil
|
50
|
+
@table_index = nil
|
51
|
+
@footnote_index = nil
|
52
|
+
@image_index = nil
|
53
|
+
@icon_index = nil
|
54
|
+
@numberless_image_index = nil
|
55
|
+
@indepimage_index = nil
|
56
|
+
@headline_index = nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def inspect
|
60
|
+
"\#<#{self.class} #{@number} #{@path}>"
|
61
|
+
end
|
62
|
+
|
63
|
+
def on_CHAPS?
|
64
|
+
on_FILE?(@book.read_CHAPS())
|
65
|
+
end
|
66
|
+
|
67
|
+
def on_PREDEF?
|
68
|
+
on_FILE?(@book.read_PREDEF())
|
69
|
+
end
|
70
|
+
|
71
|
+
def on_POSTDEF?
|
72
|
+
on_FILE?(@book.read_POSTDEF())
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
def on_FILE?(contents)
|
77
|
+
contents.lines.map(&:strip).include?(id() + @book.ext())
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
#
|
2
|
+
# $Id: book.rb 4315 2009-09-02 04:15:24Z kmuto $
|
3
|
+
#
|
4
|
+
# Copyright (c) 2002-2008 Minero Aoki
|
5
|
+
# 2009 Minero Aoki, Kenshi Muto
|
6
|
+
#
|
7
|
+
# This program is free software.
|
8
|
+
# You can distribute or modify this program under the terms of
|
9
|
+
# the GNU LGPL, Lesser General Public License version 2.1.
|
10
|
+
# For details of the GNU LGPL, see the file "COPYING".
|
11
|
+
#
|
12
|
+
require 'review/textutils'
|
13
|
+
module ReVIEW
|
14
|
+
module Book
|
15
|
+
module Compilable
|
16
|
+
include TextUtils
|
17
|
+
attr_reader :book
|
18
|
+
attr_reader :path
|
19
|
+
|
20
|
+
def env
|
21
|
+
@book
|
22
|
+
end
|
23
|
+
|
24
|
+
def dirname
|
25
|
+
return nil unless @path
|
26
|
+
File.dirname(@path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def basename
|
30
|
+
return nil unless @path
|
31
|
+
File.basename(@path)
|
32
|
+
end
|
33
|
+
|
34
|
+
def name
|
35
|
+
File.basename(@name, '.*')
|
36
|
+
end
|
37
|
+
|
38
|
+
alias id name
|
39
|
+
|
40
|
+
def title
|
41
|
+
@title = ""
|
42
|
+
open {|f|
|
43
|
+
f.each_line {|l|
|
44
|
+
l = convert_inencoding(l, ReVIEW.book.param["inencoding"])
|
45
|
+
if l =~ /\A=+/
|
46
|
+
@title = l.sub(/\A=+/, '').strip
|
47
|
+
break
|
48
|
+
end
|
49
|
+
}
|
50
|
+
}
|
51
|
+
@title
|
52
|
+
end
|
53
|
+
|
54
|
+
def size
|
55
|
+
File.size(path())
|
56
|
+
end
|
57
|
+
|
58
|
+
def volume
|
59
|
+
@volume ||= Volume.count_file(path())
|
60
|
+
end
|
61
|
+
|
62
|
+
def open(&block)
|
63
|
+
return (block_given?() ? yield(@io) : @io) if @io
|
64
|
+
File.open(path(), &block)
|
65
|
+
end
|
66
|
+
|
67
|
+
attr_writer :content
|
68
|
+
|
69
|
+
def content
|
70
|
+
@content = convert_inencoding(File.read(path()),
|
71
|
+
ReVIEW.book.param["inencoding"])
|
72
|
+
rescue
|
73
|
+
@content
|
74
|
+
end
|
75
|
+
|
76
|
+
def lines
|
77
|
+
# FIXME: we cannot duplicate Enumerator on ruby 1.9 HEAD
|
78
|
+
(@lines ||= content().lines.to_a).dup
|
79
|
+
end
|
80
|
+
|
81
|
+
def list(id)
|
82
|
+
list_index()[id]
|
83
|
+
end
|
84
|
+
|
85
|
+
def list_index
|
86
|
+
@list_index ||= ListIndex.parse(lines())
|
87
|
+
@list_index
|
88
|
+
end
|
89
|
+
|
90
|
+
def table(id)
|
91
|
+
table_index()[id]
|
92
|
+
end
|
93
|
+
|
94
|
+
def table_index
|
95
|
+
@table_index ||= TableIndex.parse(lines())
|
96
|
+
@table_index
|
97
|
+
end
|
98
|
+
|
99
|
+
def footnote(id)
|
100
|
+
footnote_index()[id]
|
101
|
+
end
|
102
|
+
|
103
|
+
def footnote_index
|
104
|
+
@footnote_index ||= FootnoteIndex.parse(lines())
|
105
|
+
@footnote_index
|
106
|
+
end
|
107
|
+
|
108
|
+
def image(id)
|
109
|
+
return image_index()[id] if image_index().has_key?(id)
|
110
|
+
return icon_index()[id] if icon_index().has_key?(id)
|
111
|
+
return numberless_image_index()[id] if numberless_image_index().has_key?(id)
|
112
|
+
indepimage_index()[id]
|
113
|
+
end
|
114
|
+
|
115
|
+
def numberless_image_index
|
116
|
+
@numberless_image_index ||=
|
117
|
+
NumberlessImageIndex.parse(lines(), id(),
|
118
|
+
"#{book.basedir}#{@book.image_dir}",
|
119
|
+
@book.image_types)
|
120
|
+
end
|
121
|
+
|
122
|
+
def image_index
|
123
|
+
@image_index ||= ImageIndex.parse(lines(), id(),
|
124
|
+
"#{book.basedir}#{@book.image_dir}",
|
125
|
+
@book.image_types)
|
126
|
+
@image_index
|
127
|
+
end
|
128
|
+
|
129
|
+
def icon_index
|
130
|
+
@icon_index ||= IconIndex.parse(lines(), id(),
|
131
|
+
"#{book.basedir}#{@book.image_dir}",
|
132
|
+
@book.image_types)
|
133
|
+
@icon_index
|
134
|
+
end
|
135
|
+
|
136
|
+
def indepimage_index
|
137
|
+
@indepimage_index ||=
|
138
|
+
IndepImageIndex.parse(lines(), id(),
|
139
|
+
"#{book.basedir}#{@book.image_dir}",
|
140
|
+
@book.image_types)
|
141
|
+
end
|
142
|
+
|
143
|
+
def bibpaper(id)
|
144
|
+
bibpaper_index()[id]
|
145
|
+
end
|
146
|
+
|
147
|
+
def bibpaper_index
|
148
|
+
raise FileNotFound, "no such bib file: #{@book.bib_file}" unless @book.bib_exist?
|
149
|
+
@bibpaper_index ||= BibpaperIndex.parse(@book.read_bib.lines.to_a)
|
150
|
+
@bibpaper_index
|
151
|
+
end
|
152
|
+
|
153
|
+
def headline(caption)
|
154
|
+
headline_index()[caption]
|
155
|
+
end
|
156
|
+
|
157
|
+
def headline_index
|
158
|
+
@headline_index ||= HeadlineIndex.parse(lines(), self)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|