rwdeliza 0.03

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 (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