fsorg 0.3.0 → 0.4.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.
- checksums.yaml +4 -4
- data/lib/fsorg.rb +102 -113
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c401683bfc45360ee97ed03f82c6b4175991662e03b553189a7a1766838b578
|
4
|
+
data.tar.gz: f95ab0033bb60452eda403e6cd39ed8d7c84e3f66ed27693f98d5eab5663c7a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83d7e4eae3fd10ab1acca9d12b35880075a33dc4d19cc3e5ecc7c792860dfbd16fcebe401ffd93fc395199b4f6d7a5116bc8061e59ff9f61eb9f25202599c5b4
|
7
|
+
data.tar.gz: a8795af243ce4767ebeb885b3cc2dc9765a809da18ce2edb62293a58cd1eee5559b472cc3963fedb04179b01b08200caed39dc74a6b6fdb6c40fd541dea5597c
|
data/lib/fsorg.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "colorize"
|
2
4
|
require "set"
|
3
5
|
require "shellwords"
|
@@ -7,17 +9,17 @@ PERMISSIONS_PATTERN = /[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+/
|
|
7
9
|
|
8
10
|
def d(thing)
|
9
11
|
p thing
|
10
|
-
|
12
|
+
thing
|
11
13
|
end
|
12
14
|
|
13
|
-
class Fsorg
|
15
|
+
class Fsorg # rubocop:disable Metrics/ClassLength,Style/Documentation
|
14
16
|
attr_accessor :data, :document, :document_path, :root_directory
|
15
17
|
|
16
18
|
def initialize(root_directory, data, document, absolute_path)
|
17
19
|
@root_directory = root_directory
|
18
20
|
@shell = "/usr/bin/env bash"
|
19
21
|
@document_path = absolute_path
|
20
|
-
@data = { :
|
22
|
+
@data = { HERE: @root_directory.to_s }.merge data
|
21
23
|
@document = document
|
22
24
|
@current_line = 0
|
23
25
|
@current_depth = -1
|
@@ -29,10 +31,10 @@ class Fsorg
|
|
29
31
|
|
30
32
|
def depth_change(line)
|
31
33
|
change = 0
|
32
|
-
if /\}
|
34
|
+
if /\}\s*$/ =~ line
|
33
35
|
change -= 1
|
34
36
|
end
|
35
|
-
if /^\{/ =~ line
|
37
|
+
if /^\s*\{/ =~ line
|
36
38
|
change += 1
|
37
39
|
end
|
38
40
|
change
|
@@ -43,7 +45,7 @@ class Fsorg
|
|
43
45
|
strip_comments
|
44
46
|
desugar
|
45
47
|
process_includes
|
46
|
-
# TODO process_see
|
48
|
+
# TODO: process_see
|
47
49
|
process_for
|
48
50
|
process_if
|
49
51
|
turn_puts_into_runs
|
@@ -52,13 +54,14 @@ class Fsorg
|
|
52
54
|
process_shell
|
53
55
|
end
|
54
56
|
|
55
|
-
def process_front_matter
|
57
|
+
def process_front_matter # rubocop:disable Metrics/MethodLength
|
56
58
|
unless /^-+$/ =~ @document.lines(chomp: true).first
|
57
59
|
return
|
58
60
|
end
|
59
61
|
|
60
62
|
# Remove front matter from document
|
61
|
-
front_matter_raw
|
63
|
+
front_matter_raw = ""
|
64
|
+
rest = ""
|
62
65
|
inside_front_matter = false
|
63
66
|
@document.lines(chomp: true).each_with_index do |line, index|
|
64
67
|
@current_linecur = index + 1
|
@@ -68,19 +71,19 @@ class Fsorg
|
|
68
71
|
end
|
69
72
|
|
70
73
|
if inside_front_matter
|
71
|
-
front_matter_raw += line
|
74
|
+
front_matter_raw += "#{line}\n"
|
72
75
|
else
|
73
|
-
rest += line
|
76
|
+
rest += "#{line}\n"
|
74
77
|
end
|
75
78
|
end
|
76
79
|
|
77
|
-
front_matter = YAML.
|
80
|
+
front_matter = YAML.safe_load(front_matter_raw, symbolize_names: true) or {}
|
78
81
|
|
79
82
|
@data = front_matter.merge @data
|
80
83
|
@document = rest
|
81
84
|
end
|
82
85
|
|
83
|
-
def desugar
|
86
|
+
def desugar # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
84
87
|
output = []
|
85
88
|
inside_write_directive_shorthand = false
|
86
89
|
@document.lines(chomp: true).each_with_index do |line, index|
|
@@ -101,8 +104,8 @@ class Fsorg
|
|
101
104
|
output << $~[1]
|
102
105
|
output << $~[2]
|
103
106
|
output << "}"
|
104
|
-
elsif /^(?<filename>.+?)\s*(\s+\((?<permissions>#{PERMISSIONS_PATTERN
|
105
|
-
output << "WRITE #{$~[:filename]}
|
107
|
+
elsif /^(?<filename>.+?)\s*(\s+\((?<permissions>#{PERMISSIONS_PATTERN})\))?\s*\[$/.match line.strip
|
108
|
+
output << "WRITE #{$~[:filename]} #{$~[:permissions] ? "MODE #{$~[:permissions]}" : ""} {"
|
106
109
|
inside_write_directive_shorthand = true
|
107
110
|
else
|
108
111
|
output << line
|
@@ -113,23 +116,23 @@ class Fsorg
|
|
113
116
|
|
114
117
|
def strip_comments
|
115
118
|
output = []
|
116
|
-
@document.lines(chomp: true).each_with_index do |line,
|
117
|
-
if /^(?<content>.*)#\s.*$/ =~ line
|
118
|
-
|
119
|
+
@document.lines(chomp: true).each_with_index do |line, _index|
|
120
|
+
output << if /^(?<content>.*)#\s.*$/ =~ line
|
121
|
+
content
|
119
122
|
else
|
120
|
-
|
123
|
+
line
|
121
124
|
end
|
122
125
|
end
|
123
126
|
@document = output.join "\n"
|
124
127
|
end
|
125
128
|
|
126
|
-
def process_includes
|
129
|
+
def process_includes # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
127
130
|
output = []
|
128
131
|
|
129
132
|
@document.lines(chomp: true).each_with_index do |line, index|
|
130
133
|
@current_line = index + 1
|
131
134
|
if line.start_with? "INCLUDE "
|
132
|
-
filepath = @document_path.parent.join(line.sub
|
135
|
+
filepath = @document_path.parent.join(line.sub(/^INCLUDE /, ""))
|
133
136
|
included_raw = File.new(filepath).read.strip
|
134
137
|
included_fsorg = Fsorg.new(@root_directory, @data, included_raw, filepath)
|
135
138
|
included_fsorg.preprocess
|
@@ -149,25 +152,25 @@ class Fsorg
|
|
149
152
|
@document = output.join "\n"
|
150
153
|
end
|
151
154
|
|
152
|
-
def turn_puts_into_runs
|
155
|
+
def turn_puts_into_runs # rubocop:disable Metrics/MethodLength
|
153
156
|
output = []
|
154
157
|
@document.lines(chomp: true).each_with_index do |line, index|
|
155
158
|
@current_line = index + 1
|
156
|
-
if /^PUT\s+(?<source>.+?)(\s+AS\s+(?<destination>.+?))?(\s+MODE\s+(?<permissions>.+?))?$/.match(line.strip)
|
157
|
-
|
159
|
+
output << if /^PUT\s+(?<source>.+?)(\s+AS\s+(?<destination>.+?))?(\s+MODE\s+(?<permissions>.+?))?$/.match(line.strip) # rubocop:disable Lint/MixedRegexpCaptureTypes
|
160
|
+
"RUN install -D \"$FSORG_ROOT/#{$~[:source]}\" #{($~[:destination] || $~[:source]).shellescape}" + (if $~[:permissions]
|
158
161
|
" -m #{$~[:permissions].shellescape}"
|
159
162
|
else
|
160
163
|
""
|
161
164
|
end)
|
162
165
|
else
|
163
|
-
|
166
|
+
line
|
164
167
|
end
|
165
168
|
end
|
166
169
|
|
167
170
|
@document = output.join "\n"
|
168
171
|
end
|
169
172
|
|
170
|
-
def process_for
|
173
|
+
def process_for # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
171
174
|
output = []
|
172
175
|
inside_for_directive = false
|
173
176
|
body = []
|
@@ -177,7 +180,7 @@ class Fsorg
|
|
177
180
|
@current_line = index + 1
|
178
181
|
if inside_for_directive
|
179
182
|
current_depth += depth_change line.strip
|
180
|
-
if current_depth
|
183
|
+
if current_depth.zero?
|
181
184
|
inside_for_directive = false
|
182
185
|
output += repeat_for_each(args[:iteratee], args[:iterator], body)
|
183
186
|
else
|
@@ -195,14 +198,11 @@ class Fsorg
|
|
195
198
|
end
|
196
199
|
end
|
197
200
|
|
198
|
-
def repeat_for_each(iteratee, iterator, directives)
|
201
|
+
def repeat_for_each(iteratee, iterator, directives) # rubocop:disable Metrics/AbcSize
|
199
202
|
output = []
|
200
|
-
unless data[iterator.to_sym]
|
201
|
-
|
202
|
-
|
203
|
-
unless data[iterator.to_sym].is_a? Array
|
204
|
-
raise "[#{@document_path}:#{@current_line}]".colorize :red + "Cannot iterate over '#{iterator}', which is of type #{data[iterator.to_sym].class}."
|
205
|
-
end
|
203
|
+
raise "[#{@document_path}:#{@current_line}]".colorize :red + "Variable '#{iterator}' not found (iterators cannot be asked for interactively). Available variables at this point: #{data.keys.join(", ")}." unless data[iterator.to_sym]
|
204
|
+
raise "[#{@document_path}:#{@current_line}]".colorize :red + "Cannot iterate over '#{iterator}', which is of type #{data[iterator.to_sym].class}." unless data[iterator.to_sym].is_a? Array
|
205
|
+
|
206
206
|
data[iterator.to_sym].each do |item|
|
207
207
|
output += directives.map do |directive|
|
208
208
|
directive.gsub "{{#{iteratee}}}", item.to_s
|
@@ -211,7 +211,7 @@ class Fsorg
|
|
211
211
|
output
|
212
212
|
end
|
213
213
|
|
214
|
-
def process_if
|
214
|
+
def process_if # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
215
215
|
output = []
|
216
216
|
inside_if = false
|
217
217
|
body_if = []
|
@@ -220,11 +220,11 @@ class Fsorg
|
|
220
220
|
current_condition = nil
|
221
221
|
current_depth = 0
|
222
222
|
|
223
|
-
@document.lines(chomp: true).each_with_index do |line, index|
|
223
|
+
@document.lines(chomp: true).each_with_index do |line, index| # rubocop:disable Metrics/BlockLength
|
224
224
|
@current_line = index + 1
|
225
225
|
if inside_if
|
226
226
|
current_depth += depth_change line.strip
|
227
|
-
if current_depth
|
227
|
+
if current_depth.zero?
|
228
228
|
inside_if = false
|
229
229
|
inside_else = false
|
230
230
|
output += body_if if evaluates_to_true? current_condition
|
@@ -233,7 +233,7 @@ class Fsorg
|
|
233
233
|
end
|
234
234
|
elsif inside_else
|
235
235
|
current_depth += depth_change line.strip
|
236
|
-
if current_depth
|
236
|
+
if current_depth.zero?
|
237
237
|
inside_else = false
|
238
238
|
inside_if = false
|
239
239
|
output += body_else if evaluates_to_false? current_condition
|
@@ -246,9 +246,8 @@ class Fsorg
|
|
246
246
|
current_depth = 1
|
247
247
|
inside_if = true
|
248
248
|
elsif /^(\}\s*)?ELSE\s*\{$/.match(line.strip)
|
249
|
-
if current_condition.nil?
|
250
|
-
|
251
|
-
end
|
249
|
+
raise "[#{@document_path}:#{@current_line}] Cannot use ELSE without IF." if current_condition.nil?
|
250
|
+
|
252
251
|
inside_else = true
|
253
252
|
current_depth = 1
|
254
253
|
else
|
@@ -263,29 +262,25 @@ class Fsorg
|
|
263
262
|
end
|
264
263
|
|
265
264
|
def evaluates_to_true?(condition)
|
266
|
-
unless @data.include? condition.to_sym
|
267
|
-
raise "[#{@document_path}:#{@current_line}] Variable '#{condition}' not found. Available variables at this point: #{data.keys.join(", ")}."
|
268
|
-
end
|
265
|
+
raise "[#{@document_path}:#{@current_line}] Variable '#{condition}' not found. Available variables at this point: #{data.keys.join(", ")}." unless @data.include? condition.to_sym
|
269
266
|
|
270
267
|
@data[condition.to_sym]
|
271
268
|
end
|
272
269
|
|
273
270
|
def ask_missing_variables
|
274
|
-
@document.scan
|
275
|
-
unless @data.include? variable[0].to_sym
|
276
|
-
@data[variable[0].to_sym] = :ask
|
277
|
-
end
|
271
|
+
@document.scan(/\{\{(?<variable>[^}]+?)\}\}/) do |variable|
|
272
|
+
@data[variable[0].to_sym] = :ask unless @data.include? variable[0].to_sym
|
278
273
|
end
|
279
274
|
|
280
275
|
@data.each do |key, value|
|
281
276
|
if value == :ask
|
282
277
|
print "#{key}? "
|
283
|
-
@data[key] = YAML.
|
278
|
+
@data[key] = YAML.safe_load $stdin.gets.chomp
|
284
279
|
end
|
285
280
|
end
|
286
281
|
end
|
287
282
|
|
288
|
-
def process_root
|
283
|
+
def process_root # rubocop:disable Metrics/MethodLength
|
289
284
|
@document = @document.lines(chomp: true).map.with_index do |line, index|
|
290
285
|
@current_line = index + 1
|
291
286
|
if /^ROOT\s+(?<root>.+?)$/.match line.strip
|
@@ -317,18 +312,16 @@ class Fsorg
|
|
317
312
|
@data[:HERE]
|
318
313
|
end
|
319
314
|
|
320
|
-
def walk(dry_run, quiet, verbose)
|
315
|
+
def walk(dry_run, quiet, verbose) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
321
316
|
current_path = [@root_directory]
|
322
317
|
@data[:HERE] = @root_directory
|
323
318
|
file_to_write = {}
|
324
319
|
inside_write_directive = -> { !file_to_write.keys.empty? }
|
325
|
-
current_path_as_pathname = -> { current_path.reduce(Pathname.new
|
320
|
+
current_path_as_pathname = -> { current_path.reduce(Pathname.new("")) { |path, fragment| path.join fragment } }
|
326
321
|
|
327
|
-
if verbose
|
328
|
-
puts "Data is #{@data.except :HERE}".light_black
|
329
|
-
end
|
322
|
+
puts "Data is #{@data.except :HERE}".light_black if verbose
|
330
323
|
|
331
|
-
@document.lines(chomp: true).each_with_index do |line, index|
|
324
|
+
@document.lines(chomp: true).each_with_index do |line, index| # rubocop:disable Metrics/BlockLength
|
332
325
|
@current_line = index + 1
|
333
326
|
@current_depth = current_path.length - 1
|
334
327
|
|
@@ -336,29 +329,25 @@ class Fsorg
|
|
336
329
|
line = line.gsub "{{#{key}}}", value.to_s
|
337
330
|
end
|
338
331
|
|
339
|
-
if inside_write_directive.
|
332
|
+
if inside_write_directive.call
|
340
333
|
if line.strip == "}"
|
341
334
|
do_write file_to_write, dry_run, quiet
|
342
335
|
file_to_write = {}
|
343
336
|
else
|
344
|
-
file_to_write[:content] += line
|
337
|
+
file_to_write[:content] += "#{line}\n"
|
345
338
|
end
|
346
|
-
elsif /^WRITE(\s+INTO)?\s+(?<destination>.+?)(?:\s+MODE\s+(?<permissions>.+?))?\s*\{$/.match line.strip
|
339
|
+
elsif /^WRITE(\s+INTO)?\s+(?<destination>.+?)(?:\s+MODE\s+(?<permissions>.+?))?\s*\{$/.match line.strip # rubocop:disable Lint/MixedRegexpCaptureTypes
|
347
340
|
file_to_write = $~.named_captures.transform_keys(&:to_sym)
|
348
341
|
file_to_write[:content] = ""
|
349
342
|
elsif /^(?<leaf>.+?)\s+\{/ =~ line.strip
|
350
343
|
current_path << leaf
|
351
|
-
@data[:HERE] = current_path_as_pathname.
|
352
|
-
if verbose
|
353
|
-
puts "currenly #{current_path.map { |fragment| fragment.to_s }.join " -> "}".light_black
|
354
|
-
end
|
344
|
+
@data[:HERE] = current_path_as_pathname.call
|
345
|
+
puts "currenly #{current_path.map(&:to_s).join " -> "}".light_black if verbose
|
355
346
|
do_mkpath current_location, dry_run, quiet
|
356
347
|
elsif line.strip == "}"
|
357
348
|
current_path.pop
|
358
|
-
@data[:HERE] = current_path_as_pathname.
|
359
|
-
if verbose
|
360
|
-
puts "currenly #{current_path.map { |fragment| fragment.to_s }.join " -> "}".light_black
|
361
|
-
end
|
349
|
+
@data[:HERE] = current_path_as_pathname.call
|
350
|
+
puts "currenly #{current_path.map(&:to_s).join " -> "}".light_black if verbose
|
362
351
|
elsif /^RUN\s+(?<command>.+?)$/ =~ line.strip
|
363
352
|
environment = {
|
364
353
|
"FSORG_ROOT" => @root_directory.to_s,
|
@@ -372,58 +361,54 @@ class Fsorg
|
|
372
361
|
end
|
373
362
|
|
374
363
|
def do_mkpath(path, dry_run, quiet)
|
375
|
-
unless quiet
|
376
|
-
|
377
|
-
end
|
378
|
-
unless dry_run
|
379
|
-
path.mkpath
|
380
|
-
end
|
364
|
+
puts "#{" " * @current_depth}+ ".cyan.bold + path.relative_path_from(@root_directory).to_s unless quiet
|
365
|
+
path.mkpath unless dry_run
|
381
366
|
end
|
382
367
|
|
383
|
-
def do_write(future_file, dry_run, quiet)
|
368
|
+
def do_write(future_file, dry_run, quiet) # rubocop:disable Metrics/AbcSize
|
384
369
|
indentation = " " * @current_depth
|
385
370
|
dest = from_relative_to_root(current_location) / future_file[:destination]
|
386
|
-
unless dest.parent.relative_path_from(@root_directory).to_s == "."
|
387
|
-
do_mkpath dest.parent, dry_run, quiet
|
388
|
-
end
|
371
|
+
do_mkpath dest.parent, dry_run, quiet unless dest.parent.relative_path_from(@root_directory).to_s == "."
|
389
372
|
|
390
373
|
unless quiet
|
391
374
|
puts "#{indentation}> ".cyan.bold + dest.relative_path_from(@root_directory).to_s + (future_file[:permissions] ? " mode #{future_file[:permissions]}".yellow : "")
|
392
375
|
end
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
376
|
+
|
377
|
+
return if dry_run
|
378
|
+
|
379
|
+
dest.write ensure_final_newline(dedent(replace_data(future_file[:content])))
|
380
|
+
# Not using dest.chmod as the syntax for permissions is more than just integers,
|
381
|
+
# and matches in fact the exact syntax of chmod's argument, per the manpage, chmod(1) (line "Each MODE is of the form…")
|
382
|
+
`chmod #{future_file[:permissions]} #{dest}` if future_file[:permissions]
|
399
383
|
end
|
400
384
|
|
401
385
|
def replace_data(content)
|
402
|
-
content.gsub
|
386
|
+
content.gsub(/\{\{(?<variable>[^}]+?)\}\}/) do |interpolation|
|
403
387
|
variable = interpolation[2..-3]
|
404
388
|
@data[variable.to_sym]
|
405
389
|
end
|
406
390
|
end
|
407
391
|
|
408
|
-
def do_run(command, inside, environment, dry_run, quiet, verbose)
|
392
|
+
def do_run(command, inside, environment, dry_run, quiet, verbose) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/ParameterLists
|
409
393
|
indentation = " " * @current_depth
|
410
394
|
unless quiet
|
411
395
|
puts "#{indentation}$ ".cyan.bold + command + (verbose ? " at #{inside.relative_path_from(@root_directory)}".light_blue + " with ".light_black + (format_environment_hash environment) : "")
|
412
396
|
end
|
413
|
-
unless dry_run
|
414
|
-
stdout, stdout_w = IO.pipe
|
415
|
-
stderr, stderr_w = IO.pipe
|
416
397
|
|
417
|
-
|
418
|
-
stdout_w.close
|
419
|
-
stderr_w.close
|
398
|
+
return if dry_run
|
420
399
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
400
|
+
stdout, stdout_w = IO.pipe
|
401
|
+
stderr, stderr_w = IO.pipe
|
402
|
+
|
403
|
+
system environment, command, { chdir: inside.to_s, out: stdout_w, err: stderr_w }
|
404
|
+
stdout_w.close
|
405
|
+
stderr_w.close
|
406
|
+
|
407
|
+
stdout.read.each_line(chomp: true) do |line|
|
408
|
+
puts " #{indentation}#{line}"
|
409
|
+
end
|
410
|
+
stderr.read.each_line(chomp: true) do |line|
|
411
|
+
puts " #{indentation}#{line.red}"
|
427
412
|
end
|
428
413
|
end
|
429
414
|
|
@@ -434,28 +419,32 @@ class Fsorg
|
|
434
419
|
end
|
435
420
|
end
|
436
421
|
|
437
|
-
def
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
422
|
+
def dedent(text) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
423
|
+
lines = text.split "\n"
|
424
|
+
return text if lines.empty?
|
425
|
+
|
426
|
+
indents = lines.map do |line|
|
427
|
+
if line =~ /\S/
|
428
|
+
line.start_with?(" ") ? line.match(/^ +/).offset(0)[1] : 0
|
444
429
|
end
|
445
|
-
|
446
|
-
|
447
|
-
|
430
|
+
end
|
431
|
+
indents.compact!
|
432
|
+
if indents.empty?
|
433
|
+
# No lines had any non-whitespace characters.
|
434
|
+
return ([""] * lines.size).join "\n"
|
435
|
+
end
|
436
|
+
|
437
|
+
min_indent = indents.min
|
438
|
+
return text if min_indent.zero?
|
448
439
|
|
449
|
-
|
450
|
-
line.sub pattern, ""
|
451
|
-
end.join "\n"
|
440
|
+
lines.map { |line| line =~ /\S/ ? line.gsub(/^ {#{min_indent}}/, "") : line }.join "\n"
|
452
441
|
end
|
453
442
|
|
454
443
|
def ensure_final_newline(text)
|
455
|
-
|
456
|
-
text + "\n"
|
457
|
-
else
|
444
|
+
if text.end_with? "\n"
|
458
445
|
text
|
446
|
+
else
|
447
|
+
"#{text}\n"
|
459
448
|
end
|
460
449
|
end
|
461
450
|
|
@@ -465,7 +454,7 @@ def capture_output
|
|
465
454
|
$stdout = StringIO.new
|
466
455
|
$stderr = StringIO.new
|
467
456
|
yield
|
468
|
-
|
457
|
+
[$stdout.string, $stderr.string]
|
469
458
|
ensure
|
470
459
|
$stdout = old_stdout
|
471
460
|
$stderr = old_stderr
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fsorg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ewen Le Bihan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: docopt
|
@@ -94,7 +94,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
94
|
- !ruby/object:Gem::Version
|
95
95
|
version: '0'
|
96
96
|
requirements: []
|
97
|
-
rubygems_version: 3.3.
|
97
|
+
rubygems_version: 3.3.25
|
98
98
|
signing_key:
|
99
99
|
specification_version: 4
|
100
100
|
summary: Create directories from a file that describes them
|