infod 0.0.3.3 → 0.0.3.4

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