Name | Total Lines | Lines of Code | Total Coverage | Code Coverage |
---|---|---|---|---|
lib/jldrill/model/Quiz/Quiz.rb | 402 | 308 | 84.08%
|
81.49%
|
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
1 require 'jldrill/model/items/Vocabulary' |
2 require 'jldrill/model/Problem' |
3 require 'jldrill/model/Quiz/Options' |
4 require 'jldrill/model/Contents' |
5 require 'jldrill/model/Quiz/Strategy' |
6 require 'jldrill/model/DataFile' |
7 require 'Context/Publisher' |
8 require 'jldrill/Version' |
9 |
10 module JLDrill |
11 class Quiz < DataFile |
12 |
13 JLDRILL_HEADER_RE = /^(\d+\.\d+\.\d+)?-?LDRILL-SAVE (.*)/ |
14 COMMENT_RE = /^\#[ ]?(.*)/ |
15 VERSION_RE = /^(\d+)\.(\d+)\.(\d+)/ |
16 JLDRILL_CANLOAD_RE = /^(\d+\.\d+\.\d+)?-?LDRILL-SAVE/ |
17 |
18 attr_reader :needsSave, :info, :name, |
19 :contents, :options, :currentProblem, |
20 :strategy |
21 attr_writer :info, :name, :currentProblem |
22 |
23 def initialize() |
24 super |
25 # Make the file progress indicator report every 10 lines |
26 @stepSize = 10 |
27 end |
28 |
29 def reset |
30 @name = "" |
31 @info = "" |
32 @options = Options.new(self) |
33 @contents = Contents.new(self) |
34 @strategy = Strategy.new(self) |
35 @currentProblem = nil |
36 @needsSave = false |
37 @last = nil |
38 update |
39 super |
40 end |
41 |
42 def length |
43 @contents.length |
44 end |
45 |
46 def size |
47 length |
48 end |
49 |
50 def bin |
51 retVal = nil |
52 if !@currentProblem.nil? |
53 retVal = @currentProblem.item.bin |
54 end |
55 retVal |
56 end |
57 |
58 def subscribe(subscriber) |
59 @publisher.subscribe(subscriber, "quiz") |
60 end |
61 |
62 def unsubscribe(subscriber) |
63 @publisher.unsubscribe(subscriber, "quiz") |
64 end |
65 |
66 def update |
67 @publisher.update("quiz") |
68 end |
69 |
70 def updateLoad |
71 @publisher.update("load") |
72 end |
73 |
74 def setLoaded(bool) |
75 if bool |
76 updateLoad |
77 end |
78 end |
79 |
80 def updateItemDeleted(item) |
81 @publisher.update("itemDeleted", item) |
82 end |
83 |
84 def updateItemAdded(item) |
85 @publisher.update("itemAdded", item) |
86 end |
87 |
88 def updateNewProblem(problem) |
89 @publisher.update("newProblem", problem) |
90 end |
91 |
92 def problemModified(problem) |
93 @publisher.update("problemModified", problem) |
94 setNeedsSave(true) |
95 if !problem.valid? && |
96 !@currentProblem.nil? && (problem == @currentProblem) |
97 # The current problem has been edited and can't be displayed |
98 # like it is (i.e., A Kanji problem has had it's kanji removed) |
99 # Recreate it. |
100 recreateProblem |
101 end |
102 end |
103 |
104 def setNeedsSave(bool) |
105 @needsSave = bool |
106 update |
107 end |
108 |
109 def needsSave? |
110 @needsSave |
111 end |
112 |
113 def fileHeader |
114 JLDrill::VERSION + "-LDRILL-SAVE #{@name}\n" |
115 end |
116 |
117 def saveToString |
118 retVal = fileHeader |
119 @info.split("\n").each { |line| |
120 retVal += "# " + line + "\n" |
121 } |
122 retVal += @options.to_s |
123 retVal += @contents.to_s |
124 retVal |
125 end |
126 |
127 def save |
128 if (@contents.length == 0) || !@needsSave |
129 return true |
130 elsif @file == "" |
131 return false |
132 else |
133 begin |
134 saveFile = File.new(@file, "w") |
135 if saveFile |
136 saveFile.print(saveToString) |
137 saveFile.close |
138 setNeedsSave(false) |
139 retVal = true |
140 end |
141 rescue |
142 return false |
143 end |
144 end |
145 end |
146 |
147 # Returns the filename relative to the Quiz's current |
148 # path. If a filename hasn't been set, the file is |
149 # expanded using the applications current path. |
150 def useSavePath(filename) |
151 if !@file.empty? |
152 dirname = File.expand_path(File.dirname(@file)) |
153 return File.expand_path(filename, dirname) |
154 else |
155 return File.expand_path(filename) |
156 end |
157 end |
158 |
159 def Quiz.canLoad?(header) |
160 retVal = false |
161 if header =~ JLDRILL_CANLOAD_RE |
162 if $1 != "" |
163 if $1 =~ VERSION_RE |
164 if $1.to_i > 0 || $2.to_i < 6 |
165 retVal = true |
166 end |
167 end |
168 end |
169 end |
170 return retVal |
171 end |
172 |
173 def Quiz.drillFile?(file) |
174 retVal = false |
175 loadFile = File.new(file, "r") |
176 if(loadFile) |
177 retVal = Quiz.canLoad?(loadFile.readline) |
178 end |
179 return retVal |
180 end |
181 |
182 def parseLine(line) |
183 # These are put in a specific order for performance |
184 # By checking the most common items first we avoid doing |
185 # needless regular expression checks. It's not a huge |
186 # savings, but it helps for very big files. |
187 # Normal contents are the most common |
188 if !@contents.parseLine(line) |
189 # Quiz options are the next most common |
190 if !@options.parseLine(line) |
191 # Comments, headers and unparsable lines are |
192 # the least common |
193 case line |
194 when JLDRILL_HEADER_RE |
195 @name = $2 |
196 when COMMENT_RE |
197 @info += $1 + "\n" |
198 else |
199 # Ignore things we don't understand |
200 end |
201 end |
202 end |
203 end |
204 |
205 def parseEntry |
206 parseLine(@lines[@parsed]) |
207 @parsed += 1 |
208 end |
209 |
210 def dataSize |
211 @contents.size |
212 end |
213 |
214 def finishParsing |
215 # Need to sort the new set to deal with older files that |
216 # may not be sorted. |
217 @strategy.newSet.sort! do |x, y| |
218 x.position <=> y.position |
219 end |
220 # Resort the review set according to schedule |
221 reschedule |
222 setNeedsSave(true) |
223 update |
224 super |
225 end |
226 |
227 def loadFromString(file, string) |
228 reset |
229 @file = file |
230 @lines = string.split("\n") |
231 parse |
232 end |
233 |
234 def loadFromDict(dict) |
235 if dict |
236 # Don't update the status while we're loading the file |
237 @publisher.block |
238 reset |
239 @name = dict.shortFilename |
240 dict.eachVocab do |vocab| |
241 @contents.add(vocab, 0) |
242 end |
243 reschedule |
244 # Update status again |
245 @publisher.unblock |
246 setNeedsSave(true) |
247 end |
248 end |
249 |
250 # Append any new items in the quiz to this quiz |
251 # Note: Does not add items that are already existing. |
252 # nor does it update the status of existing items |
253 def append(quiz) |
254 # Don't update the status while we're appending the file |
255 @publisher.block |
256 lastItem = @contents.addContents(quiz.contents) |
257 # Update status again |
258 @publisher.unblock |
259 # The quiz has been modified |
260 update |
261 # A file has been loaded |
262 updateLoad |
263 # Indicate the last item that was loaded |
264 updateItemAdded(lastItem) unless lastItem.nil? |
265 if @currentProblem.nil? |
266 # Drill a problem if there wasn't one before |
267 drill |
268 end |
269 end |
270 |
271 # Sort the items in the ReviewSet according to their schedule |
272 def reschedule |
273 @strategy.reschedule |
274 end |
275 |
276 # Returns true if the vocabulary already exists in the Quiz |
277 def exists?(vocab) |
278 @contents.exists?(vocab) |
279 end |
280 |
281 def appendVocab(vocab) |
282 item = Item.new(vocab) |
283 @contents.addUniquely(item) |
284 return item |
285 end |
286 |
287 def status |
288 retVal = "" |
289 if(@needsSave) then retVal += "* " else retVal += " " end |
290 retVal += @contents.status + " " |
291 if !@currentProblem.nil? |
292 retVal += @currentProblem.status + " " |
293 end |
294 retVal += @strategy.status |
295 return retVal |
296 end |
297 |
298 def to_s |
299 status |
300 end |
301 |
302 # Get an array containing all the items in the quiz |
303 def allItems |
304 @contents.allItems |
305 end |
306 |
307 # Resets the quiz back to it's original state |
308 def resetContents |
309 @contents.reset() |
310 drill() |
311 end |
312 |
313 # Delete an item from the quiz |
314 def deleteItem(item) |
315 @contents.delete(item) |
316 updateItemDeleted(item) |
317 end |
318 |
319 def incorrect |
320 if !@currentProblem.nil? |
321 item = @currentProblem.item |
322 if !item.nil? |
323 @strategy.incorrect(item) |
324 setNeedsSave(true) |
325 end |
326 end |
327 end |
328 |
329 def correct |
330 if !@currentProblem.nil? |
331 item = @currentProblem.item |
332 if !item.nil? |
333 @strategy.correct(item) |
334 setNeedsSave(true) |
335 end |
336 end |
337 end |
338 |
339 # Promote the current problem into the review set |
340 # if it is in the working set without any further |
341 # practice. |
342 def learn |
343 if !@currentProblem.nil? |
344 item = @currentProblem.item |
345 if !item.nil? |
346 @strategy.learn(item) |
347 setNeedsSave(true) |
348 end |
349 end |
350 end |
351 |
352 def setCurrentProblem(problem) |
353 @currentProblem = problem |
354 update |
355 updateNewProblem(@currentProblem) |
356 end |
357 |
358 # Creates a problem to be quizzed |
359 def createProblem(item) |
360 setCurrentProblem(@strategy.createProblem(item)) |
361 end |
362 |
363 # Creates a problem to be displayed only |
364 def displayProblem(item) |
365 problem = @strategy.createProblem(item) |
366 problem.setDisplayOnly(true) |
367 setCurrentProblem(problem) |
368 end |
369 |
370 # Creates a preview for a problem |
371 def previewProblem(item) |
372 problem = @strategy.createProblem(item) |
373 problem.setDisplayOnly(true) |
374 problem.setPreview(true) |
375 setCurrentProblem(problem) |
376 end |
377 |
378 def recreateProblem |
379 createProblem(@currentProblem.item) unless @currentProblem.nil? |
380 end |
381 |
382 def drill() |
383 item = @strategy.getItem |
384 if !item.nil? |
385 createProblem(item) |
386 end |
387 end |
388 |
389 def answer() |
390 @currentProblem.answer |
391 end |
392 |
393 def currentDrill |
394 @currentProblem.question |
395 end |
396 |
397 def currentAnswer |
398 @currentProblem.answer |
399 end |
400 |
401 end |
402 end |
Generated on Mon May 23 16:17:46 +0900 2011 with rcov 0.9.8