infod 0.0.3.3 → 0.0.3.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 06750f111d49bb2b62f28cd18bc35dd5212dc0ac
4
- data.tar.gz: 7ee2db920696465b43201741aa28e6ecaf64af41
3
+ metadata.gz: 394fa91160ccafe206a32071d20edbaafbe1eef8
4
+ data.tar.gz: 5011996cc3fff07d31d2988b10226243082e668c
5
5
  SHA512:
6
- metadata.gz: 03d0d7ae6b88af73a4d5054e2736764d46d5e3247284bcbda919c811182ab0bdbf4e2ef37ccd5fdd8228f4435f3bc6f23311f6f6ac41a9f70016338e4bc6a01c
7
- data.tar.gz: ff95efae2b1690cd8f244360e903d1f9a7ea287a2450cdcb85e418b1f444eed54d9601819f4a266897ba0409711ad5af572f984f65a9b076395c2cf690a26cdb
6
+ metadata.gz: eb8da61e738b9efd9e8df67e0c904ccd0a9db0247b38594d2720b5740368da1702687a2d6589f3a3b6b767dd00c2f34794bdb741223ce82e2e78f934899d2388
7
+ data.tar.gz: cdc489d2660c7dfe8f44f9048dd0b1ba88267ba6484f3cd4d31daf58c4bbe85bdc7efaed742878f9bc893fa7b7bb2f745391d2668a2a85754d0a1d9d4e91d8d0
data/infod.rb CHANGED
@@ -15,7 +15,7 @@ shellwords}.map{|r|require r}
15
15
  %w{
16
16
  constants
17
17
  lambda
18
- mime
18
+ MIME
19
19
  404
20
20
  500
21
21
  grep
@@ -45,6 +45,7 @@ names
45
45
  POST
46
46
  rdf
47
47
  schema
48
+ SPARQL
48
49
  text
49
50
  threads
50
51
  time
@@ -23,15 +23,12 @@ s[HTTP+'statusCodeValue'] = [404]
23
23
 
24
24
  [404,{'Content-Type'=> r.format},[e.render(r.format,g,r)]]}
25
25
 
26
- F['/cache/GET'] = F[E404]
27
-
28
26
  fn 'view/404',->d,e{
29
27
  [H.css('/css/404'),{_: :style, c: "a {background-color:#{R.cs}}"},
30
28
  d.html]}
31
29
 
32
- # a resource-placeholder graph
33
- fn 'protograph/blank',->d,_,m{
34
- m[d.uri] = {}
30
+ fn 'protograph/blank',->d,_,m{ # 404 is determined by #empty?
31
+ m[d.uri] = {} # insert a resource
35
32
  rand.to_s.h}
36
33
 
37
34
  def checkURIs
@@ -40,9 +37,11 @@ s[HTTP+'statusCodeValue'] = [404]
40
37
  puts c.join ' '
41
38
  c } # status, uri tuple
42
39
  puts "\n\n"
43
- r.map{|c|
44
- # show anomalies
40
+ r.map{|c| # inspect anomalies
45
41
  puts c.join(' ') unless c[0] == 200 }
46
42
  end
47
43
 
44
+ F['/cache/GET'] = F[E404]
45
+ F['/index/GET'] = F[E404]
46
+
48
47
  end
@@ -1,54 +1,18 @@
1
- #watch __FILE__
2
1
  class R
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
2
 
3
+ fn 'E500',->x,e{ $stderr.puts [500, e['REQUEST_URI'], x.class, x.message].join ' '
14
4
  [500,{'Content-Type'=>'text/html'},
15
5
  [H[{_: :html,
16
- c: [{_: :head,
17
- c: [{_: :title, c: 500},(H.css '/css/500')]},
6
+ c: [{_: :head,c: [{_: :title, c: 500},(H.css '/css/500')]},
18
7
  {_: :body,
19
8
  c: [{_: :h1, c: 500},
20
9
  {_: :table,
21
10
  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
11
+ x.backtrace.map{|f| p = f.split /:/, 3
23
12
  {_: :tr,
24
13
  c: [{_: :td, class: :path, c: p[0].abbrURI},
25
14
  {_: :td, class: :index, c: p[1]},
26
15
  {_: :td, class: :context, c: (p[2]||'').hrefs}].cr}}.cr]}]}]}]]]}
27
16
 
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]]}
31
-
32
- F['view/'+COGS+'Exception']=->e,r{
33
- e.values.map{|e|
34
- {style: 'border-radius:1em;background-color:#333;color:#eee;float:left;max-width:42em',
35
- c: [{_: :h2, c: "<b style='background-color:#f00'>&#0191;</b>"+(e[Title]||[]).to_s},
36
- e[Content]||''
37
- ]}}}
38
-
39
- # filesystem takes priority over this if it's found
40
- fn '/css/500.css/GET',->e,r{
41
- [200,{'Content-Type'=>'text/css'},["
42
- body {margin:0; font-family: sans-serif; background-color:#fff; color:#000}
43
- h1 {padding:.2em; background-color:#f00; color:#fff; margin:0}
44
- div {display:inline}
45
- table {border-spacing:0;margin:0}
46
- b {background-color:#eee;color:#500;padding:.1em .3em .1em .3em}
47
- .frag {font-weight:bold; color:#000; background-color:#{R.cs}}
48
- td.space {background-color:#ddd}
49
- td.message {background-color:#009;color:#fff}
50
- td.path {text-align:right}
51
- td.index {text-align:right;border-color:#000;border-width:0 0 .1em 0;border-style:dotted;background-color:#ddd;color:#000}
52
- td.context {border-color:#ddd;border-width:0 0 .1em 0;border-style:dotted;padding:.15em}"]]}
53
-
54
17
  end
18
+
@@ -14,7 +14,7 @@ class R
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')) ?
17
- resource : (file.env @r).getFile
17
+ resource : (file.env @r).fileGET
18
18
 
19
19
  else
20
20
  resource
@@ -29,13 +29,13 @@ class R
29
29
  [200,{},[]]
30
30
  end
31
31
 
32
- def getFile
32
+ def fileGET
33
33
  @r['ETag'] = [m,size].h
34
34
  maybeSend mimeP,->{self}
35
35
  end
36
36
 
37
37
  def resource
38
- # bubble up site then global tree until handled (false return-value to pass)
38
+ # bubble up full-hostname then path tree until handled
39
39
  pathSegment.do{|path|
40
40
  lambdas = path.cascade.map{|p| p.uri.t + 'GET' }
41
41
  ['http://'+@r['SERVER_NAME'],""].map{|h| lambdas.map{|p|
@@ -48,31 +48,30 @@ class R
48
48
 
49
49
  def response
50
50
 
51
- m = {}
51
+ m = {} # TODO request-model as RDF::Graph, benchmark vs our pickle-able JSON graphs, + write persistable RDF::Repository class of similar performance
52
52
 
53
- # graph-identity (model)
54
- g = @r.q['graph']
53
+ # Model
54
+ g = @r.q['graph'] # model identity
55
55
  graphID = (g && F['protograph/' + g] || F['protograph/'])[self,@r.q,m]
56
56
 
57
- return F[E404][self,@r] if m.empty?
57
+ return F[E404][self,@r] if m.empty? # protograph empty - nothing found
58
58
 
59
- # response-identity (view)
59
+ # View
60
60
  @r['ETag'] ||= [@r.q['view'].do{|v|F['view/' + v] && v}, graphID, @r.format, Watch].h
61
61
 
62
62
  maybeSend @r.format, ->{
63
-
64
63
  r = R '/cache/view/' + @r['ETag'].dive
65
- if r.e # exists?
64
+ if r.e # view exists?
66
65
  r
67
66
  else
68
67
  c = R '/cache/model/' + graphID.dive
69
- if c.e # exists?
68
+ if c.e # model exists?
70
69
  m = c.r true
71
70
  else
72
- (g && F['graph/' + g] || F['graph/'])[self, @r.q,m] # construct
73
- c.w m,true # cache
71
+ (g && F['graph/' + g] || F['graph/'])[self, @r.q,m]
72
+ c.w m,true # model -> cache
74
73
  end
75
- r.w render @r.format, m, @r # construct -> cache
74
+ r.w render @r.format, m, @r # view -> cache
76
75
  end }
77
76
  end
78
77
 
@@ -98,13 +97,13 @@ class R
98
97
  [304,{},[]] # client has response
99
98
  end
100
99
 
101
- # user-patchable default handler - use index.html or defer to internal default-handler #response
100
+ # user-patchable root-level handler - index.html or continue to default-handler
102
101
  fn '/GET',->e,r{
103
102
  x = 'index.html'
104
103
  i = [e,e.pathSegment].compact.map{|e|e.as x}.find &:e
105
104
  if i && !r['REQUEST_URI'].match(/\?/)
106
105
  if e.uri[-1] == '/' # inside dir?
107
- i.env(r).getFile # show index
106
+ i.env(r).fileGET # show index
108
107
  else # descend into dir
109
108
  [301, {Location: e.uri.t}, []]
110
109
  end
@@ -1,9 +1,13 @@
1
1
  #watch __FILE__
2
2
  class R
3
3
 
4
+ def env r=nil
5
+ r ? (@r = r; self) : @r
6
+ end
7
+
4
8
  def R.call e
5
9
  e.extend Th # HTTP utility functions
6
- dev # see if watched files were changed
10
+ dev # watched files changed?
7
11
  e['HTTP_X_FORWARDED_HOST'].do{|h| e['SERVER_NAME'] = h }
8
12
  path = CGI.unescape e['REQUEST_PATH'].force_encoding('UTF-8').gsub '+','%2B'
9
13
  resource = R['http://'+e['SERVER_NAME']+path]
@@ -137,9 +137,7 @@ class R
137
137
 
138
138
  # prefer a view even if requested file exists
139
139
  MIMEcook={
140
- 'application/atom+xml' => true,
141
140
  'application/markdown' => true,
142
- 'application/json+rdf' => true,
143
141
  'application/org' => true,
144
142
  'application/postscript' => true,
145
143
  'application/textile' => true,
@@ -2,42 +2,43 @@
2
2
  class R
3
3
 
4
4
  def POST
5
- # custom handler lookup cascade
6
- pathSegment.do{|path|
5
+ pathSegment.do{|path| # handler cascade
7
6
  lambdas = path.cascade.map{|p| p.uri.t + 'POST' }
8
7
  ['http://'+@r['SERVER_NAME'],""].map{|h| lambdas.map{|p|
9
- F[h + p].do{|fn| fn[self,@r].do{|r|
10
- $stdout.puts [r[0],'http://'+@r['SERVER_NAME']+@r['REQUEST_URI'],@r['HTTP_USER_AGENT'],@r['HTTP_REFERER'],@r.format].join ' '
11
- return r
12
- }}}}}
13
- basicPOST
14
- end
15
- def basicPOST
16
- return [303,{'Location'=>uri},[]]
8
+ F[h + p].do{|fn|fn[self,@r].do{|r| return r }}}}}
9
+
17
10
  case @r['CONTENT_TYPE']
18
11
  when /^application\/x-www-form-urlencoded/
19
- changed = false
20
- (Rack::Request.new @r).params.map{|k,v| s, p, tripleA = JSON.parse CGI.unescape k
21
- s = s.R
22
- pp = s.predicatePath p
23
- o = v.match(/\A(\/|http)[\S]+\Z/) ? v.R : F['cleanHTML'][v]
24
- tripleB = pp.objectPath(o)[0]
25
- if tripleA.to_s != tripleB.to_s
26
- tripleA && tripleA.R.do{|t| t.delete if t.e }
27
- s[p] = o unless o.class==String && o.empty?
12
+ formPOST
13
+ end
14
+ end
15
+
16
+ def formPOST
17
+ changed = false
18
+ params = (Rack::Request.new @r).params
19
+ params.map{|k,v|
20
+ s, p, tripleX = JSON.parse CGI.unescape k rescue JSON::ParserError
21
+ if s # parse successful?
22
+ s = s.R # subject URI
23
+ pp = s.predicatePath p # s+p path
24
+ o = v.match(/\A(\/|http)[\S]+\Z/) ? v.R : F['cleanHTML'][v] # HTML cleanup
25
+ tripleY = pp.objectPath(o)[0] # editable triple
26
+ if tripleX.to_s != tripleY.to_s # changed?
27
+ tripleX && tripleX.R.do{|t| t.delete if t.e } # remove triple
28
+ s[p] = o unless o.class==String && o.empty? # add triple
28
29
  changed = true
29
- end}
30
- if changed
31
- g = {}
32
- fromStream g, :triplrDoc
33
- if g.empty? # no triples left
34
- ef.delete
35
- else # snapshot to graph-doc
36
- ef.w g, true
37
30
  end
31
+ end}
32
+ if changed # update doc
33
+ g = {} # triples -> graph
34
+ fromStream g, :triplrDoc
35
+ if g.empty? # 0 triples
36
+ ef.delete
37
+ else # graph -> doc #TODO write doc-per-version and symlink current (optional history)
38
+ ef.w g, true
38
39
  end
39
40
  end
40
- [303,{'Location'=>uri+'?graph=edit'},[]]
41
+ [303,{'Location'=>uri+'?graph=edit'+(params['mono'] ? '&mono' : '')},[]]
41
42
  end
42
43
 
43
44
  end
@@ -0,0 +1,6 @@
1
+ #watch __FILE__
2
+ class R
3
+
4
+ # TODO expose fs.rb as an RDF::Queryable interface - maybe RDF::Repository too
5
+
6
+ end
@@ -0,0 +1,3 @@
1
+ class R
2
+
3
+ end
@@ -1,20 +1,29 @@
1
1
  #watch __FILE__
2
2
  class R
3
3
 
4
+ # traverse collection of blog-posts
5
+ F['/blog/GET'] = -> d,e {
6
+ e.q['set'] = 'depth' # post-range in date-order
7
+ e.q['local'] = true # hostname-specific paths
8
+ e.q['c'] = 8 # count
9
+ R['http://'+e['SERVER_NAME']+'/time'].env(e).response}
10
+
11
+ # decode POSTed title, mint derived-URI, set title+type properties, continue to editor
4
12
  F['/blog/post/POST'] = -> d,e {
5
- name = URI.escape (Rack::Request.new d.env).params['name'].gsub /[?#\s\/]/,'_'
6
- doc = 'http://'+e['SERVER_NAME']+Time.now.strftime('/%Y/%m/')+name
7
- post = doc.R.a '#'
8
- post[Type] = R[SIOCt+'BlogPost']
9
- post[Title] = name
10
- edit = "?prototype=sioct:BlogPost&graph=edit"
11
- [303,{'Location' => doc + edit},[]]}
13
+ host = 'http://' + e['SERVER_NAME']
14
+ title = (Rack::Request.new d.env).params['title']
15
+ base = R[host+Time.now.strftime('/%Y/%m/%d/')+URI.escape(title.gsub /[?#\s\/]/,'_')] # doc
16
+ post = base.a '#' # editable resource
17
+ post[Type] = R[SIOCt+'BlogPost']; post[Title] = title # add type-tag & title
18
+ base.ef.ln_s R[host + '/time/' + Time.now.iso8601[0..18].gsub('-','/') + '.e'] # add to datetime-index
19
+ [303,{'Location' => (base+"?prototype=sioct:BlogPost&graph=edit&mono").uri},[]]}
12
20
 
21
+ # POST post-title to /blog/post
13
22
  F['/blog/post/GET'] = -> d,e {
14
23
  [200,{'Content-Type'=>'text/html'},
15
- [H(['name',
24
+ [H(['title',
16
25
  {_: :form, method: :POST,
17
- c: [{_: :input, name: :name, style: "font-size:1.6em;width:48ex"},
26
+ c: [{_: :input, name: :title, style: "font-size:1.6em;width:48ex"},
18
27
  {_: :input, type: :submit, value: ' go '}
19
28
  ]}])]]}
20
29
 
@@ -25,11 +34,11 @@ class R
25
34
  case u # match against URIs for customized view
26
35
  when /artery.wbur/ # compact whitespace a bit
27
36
  r[Content] = {class: :WBUR, c: [{_: :style, c: ".WBUR p {margin:0}"},r[Content]]}
28
- F['view/base'][{u => r},e,false]
37
+ F['view/base'][{u => r},e]
29
38
 
30
39
  when /boston\.com/ # crop sharebuttons
31
40
  (Nokogiri::HTML.parse r[Content][0]).css('p')[0].do{|p|r[Content]=p.inner_html}
32
- F['view/base'][{u => r},e,false]
41
+ F['view/base'][{u => r},e]
33
42
 
34
43
  when /flickr/
35
44
  r[Content]
@@ -44,8 +53,21 @@ class R
44
53
  c: [{_: :img, src: '/logos/uhub.png',style: 'position:absolute;top:-93px'},
45
54
  {_: :h2, style: 'color:#000;margin:0',c: r[Title]}]},c.to_s],
46
55
  style: 'float:left;max-width:40em;position:relative;background-color:#fff;border-color:#eee;margin-top:93px;margin-right:.3em;padding-top:0;border-style:dotted;border-width:.3em;border-radius:0 .8em .8em .8em'}
47
- else
48
- F['view/base'][{u => r},e,false]
56
+ else # host matches current
57
+ if u.index('http://'+e['SERVER_NAME']) == 0
58
+ [{_: :a, href: u, c: {_: :h1, c: r[Title]}},
59
+ {_: :p, c: r[Content]},
60
+ # F['view/comment'][{u => r},e]
61
+ ]
62
+ else # non-local (imported) host
63
+ F['view/base'][{u => r},e]
64
+ end
49
65
  end}}
50
66
 
67
+ F['view/comment'] = -> g,e {
68
+ g.map{|u,r|
69
+ "comment"
70
+ }
71
+ }
72
+
51
73
  end
@@ -1,4 +1,4 @@
1
- class R
1
+ class R < RDF::URI
2
2
 
3
3
  FSbase = `pwd`.chomp ; BaseLen = FSbase.size
4
4
 
@@ -52,6 +52,4 @@ class R
52
52
  "stat" => Stat,
53
53
  }
54
54
 
55
- attr_reader :uri
56
-
57
55
  end
@@ -28,10 +28,9 @@ class R
28
28
  puts "graph.edit #{e}"
29
29
  e.fromStream g, :triplrDoc} # add fs-sourced triples
30
30
 
31
- =begin HTML <form> RDF editor
32
- arg
33
- prototype - initialize fields for a resource-type
34
- predicate - initialize field for a particular predicate
31
+ =begin HTML <form> triple-editor
32
+ @prototype bundle of fields for a type
33
+ @predicate add field for a particular predicate
35
34
  =end
36
35
  fn 'view/edit',->g,e{
37
36
 
@@ -54,6 +53,7 @@ class R
54
53
  e.q['prototype'].do{|pr| pr = pr.expand
55
54
  Prototypes[pr].do{|v|ps.concat v }} # prototype imports
56
55
  e.q['predicate'].do{|p|ps.push p } # explicit predicate
56
+ mono = e.q.has_key? 'mono' # 1:1 predicate->object
57
57
 
58
58
  [H.css('/css/html'),
59
59
  {_: :form, name: :editor, method: :POST, action: e['REQUEST_PATH'],
@@ -68,10 +68,12 @@ class R
68
68
  {_: :tr,
69
69
  c: [{_: :td, class: :key, c: {_: :a, title: p, href: p, c: p.abbrURI}}, # property
70
70
  {_: :td,
71
- c: [r[p].do{|o| # objects
72
- (o.class == Array ? o : [o]).map{|o| # each object
73
- triple[s,p,o]}}, # existing triples
74
- triple[s,p,nil]]}]}}]}}, # new triple
71
+ c: [r[p].do{|o| # object?
72
+ o.justArray.map{|o| # each object
73
+ triple[s,p,o]}}, # extant triples
74
+ (triple[s,p,nil] unless r[p] && mono) # add triple
75
+ ]}]}}]}},
76
+ ({_: :input, type: :hidden, name: :mono, value: :true} if mono),
75
77
  {_: :input, type: :submit, value: 'save'}]}]}
76
78
 
77
79
  # select a property to edit
@@ -17,7 +17,7 @@ class R
17
17
  # facet stats
18
18
  m.map{|s,r| a.map{|p,_|
19
19
  r[p].do{|o|
20
- (o.class==Array ? o : [o]).map{|o|
20
+ o.justArray.map{|o|
21
21
  a[p][o]=(a[p][o]||0)+1}}}}
22
22
 
23
23
 
@@ -31,7 +31,7 @@ class R
31
31
  m.map{|u,r| # each resource
32
32
  a.map{|p,_| # each facet
33
33
  [n[p], r[p].do{|o| # value
34
- (o.class==Array ? o : [o]).map{|o|
34
+ o.justArray.map{|o|
35
35
  n[o.to_s] # identifier
36
36
  }}].join ' '
37
37
  }.do{|f|