infod 0.0.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.
- data/bin/infod +37 -0
- data/config.ru +3 -0
- data/infod/Es/fs.rb +154 -0
- data/infod/Es/groonga.rb +101 -0
- data/infod/Es/redis.rb +3 -0
- data/infod/Es/sqlite.rb +3 -0
- data/infod/Es.rb +67 -0
- data/infod/H.rb +29 -0
- data/infod/K.rb +197 -0
- data/infod/N.rb +248 -0
- data/infod/Rb.rb +71 -0
- data/infod/Th/404.rb +55 -0
- data/infod/Th/500.rb +10 -0
- data/infod/Th/GET.rb +132 -0
- data/infod/Th/HEAD.rb +5 -0
- data/infod/Th/PATCH.rb +5 -0
- data/infod/Th/POST.rb +19 -0
- data/infod/Th/local.rb +22 -0
- data/infod/Th/uid.rb +24 -0
- data/infod/Th.rb +110 -0
- data/infod/W/audio.rb +56 -0
- data/infod/W/blog.rb +3 -0
- data/infod/W/cal.rb +110 -0
- data/infod/W/chat.rb +81 -0
- data/infod/W/color.rb +28 -0
- data/infod/W/core.rb +77 -0
- data/infod/W/css.rb +24 -0
- data/infod/W/csv.rb +13 -0
- data/infod/W/du.rb +35 -0
- data/infod/W/edit.rb +8 -0
- data/infod/W/examine/examine.rb +59 -0
- data/infod/W/examine/exhibit.rb +34 -0
- data/infod/W/examine/hist.rb +55 -0
- data/infod/W/examine/history.rb +19 -0
- data/infod/W/examine/normal.rb +31 -0
- data/infod/W/examine/protovis.rb +30 -0
- data/infod/W/examine/sw.rb +114 -0
- data/infod/W/examine/time/graph.rb +86 -0
- data/infod/W/examine/time/line.rb +24 -0
- data/infod/W/feed.rb +116 -0
- data/infod/W/find.rb +24 -0
- data/infod/W/forum.rb +3 -0
- data/infod/W/grep.rb +27 -0
- data/infod/W/html.rb +143 -0
- data/infod/W/image.rb +61 -0
- data/infod/W/json.rb +44 -0
- data/infod/W/kv.rb +66 -0
- data/infod/W/ls.rb +50 -0
- data/infod/W/mail.rb +248 -0
- data/infod/W/page.rb +30 -0
- data/infod/W/pdf.rb +16 -0
- data/infod/W/post.rb +9 -0
- data/infod/W/rdf.rb +32 -0
- data/infod/W/schema.rb +172 -0
- data/infod/W/search.rb +33 -0
- data/infod/W/shell.rb +30 -0
- data/infod/W/source.rb +35 -0
- data/infod/W/table.rb +87 -0
- data/infod/W/text.rb +94 -0
- data/infod/W/tree.rb +26 -0
- data/infod/W/vfs.rb +175 -0
- data/infod/W/wiki.rb +18 -0
- data/infod/W.rb +34 -0
- data/infod/Y.rb +17 -0
- data/infod/infod.rb +13 -0
- data/infod.rb +13 -0
- metadata +129 -0
data/bin/infod
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
case ARGV[0]
|
4
|
+
when 'apache'
|
5
|
+
ARGV.shift
|
6
|
+
ENV['apache']='y'
|
7
|
+
when /^(lo|local)$/
|
8
|
+
ARGV.shift
|
9
|
+
ARGV.unshift *%w{-p 80 -a 127.0.0.1}
|
10
|
+
when 'nginx'
|
11
|
+
ARGV.shift
|
12
|
+
ENV['nginx']='y'
|
13
|
+
else
|
14
|
+
ARGV.unshift *%w{-p 80}
|
15
|
+
end
|
16
|
+
ARGV.unshift *%w{--threaded}
|
17
|
+
unless ARGV[-1].match /(start|stop|restart|config|install)/
|
18
|
+
ARGV.push 'start'
|
19
|
+
end
|
20
|
+
|
21
|
+
%w{infod thin}.map{|l| require l}
|
22
|
+
|
23
|
+
module Rack
|
24
|
+
module Adapter
|
25
|
+
def self.guess _
|
26
|
+
:rack
|
27
|
+
end
|
28
|
+
def self.load _
|
29
|
+
Rack::Builder.new {
|
30
|
+
use Rack::Deflater
|
31
|
+
run E
|
32
|
+
}.to_app
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
Thin::Runner.new(ARGV).run!
|
data/config.ru
ADDED
data/infod/Es/fs.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
class E
|
2
|
+
|
3
|
+
# POSIX-filesystem index for triples
|
4
|
+
#
|
5
|
+
|
6
|
+
# index a triple
|
7
|
+
def index p,o
|
8
|
+
# normalize predicate typeclass (accept URI string or resources)
|
9
|
+
indexEdit E(p),
|
10
|
+
# literal -> URI conversion
|
11
|
+
(o.class == E ? o : E(p).literal(o)),
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
|
15
|
+
# index a triple - no input-cleanup
|
16
|
+
def indexEdit p,o,a
|
17
|
+
return if @noIndex
|
18
|
+
p.pIndex.noIndex[o,self,a]
|
19
|
+
end
|
20
|
+
def noIndex
|
21
|
+
@noIndex = 1
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
# subtree traverse
|
26
|
+
fn 'set/subtree',->d,r,m{
|
27
|
+
c =(r['c'].do{|c|c.to_i + 1} || 3).max(100) # one extra for start of next-page
|
28
|
+
o = r['d'] =~ /^a/ ? :asc : :desc # direction
|
29
|
+
('/'.E.take c, o, d.uri).do{|s| # take subtree
|
30
|
+
desc, asc = o == :desc ? # orient pagination hints
|
31
|
+
[s.pop, s[0]] : [s[0], s.pop]
|
32
|
+
m['prev'] = {'uri' => 'prev', 'url' => desc.url,'d' => 'desc'}
|
33
|
+
m['next'] = {'uri' => 'next', 'url' => asc.url, 'd' => 'asc'}
|
34
|
+
s }}
|
35
|
+
|
36
|
+
# subtree traverse index on p+o cursor
|
37
|
+
fn 'set/index',->d,r,m,f=:rangePO{
|
38
|
+
(# predicate
|
39
|
+
(f == :rangeP ? d : r['p']).expand.E.
|
40
|
+
# query
|
41
|
+
send f,
|
42
|
+
# count
|
43
|
+
(r['c']&&
|
44
|
+
r['c'].to_i.max(808)+1 || 22),
|
45
|
+
# direction
|
46
|
+
(r['d']&&
|
47
|
+
r['d'].match(/^(a|de)sc$/) &&
|
48
|
+
r['d'].to_sym ||
|
49
|
+
:desc),
|
50
|
+
# offset
|
51
|
+
r['offset'],
|
52
|
+
# object
|
53
|
+
(d if f == :rangePO)
|
54
|
+
).do{|s|
|
55
|
+
# pagination pointers
|
56
|
+
a,b = s[0], s.size > 1 && s.pop
|
57
|
+
desc,asc = r['d'] && r['d']=='asc' && [a,b]||[b,a]
|
58
|
+
# insert pointers in response-graph
|
59
|
+
m['prev']={'uri' => 'prev','url' => d.url,'d' => 'desc','offset' => desc.uri} if desc
|
60
|
+
m['next']={'uri' => 'next','url' => d.url,'d' => 'asc', 'offset' => asc.uri} if asc
|
61
|
+
s }}
|
62
|
+
F['set/indexPO']=F['set/index']
|
63
|
+
fn 'set/indexP',->d,r,m{Fn 'set/index',d,r,m,:rangeP}
|
64
|
+
|
65
|
+
# predicate index
|
66
|
+
def pIndex
|
67
|
+
'/index'.E.s self
|
68
|
+
end
|
69
|
+
|
70
|
+
# predicate-object index
|
71
|
+
def poIndex o
|
72
|
+
pIndex.s o
|
73
|
+
end
|
74
|
+
|
75
|
+
# predicate-object index lookup
|
76
|
+
def po o
|
77
|
+
pIndex[o.class == E ? o : literal(o)]
|
78
|
+
end
|
79
|
+
|
80
|
+
# range query - predicate
|
81
|
+
def rangeP n=8,d=:desc,s=nil,o=nil
|
82
|
+
puts "rangeP #{uri} count #{n} dir #{d} cursor #{s}"
|
83
|
+
pIndex.subtree(n,d,s).map &:ro
|
84
|
+
end
|
85
|
+
|
86
|
+
# range query - predicate-object
|
87
|
+
def rangePO n=8,d=:desc,s=nil,o
|
88
|
+
puts "rangePO #{uri} #{o} count #{n} dir #{d} cursor #{s}"
|
89
|
+
poIndex(o).subtree(n,d,s).map &:ro
|
90
|
+
end
|
91
|
+
|
92
|
+
# E -> [node]
|
93
|
+
def subtree *a
|
94
|
+
u.take *a
|
95
|
+
end
|
96
|
+
|
97
|
+
# E -> [E]
|
98
|
+
def take *a
|
99
|
+
no.take(*a).map &:E
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
class Pathname
|
106
|
+
|
107
|
+
# take N els from fs tree in sorted, depth-first order
|
108
|
+
def take count=1000, direction=:desc, offset=nil
|
109
|
+
|
110
|
+
# construct offset-path
|
111
|
+
offset = to_s + offset.gsub(/\/+/,'/').E.path if offset
|
112
|
+
|
113
|
+
# in-range indicator
|
114
|
+
ok = false
|
115
|
+
|
116
|
+
# result set
|
117
|
+
set=[]
|
118
|
+
|
119
|
+
# asc/desc operators
|
120
|
+
v,m={asc: [:id,:>=],
|
121
|
+
desc: [:reverse,:<=]}[direction]
|
122
|
+
|
123
|
+
# visitation function
|
124
|
+
visit=->nodes{
|
125
|
+
|
126
|
+
# sort nodes in asc or desc order
|
127
|
+
nodes.sort_by(&:to_s).send(v).each{|n|
|
128
|
+
ns = n.to_s
|
129
|
+
# have we got enough nodes?
|
130
|
+
return if 0 >= count
|
131
|
+
|
132
|
+
# continue if
|
133
|
+
(# already in-range
|
134
|
+
ok ||
|
135
|
+
# no offset specified
|
136
|
+
!offset ||
|
137
|
+
# offset satisfies in-range operator
|
138
|
+
(sz = [ns,offset].map(&:size).min
|
139
|
+
ns[0..sz-1].send(m,offset[0..sz-1]))) && (
|
140
|
+
if !(c = n.c).empty? # has children?
|
141
|
+
visit.(c) # visit children
|
142
|
+
else
|
143
|
+
count = count - 1 # decrement wanted-nodes count
|
144
|
+
set.push n # add node to result-set
|
145
|
+
ok = true # iterator is now within range
|
146
|
+
end )}}
|
147
|
+
|
148
|
+
visit.(c) # start
|
149
|
+
|
150
|
+
# result set
|
151
|
+
set
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
data/infod/Es/groonga.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
#watch __FILE__
|
2
|
+
class E
|
3
|
+
|
4
|
+
# http://groonga.org/ http://ranguba.org/
|
5
|
+
# https://github.com/groonga/groonga
|
6
|
+
# https://github.com/ranguba/rroonga
|
7
|
+
# default DB
|
8
|
+
def E.groonga
|
9
|
+
@groonga ||= (require 'groonga'
|
10
|
+
E['/E/groonga'].groonga
|
11
|
+
Groonga["E"] )
|
12
|
+
end
|
13
|
+
|
14
|
+
# load or create groongaDB at URI
|
15
|
+
def groonga
|
16
|
+
return Groonga::Database.open d if e # open db
|
17
|
+
dirname.dir # create containing dir
|
18
|
+
Groonga::Database.create(:path => d) # create db
|
19
|
+
Groonga::Schema.define{|s| # create schema
|
20
|
+
s.create_table("E",:type => :hash,:key_type => "ShortText"){|t|
|
21
|
+
t.short_text "uri"
|
22
|
+
t.short_text "graph"
|
23
|
+
t.text "content"
|
24
|
+
t.time "time" }
|
25
|
+
s.create_table("Bigram",
|
26
|
+
:type => :patricia_trie,
|
27
|
+
:key_normalize => true,
|
28
|
+
:default_tokenizer => "TokenBigram"){|t|
|
29
|
+
%w{graph content}.map{|c| t.index("E." + c) }}}
|
30
|
+
end
|
31
|
+
|
32
|
+
# index resource
|
33
|
+
def roonga graph="global", m = self.graph
|
34
|
+
g = E.groonga # db
|
35
|
+
m.map{|u,i|
|
36
|
+
r = g[u] || g.add(u) # create or load entry
|
37
|
+
r.uri = u # update data
|
38
|
+
r.graph = graph.to_s
|
39
|
+
r.content = i.to_s
|
40
|
+
r.time = i[E::Date][0].to_time
|
41
|
+
}
|
42
|
+
self
|
43
|
+
rescue Exception => x
|
44
|
+
$stderr.puts x,x.message
|
45
|
+
end
|
46
|
+
|
47
|
+
# remove
|
48
|
+
def unroonga
|
49
|
+
g = E.groonga
|
50
|
+
graph.keys.push(uri).map{|u|g[u].delete}
|
51
|
+
end
|
52
|
+
|
53
|
+
# query
|
54
|
+
fn 'graph/roonga',->d,e,m{
|
55
|
+
|
56
|
+
# db
|
57
|
+
ga = E.groonga
|
58
|
+
|
59
|
+
# search expression
|
60
|
+
q = e['q']
|
61
|
+
|
62
|
+
# context
|
63
|
+
g = e["context"] || d.env['SERVER_NAME']
|
64
|
+
|
65
|
+
# offset
|
66
|
+
start = e['start'].do{|c|c.to_i} || 0
|
67
|
+
|
68
|
+
# number of results
|
69
|
+
c = e['c'].do{|c|c.to_i.max(1000).min(0)} || 8
|
70
|
+
|
71
|
+
# exec expression
|
72
|
+
r = q ? ga.select{|r|(r['graph'] == g) & r["content"].match(q)} : # expression if exists
|
73
|
+
ga.select{|r| r['graph'] == g} # ordered set (index date-range)
|
74
|
+
|
75
|
+
# are further results traversible?
|
76
|
+
down = r.size > start+c
|
77
|
+
up = !(start<=0)
|
78
|
+
|
79
|
+
# sort results
|
80
|
+
r = r.sort(e.has_key?('score') ? [["_score"]] : [["time", "descending"]],:offset => start,:limit => c)
|
81
|
+
|
82
|
+
# pagination resources
|
83
|
+
m['prev']={'uri' => 'prev','url' => '/search','start' => start + c, 'c' => c} if down
|
84
|
+
m['next']={'uri' => 'next','url' => '/search','start' => start - c, 'c' => c} if up
|
85
|
+
|
86
|
+
# search-result identifiers
|
87
|
+
r = r.map{|r| r['.uri'].E }
|
88
|
+
|
89
|
+
# fragment identifiers
|
90
|
+
m['frag'] = {'uri' => 'frag', 'res' => r}
|
91
|
+
|
92
|
+
# containing documents
|
93
|
+
r.map(&:docs).flatten.uniq.map{|r| m[r.uri] = r.env e}
|
94
|
+
|
95
|
+
# no 404 on 0 results - searchbox view
|
96
|
+
m['/s']={'uri'=>'/s'} if m.empty?
|
97
|
+
|
98
|
+
m # result set
|
99
|
+
}
|
100
|
+
|
101
|
+
end
|
data/infod/Es/redis.rb
ADDED
data/infod/Es/sqlite.rb
ADDED
data/infod/Es.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
%w{fs groonga redis}.map{|e|require_relative 'Es/'+e}
|
2
|
+
|
3
|
+
class E
|
4
|
+
|
5
|
+
# accumulate a graph recursively along set-membership arc
|
6
|
+
def walk p,m={}
|
7
|
+
graph m # accumulative graph
|
8
|
+
o = [] # resources to visit
|
9
|
+
o.concat m[uri][p] # outgoing arc targets
|
10
|
+
o.concat (E p).po self # incoming arc sources
|
11
|
+
o.map{|r| # walk
|
12
|
+
r.E.walk p,m unless m[r.uri]}
|
13
|
+
m
|
14
|
+
end
|
15
|
+
|
16
|
+
# random leaf
|
17
|
+
def randomLeaf
|
18
|
+
c.empty? && self || c.r.randomLeaf
|
19
|
+
end
|
20
|
+
fn 'set/randomLeaf',->d,e,m{[d.randomLeaf]}
|
21
|
+
fn 'req/randomLeaf',->e,r{[302, {Location: e.randomLeaf.uri},[]]}
|
22
|
+
|
23
|
+
# Graph -> [Predicate]
|
24
|
+
def E.graphProperties g
|
25
|
+
g.values.select{|v|v.respond_to? :keys}.map(&:keys).flatten.uniq
|
26
|
+
end
|
27
|
+
|
28
|
+
fn 'filter/p',->e,m,_{
|
29
|
+
a=Hash[*e['p'].split(/,/).map(&:expand).map{|p|[p,true]}.flatten]
|
30
|
+
m.values.map{|r|
|
31
|
+
r.delete_if{|p,o|!a[p]}}}
|
32
|
+
|
33
|
+
fn 'filter/frag',->e,m,r{
|
34
|
+
f = [r.uri].concat m['frag']['res']
|
35
|
+
m.keys.map{|u|
|
36
|
+
m.delete u unless f.member? u}}
|
37
|
+
|
38
|
+
fn 'filter/basic',->o,m,_{
|
39
|
+
d=m.values
|
40
|
+
o['match'] && (p=o['matchP'].expand
|
41
|
+
d=d.select{|r|r[p].do{|p|(p.class==Array ? p[0] : p).to_s.match o['match']}})
|
42
|
+
o['min'] && (min=o['min'].to_f
|
43
|
+
p=o['minP'].expand
|
44
|
+
d=d.select{|r|r[p].do{|p|(p.class==Array ? p[0] : p).to_f >= min }})
|
45
|
+
o['max'] && (max=o['max'].to_f
|
46
|
+
p=o['maxP'].expand
|
47
|
+
d=d.select{|r|r[p].do{|p|(p.class==Array ? p[0] : p).to_f <= max }})
|
48
|
+
o['sort'] && (p=o['sort'].expand
|
49
|
+
_ = d.partition{|r|r[p]}
|
50
|
+
d =_[0].sort_by{|r|r[p]}.concat _[1] rescue d)
|
51
|
+
o['sortN'] && (p=o['sortN'].expand
|
52
|
+
_ = d.partition{|r|r[p]}
|
53
|
+
d =_[0].sort_by{|r|
|
54
|
+
(r[p].class==Array && r[p] || [r[p]])[0].do{|d|
|
55
|
+
d.class==String && d.to_i || d
|
56
|
+
}
|
57
|
+
}.concat _[1])
|
58
|
+
o.has_key?('reverse') && d.reverse!
|
59
|
+
m.clear;d.map{|r|m[r['uri']]=r}}
|
60
|
+
|
61
|
+
def self.filter o,m,r
|
62
|
+
o['filter'].do{|f|f.split(/,/).map{|f|Fn 'filter/'+f,o,m,r}}
|
63
|
+
Fn'filter/basic',o,m,r if o.has_any_key ['reverse','sort','max','min','match']
|
64
|
+
m
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/infod/H.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
def H _
|
2
|
+
case _
|
3
|
+
when Hash then
|
4
|
+
'<'+(_[:_]||:div).to_s+(_.keys-[:_,:c]).map{|a|
|
5
|
+
' '+a.to_s+'='+"'"+
|
6
|
+
_[a].to_s.hsub({"'"=>'%27',
|
7
|
+
'>'=>'%3E',
|
8
|
+
'<'=>'%3C'})+"'"}.join+'>'+
|
9
|
+
(_[:c] ? (H _[:c]) : '')+
|
10
|
+
(_[:_] == :link ? '' : ('</'+(_[:_]||:div).to_s+'>'))
|
11
|
+
when Array then
|
12
|
+
_.map{|n|H n}.join
|
13
|
+
else
|
14
|
+
_.to_s
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class H
|
19
|
+
|
20
|
+
def H.js a,inline=false
|
21
|
+
p=a+'.js'
|
22
|
+
inline ? {_: :script, c: p.E.r} :
|
23
|
+
{_: :script, type: "text/javascript", src: p} end
|
24
|
+
|
25
|
+
def H.once e,n,*h
|
26
|
+
return if e[n]
|
27
|
+
e[n]=true
|
28
|
+
h end
|
29
|
+
end
|
data/infod/K.rb
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
class E
|
2
|
+
|
3
|
+
FSbase = `pwd`.chomp
|
4
|
+
Prefix = '/@' # resolver for non-local and non-HTTP URIs
|
5
|
+
S = '<>' # path separator
|
6
|
+
|
7
|
+
# frequently-used URIs
|
8
|
+
W3 = 'http://www.w3.org/'
|
9
|
+
Purl = 'http://purl.org/'
|
10
|
+
FOAF = "http://xmlns.com/foaf/0.1/"
|
11
|
+
SIOC = 'http://rdfs.org/sioc/ns#'
|
12
|
+
SIOCt = 'http://rdfs.org/sioc/types#'
|
13
|
+
MIMEtype = 'http://www.iana.org/assignments/media-types/'
|
14
|
+
DC = Purl + 'dc/terms/'
|
15
|
+
Date = DC + 'date'
|
16
|
+
Modified = DC + 'modified'
|
17
|
+
Title = DC + 'title'
|
18
|
+
Name = FOAF + 'name'
|
19
|
+
To = SIOC + 'addressed_to'
|
20
|
+
Creator = SIOC + 'has_creator'
|
21
|
+
Content = SIOC + 'content'
|
22
|
+
Type = W3 + "1999/02/22-rdf-syntax-ns#type"
|
23
|
+
RDFs = W3 + '2000/01/rdf-schema#'
|
24
|
+
HTTP = W3 + '2011/http#'
|
25
|
+
Posix = W3 + 'ns/posix/'
|
26
|
+
Stat = Posix + 'stat#'
|
27
|
+
Label = RDFs + 'label'
|
28
|
+
EXIF = 'http://www.w3.org/2003/12/exif/ns#'
|
29
|
+
Audio = 'http://www.semanticdesktop.org/ontologies/nid3/#'
|
30
|
+
|
31
|
+
# file-name extension -> MIME type
|
32
|
+
MIME={
|
33
|
+
aif: 'audio/aif',
|
34
|
+
ans: 'text/ansi',
|
35
|
+
atom: 'application/atom+xml',
|
36
|
+
avi: 'video/avi',
|
37
|
+
e: 'application/json+rdf',
|
38
|
+
coffee: 'text/plain',
|
39
|
+
css: 'text/css',
|
40
|
+
csv: 'text/comma-separated-values',
|
41
|
+
doc: 'application/word',
|
42
|
+
flv: 'video/flv',
|
43
|
+
for: 'application/fortran',
|
44
|
+
gemspec: 'application/ruby',
|
45
|
+
gif: 'image/gif',
|
46
|
+
hs: 'application/haskell',
|
47
|
+
html: 'text/html',
|
48
|
+
ico: 'image/x-ico',
|
49
|
+
jpeg: 'image/jpeg',
|
50
|
+
jpg: 'image/jpeg',
|
51
|
+
js: 'application/javascript',
|
52
|
+
json: 'application/json',
|
53
|
+
log: 'text/log',
|
54
|
+
markdown: 'application/markdown',
|
55
|
+
m4a: 'audio/mp4',
|
56
|
+
md: 'application/markdown',
|
57
|
+
mkv: 'video/matroska',
|
58
|
+
mp3: 'audio/mpeg',
|
59
|
+
mp4: 'video/mp4',
|
60
|
+
mpg: 'video/mpg',
|
61
|
+
n3: 'text/rdf+n3',
|
62
|
+
nfo: 'text/nfo',
|
63
|
+
nt: 'text/ntriples',
|
64
|
+
ntriples: 'text/ntriples',
|
65
|
+
owl: 'application/rdf+xml',
|
66
|
+
pdf: 'application/pdf',
|
67
|
+
png: 'image/png',
|
68
|
+
py: 'application/python',
|
69
|
+
rb: 'application/ruby',
|
70
|
+
ru: 'application/ruby',
|
71
|
+
rdf: 'application/rdf+xml',
|
72
|
+
rtf: 'text/rtf',
|
73
|
+
ssv: 'text/semicolon-separated-values',
|
74
|
+
textile: 'application/textile',
|
75
|
+
tsv: 'text/tab-separated-values',
|
76
|
+
ttl: 'text/turtle',
|
77
|
+
txt: 'text/plain',
|
78
|
+
u: 'application/uri',
|
79
|
+
wav: 'audio/wav',
|
80
|
+
wmv: 'video/wmv',
|
81
|
+
xlsx: 'application/excel',
|
82
|
+
}
|
83
|
+
|
84
|
+
# MIME type -> triplrFn
|
85
|
+
MIMEsource={
|
86
|
+
'application/atom+xml' => [:triplrFeed],
|
87
|
+
'application/markdown' => [:triplrMarkdown],
|
88
|
+
'application/org' => [:triplrOrg],
|
89
|
+
'application/rdf+xml' => [:triplrRDFformats,:rdfxml],
|
90
|
+
'application/json' => [:triplrJSON],
|
91
|
+
'application/pdf' => [:triplrPDF],
|
92
|
+
'application/textile' => [:triplrTextile],
|
93
|
+
'application/uri' => [:triplrUriList],
|
94
|
+
'application/word' => [:triplrWord],
|
95
|
+
'audio/mp4' => [:triplrStdOut,'faad -i',Audio],
|
96
|
+
'audio/mpeg' => [:triplrStdOut,'id3info',Audio,/\((.*?)\)$/],
|
97
|
+
'audio' => [:triplrStdOut,'sndfile-info',Audio],
|
98
|
+
'image' => [:triplrStdOut,'exiftool',EXIF],
|
99
|
+
'message/rfc822' => [:triplrMail],
|
100
|
+
'text/ansi' => [:triplrANSI],
|
101
|
+
'text/comma-separated-values'=>[:triplrCSV,/,/],
|
102
|
+
'text/html' => [:triplrRDFformats, :rdfa],
|
103
|
+
'text/log' => [:triplrLog],
|
104
|
+
'text/nfo' => [:triplrHref,'cp437'],
|
105
|
+
'text/ntriples' => [:triplrRDFformats, :ntriples],
|
106
|
+
'text/plain' => [:triplrHref],
|
107
|
+
'text/rtf' => [:triplrRTF],
|
108
|
+
'text/semicolon-separated-values'=>[:triplrCSV,/;/],
|
109
|
+
'text/tab-separated-values'=>[:triplrCSV,/\t/],
|
110
|
+
'text/turtle' => [:triplrRDFformats,:turtle],
|
111
|
+
}
|
112
|
+
|
113
|
+
# MIME type -> formatted content
|
114
|
+
Render='render/'
|
115
|
+
fn Render+'application/ld+json',->d,_=nil{E.renderRDF d, :jsonld}
|
116
|
+
fn Render+'application/rdf+xml',->d,_=nil{E.renderRDF d, :rdfxml}
|
117
|
+
fn Render+'text/ntriples',->d,_=nil{E.renderRDF d, :ntriples}
|
118
|
+
fn Render+'text/turtle', ->d,_=nil{E.renderRDF d, :turtle}
|
119
|
+
fn Render+'text/rdf+n3', ->d,_=nil{E.renderRDF d, :n3}
|
120
|
+
fn Render+'text/n3', ->d,_=nil{E.renderRDF d, :n3}
|
121
|
+
|
122
|
+
# render a view even if requested file exists
|
123
|
+
MIMEcook={
|
124
|
+
'application/atom+xml' => true,
|
125
|
+
'application/markdown' => true,
|
126
|
+
'application/json' => true,
|
127
|
+
'application/json+rdf' => true,
|
128
|
+
'application/org' => true,
|
129
|
+
'application/textile' => true,
|
130
|
+
'application/word' => true,
|
131
|
+
'message/rfc822'=> true,
|
132
|
+
'text/ansi'=>true,
|
133
|
+
'text/log'=>true,
|
134
|
+
'text/nfo'=>true,
|
135
|
+
'text/rtf'=>true,
|
136
|
+
}
|
137
|
+
%w{c ruby haskell php python}.map{|t|
|
138
|
+
%w{application/ text/x-}.map{|m|
|
139
|
+
MIMEcook[m+t] = true
|
140
|
+
}}
|
141
|
+
|
142
|
+
# short -> full URI
|
143
|
+
Abbrev={
|
144
|
+
"dc" => DC,
|
145
|
+
"foaf" => FOAF,
|
146
|
+
"rdf" => W3+"1999/02/22-rdf-syntax-ns#",
|
147
|
+
"rdfs" => RDFs,
|
148
|
+
"sioc" => SIOC,
|
149
|
+
"stat" => Stat,
|
150
|
+
}
|
151
|
+
|
152
|
+
# expose these literals as a path-name
|
153
|
+
Literal={}
|
154
|
+
[Purl+'dc/elements/1.1/date',
|
155
|
+
Date,
|
156
|
+
DC+'created',
|
157
|
+
Modified,
|
158
|
+
].map{|f|Literal[f]=true}
|
159
|
+
|
160
|
+
def mime
|
161
|
+
@mime ||= (# dereferenced symlink
|
162
|
+
f = readlink
|
163
|
+
|
164
|
+
# filename extension
|
165
|
+
x = f.ext.downcase.to_sym
|
166
|
+
|
167
|
+
# directory?
|
168
|
+
if d?
|
169
|
+
"inode/directory"
|
170
|
+
# local MIME-types table
|
171
|
+
elsif MIME[x]
|
172
|
+
# puts "found mime for #{x} -> #{MIME[x]}"
|
173
|
+
MIME[x]
|
174
|
+
# Rack MIME-types table
|
175
|
+
elsif Rack::Mime::MIME_TYPES[t = '.' + x.to_s]
|
176
|
+
Rack::Mime::MIME_TYPES[t]
|
177
|
+
# procmail uses a prefix not an extension
|
178
|
+
elsif f.base.index('msg.')==0
|
179
|
+
"message/rfc822"
|
180
|
+
# ask FILE(1)
|
181
|
+
elsif f.e
|
182
|
+
`file --mime-type -b #{f.sh}`.chomp
|
183
|
+
# default
|
184
|
+
else
|
185
|
+
"application/octet-stream"
|
186
|
+
end)
|
187
|
+
end
|
188
|
+
|
189
|
+
def == u
|
190
|
+
to_s == u.to_s
|
191
|
+
end
|
192
|
+
|
193
|
+
Nginx = ENV['nginx']
|
194
|
+
Apache = ENV['apache']
|
195
|
+
Version = 'http://web.whats-your.name/www/'
|
196
|
+
|
197
|
+
end
|