ankit 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -14,12 +14,14 @@ module Ankit
14
14
  def execute()
15
15
  validate_options
16
16
  each_text do |text|
17
- card = Card.parse(text)
18
- # TODO: gaurd ovewrite
19
- # TODO: guard out-of-path write
20
- filename = to_card_path(dest_dir, card.name)
21
- File.open(filename, "w") { |f| f.write(text) }
22
- runtime.stdout.write("#{filename}\n")
17
+ text.split(/\n\n+/).each do |chunk|
18
+ card = Card.parse(chunk)
19
+ # TODO: gaurd ovewrite
20
+ # TODO: guard out-of-path write
21
+ filename = to_card_path(dest_dir, card.name)
22
+ File.open(filename, "w") { |f| f.write(text) }
23
+ runtime.stdout.write("#{filename}\n")
24
+ end
23
25
  end
24
26
  end
25
27
 
@@ -25,6 +25,8 @@ module Ankit
25
25
  HighLine.color(text, HighLine::GREEN_STYLE)
26
26
  when :wrong
27
27
  HighLine.color(text, HighLine::RED_STYLE)
28
+ when :fyi
29
+ HighLine.color(text, HighLine::DARK)
28
30
  else
29
31
  raise
30
32
  end
@@ -35,7 +37,6 @@ module Ankit
35
37
  def decorated(type)
36
38
  raise
37
39
  decorated = @text.gsub(/\[(.*?)\]/) { |t|
38
- p Regexp::last_match.offset(0)
39
40
  self.class.styled_text($1, type)
40
41
  }
41
42
  decorated != @text ? decorated : self.class.styled_text(@text, type)
@@ -74,23 +75,58 @@ module Ankit
74
75
  end
75
76
  end
76
77
  end
78
+
79
+ def mixed_hilight_for_flash(wrong)
80
+ diff_from_original(wrong) do |ch|
81
+ case ch.action
82
+ when "="
83
+ StylableText.styled_text(ch.old_element, :fyi)
84
+ when "!", "-"
85
+ StylableText.styled_text(ch.old_element, :wrong) + StylableText.styled_text(ch.new_element, :correct)
86
+ when "+"
87
+ StylableText.styled_text(ch.new_element, :correct)
88
+ else
89
+ raise
90
+ end
91
+ end
92
+ end
77
93
  end
78
94
 
79
95
  module Challenge
80
96
  class Slot < Struct.new(:path, :rating, :event)
97
+ BATCH_SIZE = 10
81
98
  def maturity; self.event ? self.event.maturity : 0; end
82
99
  end
83
100
 
84
- class Session < Struct.new(:runtime, :npassed, :nfailed, :mature_names)
85
- def self.make(runtime)
86
- self.new(runtime, 0, 0, [])
101
+ class Session < Struct.new(:runtime, :limit, :npassed, :nfailed, :passed_events)
102
+ def self.make(runtime, limit)
103
+ self.new(runtime, limit, 0, 0, {})
87
104
  end
88
105
 
89
106
  def summary_text
90
- total = self.npassed + self.nfailed
91
- return "" if 0 == total
92
- "#{self.npassed}/#{total} = #{self.npassed.to_f/total.to_f}, #mature: #{mature_names.size}"
107
+ ""
108
+ end
109
+
110
+ def passed_on(event)
111
+ self.passed_events[event.name] = event
112
+ self.npassed += 1
113
+ end
114
+
115
+ def failed_on(event)
116
+ self.nfailed += 1
117
+ end
118
+
119
+ def maturity_triple
120
+ return [0,0,0] if passed_events.empty?
121
+ mats = passed_events.values.map(&:maturity)
122
+ avg = mats.inject(0,:+)/mats.size
123
+ [mats.min, avg, mats.max]
93
124
  end
125
+
126
+ def reached_limit?; limit <= passed_events.size; end
127
+ def limit_reach; passed_events.size ;end
128
+ def ntotal; npassed + nfailed; end
129
+ def hitrate; 0 < ntotal ? npassed.to_f/ntotal : 0; end
94
130
  end
95
131
 
96
132
  class Progress
@@ -103,19 +139,17 @@ module Ankit
103
139
  @this_round = latest_round
104
140
  end
105
141
 
106
- def current_card
107
- # XXX: might be better to cache
108
- Card.parse(open(current_path, "r") { |f| f.read })
109
- end
110
-
111
142
  def round_delta
112
143
  latest_round - this_round
113
144
  end
114
145
 
115
146
  def runtime; @session.runtime; end
116
147
  def last_slot; @slots[@index-1]; end
148
+ def last_path; last_slot.path; end
149
+ def last_card; card_at(last_path); end
117
150
  def current_slot; @slots[@index]; end
118
151
  def current_path; current_slot.path; end
152
+ def current_card; card_at(current_path); end
119
153
  def size; @slots.size; end
120
154
  def over?; @slots.size <= @index; end
121
155
  def npassed; @slots.count { |c| c.rating == :passed }; end
@@ -133,6 +167,7 @@ module Ankit
133
167
  last_slot = current_slot
134
168
  last_slot.rating = :failed
135
169
  last_slot.event = make_happen(FailCommand::EVENT_HAPPENING, to_card_name(current_path), this_round)
170
+ session.failed_on(last_slot.event)
136
171
  end
137
172
 
138
173
  self
@@ -143,6 +178,7 @@ module Ankit
143
178
  last_slot = current_slot
144
179
  last_slot.rating = :passed
145
180
  last_slot.event = make_happen(PassCommand::EVENT_HAPPENING, to_card_name(current_path), this_round)
181
+ session.passed_on(last_slot.event)
146
182
  end
147
183
 
148
184
  @index += 1
@@ -171,13 +207,14 @@ module Ankit
171
207
  end.join
172
208
  end
173
209
 
174
- def update_session
175
- session.npassed += npassed
176
- session.nfailed += nfailed
177
- session.mature_names = (session.mature_names + slots.select{ |s| 1 < s.maturity }.map(&:path)).uniq
178
- end
179
-
180
210
  def maturities; slots.map(&:maturity); end
211
+
212
+
213
+ private
214
+ def card_at(path)
215
+ # XXX: might be better to cache
216
+ Card.parse(open(path, "r") { |f| f.read })
217
+ end
181
218
  end
182
219
 
183
220
  class State
@@ -199,30 +236,21 @@ module Ankit
199
236
  runtime.stdout.print("\033[1A")
200
237
  end
201
238
 
202
- def clear_screen
203
- runtime.stdout.print("\033[2J")
204
- h = HighLine::SystemExtensions.terminal_size[0]
205
- runtime.stdout.print("\033[#{h}A")
206
- end
207
-
208
239
  def say(msg, type=:progress)
209
240
  line.say(message_for(msg, type))
210
241
  end
211
242
 
212
- def show_summary_status
213
- line.say("Round #{progress.this_round}: #{progress.styled_indicator}")
243
+ def show_summary_header
244
+ status = ["P:#{self.session.limit_reach}/#{self.session.limit}",
245
+ "H:#{self.session.hitrate}",
246
+ "M:" + self.session.maturity_triple.map(&:to_s).join(","),
247
+ "R:#{self.progress.this_round.to_s}"
248
+ ]
249
+ line.say(status.join(" "))
214
250
  end
215
251
 
216
252
  def show_breaking_status
217
- show_summary_status
218
- line.say("Maturity: #{progress.maturities.map(&:to_s).join(',')}")
219
- line.say("Session: #{progress.session.summary_text}")
220
- line.say("next round will be +#{progress.round_delta}")
221
- end
222
-
223
- def show_header
224
- show_summary_status
225
- line.say("\n")
253
+ show_summary_header
226
254
  end
227
255
 
228
256
  def show_and_ask_enter(msg, type)
@@ -273,116 +301,149 @@ module Ankit
273
301
  end
274
302
  end
275
303
 
276
- module CommandRecognizing
277
- def may_pump_command(answered)
278
- /^\/(\w+)/.match(answered) ? pump_command($1) : nil
304
+ module SlashRecognizing
305
+ def pump_slash_or(answered, &block)
306
+ if /^\/(\w+)/.match(answered)
307
+ pump_slash($1)
308
+ else
309
+ block.call()
310
+ end
279
311
  end
280
312
 
281
- def pump_command(command)
313
+ def pump_slash(command)
282
314
  case command
283
315
  when "e", "edit"
284
316
  EditState.new(progress)
285
317
  when "z", "zero"
286
318
  QuestionState.new(progress)
287
319
  else
288
- raise
320
+ MessageState.new(progress, "Unknown command: #{command} (Available: /edit, /zero)")
321
+ end
322
+ end
323
+ end
324
+
325
+ class MessageState < State
326
+ include SlashRecognizing
327
+
328
+ def initialize(progress, message)
329
+ super(progress)
330
+ @message = message
331
+ end
332
+
333
+ def pump
334
+ say(@message, :fail) # XXX: should :error
335
+ answered = ask().strip
336
+ pump_slash_or(answered) do
337
+ pump_slash(answered)
289
338
  end
290
339
  end
291
340
  end
292
341
 
293
342
  class QuestionState < State
294
- include CommandRecognizing
343
+ include SlashRecognizing
344
+ attr_reader :flash
295
345
 
296
346
  def pump
297
347
  progress.attack
298
- clear_screen
348
+ runtime.clear_screen
299
349
  show_header
300
350
  card = progress.current_card
301
351
  say("#{card.translation}")
302
352
  say("#{card.hidden_original}", :cont)
303
353
  answered = ask().strip
304
- c = may_pump_command(answered)
305
- return c if c
306
- case card.match?(answered.strip)
307
- when :match
308
- PassedState.new(progress, answered)
309
- when :wrong
310
- FailedState.new(progress, answered)
311
- when :typo
312
- TypoState.new(progress, answered)
354
+ pump_slash_or(answered) do
355
+ case card.match?(answered.strip)
356
+ when :match
357
+ PassedState.new(progress, answered)
358
+ when :wrong
359
+ FailedState.new(progress, answered)
360
+ when :typo
361
+ TypoState.new(progress, answered)
362
+ else
363
+ raise
364
+ end
365
+ end
366
+ end
367
+
368
+ def put_flash(flash)
369
+ @flash = flash
370
+ self
371
+ end
372
+
373
+ private
374
+
375
+ def show_header
376
+ show_summary_header
377
+ if flash
378
+ line.say(flash)
313
379
  else
314
- raise
380
+ line.say("\n")
315
381
  end
382
+ line.say("\n")
316
383
  end
317
384
  end
318
385
 
319
- class FailedStateBase < State
320
- include CommandRecognizing
386
+ class FailedState < State
387
+ include SlashRecognizing
321
388
 
322
389
  def pump
323
390
  original = progress.current_card.corrected_original_over(last_answer)
324
391
  typed = progress.current_card.hilight_against_original(last_answer)
392
+ typed = "\n" if typed.empty?
325
393
  erase_last
326
394
  say("#{typed}", :ask)
327
- say("#{original}", decoration_type)
395
+ say("#{original}", :fail)
328
396
  answered = ask("", :hit_return)
329
- c = may_pump_command(answered)
330
- return c if c
331
- mark_progress
332
- QuestionState.new(progress)
397
+ pump_slash_or(answered) do
398
+ progress.fail
399
+ QuestionState.new(progress)
400
+ end
333
401
  end
334
-
335
402
  end
336
403
 
337
- class FailedState < FailedStateBase
338
- def mark_progress
339
- progress.fail
340
- end
404
+ class PassedStateBase < State
405
+ include SlashRecognizing
341
406
 
342
- def decoration_type
343
- :fail
407
+ def pump
408
+ progress.pass
409
+ last_maturity = progress.last_slot.event.maturity
410
+ progress.over? ? RefillState.new(progress) : QuestionState.new(progress).put_flash(flash)
344
411
  end
345
412
  end
346
413
 
347
- class TypoState < FailedStateBase
348
- def mark_progress
349
-
350
- end
351
-
352
- def decoration_type
353
- :typo
414
+ class TypoState < PassedStateBase
415
+ def flash
416
+ hilited = progress.last_card.mixed_hilight_for_flash(last_answer)
417
+ StylableText.styled_text("last: ", :fyi) + hilited
354
418
  end
355
419
  end
356
420
 
357
- class PassedState < State
358
- include CommandRecognizing
359
-
360
- def pump
361
- progress.pass
362
- last_maturity = progress.last_slot.event.maturity
363
- progress.over? ? BreakingState.new(progress) : QuestionState.new(progress)
421
+ class PassedState < PassedStateBase
422
+ def flash
423
+ StylableText.styled_text("last: #{last_answer}", :fyi)
364
424
  end
365
425
  end
366
426
 
367
- class BreakingState < State
427
+ class RefillState < State
368
428
  def pump
369
- progress.update_session
370
- clear_screen
371
- show_breaking_status
429
+ return initial_state unless session.reached_limit?
372
430
 
373
- case ask_more
431
+ runtime.clear_screen
432
+ show_breaking_status
433
+ case ask_over
374
434
  when :yes
375
- initial_state
376
- when :no
435
+ runtime.clear_screen
377
436
  OverState.new(progress)
437
+ when :no
438
+ initial_state
378
439
  else
379
440
  # TODO: handle help
380
441
  self
381
442
  end
382
443
  end
383
444
 
384
- def ask_more
385
- case line.ask("More(y/n/?) ").strip
445
+ def ask_over
446
+ case line.ask("Over(Y/n/?) ").strip
386
447
  when /^y/, ""
387
448
  :yes
388
449
  when /^n/
@@ -391,10 +452,6 @@ module Ankit
391
452
  :help
392
453
  end
393
454
  end
394
-
395
- def coming_limit
396
- progress.size
397
- end
398
455
  end
399
456
 
400
457
  class OverState < State
@@ -403,11 +460,13 @@ module Ankit
403
460
 
404
461
  module Approaching
405
462
  def initial_state
406
- slots = Coming.coming_paths(self.runtime).take(self.coming_limit).map { |path| Slot.new(path, nil) }
463
+ # XXX: Care the case where |card| < limit
464
+ limit = [self.session.limit, Slot::BATCH_SIZE].min
465
+ slots = Coming.coming_paths(self.runtime).take(limit).map { |path| Slot.new(path, nil) }
407
466
  QuestionState.new(Progress.new(self.session, slots))
408
467
  end
409
468
  end
410
469
 
411
- class BreakingState; include Approaching; end
470
+ class RefillState; include Approaching; end
412
471
  end
413
472
  end
@@ -13,12 +13,13 @@ module Ankit
13
13
  spec.on("-l", "--limit N") { |n| options[:limit] = n.to_i }
14
14
  end
15
15
 
16
- DEFAULT_COUNT = 5
16
+ DEFAULT_COUNT = 50
17
17
 
18
- def session; @session ||= Challenge::Session.make(runtime); end
18
+ def session; @session ||= Challenge::Session.make(runtime, coming_limit); end
19
19
 
20
20
  def execute()
21
21
  Signal.trap("INT") do
22
+ runtime.clear_screen
22
23
  STDERR.print("Quit.\n")
23
24
  exit(0)
24
25
  end
@@ -27,6 +28,8 @@ module Ankit
27
28
  Signal.trap("INT", "DEFAULT")
28
29
  end
29
30
 
31
+ private
32
+
30
33
  def coming_limit
31
34
  options[:limit] or DEFAULT_COUNT
32
35
  end
data/lib/ankit/event.rb CHANGED
@@ -24,6 +24,7 @@ module Ankit
24
24
  def name() @values["name"]; end
25
25
  def type() @values["type"]; end
26
26
  def maturity() @values["maturity"] || 0; end
27
+ def best() @values["best"] || maturity; end
27
28
  def card?() type == "card"; end
28
29
  def round() @envelope.round or 0; end
29
30
  def next_round() round + 2**maturity; end
@@ -39,11 +40,11 @@ module Ankit
39
40
  def to_json(*a) { envelope: @envelope, values: @values }.to_json(*a); end
40
41
 
41
42
  def to_passed(env)
42
- Event.new(env, @values.merge({ "verb" => "passed", "maturity" => maturity + 1 }))
43
+ Event.new(env, Event.sweep(@values.merge({ "verb" => "passed", "maturity" => next_maturity })))
43
44
  end
44
45
 
45
46
  def to_failed(env)
46
- Event.new(env, @values.merge({ "verb" => "failed", "maturity" => 0 }))
47
+ Event.new(env, @values.merge({ "verb" => "failed", "maturity" => 0, "best" => best }))
47
48
  end
48
49
 
49
50
  def self.for_card(name, verb, env)
@@ -52,6 +53,17 @@ module Ankit
52
53
 
53
54
  def self.from_hash(hash) Event.new(Envelope.from_hash(hash["envelope"]), hash["values"]); end
54
55
  def self.parse(text) from_hash(JSON.parse(text)); end
56
+
57
+ def self.sweep(values)
58
+ values.delete("best") if values.include?("best") and values["best"] <= values["maturity"]
59
+ values
60
+ end
61
+
62
+ private
63
+
64
+ def next_maturity
65
+ maturity + [(best - maturity)/2, 1].max
66
+ end
55
67
  end
56
68
 
57
69
  module EventFormatting
data/lib/ankit/runtime.rb CHANGED
@@ -135,6 +135,14 @@ module Ankit
135
135
  saved
136
136
  end
137
137
 
138
+ def clear_screen
139
+ w = HighLine::SystemExtensions.terminal_size[1]
140
+ stdout.print("\033[#{w}D")
141
+ stdout.print("\033[2J")
142
+ h = HighLine::SystemExtensions.terminal_size[0]
143
+ stdout.print("\033[#{h}A")
144
+ end
145
+
138
146
  def unsupress_io(saved)
139
147
  @stdin, @stdout, @stderr = saved
140
148
  end
data/test/command_test.rb CHANGED
@@ -149,7 +149,7 @@ class AddTest < Test::Unit::TestCase
149
149
  end
150
150
  end
151
151
 
152
- def test_hello_two_file
152
+ def test_hello_two_files
153
153
  with_runtime_on_temp_repo do |target|
154
154
  dst_dir = target.config.card_search_paths[1]
155
155
  assert_written(target.dispatch_then(["add", test_data_at("hope.card"), test_data_at("luck.card")]),
@@ -157,6 +157,15 @@ class AddTest < Test::Unit::TestCase
157
157
  File.join(target.config.card_paths[0], "luck-is-not-chance.card")])
158
158
  end
159
159
  end
160
+
161
+ def test_hello_two_chunks
162
+ with_runtime_on_temp_repo do |target|
163
+ dst_dir = target.config.card_search_paths[1]
164
+ assert_written(target.dispatch_then(["add", test_data_at("hope-and-luck.txt")]),
165
+ [File.join(target.config.card_paths[0], "hope-is-the-thing-with-feathers.card"),
166
+ File.join(target.config.card_paths[0], "luck-is-not-chance.card")])
167
+ end
168
+ end
160
169
  end
161
170
 
162
171
  class ComingTest < Test::Unit::TestCase
@@ -264,8 +273,9 @@ class ChallengeTest < Test::Unit::TestCase
264
273
 
265
274
  def test_initial_state_limit_default
266
275
  runtime = make_runtime(NUMBER_REPO)
267
- actual = runtime.make_command(["challenge"]).initial_state
268
- assert_equal(actual.progress.size, ChallengeCommand::DEFAULT_COUNT)
276
+ limit = 5
277
+ actual = runtime.make_command(["challenge", "--limit", limit.to_s]).initial_state
278
+ assert_equal(actual.progress.size, limit)
269
279
  end
270
280
 
271
281
  def test_initial_state_limit_args
@@ -300,6 +310,13 @@ class ChallengeTest < Test::Unit::TestCase
300
310
  state.pump
301
311
  end
302
312
 
313
+ def agree_pump(state, that)
314
+ state.progress.runtime.line = HighLine.new
315
+ state.progress.runtime.line.stubs(:say).at_least(0)
316
+ state.progress.runtime.line.stubs(:ask).once().returns(that)
317
+ state.pump
318
+ end
319
+
303
320
  def test_question_to_fail
304
321
  with_runtime_on_temp_repo do |runtime|
305
322
  actual = ChallengeCommand.new(runtime).initial_state
@@ -312,6 +329,17 @@ class ChallengeTest < Test::Unit::TestCase
312
329
  end
313
330
  end
314
331
 
332
+ def test_question_to_unknown_command
333
+ with_runtime_on_temp_repo do |runtime|
334
+ actual = ChallengeCommand.new(runtime).initial_state
335
+ assert_instance_of(Challenge::QuestionState, actual)
336
+ actual = enter_text_pump(actual, "/unknown")
337
+ assert_instance_of(Challenge::MessageState, actual)
338
+ actual = enter_text_pump(actual, "/zero")
339
+ assert_instance_of(Challenge::QuestionState, actual)
340
+ end
341
+ end
342
+
315
343
  def test_question_to_fail_but_zero_it
316
344
  with_runtime_on_temp_repo do |runtime|
317
345
  actual = ChallengeCommand.new(runtime).initial_state
@@ -329,13 +357,8 @@ class ChallengeTest < Test::Unit::TestCase
329
357
  actual = ChallengeCommand.new(runtime).initial_state
330
358
  actual_next = enter_text_pump(actual, FIRST_TYPO_ANSWER)
331
359
  assert_instance_of(Challenge::TypoState, actual_next)
332
- actual_next = hit_return_pump(actual_next)
360
+ actual_next = actual_next.pump
333
361
  assert_instance_of(Challenge::QuestionState, actual_next)
334
- assert_equal(actual_next.progress.npassed, 0)
335
- assert_equal(actual_next.progress.nfailed, 0)
336
- actual_next = enter_text_pump(actual_next, FIRST_CORRECT_ANSWER)
337
- assert_instance_of(Challenge::PassedState, actual_next)
338
- actual_next.pump
339
362
  assert_equal(actual_next.progress.npassed, 1)
340
363
  assert_equal(actual_next.progress.nfailed, 0)
341
364
  end
@@ -365,38 +388,39 @@ class ChallengeTest < Test::Unit::TestCase
365
388
  state
366
389
  end
367
390
 
368
- def test_to_breaking
391
+ def test_to_refill
369
392
  with_runtime_on_temp_repo do |runtime|
370
393
  actual = ChallengeCommand.new(runtime).initial_state
371
394
  actual_next = pass_two(actual)
372
- assert_instance_of(Challenge::BreakingState, actual_next)
395
+ assert_instance_of(Challenge::RefillState, actual_next)
373
396
  end
374
397
  end
375
398
 
376
- def test_to_breaking_to_over
399
+ def test_to_refill_to_over
377
400
  with_runtime_on_temp_repo do |runtime|
378
- actual = ChallengeCommand.new(runtime).initial_state
401
+ actual = ChallengeCommand.new(runtime, ["--limit", "2"]).initial_state
379
402
  actual = pass_two(actual)
380
- actual = agree_pump(actual, "n")
403
+ actual = agree_pump(actual, "y")
381
404
  assert_instance_of(Challenge::OverState, actual)
382
405
  end
383
406
  end
384
407
 
385
- def test_to_breaking_to_more
408
+ def test_to_refill_to_more
386
409
  with_runtime_on_temp_repo do |runtime|
387
- actual = ChallengeCommand.new(runtime).initial_state
410
+ actual = ChallengeCommand.new(runtime, ["--limit", "2"]).initial_state
388
411
  actual = pass_two(actual)
389
- actual = agree_pump(actual, "y")
412
+ p actual.class
413
+ actual = agree_pump(actual, "n")
390
414
  assert_instance_of(Challenge::QuestionState, actual)
391
415
  end
392
416
  end
393
417
 
394
- def test_to_breaking_to_more
418
+ def test_to_refill_to_more
395
419
  with_runtime_on_temp_repo do |runtime|
396
- actual = ChallengeCommand.new(runtime).initial_state
420
+ actual = ChallengeCommand.new(runtime, ["--limit", "2"]).initial_state
397
421
  actual = pass_two(actual)
398
422
  actual = agree_pump(actual, "?")
399
- assert_instance_of(Challenge::BreakingState, actual)
423
+ assert_instance_of(Challenge::RefillState, actual)
400
424
  end
401
425
  end
402
426
  end
@@ -0,0 +1,4 @@
1
+ O: Hope is the thing with feathers
2
+
3
+
4
+ O: Luck is not chance
data/test/event_test.rb CHANGED
@@ -9,6 +9,7 @@ class EventTest < Test::Unit::TestCase
9
9
  @target = Event.parse('{"envelope":{"at":"2001-02-03T04:05:06+00:00","round":1},' +
10
10
  '"values":{"type":"card","verb":"add","name":"hello","maturity":1}}')
11
11
  @next_envelope = Envelope.parse('{"at":"2002-03-04T05:06:07+00:00","round":2}')
12
+ @another_next_envelope = Envelope.parse('{"at":"2002-03-04T05:06:08+00:00","round":3}')
12
13
  end
13
14
 
14
15
  def test_to_passed
@@ -17,10 +18,30 @@ class EventTest < Test::Unit::TestCase
17
18
  assert_equal(actual, expected)
18
19
  end
19
20
 
21
+ def test_to_passed_recovering
22
+ recovering_target = Event.parse('{"envelope":{"at":"2001-02-03T04:05:06+00:00","round":1},' +
23
+ '"values":{"type":"card","verb":"add","name":"hello","maturity":1,"best":6}}')
24
+ expected = Event.new(@next_envelope, JSON.parse('{"type":"card","verb":"passed","name":"hello","maturity":3,"best":6}'))
25
+ actual = recovering_target.to_passed(@next_envelope)
26
+ assert_equal(actual, expected)
27
+ end
28
+
29
+ def test_to_passed_recovering_to_best
30
+ recovering_target = Event.parse('{"envelope":{"at":"2001-02-03T04:05:06+00:00","round":1},' +
31
+ '"values":{"type":"card","verb":"add","name":"hello","maturity":5,"best":6}}')
32
+ expected = Event.new(@next_envelope, JSON.parse('{"type":"card","verb":"passed","name":"hello","maturity":6}'))
33
+ actual = recovering_target.to_passed(@next_envelope)
34
+ assert_equal(actual, expected)
35
+ end
36
+
20
37
  def test_to_failed
21
- expected = Event.new(@next_envelope, JSON.parse('{"type":"card","verb":"failed","name":"hello","maturity":0}'))
38
+ json = JSON.parse('{"type":"card","verb":"failed","name":"hello","maturity":0,"best":1}')
39
+ expected = Event.new(@next_envelope, json)
22
40
  actual = @target.to_failed(@next_envelope)
23
41
  assert_equal(actual, expected)
42
+ another_expected = Event.new(@another_next_envelope, json)
43
+ actual = @target.to_failed(@another_next_envelope)
44
+ assert_equal(actual, another_expected)
24
45
  end
25
46
 
26
47
  def test_next_round
@@ -8,8 +8,8 @@ class ProgressTest < Test::Unit::TestCase
8
8
  include Ankit::CardNaming
9
9
  include Challenge
10
10
 
11
- def make_target(runtime)
12
- Progress.new(Session.new(runtime), [Slot.new("path1"), Slot.new("path2"), Slot.new("path3")])
11
+ def make_target(runtime, sess=nil)
12
+ Progress.new(sess || Session.make(runtime, 5), [Slot.new("path1"), Slot.new("path2"), Slot.new("path3")])
13
13
  end
14
14
 
15
15
  def first_slot_event_of(progress)
@@ -47,6 +47,32 @@ class ProgressTest < Test::Unit::TestCase
47
47
  end
48
48
  end
49
49
 
50
+ def test_passed_size
51
+ with_runtime_on_temp_repo do |runtime|
52
+ target1 = make_target(runtime)
53
+ session = target1.session
54
+
55
+ target1.pass
56
+ assert_equal(session.limit_reach, 1)
57
+ target1.pass
58
+ assert_equal(session.limit_reach, 2)
59
+ target1.fail
60
+ assert_equal(session.limit_reach, 2)
61
+ target1.pass
62
+ assert_equal(session.limit_reach, 2)
63
+
64
+ target2 = make_target(runtime, session)
65
+ target2.fail
66
+ assert_equal(session.limit_reach, 2)
67
+ target2.pass
68
+ assert_equal(session.limit_reach, 2)
69
+ target2.pass
70
+ assert_equal(session.limit_reach, 2)
71
+ target2.pass
72
+ assert_equal(session.limit_reach, 3)
73
+ end
74
+ end
75
+
50
76
  def test_fail_then_pass
51
77
  with_runtime_on_temp_repo do |runtime|
52
78
  target = make_target(runtime)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ankit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-10 00:00:00.000000000 Z
12
+ date: 2012-03-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: highline
16
- requirement: &20716680 !ruby/object:Gem::Requirement
16
+ requirement: &7184500 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 1.6.11
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *20716680
24
+ version_requirements: *7184500
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: diff-lcs
27
- requirement: &20716160 !ruby/object:Gem::Requirement
27
+ requirement: &7183860 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 1.1.3
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *20716160
35
+ version_requirements: *7183860
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: json
38
- requirement: &20715660 !ruby/object:Gem::Requirement
38
+ requirement: &7183300 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 1.6.5
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *20715660
46
+ version_requirements: *7183300
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: mocha
49
- requirement: &20715080 !ruby/object:Gem::Requirement
49
+ requirement: &7182720 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: 0.10.4
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *20715080
57
+ version_requirements: *7182720
58
58
  description: ! 'Ankit is a CLI and terminal based flashcard program to learn your
59
59
  new language.
60
60
 
@@ -87,6 +87,7 @@ files:
87
87
  - lib/ankit/list_command.rb
88
88
  - lib/ankit.rb
89
89
  - test/data/vanilla_repo/anemone.journal
90
+ - test/data/hope-and-luck.txt
90
91
  - test/data/hello_card.card
91
92
  - test/data/hello_repo/cards/foo/this_is_not_a_card.txt
92
93
  - test/data/hello_repo/cards/foo/vanilla-please.card