infod 0.0.2 → 0.0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/infod.rb +51 -12
  2. data/infod/{Th/404.rb → 404.rb} +4 -16
  3. data/infod/500.rb +53 -0
  4. data/infod/GET.rb +104 -0
  5. data/infod/HEAD.rb +23 -0
  6. data/infod/HTTP.rb +105 -0
  7. data/infod/{Th/PATCH.rb → PATCH.rb} +0 -0
  8. data/infod/POST.rb +34 -0
  9. data/infod/audio.rb +30 -0
  10. data/infod/blog.rb +34 -0
  11. data/infod/cal.rb +72 -0
  12. data/infod/{Es/code.rb → code.rb} +7 -4
  13. data/infod/constants.rb +55 -0
  14. data/infod/{Es/css.rb → css.rb} +0 -0
  15. data/infod/{Es/csv.rb → csv.rb} +0 -0
  16. data/infod/{Es/du.rb → du.rb} +0 -0
  17. data/infod/edit.rb +73 -0
  18. data/infod/{H/facets.rb → facets.rb} +20 -11
  19. data/infod/{Es/feed.rb → feed.rb} +17 -16
  20. data/infod/{Es/find.rb → find.rb} +2 -3
  21. data/infod/forum.rb +13 -0
  22. data/infod/{Es/fs.rb → fs.rb} +5 -2
  23. data/infod/glob.rb +26 -0
  24. data/infod/graph.rb +131 -0
  25. data/infod/{Es/grep.rb → grep.rb} +3 -3
  26. data/infod/{Es/groonga.rb → groonga.rb} +35 -26
  27. data/infod/{H/histogram.rb → histogram.rb} +23 -16
  28. data/infod/html.rb +231 -0
  29. data/infod/{Es/image.rb → image.rb} +16 -26
  30. data/infod/{Es/index.rb → index.rb} +44 -49
  31. data/infod/infod.rb +51 -12
  32. data/infod/json.rb +38 -0
  33. data/infod/{Es/kv.rb → kv.rb} +3 -9
  34. data/infod/{Y.rb → lambda.rb} +18 -1
  35. data/infod/ls.rb +49 -0
  36. data/infod/mail.rb +108 -0
  37. data/infod/{Es/man.rb → man.rb} +3 -15
  38. data/infod/{H/microblog.rb → microblog.rb} +22 -31
  39. data/infod/{K.rb → mime.rb} +68 -52
  40. data/infod/{N.rb → names.rb} +77 -56
  41. data/infod/page.rb +13 -0
  42. data/infod/postscript.rb +26 -0
  43. data/infod/rdf.rb +51 -0
  44. data/infod/{Rb.rb → ruby.rb} +18 -33
  45. data/infod/{Es/schema.rb → schema.rb} +23 -8
  46. data/infod/{Es/search.rb → search.rb} +5 -11
  47. data/infod/{Es/sh.rb → sh.rb} +0 -0
  48. data/infod/{Es/text.rb → text.rb} +33 -29
  49. data/infod/{H/threads.rb → threads.rb} +20 -27
  50. data/infod/{H/time.rb → time.rb} +14 -34
  51. data/infod/{H/wiki.rb → wiki.rb} +0 -0
  52. metadata +53 -64
  53. data/config.ru +0 -3
  54. data/infod/Es.rb +0 -31
  55. data/infod/Es/filter.rb +0 -75
  56. data/infod/Es/glob.rb +0 -22
  57. data/infod/Es/html.rb +0 -271
  58. data/infod/Es/in.rb +0 -68
  59. data/infod/Es/json.rb +0 -68
  60. data/infod/Es/ls.rb +0 -58
  61. data/infod/Es/mail.rb +0 -87
  62. data/infod/Es/mime.rb +0 -59
  63. data/infod/Es/out.rb +0 -52
  64. data/infod/Es/pager.rb +0 -34
  65. data/infod/Es/pdf.rb +0 -19
  66. data/infod/Es/rdf.rb +0 -35
  67. data/infod/H.rb +0 -15
  68. data/infod/H/audio.rb +0 -19
  69. data/infod/H/blog.rb +0 -15
  70. data/infod/H/cal.rb +0 -81
  71. data/infod/H/edit.rb +0 -88
  72. data/infod/H/forum.rb +0 -4
  73. data/infod/H/hf.rb +0 -114
  74. data/infod/H/mail.rb +0 -92
  75. data/infod/H/who.rb +0 -30
  76. data/infod/Th.rb +0 -36
  77. data/infod/Th/500.rb +0 -41
  78. data/infod/Th/GET.rb +0 -62
  79. data/infod/Th/HEAD.rb +0 -5
  80. data/infod/Th/POST.rb +0 -39
  81. data/infod/Th/perf.rb +0 -37
  82. data/infod/Th/uid.rb +0 -24
  83. data/infod/Th/util.rb +0 -89
data/infod.rb CHANGED
@@ -1,12 +1,51 @@
1
-
2
- # the table of elements
3
-
4
- # Es infrastructure
5
- # H HTML
6
- # K constants
7
- # N naming
8
- # Rb Ruby native-types
9
- # Th HTTP
10
- # Y lambdas
11
-
12
- %w{Y K Rb N Es H Th}.map{|e| require 'infod/' + e}
1
+ %w{
2
+ constants
3
+ lambda
4
+ mime
5
+ 404
6
+ 500
7
+ audio
8
+ blog
9
+ cal
10
+ code
11
+ css
12
+ csv
13
+ du
14
+ edit
15
+ facets
16
+ feed
17
+ find
18
+ forum
19
+ fs
20
+ GET
21
+ glob
22
+ graph
23
+ grep
24
+ groonga
25
+ HEAD
26
+ histogram
27
+ html
28
+ HTTP
29
+ image
30
+ index
31
+ json
32
+ kv
33
+ ls
34
+ mail
35
+ man
36
+ microblog
37
+ names
38
+ page
39
+ PATCH
40
+ POST
41
+ postscript
42
+ rdf
43
+ ruby
44
+ schema
45
+ search
46
+ sh
47
+ text
48
+ threads
49
+ time
50
+ wiki
51
+ }.map{|e|require_relative e}
@@ -1,4 +1,3 @@
1
- watch __FILE__
2
1
  class E
3
2
 
4
3
  E404 = 'req/'+HTTP+'404'
@@ -16,8 +15,8 @@ r.map{|k,v| s[Header + k] = k == 'uri' ? v : [v] }
16
15
  s[Header+'ACCEPT'] = [r.accept]
17
16
  s[Type] = [E[HTTP+'Response']]
18
17
  s[HTTP+'statusCodeValue'] = [404]
19
- s[Header+'HTTP_HOST'] = [E['http://' + s[Header+'HTTP_HOST'][0]]]
20
- s[Edit] = [E[r['REQUEST_PATH']+'?view=edit&graph=editable']]
18
+ s[Header+'HTTP_HOST'] = [E['http://' + s[Header+'HTTP_HOST'][0]]] if s[Header+'HTTP_HOST']
19
+ s[Edit] = [E[r['REQUEST_PATH']+'?graph=edit']]
21
20
  s['#query'] = [r.q]
22
21
  s['#seeAlso'] = [e.parent,*e.a('*').glob]
23
22
  r.q['view'] = '404'
@@ -28,21 +27,10 @@ s[HTTP+'statusCodeValue'] = [404]
28
27
  [H.css('/css/404'),{_: :style, c: "a {background-color:#{E.cs}}"},
29
28
  d.html]}
30
29
 
31
- # a small non-empty graph
30
+ # a placeholder graph
31
+ # useful for initializing an editor on an empty resource
32
32
  fn 'protograph/_',->d,_,m{
33
33
  m[d.uri] = {}
34
34
  rand.to_s.h}
35
35
 
36
- # check response-codes for a list of URIs (linebreak-separated *.u files)
37
- def checkURIs
38
- r = uris.select{|u|u.to_s.match /^http/}.map{|u|
39
- c = [`curl -IsA 404? "#{u}"`.lines.to_a[0].match(/\d{3}/)[0].to_i,u] # HEAD
40
- puts c.join ' '
41
- c } # status, uri tuple
42
- puts "\n\n"
43
- r.map{|c|
44
- # show anomalies
45
- puts c.join(' ') unless c[0] == 200 }
46
- end
47
-
48
36
  end
data/infod/500.rb ADDED
@@ -0,0 +1,53 @@
1
+ #watch __FILE__
2
+ class E
3
+
4
+ Errors ||= {}
5
+
6
+ fn 'E500',->x,e{
7
+ stack = x.backtrace
8
+ $stderr.puts [500, e['REQUEST_URI'], x.class, x.message, stack[0]].join ' '
9
+
10
+ Errors[e['uri']] ||= {}
11
+ Errors[e['uri']][:time] = Time.now
12
+ Errors[e['uri']][:env] = [e, x.class.to_s, x.message, stack.join('<br>')]
13
+
14
+ [500,{'Content-Type'=>'text/html'},
15
+ [H[{_: :html,
16
+ c: [{_: :head,
17
+ c: [{_: :title, c: 500},(H.css '/css/500')]},
18
+ {_: :body,
19
+ c: [{_: :h1, c: 500},
20
+ {_: :table,
21
+ c: [{_: :tr,c: [{_: :td, c: {_: :b, c: x.class}},{_: :td, class: :space},{_: :td, class: :message, c: x.message.hrefs}]},
22
+ stack.map{|f| p = f.split /:/, 3
23
+ {_: :tr,
24
+ c: [{_: :td, class: :path, c: p[0].abbrURI},
25
+ {_: :td, class: :index, c: p[1]},
26
+ {_: :td, class: :context, c: (p[2]||'').hrefs}].cr}}.cr]}]}]}]]]}
27
+
28
+ F['/500/GET'] = ->e,r{H([Errors.sort_by{|u,r|r[:time]}.reverse.html,H.css('/css/500')]).hR}
29
+ F['/500/test/GET'] = ->e,r{1/0}
30
+
31
+ F['view/'+COGS+'Exception']=->e,r{
32
+ e.values.map{|e|
33
+ {style: 'border-radius:1em;background-color:#333;color:#eee;float:left;max-width:42em',
34
+ c: [{_: :h2, c: "<b style='background-color:#f00'>&#0191;</b>"+(e[Title]||[]).to_s},
35
+ e[Content]||''
36
+ ]}}}
37
+
38
+ # filesystem takes priority over this if it's found
39
+ fn '/css/500.css/GET',->e,r{
40
+ [200,{'Content-Type'=>'text/css'},["
41
+ body {margin:0; font-family: sans-serif; background-color:#fff; color:#000}
42
+ h1 {padding:.2em; background-color:#f00; color:#fff; margin:0}
43
+ div {display:inline}
44
+ table {border-spacing:0;margin:0}
45
+ b {background-color:#eee;color:#500;padding:.1em .3em .1em .3em}
46
+ .frag {font-weight:bold; color:#000; background-color:#{E.cs}}
47
+ td.space {background-color:#ddd}
48
+ td.message {background-color:#009;color:#fff}
49
+ td.path {text-align:right}
50
+ td.index {text-align:right;border-color:#000;border-width:0 0 .1em 0;border-style:dotted;background-color:#ddd;color:#000}
51
+ td.context {border-color:#ddd;border-width:0 0 .1em 0;border-style:dotted;padding:.15em}"]]}
52
+
53
+ end
data/infod/GET.rb ADDED
@@ -0,0 +1,104 @@
1
+ class E
2
+
3
+ Apache = ENV['apache']
4
+ Nginx = ENV['nginx']
5
+
6
+ def GET
7
+ if reqFn = @r.q['y'].do{|r| F['req/'+r] }
8
+ # bespoke handler
9
+ reqFn[self,@r]
10
+
11
+ elsif file = [self,pathSegment].compact.find(&:f)
12
+
13
+ # file exists, but client might not accept its MIME, or want it transformed to another MIME
14
+ a = @r.accept.values.flatten
15
+ accepted = a.empty? || (a.member? file.mimeP) || (a.member? '*/*')
16
+ (!accepted || MIMEcook[file.mimeP] || @r.q.has_key?('view')) ?
17
+ resource : (file.env @r).getFile
18
+
19
+ else
20
+ resource
21
+ end
22
+ end
23
+
24
+ def getFile
25
+ @r['ETag'] = [m,size].h
26
+ maybeSend mimeP,->{self},:link
27
+ end
28
+
29
+ def resource
30
+ # bubble up site then global tree until handled (false return-value to pass)
31
+ pathSegment.do{|path|
32
+ lambdas = path.cascade.map{|p| p.uri.t + 'GET' }
33
+ ['http://'+@r['SERVER_NAME'],""].map{|h| lambdas.map{|p|
34
+ F[h + p].do{|fn| fn[self,@r].do{|r|
35
+ $stdout.puts [r[0],'http://'+@r['SERVER_NAME']+@r['REQUEST_URI'],@r['HTTP_USER_AGENT'],@r['HTTP_REFERER'],@r.format].join ' '
36
+ return r
37
+ }}}}}
38
+ response
39
+ end
40
+
41
+ def response
42
+
43
+ m = {}
44
+
45
+ # graph identity (model)
46
+ g = @r.q['graph']
47
+ graphID = (g && F['protograph/' + g] || F['protograph/'])[self,@r.q,m]
48
+
49
+ return F[E404][self,@r] if m.empty?
50
+
51
+ # response identity (view)
52
+ @r['ETag'] ||= [@r.q['view'].do{|v|F['view/' + v] && v}, graphID, @r.format, Watch].h
53
+
54
+ maybeSend @r.format, ->{
55
+
56
+ # response
57
+ r = E'/E/req/' + @r['ETag'].dive
58
+ if r.e # exists
59
+ r
60
+ else
61
+
62
+ # graph
63
+ c = E '/E/graph/' + graphID.dive
64
+ if c.e # exists
65
+ m = c.r true
66
+ else
67
+ # construct
68
+ (g && F['graph/' + g] || F['graph/'])[self, @r.q,m]
69
+ # cache
70
+ c.w m,true
71
+ end
72
+ # cache < construct
73
+ r.w render @r.format, m, @r
74
+ end }
75
+ end
76
+
77
+ def send?
78
+ !((m=@r['HTTP_IF_NONE_MATCH']) && m.strip.split(/\s*,\s*/).include?(@r['ETag']))
79
+ end
80
+
81
+ def maybeSend m, b, iR = false; c = 200
82
+ send? ? # does agent have this version?
83
+ b[].do{|b| # continue with response
84
+
85
+ h = {'Content-Type'=> m,
86
+ 'ETag'=> @r['ETag']}
87
+
88
+ h.update({'Cache-Control' => 'no-transform'}) if m.match /^(audio|image|video)/ # already compresed
89
+ h.update({'Link' => '<' + @r['uri'] + '?view>; rel=meta'}) if iR # link to description
90
+ # h.update({'MS-Author-Via' => 'SPARQL'}) # authoring preference
91
+
92
+ # frontend-specific response handlers
93
+ b.class == E ? (Nginx ? # nginx chosen?
94
+ [c,h.update({'X-Accel-Redirect' => '/fs' + b.path}),[]] : # Nginx handler
95
+ Apache ? # Apache chosen?
96
+ [c,h.update({'X-Sendfile' => b.d}),[]] : # Apache handler
97
+ (r = Rack::File.new nil # Rack handler
98
+ r.instance_variable_set '@path',b.d # configure Rack response
99
+ r.serving(@r).do{|s,m,b|[(s == 200 ? c : s),m.update(h),b]})) :
100
+ [c, h, b]} : # normal response
101
+ [304,{},[]] # client has response
102
+ end
103
+
104
+ end
data/infod/HEAD.rb ADDED
@@ -0,0 +1,23 @@
1
+ class E
2
+
3
+ def HEAD
4
+ self.GET.do{|s,h,b|[s,h,[]]}
5
+ end
6
+
7
+ def OPTIONS
8
+ [200,{},[]]
9
+ end
10
+
11
+ # HEAD response-codes on a (.u) list of URIs
12
+ def checkURIs
13
+ r = uris.select{|u|u.to_s.match /^http/}.map{|u|
14
+ c = [`curl -IsA 404? "#{u}"`.lines.to_a[0].match(/\d{3}/)[0].to_i,u] # HEAD
15
+ puts c.join ' '
16
+ c } # status, uri tuple
17
+ puts "\n\n"
18
+ r.map{|c|
19
+ # show anomalies
20
+ puts c.join(' ') unless c[0] == 200 }
21
+ end
22
+
23
+ end
data/infod/HTTP.rb ADDED
@@ -0,0 +1,105 @@
1
+ #watch __FILE__
2
+ require 'rack'
3
+
4
+ class E
5
+
6
+ def E.call e
7
+ dev
8
+ e.extend Th
9
+ e['HTTP_X_FORWARDED_HOST'].do{|h| e['SERVER_NAME'] = h }
10
+ p = e['REQUEST_PATH'].force_encoding 'UTF-8'
11
+
12
+ uri = CGI.unescape((p.index(URIURL) == 0) ? p[URIURL.size..-1] : ('http://'+e['SERVER_NAME']+(p.gsub '+','%2B'))).E.env e
13
+
14
+ uri.inside ? ( e['uri'] = uri.uri
15
+ uri.send e.verb ) : [403,{},[]]
16
+
17
+ rescue Exception => x
18
+ F['E500'][x,e]
19
+ end
20
+
21
+ end
22
+
23
+ class String
24
+
25
+ # querystring parse
26
+ def qp
27
+ d={}
28
+ split(/&/).map{|e|
29
+ k,v=e.split(/=/,2).map{|x|
30
+ CGI.unescape x}
31
+ d[k]=v}
32
+ d
33
+ end
34
+
35
+ def hR
36
+ [200, {'Content-Type'=>'text/html; charset=utf-8'}, [self]]
37
+ end
38
+
39
+ end
40
+
41
+ module Th
42
+
43
+ # query-string
44
+ def qs
45
+ (['GET','HEAD'].member? verb) ? self['QUERY_STRING'] : self['rack.input'].read
46
+ end
47
+
48
+ # parsed query-string
49
+ def q
50
+ @q ||= (qs||'').qp
51
+ end
52
+
53
+ # Accept header -> Hash
54
+ def accept_ k=''
55
+ d={}
56
+ self['HTTP_ACCEPT'+k].do{|k|
57
+ (k.split /,/).map{|e| # each pair
58
+ f,q = e.split /;/ # split MIME from q value
59
+ i = q && q.split(/=/)[1].to_f || 1.0
60
+ d[i] ||= []; d[i].push f.strip}} # append
61
+ d
62
+ end
63
+
64
+ def format
65
+ @format ||= conneg
66
+ end
67
+
68
+ def conneg
69
+ # extension
70
+ { '.html' => 'text/html',
71
+ '.jsonld' => 'application/ld+json',
72
+ '.nt' => 'text/ntriples',
73
+ '.n3' => 'text/n3',
74
+ '.ttl' => 'text/turtle',
75
+ }[File.extname self['uri']].do{|mime|
76
+ return mime}
77
+
78
+ # Accept header
79
+ accept.sort.reverse.map{|q,mimes|
80
+ mimes.map{|mime|
81
+ return mime if E::F[E::Render+mime]}}
82
+ 'text/html'
83
+ end
84
+
85
+ def accept; @accept ||= accept_ end
86
+
87
+ def verb
88
+ self['REQUEST_METHOD']
89
+ end
90
+
91
+ end
92
+
93
+ class Hash
94
+
95
+ # unparse querystring
96
+ def qs
97
+ '?'+map{|k,v|k.to_s+'='+(v ? (CGI.escape [*v][0].to_s) : '')}.intersperse("&").join('')
98
+ end
99
+
100
+ def env
101
+ @r = r
102
+ self
103
+ end
104
+
105
+ end
File without changes
data/infod/POST.rb ADDED
@@ -0,0 +1,34 @@
1
+ #watch __FILE__
2
+ class E
3
+
4
+ def POST
5
+ type = @r['CONTENT_TYPE']
6
+ case type
7
+ when /^application\/sparql-update/
8
+
9
+ when /^application\/x-www-form-urlencoded/
10
+ ch = nil
11
+ (Rack::Request.new @r).params.map{|k,v|
12
+ s, p, o = (CGI.unescape k).split S
13
+ if s && p && o
14
+ oP = o
15
+ s, p, o = [s, p, o].map &:unpath
16
+ s = s.uri[0..-2].E if s.uri[-1] == '/'
17
+ p = p.uri[0..-2].E if p.uri[-1] == '/'
18
+ if oP.E == (E.literal v) && s.uri.match(/^http/) && p.uri.match(/^http/)
19
+ puts "POST <#{s}> <#{p}> <#{o}>"
20
+ s[p,o,v]
21
+ ch = true
22
+ end
23
+ end}
24
+ if ch # state changed
25
+ g = {}
26
+ fromStream g, :triplrFsStore
27
+ ef.w g, true
28
+ end
29
+ end
30
+
31
+ [303,{'Location'=>uri},[]]
32
+ end
33
+
34
+ end
data/infod/audio.rb ADDED
@@ -0,0 +1,30 @@
1
+ class E
2
+
3
+ VideoFile = /(avi|flv|mkv|mpg|mp4|wmv)$/i
4
+ AudioFile = /(aif|wav|flac|mp3|m4a|aac|ogg)$/i
5
+
6
+ fn 'set/audio',->d,e,m{d.take.select{|e|e.ext.match AudioFile}}
7
+ fn 'set/video',->d,e,m{d.take.select{|e|e.ext.match VideoFile}}
8
+
9
+ AudioK = {}
10
+ %w{Album-Movie-Show_title Lead_performers-Soloists Title-songname-content_description}.
11
+ map{|a|Audio + a}.concat(['uri', Stat+'mtime', Stat+'size']).map{|p|AudioK[p] = true}
12
+
13
+ fn 'view/audio',->d,e{ d = d.dup
14
+
15
+ # skip non-audio files
16
+ d.delete_if{|p,o|
17
+ (p.respond_to? :match) &&
18
+ (!p.match AudioFile)}
19
+
20
+ # select data-fields
21
+ d.values.map{|r|
22
+ r.class==Hash &&
23
+ r.delete_if{|p,o|!AudioK[p]}}
24
+
25
+ [(H.once e, :mu, (H.js '/js/mu')),(H.once e, :audio,(H.js '/js/audio'),(H.css '/css/audio'),
26
+ {id: :rand, c: :r}, {id: :jump, c: '&rarr;'}, {id: :info, target: :_blank, _: :a},
27
+ {_: e.q.has_key?('video') ? :video : :audio, id: :media, controls: true}), '<br>',
28
+ F['view/table'][d,e]]}
29
+
30
+ end