infod 0.0.2 → 0.0.3.0

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.
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