infod 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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