infod 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|