ankit 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/ankit/card.rb CHANGED
@@ -1,4 +1,5 @@
1
1
 
2
+ require 'diff/lcs'
2
3
 
3
4
  module Ankit
4
5
  class Card
@@ -33,7 +34,7 @@ module Ankit
33
34
 
34
35
  def match?(text)
35
36
  return :match if plain_original == text
36
- return :wrong if text.empty?
37
+ return :wrong if 1 < (text.length - plain_original.length).abs
37
38
 
38
39
  hiddens = decorated_original { |m| "*"*m[1].size }.chars.to_a
39
40
  inside_essentials = to_enum(:diff_from_original, text).find do |ch|
@@ -0,0 +1,413 @@
1
+ require 'ankit/coming_command'
2
+ require 'ankit/fail_command'
3
+ require 'ankit/find_command'
4
+ require 'ankit/pass_command'
5
+ require 'ankit/round_command'
6
+ require 'highline'
7
+ require 'diff/lcs'
8
+
9
+ module Ankit
10
+
11
+ class StylableText
12
+ def self.styled_text(text, type)
13
+ case type
14
+ when :hidden
15
+ text.gsub(/\w/, "*")
16
+ when :failed
17
+ HighLine.color(text, HighLine::RED_STYLE)
18
+ when :warn
19
+ HighLine.color(text, HighLine::YELLOW_STYLE)
20
+ when :passed
21
+ HighLine.color(text, HighLine::GREEN_STYLE)
22
+ when :pending
23
+ HighLine.color(text, HighLine::DARK)
24
+ when :correct
25
+ HighLine.color(text, HighLine::GREEN_STYLE)
26
+ when :wrong
27
+ HighLine.color(text, HighLine::RED_STYLE)
28
+ else
29
+ raise
30
+ end
31
+ end
32
+
33
+ def initialize(text); @text = text; end
34
+
35
+ def decorated(type)
36
+ raise
37
+ decorated = @text.gsub(/\[(.*?)\]/) { |t|
38
+ p Regexp::last_match.offset(0)
39
+ self.class.styled_text($1, type)
40
+ }
41
+ decorated != @text ? decorated : self.class.styled_text(@text, type)
42
+ end
43
+
44
+ end
45
+
46
+ class Card
47
+ def hidden_original; decorated_original{ |m| StylableText.styled_text(m[1], :hidden) }; end
48
+ def corrected_original_over(wrong)
49
+ diff_from_original(wrong) do |ch|
50
+ case ch.action
51
+ when "="
52
+ ch.new_element
53
+ when "!", "+"
54
+ StylableText.styled_text(ch.new_element, :correct)
55
+ when "-"
56
+ ""
57
+ else
58
+ raise
59
+ end
60
+ end
61
+ end
62
+
63
+ def hilight_against_original(wrong)
64
+ diff_from_original(wrong) do |ch|
65
+ case ch.action
66
+ when "="
67
+ ch.old_element
68
+ when "!", "-"
69
+ StylableText.styled_text(ch.old_element, :wrong)
70
+ when "+"
71
+ ""
72
+ else
73
+ raise
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ module Challenge
80
+ class Slot < Struct.new(:path, :rating, :event)
81
+ def maturity; self.event ? self.event.maturity : 0; end
82
+ end
83
+
84
+ class Session < Struct.new(:runtime, :npassed, :nfailed, :mature_names)
85
+ def self.make(runtime)
86
+ self.new(runtime, 0, 0, [])
87
+ end
88
+
89
+ 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}"
93
+ end
94
+ end
95
+
96
+ class Progress
97
+ include CardHappening, CardNaming, RoundCounting
98
+
99
+ attr_reader :session, :slots, :index, :this_round
100
+
101
+ def initialize(session, slots)
102
+ @session, @slots, @index = session, slots, 0
103
+ @this_round = latest_round
104
+ end
105
+
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
+ def round_delta
112
+ latest_round - this_round
113
+ end
114
+
115
+ def runtime; @session.runtime; end
116
+ def last_slot; @slots[@index-1]; end
117
+ def current_slot; @slots[@index]; end
118
+ def current_path; current_slot.path; end
119
+ def size; @slots.size; end
120
+ def over?; @slots.size <= @index; end
121
+ def npassed; @slots.count { |c| c.rating == :passed }; end
122
+ def nfailed; @slots.count { |c| c.rating == :failed }; end
123
+
124
+ def already_failed?; current_slot.rating == :failed; end
125
+
126
+ def attack
127
+ current_slot.rating = :attacking unless current_slot.rating
128
+ self
129
+ end
130
+
131
+ def fail
132
+ unless already_failed?
133
+ last_slot = current_slot
134
+ last_slot.rating = :failed
135
+ last_slot.event = make_happen(FailCommand::EVENT_HAPPENING, to_card_name(current_path), this_round)
136
+ end
137
+
138
+ self
139
+ end
140
+
141
+ def pass
142
+ unless already_failed?
143
+ last_slot = current_slot
144
+ last_slot.rating = :passed
145
+ last_slot.event = make_happen(PassCommand::EVENT_HAPPENING, to_card_name(current_path), this_round)
146
+ end
147
+
148
+ @index += 1
149
+ self
150
+ end
151
+
152
+ def indicator
153
+ @slots.inject("") do |a, i|
154
+ a += case i.rating
155
+ when :failed; "x"
156
+ when :passed; "o"
157
+ when :attacking; "*"
158
+ else; "-"
159
+ end
160
+ end
161
+ end
162
+
163
+ def styled_indicator
164
+ indicator.to_enum(:each_char).map do |i|
165
+ case i
166
+ when "x"; StylableText.styled_text(i, :failed)
167
+ when "o"; StylableText.styled_text(i, :passed)
168
+ when "-"; StylableText.styled_text(i, :pending)
169
+ else; i
170
+ end
171
+ end.join
172
+ end
173
+
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
+ def maturities; slots.map(&:maturity); end
181
+ end
182
+
183
+ class State
184
+ attr_reader :progress, :last_answer
185
+
186
+ def initialize(progress, last_answer=nil)
187
+ @progress, @last_answer = progress, last_answer
188
+ end
189
+
190
+ def keep_pumping_until(&block)
191
+ state = self
192
+ until block.call(state)
193
+ state = state.pump
194
+ end
195
+ end
196
+
197
+ # http://7ujm.net/etc/esc.html
198
+ def erase_last
199
+ runtime.stdout.print("\033[1A")
200
+ end
201
+
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
+ def say(msg, type=:progress)
209
+ line.say(message_for(msg, type))
210
+ end
211
+
212
+ def show_summary_status
213
+ line.say("Round #{progress.this_round}: #{progress.styled_indicator}")
214
+ end
215
+
216
+ 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")
226
+ end
227
+
228
+ def show_and_ask_enter(msg, type)
229
+ line.ask(message_for(msg, type) + " ") { |q| q.readline = true }
230
+ end
231
+
232
+ def ask(msg="", type=:ask)
233
+ line.ask(message_for(msg, type)) { |q| q.readline = true }
234
+ end
235
+
236
+ def over?; false; end
237
+ def runtime; progress.runtime; end
238
+ def line; progress.runtime.line; end
239
+ def session; progress.session; end
240
+
241
+ private
242
+
243
+ def message_for(body, type)
244
+ case type
245
+ when :progress
246
+ " "
247
+ when :fail
248
+ StylableText.styled_text("FAIL: ", :failed)
249
+ when :typo
250
+ StylableText.styled_text("TYPO: ", :warn)
251
+ when :pass
252
+ StylableText.styled_text("PASS: ", :passed)
253
+ when :ask
254
+ " > "
255
+ when :hit_return
256
+ " < "
257
+ when :cont
258
+ " "
259
+ else
260
+ raise "Unknown header type:#{type}"
261
+ end + body
262
+ end
263
+
264
+ def ask_header; " > "; end
265
+ end
266
+
267
+ # XXX: test
268
+ class EditState < State
269
+ def pump
270
+ # XXX: makes configurable
271
+ system("vi " + progress.current_path)
272
+ QuestionState.new(progress)
273
+ end
274
+ end
275
+
276
+ module CommandRecognizing
277
+ def may_pump_command(answered)
278
+ /^\/(\w+)/.match(answered) ? pump_command($1) : nil
279
+ end
280
+
281
+ def pump_command(command)
282
+ case command
283
+ when "e", "edit"
284
+ EditState.new(progress)
285
+ when "z", "zero"
286
+ QuestionState.new(progress)
287
+ else
288
+ raise
289
+ end
290
+ end
291
+ end
292
+
293
+ class QuestionState < State
294
+ include CommandRecognizing
295
+
296
+ def pump
297
+ progress.attack
298
+ clear_screen
299
+ show_header
300
+ card = progress.current_card
301
+ say("#{card.translation}")
302
+ say("#{card.hidden_original}", :cont)
303
+ 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)
313
+ else
314
+ raise
315
+ end
316
+ end
317
+ end
318
+
319
+ class FailedStateBase < State
320
+ include CommandRecognizing
321
+
322
+ def pump
323
+ original = progress.current_card.corrected_original_over(last_answer)
324
+ typed = progress.current_card.hilight_against_original(last_answer)
325
+ erase_last
326
+ say("#{typed}", :ask)
327
+ say("#{original}", decoration_type)
328
+ answered = ask("", :hit_return)
329
+ c = may_pump_command(answered)
330
+ return c if c
331
+ mark_progress
332
+ QuestionState.new(progress)
333
+ end
334
+
335
+ end
336
+
337
+ class FailedState < FailedStateBase
338
+ def mark_progress
339
+ progress.fail
340
+ end
341
+
342
+ def decoration_type
343
+ :fail
344
+ end
345
+ end
346
+
347
+ class TypoState < FailedStateBase
348
+ def mark_progress
349
+
350
+ end
351
+
352
+ def decoration_type
353
+ :typo
354
+ end
355
+ end
356
+
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)
364
+ end
365
+ end
366
+
367
+ class BreakingState < State
368
+ def pump
369
+ progress.update_session
370
+ clear_screen
371
+ show_breaking_status
372
+
373
+ case ask_more
374
+ when :yes
375
+ initial_state
376
+ when :no
377
+ OverState.new(progress)
378
+ else
379
+ # TODO: handle help
380
+ self
381
+ end
382
+ end
383
+
384
+ def ask_more
385
+ case line.ask("More(y/n/?) ").strip
386
+ when /^y/, ""
387
+ :yes
388
+ when /^n/
389
+ :no
390
+ else
391
+ :help
392
+ end
393
+ end
394
+
395
+ def coming_limit
396
+ progress.size
397
+ end
398
+ end
399
+
400
+ class OverState < State
401
+ def over?; true; end
402
+ end
403
+
404
+ module Approaching
405
+ def initial_state
406
+ slots = Coming.coming_paths(self.runtime).take(self.coming_limit).map { |path| Slot.new(path, nil) }
407
+ QuestionState.new(Progress.new(self.session, slots))
408
+ end
409
+ end
410
+
411
+ class BreakingState; include Approaching; end
412
+ end
413
+ end
@@ -1,419 +1,9 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  require 'ankit/command'
4
- require 'ankit/coming_command'
5
- require 'ankit/fail_command'
6
- require 'ankit/find_command'
7
- require 'ankit/pass_command'
8
- require 'ankit/round_command'
9
- require 'highline'
10
- require 'diff/lcs'
4
+ require 'ankit/challenge'
11
5
 
12
6
  module Ankit
13
-
14
- class StylableText
15
- def self.styled_text(text, type)
16
- case type
17
- when :hidden
18
- text.gsub(/\w/, "*")
19
- when :failed
20
- HighLine.color(text, HighLine::RED_STYLE)
21
- when :warn
22
- HighLine.color(text, HighLine::YELLOW_STYLE)
23
- when :passed
24
- HighLine.color(text, HighLine::GREEN_STYLE)
25
- when :pending
26
- HighLine.color(text, HighLine::DARK)
27
- when :plus
28
- HighLine.color(text, HighLine::RED_STYLE)
29
- when :minus
30
- HighLine.color(text, HighLine::REVERSE_STYLE)
31
- else
32
- raise
33
- end
34
- end
35
-
36
- def initialize(text); @text = text; end
37
-
38
- def decorated(type)
39
- raise
40
- decorated = @text.gsub(/\[(.*?)\]/) { |t|
41
- p Regexp::last_match.offset(0)
42
- self.class.styled_text($1, type)
43
- }
44
- decorated != @text ? decorated : self.class.styled_text(@text, type)
45
- end
46
-
47
- def diff(orig)
48
- return @text if @text.empty?
49
-
50
- changes = Diff::LCS.sdiff(orig, @text)
51
- changes.map do |ch|
52
- case ch.action
53
- when "="
54
- ch.new_element
55
- when "!"
56
- self.class.styled_text(ch.new_element, :plus)
57
- when "-"
58
- self.class.styled_text(ch.old_element, :minus)
59
- when "+"
60
- self.class.styled_text(ch.new_element, :plus)
61
- else
62
- raise
63
- end
64
- end.join("")
65
- end
66
- end
67
-
68
- class Card
69
- def hidden_original; decorated_original{ |m| StylableText.styled_text(m[1], :hidden) }; end
70
- def hilighted_diff_from_original(text)
71
- diff_from_original(text) do |ch|
72
- case ch.action
73
- when "="
74
- ch.new_element
75
- when "!"
76
- StylableText.styled_text(ch.new_element, :plus)
77
- when "-"
78
- StylableText.styled_text(ch.old_element, :minus)
79
- when "+"
80
- StylableText.styled_text(ch.new_element, :plus)
81
- else
82
- raise
83
- end
84
- end
85
- end
86
- end
87
-
88
- module Challenge
89
- class Slot < Struct.new(:path, :rating, :event)
90
- def maturity; self.event ? self.event.maturity : 0; end
91
- end
92
-
93
- class Session < Struct.new(:runtime, :npassed, :nfailed, :mature_names)
94
- def self.make(runtime)
95
- self.new(runtime, 0, 0, [])
96
- end
97
-
98
- def summary_text
99
- total = self.npassed + self.nfailed
100
- return "" if 0 == total
101
- "#{self.npassed}/#{total} = #{self.npassed.to_f/total.to_f}, #mature: #{mature_names.size}"
102
- end
103
- end
104
-
105
- class Progress
106
- include CardHappening, CardNaming, RoundCounting
107
-
108
- attr_reader :session, :slots, :index, :this_round
109
-
110
- def initialize(session, slots)
111
- @session, @slots, @index = session, slots, 0
112
- @this_round = latest_round
113
- end
114
-
115
- def current_card
116
- # XXX: might be better to cache
117
- Card.parse(open(current_path, "r") { |f| f.read })
118
- end
119
-
120
- def round_delta
121
- latest_round - this_round
122
- end
123
-
124
- def runtime; @session.runtime; end
125
- def last_slot; @slots[@index-1]; end
126
- def current_slot; @slots[@index]; end
127
- def current_path; current_slot.path; end
128
- def size; @slots.size; end
129
- def over?; @slots.size <= @index; end
130
- def npassed; @slots.count { |c| c.rating == :passed }; end
131
- def nfailed; @slots.count { |c| c.rating == :failed }; end
132
-
133
- def already_failed?; current_slot.rating == :failed; end
134
-
135
- def attack
136
- current_slot.rating = :attacking unless current_slot.rating
137
- self
138
- end
139
-
140
- def fail
141
- unless already_failed?
142
- last_slot = current_slot
143
- last_slot.rating = :failed
144
- last_slot.event = make_happen(FailCommand::EVENT_HAPPENING, to_card_name(current_path), this_round)
145
- end
146
-
147
- self
148
- end
149
-
150
- def pass
151
- unless already_failed?
152
- last_slot = current_slot
153
- last_slot.rating = :passed
154
- last_slot.event = make_happen(PassCommand::EVENT_HAPPENING, to_card_name(current_path), this_round)
155
- end
156
-
157
- @index += 1
158
- self
159
- end
160
-
161
- def indicator
162
- @slots.inject("") do |a, i|
163
- a += case i.rating
164
- when :failed; "x"
165
- when :passed; "o"
166
- when :attacking; "*"
167
- else; "-"
168
- end
169
- end
170
- end
171
-
172
- def styled_indicator
173
- indicator.to_enum(:each_char).map do |i|
174
- case i
175
- when "x"; StylableText.styled_text(i, :failed)
176
- when "o"; StylableText.styled_text(i, :passed)
177
- when "-"; StylableText.styled_text(i, :pending)
178
- else; i
179
- end
180
- end.join
181
- end
182
-
183
- def update_session
184
- session.npassed += npassed
185
- session.nfailed += nfailed
186
- session.mature_names = (session.mature_names + slots.select{ |s| 1 < s.maturity }.map(&:path)).uniq
187
- end
188
-
189
- def maturities; slots.map(&:maturity); end
190
- end
191
-
192
- class State
193
- attr_reader :progress, :last_answer
194
-
195
- def initialize(progress, last_answer=nil)
196
- @progress, @last_answer = progress, last_answer
197
- end
198
-
199
- def keep_pumping_until(&block)
200
- state = self
201
- until block.call(state)
202
- state = state.pump
203
- end
204
- end
205
-
206
- def clear_screen
207
- runtime.stdout.print("\033[2J")
208
- h = HighLine::SystemExtensions.terminal_size[0]
209
- runtime.stdout.print("\033[#{h}0A")
210
- end
211
-
212
- def say(msg, type=:progress)
213
- line.say(message_for(msg, type))
214
- end
215
-
216
- def show_summary_status
217
- line.say("Round #{progress.this_round}: #{progress.styled_indicator}")
218
- end
219
-
220
- def show_breaking_status
221
- show_summary_status
222
- line.say("Maturity: #{progress.maturities.map(&:to_s).join(',')}")
223
- line.say("Session: #{progress.session.summary_text}")
224
- line.say("next round will be +#{progress.round_delta}")
225
- end
226
-
227
- def show_header
228
- show_summary_status
229
- line.say("\n")
230
- end
231
-
232
- def show_and_ask_enter(msg, type)
233
- line.ask(message_for(msg, type) + " ") { |q| q.readline = true }
234
- end
235
-
236
- def ask(msg="", type=:ask)
237
- line.ask(message_for(msg, type)) { |q| q.readline = true }
238
- end
239
-
240
- def over?; false; end
241
- def runtime; progress.runtime; end
242
- def line; progress.runtime.line; end
243
- def session; progress.session; end
244
-
245
- private
246
-
247
- def message_for(body, type)
248
- case type
249
- when :progress
250
- " "
251
- when :fail
252
- StylableText.styled_text("FAIL: ", :failed)
253
- when :typo
254
- StylableText.styled_text("TYPO: ", :warn)
255
- when :pass
256
- StylableText.styled_text("PASS: ", :passed)
257
- when :ask
258
- " > "
259
- when :hit_return
260
- " < "
261
- when :cont
262
- " "
263
- else
264
- raise "Unknown header type:#{type}"
265
- end + body
266
- end
267
-
268
- def ask_header; " > "; end
269
- end
270
-
271
- # XXX: test
272
- class EditState < State
273
- def pump
274
- # XXX: makes configurable
275
- system("vi " + progress.current_path)
276
- QuestionState.new(progress)
277
- end
278
- end
279
-
280
- module CommandRecognizing
281
- def may_pump_command(answered)
282
- /^\/(\w+)/.match(answered) ? pump_command($1) : nil
283
- end
284
-
285
- def pump_command(command)
286
- case command
287
- when "edit"
288
- EditState.new(progress)
289
- else
290
- raise
291
- end
292
- end
293
- end
294
-
295
- class QuestionState < State
296
- include CommandRecognizing
297
-
298
- def pump
299
- progress.attack
300
- clear_screen
301
- show_header
302
- card = progress.current_card
303
- say("#{card.translation}")
304
- say("#{card.hidden_original}", :cont)
305
- answered = ask().strip
306
- c = may_pump_command(answered)
307
- return c if c
308
- case card.match?(answered.strip)
309
- when :match
310
- PassedState.new(progress, answered)
311
- when :wrong
312
- FailedState.new(progress, answered)
313
- when :typo
314
- TypoState.new(progress, answered)
315
- else
316
- raise
317
- end
318
- end
319
- end
320
-
321
- class FailedStateBase < State
322
- include CommandRecognizing
323
-
324
- def pump
325
- diff_from_original = progress.current_card.hilighted_diff_from_original(last_answer)
326
- say("#{diff_from_original}", decoration_type)
327
- answered = ask("", :hit_return)
328
- c = may_pump_command(answered)
329
- return c if c
330
- mark_progress
331
- QuestionState.new(progress)
332
- end
333
-
334
- end
335
-
336
- class FailedState < FailedStateBase
337
- def mark_progress
338
- progress.fail
339
- end
340
-
341
- def decoration_type
342
- :fail
343
- end
344
- end
345
-
346
- class TypoState < FailedStateBase
347
- def mark_progress
348
-
349
- end
350
-
351
- def decoration_type
352
- :typo
353
- end
354
- end
355
-
356
- class PassedState < State
357
- include CommandRecognizing
358
-
359
- def pump
360
- progress.pass
361
- last_maturity = progress.last_slot.event.maturity
362
- say("Maturity: #{last_maturity}", :pass)
363
- answered = ask("", :hit_return)
364
- c = may_pump_command(answered)
365
- return c if c
366
- progress.over? ? BreakingState.new(progress) : QuestionState.new(progress)
367
- end
368
- end
369
-
370
- class BreakingState < State
371
- def pump
372
- progress.update_session
373
- clear_screen
374
- show_breaking_status
375
-
376
- case ask_more
377
- when :yes
378
- initial_state
379
- when :no
380
- OverState.new(progress)
381
- else
382
- # TODO: handle help
383
- self
384
- end
385
- end
386
-
387
- def ask_more
388
- case line.ask("More(y/n/?) ").strip
389
- when /^y/, ""
390
- :yes
391
- when /^n/
392
- :no
393
- else
394
- :help
395
- end
396
- end
397
-
398
- def coming_limit
399
- progress.size
400
- end
401
- end
402
-
403
- class OverState < State
404
- def over?; true; end
405
- end
406
-
407
- module Approaching
408
- def initial_state
409
- slots = Coming.coming_paths(self.runtime).take(self.coming_limit).map { |path| Slot.new(path, nil) }
410
- QuestionState.new(Progress.new(self.session, slots))
411
- end
412
- end
413
-
414
- class BreakingState; include Approaching; end
415
- end
416
-
417
7
  class ChallengeCommand < Command
418
8
  include Challenge, RoundCounting, Coming, Finding
419
9
  include Challenge::Approaching
data/lib/ankit/command.rb CHANGED
@@ -11,10 +11,13 @@ module Ankit
11
11
  COMMANDS.push(self)
12
12
  end
13
13
 
14
+ def self.command_name
15
+ /(.*)\:\:(\w+)Command/.match(self.name).to_a[-1].downcase
16
+ end
17
+
14
18
  def self.by_name
15
19
  COMMANDS.inject({}) do |a, cls|
16
- name = /(.*)\:\:(\w+)Command/.match(cls.name).to_a[-1].downcase
17
- a[name] = cls
20
+ a[cls.command_name] = cls
18
21
  a
19
22
  end
20
23
  end
data/lib/ankit/runtime.rb CHANGED
@@ -102,7 +102,7 @@ module Ankit
102
102
  splitted = self.split_subcommand(args)
103
103
  r = self.setup(splitted[:global])
104
104
  if splitted[:subcommand].empty?
105
- # TODO: show help
105
+ r.dispatch([ChallengeCommand.command_name])
106
106
  else
107
107
  r.dispatch(splitted[:subcommand])
108
108
  end
data/test/card_test.rb CHANGED
@@ -86,7 +86,17 @@ T: Konichiwa, Genki?
86
86
  assert_equal(:typo, target.match?("Hallo, World."))
87
87
  assert_equal(:typo, target.match?("Halo, World."))
88
88
  assert_equal(:typo, target.match?("Hello, World!"))
89
- assert_equal(:typo, target.match?("World!"))
89
+ assert_equal(:wrong, target.match?("World."))
90
+ assert_equal(:wrong, target.match?("Hello,"))
90
91
  assert_equal(:wrong, target.match?(""))
91
92
  end
93
+
94
+ def test_diff
95
+ Card.new(o:"hello").corrected_original_over("helo") {}
96
+ Card.new(o:"helo").corrected_original_over("hello") {}
97
+ Card.new(o:"helxo").corrected_original_over("hello") {}
98
+ Card.new(o:"hello").hilight_against_original("helo") {}
99
+ Card.new(o:"helo").hilight_against_original("hello") {}
100
+ Card.new(o:"helxo").hilight_against_original("hello") {}
101
+ end
92
102
  end
data/test/command_test.rb CHANGED
@@ -312,6 +312,18 @@ class ChallengeTest < Test::Unit::TestCase
312
312
  end
313
313
  end
314
314
 
315
+ def test_question_to_fail_but_zero_it
316
+ with_runtime_on_temp_repo do |runtime|
317
+ actual = ChallengeCommand.new(runtime).initial_state
318
+ actual_next = enter_text_pump(actual, FIRST_WRONG_ANSWER)
319
+ assert_instance_of(Challenge::FailedState, actual_next)
320
+ actual_next = enter_text_pump(actual_next, "/zero")
321
+ assert_instance_of(Challenge::QuestionState, actual_next)
322
+ assert_equal(actual_next.progress.npassed, 0)
323
+ assert_equal(actual_next.progress.nfailed, 0)
324
+ end
325
+ end
326
+
315
327
  def test_question_to_typo
316
328
  with_runtime_on_temp_repo do |runtime|
317
329
  actual = ChallengeCommand.new(runtime).initial_state
@@ -323,7 +335,7 @@ class ChallengeTest < Test::Unit::TestCase
323
335
  assert_equal(actual_next.progress.nfailed, 0)
324
336
  actual_next = enter_text_pump(actual_next, FIRST_CORRECT_ANSWER)
325
337
  assert_instance_of(Challenge::PassedState, actual_next)
326
- hit_return_pump(actual_next)
338
+ actual_next.pump
327
339
  assert_equal(actual_next.progress.npassed, 1)
328
340
  assert_equal(actual_next.progress.nfailed, 0)
329
341
  end
@@ -334,7 +346,7 @@ class ChallengeTest < Test::Unit::TestCase
334
346
  actual = ChallengeCommand.new(runtime).initial_state
335
347
  actual_next = enter_text_pump(actual, FIRST_CORRECT_ANSWER)
336
348
  assert_instance_of(Challenge::PassedState, actual_next)
337
- actual_next = hit_return_pump(actual_next)
349
+ actual_next = actual_next.pump
338
350
  assert_instance_of(Challenge::QuestionState, actual_next)
339
351
  assert_equal(actual_next.progress.npassed, 1)
340
352
  assert_equal(actual_next.progress.nfailed, 0)
@@ -344,11 +356,13 @@ class ChallengeTest < Test::Unit::TestCase
344
356
  def pass_two(state)
345
357
  state = enter_text_pump(state, FIRST_CORRECT_ANSWER)
346
358
  assert_instance_of(Challenge::PassedState, state)
347
- state = hit_return_pump(state)
359
+ state = state.pump
348
360
  assert_instance_of(Challenge::QuestionState, state)
349
361
  state = enter_text_pump(state, SECOND_CORRECT_ANSWER)
350
362
  assert_instance_of(Challenge::PassedState, state)
351
- hit_return_pump(state)
363
+ state = state.pump
364
+ assert_equal(state.progress.npassed, 2)
365
+ state
352
366
  end
353
367
 
354
368
  def test_to_breaking
@@ -397,10 +411,4 @@ class StylableTextTest < Test::Unit::TestCase
397
411
  assert_equal("*****, *****!",
398
412
  Card.new(o: "Hello, World!").hidden_original)
399
413
  end
400
-
401
- def test_diff
402
- StylableText.new("hello").diff("helo")
403
- StylableText.new("helo").diff("hello")
404
- StylableText.new("helxo").diff("hello")
405
- end
406
414
  end
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.1
4
+ version: 0.0.2
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-02-19 00:00:00.000000000 Z
12
+ date: 2012-03-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: highline
16
- requirement: &9187100 !ruby/object:Gem::Requirement
16
+ requirement: &20716680 !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: *9187100
24
+ version_requirements: *20716680
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: diff-lcs
27
- requirement: &9186500 !ruby/object:Gem::Requirement
27
+ requirement: &20716160 !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: *9186500
35
+ version_requirements: *20716160
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: json
38
- requirement: &9185920 !ruby/object:Gem::Requirement
38
+ requirement: &20715660 !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: *9185920
46
+ version_requirements: *20715660
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: mocha
49
- requirement: &9185360 !ruby/object:Gem::Requirement
49
+ requirement: &20715080 !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: *9185360
57
+ version_requirements: *20715080
58
58
  description: ! 'Ankit is a CLI and terminal based flashcard program to learn your
59
59
  new language.
60
60
 
@@ -82,6 +82,7 @@ files:
82
82
  - lib/ankit/name_command.rb
83
83
  - lib/ankit/score_command.rb
84
84
  - lib/ankit/card_happening_command.rb
85
+ - lib/ankit/challenge.rb
85
86
  - lib/ankit/fail_command.rb
86
87
  - lib/ankit/list_command.rb
87
88
  - lib/ankit.rb