jk-ferret 0.11.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (228) hide show
  1. data/CHANGELOG +24 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README +90 -0
  4. data/RELEASE_CHANGES +137 -0
  5. data/RELEASE_NOTES +60 -0
  6. data/Rakefile +443 -0
  7. data/TODO +109 -0
  8. data/TUTORIAL +231 -0
  9. data/bin/ferret-browser +79 -0
  10. data/ext/BZLIB_blocksort.c +1094 -0
  11. data/ext/BZLIB_bzlib.c +1578 -0
  12. data/ext/BZLIB_compress.c +672 -0
  13. data/ext/BZLIB_crctable.c +104 -0
  14. data/ext/BZLIB_decompress.c +626 -0
  15. data/ext/BZLIB_huffman.c +205 -0
  16. data/ext/BZLIB_randtable.c +84 -0
  17. data/ext/STEMMER_api.c +66 -0
  18. data/ext/STEMMER_libstemmer.c +93 -0
  19. data/ext/STEMMER_stem_ISO_8859_1_danish.c +337 -0
  20. data/ext/STEMMER_stem_ISO_8859_1_dutch.c +624 -0
  21. data/ext/STEMMER_stem_ISO_8859_1_english.c +1117 -0
  22. data/ext/STEMMER_stem_ISO_8859_1_finnish.c +762 -0
  23. data/ext/STEMMER_stem_ISO_8859_1_french.c +1246 -0
  24. data/ext/STEMMER_stem_ISO_8859_1_german.c +503 -0
  25. data/ext/STEMMER_stem_ISO_8859_1_hungarian.c +1230 -0
  26. data/ext/STEMMER_stem_ISO_8859_1_italian.c +1065 -0
  27. data/ext/STEMMER_stem_ISO_8859_1_norwegian.c +297 -0
  28. data/ext/STEMMER_stem_ISO_8859_1_porter.c +749 -0
  29. data/ext/STEMMER_stem_ISO_8859_1_portuguese.c +1017 -0
  30. data/ext/STEMMER_stem_ISO_8859_1_spanish.c +1093 -0
  31. data/ext/STEMMER_stem_ISO_8859_1_swedish.c +307 -0
  32. data/ext/STEMMER_stem_ISO_8859_2_romanian.c +998 -0
  33. data/ext/STEMMER_stem_KOI8_R_russian.c +700 -0
  34. data/ext/STEMMER_stem_UTF_8_danish.c +339 -0
  35. data/ext/STEMMER_stem_UTF_8_dutch.c +634 -0
  36. data/ext/STEMMER_stem_UTF_8_english.c +1125 -0
  37. data/ext/STEMMER_stem_UTF_8_finnish.c +768 -0
  38. data/ext/STEMMER_stem_UTF_8_french.c +1256 -0
  39. data/ext/STEMMER_stem_UTF_8_german.c +509 -0
  40. data/ext/STEMMER_stem_UTF_8_hungarian.c +1234 -0
  41. data/ext/STEMMER_stem_UTF_8_italian.c +1073 -0
  42. data/ext/STEMMER_stem_UTF_8_norwegian.c +299 -0
  43. data/ext/STEMMER_stem_UTF_8_porter.c +755 -0
  44. data/ext/STEMMER_stem_UTF_8_portuguese.c +1023 -0
  45. data/ext/STEMMER_stem_UTF_8_romanian.c +1004 -0
  46. data/ext/STEMMER_stem_UTF_8_russian.c +694 -0
  47. data/ext/STEMMER_stem_UTF_8_spanish.c +1097 -0
  48. data/ext/STEMMER_stem_UTF_8_swedish.c +309 -0
  49. data/ext/STEMMER_stem_UTF_8_turkish.c +2205 -0
  50. data/ext/STEMMER_utilities.c +478 -0
  51. data/ext/analysis.c +1710 -0
  52. data/ext/analysis.h +266 -0
  53. data/ext/api.h +26 -0
  54. data/ext/array.c +125 -0
  55. data/ext/array.h +62 -0
  56. data/ext/bitvector.c +96 -0
  57. data/ext/bitvector.h +594 -0
  58. data/ext/bzlib.h +282 -0
  59. data/ext/bzlib_private.h +503 -0
  60. data/ext/compound_io.c +384 -0
  61. data/ext/config.h +52 -0
  62. data/ext/document.c +159 -0
  63. data/ext/document.h +63 -0
  64. data/ext/except.c +102 -0
  65. data/ext/except.h +176 -0
  66. data/ext/extconf.rb +15 -0
  67. data/ext/ferret.c +416 -0
  68. data/ext/ferret.h +94 -0
  69. data/ext/field_index.c +262 -0
  70. data/ext/field_index.h +52 -0
  71. data/ext/filter.c +157 -0
  72. data/ext/fs_store.c +493 -0
  73. data/ext/global.c +458 -0
  74. data/ext/global.h +302 -0
  75. data/ext/hash.c +524 -0
  76. data/ext/hash.h +515 -0
  77. data/ext/hashset.c +192 -0
  78. data/ext/hashset.h +215 -0
  79. data/ext/header.h +58 -0
  80. data/ext/helper.c +63 -0
  81. data/ext/helper.h +21 -0
  82. data/ext/index.c +6804 -0
  83. data/ext/index.h +935 -0
  84. data/ext/internal.h +1019 -0
  85. data/ext/lang.c +10 -0
  86. data/ext/lang.h +68 -0
  87. data/ext/libstemmer.h +79 -0
  88. data/ext/mempool.c +88 -0
  89. data/ext/mempool.h +43 -0
  90. data/ext/modules.h +190 -0
  91. data/ext/multimapper.c +351 -0
  92. data/ext/multimapper.h +60 -0
  93. data/ext/posh.c +1006 -0
  94. data/ext/posh.h +973 -0
  95. data/ext/priorityqueue.c +149 -0
  96. data/ext/priorityqueue.h +155 -0
  97. data/ext/q_boolean.c +1621 -0
  98. data/ext/q_const_score.c +162 -0
  99. data/ext/q_filtered_query.c +212 -0
  100. data/ext/q_fuzzy.c +280 -0
  101. data/ext/q_match_all.c +149 -0
  102. data/ext/q_multi_term.c +673 -0
  103. data/ext/q_parser.c +3103 -0
  104. data/ext/q_phrase.c +1206 -0
  105. data/ext/q_prefix.c +98 -0
  106. data/ext/q_range.c +682 -0
  107. data/ext/q_span.c +2390 -0
  108. data/ext/q_term.c +337 -0
  109. data/ext/q_wildcard.c +167 -0
  110. data/ext/r_analysis.c +2626 -0
  111. data/ext/r_index.c +3468 -0
  112. data/ext/r_qparser.c +635 -0
  113. data/ext/r_search.c +4490 -0
  114. data/ext/r_store.c +513 -0
  115. data/ext/r_utils.c +1131 -0
  116. data/ext/ram_store.c +476 -0
  117. data/ext/scanner.c +895 -0
  118. data/ext/scanner.h +36 -0
  119. data/ext/scanner_mb.c +6701 -0
  120. data/ext/scanner_utf8.c +4415 -0
  121. data/ext/search.c +1864 -0
  122. data/ext/search.h +953 -0
  123. data/ext/similarity.c +151 -0
  124. data/ext/similarity.h +89 -0
  125. data/ext/sort.c +786 -0
  126. data/ext/stem_ISO_8859_1_danish.h +16 -0
  127. data/ext/stem_ISO_8859_1_dutch.h +16 -0
  128. data/ext/stem_ISO_8859_1_english.h +16 -0
  129. data/ext/stem_ISO_8859_1_finnish.h +16 -0
  130. data/ext/stem_ISO_8859_1_french.h +16 -0
  131. data/ext/stem_ISO_8859_1_german.h +16 -0
  132. data/ext/stem_ISO_8859_1_hungarian.h +16 -0
  133. data/ext/stem_ISO_8859_1_italian.h +16 -0
  134. data/ext/stem_ISO_8859_1_norwegian.h +16 -0
  135. data/ext/stem_ISO_8859_1_porter.h +16 -0
  136. data/ext/stem_ISO_8859_1_portuguese.h +16 -0
  137. data/ext/stem_ISO_8859_1_spanish.h +16 -0
  138. data/ext/stem_ISO_8859_1_swedish.h +16 -0
  139. data/ext/stem_ISO_8859_2_romanian.h +16 -0
  140. data/ext/stem_KOI8_R_russian.h +16 -0
  141. data/ext/stem_UTF_8_danish.h +16 -0
  142. data/ext/stem_UTF_8_dutch.h +16 -0
  143. data/ext/stem_UTF_8_english.h +16 -0
  144. data/ext/stem_UTF_8_finnish.h +16 -0
  145. data/ext/stem_UTF_8_french.h +16 -0
  146. data/ext/stem_UTF_8_german.h +16 -0
  147. data/ext/stem_UTF_8_hungarian.h +16 -0
  148. data/ext/stem_UTF_8_italian.h +16 -0
  149. data/ext/stem_UTF_8_norwegian.h +16 -0
  150. data/ext/stem_UTF_8_porter.h +16 -0
  151. data/ext/stem_UTF_8_portuguese.h +16 -0
  152. data/ext/stem_UTF_8_romanian.h +16 -0
  153. data/ext/stem_UTF_8_russian.h +16 -0
  154. data/ext/stem_UTF_8_spanish.h +16 -0
  155. data/ext/stem_UTF_8_swedish.h +16 -0
  156. data/ext/stem_UTF_8_turkish.h +16 -0
  157. data/ext/stopwords.c +410 -0
  158. data/ext/store.c +698 -0
  159. data/ext/store.h +799 -0
  160. data/ext/symbol.c +10 -0
  161. data/ext/symbol.h +23 -0
  162. data/ext/term_vectors.c +73 -0
  163. data/ext/threading.h +31 -0
  164. data/ext/win32.h +62 -0
  165. data/lib/ferret.rb +30 -0
  166. data/lib/ferret/browser.rb +246 -0
  167. data/lib/ferret/browser/s/global.js +192 -0
  168. data/lib/ferret/browser/s/style.css +148 -0
  169. data/lib/ferret/browser/views/document/list.rhtml +49 -0
  170. data/lib/ferret/browser/views/document/show.rhtml +27 -0
  171. data/lib/ferret/browser/views/error/index.rhtml +7 -0
  172. data/lib/ferret/browser/views/help/index.rhtml +8 -0
  173. data/lib/ferret/browser/views/home/index.rhtml +29 -0
  174. data/lib/ferret/browser/views/layout.rhtml +22 -0
  175. data/lib/ferret/browser/views/term-vector/index.rhtml +4 -0
  176. data/lib/ferret/browser/views/term/index.rhtml +199 -0
  177. data/lib/ferret/browser/views/term/termdocs.rhtml +1 -0
  178. data/lib/ferret/browser/webrick.rb +14 -0
  179. data/lib/ferret/document.rb +130 -0
  180. data/lib/ferret/field_infos.rb +44 -0
  181. data/lib/ferret/field_symbol.rb +87 -0
  182. data/lib/ferret/index.rb +973 -0
  183. data/lib/ferret/number_tools.rb +157 -0
  184. data/lib/ferret/version.rb +3 -0
  185. data/setup.rb +1555 -0
  186. data/test/long_running/largefile/tc_largefile.rb +46 -0
  187. data/test/test_all.rb +5 -0
  188. data/test/test_helper.rb +29 -0
  189. data/test/test_installed.rb +1 -0
  190. data/test/threading/number_to_spoken.rb +132 -0
  191. data/test/threading/thread_safety_index_test.rb +88 -0
  192. data/test/threading/thread_safety_read_write_test.rb +73 -0
  193. data/test/threading/thread_safety_test.rb +133 -0
  194. data/test/unit/analysis/tc_analyzer.rb +550 -0
  195. data/test/unit/analysis/tc_token_stream.rb +653 -0
  196. data/test/unit/index/tc_index.rb +867 -0
  197. data/test/unit/index/tc_index_reader.rb +699 -0
  198. data/test/unit/index/tc_index_writer.rb +447 -0
  199. data/test/unit/index/th_doc.rb +332 -0
  200. data/test/unit/query_parser/tc_query_parser.rb +238 -0
  201. data/test/unit/search/tc_filter.rb +156 -0
  202. data/test/unit/search/tc_fuzzy_query.rb +147 -0
  203. data/test/unit/search/tc_index_searcher.rb +67 -0
  204. data/test/unit/search/tc_multi_searcher.rb +128 -0
  205. data/test/unit/search/tc_multiple_search_requests.rb +58 -0
  206. data/test/unit/search/tc_search_and_sort.rb +179 -0
  207. data/test/unit/search/tc_sort.rb +49 -0
  208. data/test/unit/search/tc_sort_field.rb +27 -0
  209. data/test/unit/search/tc_spans.rb +190 -0
  210. data/test/unit/search/tm_searcher.rb +436 -0
  211. data/test/unit/store/tc_fs_store.rb +115 -0
  212. data/test/unit/store/tc_ram_store.rb +35 -0
  213. data/test/unit/store/tm_store.rb +34 -0
  214. data/test/unit/store/tm_store_lock.rb +68 -0
  215. data/test/unit/tc_document.rb +81 -0
  216. data/test/unit/tc_field_symbol.rb +26 -0
  217. data/test/unit/ts_analysis.rb +2 -0
  218. data/test/unit/ts_index.rb +2 -0
  219. data/test/unit/ts_largefile.rb +4 -0
  220. data/test/unit/ts_query_parser.rb +2 -0
  221. data/test/unit/ts_search.rb +2 -0
  222. data/test/unit/ts_store.rb +2 -0
  223. data/test/unit/ts_utils.rb +2 -0
  224. data/test/unit/utils/tc_bit_vector.rb +295 -0
  225. data/test/unit/utils/tc_number_tools.rb +117 -0
  226. data/test/unit/utils/tc_priority_queue.rb +106 -0
  227. data/test/utils/content_generator.rb +226 -0
  228. metadata +319 -0
data/ext/symbol.c ADDED
@@ -0,0 +1,10 @@
1
+ #include <stdlib.h>
2
+ #include "symbol.h"
3
+
4
+
5
+ FrtSymbol frt_intern_and_free(char *str)
6
+ {
7
+ FrtSymbol sym = (FrtSymbol)rb_intern(str);
8
+ free(str);
9
+ return sym;
10
+ }
data/ext/symbol.h ADDED
@@ -0,0 +1,23 @@
1
+ #ifndef FRT_SYMBOL_H
2
+ #define FRT_SYMBOL_H
3
+
4
+ #include <ruby.h>
5
+
6
+ typedef void *FrtSymbol;
7
+
8
+ VALUE rb_id2str(ID id);
9
+ #define frt_symbol_init()
10
+ #define frt_intern(str) (FrtSymbol)rb_intern(str)
11
+ extern FrtSymbol frt_intern_and_free(char *str);
12
+
13
+ #define FRT_I frt_intern
14
+ #define FRT_InF frt_intern_and_free
15
+ #define FRT_S(_sym) rb_id2name((ID)_sym)
16
+
17
+ #define FSYM2SYM(_sym) ID2SYM((ID)_sym)
18
+ #define SYM2FSYM(_sym) (FrtSymbol)SYM2ID(_sym)
19
+
20
+ #define frt_sym_hash(sym) frt_str_hash(rb_id2name((ID)sym))
21
+ #define frt_sym_len(sym) strlen(rb_id2name((ID)sym))
22
+
23
+ #endif
@@ -0,0 +1,73 @@
1
+ #include <string.h>
2
+ #include "index.h"
3
+ #include "array.h"
4
+ #include "helper.h"
5
+ #include "symbol.h"
6
+ #include "internal.h"
7
+
8
+ /****************************************************************************
9
+ *
10
+ * TermVector
11
+ *
12
+ ****************************************************************************/
13
+
14
+ void tv_destroy(TermVector *tv)
15
+ {
16
+ int i = tv->term_cnt;
17
+ while (i > 0) {
18
+ i--;
19
+ free(tv->terms[i].text);
20
+ free(tv->terms[i].positions);
21
+ }
22
+ free(tv->offsets);
23
+ free(tv->terms);
24
+ free(tv);
25
+ }
26
+
27
+ int tv_scan_to_term_index(TermVector *tv, const char *term)
28
+ {
29
+ int lo = 0; /* search starts array */
30
+ int hi = tv->term_cnt - 1; /* for 1st element < n, return its index */
31
+ int mid;
32
+ int cmp;
33
+ char *mid_term;
34
+
35
+ while (hi >= lo) {
36
+ mid = (lo + hi) >> 1;
37
+ mid_term = tv->terms[mid].text;
38
+ cmp = strcmp(term, mid_term);
39
+ if (cmp < 0) {
40
+ hi = mid - 1;
41
+ }
42
+ else if (cmp > 0) {
43
+ lo = mid + 1;
44
+ }
45
+ else { /* found a match */
46
+ return mid;
47
+ }
48
+ }
49
+ return lo;
50
+ }
51
+
52
+ int tv_get_term_index(TermVector *tv, const char *term)
53
+ {
54
+ int index = tv_scan_to_term_index(tv, term);
55
+ if (index < tv->term_cnt && (0 == strcmp(term, tv->terms[index].text))) {
56
+ /* found term */
57
+ return index;
58
+ }
59
+ else {
60
+ return -1;
61
+ }
62
+ }
63
+
64
+ TVTerm *tv_get_tv_term(TermVector *tv, const char *term)
65
+ {
66
+ int index = tv_get_term_index(tv, term);
67
+ if (index >= 0) {
68
+ return &(tv->terms[index]);
69
+ }
70
+ else {
71
+ return NULL;
72
+ }
73
+ }
data/ext/threading.h ADDED
@@ -0,0 +1,31 @@
1
+ #ifndef FRT_THREADING_H
2
+ #define FRT_THREADING_H
3
+
4
+ #include "hash.h"
5
+ #define UNTHREADED 1
6
+
7
+ typedef void * frt_mutex_t;
8
+ typedef struct FrtHash *frt_thread_key_t;
9
+ typedef int frt_thread_once_t;
10
+ #define FRT_MUTEX_INITIALIZER NULL
11
+ #define FRT_MUTEX_RECURSIVE_INITIALIZER NULL
12
+ #define FRT_THREAD_ONCE_INIT 1;
13
+ #define frt_mutex_init(a, b)
14
+ #define frt_mutex_lock(a)
15
+ #define frt_mutex_trylock(a)
16
+ #define frt_mutex_unlock(a)
17
+ #define frt_mutex_destroy(a)
18
+ #define frt_thread_key_create(a, b) frb_thread_key_create(a, b)
19
+ #define frt_thread_key_delete(a) frb_thread_key_delete(a)
20
+ #define frt_thread_setspecific(a, b) frb_thread_setspecific(a, b)
21
+ #define frt_thread_getspecific(a) frb_thread_getspecific(a)
22
+ #define frt_thread_exit(a)
23
+ #define frt_thread_once(a, b) frb_thread_once(a, b)
24
+
25
+ void frb_thread_once(int *once_control, void (*init_routine)(void));
26
+ void frb_thread_key_create(frt_thread_key_t *key, frt_free_ft destroy);
27
+ void frb_thread_key_delete(frt_thread_key_t key);
28
+ void frb_thread_setspecific(frt_thread_key_t key, const void *pointer);
29
+ void *frb_thread_getspecific(frt_thread_key_t key);
30
+
31
+ #endif
data/ext/win32.h ADDED
@@ -0,0 +1,62 @@
1
+ #ifndef FRT_WIN32_H
2
+ #define FRT_WIN32_H
3
+
4
+ #ifdef __cplusplus
5
+ extern "C" {
6
+ #endif
7
+
8
+ #include "global.h"
9
+ #include <io.h>
10
+
11
+ struct dirent
12
+ {
13
+ char *d_name;
14
+ };
15
+
16
+ typedef struct DIR
17
+ {
18
+ struct _finddata_t find_data;
19
+ struct dirent de;
20
+ long handle;
21
+ } DIR;
22
+
23
+ DIR *opendir(const char *dirname)
24
+ {
25
+ DIR *d = FRT_ALLOC_AND_ZERO(DIR);
26
+ char dirname_buf[FRT_MAX_FILE_PATH];
27
+ long ff_res;
28
+ sprintf(dirname_buf, "%s\\*", dirname);
29
+ ff_res = _findfirst(dirname_buf, &d->find_data);
30
+ if (ff_res < 0) {
31
+ free(d);
32
+ d = NULL;
33
+ } else {
34
+ d->de.d_name = NULL;
35
+ d->handle = ff_res;
36
+ }
37
+ return d;
38
+ }
39
+
40
+ struct dirent *readdir(DIR *d)
41
+ {
42
+ /* _findfirst already returned so do _findnext */
43
+ if (d->de.d_name != NULL) {
44
+ if (_findnext(d->handle, &d->find_data) < 0) {
45
+ return NULL;
46
+ }
47
+ }
48
+ d->de.d_name = d->find_data.name;
49
+ return &d->de;
50
+ }
51
+
52
+ void closedir(DIR *d)
53
+ {
54
+ _findclose(d->handle);
55
+ free(d);
56
+ }
57
+
58
+ #ifdef __cplusplus
59
+ } // extern "C"
60
+ #endif
61
+
62
+ #endif
data/lib/ferret.rb ADDED
@@ -0,0 +1,30 @@
1
+ #--
2
+ # Copyright (c) 2005 David Balmain
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+ # :include: ../TUTORIAL
24
+ $: << File.expand_path(File.join(File.dirname(__FILE__), "../ext"))
25
+ require 'ferret_ext'
26
+ require 'ferret/version'
27
+ require 'ferret/document'
28
+ require 'ferret/index'
29
+ require 'ferret/field_infos'
30
+ require 'ferret/field_symbol'
@@ -0,0 +1,246 @@
1
+ require 'erb'
2
+
3
+
4
+ module Ferret::Browser
5
+ class Delegator
6
+ def initialize(reader, path)
7
+ @reader, @path = reader, path
8
+ end
9
+
10
+ def run(env)
11
+ controller, action, args = :home, :index, nil
12
+ query_string = env['QUERY_STRING']||''
13
+ params = parse_query_string(query_string)
14
+ req_path = env['PATH_INFO'].gsub(/\/+/, '/')
15
+ case req_path
16
+ when '/'
17
+ # nothing to do
18
+ when /^\/?([-a-zA-Z]+)\/?$/
19
+ controller = $1
20
+ when /^\/?([-a-zA-Z]+)\/([-a-zA-Z]+)\/?(.*)?$/
21
+ controller = $1
22
+ action = $2
23
+ args = $3
24
+ else
25
+ controller = :error
26
+ args = req_path
27
+ end
28
+ controller_vars = {
29
+ :params => params,
30
+ :req_path => req_path,
31
+ :query_string => query_string,
32
+ }
33
+ delegate(controller, action, args, controller_vars)
34
+ end
35
+
36
+ private
37
+
38
+ def delegate(controller, action, args, controller_vars)
39
+ begin
40
+ controller = to_const(controller, 'Controller').
41
+ new(@reader, @path, controller_vars)
42
+ controller.send(action, args)
43
+ rescue Exception => e
44
+ puts e.to_s
45
+ controller_vars[:params][:error] = e
46
+ ErrorController.new(@reader, @path, controller_vars).index
47
+ end
48
+ end
49
+
50
+ def to_const(str, suffix='')
51
+ Ferret::Browser.const_get(str.to_s.split('-').
52
+ map {|w| w.capitalize}.join('') + suffix)
53
+ end
54
+
55
+ # from _why's camping
56
+ def unescape_uri(s)
57
+ s.tr('+', ' ').gsub(/%([\da-f]{2})/in){[$1].pack('H*')}
58
+ end
59
+
60
+ def parse_query_string(query_string, delim = '&;')
61
+ m = proc {|_,o,n| o.update(n, &m) rescue ([*o] << n)}
62
+ (query_string||'').split(/[#{delim}] */n).
63
+ inject({}) { |hash, param| key, val = unescape_uri(param).split('=',2)
64
+ hash.update(key.split(/[\]\[]+/).reverse.
65
+ inject(val) { |x,i| Hash[i,x] }, &m)
66
+ }
67
+ end
68
+ end
69
+
70
+ module ViewHelper
71
+ # truncates the string at the first space after +len+ characters
72
+ def truncate(str, len = 80)
73
+ if str and str.length > len and (add = str[len..-1].index(' '))
74
+ str = str[0, len + add] + '&#8230;'
75
+ end
76
+ str
77
+ end
78
+
79
+ def tick_or_cross(t)
80
+ "<img src=\"/s/i/#{t ? 'tick' : 'cross'}.png\" alt=\"#{t ? 'yes' : 'no'}\"/>"
81
+ end
82
+ end
83
+
84
+ class Controller
85
+ include ViewHelper
86
+ APP_DIR = File.expand_path(File.join(File.dirname(__FILE__), "browser/"))
87
+ STATIC_DIR = File.expand_path(File.join(APP_DIR, "s/"))
88
+
89
+ def initialize(reader, path, vars)
90
+ @reader = reader
91
+ @path = path
92
+ vars.each_pair {|key, val| instance_eval("@#{key} = val")}
93
+ @controller_path = pathify(self.class.to_s.gsub(/.*:/, ''))
94
+ end
95
+
96
+ def method_missing(meth_id, *args)
97
+ render(:action => meth_id)
98
+ end
99
+
100
+ protected
101
+
102
+ def load_page(page)
103
+ File.read(File.join(APP_DIR, page))
104
+ end
105
+
106
+ def render(options = {})
107
+ options = {
108
+ :controller => @controller_path,
109
+ :action => :index,
110
+ :status => 200,
111
+ :content_type => 'text/html',
112
+ :env => nil,
113
+ :layout => 'views/layout.rhtml',
114
+ }.update(options)
115
+
116
+ path = "views/#{options[:controller]}/#{options[:action]}.rhtml"
117
+ content = ERB.new(load_page(path)).result(lambda{})
118
+ if options[:layout]
119
+ content = ERB.new(load_page(options[:layout])).result(lambda{})
120
+ end
121
+
122
+ return options[:status], options[:content_type], content
123
+ end
124
+
125
+ # takes an optional block to set optional attributes in the links
126
+ def paginate(idx, max, url, &b)
127
+ return '' if max == 0
128
+ url = url.gsub(%r{^/?(.*?)/?$}, '\1')
129
+ b ||= lambda{}
130
+ link = lambda {|*args|
131
+ i, title, text = args
132
+ "<a href=\"/#{url}/#{i}#{'?' + @query_string if @query_string}\" " +
133
+ "#{'onclick="return false;"' if (i == idx)} " +
134
+ "class=\"#{'disabled ' if (i == idx)}#{b.call(i)}\" " +
135
+ "title=\"#{title||"Go to page #{i}"}\">#{text||i}</a>"
136
+ }
137
+ res = '<div class="nav">'
138
+ if (idx > 0)
139
+ res << link.call(idx - 1, "Go to previous page", "&#171; Previous")
140
+ else
141
+ res << "<a href=\"/#{url}/0\" onclick=\"return false;\" " +
142
+ "class=\"disabled\" title=\"Disabled\">&#171; Previous</a>"
143
+ end
144
+ if idx < 10
145
+ idx.times {|i| res << link.call(i)}
146
+ else
147
+ (0..2).each {|i| res << link.call(i)}
148
+ res << '&nbsp;&#8230;&nbsp;'
149
+ ((idx-4)...idx).each {|i| res << link.call(i)}
150
+ end
151
+ res << link.call(idx, 'Current Page')
152
+ if idx > (max - 10)
153
+ ((idx+1)...max).each {|i| res << link.call(i)}
154
+ else
155
+ ((idx+1)..(idx+4)).each {|i| res << link.call(i)}
156
+ res << '&nbsp;&#8230;&nbsp;'
157
+ ((max-3)...max).each {|i| res << link.call(i)}
158
+ end
159
+ if (idx < (max - 1))
160
+ res << link.call(idx + 1, "Go to next page", "Next &#187;")
161
+ else
162
+ res << "<a href=\"/#{url}/#{max-1}\" onclick=\"return false;\" " +
163
+ "class=\"disabled\" title=\"Disabled\"}\">Next &#187;</a>"
164
+ end
165
+ res << '</div>'
166
+ end
167
+ private
168
+
169
+ def pathify(str)
170
+ str.gsub(/Controller$/, '').gsub(/([a-z])([A-Z])/) {"#{$1}-#{$2}"}.downcase
171
+ end
172
+ end
173
+
174
+ class ErrorController < Controller
175
+ def index
176
+ render(:status => 404)
177
+ end
178
+ end
179
+
180
+ class HomeController < Controller
181
+ end
182
+
183
+ class DocumentController < Controller
184
+ def list(page = 0)
185
+ @page = (page||0).to_i
186
+ @page_size = @params[:page_size]||10
187
+ @first = @page * @page_size
188
+ @last = [@reader.max_doc, (@page + 1) * @page_size].min
189
+ render(:action => :list)
190
+ end
191
+ alias :index :list
192
+
193
+ def show(doc_id)
194
+ doc_id = @params['doc_id']||doc_id||'0'
195
+ if doc_id !~ /^\d+$/
196
+ raise ArgumentError.new("invalid document number '#{doc_id}'")
197
+ end
198
+ @doc_id = doc_id.to_i
199
+ @doc = @reader[@doc_id].load unless @reader.deleted?(@doc_id)
200
+ render(:action => :show)
201
+ end
202
+
203
+ private
204
+
205
+ def choose_document(doc_id='')
206
+ <<-EOF
207
+ <form action="" method="get" onsubmit="location.href='/document/show/' + document.getElementById('doc_id').value;return false;">
208
+ <label for="doc_id">Go to document:
209
+ <input type="text" id="doc_id" name="doc_id" size="4" value="#{@doc_id}"/>
210
+ </label>
211
+ </form>
212
+ EOF
213
+ end
214
+
215
+ def paginate_docs
216
+ paginate(@doc_id, @reader.max_doc, '/document/show/') {|i|
217
+ 'deleted' if @reader.deleted?(i)
218
+ }
219
+ end
220
+ end
221
+
222
+ class TermController < Controller
223
+ def show(field)
224
+ if field and field.length > 0
225
+ @field = field.to_sym
226
+ @terms = @reader.terms(@field).to_json(:fast)
227
+ end
228
+ render(:action => :index)
229
+ end
230
+
231
+ def termdocs(args)
232
+ args = args.split('/')
233
+ @field = args.shift.intern
234
+ @term = args.join('/')
235
+ render(:action => :termdocs,
236
+ :content_type => 'text/plain',
237
+ :layout => false)
238
+ end
239
+ end
240
+
241
+ class TermVectorController < Controller
242
+ end
243
+
244
+ class HelpController < Controller
245
+ end
246
+ end