rock_books 0.7.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/{manual.md → MANUAL.md} +26 -26
- data/README.md +20 -12
- data/RELEASE_NOTES.md +55 -10
- data/REPORTS.md +81 -0
- data/assets/doc-images/sample-index-html.png +0 -0
- data/lib/rock_books/cmd_line/command_line_interface.rb +12 -21
- data/lib/rock_books/documents/book_set.rb +4 -1
- data/lib/rock_books/documents/journal.rb +2 -3
- data/lib/rock_books/reports/balance_sheet.rb +2 -2
- data/lib/rock_books/reports/book_set_reporter.rb +69 -55
- data/lib/rock_books/reports/data/bs_is_section_data.rb +3 -1
- data/lib/rock_books/reports/data/journal_data.rb +1 -0
- data/lib/rock_books/reports/data/multidoc_txn_by_account_data.rb +1 -1
- data/lib/rock_books/reports/data/tx_one_account_data.rb +1 -1
- data/lib/rock_books/reports/helpers/erb_helper.rb +1 -6
- data/lib/rock_books/reports/helpers/receipts_hyperlink_converter.rb +59 -0
- data/lib/rock_books/reports/helpers/{reporter.rb → text_report_helper.rb} +11 -1
- data/lib/rock_books/reports/income_statement.rb +2 -2
- data/lib/rock_books/reports/index_html_page.rb +27 -0
- data/lib/rock_books/reports/journal_report.rb +18 -8
- data/lib/rock_books/reports/multidoc_txn_by_account_report.rb +2 -2
- data/lib/rock_books/reports/multidoc_txn_report.rb +2 -2
- data/lib/rock_books/reports/receipts_report.rb +1 -1
- data/lib/rock_books/reports/templates/html/index.html.erb +45 -6
- data/lib/rock_books/reports/templates/html/report_page.html.erb +14 -1
- data/lib/rock_books/reports/templates/text/balance_sheet.txt.erb +2 -0
- data/lib/rock_books/reports/templates/text/income_statement.txt.erb +3 -1
- data/lib/rock_books/reports/templates/text/journal.txt.erb +5 -2
- data/lib/rock_books/reports/templates/text/multidoc_txn_by_account_report.txt.erb +3 -0
- data/lib/rock_books/reports/templates/text/multidoc_txn_report.txt.erb +3 -0
- data/lib/rock_books/reports/templates/text/receipts_report.txt.erb +3 -0
- data/lib/rock_books/reports/templates/text/tx_one_account.txt.erb +3 -0
- data/lib/rock_books/reports/tx_one_account.rb +2 -2
- data/lib/rock_books/version.rb +2 -1
- data/rock_books.gemspec +1 -0
- metadata +22 -5
- data/lib/rock_books/helpers/html_helper.rb +0 -35
@@ -4,7 +4,8 @@ require 'ostruct'
|
|
4
4
|
|
5
5
|
require_relative '../../rock_books'
|
6
6
|
require_relative '../version'
|
7
|
-
require_relative '../reports/
|
7
|
+
require_relative '../reports/data/receipts_report_data'
|
8
|
+
require_relative '../reports/helpers/text_report_helper'
|
8
9
|
require_relative '../helpers/book_set_loader'
|
9
10
|
|
10
11
|
module RockBooks
|
@@ -51,12 +52,11 @@ Commands:
|
|
51
52
|
|
52
53
|
rec[eipts] - receipts: a/:a all, m/:m missing, e/:e existing, u/:u unused
|
53
54
|
rep[orts] - return an OpenStruct containing all reports (interactive shell mode only)
|
54
|
-
d[isplay_reports] - display all reports on stdout
|
55
55
|
w[rite_reports] - write all reports to the output directory (see -o option)
|
56
56
|
c[hart_of_accounts] - chart of accounts
|
57
57
|
h[elp] - prints this help
|
58
58
|
jo[urnals] - list of the journals' short names
|
59
|
-
proj[ect_page] -
|
59
|
+
proj[ect_page] - prints the RockBooks Github project page URL
|
60
60
|
rel[oad_data] - reload data from input files
|
61
61
|
q[uit] - exits this program (interactive shell mode only) (see also 'x')
|
62
62
|
x[it] - exits this program (interactive shell mode only) (see also 'q')
|
@@ -281,7 +281,7 @@ When in interactive shell mode:
|
|
281
281
|
# All reports as Ruby objects; only makes sense in shell mode.
|
282
282
|
def cmd_rep
|
283
283
|
unless run_options.interactive_mode
|
284
|
-
raise Error.new("Option 'all_reports' is only available in shell mode. Try '
|
284
|
+
raise Error.new("Option 'all_reports' is only available in shell mode. Try 'write_reports'.")
|
285
285
|
end
|
286
286
|
|
287
287
|
os = OpenStruct.new(book_set.all_reports($filter))
|
@@ -297,18 +297,8 @@ When in interactive shell mode:
|
|
297
297
|
end
|
298
298
|
|
299
299
|
|
300
|
-
def cmd_d
|
301
|
-
book_set.all_reports($filter).each do |short_name, text_report|
|
302
|
-
puts "#{short_name}:\n\n"
|
303
|
-
puts text_report
|
304
|
-
puts "\n\n\n"
|
305
|
-
end
|
306
|
-
nil
|
307
|
-
end
|
308
|
-
|
309
|
-
|
310
300
|
def cmd_proj
|
311
|
-
|
301
|
+
puts 'https://github.com/keithrbennett/rock_books'
|
312
302
|
end
|
313
303
|
|
314
304
|
|
@@ -317,7 +307,9 @@ When in interactive shell mode:
|
|
317
307
|
raise Error.new("Receipt processing was requested but has been disabled with --no-receipts.")
|
318
308
|
end
|
319
309
|
|
320
|
-
|
310
|
+
data = ReceiptsReportData.new(all_entries, run_options.receipt_dir).fetch
|
311
|
+
|
312
|
+
missing, existing, unused = data[:missing], data[:existing], data[:unused]
|
321
313
|
|
322
314
|
print_missing = -> { puts "\n\nMissing Receipts:"; ap missing }
|
323
315
|
print_existing = -> { puts "\n\nExisting Receipts:"; ap existing }
|
@@ -326,11 +318,11 @@ When in interactive shell mode:
|
|
326
318
|
case options.first.to_s
|
327
319
|
when 'a' # all
|
328
320
|
if run_options.interactive_mode
|
329
|
-
|
321
|
+
data
|
330
322
|
else
|
331
|
-
|
332
|
-
|
333
|
-
|
323
|
+
print_missing.()
|
324
|
+
print_existing.()
|
325
|
+
print_unused.()
|
334
326
|
end
|
335
327
|
|
336
328
|
when 'm'
|
@@ -372,7 +364,6 @@ When in interactive shell mode:
|
|
372
364
|
@commands_ ||= [
|
373
365
|
Command.new('rec', 'receipts', -> (*options) { cmd_rec(options) }),
|
374
366
|
Command.new('rep', 'reports', -> (*_options) { cmd_rep }),
|
375
|
-
Command.new('d', 'display_reports', -> (*_options) { cmd_d }),
|
376
367
|
Command.new('w', 'write_reports', -> (*_options) { cmd_w }),
|
377
368
|
Command.new('c', 'chart_of_accounts', -> (*_options) { cmd_c }),
|
378
369
|
Command.new('jo', 'journals', -> (*_options) { cmd_j }),
|
@@ -4,7 +4,6 @@ require 'os'
|
|
4
4
|
require_relative 'chart_of_accounts'
|
5
5
|
require_relative 'journal'
|
6
6
|
require_relative '../filters/journal_entry_filters' # for shell mode
|
7
|
-
require_relative '../helpers/html_helper'
|
8
7
|
require_relative '../helpers/parse_helper'
|
9
8
|
require_relative '../reports/book_set_reporter'
|
10
9
|
|
@@ -39,6 +38,10 @@ module RockBooks
|
|
39
38
|
@all_entries ||= Journal.entries_in_documents(journals)
|
40
39
|
end
|
41
40
|
|
41
|
+
|
42
|
+
def all_reports(filter = nil)
|
43
|
+
BookSetReporter.new(self, nil, filter).get_all_report_data
|
44
|
+
end
|
42
45
|
end
|
43
46
|
end
|
44
47
|
|
@@ -7,7 +7,7 @@ require_relative '../types/acct_amount'
|
|
7
7
|
require_relative '../types/journal_entry_context'
|
8
8
|
require_relative 'journal_entry'
|
9
9
|
require_relative 'journal_entry_builder'
|
10
|
-
require_relative '../reports/helpers/
|
10
|
+
require_relative '../reports/helpers/text_report_helper'
|
11
11
|
|
12
12
|
module RockBooks
|
13
13
|
|
@@ -65,9 +65,8 @@ class Journal
|
|
65
65
|
attr_reader :short_name, :account_code, :chart_of_accounts, :date_prefix, :debit_or_credit, :doc_type, :title, :entries
|
66
66
|
|
67
67
|
# short_name is a name that will appear on reports identifying the journal from which a transaction comes
|
68
|
-
def initialize(chart_of_accounts, input_lines
|
68
|
+
def initialize(chart_of_accounts, input_lines)
|
69
69
|
@chart_of_accounts = chart_of_accounts
|
70
|
-
@short_name = short_name
|
71
70
|
@entries = []
|
72
71
|
@date_prefix = ''
|
73
72
|
input_lines.each_with_index do |line, linenum|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require_relative 'helpers/erb_helper'
|
2
|
-
require_relative 'helpers/
|
2
|
+
require_relative 'helpers/text_report_helper'
|
3
3
|
|
4
4
|
module RockBooks
|
5
5
|
|
@@ -8,7 +8,7 @@ module RockBooks
|
|
8
8
|
# in order to calculate the correct balances, so we ignore the global $filter.
|
9
9
|
class BalanceSheet
|
10
10
|
|
11
|
-
include
|
11
|
+
include TextReportHelper
|
12
12
|
include ErbHelper
|
13
13
|
|
14
14
|
attr_accessor :context, :data
|
@@ -4,6 +4,7 @@ require_relative 'balance_sheet'
|
|
4
4
|
require_relative 'data/bs_is_data'
|
5
5
|
require_relative 'data/receipts_report_data'
|
6
6
|
require_relative 'income_statement'
|
7
|
+
require_relative 'index_html_page'
|
7
8
|
require_relative 'multidoc_txn_report'
|
8
9
|
require_relative 'receipts_report'
|
9
10
|
require_relative 'report_context'
|
@@ -11,16 +12,18 @@ require_relative 'journal_report'
|
|
11
12
|
require_relative 'multidoc_txn_by_account_report'
|
12
13
|
require_relative 'tx_one_account'
|
13
14
|
require_relative 'helpers/erb_helper'
|
14
|
-
require_relative 'helpers/
|
15
|
+
require_relative 'helpers/text_report_helper'
|
16
|
+
require_relative 'helpers/receipts_hyperlink_converter'
|
15
17
|
|
16
18
|
require 'prawn'
|
19
|
+
require 'tty-progressbar'
|
17
20
|
|
18
21
|
module RockBooks
|
19
22
|
class BookSetReporter
|
20
23
|
|
21
24
|
extend Forwardable
|
22
25
|
|
23
|
-
attr_reader :book_set, :
|
26
|
+
attr_reader :book_set, :context, :filter, :output_dir, :progress_bar
|
24
27
|
|
25
28
|
def_delegator :book_set, :all_entries
|
26
29
|
def_delegator :book_set, :journals
|
@@ -35,6 +38,7 @@ class BookSetReporter
|
|
35
38
|
@output_dir = output_dir
|
36
39
|
@filter = filter
|
37
40
|
@context = ReportContext.new(book_set.chart_of_accounts, book_set.journals, 80)
|
41
|
+
@progress_bar = TTY::ProgressBar.new("[:bar] :caption", total: report_count + 10)
|
38
42
|
end
|
39
43
|
|
40
44
|
|
@@ -47,9 +51,44 @@ class BookSetReporter
|
|
47
51
|
do_transaction_reports
|
48
52
|
do_single_account_reports
|
49
53
|
do_receipts_report
|
54
|
+
progress_bar.advance(caption: 'Finished generating reports.')
|
55
|
+
progress_bar.finish
|
50
56
|
end
|
51
57
|
|
52
58
|
|
59
|
+
def report_count
|
60
|
+
bal_sheet_income_statement = 2
|
61
|
+
journal_count = journals.size
|
62
|
+
txn_report_count = 3
|
63
|
+
single_account_count = chart_of_accounts.accounts.size
|
64
|
+
receipt_report_count = 1
|
65
|
+
bal_sheet_income_statement + journal_count + txn_report_count + single_account_count + receipt_report_count
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def get_all_report_data
|
70
|
+
reports = {}
|
71
|
+
|
72
|
+
reports[:bs_is] = BsIsData.new(context)
|
73
|
+
|
74
|
+
reports[:journals] = journals.each_with_object({}) do |journal, journals|
|
75
|
+
journals[journal.short_name] = JournalData.new(journal, context, filter).fetch
|
76
|
+
end
|
77
|
+
|
78
|
+
reports[:txn_reports] = {
|
79
|
+
by_account: MultidocTxnByAccountData.new(context).fetch,
|
80
|
+
by_date: MultidocTxnReportData.new(context, :date, filter).fetch,
|
81
|
+
by_amount: MultidocTxnReportData.new(context, :amount, filter).fetch
|
82
|
+
}
|
83
|
+
|
84
|
+
reports[:single_accounts] = chart_of_accounts.accounts.each_with_object({}) do |account, single_accts|
|
85
|
+
single_accts[account.code.to_sym] = TxOneAccountData.new(context, account.code).fetch
|
86
|
+
end
|
87
|
+
|
88
|
+
reports[:receipts] = ReceiptsReportData.new(book_set.all_entries, run_options.receipt_dir).fetch
|
89
|
+
reports
|
90
|
+
end
|
91
|
+
|
53
92
|
# All methods after this point are private.
|
54
93
|
|
55
94
|
private def do_statements
|
@@ -109,16 +148,6 @@ class BookSetReporter
|
|
109
148
|
end
|
110
149
|
|
111
150
|
|
112
|
-
private def run_command(command)
|
113
|
-
puts "\n----\nRunning command: #{command}"
|
114
|
-
stdout, stderr, status = Open3.capture3(command)
|
115
|
-
puts "Exit code was #{status.exitstatus}."
|
116
|
-
puts "\nStdout was:\n\n#{stdout}" unless stdout.size == 0
|
117
|
-
puts "\nStderr was:\n\n#{stderr}" unless stderr.size == 0
|
118
|
-
puts
|
119
|
-
end
|
120
|
-
|
121
|
-
|
122
151
|
private def create_directories
|
123
152
|
%w(txt pdf html).each do |format|
|
124
153
|
dir = File.join(output_dir, format, SINGLE_ACCT_SUBDIR)
|
@@ -138,22 +167,29 @@ class BookSetReporter
|
|
138
167
|
end
|
139
168
|
|
140
169
|
|
141
|
-
private def
|
142
|
-
|
143
|
-
|
144
|
-
|
170
|
+
private def report_metadata(doc_short_name)
|
171
|
+
{
|
172
|
+
RBCreator: "RockBooks v#{VERSION} (#{PROJECT_URL})",
|
173
|
+
RBEntity: context.entity,
|
174
|
+
RBCreated: Time.now.to_s,
|
175
|
+
RBDocumentCode: doc_short_name.to_s,
|
176
|
+
}
|
145
177
|
end
|
146
178
|
|
147
179
|
|
148
|
-
private def prawn_create_document(pdf_filespec,
|
149
|
-
Prawn::Document.generate(pdf_filespec) do
|
180
|
+
private def prawn_create_document(pdf_filespec, report_text, doc_short_name)
|
181
|
+
Prawn::Document.generate(pdf_filespec, info: report_metadata(doc_short_name)) do
|
150
182
|
font(FONT_FILESPEC, size: 10)
|
151
183
|
|
152
184
|
utf8_nonbreaking_space = "\uC2A0"
|
153
185
|
unicode_nonbreaking_space = "\u00A0"
|
154
|
-
text(
|
186
|
+
text(report_text.gsub(' ', unicode_nonbreaking_space))
|
155
187
|
end
|
156
|
-
|
188
|
+
end
|
189
|
+
|
190
|
+
|
191
|
+
private def html_metadata_comment(doc_short_name)
|
192
|
+
"\n" + report_metadata(doc_short_name).ai(plain: true) + "\n"
|
157
193
|
end
|
158
194
|
|
159
195
|
|
@@ -165,12 +201,16 @@ class BookSetReporter
|
|
165
201
|
|
166
202
|
create_text_report = -> { File.write(txt_filespec, text_report) }
|
167
203
|
|
168
|
-
create_pdf_report = -> { prawn_create_document(pdf_filespec, text_report) }
|
204
|
+
create_pdf_report = -> { prawn_create_document(pdf_filespec, text_report, short_name) }
|
169
205
|
|
170
206
|
create_html_report = -> do
|
171
|
-
data = {
|
207
|
+
data = {
|
208
|
+
report_body: text_report,
|
209
|
+
title: "#{short_name} Report -- RockBooks",
|
210
|
+
metadata_comment: html_metadata_comment(short_name)
|
211
|
+
}
|
172
212
|
html_raw_report = ErbHelper.render_hashes("html/report_page.html.erb", data, {})
|
173
|
-
html_report =
|
213
|
+
html_report = ReceiptsHyperlinkConverter.convert(html_raw_report, html_filespec)
|
174
214
|
File.write(html_filespec, html_report)
|
175
215
|
end
|
176
216
|
|
@@ -178,41 +218,15 @@ class BookSetReporter
|
|
178
218
|
create_pdf_report.()
|
179
219
|
create_html_report.()
|
180
220
|
|
181
|
-
|
182
|
-
puts "Created reports in txt, html, and pdf for #{"%-20s" % short_name} at #{File.dirname(txt_filespec)}.\n\n\n"
|
183
|
-
end
|
184
|
-
|
185
|
-
|
186
|
-
private def missing_existing_unused_receipts
|
187
|
-
missing_receipts = []
|
188
|
-
existing_receipts = []
|
189
|
-
receipt_full_filespec = ->(receipt_filespec) { File.join(run_options.receipt_dir, receipt_filespec) }
|
190
|
-
|
191
|
-
# We will start out putting all filespecs in the unused array, and delete them as they are found in the transactions.
|
192
|
-
unused_receipt_filespecs = Dir['receipts/**/*'].select { |s| File.file?(s) } \
|
193
|
-
.sort \
|
194
|
-
.map { |s| "./" + s } # Prepend './' to match the data
|
195
|
-
|
196
|
-
all_entries.each do |entry|
|
197
|
-
entry.receipts.each do |receipt|
|
198
|
-
filespec = receipt_full_filespec.(receipt)
|
199
|
-
unused_receipt_filespecs.delete(filespec)
|
200
|
-
file_exists = File.file?(filespec)
|
201
|
-
list = (file_exists ? existing_receipts : missing_receipts)
|
202
|
-
list << { receipt: receipt, journal: entry.doc_short_name }
|
203
|
-
end
|
204
|
-
end
|
205
|
-
[missing_receipts, existing_receipts, unused_receipt_filespecs]
|
221
|
+
progress_bar.advance(caption: "Generating report: #{short_name}")
|
206
222
|
end
|
207
223
|
|
208
224
|
|
209
|
-
private def
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
chart_of_accounts: chart_of_accounts,
|
215
|
-
run_options: run_options)
|
225
|
+
private def create_index_html
|
226
|
+
progress_bar.advance(caption: 'Generating index.html')
|
227
|
+
filespec = build_filespec(output_dir, 'index', 'html')
|
228
|
+
content = IndexHtmlPage.new(context, html_metadata_comment('index.html'), run_options).generate
|
229
|
+
File.write(filespec, content)
|
216
230
|
end
|
217
231
|
end
|
218
232
|
end
|
@@ -19,7 +19,9 @@ class BsIsSectionData
|
|
19
19
|
totals = journals_acct_totals.select { |code, _amount| codes.include?(code) }
|
20
20
|
need_to_reverse_sign = %i{liability equity income}.include?(type)
|
21
21
|
if need_to_reverse_sign
|
22
|
-
totals.keys.each
|
22
|
+
totals.keys.each do |code|
|
23
|
+
totals[code] = -totals[code] unless totals[code] == 0.0
|
24
|
+
end
|
23
25
|
end
|
24
26
|
totals
|
25
27
|
end
|
@@ -25,6 +25,7 @@ class JournalData
|
|
25
25
|
name: journal.chart_of_accounts.name_for_code(journal.account_code),
|
26
26
|
title: journal.title,
|
27
27
|
short_name: journal.short_name,
|
28
|
+
debit_or_credit: journal.debit_or_credit,
|
28
29
|
start_date: context.chart_of_accounts.start_date,
|
29
30
|
end_date: context.chart_of_accounts.end_date,
|
30
31
|
entries: entries,
|
@@ -8,19 +8,14 @@ module ErbHelper
|
|
8
8
|
|
9
9
|
|
10
10
|
def self.render_binding(erb_relative_filespec, template_binding)
|
11
|
-
print "Rendering template #{erb_relative_filespec}..."
|
12
11
|
result = erb_template(erb_relative_filespec).result(template_binding)
|
13
|
-
puts 'done.'
|
14
12
|
result
|
15
13
|
end
|
16
14
|
|
17
15
|
# Takes 2 hashes, one with data, and the other with presentation functions/lambdas, and passes their union to ERB
|
18
16
|
# for rendering.
|
19
17
|
def self.render_hashes(erb_relative_filespec, data_hash, presentation_hash)
|
20
|
-
print "Rendering template #{erb_relative_filespec}..."
|
21
18
|
combined_hash = (data_hash || {}).merge(presentation_hash || {})
|
22
|
-
|
23
|
-
puts 'done.'
|
24
|
-
result
|
19
|
+
erb_template(erb_relative_filespec).result_with_hash(combined_hash)
|
25
20
|
end
|
26
21
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module RockBooks
|
2
|
+
class ReceiptsHyperlinkConverter
|
3
|
+
|
4
|
+
def self.convert(html_string, html_filespec)
|
5
|
+
ReceiptsHyperlinkConverter.new(html_string, html_filespec).convert
|
6
|
+
end
|
7
|
+
|
8
|
+
RECEIPT_REGEX = /Receipt:\s*(\S*)/
|
9
|
+
INVOICE_REGEX = /Invoice:\s*(\S*)/
|
10
|
+
|
11
|
+
attr_reader :html_string, :num_dirs_up
|
12
|
+
|
13
|
+
def initialize(html_string, html_filespec)
|
14
|
+
@html_string = html_string
|
15
|
+
@num_dirs_up = html_filespec.include?('/single-account/') ? 3 : 2
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def convert
|
20
|
+
process_link_type = ->(line, regex, dir_name) do
|
21
|
+
matches = regex.match(line)
|
22
|
+
if matches
|
23
|
+
listed_filespec = matches[1]
|
24
|
+
anchor_line(line, listed_filespec, dir_name)
|
25
|
+
else
|
26
|
+
line
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
html_string.split("\n").map do |line|
|
31
|
+
line = process_link_type.(line, RECEIPT_REGEX, 'receipts')
|
32
|
+
process_link_type.(line, INVOICE_REGEX, 'invoices')
|
33
|
+
end.join("\n")
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
# If the HTML file being created is in DATA_DIR/rockbooks-reports/html/single-account, then
|
38
|
+
# the processed link should be '../../../receipts/[receipt_filespec]'
|
39
|
+
# else it's in DATA_DIR/rockbooks-reports/html, and
|
40
|
+
# the processed link should be '../../receipts/[receipt_filespec]'
|
41
|
+
#
|
42
|
+
# `dir_name` will be 'receipts' or 'invoices'
|
43
|
+
private def dirized_filespec(listed_filespec, dir_name)
|
44
|
+
File.join(('../' * num_dirs_up), dir_name, listed_filespec)
|
45
|
+
end
|
46
|
+
|
47
|
+
private def anchor_line(line, listed_filespec, dir_name)
|
48
|
+
label = {
|
49
|
+
'receipts' => 'Receipt',
|
50
|
+
'invoices' => 'Invoice'
|
51
|
+
}.fetch(dir_name)
|
52
|
+
|
53
|
+
line.gsub( \
|
54
|
+
/#{label}:\s*#{listed_filespec}/, \
|
55
|
+
%Q{#{label}: <a href="#{dirized_filespec(listed_filespec, dir_name)}">#{listed_filespec}</a>})
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|