infod 0.0.3.1 → 0.0.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/infod.rb CHANGED
@@ -4,13 +4,12 @@ lambda
4
4
  mime
5
5
  404
6
6
  500
7
+ grep
7
8
  audio
8
9
  blog
9
10
  cal
10
11
  code
11
- css
12
12
  csv
13
- du
14
13
  edit
15
14
  facets
16
15
  feed
@@ -20,7 +19,6 @@ fs
20
19
  GET
21
20
  glob
22
21
  graph
23
- grep
24
22
  groonga
25
23
  HEAD
26
24
  histogram
@@ -28,7 +26,6 @@ html
28
26
  HTTP
29
27
  image
30
28
  index
31
- json
32
29
  kv
33
30
  ls
34
31
  mail
@@ -36,13 +33,11 @@ man
36
33
  microblog
37
34
  names
38
35
  page
39
- PATCH
40
36
  POST
41
37
  postscript
42
38
  rdf
43
39
  ruby
44
40
  schema
45
- search
46
41
  sh
47
42
  text
48
43
  threads
@@ -16,7 +16,7 @@ r.map{|k,v| s[Header + k] = k == 'uri' ? v : [v] }
16
16
  s[Type] = [E[HTTP+'Response']]
17
17
  s[HTTP+'statusCodeValue'] = [404]
18
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']]
19
+ s[Edit] = [E[r['REQUEST_PATH']+'?graph=create']]
20
20
  s['#query'] = [r.q]
21
21
  s['#seeAlso'] = [e.parent,*e.a('*').glob]
22
22
  r.q['view'] = '404'
@@ -27,9 +27,8 @@ s[HTTP+'statusCodeValue'] = [404]
27
27
  [H.css('/css/404'),{_: :style, c: "a {background-color:#{E.cs}}"},
28
28
  d.html]}
29
29
 
30
- # a placeholder graph
31
- # useful for initializing an editor on an empty resource
32
- fn 'protograph/_',->d,_,m{
30
+ # a resource-placeholder graph
31
+ fn 'protograph/blank',->d,_,m{
33
32
  m[d.uri] = {}
34
33
  rand.to_s.h}
35
34
 
@@ -25,8 +25,9 @@ class E
25
25
  {_: :td, class: :index, c: p[1]},
26
26
  {_: :td, class: :context, c: (p[2]||'').hrefs}].cr}}.cr]}]}]}]]]}
27
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}
28
+ F['/500/GET'] = ->e,r{
29
+ body = H [Errors.sort_by{|u,r|r[:time]}.reverse.html, H.css('/css/500')]
30
+ [200, {'Content-Type'=>'text/html; charset=utf-8'}, [body]]}
30
31
 
31
32
  F['view/'+COGS+'Exception']=->e,r{
32
33
  e.values.map{|e|
@@ -10,7 +10,7 @@ class E
10
10
 
11
11
  elsif file = [self,pathSegment].compact.find(&:f)
12
12
 
13
- # file exists, but client might not accept its MIME, or want it transformed to another MIME
13
+ # file exists. check if client or server want it transformed to another MIME
14
14
  a = @r.accept.values.flatten
15
15
  accepted = a.empty? || (a.member? file.mimeP) || (a.member? '*/*')
16
16
  (!accepted || MIMEcook[file.mimeP] || @r.q.has_key?('view')) ?
@@ -23,7 +23,7 @@ class E
23
23
 
24
24
  def getFile
25
25
  @r['ETag'] = [m,size].h
26
- maybeSend mimeP,->{self},:link
26
+ maybeSend mimeP,->{self}
27
27
  end
28
28
 
29
29
  def resource
@@ -42,13 +42,13 @@ class E
42
42
 
43
43
  m = {}
44
44
 
45
- # graph identity (model)
45
+ # graph-identity (model)
46
46
  g = @r.q['graph']
47
47
  graphID = (g && F['protograph/' + g] || F['protograph/'])[self,@r.q,m]
48
48
 
49
49
  return F[E404][self,@r] if m.empty?
50
50
 
51
- # response identity (view)
51
+ # response-identity (view)
52
52
  @r['ETag'] ||= [@r.q['view'].do{|v|F['view/' + v] && v}, graphID, @r.format, Watch].h
53
53
 
54
54
  maybeSend @r.format, ->{
@@ -78,18 +78,13 @@ class E
78
78
  !((m=@r['HTTP_IF_NONE_MATCH']) && m.strip.split(/\s*,\s*/).include?(@r['ETag']))
79
79
  end
80
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
-
81
+ def maybeSend m, b; c = 200
82
+ send? ?
83
+ b[].do{|b| # continue
84
+ h = {'Content-Type'=> m, 'ETag'=> @r['ETag']}
88
85
  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
86
 
92
- # frontend-specific response handlers
87
+ # frontend-specific handlers
93
88
  b.class == E ? (Nginx ? # nginx chosen?
94
89
  [c,h.update({'X-Accel-Redirect' => '/fs' + b.path}),[]] : # Nginx handler
95
90
  Apache ? # Apache chosen?
@@ -101,4 +96,18 @@ class E
101
96
  [304,{},[]] # client has response
102
97
  end
103
98
 
99
+ # user-patchable default handler - use index.html or defer to internal default-handler #response
100
+ fn '/GET',->e,r{
101
+ x = 'index.html'
102
+ i = [e,e.pathSegment].compact.map{|e|e.as x}.find &:e
103
+ if i && !r['REQUEST_URI'].match(/\?/)
104
+ if e.uri[-1] == '/' # inside dir?
105
+ i.env(r).getFile # show index
106
+ else # descend into dir
107
+ [301, {Location: e.uri.t}, []]
108
+ end
109
+ else
110
+ e.response
111
+ end}
112
+
104
113
  end
@@ -10,7 +10,7 @@ class E
10
10
 
11
11
  # HEAD response-codes on a (.u) list of URIs
12
12
  def checkURIs
13
- r = uris.select{|u|u.to_s.match /^http/}.map{|u|
13
+ r = uris.map{|u|
14
14
  c = [`curl -IsA 404? "#{u}"`.lines.to_a[0].match(/\d{3}/)[0].to_i,u] # HEAD
15
15
  puts c.join ' '
16
16
  c } # status, uri tuple
@@ -4,15 +4,18 @@ require 'rack'
4
4
  class E
5
5
 
6
6
  def E.call e
7
- dev
8
- e.extend Th
7
+ e.extend Th # add HTTP utility functions to environment table
8
+ dev # see if watched files were changed
9
9
  e['HTTP_X_FORWARDED_HOST'].do{|h| e['SERVER_NAME'] = h }
10
- p = e['REQUEST_PATH'].force_encoding 'UTF-8'
10
+ path = CGI.unescape e['REQUEST_PATH'].force_encoding('UTF-8').gsub '+','%2B'
11
+ resource = E['http://'+e['SERVER_NAME']+path]
11
12
 
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,{},[]]
13
+ if resource.inside
14
+ e['uri'] = resource.uri
15
+ (resource.env e).send e['REQUEST_METHOD']
16
+ else
17
+ [403,{},[]]
18
+ end
16
19
 
17
20
  rescue Exception => x
18
21
  F['E500'][x,e]
@@ -20,37 +23,22 @@ class E
20
23
 
21
24
  end
22
25
 
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
26
  module Th
42
27
 
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
28
+ # Query-String -> Hash
49
29
  def q
50
- @q ||= (qs||'').qp
30
+ @q ||=
31
+ (if q = self['QUERY_STRING']
32
+ h = {}
33
+ q.split(/&/).map{|e| k,v = e.split(/=/,2).map{|x| CGI.unescape x }
34
+ h[k] = v }
35
+ h
36
+ else
37
+ {}
38
+ end)
51
39
  end
52
40
 
53
- # Accept header -> Hash
41
+ # Accept -> Hash
54
42
  def accept_ k=''
55
43
  d={}
56
44
  self['HTTP_ACCEPT'+k].do{|k|
@@ -66,7 +54,7 @@ module Th
66
54
  end
67
55
 
68
56
  def conneg
69
- # extension
57
+ # specific format-variant URI
70
58
  { '.html' => 'text/html',
71
59
  '.jsonld' => 'application/ld+json',
72
60
  '.nt' => 'text/ntriples',
@@ -75,7 +63,7 @@ module Th
75
63
  }[File.extname self['uri']].do{|mime|
76
64
  return mime}
77
65
 
78
- # Accept header
66
+ # Accept formats
79
67
  accept.sort.reverse.map{|q,mimes|
80
68
  mimes.map{|mime|
81
69
  return mime if E::F[E::Render+mime]}}
@@ -84,15 +72,11 @@ module Th
84
72
 
85
73
  def accept; @accept ||= accept_ end
86
74
 
87
- def verb
88
- self['REQUEST_METHOD']
89
- end
90
-
91
75
  end
92
76
 
93
77
  class Hash
94
78
 
95
- # unparse querystring
79
+ # Hash -> Query-String
96
80
  def qs
97
81
  '?'+map{|k,v|k.to_s+'='+(v ? (CGI.escape [*v][0].to_s) : '')}.intersperse("&").join('')
98
82
  end
@@ -1,34 +1,36 @@
1
- #watch __FILE__
1
+ watch __FILE__
2
2
  class E
3
3
 
4
4
  def POST
5
5
  type = @r['CONTENT_TYPE']
6
6
  case type
7
7
  when /^application\/sparql-update/
8
-
8
+ puts "SPARQL"
9
9
  when /^application\/x-www-form-urlencoded/
10
- ch = nil
10
+ changed = false
11
11
  (Rack::Request.new @r).params.map{|k,v|
12
- s, p, o = (CGI.unescape k).split S
12
+ s, p, o = (CGI.unescape k).split /\/\._/
13
13
  if s && p && o
14
- oP = o
15
14
  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
15
+ if s.uri.match(/^http/) && p.uri.match(/^http/)
16
+ oO = v.match(/\A(\/|http)[\S]+\Z/) ? v.E : F['cleanHTML'][v]
17
+ if o != oO
18
+ changed = true
19
+ s[p,o,oO]
20
+ end
22
21
  end
23
22
  end}
24
- if ch # state changed
23
+ if changed
25
24
  g = {}
26
- fromStream g, :triplrFsStore
27
- ef.w g, true
25
+ fromStream g, :triplrDoc
26
+ if g.empty?
27
+ ef.deleteNode
28
+ else
29
+ ef.w g, true
30
+ end
28
31
  end
29
32
  end
30
-
31
- [303,{'Location'=>uri},[]]
33
+ [303,{'Location'=>uri+'?graph=edit'},[]]
32
34
  end
33
35
 
34
36
  end
@@ -3,12 +3,20 @@ class E
3
3
  VideoFile = /(avi|flv|mkv|mpg|mp4|wmv)$/i
4
4
  AudioFile = /(aif|wav|flac|mp3|m4a|aac|ogg)$/i
5
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}}
6
+ fn 'set/audio',->d,e,m{
7
+ e['view'] = 'audio'
8
+ d.take.select{|e|e.ext.match AudioFile}}
8
9
 
10
+ fn 'set/video',->d,e,m{
11
+ e['view'] = 'audio'
12
+ e['video'] = true
13
+ d.take.select{|e|e.ext.match VideoFile}}
14
+
15
+ # table of audio-resource properties
9
16
  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}
17
+ %w{Album-Movie-Show_title Lead_performers-Soloists Title-songname-content_description}.map{|a|Audio + a}.
18
+ concat(['uri', Stat+'mtime', Stat+'size']).
19
+ map{|p|AudioK[p] = true}
12
20
 
13
21
  fn 'view/audio',->d,e{ d = d.dup
14
22
 
@@ -21,7 +21,7 @@ class E
21
21
  when /reddit/ # minimal view
22
22
  F['view/'+SIOCt+'BoardPost'][{u => r},e]
23
23
  when /universalhub/ # logo + trim spacehogging tagjunk
24
- c = Nokogiri::HTML.parse r[Content][0]
24
+ c = Nokogiri::HTML.fragment r[Content][0]
25
25
  c.css('section').map{|x|x.remove}
26
26
  {c: [{_: :a, href: r['http://purl.org/rss/1.0/link'][0].E.uri,
27
27
  c: [{_: :img, src: '/logos/uhub.png',style: 'position:absolute;top:-93px'},
@@ -27,8 +27,8 @@ class E
27
27
  # triplr/view mappings
28
28
  [ma,mt].map{|m|
29
29
  MIMEsource[m] ||= [:triplrSourceCode]
30
+ fn 'view/'+m, F['view/code']}}
30
31
 
31
- fn 'view/'+m, F['view/code']
32
- }}
32
+ MIMEsource['text/css'] ||= [:triplrSourceCode] # i hear CSS is Turing complete now, http://inamidst.com/whits/2014/formats
33
33
 
34
34
  end
@@ -1,7 +1,6 @@
1
1
  class E
2
2
 
3
3
  FSbase = `pwd`.chomp ; BaseLen = FSbase.size
4
- URIURL = '/@' # non-HTTP URI path resolution-prefix
5
4
  S = /\._/ # data path-separator
6
5
 
7
6
  W3 = 'http://www.w3.org/'
@@ -15,23 +14,24 @@ class E
15
14
  DC = Purl + 'dc/terms/'
16
15
  Date = DC + 'date'
17
16
  Title = DC + 'title'
18
- Name = FOAF + 'name'
17
+ Name = SIOC + 'name'
19
18
  To = SIOC + 'addressed_to'
20
19
  Creator = SIOC + 'has_creator'
21
20
  Content = SIOC + 'content'
22
21
  XHV = W3 + '1999/xhtml/vocab#'
23
22
  RDFns = W3 + "1999/02/22-rdf-syntax-ns#"
23
+ RDFs = W3 + '2000/01/rdf-schema#'
24
+ # RDFns = W3 + 'ns/rdf#'
25
+ # RDFs = W3 + 'ns/rdfs#'
24
26
  EXIF = W3 + '2003/12/exif/ns#'
25
27
  WF = W3 + '2005/01/wf/flow#'
26
28
  HTTP = W3 + '2011/http#'
27
29
  Header = W3 + '2011/http-headers#'
28
30
  LDP = W3 + 'ns/ldp#'
29
- Next = LDP+'nextPage'
30
- Prev = LDP+'prevPage'
31
+ Next = LDP + 'nextPage'
32
+ Prev = LDP + 'prevPage'
31
33
  Posix = W3 + 'ns/posix/'
32
34
  Type = RDFns+ "type"
33
- # Type = W3 + "ns/rdf#type"
34
- RDFs = W3 + 'ns/rdfs#'
35
35
  PAC = DIG + '2008/PAC/ontology/pac#'
36
36
  COGS = Deri + 'cogs#'
37
37
  CSV = Deri + 'scsv#'
@@ -1,13 +1,36 @@
1
+ #watch __FILE__
1
2
  class E
2
3
 
3
4
  # CSV -> tripleStream
4
5
  def triplrCSV d
5
6
  d = @r.q['delim']||d
6
- open(node).readlines.map{|l|l.chomp.split(d) rescue []}.do{|t|
7
- t[0].do{|x|
8
- t[1..-1].each_with_index{|r,ow|r.each_with_index{|v,i|
9
- yield uri+'#r'+ow.to_s,x[i],v
7
+ open(node).readlines.map{|l|l.chomp.split(d) rescue []}.do{|lines|
8
+ lines[0].do{|fields| # ok, we have at least one line..
9
+ yield uri+'#', Type, E[CSV+'Table']
10
+ yield uri+'#', CSV+'rowCount', lines.size
11
+ yield uri+'#', COGS+'View', E[uri+'?view=csv']
12
+ lines[1..-1].each_with_index{|row,line|
13
+ row.each_with_index{|field,i|
14
+ id = uri + '#row:' + line.to_s
15
+ yield id, fields[i], field
16
+ yield id, Type, E[CSV+'Row']
10
17
  }}}}
11
18
  end
12
19
 
20
+ F['view/csv'] = -> d,e {
21
+ # ignore non-rows
22
+ d.delete_if{|s,r|
23
+ !(r.class==Hash &&
24
+ r[Type].do{|t|
25
+ t.class == Array &&
26
+ t.map(&:maybeURI).member?(CSV+'Row')})}
27
+ # done w/ the type-tag
28
+ d.values.map{|r|r.delete Type}
29
+
30
+ [F['view/p'][d,e],
31
+ {_: :style, c: 'table.tab .abbr, table.tab .scheme {display: inline}'}
32
+ ]}
33
+
34
+ F['view/'+CSV+'Row'] = NullView
35
+
13
36
  end