ankit 0.0.1 → 0.0.2

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