ttl2html 3.1.1 → 3.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d181672a5b0aa4cd6f7f1599fb20b6102c5232b3f774069038023bfc65d05a0
4
- data.tar.gz: 313970beef022f77e7f4d5bac9da410adcc6203a6dd7d073d6ab2b49a516ebae
3
+ metadata.gz: 179d7c92158302822e387bc70e386abc8ebc592c36b2ddf675f52471fda5a087
4
+ data.tar.gz: 3d8a36b2de920b07401a7360b11bdb501c0dfa41ffcae314746919d703d415da
5
5
  SHA512:
6
- metadata.gz: fdd4152bfc2ab82d97bed51c63dd8814e89a40b31d2e84ca2a64406dcfd4be373a393ccf8b2ebd477adb8e1f4df844f355872537232e7ec9357063ff2c123760
7
- data.tar.gz: 7821d98735d0fadf33671017573accb9d765e2e2bd571c9fb66435251a056cedab6e05ec20b760c940cc2f1daa16bbdde11496cadcf4045fa9016344904e36db
6
+ metadata.gz: fa1f3bf8f3e62ae2a73474b2469a86818af359f1ce67797f1f29f2738577b9edc12b9d2a91aada8c6d381404518815a33abb830186df2a722e488e80cb49848a
7
+ data.tar.gz: 1084e5c116dac281ec54a4ab79bedf13a545cb989ada95294d937deae6fdfd4663b52eb251927a4b1fea8f79012a46408235887b8ca7bcfeda026727d9bbe2ec
@@ -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.0"
data/lib/ttl2html.rb CHANGED
@@ -72,51 +72,60 @@ 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
+ turtle_writer.format_literal(object)
120
129
  end
121
130
  end.join(", ")
122
131
  str
@@ -138,27 +147,37 @@ module TTL2HTML
138
147
  end.join
139
148
  end
140
149
  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
150
  return triples if visited.include?(object.to_s)
145
151
  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?("_:")
152
+ if @data_inverse.key?(object.to_s)
153
+ @data_inverse[object.to_s].each do |predicate, subjects|
154
+ subjects.each do |subject|
155
+ triples << [subject.to_s, predicate.to_s, object.to_s]
156
+ collect_inverse_triples_for_bnode(subject.to_s, triples, visited) if subject.to_s.start_with?("_:")
157
+ end
150
158
  end
151
159
  end
152
160
  triples
153
161
  end
154
162
  def collect_inverse_triples_for_bnode(node, triples, visited)
155
- return triples unless @data_inverse.key?(node)
156
163
  return triples if visited.include?(node)
157
164
  visited << node
158
- @data_inverse[node].each do |predicate, subjects|
159
- subjects.each do |subject|
165
+ if @data.key?(node)
166
+ @data[node].each do |predicate, objects|
167
+ objects.each do |object|
168
+ triples << [node, predicate.to_s, object]
169
+ if object.to_s.start_with?("_:")
170
+ collect_inverse_triples_for_bnode(object.to_s, triples, visited)
171
+ end
172
+ end
173
+ end
174
+ end
175
+ if @data_inverse.key?(node)
176
+ @data_inverse[node].each do |predicate, subjects|
177
+ subjects.each do |subject|
160
178
  triples << [subject.to_s, predicate.to_s, node]
161
179
  collect_inverse_triples_for_bnode(subject.to_s, triples, visited) if subject.to_s.start_with?("_:")
180
+ end
162
181
  end
163
182
  end
164
183
  triples
@@ -183,13 +202,15 @@ module TTL2HTML
183
202
  def find_inverse_roots(by_subject)
184
203
  all_subjects = by_subject.keys
185
204
  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
205
+ roots = all_subjects.reject do |subject|
206
+ all_objects.include?(subject)
207
+ end
208
+ roots.sort_by { |subject| sort_key_for_resource(subject) }
189
209
  end
190
210
  def format_inverse_subject(subject, by_subject, ref_count, visited, depth = 1)
211
+ turtle_writer = RDF::Turtle::Writer.new(nil, prefixes: @prefix)
191
212
  props = by_subject[subject]
192
- return format_node(subject) if props.nil? || props.empty?
213
+ return format_node(subject, turtle_writer) if props.nil? || props.empty?
193
214
  indent = " " * (depth - 1)
194
215
  inner = " " * depth
195
216
  if subject.start_with?("_:")
@@ -199,18 +220,18 @@ module TTL2HTML
199
220
  head = "[\n#{inner}"
200
221
  tail = "\n#{indent}]"
201
222
  else
202
- head = "<#{subject}> "
223
+ head = format_uri(subject, turtle_writer) << " "
203
224
  tail = ""
204
225
  end
205
226
  body = props.keys.sort.map do |predicate|
206
227
  objects = props[predicate].sort.map do |object|
207
- format_inverse_object(object, by_subject, ref_count, visited, depth + 1)
228
+ format_inverse_object(object, by_subject, ref_count, visited, depth + 1, turtle_writer)
208
229
  end.join(", ")
209
- "<#{predicate}> #{objects}"
230
+ format_uri(predicate, turtle_writer) << " " << objects
210
231
  end.join(";\n#{inner}")
211
232
  head + body + tail
212
233
  end
213
- def format_inverse_object(object, by_subject, ref_count, visited, depth = 1)
234
+ def format_inverse_object(object, by_subject, ref_count, visited, depth = 1, turtle_writer = RDF::Turtle::Writer.new(nil, prefixes: @prefix))
214
235
  if object.to_s.start_with?("_:") && by_subject.key?(object.to_s)
215
236
  if ref_count[object.to_s] <= 1
216
237
  format_inverse_subject(object.to_s, by_subject, ref_count, visited, depth)
@@ -218,19 +239,16 @@ module TTL2HTML
218
239
  object.to_s
219
240
  end
220
241
  else
221
- format_node(object)
242
+ format_node(object, turtle_writer)
222
243
  end
223
244
  end
224
- def format_node(value)
225
- turtle = RDF::Turtle::Writer.new
245
+ def format_node(value, writer = RDF::Turtle::Writer.new(nil, prefixes: @prefix))
226
246
  if value.to_s.start_with?("_:")
227
247
  value.to_s
228
248
  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]))
249
+ format_uri(value, writer)
232
250
  else
233
- turtle.format_literal(value)
251
+ writer.format_literal(value)
234
252
  end
235
253
  end
236
254
 
@@ -243,8 +261,8 @@ module TTL2HTML
243
261
  data = @data.keys.sort_by do|uri|
244
262
  local_path = uri_mapping_to_path(uri, @config, ".html")
245
263
  #p [ local_path.size, local_path.count("/"), local_path ]
246
- [ local_path.size, local_path.count("/"), local_path ]
247
- end.reverse
264
+ [ -(local_path.count("/")), -(local_path.size), local_path ]
265
+ end
248
266
  Parallel.each(data, progress: progressbar_options) do |uri|
249
267
  next if not uri.start_with? @config[:base_uri]
250
268
  yield uri, @data[uri]
@@ -275,7 +293,7 @@ module TTL2HTML
275
293
  end
276
294
  end
277
295
  @config[:orders_with_class] = shapes2orders(shapes)
278
- Dir.mkdir @config[:output_dir] if @config[:output_dir] and not File.exist? @config[:output_dir]
296
+ FileUtils.mkdir_p(@config[:output_dir]) if @config[:output_dir]
279
297
  template = Template.new("default.html.erb", @config)
280
298
  each_data(:output_html_files) do |uri, v|
281
299
  param = @config.dup
@@ -297,11 +315,14 @@ module TTL2HTML
297
315
  if @config[:output_dir]
298
316
  file = File.join(@config[:output_dir], file)
299
317
  end
300
- if template.find_template_path("_default.html.erb")
301
- param[:additional_content] = template.to_html_raw("_default.html.erb", param)
318
+ file = safe_output_path(file)
319
+ if file
320
+ if template.find_template_path("_default.html.erb")
321
+ param[:additional_content] = template.to_html_raw("_default.html.erb", param)
322
+ end
323
+ param[:about_file] = about_file if about_required
324
+ template.output_to(file, param)
302
325
  end
303
- param[:about_file] = about_file if about_required
304
- template.output_to(file, param)
305
326
  end
306
327
  index_html = "index.html"
307
328
  index_html = File.join(@config[:output_dir], "index.html") if @config[:output_dir]
@@ -343,7 +364,8 @@ module TTL2HTML
343
364
  param[:output_file] = index_html
344
365
  param[:index_list] = template.to_html_raw("index-list.html.erb", param)
345
366
  param[:about_file] = about_file if about_required
346
- template.output_to(index_html, param)
367
+ index_html = safe_output_path(index_html)
368
+ template.output_to(index_html, param) if index_html
347
369
  end
348
370
  end
349
371
  if about_required
@@ -388,7 +410,8 @@ module TTL2HTML
388
410
  order: orders,
389
411
  }
390
412
  end
391
- template.output_to(about_html, param)
413
+ about_html = safe_output_path(about_html)
414
+ template.output_to(about_html, param) if about_html
392
415
  end
393
416
  end
394
417
 
@@ -620,20 +643,29 @@ module TTL2HTML
620
643
  end
621
644
 
622
645
  def output_turtle_files
623
- Dir.mkdir @config[:output_dir] if @config[:output_dir] and not File.exist? @config[:output_dir]
646
+ FileUtils.mkdir_p(@config[:output_dir]) if @config[:output_dir]
624
647
  each_data(:output_turtle_files) do |uri, v|
625
648
  file = uri_mapping_to_path(uri, @config, ".ttl")
626
649
  if @config[:output_dir]
627
- Dir.mkdir @config[:output_dir] if not File.exist? @config[:output_dir]
628
650
  file = File.join(@config[:output_dir], file)
629
651
  end
652
+ file = safe_output_path(file)
653
+ next if not file
630
654
  dir = File.dirname(file)
631
655
  FileUtils.mkdir_p(dir) if not File.exist?(dir)
632
656
  @cache ||= {}
633
657
  @cache[:output_turtle_files] = Set.new
658
+ @used_prefixes = Set.new
634
659
  str = format_turtle(uri)
635
660
  str << format_turtle_inverse(uri)
636
- open(file, "w") do |io|
661
+ File.open(file, "w") do |io|
662
+ @used_prefixes.each do |prefix|
663
+ if prefix.empty?
664
+ io.puts "@prefix : <#{@prefix[nil]}>."
665
+ else
666
+ io.puts "@prefix #{prefix}: <#{@prefix[prefix.to_sym]}>."
667
+ end
668
+ end
637
669
  io.puts str.strip
638
670
  end
639
671
  end
@@ -653,30 +685,42 @@ module TTL2HTML
653
685
  end.each do |uri, v|
654
686
  html_file = uri_mapping_to_path(uri, @config, ".html")
655
687
  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
688
+ html_file = safe_output_path(html_file)
689
+ if html_file and File.file? html_file
690
+ dirs << File.dirname(html_file)
691
+ File.unlink html_file
692
+ end
658
693
  ttl_file = uri_mapping_to_path(uri, @config, ".ttl")
659
694
  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
695
+ ttl_file = safe_output_path(ttl_file)
696
+ if ttl_file and File.file? ttl_file
697
+ dirs << File.dirname(ttl_file)
698
+ File.unlink ttl_file
699
+ end
664
700
  end
665
701
  index_html = "index.html"
666
702
  index_html = File.join(@config[:output_dir], "index.html") if @config[:output_dir]
667
- if @config[:top_class] and File.exist? index_html
703
+ index_html = safe_output_path(index_html)
704
+ if index_html and @config[:top_class] and File.file? index_html
668
705
  File.unlink index_html
669
706
  end
670
707
  about_html = (@config[:about_file] || "about.html")
671
708
  about_html = File.join(@config[:output_dir], about_html) if @config[:output_dir]
672
- File.unlink about_html if File.exist? about_html
709
+ about_html = safe_output_path(about_html)
710
+ if about_html and File.file? about_html
711
+ File.unlink about_html
712
+ end
673
713
 
674
714
  dirs = dirs.uniq.sort_by{|e| -(e.size) }
675
715
  #p dirs
676
716
  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
717
+ dir = safe_output_path(dir)
718
+ next unless dir
719
+ next if dir == File.expand_path(".") # failsafe...
720
+ next if @config[:output_dir] and dir == File.expand_path(@config[:output_dir]) # failsafe...
721
+ if dir and File.exist?(dir) and File.directory?(dir)
722
+ FileUtils.remove_entry_secure(dir)
723
+ end
680
724
  end
681
725
  end
682
726
  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.0
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-04 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: nokogiri