infod 0.0.1 → 0.0.2

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 (93) hide show
  1. data/infod.rb +2 -3
  2. data/infod/Es.rb +31 -67
  3. data/infod/{W/source.rb → Es/code.rb} +6 -10
  4. data/infod/Es/css.rb +21 -0
  5. data/infod/{W → Es}/csv.rb +0 -0
  6. data/infod/Es/du.rb +16 -0
  7. data/infod/{W → Es}/feed.rb +13 -11
  8. data/infod/Es/filter.rb +75 -0
  9. data/infod/Es/find.rb +20 -0
  10. data/infod/Es/fs.rb +145 -136
  11. data/infod/Es/glob.rb +22 -0
  12. data/infod/Es/grep.rb +61 -0
  13. data/infod/Es/groonga.rb +47 -56
  14. data/infod/Es/html.rb +271 -0
  15. data/infod/Es/image.rb +114 -0
  16. data/infod/Es/in.rb +68 -0
  17. data/infod/Es/index.rb +183 -0
  18. data/infod/{W → Es}/json.rb +28 -4
  19. data/infod/Es/kv.rb +60 -0
  20. data/infod/Es/ls.rb +58 -0
  21. data/infod/Es/mail.rb +87 -0
  22. data/infod/Es/man.rb +112 -0
  23. data/infod/Es/mime.rb +59 -0
  24. data/infod/Es/out.rb +52 -0
  25. data/infod/{W/page.rb → Es/pager.rb} +7 -3
  26. data/infod/Es/pdf.rb +19 -0
  27. data/infod/Es/rdf.rb +35 -0
  28. data/infod/Es/schema.rb +99 -0
  29. data/infod/Es/search.rb +24 -0
  30. data/infod/Es/sh.rb +21 -0
  31. data/infod/{W → Es}/text.rb +26 -14
  32. data/infod/H.rb +15 -29
  33. data/infod/H/audio.rb +19 -0
  34. data/infod/H/blog.rb +15 -0
  35. data/infod/{W → H}/cal.rb +2 -31
  36. data/infod/H/edit.rb +88 -0
  37. data/infod/{W/examine/examine.rb → H/facets.rb} +17 -17
  38. data/infod/{W → H}/forum.rb +1 -0
  39. data/infod/{W/examine/sw.rb → H/hf.rb} +12 -12
  40. data/infod/H/histogram.rb +78 -0
  41. data/infod/H/mail.rb +92 -0
  42. data/infod/{W/chat.rb → H/microblog.rb} +21 -16
  43. data/infod/H/threads.rb +77 -0
  44. data/infod/H/time.rb +131 -0
  45. data/infod/H/who.rb +30 -0
  46. data/infod/{W → H}/wiki.rb +0 -0
  47. data/infod/K.rb +28 -60
  48. data/infod/N.rb +151 -74
  49. data/infod/Rb.rb +3 -3
  50. data/infod/Th.rb +27 -101
  51. data/infod/Th/404.rb +29 -36
  52. data/infod/Th/500.rb +36 -5
  53. data/infod/Th/GET.rb +48 -118
  54. data/infod/Th/POST.rb +31 -11
  55. data/infod/Th/perf.rb +37 -0
  56. data/infod/Th/util.rb +89 -0
  57. data/infod/Y.rb +24 -7
  58. data/infod/infod.rb +2 -3
  59. metadata +92 -64
  60. data/infod/Es/redis.rb +0 -3
  61. data/infod/Es/sqlite.rb +0 -3
  62. data/infod/Th/local.rb +0 -22
  63. data/infod/W.rb +0 -34
  64. data/infod/W/audio.rb +0 -56
  65. data/infod/W/blog.rb +0 -3
  66. data/infod/W/color.rb +0 -28
  67. data/infod/W/core.rb +0 -77
  68. data/infod/W/css.rb +0 -24
  69. data/infod/W/du.rb +0 -35
  70. data/infod/W/edit.rb +0 -8
  71. data/infod/W/examine/exhibit.rb +0 -34
  72. data/infod/W/examine/hist.rb +0 -55
  73. data/infod/W/examine/history.rb +0 -19
  74. data/infod/W/examine/normal.rb +0 -31
  75. data/infod/W/examine/protovis.rb +0 -30
  76. data/infod/W/examine/time/graph.rb +0 -86
  77. data/infod/W/examine/time/line.rb +0 -24
  78. data/infod/W/find.rb +0 -24
  79. data/infod/W/grep.rb +0 -27
  80. data/infod/W/html.rb +0 -143
  81. data/infod/W/image.rb +0 -61
  82. data/infod/W/kv.rb +0 -66
  83. data/infod/W/ls.rb +0 -50
  84. data/infod/W/mail.rb +0 -248
  85. data/infod/W/pdf.rb +0 -16
  86. data/infod/W/post.rb +0 -9
  87. data/infod/W/rdf.rb +0 -32
  88. data/infod/W/schema.rb +0 -172
  89. data/infod/W/search.rb +0 -33
  90. data/infod/W/shell.rb +0 -30
  91. data/infod/W/table.rb +0 -87
  92. data/infod/W/tree.rb +0 -26
  93. data/infod/W/vfs.rb +0 -175
data/infod/Es/glob.rb ADDED
@@ -0,0 +1,22 @@
1
+ class E
2
+
3
+ # glob :: pattern -> [E]
4
+ def glob p=""
5
+ (Pathname.glob d + p).map &:E
6
+ end
7
+
8
+ fn 'set/glob',->d,e=nil,_=nil{
9
+ [d,d.pathSegment].compact.map(&:glob).flatten[0..3e3]}
10
+
11
+ fn 'req/randomFile',->e,r{
12
+ g = F['set/glob'][e]
13
+ !g.empty? ? [302, {Location: g.random.uri}, []] : [404]}
14
+
15
+ def docs
16
+ doc = self if e # directly-referenced doc
17
+ docs = docBase.glob ".{e,html,n3,nt,owl,rdf,ttl}" # basename-sharing docs
18
+ dir = (d? && uri[-1]=='/' && uri.size>1) ? c : [] # trailing slash descends
19
+ [doc,docs,dir].flatten.compact
20
+ end
21
+
22
+ end
data/infod/Es/grep.rb ADDED
@@ -0,0 +1,61 @@
1
+ #watch __FILE__
2
+ class E
3
+
4
+ fn 'set/grep',->e,q,m{
5
+ q['i'] ||= true # normal people want case-insensitive, i think - comment to unstick
6
+ q['q'].do{|query|
7
+ [e,e.pathSegment].compact.select(&:e).map{|e|
8
+ grep = "grep -rl#{q.has_key?('i') && 'i'} #{query.sh} #{e.sh}" # ;puts grep
9
+ `#{grep}`}.map{|r|r.lines.to_a.map{|r|r.chomp.unpathFs}}.flatten}}
10
+
11
+ fn 'view/grep',->d,e{
12
+ w = e.q['q']
13
+ if w
14
+ # words supplied in query
15
+ w = w.scan(/[\w]+/).map(&:downcase).uniq
16
+
17
+ # word index
18
+ c={}
19
+ w.each_with_index{|w,i|
20
+ c[w] = i }
21
+
22
+ # OR pattern
23
+ a = /(#{w.join '|'})/i
24
+ # sequential pattern
25
+ p = /#{w.join '.*'}/i
26
+
27
+ [H.css('/css/search'), H.css('/css/grep'),
28
+ F['view/search/form'][e.q,e],
29
+ {_: :style, c: c.values.map{|i|
30
+ # word color
31
+ b = rand(16777216)
32
+ # keep text contrasty
33
+ f = b > 8388608 ? :black : :white
34
+
35
+ # word CSS
36
+ ".w#{i} {background-color: #{'#%06x' % b}; color: #{f}}\n"}},
37
+
38
+ # each resource
39
+ d.map{|u,r|
40
+ # model to text/plain
41
+ l = F[Render+'text/plain'][{u => r},e].gsub(/<[^>]*>/,'').lines
42
+
43
+ # try sequential match
44
+ g = l.grep p
45
+ # try OR match
46
+ g = l.grep a if g.empty?
47
+
48
+ # match?
49
+ !g.empty? &&
50
+ [# link to resource
51
+ r.E.do{|e|{_: :a, href: e.url, c: e}}, '<br>',
52
+ # show 3 matches per resource
53
+ [g[-1*(g.size.max 3)..-1].map{|l|
54
+ # exerpt
55
+ l[0..403].gsub(a){|g|
56
+ H({_: :span, class: "w w#{c[g.downcase]}",c: g})}
57
+ },"<br>"]]},
58
+ {_: :a, class: :down, href: e['REQUEST_PATH']+e.q.except('view').qs, style: "background-color: #{E.cs}",c: '&darr;'}]
59
+ end }
60
+
61
+ end
data/infod/Es/groonga.rb CHANGED
@@ -1,10 +1,51 @@
1
1
  #watch __FILE__
2
2
  class E
3
-
3
+ # ruby search-engine & column-store
4
4
  # http://groonga.org/ http://ranguba.org/
5
- # https://github.com/groonga/groonga
6
- # https://github.com/ranguba/rroonga
7
- # default DB
5
+
6
+ # query
7
+ fn 'protograph/roonga',->d,e,m{
8
+ ga = E.groonga
9
+
10
+ # search expression
11
+ q = e['q']
12
+
13
+ # context
14
+ g = e["context"] || d.env['SERVER_NAME']
15
+
16
+ # exec expression
17
+ r = q ? ga.select{|r|(r['graph'] == g) & r["content"].match(q)} : # expression if exists
18
+ ga.select{|r| r['graph'] == g} # ordered set (index date-range)
19
+
20
+ # offset, size
21
+ start = e['start'].do{|c| c.to_i.max(r.size - 1).min 0 } || 0
22
+ c = (e['c']||e['count']).do{|c|c.to_i.max(10000).min(0)} || 8
23
+
24
+ # are further results traversible?
25
+ down = r.size > start+c
26
+ up = !(start<=0)
27
+
28
+ # sort results
29
+ r = r.sort(e.has_key?('score') ? [["_score"]] : [["time", "descending"]],:offset => start,:limit => c)
30
+
31
+ # pagination resources
32
+ m['prev']={'uri' => 'prev','url' => '/search','start' => start + c, 'c' => c} if down
33
+ m['next']={'uri' => 'next','url' => '/search','start' => start - c, 'c' => c} if up
34
+
35
+ # search-result identifiers
36
+ r = r.map{|r| r['.uri'].E }
37
+
38
+ # fragment identifiers
39
+ m['frag'] = {'uri' => 'frag', 'res' => r}
40
+
41
+ # containing documents
42
+ r.map(&:docs).flatten.uniq.map{|r| m[r.uri] = r.env e}
43
+
44
+ # no 404 on 0 results - searchbox view
45
+ m['/s']={'uri'=>'/s'} if m.empty?
46
+
47
+ F['docsID'][m]}
48
+
8
49
  def E.groonga
9
50
  @groonga ||= (require 'groonga'
10
51
  E['/E/groonga'].groonga
@@ -14,7 +55,7 @@ class E
14
55
  # load or create groongaDB at URI
15
56
  def groonga
16
57
  return Groonga::Database.open d if e # open db
17
- dirname.dir # create containing dir
58
+ dirname.mk # create containing dir
18
59
  Groonga::Database.create(:path => d) # create db
19
60
  Groonga::Schema.define{|s| # create schema
20
61
  s.create_table("E",:type => :hash,:key_type => "ShortText"){|t|
@@ -37,11 +78,9 @@ class E
37
78
  r.uri = u # update data
38
79
  r.graph = graph.to_s
39
80
  r.content = i.to_s
40
- r.time = i[E::Date][0].to_time
81
+ r.time = i[E::Date].do{|t|t[0].to_time}
41
82
  }
42
83
  self
43
- rescue Exception => x
44
- $stderr.puts x,x.message
45
84
  end
46
85
 
47
86
  # remove
@@ -50,52 +89,4 @@ class E
50
89
  graph.keys.push(uri).map{|u|g[u].delete}
51
90
  end
52
91
 
53
- # query
54
- fn 'graph/roonga',->d,e,m{
55
-
56
- # db
57
- ga = E.groonga
58
-
59
- # search expression
60
- q = e['q']
61
-
62
- # context
63
- g = e["context"] || d.env['SERVER_NAME']
64
-
65
- # offset
66
- start = e['start'].do{|c|c.to_i} || 0
67
-
68
- # number of results
69
- c = e['c'].do{|c|c.to_i.max(1000).min(0)} || 8
70
-
71
- # exec expression
72
- r = q ? ga.select{|r|(r['graph'] == g) & r["content"].match(q)} : # expression if exists
73
- ga.select{|r| r['graph'] == g} # ordered set (index date-range)
74
-
75
- # are further results traversible?
76
- down = r.size > start+c
77
- up = !(start<=0)
78
-
79
- # sort results
80
- r = r.sort(e.has_key?('score') ? [["_score"]] : [["time", "descending"]],:offset => start,:limit => c)
81
-
82
- # pagination resources
83
- m['prev']={'uri' => 'prev','url' => '/search','start' => start + c, 'c' => c} if down
84
- m['next']={'uri' => 'next','url' => '/search','start' => start - c, 'c' => c} if up
85
-
86
- # search-result identifiers
87
- r = r.map{|r| r['.uri'].E }
88
-
89
- # fragment identifiers
90
- m['frag'] = {'uri' => 'frag', 'res' => r}
91
-
92
- # containing documents
93
- r.map(&:docs).flatten.uniq.map{|r| m[r.uri] = r.env e}
94
-
95
- # no 404 on 0 results - searchbox view
96
- m['/s']={'uri'=>'/s'} if m.empty?
97
-
98
- m # result set
99
- }
100
-
101
92
  end
data/infod/Es/html.rb ADDED
@@ -0,0 +1,271 @@
1
+ #watch __FILE__
2
+
3
+ def H _
4
+ # Ruby object-literal syntax as HTML constructors
5
+ case _
6
+ when Hash then
7
+ '<'+(_[:_]||:div).to_s+(_.keys-[:_,:c]).map{|a|
8
+ ' '+a.to_s+'='+"'"+
9
+ _[a].to_s.hsub({"'"=>'%27',
10
+ '>'=>'%3E',
11
+ '<'=>'%3C'})+"'"}.join+'>'+
12
+ (_[:c] ? (H _[:c]) : '')+
13
+ (_[:_] == :link ? '' : ('</'+(_[:_]||:div).to_s+'>'))
14
+ when Array then
15
+ _.map{|n|H n}.join
16
+ else
17
+ _.to_s
18
+ end
19
+ end
20
+
21
+ class H
22
+
23
+ def H.[] h; H h end
24
+
25
+ def H.js a,inline=false
26
+ p=a+'.js'
27
+ inline ? {_: :script, c: p.E.r} :
28
+ {_: :script, type: "text/javascript", src: p}
29
+ end
30
+
31
+ def H.once e,n,*h
32
+ return if e[n]
33
+ e[n]=true
34
+ h
35
+ end
36
+ end
37
+
38
+ class Array
39
+ def html table=true
40
+ map(&:html).join ' '
41
+ end
42
+ end
43
+
44
+ class Object
45
+ def html *a
46
+ name = self.class
47
+ href = "https://duckduckgo.com/?q=ruby+#{name}"
48
+ "<a href=#{href}><b>#{name}</b></a>"
49
+ end
50
+ end
51
+
52
+ class String
53
+ def br
54
+ gsub(/\n/,"<br>\n")
55
+ end
56
+ def href name=nil
57
+ '<a href="'+self+'">'+(name||(Fn 'abbrURI',self))+'</a>'
58
+ end
59
+ def html
60
+ if match /\A(\/|http)[\S]+\Z/
61
+ href
62
+ else
63
+ self
64
+ end
65
+ rescue
66
+ self
67
+ end
68
+ end
69
+
70
+ class Fixnum
71
+ def html; H({_: :input, type: :number, value: to_s}) end
72
+ end
73
+
74
+ class Float
75
+ def html; H({_: :input, type: :number, value: to_s}) end
76
+ end
77
+
78
+ class TrueClass
79
+ def html; H({_: :input, type: :checkbox, title: :True, checked: :checked}) end
80
+ end
81
+
82
+ class FalseClass
83
+ def html; H({_: :input, type: :checkbox, title: :False}) end
84
+ end
85
+
86
+ class Hash
87
+ def html
88
+ H({_: :table, class: :html, c:
89
+ map{|k,v|
90
+ {_: :tr, property: k, c:
91
+ [{_: :td,
92
+ c: {_: :a, name: k,
93
+ href: (k == 'uri' ? v : k),
94
+ c: (Fn 'abbrURI',k)}, class: :key},
95
+ {_: :td,
96
+ c: (case k
97
+ when E::Content
98
+ {_: :pre, style: "white-space: pre-wrap", c: v}
99
+ when 'uri'
100
+ u = v.E
101
+ {_: :a, id: u, href: u.url, c: v}
102
+ else
103
+ v.html
104
+ end), class: :val}].cr}}.cr})
105
+ end
106
+ end
107
+
108
+ class E
109
+ def html name=nil,l=false
110
+ (l ? url : uri).href name
111
+ end
112
+
113
+ def link
114
+ html '#',true
115
+ end
116
+
117
+ fn 'abbrURI',->u{
118
+ u.to_s.sub(/(?<scheme>[a-z]+:\/\/)?(?<abbr>.*?)(?<frag>[^#\/]*)$/,
119
+ '<div class="abbr"><div class="scheme">\k<scheme></div>\k<abbr></div><div class="frag">\k<frag></div>')}
120
+
121
+ fn 'head',->d,e{
122
+ [{_: :title, c: e.uri},
123
+ (Fn 'head.formats',e),
124
+ (Fn 'head.icon')].cr}
125
+
126
+ fn 'head.formats',->e{
127
+ formats = %w{text/plain text/n3 application/json+ld}
128
+ formats.map{|f|
129
+ {_: :link, rel: :meta, type: f,
130
+ href:'http://' + e['SERVER_NAME'] + e['REQUEST_PATH'] + e.q.merge({'format' => f}).qs}}.cr}
131
+
132
+ fn 'head.icon',->{{_: :link, href:'/css/misc/favicon.ico', rel: :icon}}
133
+
134
+ fn 'view/select',->d,e{#(Fn 'view/divine/set',d,e)||
135
+ (Fn 'view/divine/files',d,e)||
136
+ d.values.map{|r|Fn 'view/divine/resource',r,e}}
137
+
138
+ # default view
139
+ F['view'] = F['view/select']
140
+
141
+ fn 'view/base',->d,e{
142
+ [H.once(e,'base',H.css('/css/html')),
143
+ d.values.map(&:html)]}
144
+
145
+ # select a view based on RDF-type majority
146
+ fn 'view/divine/set',->d,e{
147
+ # we'd be throwing away oddball resources to select one view for all of them
148
+ # but maybe you have a specific reason to
149
+ # inbuilt views support calling on set or per-resource basis, via "once" spec of set-wide components
150
+ # so views are selected per-resource unless you hack here..
151
+ }
152
+
153
+ fn 'view/divine/files',->d,e{
154
+ d.values.map{|e|e.E.base}.do{|b|
155
+ s = b.size.to_f # size of set
156
+ t = 0.42 # threshold, max of 0.5 as file and RDF resource are separate
157
+ if b.grep(/^msg\./).size / s > t
158
+ Fn 'view/threads',d,e
159
+
160
+ elsif b.grep(AudioFile).size / s > t
161
+ Fn 'view/audio', d,e
162
+
163
+ elsif b.grep(/(gif|jpe?g|png)$/i).size / s > t
164
+ Fn 'view/th', d,e
165
+
166
+ elsif b.grep(/\.log$/).size / s > t
167
+ Fn 'view/chat', d,e
168
+
169
+ else false
170
+ end}}
171
+
172
+ # select a view for a RDF resource
173
+ fn 'view/divine/resource',->r,e{
174
+ graph = {r.uri => r}
175
+ view = F['view/base']
176
+ # find types, skipping malformed/missing info
177
+ if r.class == Hash
178
+ r[Type].do{|types|
179
+ views = types.map{|t|
180
+ # discard non-URIs
181
+ t.uri if t.respond_to? :uri}.
182
+ compact.map{|t|
183
+ subtype = t
184
+ type = subtype.split(/\//)[-2]
185
+ [F['view/' + subtype],
186
+ F['view/' + type]]}.flatten.compact
187
+ view = views[0] unless views.empty?}
188
+ end
189
+ view[graph,e]}
190
+
191
+ # multiple views (comma-separated)
192
+ fn 'view/multi',->d,e{
193
+ e.q['views'].split(',').map{|v|
194
+ F['view/'+v].do{|f|f[d,e]}}}
195
+
196
+ def triplrBlob
197
+ glob.select(&:f).do{|f|f.map{|r|
198
+ yield r.uri,Type,E('blob')
199
+ yield r.uri,Content,r.r}} end
200
+
201
+ def triplrHref enc=nil
202
+ puts "triplrHref #{uri} #{d}"
203
+ yield uri,Content,(f && read).do{|r|enc ? r.force_encoding(enc).to_utf8 : r}.hrefs
204
+ end
205
+
206
+ fn Render+'text/html',->d,e{
207
+ v = e.q['view'].to_s
208
+ h = F['head/'+v] || F['head']
209
+ v = F['view/'+v] || F['view']
210
+ H(e.q.has_key?('un') ? v[d,e] :
211
+ ['<!DOCTYPE html>',
212
+ {_: :html,
213
+ c: [{_: :head,
214
+ c: ['<meta charset="utf-8" />',
215
+ h[d,e]]},
216
+ {_: :body, c: v[d,e]}].cr}].cr)}
217
+
218
+
219
+ # property-selector toolbar (requires RDFa views)
220
+ fn 'view/p',->d,e{
221
+ [H.once(e,'property.toolbar',H.once(e,'p',(H.once e,:mu,H.js('/js/mu')),
222
+ H.js('/js/p'),
223
+ H.css('/css/table')),
224
+ {_: :a, href: '#', c: '-', id: :hideP},
225
+ {_: :a, href: '#', c: '+', id: :showP},
226
+ {_: :span, id: 'properties',
227
+ c: E.graphProperties(d).map{|k|
228
+ {_: :a, class: :n, href: k, c: k.label+' '}}},
229
+ {_: :style, id: :pS},
230
+ {_: :style, id: :lS}),
231
+ (Fn 'view/'+(e.q['pv']||'table'),d,e)]}
232
+
233
+ # table-cell placement on sparse matrix of rows/columns
234
+ # cal.rb contains an example usage
235
+ fn 'view/t',->d,e,l=nil,a=nil{
236
+ [H.once(e,'table',H.css('/css/table')),
237
+ {_: :table, c:
238
+ {_: :tbody, c: (Fn 'table/'+(l||e.q['table']),d).do{|t|
239
+ rx = t.keys.max
240
+ rm = t.keys.min
241
+ c = t.values.map(&:keys)
242
+ cm = c.map(&:min).min
243
+ cx = c.map(&:max).max
244
+ (rm..rx).map{|r|
245
+ {_: :tr, c:
246
+ t[r].do{|r|
247
+ (cm..cx).map{|c|
248
+ r[c].do{|c|
249
+ {_: :td, class: :cell, c:(Fn 'view/'+(a||e.q['cellview']),c,e)}
250
+ }||{_: :td}}}}}}}}]}
251
+
252
+ fn 'view/table',->i,e{[H.css('/css/table'),(Fn 'table',i.values,e)]}
253
+
254
+ fn 'table',->es,q=nil{
255
+ ks = {} # predicate table
256
+ es.map{|e|e.respond_to?(:keys) &&
257
+ e.keys.map{|k|ks[k]=true}}
258
+ keys = ks.keys
259
+ keys.empty? ? es.html :
260
+ H({_: :table,:class => :tab,
261
+ c: [{_: :tr,
262
+ c: keys.map{|k|
263
+ {_: :th, class: :label, property: k,
264
+ c: q ? {_: :a,
265
+ href: q['REQUEST_PATH']+q.q.except('reverse').merge({'sort'=>k}).merge(q.q.member?('reverse') ? {} : {'reverse'=>true}).qs,
266
+ c: (Fn 'abbrURI',k)} : k}}},
267
+ *es.map{|e|
268
+ {_: :tr, about: e.uri, c:
269
+ keys.map{|k| {_: :td, property: k, c: e[k].send(k=='uri' ? :href : :html)} }}}]})}
270
+
271
+ end