ttl2html 3.1.1 → 3.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d181672a5b0aa4cd6f7f1599fb20b6102c5232b3f774069038023bfc65d05a0
4
- data.tar.gz: 313970beef022f77e7f4d5bac9da410adcc6203a6dd7d073d6ab2b49a516ebae
3
+ metadata.gz: ba8e10380abeb01499c02f9d6f2eedc7ad6e36d876fbb71e457529c824fe1a11
4
+ data.tar.gz: 1a774fb36a6b550f1d98106dd51439455c3f4ba5ccb34e0d89cbf905a9e7f481
5
5
  SHA512:
6
- metadata.gz: fdd4152bfc2ab82d97bed51c63dd8814e89a40b31d2e84ca2a64406dcfd4be373a393ccf8b2ebd477adb8e1f4df844f355872537232e7ec9357063ff2c123760
7
- data.tar.gz: 7821d98735d0fadf33671017573accb9d765e2e2bd571c9fb66435251a056cedab6e05ec20b760c940cc2f1daa16bbdde11496cadcf4045fa9016344904e36db
6
+ metadata.gz: ce92faf8d3d04972844feb4e2f779bb672aadedfbe489b55d50952d34776e8d6b66798932640eeeaf15c385a0b09a7cedb98b5436b54a57dc740315d459a97cd
7
+ data.tar.gz: 5bdc9429e03ed993030fb1030f9c1b52213feb726b9f49d572365f11f68b309bb9766f4978d085e7497bb50661231cb7d421efa6b18c090c31b4a7f7eefd9f2b
@@ -24,8 +24,7 @@ module TTL2HTML
24
24
  def output_to(file, param = {})
25
25
  param = @param.merge(param)
26
26
  param[:output_file] = file
27
- dir = File.dirname(file)
28
- FileUtils.mkdir_p(dir) if not File.exist?(dir)
27
+ FileUtils.mkdir_p(File.dirname(file))
29
28
  File.open(file, "w") do |io|
30
29
  io.print to_html(param)
31
30
  end
@@ -41,7 +40,9 @@ module TTL2HTML
41
40
  tmpl = File.open(template) { |io| io.read }
42
41
  erb = ERB.new(tmpl, trim_mode: "-")
43
42
  erb.filename = template
44
- erb.result(binding)
43
+ I18n.with_locale(@param[:locale] || I18n.default_locale) do
44
+ erb.result(binding)
45
+ end
45
46
  end
46
47
 
47
48
  def find_template_path(fname)
@@ -51,14 +52,15 @@ module TTL2HTML
51
52
  end
52
53
  @template_path.each do |dir|
53
54
  file = File.join(dir, fname)
54
- return file if File.exist? file
55
+ return file if File.file? file
55
56
  end
56
- return nil
57
+ nil
57
58
  end
58
59
 
59
- def expand_shape(data, uri, prefixes = {})
60
+ def expand_shape(data, uri, prefixes = {}, depth = 0)
60
61
  return nil if not data[uri]
61
62
  return nil if not data[uri]["http://www.w3.org/ns/shacl#property"]
63
+ return nil if depth > 10
62
64
  prefix_used = {}
63
65
  result = data[uri]["http://www.w3.org/ns/shacl#property"].sort_by do |e|
64
66
  e["http://www.w3.org/ns/shacl#order"]
@@ -87,14 +89,14 @@ module TTL2HTML
87
89
  node_or = data[data[node]["http://www.w3.org/ns/shacl#or"].first]
88
90
  node_mode = :or
89
91
  nodes = []
90
- nodes << expand_shape(data, node_or["http://www.w3.org/1999/02/22-rdf-syntax-ns#first"].first, prefixes)
92
+ nodes << expand_shape(data, node_or["http://www.w3.org/1999/02/22-rdf-syntax-ns#first"].first, prefixes, depth + 1)
91
93
  rest = node_or["http://www.w3.org/1999/02/22-rdf-syntax-ns#rest"].first
92
94
  while data[rest] do
93
- nodes << expand_shape(data, data[rest]["http://www.w3.org/1999/02/22-rdf-syntax-ns#first"].first, prefixes)
95
+ nodes << expand_shape(data, data[rest]["http://www.w3.org/1999/02/22-rdf-syntax-ns#first"].first, prefixes, depth + 1)
94
96
  rest = data[rest]["http://www.w3.org/1999/02/22-rdf-syntax-ns#rest"].first
95
97
  end
96
98
  else
97
- nodes = expand_shape(data, node, prefixes)
99
+ nodes = expand_shape(data, node, prefixes, depth + 1)
98
100
  end
99
101
  #p nodes
100
102
  end
@@ -126,8 +128,7 @@ module TTL2HTML
126
128
  }
127
129
  end
128
130
  template = "shape-table.html.erb"
129
- tmpl = Template.new(template)
130
- tmpl.to_html_raw(template, { properties: result, prefix: prefix_used })
131
+ to_html_raw(template, { properties: result, prefix: prefix_used })
131
132
  end
132
133
 
133
134
  # helper method:
@@ -138,9 +139,15 @@ module TTL2HTML
138
139
  if dest_uri.absolute?
139
140
  path = dest
140
141
  else
141
- src = Pathname.new(src).relative_path_from(Pathname.new(@param[:output_dir])) if @param[:output_dir]
142
+ dest.sub!(/^\/+/, "")
143
+ #p [:relative_path, src, dest]
144
+ if @param[:output_dir]
145
+ src = Pathname.new(src).relative_path_from(Pathname.new(@param[:output_dir]))
146
+ else
147
+ src = Pathname.new(File.expand_path(src)).relative_path_from(Pathname.new(File.expand_path(".")))
148
+ end
142
149
  path = Pathname(dest).relative_path_from(Pathname(File.dirname src))
143
- if @param[:output_dir] and File.directory?(Pathname.new(@param[:output_dir]) + path)
150
+ if @param[:output_dir] and File.directory?(File.join(@param[:output_dir], path))
144
151
  path = path.to_s + "/"
145
152
  elsif File.directory?(path)
146
153
  path = path.to_s + "/"
@@ -222,6 +229,7 @@ module TTL2HTML
222
229
  type = param[:type] || {}
223
230
  data = param[:data] || {}
224
231
  if /\Ahttps?:\/\// =~ object.to_s
232
+ #p [:format_object_uri, param[:output_file], object]
225
233
  rel_path = relative_path_uri(param[:output_file], object)
226
234
  if param[:data_global][object]
227
235
  result = "<a href=\"#{rel_path}\">#{get_title(param[:data_global][object]) or ERB::Util.html_escape(object)}</a>"
@@ -325,6 +333,7 @@ module TTL2HTML
325
333
  "http://www.w3.org/2000/01/rdf-schema#label",
326
334
  "http://purl.org/dc/terms/title",
327
335
  "http://purl.org/dc/elements/1.1/title",
336
+ "https://schema.org/name",
328
337
  "http://schema.org/name",
329
338
  "http://www.w3.org/2004/02/skos/core#prefLabel"
330
339
  ].each do |prop|
@@ -343,6 +352,7 @@ module TTL2HTML
343
352
  results = []
344
353
  [
345
354
  "http://purl.org/linked-data/cube#order",
355
+ "https://schema.org/position",
346
356
  "http://schema.org/position",
347
357
  ].each do |order_elem|
348
358
  if resource and resource[order_elem]
data/lib/ttl2html/util.rb CHANGED
@@ -42,6 +42,23 @@ module TTL2HTML
42
42
  path << suffix
43
43
  #p [uri, path]
44
44
  path
45
- end
45
+ end
46
+ def safe_output_path(file)
47
+ base = if @config[:output_dir]
48
+ File.expand_path(@config[:output_dir])
49
+ else
50
+ File.expand_path(".")
51
+ end
52
+ target = File.expand_path(file)
53
+ unless target == base || target.start_with?(base + File::SEPARATOR)
54
+ if @config[:output_dir]
55
+ warn "Attempting to write outside of output_dir: #{file}"
56
+ else
57
+ warn "Attempting to write outside of current directory: #{file}"
58
+ end
59
+ return nil
60
+ end
61
+ target
62
+ end
46
63
  end
47
- end
64
+ end
@@ -1 +1 @@
1
- TTL2HTML::VERSION = "3.1.1"
1
+ TTL2HTML::VERSION = "3.2.1"
data/lib/ttl2html.rb CHANGED
@@ -72,51 +72,63 @@ module TTL2HTML
72
72
  $stderr.puts "#{count} triples. #{subjects.size} subjects."
73
73
  @data
74
74
  end
75
+
75
76
  QB_ORDER_URI = "http://purl.org/linked-data/cube#order"
76
77
  SCHEMA_POSITION_URI = "http://schema.org/position"
78
+ SCHEMA_POSITION_URI_S = "https://schema.org/position"
77
79
  SHACL_ORDER_URI = "http://www.w3.org/ns/shacl#order"
78
- def sort_key_for_resource(resource, depth = 1)
80
+ def sort_key_for_resource(resource)
79
81
  qb_order = Float::INFINITY
80
82
  schema_position = Float::INFINITY
81
83
  shacl_order = Float::INFINITY
82
84
  if @data[resource.to_s]
83
85
  qb_order = @data[resource.to_s][QB_ORDER_URI].first.to_i if @data[resource.to_s][QB_ORDER_URI]
84
86
  schema_position = @data[resource.to_s][SCHEMA_POSITION_URI].first.to_i if @data[resource.to_s][SCHEMA_POSITION_URI]
87
+ schema_position = @data[resource.to_s][SCHEMA_POSITION_URI_S].first.to_i if @data[resource.to_s][SCHEMA_POSITION_URI_S]
85
88
  shacl_order = @data[resource.to_s][SHACL_ORDER_URI].first.to_i if @data[resource.to_s][SHACL_ORDER_URI]
86
89
  end
87
- if resource.to_s =~ /^_:/ and depth < 5
88
- [ schema_position, qb_order, shacl_order,
89
- format_turtle(resource, depth + 1, true)
90
- ]
90
+ if resource.to_s =~ /^_:/ and @data[resource.to_s]
91
+ resource_str = "{" + @data[resource.to_s].sort_by do |p, o|
92
+ [p, o]
93
+ end.join("\t") + "}"
94
+ [ schema_position, qb_order, shacl_order, resource_str ]
91
95
  else
92
- [ schema_position, qb_order, shacl_order, resource.to_s]
96
+ [ schema_position, qb_order, shacl_order, resource.to_s ]
97
+ end
98
+ end
99
+ def format_uri(uri, writer = RDF::Turtle::Writer.new(nil, prefixes: @prefix))
100
+ result = writer.format_uri(RDF::URI(uri))
101
+ if result =~ /^#{RDF::Turtle::Terminals::PN_PREFIX}?:/
102
+ @used_prefixes << result.split(":").first
93
103
  end
104
+ result
94
105
  end
95
106
  def format_turtle(subject, depth = 1, force = false)
96
- turtle = RDF::Turtle::Writer.new
107
+ turtle_writer = RDF::Turtle::Writer.new(nil, prefixes: @prefix)
97
108
  result = ""
98
109
  #p [:format_turtle, subject, depth, force]
99
110
  return result if !force && @cache[:output_turtle_files].include?(subject)
100
111
  if subject =~ /^_:/
101
112
  result << "[\n#{" "*depth}"
102
113
  else
103
- result << "<#{subject}>\n#{" "*depth}"
114
+ result << format_uri(subject) << "\n#{" "*depth}"
104
115
  end
105
116
  result << @data[subject.to_s].keys.sort.map do |predicate|
106
- str = "<#{predicate}> "
117
+ str = format_uri(predicate, turtle_writer) << " "
107
118
  #p [subject, predicate, @data[subject.to_s][predicate]]
108
119
  str << @data[subject.to_s][predicate].sort_by do |object|
109
120
  #p [subject, predicate, object, depth]
110
- sort_key_for_resource(object, depth)
121
+ sort_key_for_resource(object)
111
122
  end.map do |object|
112
123
  if /^_:/ =~ object.to_s # blank node:
113
124
  format_turtle(object, depth + 1, force)
114
125
  elsif RDF::URI::IRI =~ object.to_s
115
- turtle.format_uri(RDF::URI.new object)
116
- elsif object.respond_to?(:first) and object.first.kind_of?(Symbol)
117
- turtle.format_literal(RDF::Literal.new(object[1], language: object[0]))
126
+ format_uri(object, turtle_writer)
118
127
  else
119
- turtle.format_literal(object)
128
+ if object.respond_to?(:datatype) and object.datatype?
129
+ datatype = format_uri(object.datatype) # to add @used_prefixes
130
+ end
131
+ turtle_writer.format_literal(object)
120
132
  end
121
133
  end.join(", ")
122
134
  str
@@ -138,27 +150,37 @@ module TTL2HTML
138
150
  end.join
139
151
  end
140
152
  def collect_inverse_triples(object, triples = Set.new, visited = Set.new)
141
- return triples if object.to_s.start_with?("_:")
142
- return triples unless object.to_s.start_with?(@config[:base_uri].to_s)
143
- return triples unless @data_inverse.key?(object.to_s)
144
153
  return triples if visited.include?(object.to_s)
145
154
  visited << object.to_s
146
- @data_inverse[object.to_s].each do |predicate, subjects|
147
- subjects.each do |subject|
148
- triples << [subject.to_s, predicate.to_s, object.to_s]
149
- collect_inverse_triples_for_bnode(subject.to_s, triples, visited) if subject.to_s.start_with?("_:")
155
+ if @data_inverse.key?(object.to_s)
156
+ @data_inverse[object.to_s].each do |predicate, subjects|
157
+ subjects.each do |subject|
158
+ triples << [subject.to_s, predicate.to_s, object.to_s]
159
+ collect_inverse_triples_for_bnode(subject.to_s, triples, visited) if subject.to_s.start_with?("_:")
160
+ end
150
161
  end
151
162
  end
152
163
  triples
153
164
  end
154
165
  def collect_inverse_triples_for_bnode(node, triples, visited)
155
- return triples unless @data_inverse.key?(node)
156
166
  return triples if visited.include?(node)
157
167
  visited << node
158
- @data_inverse[node].each do |predicate, subjects|
159
- subjects.each do |subject|
168
+ if @data.key?(node)
169
+ @data[node].each do |predicate, objects|
170
+ objects.each do |object|
171
+ triples << [node, predicate.to_s, object]
172
+ if object.to_s.start_with?("_:")
173
+ collect_inverse_triples_for_bnode(object.to_s, triples, visited)
174
+ end
175
+ end
176
+ end
177
+ end
178
+ if @data_inverse.key?(node)
179
+ @data_inverse[node].each do |predicate, subjects|
180
+ subjects.each do |subject|
160
181
  triples << [subject.to_s, predicate.to_s, node]
161
182
  collect_inverse_triples_for_bnode(subject.to_s, triples, visited) if subject.to_s.start_with?("_:")
183
+ end
162
184
  end
163
185
  end
164
186
  triples
@@ -183,13 +205,15 @@ module TTL2HTML
183
205
  def find_inverse_roots(by_subject)
184
206
  all_subjects = by_subject.keys
185
207
  all_objects = by_subject.values.flat_map { |preds| preds.values.flatten }.uniq
186
- all_subjects.reject do |subject|
187
- subject.start_with?("_:") || all_objects.include?(subject)
188
- end.sort
208
+ roots = all_subjects.reject do |subject|
209
+ all_objects.include?(subject)
210
+ end
211
+ roots.sort_by { |subject| sort_key_for_resource(subject) }
189
212
  end
190
213
  def format_inverse_subject(subject, by_subject, ref_count, visited, depth = 1)
214
+ turtle_writer = RDF::Turtle::Writer.new(nil, prefixes: @prefix)
191
215
  props = by_subject[subject]
192
- return format_node(subject) if props.nil? || props.empty?
216
+ return format_node(subject, turtle_writer) if props.nil? || props.empty?
193
217
  indent = " " * (depth - 1)
194
218
  inner = " " * depth
195
219
  if subject.start_with?("_:")
@@ -199,18 +223,18 @@ module TTL2HTML
199
223
  head = "[\n#{inner}"
200
224
  tail = "\n#{indent}]"
201
225
  else
202
- head = "<#{subject}> "
226
+ head = format_uri(subject, turtle_writer) << " "
203
227
  tail = ""
204
228
  end
205
229
  body = props.keys.sort.map do |predicate|
206
230
  objects = props[predicate].sort.map do |object|
207
- format_inverse_object(object, by_subject, ref_count, visited, depth + 1)
231
+ format_inverse_object(object, by_subject, ref_count, visited, depth + 1, turtle_writer)
208
232
  end.join(", ")
209
- "<#{predicate}> #{objects}"
233
+ format_uri(predicate, turtle_writer) << " " << objects
210
234
  end.join(";\n#{inner}")
211
235
  head + body + tail
212
236
  end
213
- def format_inverse_object(object, by_subject, ref_count, visited, depth = 1)
237
+ def format_inverse_object(object, by_subject, ref_count, visited, depth = 1, turtle_writer = RDF::Turtle::Writer.new(nil, prefixes: @prefix))
214
238
  if object.to_s.start_with?("_:") && by_subject.key?(object.to_s)
215
239
  if ref_count[object.to_s] <= 1
216
240
  format_inverse_subject(object.to_s, by_subject, ref_count, visited, depth)
@@ -218,19 +242,16 @@ module TTL2HTML
218
242
  object.to_s
219
243
  end
220
244
  else
221
- format_node(object)
245
+ format_node(object, turtle_writer)
222
246
  end
223
247
  end
224
- def format_node(value)
225
- turtle = RDF::Turtle::Writer.new
248
+ def format_node(value, writer = RDF::Turtle::Writer.new(nil, prefixes: @prefix))
226
249
  if value.to_s.start_with?("_:")
227
250
  value.to_s
228
251
  elsif RDF::URI::IRI =~ value.to_s
229
- "<#{value}>"
230
- elsif value.respond_to?(:first) && value.first.kind_of?(Symbol)
231
- turtle.format_literal(RDF::Literal.new(value[1], language: value[0]))
252
+ format_uri(value, writer)
232
253
  else
233
- turtle.format_literal(value)
254
+ writer.format_literal(value)
234
255
  end
235
256
  end
236
257
 
@@ -243,8 +264,8 @@ module TTL2HTML
243
264
  data = @data.keys.sort_by do|uri|
244
265
  local_path = uri_mapping_to_path(uri, @config, ".html")
245
266
  #p [ local_path.size, local_path.count("/"), local_path ]
246
- [ local_path.size, local_path.count("/"), local_path ]
247
- end.reverse
267
+ [ -(local_path.count("/")), -(local_path.size), local_path ]
268
+ end
248
269
  Parallel.each(data, progress: progressbar_options) do |uri|
249
270
  next if not uri.start_with? @config[:base_uri]
250
271
  yield uri, @data[uri]
@@ -275,7 +296,7 @@ module TTL2HTML
275
296
  end
276
297
  end
277
298
  @config[:orders_with_class] = shapes2orders(shapes)
278
- Dir.mkdir @config[:output_dir] if @config[:output_dir] and not File.exist? @config[:output_dir]
299
+ FileUtils.mkdir_p(@config[:output_dir]) if @config[:output_dir]
279
300
  template = Template.new("default.html.erb", @config)
280
301
  each_data(:output_html_files) do |uri, v|
281
302
  param = @config.dup
@@ -297,11 +318,14 @@ module TTL2HTML
297
318
  if @config[:output_dir]
298
319
  file = File.join(@config[:output_dir], file)
299
320
  end
300
- if template.find_template_path("_default.html.erb")
301
- param[:additional_content] = template.to_html_raw("_default.html.erb", param)
321
+ file = safe_output_path(file)
322
+ if file
323
+ if template.find_template_path("_default.html.erb")
324
+ param[:additional_content] = template.to_html_raw("_default.html.erb", param)
325
+ end
326
+ param[:about_file] = about_file if about_required
327
+ template.output_to(file, param)
302
328
  end
303
- param[:about_file] = about_file if about_required
304
- template.output_to(file, param)
305
329
  end
306
330
  index_html = "index.html"
307
331
  index_html = File.join(@config[:output_dir], "index.html") if @config[:output_dir]
@@ -343,7 +367,8 @@ module TTL2HTML
343
367
  param[:output_file] = index_html
344
368
  param[:index_list] = template.to_html_raw("index-list.html.erb", param)
345
369
  param[:about_file] = about_file if about_required
346
- template.output_to(index_html, param)
370
+ index_html = safe_output_path(index_html)
371
+ template.output_to(index_html, param) if index_html
347
372
  end
348
373
  end
349
374
  if about_required
@@ -388,7 +413,8 @@ module TTL2HTML
388
413
  order: orders,
389
414
  }
390
415
  end
391
- template.output_to(about_html, param)
416
+ about_html = safe_output_path(about_html)
417
+ template.output_to(about_html, param) if about_html
392
418
  end
393
419
  end
394
420
 
@@ -620,20 +646,29 @@ module TTL2HTML
620
646
  end
621
647
 
622
648
  def output_turtle_files
623
- Dir.mkdir @config[:output_dir] if @config[:output_dir] and not File.exist? @config[:output_dir]
649
+ FileUtils.mkdir_p(@config[:output_dir]) if @config[:output_dir]
624
650
  each_data(:output_turtle_files) do |uri, v|
625
651
  file = uri_mapping_to_path(uri, @config, ".ttl")
626
652
  if @config[:output_dir]
627
- Dir.mkdir @config[:output_dir] if not File.exist? @config[:output_dir]
628
653
  file = File.join(@config[:output_dir], file)
629
654
  end
655
+ file = safe_output_path(file)
656
+ next if not file
630
657
  dir = File.dirname(file)
631
658
  FileUtils.mkdir_p(dir) if not File.exist?(dir)
632
659
  @cache ||= {}
633
660
  @cache[:output_turtle_files] = Set.new
661
+ @used_prefixes = Set.new
634
662
  str = format_turtle(uri)
635
663
  str << format_turtle_inverse(uri)
636
- open(file, "w") do |io|
664
+ File.open(file, "w") do |io|
665
+ @used_prefixes.each do |prefix|
666
+ if prefix.empty?
667
+ io.puts "@prefix : <#{@prefix[nil]}>."
668
+ else
669
+ io.puts "@prefix #{prefix}: <#{@prefix[prefix.to_sym]}>."
670
+ end
671
+ end
637
672
  io.puts str.strip
638
673
  end
639
674
  end
@@ -653,30 +688,42 @@ module TTL2HTML
653
688
  end.each do |uri, v|
654
689
  html_file = uri_mapping_to_path(uri, @config, ".html")
655
690
  html_file = File.join(@config[:output_dir], html_file) if @config[:output_dir]
656
- dirs << File.dirname(html_file)
657
- File.unlink html_file if File.exist? html_file
691
+ html_file = safe_output_path(html_file)
692
+ if html_file and File.file? html_file
693
+ dirs << File.dirname(html_file)
694
+ File.unlink html_file
695
+ end
658
696
  ttl_file = uri_mapping_to_path(uri, @config, ".ttl")
659
697
  ttl_file = File.join(@config[:output_dir], ttl_file) if @config[:output_dir]
660
- File.unlink ttl_file if File.exist? ttl_file
661
- dir = uri.sub(@config[:base_uri], "")
662
- dir = File.join(@config[:output_dir], dir) if @config[:output_dir]
663
- dirs << dir
698
+ ttl_file = safe_output_path(ttl_file)
699
+ if ttl_file and File.file? ttl_file
700
+ dirs << File.dirname(ttl_file)
701
+ File.unlink ttl_file
702
+ end
664
703
  end
665
704
  index_html = "index.html"
666
705
  index_html = File.join(@config[:output_dir], "index.html") if @config[:output_dir]
667
- if @config[:top_class] and File.exist? index_html
706
+ index_html = safe_output_path(index_html)
707
+ if index_html and @config[:top_class] and File.file? index_html
668
708
  File.unlink index_html
669
709
  end
670
710
  about_html = (@config[:about_file] || "about.html")
671
711
  about_html = File.join(@config[:output_dir], about_html) if @config[:output_dir]
672
- File.unlink about_html if File.exist? about_html
712
+ about_html = safe_output_path(about_html)
713
+ if about_html and File.file? about_html
714
+ File.unlink about_html
715
+ end
673
716
 
674
717
  dirs = dirs.uniq.sort_by{|e| -(e.size) }
675
718
  #p dirs
676
719
  dirs.each do |dir|
677
- next if dir == "." # failsafe...
678
- next if dir == @config[:output_dir] # failsafe...
679
- FileUtils.remove_entry_secure(dir) if File.exist? dir
720
+ dir = safe_output_path(dir)
721
+ next unless dir
722
+ next if dir == File.expand_path(".") # failsafe...
723
+ next if @config[:output_dir] and dir == File.expand_path(@config[:output_dir]) # failsafe...
724
+ if dir and File.exist?(dir) and File.directory?(dir)
725
+ FileUtils.remove_entry_secure(dir)
726
+ end
680
727
  end
681
728
  end
682
729
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ttl2html
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.1
4
+ version: 3.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masao Takaku
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-04-29 00:00:00.000000000 Z
10
+ date: 2026-05-05 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: nokogiri