ttl2html 3.0.0 → 3.1.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: 8120a06f3f763ca13d8b9f35c920f9129d57876a5dde434b264c052bd1aa9151
4
- data.tar.gz: 9d1cd9d09f5a14a5d56856c00099b38abe059a9c27f775df2074b42db99e9a7a
3
+ metadata.gz: 5d181672a5b0aa4cd6f7f1599fb20b6102c5232b3f774069038023bfc65d05a0
4
+ data.tar.gz: 313970beef022f77e7f4d5bac9da410adcc6203a6dd7d073d6ab2b49a516ebae
5
5
  SHA512:
6
- metadata.gz: 4935a3824f19a696ea4657d652c4afd482af6a8a2e33aa98725d4739375480654b86dd4a9781dae357d816d422dc7807a974bb7dfb38fbe1d6cb5d84a84a3792
7
- data.tar.gz: 9e561cb7abbfc195e8a13f09af9161a0b88f301e73b040ab149707e5f47905728a9ed603210b9f2818771350fd11670072941df4b58d92e4f5c268c608845274
6
+ metadata.gz: fdd4152bfc2ab82d97bed51c63dd8814e89a40b31d2e84ca2a64406dcfd4be373a393ccf8b2ebd477adb8e1f4df844f355872537232e7ec9357063ff2c123760
7
+ data.tar.gz: 7821d98735d0fadf33671017573accb9d765e2e2bd571c9fb66435251a056cedab6e05ec20b760c940cc2f1daa16bbdde11496cadcf4045fa9016344904e36db
@@ -98,12 +98,24 @@ module TTL2HTML
98
98
  end
99
99
  #p nodes
100
100
  end
101
+ example = data[property]["http://www.w3.org/2004/02/skos/core#example"].first if data[property]["http://www.w3.org/2004/02/skos/core#example"]
102
+ if example.respond_to?(:datatype) and example.datatype?
103
+ datatype = example.datatype.pname or example.datatype.to_s
104
+ example = example.to_s + "<span class=\"datatype\">^^" + example.datatype.pname + "</span>"
105
+ end
106
+ descriptions = [ get_language_literal(data[property]["http://www.w3.org/ns/shacl#description"]) ]
107
+ if data[property]["http://www.w3.org/ns/shacl#datatype"]
108
+ datatypes = data[property]["http://www.w3.org/ns/shacl#datatype"].map do |datatype|
109
+ datatype.to_s.sub(%r{\Ahttp://www\.w3\.org/2001/XMLSchema#}, 'xsd:')
110
+ end.join(", ")
111
+ descriptions << t('shape-table.datatype') + datatypes
112
+ end
101
113
  {
102
114
  path: path,
103
115
  shorten_path: shorten_path,
104
116
  name: get_language_literal(data[property]["http://www.w3.org/ns/shacl#name"]),
105
- example: data[property]["http://www.w3.org/2004/02/skos/core#example"] ? data[property]["http://www.w3.org/2004/02/skos/core#example"].first : nil,
106
- description: get_language_literal(data[property]["http://www.w3.org/ns/shacl#description"]),
117
+ example: example,
118
+ description: descriptions.compact.join("<br>"),
107
119
  required: data[property]["http://www.w3.org/ns/shacl#minCount"] ? data[property]["http://www.w3.org/ns/shacl#minCount"].first.to_i > 0 : false,
108
120
  repeatable: repeatable,
109
121
  nodeKind: data[property]["http://www.w3.org/ns/shacl#nodeKind"] ? data[property]["http://www.w3.org/ns/shacl#nodeKind"].first : nil,
@@ -1 +1 @@
1
- TTL2HTML::VERSION = "3.0.0"
1
+ TTL2HTML::VERSION = "3.1.1"
data/lib/ttl2html.rb CHANGED
@@ -72,9 +72,31 @@ module TTL2HTML
72
72
  $stderr.puts "#{count} triples. #{subjects.size} subjects."
73
73
  @data
74
74
  end
75
- def format_turtle(subject, depth = 1)
75
+ QB_ORDER_URI = "http://purl.org/linked-data/cube#order"
76
+ SCHEMA_POSITION_URI = "http://schema.org/position"
77
+ SHACL_ORDER_URI = "http://www.w3.org/ns/shacl#order"
78
+ def sort_key_for_resource(resource, depth = 1)
79
+ qb_order = Float::INFINITY
80
+ schema_position = Float::INFINITY
81
+ shacl_order = Float::INFINITY
82
+ if @data[resource.to_s]
83
+ qb_order = @data[resource.to_s][QB_ORDER_URI].first.to_i if @data[resource.to_s][QB_ORDER_URI]
84
+ schema_position = @data[resource.to_s][SCHEMA_POSITION_URI].first.to_i if @data[resource.to_s][SCHEMA_POSITION_URI]
85
+ shacl_order = @data[resource.to_s][SHACL_ORDER_URI].first.to_i if @data[resource.to_s][SHACL_ORDER_URI]
86
+ end
87
+ if resource.to_s =~ /^_:/ and depth < 5
88
+ [ schema_position, qb_order, shacl_order,
89
+ format_turtle(resource, depth + 1, true)
90
+ ]
91
+ else
92
+ [ schema_position, qb_order, shacl_order, resource.to_s]
93
+ end
94
+ end
95
+ def format_turtle(subject, depth = 1, force = false)
76
96
  turtle = RDF::Turtle::Writer.new
77
97
  result = ""
98
+ #p [:format_turtle, subject, depth, force]
99
+ return result if !force && @cache[:output_turtle_files].include?(subject)
78
100
  if subject =~ /^_:/
79
101
  result << "[\n#{" "*depth}"
80
102
  else
@@ -85,21 +107,10 @@ module TTL2HTML
85
107
  #p [subject, predicate, @data[subject.to_s][predicate]]
86
108
  str << @data[subject.to_s][predicate].sort_by do |object|
87
109
  #p [subject, predicate, object, depth]
88
- if object.to_s =~ /^_:/ and @data[object.to_s]
89
- qb_order = "http://purl.org/linked-data/cube#order"
90
- schema_position = "http://schema.org/position"
91
- shacl_order = "http://www.w3.org/ns/shacl#order"
92
- [ @data[object.to_s][schema_position] ? @data[object.to_s][schema_position].first.to_i : Float::INFINITY,
93
- @data[object.to_s][qb_order] ? @data[object.to_s][qb_order].first.to_i : Float::INFINITY,
94
- @data[object.to_s][shacl_order] ? @data[object.to_s][shacl_order].first.to_i : Float::INFINITY,
95
- format_turtle(object, depth + 1)
96
- ]
97
- else
98
- [Float::INFINITY, Float::INFINITY, Float::INFINITY, object.to_s]
99
- end
110
+ sort_key_for_resource(object, depth)
100
111
  end.map do |object|
101
112
  if /^_:/ =~ object.to_s # blank node:
102
- format_turtle(object, depth + 1)
113
+ format_turtle(object, depth + 1, force)
103
114
  elsif RDF::URI::IRI =~ object.to_s
104
115
  turtle.format_uri(RDF::URI.new object)
105
116
  elsif object.respond_to?(:first) and object.first.kind_of?(Symbol)
@@ -110,22 +121,117 @@ module TTL2HTML
110
121
  end.join(", ")
111
122
  str
112
123
  end.join(";\n#{" "*depth}")
113
- result << " ." if not subject =~ /^_:/
124
+ result << "." if not subject =~ /^_:/
114
125
  result << "\n"
115
126
  result << "#{" "*(depth-1)}]" if subject =~ /^_:/
127
+ @cache[:output_turtle_files] << subject unless force
116
128
  result
117
129
  end
118
130
  def format_turtle_inverse(object)
119
- result = ""
120
- return result if not object.start_with? @config[:base_uri]
121
- return result if not @data_inverse.has_key? object
122
- @data_inverse[object].keys.sort.each do |predicate|
123
- @data_inverse[object.to_s][predicate].sort.each do |subject|
124
- next if subject =~ /^_:/
125
- result << "<#{subject}> <#{predicate}> <#{object}>.\n"
131
+ triples = collect_inverse_triples(object)
132
+ return "" if triples.empty?
133
+ by_subject = build_subject_index(triples)
134
+ ref_count = build_object_ref_count(triples)
135
+ roots = find_inverse_roots(by_subject)
136
+ roots.map do |root|
137
+ "#{format_inverse_subject(root, by_subject, ref_count, Set.new, 1)}.\n"
138
+ end.join
139
+ end
140
+ 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
+ return triples if visited.include?(object.to_s)
145
+ 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?("_:")
126
150
  end
127
151
  end
128
- result
152
+ triples
153
+ end
154
+ def collect_inverse_triples_for_bnode(node, triples, visited)
155
+ return triples unless @data_inverse.key?(node)
156
+ return triples if visited.include?(node)
157
+ visited << node
158
+ @data_inverse[node].each do |predicate, subjects|
159
+ subjects.each do |subject|
160
+ triples << [subject.to_s, predicate.to_s, node]
161
+ collect_inverse_triples_for_bnode(subject.to_s, triples, visited) if subject.to_s.start_with?("_:")
162
+ end
163
+ end
164
+ triples
165
+ end
166
+ def build_subject_index(triples)
167
+ by_subject = Hash.new { |h, k| h[k] = Hash.new { |hh, kk| hh[kk] = [] } }
168
+ triples.each do |subject, predicate, object|
169
+ by_subject[subject][predicate] << object
170
+ end
171
+ by_subject.each_value do |predicates|
172
+ predicates.each_value(&:uniq!)
173
+ end
174
+ by_subject
175
+ end
176
+ def build_object_ref_count(triples)
177
+ count = Hash.new(0)
178
+ triples.each do |_subject, _predicate, object|
179
+ count[object] += 1 if object.to_s.start_with?("_:")
180
+ end
181
+ count
182
+ end
183
+ def find_inverse_roots(by_subject)
184
+ all_subjects = by_subject.keys
185
+ 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
189
+ end
190
+ def format_inverse_subject(subject, by_subject, ref_count, visited, depth = 1)
191
+ props = by_subject[subject]
192
+ return format_node(subject) if props.nil? || props.empty?
193
+ indent = " " * (depth - 1)
194
+ inner = " " * depth
195
+ if subject.start_with?("_:")
196
+ return "[]" if visited.include?(subject)
197
+ visited = visited.dup
198
+ visited << subject
199
+ head = "[\n#{inner}"
200
+ tail = "\n#{indent}]"
201
+ else
202
+ head = "<#{subject}> "
203
+ tail = ""
204
+ end
205
+ body = props.keys.sort.map do |predicate|
206
+ objects = props[predicate].sort.map do |object|
207
+ format_inverse_object(object, by_subject, ref_count, visited, depth + 1)
208
+ end.join(", ")
209
+ "<#{predicate}> #{objects}"
210
+ end.join(";\n#{inner}")
211
+ head + body + tail
212
+ end
213
+ def format_inverse_object(object, by_subject, ref_count, visited, depth = 1)
214
+ if object.to_s.start_with?("_:") && by_subject.key?(object.to_s)
215
+ if ref_count[object.to_s] <= 1
216
+ format_inverse_subject(object.to_s, by_subject, ref_count, visited, depth)
217
+ else
218
+ object.to_s
219
+ end
220
+ else
221
+ format_node(object)
222
+ end
223
+ end
224
+ def format_node(value)
225
+ turtle = RDF::Turtle::Writer.new
226
+ if value.to_s.start_with?("_:")
227
+ value.to_s
228
+ 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]))
232
+ else
233
+ turtle.format_literal(value)
234
+ end
129
235
  end
130
236
 
131
237
  def each_data(label = :each_data)
@@ -218,7 +324,9 @@ module TTL2HTML
218
324
  param[:versions] = versions
219
325
  param[:toplevel] = toplevel
220
326
  param[:description] = template.to_html_raw("description.html", {}) if template.find_template_path("description.html")
221
- subjects.sort.each do |subject|
327
+ subjects.sort_by do |subject|
328
+ sort_key_for_resource(subject)
329
+ end.each do |subject|
222
330
  objects = []
223
331
  if @config.has_key? :top_additional_property
224
332
  @config[:top_additional_property].each do |property|
@@ -521,6 +629,8 @@ module TTL2HTML
521
629
  end
522
630
  dir = File.dirname(file)
523
631
  FileUtils.mkdir_p(dir) if not File.exist?(dir)
632
+ @cache ||= {}
633
+ @cache[:output_turtle_files] = Set.new
524
634
  str = format_turtle(uri)
525
635
  str << format_turtle_inverse(uri)
526
636
  open(file, "w") do |io|
data/locales/en.yml CHANGED
@@ -45,6 +45,7 @@ en:
45
45
  non-repeatable: "Non repeatable"
46
46
  blank-node-structure: "The structural contents of a blank node are as follows:"
47
47
  blank-node-or-structure: "The structural contents of a blank node are either of the followings:"
48
+ datatype: "Datatype: "
48
49
  triples:
49
50
  inverse_refered: "Referred to as '%{property}' from:"
50
51
  blank_node: "Blank node"
data/locales/ja.yml CHANGED
@@ -45,6 +45,7 @@ ja:
45
45
  non-repeatable: 繰り返し無し
46
46
  blank-node-structure: ブランクノードの内容は以下の内容からなる構造を持ちます。
47
47
  blank-node-or-structure: ブランクノードの内容は以下のいずれかの内容からなる構造を持ちます。
48
+ datatype: "データ型: "
48
49
  triples:
49
50
  inverse_refered: "'%{property}'としての参照元:"
50
51
  blank_node: "空ノード"
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.0.0
4
+ version: 3.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masao Takaku
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-04-08 00:00:00.000000000 Z
10
+ date: 2026-04-29 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: nokogiri
@@ -210,7 +210,9 @@ files:
210
210
  homepage: https://github.com/masao/ttl2html
211
211
  licenses:
212
212
  - MIT
213
- metadata: {}
213
+ metadata:
214
+ bug_tracker_uri: https://github.com/masao/ttl2html/issues
215
+ documentation_uri: https://ttl2html-doc.readthedocs.io/
214
216
  rdoc_options: []
215
217
  require_paths:
216
218
  - lib
@@ -225,7 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
225
227
  - !ruby/object:Gem::Version
226
228
  version: '0'
227
229
  requirements: []
228
- rubygems_version: 4.0.3
230
+ rubygems_version: 4.0.10
229
231
  specification_version: 4
230
232
  summary: ttl2html
231
233
  test_files: []