infod 0.0.3.1 → 0.0.3.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.
@@ -1,66 +1,52 @@
1
1
  #watch __FILE__
2
2
  class E
3
+ =begin
4
+ gem install rroonga
5
+ a ruby full-text searcher & column-store
6
+ http://groonga.org/
7
+ =end
8
+
9
+ fn 'view/'+Search+'Groonga',-> d,e {{_: :form, action: '/', c: [{_: :input, name: :q, style: 'font-size:2em'},{_: :input, type: :hidden, name: :graph, value: :groonga}]}}
3
10
 
4
- # adaptor for ruby text-search-engine & column-store
5
- # http://groonga.org/ http://ranguba.org/
6
-
7
- # query
8
- fn 'protograph/roonga',->d,e,m{
9
-
10
- # groonga engine
11
- ga = E.groonga
12
-
13
- # search expression
14
- q = e['q']
15
-
16
- # context
17
- g = e["context"] || d.env['SERVER_NAME']
11
+ fn 'protograph/groonga',->d,e,m{
12
+ E.groonga.do{|ga|
13
+ q = e['q'] # search expression
14
+ g = e["context"] || d.env['SERVER_NAME'] # context
18
15
 
19
16
  begin
20
- # execute
21
- r = (q && !q.empty?) ? ga.select{|r|(r['graph'] == g) & r["content"].match(q)} : # expression if exists
22
- ga.select{|r| r['graph'] == g} # ordered set (index date-range)
23
-
24
- # offset, size
25
- start = e['start'].do{|c| c.to_i.max(r.size - 1).min 0 } || 0
26
- c = (e['c']||e['count']).do{|c|c.to_i.max(10000).min(0)} || 8
17
+ m['/'] = {Type => E[Search+'Groonga']} # add a groonga resource to the graph
27
18
 
28
- # are further results traversible?
29
- down = r.size > start+c
30
- up = !(start<=0)
31
-
32
- # sort results
33
- r = r.sort(e.has_key?('score') ? [["_score"]] : [["time", "descending"]],:offset => start,:limit => c)
19
+ r = (q && !q.empty?) ? ga.select{|r|(r['graph'] == g) & r["content"].match(q)} : # expression if exists
20
+ ga.select{|r| r['graph'] == g} # or just an ordered set
34
21
 
35
- # results -> graph
36
- r = r.map{|r| r['.uri'].E }
37
- (r.map &:docs).flatten.uniq.map{|r| m[r.uri] = r.env e}
22
+ start = e['start'].do{|c| c.to_i.max(r.size - 1).min 0 } || 0 # offset
23
+ c = (e['c']||e['count']).do{|c|c.to_i.max(10000).min(0)} || 16 # count
24
+ down = r.size > start+c # prev
25
+ up = !(start<=0) # next
26
+ r = r.sort(e.has_key?('best') ? [["_score"]]:[["time","descending"]],:offset =>start,:limit =>c) # sort
27
+ r = r.map{|r| r['.uri'].E } # URI
28
+ (r.map &:docs).flatten.uniq.map{|r|m[r.uri] = r.env e} # set resource thunks
38
29
 
39
- m['#'] = {'uri' => '#',
40
- RDFs+'member' => r,
41
- Type=>E[HTTP+'Response']}
42
- m['#'][Prev]={'uri' => '/search' + {'q' => q, 'start' => start + c, 'c' => c}.qs} if down
43
- m['#'][Next]={'uri' => '/search' + {'q' => q, 'start' => start - c, 'c' => c}.qs} if up
44
- m['/search'] = {Type => E[Search]}
30
+ m['#'] = {'uri' => '#', RDFs+'member' => r, Type=>E[HTTP+'Response']} # add pagination data to request-graph
31
+ m['#'][Prev]={'uri' => '/' + {'graph' => 'groonga', 'q' => q, 'start' => start + c, 'c' => c}.qs} if down
32
+ m['#'][Next]={'uri' => '/' + {'graph' => 'groonga', 'q' => q, 'start' => start - c, 'c' => c}.qs} if up
45
33
 
46
34
  rescue Groonga::SyntaxError => x
47
- m['/search'] = {Type => E[Search]}
48
- m['#'] = {
49
- Type => E[COGS+'Exception'],
50
- Title => "bad expression",
51
- Content => CGI.escapeHTML(x.message)}
35
+ m['#'] = {Type => E[COGS+'Exception'], Title => "invalid expr", Content => CGI.escapeHTML(x.message)}
52
36
  e['nocache']=true
53
37
  end
54
38
 
55
- F['docsID'][m,e]}
39
+ F['docsID'][m,e]}}
56
40
 
57
41
  def E.groonga
58
- @groonga ||= (require 'groonga'
59
- E['/E/groonga'].groonga
60
- Groonga["E"] )
42
+ @groonga ||=
43
+ (begin require 'groonga'
44
+ E['/E/groonga'].groonga
45
+ Groonga["E"]
46
+ rescue LoadError => e; end)
61
47
  end
62
48
 
63
- # load or create groongaDB at URI
49
+ # init groongaDB
64
50
  def groonga
65
51
  return Groonga::Database.open d if e # open db
66
52
  dirname.mk # create containing dir
@@ -78,17 +64,16 @@ class E
78
64
  %w{graph content}.map{|c| t.index("E." + c) }}}
79
65
  end
80
66
 
81
- # index resource
67
+ # add
82
68
  def roonga graph="global", m = self.graph
83
- # puts "text #{graph} < #{uri} #{m.keys.join ' '}"
84
- g = E.groonga # db
69
+ E.groonga.do{|g|
85
70
  m.map{|u,i|
86
71
  r = g[u] || g.add(u) # create or load entry
87
72
  r.uri = u # update data
88
73
  r.graph = graph.to_s
89
74
  r.content = i.to_s
90
75
  r.time = i[E::Date].do{|t|t[0].to_time}
91
- }
76
+ }}
92
77
  self
93
78
  end
94
79
 
@@ -1,6 +1,6 @@
1
1
  #watch __FILE__
2
2
 
3
- # Ruby to HTML
3
+ # (H) templates
4
4
  def H _
5
5
  case _
6
6
  when Hash
@@ -20,11 +20,17 @@ class H
20
20
  def H.[] h; H h end
21
21
 
22
22
  def H.js a,inline=false
23
- p=a+'.js'
23
+ p = a + '.js'
24
24
  inline ? {_: :script, c: p.E.r} :
25
25
  {_: :script, type: "text/javascript", src: p}
26
26
  end
27
27
 
28
+ def H.css a,inline=false
29
+ p = a + '.css'
30
+ inline ? {_: :style, c: p.E.r} :
31
+ {_: :link, href: p, rel: :stylesheet, type: E::MIME[:css]}
32
+ end
33
+
28
34
  def H.once e,n,*h
29
35
  return if e[n]
30
36
  e[n]=true
@@ -33,8 +39,8 @@ class H
33
39
  end
34
40
 
35
41
  class Array
36
- def html v=nil
37
- map{|e|e.html v}.join ' '
42
+ def html v=nil,g=nil
43
+ map{|e|e.html v,g}.join ' '
38
44
  end
39
45
  end
40
46
 
@@ -54,49 +60,52 @@ class String
54
60
  '<a href="'+self+'">' + (name||abbrURI) + '</a>'
55
61
  end
56
62
  def abbrURI
57
- sub /(?<scheme>[a-z]+:\/\/)?(?<abbr>.*?)(?<frag>[^#\/]*)$/,
58
- '<span class="abbr"><span class="scheme">\k<scheme></span>\k<abbr></span><span class="frag">\k<frag></span>'
63
+ sub /(?<scheme>[a-z]+:\/\/)?(?<abbr>.*?)(?<frag>[^#\/]+)\/?$/,'<span class="abbr"><span class="scheme">\k<scheme></span>\k<abbr></span><span class="frag">\k<frag></span>'
59
64
  end
60
- def html e=nil
61
- # CGI.escapeHTML self
65
+ def html e=nil,g=nil
62
66
  self
63
67
  end
64
68
  end
65
69
 
66
70
  class Fixnum
67
- def html e=nil; to_s end
71
+ def html e=nil,g=nil; to_s end
68
72
  end
69
73
 
70
74
  class Float
71
- def html e=nil; to_s end
75
+ def html e=nil,g=nil; to_s end
72
76
  end
73
77
 
74
78
  class TrueClass
75
- def html e=nil; H({_: :input, type: :checkbox, title: :True, checked: :checked}) end
79
+ def html e=nil,g=nil; H({_: :input, type: :checkbox, title: :True, checked: :checked}) end
76
80
  end
77
81
 
78
82
  class FalseClass
79
- def html e=nil; H({_: :input, type: :checkbox, title: :False}) end
83
+ def html e=nil,g=nil; H({_: :input, type: :checkbox, title: :False}) end
80
84
  end
81
85
 
86
+ IsBnode = /^_:/
87
+
82
88
  class Hash
83
- def html e={'SERVER_NAME'=>'localhost'}, key=true
84
- (keys.size == 1 && has_key?('uri')) ? url.href :
85
- H({_: :table, class: :html, c:
86
- map{|k,v|
87
- {_: :tr, property: k, c:
88
- [({_: :td,
89
- c: [{_: :a, name: k, href: (k == 'uri' ? v : k), c: k.to_s.abbrURI}], class: :key} if key),
90
- {_: :td,
91
- c: (case k
92
- when E::Content
93
- v
94
- when 'uri'
95
- u = v.E
96
- {_: :a, id: u, href: u.url, c: v}
97
- else
98
- v.html e
99
- end), class: :val}]}}})
89
+ def html e={'SERVER_NAME'=>'localhost'}, g={}, key=true
90
+ if keys.size == 1 && has_key?('uri')
91
+ if uri.match IsBnode
92
+ g[uri].do{|r|
93
+ r.html e,g,key } || uri.href
94
+ else
95
+ uri.href
96
+ end
97
+ else
98
+ H({_: :table, class: :html, c: map{|k,v|
99
+ unless k == 'uri' && (v.match IsBnode)
100
+ {_: :tr, property: k, c:
101
+ [k == E::Content ? {_: :td, class: :val, colspan: 2, c: v} :
102
+ [
103
+ ({_: :td, c: [{_: :a, name: k, href: (k == 'uri' ? v : k), c: k.to_s.abbrURI}], class: :key} if key),
104
+ {_: :td, c: k == 'uri' ? v.E.do{|u| {_: :a, id: u, href: u.url, c: v}} : v.html(e,g), class: :val},
105
+ ]]}
106
+ end
107
+ }})
108
+ end
100
109
  end
101
110
  end
102
111
 
@@ -107,17 +116,21 @@ class E
107
116
  end
108
117
 
109
118
  fn 'view',->d,e{
110
- d.values.sort_by{|r| r[Date].do{|d| d[0].to_s} || ''}.reverse.
111
- map{|r| Fn 'view/select',r,e }}
119
+ d.values.select{|r|
120
+ !r.has_key?('uri') || # URI field missing
121
+ !r.uri.match(IsBnode) # blank node
122
+ true
123
+ }.
124
+ sort_by{|r| r[Date].do{|d| d[0].to_s} || ''}.reverse.
125
+ map{|r| Fn 'view/select',r,e,d}}
112
126
 
113
- fn 'view/base',->d,e,k=true{
127
+ fn 'view/base',->d,e,k=true,graph=nil{
114
128
  [H.once(e,'base',H.css('/css/html')),
115
- d.values.map{|v|v.html e,k}]}
129
+ d.values.map{|v|v.html e,graph,k}]}
116
130
 
117
- fn 'view/select',->r,e{
131
+ fn 'view/select',->r,e,d{
118
132
  graph = {r.uri => r}
119
- view = F['view/base']
120
- # find types, skipping malformed/missing info
133
+ view = nil
121
134
  if r.class == Hash
122
135
  (r[Type].class==Array ? r[Type] : [r[Type]]).do{|types|
123
136
  views = types.map{|t|
@@ -131,13 +144,11 @@ class E
131
144
  flatten.compact
132
145
  view = views[0] unless views.empty?}
133
146
  end
134
- view[graph,e]}
135
-
136
- # multiple views (comma-separated)
137
- fn 'view/multi',->d,e{
138
- e.q['views'].do{|vs|
139
- vs.split(',').map{|v|
140
- F['view/'+v].do{|f|f[d,e]}}}}
147
+ if !view # default view
148
+ F['view/base'][graph,e,true,d]
149
+ else
150
+ view[graph,e]
151
+ end}
141
152
 
142
153
  # enumerate available views
143
154
  fn 'view/?',->d,e{
@@ -154,10 +165,24 @@ class E
154
165
  yield uri,Content,(f && read).do{|r|enc ? r.force_encoding(enc).to_utf8 : r}.hrefs
155
166
  end
156
167
 
168
+ require 'nokogiri'
169
+ def nokogiri; Nokogiri::HTML.parse read end
170
+
171
+ F['HTMLbody'] = -> b {
172
+ b.to_s.split(/<body[^>]*>/)[-1].to_s.split(/<\/body>/)[0] }
173
+
174
+ F['cleanHTML'] = -> b {
175
+ h = Nokogiri::HTML.fragment F['HTMLbody'][b]
176
+ h.css('iframe').remove
177
+ h.css('script').remove
178
+ h.xpath("//@*[starts-with(name(),'on')]").remove
179
+ h.to_s
180
+ }
181
+
157
182
  def contentURIresolve *f
158
183
  send(*f){|s,p,o|
159
184
  yield s, p, p == Content ?
160
- (Nokogiri::HTML.parse o).do{|o|
185
+ (Nokogiri::HTML.fragment o).do{|o|
161
186
  o.css('a').map{|a|
162
187
  if a.has_attribute? 'href'
163
188
  (a.set_attribute 'href', (URI.join s, (a.attr 'href'))) rescue nil
@@ -216,16 +241,18 @@ class E
216
241
  "table= layout arg required"
217
242
  end}
218
243
 
219
- fn 'view/table',->i,e{[H.css('/css/table'),(Fn 'table',i.values,e)]}
220
-
221
- fn 'table',->es,q=nil{ p = {}
222
- es.map{|e|e.respond_to?(:keys) &&
223
- e.keys.map{|k|p[k]=true}}
224
- keys = p.keys
225
- keys.empty? ? es.html :
226
- H({_: :table,:class => :tab,
227
- c: [{_: :tr, c: keys.map{|k|{_: :th, class: :label, property: k, c: k.abbrURI}}},
228
- *es.map{|e|
229
- {_: :tr, about: e.uri, c: keys.map{|k| {_: :td, property: k, c: k=='uri' ? e.E.html : e[k].html}}}}]})}
244
+ fn 'view/table',->g,e{
245
+ keys = E.graphProperties g
246
+ v = g.values
247
+ e.q['sort'].do{|p|
248
+ p = p.expand
249
+ v = v.sort_by{|s|
250
+ s[p].do{|o|
251
+ o[0].to_s}||''}} # cast to a single type (String) so sort will work. every class seems to have a #to_s
252
+ v = v.reverse if e.q['reverse']
253
+ [H.css('/css/table'),
254
+ {_: :table,:class => :tab,
255
+ c: [{_: :tr, c: keys.map{|k|{_: :th, class: :label, property: k, c: k.abbrURI}}},
256
+ v.map{|e|{_: :tr, about: e.uri, c: keys.map{|k| {_: :td, property: k, c: k=='uri' ? e.E.html : e[k].html}}}}]}]}
230
257
 
231
258
  end
@@ -78,7 +78,7 @@ class E
78
78
  # view
79
79
  {uri: s,
80
80
  # img and link to containing resource
81
- c: ->{"<a href='#{v.uri.to_s.do{|u|u.path? ? u : u.E.url}}'><img style='float:left;#{h}' src='#{s}'></a>"}}}}.flatten.map{|i|
81
+ c: ->{"<a href='#{v.uri}'><img style='float:left;#{h}' src='#{s}'></a>"}}}}.flatten.map{|i|
82
82
 
83
83
  # show and mark as seen
84
84
  !seen[i[:uri]] &&
@@ -1,4 +1,4 @@
1
- watch __FILE__
1
+ #watch __FILE__
2
2
  class E
3
3
 
4
4
  # POSIX-fs based index of triples
@@ -11,9 +11,9 @@ class E
11
11
  end
12
12
 
13
13
  # index a triple - no type-normalization
14
+ # we jsut rotate them, use existing k/v store and set @noIndex to stop looping infinitely to index the index..
14
15
  def indexEdit p,o,a
15
16
  return if @noIndex
16
- # puts "index #{p} #{o} #{a}"
17
17
  p.pIndex.noIndex[o,self,a]
18
18
  end
19
19
  def noIndex
@@ -36,79 +36,46 @@ class E
36
36
  g
37
37
  end
38
38
 
39
- # subtree traverse
40
- fn 'set/subtree',->d,r,m{
41
- c =(r['c'].do{|c|c.to_i + 1} || 3).max(100) # one extra for start of next-page
42
- o = r['d'] =~ /^a/ ? :asc : :desc # direction
43
-
44
- ('/'.E.take c, o, d.uri).do{|s| # take subtree
45
- desc, asc = o == :desc ? # orient pagination hints
46
- [s.pop, s[0]] : [s[0], s.pop]
39
+ fn 'set/depth',->d,r,m{ # depth-first
40
+ global = !r.has_key?('local')
41
+ p = global ? d.pathSegment : d
42
+ loc = global ? '' : '&local'
43
+ c = ((r['c'].do{|c|c.to_i} || 12) + 1).max(128) # an extra for next-page pointer
44
+ o = r['d'] =~ /^a/ ? :asc : :desc # direction
45
+ (p.take c, o, r['offset'].do{|o|o.E}).do{|s| # take subtree
46
+ first, last = s[0], s.size > 1 && s.pop
47
+ desc, asc = o == :asc ? [first,last] : [last,first]
47
48
  u = m['#']
48
49
  u[Type] = E[HTTP+'Response']
49
- u[Prev] = {'uri' => desc.url + {'d' => 'desc'}.qs} if desc
50
- u[Next] = {'uri' => asc.url + {'d' => 'asc'}.qs} if asc
50
+ u[Prev] = {'uri' => d.uri + "?set=depth&c=#{c-1}&d=desc#{loc}&offset=" + (URI.escape desc.uri)} if desc
51
+ u[Next] = {'uri' => d.uri + "?set=depth&c=#{c-1}&d=asc#{loc}&offset=" + (URI.escape asc.uri)} if asc
51
52
  s }}
52
53
 
53
- # subtree traverse index on p+o cursor
54
- fn 'set/index',->d,r,m{
55
- top = r['p'].expand.E
56
- count = r['c'] &&
57
- r['c'].to_i.max(1000) || 8
58
- dir = r['d'] && r['d'].match(/^(a|de)sc$/) &&
59
- r['d'].to_sym || :desc
60
-
61
- (top.rangePO count+1, dir, r['offset'], r['o']).do{|s|
62
- # orient pagination pointers
63
- ascending = r['d'].do{|d| d == 'asc' }
64
- first, last = s[0], s.size > 1 && s.pop
65
- desc, asc = ascending && [first,last] || [last,first]
66
- # response description
67
- u = m['#']
68
- u[RDFs+'member'] = s
69
- u[Prev] = {'uri' => '/index/' + r['p'] + '/' + CGI.escape(r['o']) + {'offset' => desc.uri, 'c' => count}.qs } if desc
70
- u[Next] = {'uri' => '/index/' + r['p'] + '/' + CGI.escape(r['o']) + {'offset' => asc.uri, 'c' => count}.qs } if asc
71
-
72
- s.map(&:docs).flatten.uniq }}
73
-
74
- fn '/index/GET',->e,r{
75
- a = e.pathSegment.uri[7..-1].split '/',2
76
- r.q['set'] = 'index'
77
- r.q['p'] = a[0]
78
- r.q['o'] = CGI.unescape a[1]
79
- e.response}
80
-
81
- # predicate index
82
54
  def pIndex
83
55
  shorten.prependURI '/index/'
84
56
  end
85
57
 
86
- # predicate-object index
87
58
  def poIndex o
88
59
  pIndex.concatURI o
89
60
  end
90
61
 
91
- # predicate-object index lookup
62
+ # predicate+object pair lookup
92
63
  def po o
93
64
  pIndex[o.class == E ? o : literal(o)]
94
65
  end
95
66
 
96
- # range query - predicate
97
67
  def rangeP size=8, dir=:desc, offset=nil, object=nil
98
68
  pIndex.subtree(size,dir,offset).map &:ro
99
69
  end
100
70
 
101
- # range query - predicate-object
102
71
  def rangePO n=8,d=:desc,s=nil,o
103
72
  poIndex(o).subtree(n,d,s).map &:ro
104
73
  end
105
74
 
106
- # E -> [node]
107
75
  def subtree *a
108
76
  u.take *a
109
77
  end
110
78
 
111
- # E -> [E]
112
79
  def take *a
113
80
  no.take(*a).map &:E
114
81
  end
@@ -129,49 +96,32 @@ end
129
96
 
130
97
  class Pathname
131
98
 
132
- # take N els from fs tree in sorted, depth-first order
133
99
  def take count=1000, direction=:desc, offset=nil
100
+ offset = offset.d if offset
134
101
 
135
- # construct offset-path
136
- offset = (to_s + offset).gsub(/\/+/,'/').E.path if offset
137
-
138
- # in-range indicator
139
- ok = false
140
-
141
- # result set
102
+ ok = false # in-range mark
142
103
  set=[]
143
-
144
- # asc/desc operators
145
104
  v,m={asc: [:id,:>=],
146
105
  desc: [:reverse,:<=]}[direction]
147
106
 
148
- # visitation function
149
107
  visit=->nodes{
150
-
151
- # sort nodes in asc or desc order
152
108
  nodes.sort_by(&:to_s).send(v).each{|n|
153
109
  ns = n.to_s
154
- # have we got enough nodes?
155
110
  return if 0 >= count
156
111
 
157
- # continue if
158
- (# already in-range
159
- ok ||
160
- # no offset specified
161
- !offset ||
162
- # offset satisfies in-range operator
112
+ (ok || # already in-range
113
+ !offset || # no offset required
163
114
  (sz = [ns,offset].map(&:size).min
164
- ns[0..sz-1].send(m,offset[0..sz-1]))) && (
165
- if !(c = n.c).empty? # has children?
115
+ ns[0..sz-1].send(m,offset[0..sz-1]))) &&
116
+ (if !(c = n.c).empty? # has children?
166
117
  visit.(c) # visit children
167
118
  else
168
- count = count - 1 # decrement wanted-nodes count
119
+ count = count - 1 # decrement nodes-left count
169
120
  set.push n # add node to result-set
170
- ok = true # iterator is now within range
121
+ ok = true # mark iterator as within range
171
122
  end )}}
172
123
 
173
- visit.(c) # start
174
- # result set
124
+ visit.(c)
175
125
  set
176
126
  end
177
127