infod 0.0.3.3 → 0.0.3.4
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/infod.rb +2 -1
- data/infod/404.rb +6 -7
- data/infod/500.rb +4 -40
- data/infod/GET.rb +15 -16
- data/infod/HTTP.rb +5 -1
- data/infod/{mime.rb → MIME.rb} +0 -2
- data/infod/POST.rb +29 -28
- data/infod/SPARQL.rb +6 -0
- data/infod/WebID.rb +3 -0
- data/infod/blog.rb +35 -13
- data/infod/constants.rb +1 -3
- data/infod/edit.rb +10 -8
- data/infod/facets.rb +2 -2
- data/infod/feed.rb +149 -108
- data/infod/fs.rb +33 -6
- data/infod/graph.rb +50 -46
- data/infod/groonga.rb +8 -9
- data/infod/html.rb +34 -67
- data/infod/image.rb +2 -2
- data/infod/infod.rb +2 -1
- data/infod/lambda.rb +2 -6
- data/infod/mail.rb +29 -24
- data/infod/man.rb +1 -1
- data/infod/microblog.rb +10 -1
- data/infod/names.rb +9 -20
- data/infod/rdf.rb +5 -4
- data/infod/schema.rb +4 -16
- data/infod/text.rb +1 -1
- data/infod/time.rb +1 -15
- metadata +35 -5
data/infod/groonga.rb
CHANGED
@@ -33,7 +33,6 @@ class R
|
|
33
33
|
|
34
34
|
rescue Groonga::SyntaxError => x
|
35
35
|
m['#'] = {Type => R[COGS+'Exception'], Title => "invalid expr", Content => CGI.escapeHTML(x.message)}
|
36
|
-
e['nocache']=true
|
37
36
|
end
|
38
37
|
|
39
38
|
F['docsID'][m,e]}}
|
@@ -66,14 +65,14 @@ class R
|
|
66
65
|
|
67
66
|
# add
|
68
67
|
def roonga graph="global", m = self.graph
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
68
|
+
R.groonga.do{|g|
|
69
|
+
m.map{|u,i|
|
70
|
+
r = g[u] || g.add(u) # create or load entry
|
71
|
+
r.uri = u # update data
|
72
|
+
r.graph = graph.to_s
|
73
|
+
r.content = i.to_s
|
74
|
+
r.time = i[R::Date].do{|t|t[0].to_time}
|
75
|
+
}}
|
77
76
|
self
|
78
77
|
end
|
79
78
|
|
data/infod/html.rb
CHANGED
@@ -40,7 +40,7 @@ end
|
|
40
40
|
class Array
|
41
41
|
def cr; intersperse "\n" end
|
42
42
|
def head; self[0] end
|
43
|
-
def html v=nil
|
43
|
+
def html v=nil; map{|e|e.html v}.join ' ' end
|
44
44
|
def h; join.h end
|
45
45
|
def intersperse i
|
46
46
|
inject([]){|a,b|a << b << i}[0..-2]
|
@@ -63,53 +63,41 @@ class String
|
|
63
63
|
def abbrURI
|
64
64
|
sub /(?<scheme>[a-z]+:\/\/)?(?<abbr>.*?)(?<frag>[^#\/]+)\/?$/,'<span class="abbr"><span class="scheme">\k<scheme></span>\k<abbr></span><span class="frag">\k<frag></span>'
|
65
65
|
end
|
66
|
-
def html e=nil
|
66
|
+
def html e=nil
|
67
67
|
self
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
71
|
class Fixnum
|
72
|
-
def html e=nil
|
72
|
+
def html e=nil; to_s end
|
73
73
|
def max i; i > self ? self : i end
|
74
74
|
def min i; i < self ? self : i end
|
75
75
|
end
|
76
76
|
|
77
77
|
class Float
|
78
|
-
def html e=nil
|
78
|
+
def html e=nil; to_s end
|
79
79
|
def max i; i > self ? self : i end
|
80
80
|
def min i; i < self ? self : i end
|
81
81
|
end
|
82
82
|
|
83
83
|
class TrueClass
|
84
|
-
def html e=nil
|
84
|
+
def html e=nil; H({_: :input, type: :checkbox, title: :True, checked: :checked}) end
|
85
85
|
end
|
86
86
|
|
87
87
|
class FalseClass
|
88
|
-
def html e=nil
|
88
|
+
def html e=nil; H({_: :input, type: :checkbox, title: :False}) end
|
89
89
|
end
|
90
90
|
|
91
|
-
IsBnode = /^_:/
|
92
|
-
|
93
91
|
class Hash
|
94
|
-
def html e={'SERVER_NAME'=>'localhost'}
|
92
|
+
def html e={'SERVER_NAME'=>'localhost'}
|
95
93
|
if keys.size == 1 && has_key?('uri')
|
96
|
-
|
97
|
-
g[uri].do{|r|
|
98
|
-
r.html e,g,key } || uri.href
|
99
|
-
else
|
100
|
-
uri.href
|
101
|
-
end
|
94
|
+
uri.href
|
102
95
|
else
|
103
96
|
H({_: :table, class: :html, c: map{|k,v|
|
104
|
-
|
105
|
-
{_: :
|
106
|
-
|
107
|
-
|
108
|
-
({_: :td, c: [{_: :a, name: k, href: (k == 'uri' ? (v.R.docBase.localURL e)+'?graph=edit' : k), c: k.to_s.abbrURI}], class: :key} if key),
|
109
|
-
{_: :td, c: k == 'uri' ? v.R.do{|u| {_: :a, id: u, href: u.url, c: v}} : v.html(e,g), class: :val},
|
110
|
-
]]}
|
111
|
-
end
|
112
|
-
}})
|
97
|
+
{_: :tr, property: k, c:
|
98
|
+
[k == R::Content ? {_: :td, class: :val, colspan: 2, c: v} :
|
99
|
+
[{_: :td, c: [{_: :a, name: k, href: (k == 'uri' ? (v.R.docBase.localURL e)+'?graph=edit&mono' : k), c: k.to_s.abbrURI}], class: :key},
|
100
|
+
{_: :td, c: k == 'uri' ? v.R.do{|u| {_: :a, id: u, href: u.url, c: v}} : v.html(e), class: :val}]]}}})
|
113
101
|
end
|
114
102
|
end
|
115
103
|
end
|
@@ -121,36 +109,26 @@ class R
|
|
121
109
|
end
|
122
110
|
|
123
111
|
F['view']=->d,e{
|
124
|
-
d.values.
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
[F['view/' + subtype],
|
145
|
-
(F['view/' + type] if type)]}.
|
146
|
-
flatten.compact
|
147
|
-
view = views[0] unless views.empty?}
|
148
|
-
end
|
149
|
-
if !view # default view
|
150
|
-
F['view/base'][graph,e,true,d]
|
151
|
-
else
|
152
|
-
view[graph,e]
|
153
|
-
end}
|
112
|
+
d.values.map{|r|
|
113
|
+
graph = {r.uri => r}
|
114
|
+
view = nil
|
115
|
+
if r.class == Hash
|
116
|
+
r[Type].justArray.do{|types|
|
117
|
+
views = types.map(&:maybeURI).compact.map{|t|
|
118
|
+
subtype = t
|
119
|
+
type = subtype.split(/\//)[-2]
|
120
|
+
[F['view/' + subtype],
|
121
|
+
(F['view/' + type] if type)]}.
|
122
|
+
flatten.compact
|
123
|
+
view = views[0] unless views.empty?}
|
124
|
+
end
|
125
|
+
if !view
|
126
|
+
F['view/base'][graph,e]
|
127
|
+
else
|
128
|
+
view[graph,e]
|
129
|
+
end}}
|
130
|
+
|
131
|
+
F['view/base']=->d,e{[H.once(e,'base',H.css('/css/html')),d.values.map{|v|v.html e}]}
|
154
132
|
|
155
133
|
def triplrBlob
|
156
134
|
glob.select(&:f).do{|f|f.map{|r|
|
@@ -158,10 +136,10 @@ class R
|
|
158
136
|
yield r.uri,Content,r.r}} end
|
159
137
|
|
160
138
|
def triplrHref enc=nil
|
161
|
-
yield uri,Content,(
|
139
|
+
yield uri,Content,open(d).read.do{|r|enc ? r.force_encoding(enc).to_utf8 : r}.hrefs if f
|
162
140
|
end
|
163
141
|
|
164
|
-
def nokogiri; Nokogiri::HTML.parse read end
|
142
|
+
def nokogiri; Nokogiri::HTML.parse (open uri).read end
|
165
143
|
|
166
144
|
F['HTMLbody'] = -> b {
|
167
145
|
b.to_s.split(/<body[^>]*>/)[-1].to_s.split(/<\/body>/)[0] }
|
@@ -182,17 +160,6 @@ class R
|
|
182
160
|
c: {_: :img, src: '/css/misc/cube.png', style: 'height:2em;background-color:white;padding:.54em;border-radius:1em;margin:.2em'}},
|
183
161
|
(H.js '/js/pager'),(H.once e,:mu,(H.js '/js/mu'))]}} # (n)ext (p)rev key binding
|
184
162
|
|
185
|
-
def contentURIresolve *f
|
186
|
-
send(*f){|s,p,o|
|
187
|
-
yield s, p, p == Content ?
|
188
|
-
(Nokogiri::HTML.fragment o).do{|o|
|
189
|
-
o.css('a').map{|a|
|
190
|
-
if a.has_attribute? 'href'
|
191
|
-
(a.set_attribute 'href', (URI.join s, (a.attr 'href'))) rescue nil
|
192
|
-
end}
|
193
|
-
o.to_s} : o}
|
194
|
-
end
|
195
|
-
|
196
163
|
fn Render+'text/html',->d,e{ u = d['#']||{}
|
197
164
|
titles = d.map{|u,r| r[Title] if r.class==Hash }.flatten.compact
|
198
165
|
view = F['view/'+e.q['view'].to_s] || F['view']
|
data/infod/image.rb
CHANGED
@@ -21,14 +21,14 @@ class R
|
|
21
21
|
`gm convert #{i.sh} -thumbnail "#{size}x#{size}" #{path.sh}`
|
22
22
|
end
|
23
23
|
end
|
24
|
-
path.e ? (path.env r).
|
24
|
+
path.e ? (path.env r).fileGET : F[E404][e,r]
|
25
25
|
else
|
26
26
|
F[E404][e,r]
|
27
27
|
end}
|
28
28
|
|
29
29
|
fn 'view/img',->i,_{
|
30
30
|
[i.values.select{|v|v.class==Hash}.map{|i|
|
31
|
-
i[Type] &&
|
31
|
+
i[Type] && i[Type].justArray.map(&:maybeURI).include?(DC+'Image') &&
|
32
32
|
[{_: :a, href: i.url, c: {_: :img, style:'float:left;max-width:61.8%', src: i.url}},
|
33
33
|
i.html]},
|
34
34
|
(H.css '/css/img')]}
|
data/infod/infod.rb
CHANGED
data/infod/lambda.rb
CHANGED
@@ -4,7 +4,7 @@ end
|
|
4
4
|
|
5
5
|
class NilClass
|
6
6
|
def do; nil end
|
7
|
-
def html e=nil
|
7
|
+
def html e=nil; "" end
|
8
8
|
def R; "".R end
|
9
9
|
end
|
10
10
|
|
@@ -21,10 +21,6 @@ def watch f
|
|
21
21
|
|
22
22
|
class R
|
23
23
|
|
24
|
-
def initialize uri
|
25
|
-
@uri = uri.to_s
|
26
|
-
end
|
27
|
-
|
28
24
|
def R uri = nil
|
29
25
|
uri ? R.new(uri) : self
|
30
26
|
end
|
@@ -49,7 +45,7 @@ class R
|
|
49
45
|
i = i.split /:/
|
50
46
|
yield uri, (f + (i[0].match(g)||[0,i[0]])[1]. # s
|
51
47
|
gsub(/\s/,'_').gsub(/\//,'-').gsub(/[\(\)]+/,'')), # p
|
52
|
-
i.tail.join(':').strip.do{|v|v.match(/^[0-9\.]+$/) ? v.to_f : v
|
48
|
+
i.tail.join(':').strip.do{|v|v.match(/^[0-9\.]+$/) ? v.to_f : v}} # o
|
53
49
|
rescue
|
54
50
|
end
|
55
51
|
|
data/infod/mail.rb
CHANGED
@@ -1,18 +1,33 @@
|
|
1
1
|
#watch __FILE__
|
2
2
|
class R
|
3
|
+
=begin usage
|
3
4
|
|
4
|
-
|
5
|
+
messages matching an address
|
6
|
+
msgs = R['/m/semantic-web@w3.org'].take
|
7
|
+
|
8
|
+
mirror message-files backing a resource-set
|
9
|
+
src = R::DC+'source'
|
10
|
+
files = msgs.map{|g| '.' + g.graph.values.find{|r|r.has_key? src}[src].head.R.path}
|
11
|
+
`rsync -avRz #{files.join ' '} h:/www/`
|
12
|
+
|
13
|
+
summary view on directories
|
14
|
+
F['/mail/GET'] = -> e,r {
|
15
|
+
r.q['view'] ||= 'threads' if e.uri[-1] == '/'
|
16
|
+
nil }
|
17
|
+
|
18
|
+
=end
|
5
19
|
|
6
|
-
|
20
|
+
MessagePath = ->id{'/msg/' + id.h[0..2] + '/' + id}
|
21
|
+
GREP_DIRS.push /^\/m\/[^\/]+\// # allow on a single address
|
7
22
|
|
8
23
|
F['/m/GET'] = -> e,r{
|
9
|
-
#
|
24
|
+
# use summary view and start a newest-first tree-range at address
|
10
25
|
if m = e.pathSegment.uri.match(/^\/m\/([^\/]+)$/)
|
11
|
-
r.q['set']
|
26
|
+
r.q['set'] ||= 'depth'
|
12
27
|
r.q['view'] ||= 'threads'
|
13
|
-
|
28
|
+
e.response
|
14
29
|
else
|
15
|
-
false
|
30
|
+
false
|
16
31
|
end}
|
17
32
|
|
18
33
|
IndexMail = ->doc, graph, host {
|
@@ -48,12 +63,16 @@ class R
|
|
48
63
|
creator = '/m/'+f+'#'+f # author URI
|
49
64
|
yield e, Creator, R[creator] # message -> author
|
50
65
|
yield creator, DC+'identifier', R['mailto:'+f] # author ID
|
66
|
+
# reply to
|
67
|
+
r2 = m['List-Post'].do{|lp|lp.decoded[8..-2]} || # List-Post
|
68
|
+
m.reply_to.do{|t|t[0]} || # Reply-To
|
69
|
+
f # From
|
51
70
|
yield e, SIOC+'reply_to', # reply URI
|
52
|
-
R[URI.escape("mailto:#{
|
71
|
+
R[URI.escape("mailto:#{r2}?References=<#{id}>&In-Reply-To=<#{id}>&Subject=#{m.subject}&")+'#reply']}}
|
53
72
|
|
54
73
|
yield e, Date, m.date.iso8601 if m.date # date
|
55
74
|
|
56
|
-
m.subject.do{|s|yield e, Title, s.to_utf8}
|
75
|
+
m.subject.do{|s|yield e, Title, s.to_utf8.hrefs} # subject
|
57
76
|
|
58
77
|
yield e, SIOC+'has_discussion', # thread
|
59
78
|
R[e+'?graph=thread&view=timegraph#discussion'] if m.in_reply_to || m.references
|
@@ -83,7 +102,7 @@ class R
|
|
83
102
|
c: p.decoded.to_utf8.hrefs.gsub(/^\s*(>)(>|\s)*\n/,"").lines.to_a.map{|l| # skip quoted*empty lines
|
84
103
|
l.match(/(^\s*(>|On[^\n]+(said|wrote))[^\n]*)\n/) ? # quoted?
|
85
104
|
{_: :span, class: :q, depth: l.scan(/(>)/).size, c: l} : l # wrap quotes
|
86
|
-
}},(H.css '/css/mail'
|
105
|
+
}},(H.css '/css/mail')])}
|
87
106
|
|
88
107
|
attache = -> { e.R.a('.attache').mk } # filesystem container for attachments & parts
|
89
108
|
|
@@ -106,20 +125,6 @@ class R
|
|
106
125
|
end
|
107
126
|
|
108
127
|
F['view/'+MIMEtype+'message/rfc822'] = NullView # hide container-resource in default view
|
109
|
-
=
|
110
|
-
USAGE(context)
|
111
|
-
|
112
|
-
admin (irb) move messages among filesystems
|
113
|
-
p = R['/m/semantic-web@w3.org'].take.map{|g|'.' + g.graph.values.find{|r|r.has_key?(R::DC+'source')}[R::DC+'source'][0].R.path}
|
114
|
-
`rsync -avRz #{p.join ' '} h:/www/`
|
128
|
+
F['view/'+MIMEtype+'text/n3'] = NullView
|
115
129
|
|
116
|
-
view (rb) default to overview of directory
|
117
|
-
F['/mail/GET'] = -> e,r {
|
118
|
-
r.q['view'] ||= 'threads' if e.uri[-1] == '/'
|
119
|
-
false }
|
120
|
-
|
121
|
-
home (sh) current day-dir in Markdown
|
122
|
-
echo "[today](/?y=day&view=threads)" > TODAY.md
|
123
|
-
|
124
|
-
=end
|
125
130
|
end
|
data/infod/man.rb
CHANGED
data/infod/microblog.rb
CHANGED
@@ -26,9 +26,18 @@ class R
|
|
26
26
|
|
27
27
|
def tw g
|
28
28
|
node.readlines.shuffle.each_slice(22){|s|
|
29
|
-
R['https://twitter.com/search/realtime?q='+s.map{|u|'from:'+u.chomp}.intersperse('+OR+').join].addDocs :triplrTweets, g, nil,
|
29
|
+
R['https://twitter.com/search/realtime?q='+s.map{|u|'from:'+u.chomp}.intersperse('+OR+').join].addDocs :triplrTweets, g, nil, FeedArchiverJSON}
|
30
30
|
end
|
31
31
|
|
32
|
+
FeedArchiverJSON = -> doc, graph, host {
|
33
|
+
doc.roonga host
|
34
|
+
graph.map{|u,r|
|
35
|
+
r[Date].do{|t| # link doc to date-index
|
36
|
+
t = t[0].gsub(/[-T]/,'/').sub /(.00.00|Z)$/, '' # trim normalized timezones and non-unique symbols
|
37
|
+
b = (u.sub(/http:\/\//,'.').gsub(/\W/,'..').gsub(FeedStop,'').sub(/\d{12,}/,'')+'.').gsub /\.+/,'.'
|
38
|
+
doc.ln R["http://#{host}/news/#{t}#{b}e"]}}
|
39
|
+
doc}
|
40
|
+
|
32
41
|
def triplrTweets
|
33
42
|
base = 'http://twitter.com'
|
34
43
|
nokogiri.css('div.tweet').map{|t|
|
data/infod/names.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
class R
|
2
|
+
=begin
|
3
|
+
name-manipulating functions
|
4
|
+
a RDF::URI-identified-resource has an associated filesystem node when using our subclass..
|
5
|
+
=end
|
2
6
|
|
3
7
|
def appendURI u; R uri + u.to_s end
|
4
8
|
def appendSlashURI u; R uri.t + u.to_s end
|
@@ -8,33 +12,22 @@ class R
|
|
8
12
|
def children; node.c.map &:R end
|
9
13
|
def container; @u ||= R[f ? dirname + '/.' + (File.basename path) : path] end
|
10
14
|
def d; node.to_s end
|
11
|
-
def delete; node.deleteNode if e; self end
|
12
15
|
def dirname; node.dirname.do{|d| d.to_s.size <= BaseLen ? '/' : d }.R end
|
13
16
|
def docBase; uri.split(/#/)[0].R.do{|d| d.dirname.as d.bare } end
|
14
17
|
def d?; node.directory? end
|
15
|
-
def env r=nil;r ? (@r = r; self) : @r end
|
16
|
-
def exist?; node.exist? end
|
17
18
|
def expand; uri.expand.R end
|
18
19
|
def ext; File.extname(uri).tail||'' end
|
19
|
-
def file?; node.file? end
|
20
20
|
def frag; uri.frag end
|
21
|
-
def get; open(uri).read end
|
22
21
|
def glob p=""; (Pathname.glob d + p).map &:R end
|
23
22
|
def hostURL e; host='http://'+e['SERVER_NAME']; (uri.index('/') == 0 ? host : '') + uri end
|
24
23
|
def inside; node.expand_path.to_s.index(FSbase) == 0 end
|
25
24
|
def label; uri.label end
|
26
|
-
def mk; e || FileUtils.mkdir_p(d); self end
|
27
|
-
def mtime; node.stat.mtime if e end
|
28
25
|
def node; Pathname.new FSbase + path end
|
29
26
|
def parent; R Pathname.new(uri).parent end
|
30
27
|
def parents; parent.do{|p|p.uri.match(/^[.\/]+$/) ? [p] : [p].concat(p.parents)} end
|
31
28
|
def path; uri.match(/^\//) ? uri : ('/'+uri) end
|
32
29
|
def pathSegment; uri.match(/^([a-z]+:\/\/[^\/]+)?(\/.*)/).do{|p|p[2]&&p[2].R}||nil end
|
33
|
-
def predicatePath p,s=true; container.as s ? p.R.shorten : p end
|
34
|
-
def predicates; container.c.map{|c|c.base.expand.R} end
|
35
30
|
def prependURI u; R u.to_s + uri end
|
36
|
-
def read; f ? readFile : get end
|
37
|
-
def readFile; File.open(d).read end
|
38
31
|
def realpath; node.realpath rescue Errno::ENOENT end
|
39
32
|
def shorten; uri.shorten.R end
|
40
33
|
def siblings; parent.c end
|
@@ -42,11 +35,9 @@ class R
|
|
42
35
|
def == u; to_s == u.to_s end
|
43
36
|
def <=> c; to_s <=> c.to_s end
|
44
37
|
def sh; d.force_encoding('UTF-8').sh end
|
45
|
-
def to_s; uri end
|
46
|
-
def to_h; {'uri' => uri} end
|
47
|
-
def touch; FileUtils.touch node; self end
|
48
|
-
def writeFile c; File.open(d,'w'){|f|f << c} end
|
49
38
|
|
39
|
+
# shortcuts
|
40
|
+
|
50
41
|
alias_method :+, :appendURI
|
51
42
|
alias_method :a, :appendURI
|
52
43
|
alias_method :as, :appendSlashURI
|
@@ -54,11 +45,9 @@ class R
|
|
54
45
|
alias_method :bare, :barename
|
55
46
|
alias_method :c, :children
|
56
47
|
alias_method :dir, :dirname
|
57
|
-
alias_method :
|
58
|
-
alias_method :
|
59
|
-
alias_method :
|
60
|
-
alias_method :maybeURI, :uri
|
61
|
-
alias_method :url, :uri
|
48
|
+
alias_method :maybeURI, :to_s
|
49
|
+
alias_method :url, :to_s
|
50
|
+
alias_method :uri, :to_s
|
62
51
|
|
63
52
|
def localURL e
|
64
53
|
if uri.index('/') == 0
|
data/infod/rdf.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
#watch __FILE__
|
2
2
|
class R
|
3
3
|
|
4
|
-
def nt;
|
5
|
-
def n3;
|
6
|
-
def ttl;
|
4
|
+
def nt; docBase.a '.nt' end
|
5
|
+
def n3; docBase.a '.n3' end
|
6
|
+
def ttl; docBase.a '.ttl' end
|
7
7
|
|
8
8
|
def self.renderRDF d,f,e
|
9
9
|
(RDF::Writer.for f).buffer{|w|
|
@@ -23,7 +23,8 @@ class R
|
|
23
23
|
uri = (local && f) ? d : uri
|
24
24
|
RDF::Reader.open(uri, :format => format){|r|
|
25
25
|
r.each_triple{|s,p,o|
|
26
|
-
yield s.to_s, p.to_s,
|
26
|
+
yield s.to_s, p.to_s,
|
27
|
+
[RDF::Node, RDF::URI].member?(o.class) ? R(o) : o.value.do{|v|v.class == String ? v.to_utf8 : v}}}
|
27
28
|
end
|
28
29
|
|
29
30
|
[['application/ld+json',:jsonld],
|