rwdeliza 0.03

Sign up to get free protection for your applications and to get access to all the features.
Files changed (222) hide show
  1. data/ElizaData/database/db.1 +7015 -0
  2. data/ElizaData/database/db.10 +7001 -0
  3. data/ElizaData/database/db.11 +7001 -0
  4. data/ElizaData/database/db.12 +7003 -0
  5. data/ElizaData/database/db.13 +7003 -0
  6. data/ElizaData/database/db.14 +7008 -0
  7. data/ElizaData/database/db.15 +7001 -0
  8. data/ElizaData/database/db.16 +7001 -0
  9. data/ElizaData/database/db.17 +7001 -0
  10. data/ElizaData/database/db.18 +7001 -0
  11. data/ElizaData/database/db.19 +7001 -0
  12. data/ElizaData/database/db.2 +7001 -0
  13. data/ElizaData/database/db.20 +5467 -0
  14. data/ElizaData/database/db.3 +7001 -0
  15. data/ElizaData/database/db.4 +7001 -0
  16. data/ElizaData/database/db.5 +7001 -0
  17. data/ElizaData/database/db.6 +7001 -0
  18. data/ElizaData/database/db.7 +7001 -0
  19. data/ElizaData/database/db.8 +7001 -0
  20. data/ElizaData/database/db.9 +7001 -0
  21. data/ElizaData/responses/hello.res +1 -0
  22. data/ElizaData/responses/i_agree.res +1 -0
  23. data/ElizaData/responses/i_am_listening_to_you.res +1 -0
  24. data/ElizaData/responses/please_explain.res +1 -0
  25. data/ElizaData/responses/thank_you.res +1 -0
  26. data/ElizaData/tiny.dict +211 -0
  27. data/ElizaData/words/adjectives1.words +906 -0
  28. data/ElizaData/words/adjectives2w.words +1 -0
  29. data/ElizaData/words/noun0.words +15 -0
  30. data/ElizaData/words/noun1.words +1391 -0
  31. data/ElizaData/words/noun2.words +1924 -0
  32. data/ElizaData/words/noun4.words +330 -0
  33. data/ElizaData/words/pronoun1.words +6 -0
  34. data/ElizaData/words/verb4.words +350 -0
  35. data/ElizaData/words/verb42.words +391 -0
  36. data/ElizaData/words/verb42w.words +1 -0
  37. data/ElizaData/words/verb43.words +402 -0
  38. data/ElizaData/words/verb43w.words +1 -0
  39. data/ElizaData/words/verb45.words +452 -0
  40. data/ElizaData/words/verb4w.words +1 -0
  41. data/ElizaData/words/verb5.words +13 -0
  42. data/ElizaData/words/verb61.words +35 -0
  43. data/ElizaData/words/verb62.words +41 -0
  44. data/ElizaData/words/verb83.words +17 -0
  45. data/Readme.txt +462 -0
  46. data/bin/rwdeliza +19 -0
  47. data/code/01rwdcore/01rwdcore.rb +29 -0
  48. data/code/01rwdcore/02helptexthashbegin.rb +4 -0
  49. data/code/01rwdcore/03helptexthash.rb +23 -0
  50. data/code/01rwdcore/04helptextend.rb +6 -0
  51. data/code/01rwdcore/jumplinkcommand.rb +26 -0
  52. data/code/01rwdcore/openhelpwindow.rb +31 -0
  53. data/code/01rwdcore/returntomain.rb +10 -0
  54. data/code/01rwdcore/rundocuments.rb +10 -0
  55. data/code/01rwdcore/runeditconfiguration.rb +10 -0
  56. data/code/01rwdcore/runhelpabout.rb +10 -0
  57. data/code/01rwdcore/runopentinkerdocument.rb +7 -0
  58. data/code/01rwdcore/rwdtinkerversion.rb +22 -0
  59. data/code/01rwdcore/rwdwindowreturn.rb +9 -0
  60. data/code/01rwdcore/selectiontab.rb +9 -0
  61. data/code/01rwdcore/setuphelpaboutoptions.rb +13 -0
  62. data/code/01rwdcore/setuptinkerdocuments.rb +6 -0
  63. data/code/01rwdcore/test_cases.rb +109 -0
  64. data/code/01rwdcore/test_harness.rb +13 -0
  65. data/code/01rwdcore/uploadreturns.rb +62 -0
  66. data/code/dd0viewphoto/dd0viewphoto.rb +3 -0
  67. data/code/superant.com.rwdeliza/0uninstallapplet.rb +10 -0
  68. data/code/superant.com.rwdeliza/eliza01.rb +45 -0
  69. data/code/superant.com.rwdeliza/helptexthashrwdeliza.rb +39 -0
  70. data/code/superant.com.rwdeliza/openhelpwindowrwdeliza.rb +23 -0
  71. data/code/superant.com.rwdeliza/runrwdshellwindow.rb +12 -0
  72. data/code/superant.com.rwdeliza/rwdtinkerversion.rb +10 -0
  73. data/code/superant.com.rwdeliza/tagsentence.rb +39 -0
  74. data/code/superant.com.rwdtinkerbackwindow/diagnostictab.rb +19 -0
  75. data/code/superant.com.rwdtinkerbackwindow/helptexthashtinkerwin2.rb +61 -0
  76. data/code/superant.com.rwdtinkerbackwindow/initiateapplets.rb +240 -0
  77. data/code/superant.com.rwdtinkerbackwindow/installgemapplet.rb +34 -0
  78. data/code/superant.com.rwdtinkerbackwindow/installremotegem.rb +20 -0
  79. data/code/superant.com.rwdtinkerbackwindow/listgemdirs.rb +12 -0
  80. data/code/superant.com.rwdtinkerbackwindow/listgemzips.rb +53 -0
  81. data/code/superant.com.rwdtinkerbackwindow/listinstalledfiles.rb +12 -0
  82. data/code/superant.com.rwdtinkerbackwindow/listzips.rb +27 -0
  83. data/code/superant.com.rwdtinkerbackwindow/loadconfigurationrecord.rb +22 -0
  84. data/code/superant.com.rwdtinkerbackwindow/loadconfigurationvariables.rb +14 -0
  85. data/code/superant.com.rwdtinkerbackwindow/network.rb +87 -0
  86. data/code/superant.com.rwdtinkerbackwindow/openappletname.rb +19 -0
  87. data/code/superant.com.rwdtinkerbackwindow/openhelpwindowtinkerwin2.rb +38 -0
  88. data/code/superant.com.rwdtinkerbackwindow/remotegemlist.rb +24 -0
  89. data/code/superant.com.rwdtinkerbackwindow/removeapplet.rb +46 -0
  90. data/code/superant.com.rwdtinkerbackwindow/removeappletvariables.rb +52 -0
  91. data/code/superant.com.rwdtinkerbackwindow/runremoteinstall.rb +11 -0
  92. data/code/superant.com.rwdtinkerbackwindow/runrwdtinkerbackwindow.rb +15 -0
  93. data/code/superant.com.rwdtinkerbackwindow/rwdtinkerwin2version.rb +13 -0
  94. data/code/superant.com.rwdtinkerbackwindow/saveconfigurationrecord.rb +19 -0
  95. data/code/superant.com.rwdtinkerbackwindow/viewappletcontents.rb +22 -0
  96. data/code/superant.com.rwdtinkerbackwindow/viewgemappletcontents.rb +24 -0
  97. data/code/superant.com.words/dictlookup.rb +20 -0
  98. data/code/superant.com.words/runfortunewindow.rb +14 -0
  99. data/code/superant.com.words/runrwdwordsbackwindow.rb +10 -0
  100. data/code/superant.com.words/runrwdwordsversion.rb +14 -0
  101. data/code/superant.com.words/rwdtinkerversion.rb +10 -0
  102. data/code/zz0applicationend/zz0end.rb +4 -0
  103. data/configuration/language.dist +8 -0
  104. data/configuration/rwdapplicationidentity.dist +3 -0
  105. data/configuration/rwdtinker.dist +18 -0
  106. data/configuration/rwdweliza-0.03.dist +31 -0
  107. data/configuration/tinkerwin2variables.dist +17 -0
  108. data/gui/00coreguibegin/applicationguitop.rwd +4 -0
  109. data/gui/frontwindow0/cc0openphoto.rwd +22 -0
  110. data/gui/frontwindowselections/00selectiontabbegin.rwd +11 -0
  111. data/gui/frontwindowselections/jumplinkcommands.rwd +15 -0
  112. data/gui/frontwindowselections/wwselectionend.rwd +3 -0
  113. data/gui/frontwindowtdocuments/00documentbegin.rwd +6 -0
  114. data/gui/frontwindowtdocuments/tinkerdocuments.rwd +14 -0
  115. data/gui/frontwindowtdocuments/zzdocumentend.rwd +8 -0
  116. data/gui/helpaboutbegin/zzzrwdlasttab.rwd +6 -0
  117. data/gui/helpaboutbegin/zzzzhelpscreenstart.rwd +3 -0
  118. data/gui/helpaboutbegin/zzzzzzhelpabouttab.rwd +15 -0
  119. data/gui/helpaboutzend/helpscreenend.rwd +3 -0
  120. data/gui/helpaboutzend/zhelpscreenstart2.rwd +3 -0
  121. data/gui/helpaboutzend/zzzzhelpabout2.rwd +15 -0
  122. data/gui/helpaboutzend/zzzzhelpscreen2end.rwd +3 -0
  123. data/gui/tinkerbackwindows/superant.com.refreshwindow/fortunerefreshwindowtwo.rwd +9 -0
  124. data/gui/tinkerbackwindows/superant.com.rwdeliza/1appname.rwd +5 -0
  125. data/gui/tinkerbackwindows/superant.com.rwdeliza/1eliza.rwd +21 -0
  126. data/gui/tinkerbackwindows/superant.com.rwdeliza/4sentance.rwd +21 -0
  127. data/gui/tinkerbackwindows/superant.com.rwdeliza/98jumplinkcommands.rwd +17 -0
  128. data/gui/tinkerbackwindows/superant.com.rwdeliza/zbackend.rwd +6 -0
  129. data/gui/tinkerbackwindows/superant.com.rwdschedulebackwindow/1appname.rwd +5 -0
  130. data/gui/tinkerbackwindows/superant.com.rwdschedulebackwindow/20downloadftp.rwd +45 -0
  131. data/gui/tinkerbackwindows/superant.com.rwdschedulebackwindow/67viewconfiguration.rwd +29 -0
  132. data/gui/tinkerbackwindows/superant.com.rwdschedulebackwindow/70rwddiagnostics.rwd +16 -0
  133. data/gui/tinkerbackwindows/superant.com.rwdschedulebackwindow/m01menubegin.rwd +18 -0
  134. data/gui/tinkerbackwindows/superant.com.rwdschedulebackwindow/zvbackend.rwd +6 -0
  135. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/1appname.rwd +5 -0
  136. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/40rwdlistzips.rwd +41 -0
  137. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/45installremotezip.rwd +44 -0
  138. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/50rwdlistapplets.rwd +44 -0
  139. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/60editconfiguration.rwd +30 -0
  140. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/70rwddiagnostics.rwd +29 -0
  141. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/81jumplinkcommands.rwd +17 -0
  142. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/9backend.rwd +6 -0
  143. data/gui/tinkerbackwindows/superant.com.tinkerhelpwindow/1appname.rwd +31 -0
  144. data/gui/tinkerbackwindows/superant.com.tinkerhelpwindow/9end.rwd +4 -0
  145. data/gui/tinkerbackwindows/superant.com.versionwindow/1appname.rwd +19 -0
  146. data/gui/tinkerbackwindows/superant.com.versionwindow/helpaboutwindow.rwd +17 -0
  147. data/gui/tinkerbackwindows/superant.com.words/1appname.rwd +4 -0
  148. data/gui/tinkerbackwindows/superant.com.words/1dictionary.rwd +19 -0
  149. data/gui/tinkerbackwindows/superant.com.words/3rwdfortune.rwd +14 -0
  150. data/gui/tinkerbackwindows/superant.com.words/77jumplinkcommands.rwd +17 -0
  151. data/gui/tinkerbackwindows/superant.com.words/z9end.rwd +6 -0
  152. data/gui/zzcoreguiend/yy9rwdend.rwd +4 -0
  153. data/init.rb +277 -0
  154. data/installed/rwdweliza-0.03.inf +24 -0
  155. data/installed/temp.rb +1 -0
  156. data/lang/en/rwdcore/languagefile.rb +58 -0
  157. data/lang/es/rwdcore/languagefile-es.rb +62 -0
  158. data/lang/fr/rwdcore/languagefile.rb +64 -0
  159. data/lang/jp/rwdcore/languagefile.rb +72 -0
  160. data/lang/nl/rwdcore/languagefile.rb +75 -0
  161. data/lib/dict.rb +438 -0
  162. data/lib/druida.rb +499 -0
  163. data/lib/hashslice.rb +71 -0
  164. data/lib/linguistics.rb +360 -0
  165. data/lib/linguistics/en.rb +1601 -0
  166. data/lib/linguistics/en/infinitive.rb +1148 -0
  167. data/lib/linguistics/en/linkparser.rb +142 -0
  168. data/lib/linguistics/en/wordnet.rb +253 -0
  169. data/lib/linguistics/iso639.rb +456 -0
  170. data/lib/linkparser.rb +461 -0
  171. data/lib/linkparser/connection.rb +81 -0
  172. data/lib/linkparser/connector.rb +201 -0
  173. data/lib/linkparser/definition.rb +225 -0
  174. data/lib/linkparser/dictionary.rb +208 -0
  175. data/lib/linkparser/linkage.rb +185 -0
  176. data/lib/linkparser/log.rb +39 -0
  177. data/lib/linkparser/sentence.rb +79 -0
  178. data/lib/linkparser/utils.rb +540 -0
  179. data/lib/linkparser/word.rb +92 -0
  180. data/lib/rconftool.rb +380 -0
  181. data/lib/rwd/browser.rb +123 -0
  182. data/lib/rwd/ftools.rb +174 -0
  183. data/lib/rwd/mime.rb +328 -0
  184. data/lib/rwd/net.rb +866 -0
  185. data/lib/rwd/ruby.rb +889 -0
  186. data/lib/rwd/rwd.rb +1942 -0
  187. data/lib/rwd/sgml.rb +236 -0
  188. data/lib/rwd/thread.rb +63 -0
  189. data/lib/rwd/tree.rb +371 -0
  190. data/lib/rwd/xml.rb +101 -0
  191. data/lib/zip/ioextras.rb +114 -0
  192. data/lib/zip/stdrubyext.rb +111 -0
  193. data/lib/zip/tempfile_bugfixed.rb +195 -0
  194. data/lib/zip/zip.rb +1378 -0
  195. data/lib/zip/zipfilesystem.rb +558 -0
  196. data/lib/zip/ziprequire.rb +61 -0
  197. data/rwd_files/HowTo_Eliza.txt +195 -0
  198. data/rwd_files/HowTo_Tinker.txt +471 -0
  199. data/rwd_files/HowTo_TinkerWin2.txt +202 -0
  200. data/rwd_files/Readme.txt +57 -0
  201. data/rwd_files/RubyWebDialogs.html +6 -0
  202. data/rwd_files/favicon.ico +0 -0
  203. data/rwd_files/rdoc-style.css +175 -0
  204. data/rwd_files/rwdapplications.html +54 -0
  205. data/rwd_files/tinker.png +0 -0
  206. data/rwdconfig.dist +21 -0
  207. data/rwdeliza.rb +1 -0
  208. data/tests/RubyGauge.rb +179 -0
  209. data/tests/checkdepends.sh +4 -0
  210. data/tests/cleancnf.sh +6 -0
  211. data/tests/makedist-rwdweliza.rb +56 -0
  212. data/tests/makedist.rb +66 -0
  213. data/tests/rdep.rb +354 -0
  214. data/tests/totranslate.lang +93 -0
  215. data/zips/rwdwaddresses-1.05.zip +0 -0
  216. data/zips/rwdwcalc-0.61.zip +0 -0
  217. data/zips/rwdwgutenberg-0.09.zip +0 -0
  218. data/zips/rwdwschedule-1.04.zip +0 -0
  219. data/zips/rwdwshell-1.04.zip +0 -0
  220. data/zips/temp.rb +1 -0
  221. data/zips/wrubyslippers-1.06.zip +0 -0
  222. metadata +282 -0
@@ -0,0 +1,499 @@
1
+ #!/usr/bin/env ruby -w
2
+ #
3
+ #Copyright (c) 2007 Federico Zagarzaz�
4
+ #
5
+ #Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ #of this software and associated documentation files (the "Software"), to deal
7
+ #in the Software without restriction, including without limitation the rights
8
+ #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ #copies of the Software, and to permit persons to whom the Software is
10
+ #furnished to do so, subject to the following conditions:
11
+ #
12
+ #The above copyright notice and this permission notice shall be included in
13
+ #all copies or substantial portions of the Software.
14
+ #
15
+ #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ #THE SOFTWARE.
22
+ #
23
+ require 'optparse'
24
+ require 'ostruct'
25
+ require 'yaml'
26
+ require 'fileutils'
27
+ require 'enumerator'
28
+
29
+ # default settings
30
+ NAME = 'druida'
31
+ VER = '1.0.0'
32
+ DB_DIR_PATH = "ElizaData/database"
33
+ DB_PREFIX = '/db.'
34
+ DB_NELS = 1000 # max num of elements
35
+ # for each db file.
36
+ # little files =>
37
+ # faster and < mem
38
+ # when user wants
39
+ # a random quote.
40
+ LP = 5 # left padding
41
+ MAX_TAGS_LEN = 42
42
+ MAX_TAG_LEN = 40
43
+ MAX_STR_LEN = 700
44
+ MAX_STR_NLINES = 7 # quotes, not books
45
+ EX = <<EOF
46
+ Examples:
47
+ # #{NAME}
48
+ Prints a random quote.
49
+
50
+ # #{NAME} -t rating -s "4 > x > 1"
51
+ Prints all quotes with the given rating(s).
52
+
53
+ # #{NAME} -t tags -s wisdom,life
54
+ Search for quotes with the given tags.
55
+
56
+ # #{NAME} -t author -s 'Seneca' -p
57
+ Print a random quote from Seneca.
58
+
59
+ # #{NAME} -a
60
+ Adds quote to database.
61
+
62
+ # #{NAME} -e 12
63
+ Edit quote 12.
64
+
65
+ # #{NAME} -t author -s 'Joe Doe' \\
66
+ -t tags -s \"drugs,sex\" -t rating -s \">2\" \\
67
+ -t text -s 'wild'
68
+ Print quotes of 'Joe Doe', that have the
69
+ tags 'drugs' and 'sex', a rating of more than
70
+ two stars, and contain the word 'wild'.
71
+
72
+ EOF
73
+
74
+ class String
75
+ # return longest words of string
76
+ # (QuoteDB -> find_similar)
77
+ def key_words
78
+ arr = self.gsub(/[^\w\s]/, '').split(' ').sort_by {|w| w.length}
79
+ arr[(arr.length / 2)..(arr.length - 1)]
80
+ end
81
+ def count_lines
82
+ n = 0
83
+ scan(/\n/) {n+=1}
84
+ n
85
+ end
86
+ def count_words
87
+ n= 0
88
+ scan(/\b\S+\b/) {n+=1}
89
+ n
90
+ end
91
+ end
92
+
93
+ class Quote
94
+ attr_accessor :quote, :author, :info, :rating
95
+ attr_reader :tags
96
+ def initialize(a, r, t, i, q)
97
+ @quote = q
98
+ @author = a
99
+ @tags = t.uniq
100
+ @info = i
101
+ @rating = r.nil? ? 0 : r
102
+ end
103
+ def tags=(arr)
104
+ @tags = arr
105
+ @tags.uniq!
106
+ end
107
+ def print(n = nil, quiet = false)
108
+ puts @quote
109
+ puts ' '*LP + '-- ' + @author
110
+ puts ' '*LP + @info unless quiet
111
+ puts ' '*LP + "(rating: #{@rating})" unless quiet
112
+ puts ' '*LP + '(tags: ' + @tags.join(', ') + ')' unless quiet
113
+ puts ' '*LP + "[#{n}]" unless quiet || n == nil
114
+ end
115
+ end
116
+ class QuoteDB
117
+ def initialize(db_path, db_nums = [])
118
+ @db_path = db_path
119
+ @db = []
120
+ @db_nums = db_nums # -> save()
121
+
122
+ # convert relative to absolute quote index(for user), -> index()
123
+ @offset = @db_nums.length == 1 ? DB_NELS * (@db_nums[0] - 1) : 0
124
+
125
+ # database is split into multiple files of DB_NELS length
126
+ @db_nums.each {|n| @db += YAML.load_file(@db_path + n.to_s) }
127
+ end
128
+ def empty?
129
+ @db.empty?
130
+ end
131
+ def add(arr)
132
+ @db << Quote.new(*arr)
133
+ end
134
+ def del(n)
135
+ @db.delete_at(n) unless empty?
136
+ end
137
+ def last_num
138
+ empty? ? nil : @db.length - 1
139
+ end
140
+ def length
141
+ @db.length
142
+ end
143
+ def index(q)
144
+ @db.index(q) + @offset
145
+ end
146
+ def [](n)
147
+ empty? ? nil : @db[n]
148
+ end
149
+ def remove_duplicates
150
+ dups = []
151
+ @db.each do |q|
152
+ next if dups.include?(q)
153
+ a = find_similar(q.author, q.quote.key_words)
154
+ a.shift
155
+ dups += a if ! a.empty?
156
+ end
157
+ if ! dups.empty?
158
+ puts "removing #{dups.length} quotes..."
159
+ @db -= dups
160
+ end
161
+ end
162
+ def find_similar(author, key_words)
163
+ # pretty basic function, much room for improvement
164
+
165
+ # use the longest word in author's name to catch more
166
+ r = Regexp.new(author.key_words.last, true)
167
+ ret = []
168
+ @db.find_all {|q| q.author =~ r}.each do |q|
169
+ ret << q if key_words.all? {|w| q.quote.gsub(/[^\w\s]/, '') =~ Regexp.new(w, true) }
170
+ end
171
+ ret
172
+ end
173
+ def search_tags(arr, ret = [])
174
+ ret = @db.find_all {|q| arr.all? {|t| q.tags.include?(t)} } unless empty?
175
+ ret
176
+ end
177
+ def search_ratings(arr, ret = [])
178
+ return ret if empty? || arr.empty?
179
+ num_left = arr[0] =~ /\d/
180
+ num, cnd = num_left ? [arr[0].to_i, arr[1]] : [arr[1].to_i, arr[0]]
181
+ arr.slice!(0..1)
182
+
183
+ ret = @db.find_all do |q|
184
+ r = q.rating
185
+ case cnd
186
+ when '>' : num_left ? (num > r) : (r > num)
187
+ when '>=': num_left ? (num >= r) : (r >= num)
188
+ when '=' : num != r
189
+ when '<=': num_left ? (num <= r) : (r <= num)
190
+ when '<' : num_left ? (num < r) : (r < num)
191
+ end
192
+ end
193
+ search_ratings(arr, ret)
194
+ end
195
+ def search_quotes(str, ret = [])
196
+ return if empty?
197
+ r = Regexp.new(str, true)
198
+ ret = @db.find_all {|q| q.quote =~ r}
199
+ ret
200
+ end
201
+ def search_authors(str, ret = [])
202
+ return if empty?
203
+ r = Regexp.new(str, true)
204
+ ret = @db.find_all {|q| q.author =~ r}
205
+ ret
206
+ end
207
+ def save
208
+ puts 'saving...'
209
+
210
+ # delete all db files that were loaded(if any)
211
+ # to avoid files being left over when deleting quotes
212
+ @db_nums.each {|n| FileUtils.rm(@db_path + n.to_s) }
213
+
214
+ # modify only the db file that was loaded(editing)
215
+ i = @db_nums.empty? ? 1 : @db_nums.first
216
+
217
+ @db.each_slice(DB_NELS) do |a|
218
+ File.open(@db_path + i.to_s, 'w') {|f| YAML.dump(a, f)}
219
+ i += 1
220
+ end
221
+ end
222
+ end
223
+
224
+ def invalid_strings?(arr)
225
+ arr.each do |x|
226
+ return true unless x.length < MAX_STR_LEN
227
+ return true unless x.count_lines < MAX_STR_NLINES
228
+ end
229
+ false
230
+ end
231
+ def invalid_tags?(arr)
232
+ return true unless arr.empty? || arr.length <= MAX_TAGS_LEN
233
+ return true unless arr.empty? || ! arr.find {|t| t.length > MAX_TAG_LEN}
234
+ false
235
+ end
236
+ def invalid_rating?(r)
237
+ return true unless r.nil? || 0 <= r && r < 6
238
+ false
239
+ end
240
+
241
+ # handle search argument(s)
242
+ def parse_search(quotedb, type, str)
243
+ s, ret = str.dup, nil
244
+ case type
245
+ when :tags
246
+ s = s.split(',')
247
+ raise "Invalid --search argument." if s.empty?
248
+ ret = quotedb.search_tags(s)
249
+ when :rating
250
+ s = s.split(' '); s.delete("x")
251
+ raise "Invalid --search argument." if s.length != 2 && s.length != 4
252
+ raise "Invalid --search argument." if s.find {|x| x =~ /[^<>=0-6]/}
253
+ ret = quotedb.search_ratings(s)
254
+ when :author
255
+ raise "invalid author string(too long)" if invalid_strings?([s])
256
+ ret = quotedb.search_authors(s)
257
+ when :text
258
+ raise "invalid quote string(too long)" if invalid_strings?([s])
259
+ ret = quotedb.search_quotes(s)
260
+ end
261
+ ret.nil? ? [] : ret
262
+ end
263
+
264
+ # get quote data from user, don't ask for quote text
265
+ # when importing from a fortune's file
266
+ def get_quote_data(importing = false)
267
+
268
+ print 'Author : ' ; author = gets
269
+ print 'Info : ' ; info = gets
270
+ print 'Rating(int, 0 <= x < 6) : ' ; rating = gets
271
+ print 'Tags (space separated) : ' ; tags = gets
272
+ ( print 'Quote (carefully enter, ctrl + d to end) : '; quote = ''
273
+ while line = gets
274
+ quote << line
275
+ end ) unless importing
276
+
277
+ raise "you've to enter quote data before pressing ctrl + d." if tags.nil?
278
+ [author, info, tags, quote].each {|x| x.chomp! unless x.nil?}
279
+
280
+ quote = '' if quote !~ /\w/ # otherwise if user press enters more than once
281
+ # when editing, it'll accidentally delete the quote.
282
+ # see main()
283
+ tags = tags.split(' ')
284
+ rating = rating =~ /^\d/ ? rating.to_i : nil
285
+
286
+ raise "invalid string(too long)" if invalid_strings?([quote, author, info])
287
+ raise "invalid tags(too many or too long)" if invalid_tags?(tags)
288
+ raise "invalid rating" if invalid_rating?(rating)
289
+
290
+ return [author, rating, tags, info, quote]
291
+ end
292
+
293
+ def import_fortune_quotes(quotedb, in_file)
294
+ raise "#{in_file} not readable." unless File.readable?(in_file)
295
+ IO.popen("file #{in_file}") { |f| raise "not a text file." unless f.gets =~ /text/}
296
+
297
+ c, ignored, found_sep = 0, 0, false
298
+ author, rating, tags, info = get_quote_data(true)
299
+
300
+ # file's quotes separator: '%'
301
+ File.open(in_file, 'r') do |f|
302
+ quote = ''
303
+ f.each_line do |l|
304
+ if l !~ /^%$/
305
+ quote << l
306
+ else
307
+ if quote =~ /\w/ && ! invalid_strings?([quote])
308
+ c+=1; found_sep = true
309
+ quotedb.add([author, rating, tags, info, quote])
310
+ else
311
+ ignored+=1
312
+ end
313
+ quote = ''
314
+ end
315
+ end
316
+ end
317
+ raise "file has an invalid format (is it a fortune cookie file?)" unless found_sep
318
+ puts "#{c} quotes were added, #{ignored} ignored."
319
+ quotedb.save
320
+ end
321
+
322
+ def main
323
+
324
+ options = OpenStruct.new
325
+ options.num = nil # int
326
+ options.search = [] # str
327
+ options.type_ = [] # sym
328
+ options.rand = false
329
+ options.add = false
330
+ options.edit = nil # int
331
+ options.del = nil # int
332
+ options.dups = false
333
+ options.len = false
334
+ options.quiet = true
335
+ options.import = nil # str (path to file)
336
+ options.count = 0
337
+
338
+ opts = OptionParser.new do |opts|
339
+ opts.banner = "Usage: #{NAME} [options]"
340
+ opts.separator ""
341
+ opts.on("-n", "--num n", "Print quote n.",
342
+ "(first element is indexed by 0)") {|n| options.num = n; options.count+=1}
343
+ opts.on("-a", "--add", "Add a quote to database.") {options.add = true; options.count+=1}
344
+ opts.on("-e", "--edit num", "Edit quote num.") {|n| options.edit = n; options.count+=1}
345
+ opts.on("-d", "--del num", "Delete quote num.") {|n| options.del = n; options.count+=1}
346
+ opts.on("-s", "--search string",
347
+ "Search quotes database(requires --type).",
348
+ "(tags: comma separated list)",
349
+ "(rating: you can use [>,<,=])") {|s| options.search << s; options.count+=1}
350
+ opts.on("-t", "--type type", [:author, :tags, :rating, :text],
351
+ "Type of search(author, tags, rating, text).",
352
+ "(requires --search).") {|t| options.type_ << t; options.count+=1}
353
+ opts.on("-p", "--print_rand", "Print random quote.") {options.rand = true; options.count+=1}
354
+ opts.on("-v", "--verbose", "Print extra quote information.") {options.quiet = false}
355
+ opts.on("--import fortune_file",
356
+ "Import quotes from a fortune file(full path).") {|f| options.import = f; options.count+=1}
357
+ opts.on("--duplicates",
358
+ "Remove \"possible\" duplicates(slow).") {options.dups = true; options.count+=1}
359
+ opts.on("--length", "Print number of quotes in database.") {options.len = true; options.count+=1}
360
+ opts.on_tail("-?", "--examples", "Show examples.") {puts EX; exit(0)}
361
+ opts.on_tail("-V", "--version", "Show version.") {puts "#{NAME + ' ' + VER}"; exit(0)}
362
+ opts.on_tail("-h", "--help", "Show this message.") {puts opts; exit(0)}
363
+
364
+ end
365
+ begin
366
+ db_path = File.expand_path(DB_DIR_PATH)
367
+ FileUtils.mkdir_p(db_path) unless File.directory?(db_path)
368
+
369
+ # build array of numbers out of db files(see QuoteDB.new)
370
+ r = Regexp.new(/^#{DB_PREFIX.sub(/\//, '')}(\d+)/)
371
+ db_nfiles = []
372
+ Dir.foreach(db_path) {|f| db_nfiles << $1.to_i if f =~ r }
373
+
374
+ # not acceptable decrease in performace(human time)
375
+ # raise "you can't have a db.0 file. (check #{DB_DIR_PATH})" if db_nfiles.include?(0)
376
+ # n = db_nfiles.length
377
+ db_nfiles = db_nfiles.sort
378
+ # if ! db_nfiles.empty? && (db_nfiles.inject {|sum, n| sum + n} != n*(n+1) / 2)
379
+ # raise "db.* files are not continuous."
380
+ # end
381
+
382
+ # prepare path to database files for loading
383
+ db_path += DB_PREFIX
384
+
385
+ # databases must be writable text files
386
+ # db_nfiles.each do |n|
387
+ # IO.popen("file #{db_path + n.to_s}") do |f|
388
+ # raise "database #{n} is not a text file." unless f.gets =~ /text/
389
+ # end
390
+ # raise "database #{n} is not writable." unless File.writable?(db_path + n.to_s)
391
+ # end
392
+
393
+ opts.parse!
394
+ raise "invalid input (see --help)." if ARGV.length != 0
395
+
396
+ if ! db_nfiles.empty? && (options.count == 0 || (options.count == 1 && options.rand) )
397
+ # load a random database file(faster and eats less memory than lading all)
398
+ quotedb = QuoteDB.new(db_path, [rand(db_nfiles.length) + 1] )
399
+
400
+ # pick a random quote from loaded database, convert to absolute value when printing
401
+ q = quotedb[rand(quotedb.length)]
402
+ q.print(quotedb.index(q), options.quiet)
403
+ else
404
+ case options.count
405
+ when 1
406
+ if options.num || options.edit
407
+ num = (options.num || options.edit).to_i
408
+
409
+ # the file num (suffix) for num
410
+ fn = (num / DB_NELS) + 1
411
+
412
+ raise "out of range (no file)." unless File.exist?(db_path + fn.to_s)
413
+ quotedb = QuoteDB.new(db_path, [fn])
414
+
415
+ # convert user's num to relative num inside quotedb
416
+ n = num % DB_NELS
417
+
418
+ raise "out of range." unless n < quotedb.length
419
+
420
+ if options.num
421
+ quotedb[n].print(num, options.quiet)
422
+ else
423
+ puts 'leave empty the fields you don\'t want to edit.'
424
+ arr, q = get_quote_data, quotedb[n]
425
+ q.author = arr[0] unless arr[0].empty?
426
+ q.rating = arr[1] unless ! arr[1]
427
+ q.tags = arr[2] unless arr[2].empty?
428
+ q.info = arr[3] unless arr[3].empty?
429
+ q.quote = arr[4] unless arr[4].empty?
430
+ quotedb.save
431
+ end
432
+ else
433
+ quotedb = QuoteDB.new(db_path, db_nfiles)
434
+ if options.len
435
+ puts "Length: #{quotedb.length}"
436
+ elsif options.add
437
+ arr = get_quote_data
438
+ raise "missing author/quote field." if arr[0].empty? || arr[4].empty?
439
+ qn = quotedb.find_similar(arr[0], arr[4].key_words)
440
+ if ! qn.empty?
441
+ puts "Similar quote(s) exists: #{qn.join(', ')} (indices)"
442
+ print "\nProceed? (yes/no) : "; ans = gets
443
+ (puts "Canceled."; exit(0)) if ans !~ /^yes/
444
+ end
445
+ quotedb.add(arr); quotedb.save
446
+ elsif options.import
447
+ import_fortune_quotes(quotedb, options.import)
448
+ elsif options.del
449
+ raise "out of range." unless options.del.to_i < quotedb.length
450
+ quotedb.del(options.del.to_i); quotedb.save
451
+ elsif options.dups
452
+ quotedb.remove_duplicates
453
+ quotedb.save
454
+ else
455
+ raise "Invalid option (see --help)."
456
+ end
457
+ end
458
+ when 2
459
+ quotedb = QuoteDB.new(db_path, db_nfiles)
460
+ if ! options.search.empty?
461
+ raise "Incompatible options (see --help)." if options.type_.empty?
462
+ a = parse_search(quotedb, options.type_[0], options.search[0])
463
+ if a.empty?
464
+ puts 'none found'
465
+ else
466
+ a.each {|q| q.print(quotedb.index(q), options.quiet) }
467
+ end
468
+ else
469
+ raise "Incompatible options (see --help)."
470
+ end
471
+ else
472
+ quotedb = QuoteDB.new(db_path, db_nfiles)
473
+ if options.search.length == options.type_.length && options.count < 10
474
+ a = []
475
+ options.search.length.times do |i|
476
+ r = parse_search(quotedb, options.type_[i], options.search[i])
477
+ a = a.empty? ? r : a & r
478
+ end
479
+ if a.empty?
480
+ puts 'none found'
481
+ else
482
+ rnd = rand(a.length)
483
+ if options.rand
484
+ a[rnd].print(quotedb.index(a[rnd]), options.quiet)
485
+ else
486
+ a.each {|q| q.print(quotedb.index(q), options.quiet) }
487
+ end
488
+ end
489
+ else
490
+ raise "Incompatible options (see --help)."
491
+ end
492
+ end
493
+ end
494
+ rescue StandardError => bang
495
+ puts "\n" + 'Oops, ' + bang
496
+ exit(1)
497
+ end
498
+ end
499
+ main