ankit 0.0.0
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/bin/ankit +6 -0
- data/lib/ankit/add_command.rb +28 -0
- data/lib/ankit/card.rb +66 -0
- data/lib/ankit/card_happening_command.rb +31 -0
- data/lib/ankit/challenge_command.rb +444 -0
- data/lib/ankit/coming_command.rb +90 -0
- data/lib/ankit/command.rb +40 -0
- data/lib/ankit/event.rb +62 -0
- data/lib/ankit/event_traversing_command.rb +37 -0
- data/lib/ankit/fail_command.rb +14 -0
- data/lib/ankit/find_command.rb +38 -0
- data/lib/ankit/hello_command.rb +22 -0
- data/lib/ankit/list_command.rb +26 -0
- data/lib/ankit/name_command.rb +16 -0
- data/lib/ankit/pass_command.rb +9 -0
- data/lib/ankit/round_command.rb +31 -0
- data/lib/ankit/runtime.rb +151 -0
- data/lib/ankit/score_command.rb +24 -0
- data/lib/ankit/text_reading_command.rb +23 -0
- data/lib/ankit.rb +3 -0
- data/test/card_test.rb +92 -0
- data/test/command_test.rb +406 -0
- data/test/data/bye_card.card +2 -0
- data/test/data/hello_card.card +2 -0
- data/test/data/hello_config.rb +4 -0
- data/test/data/hello_repo/anemone.journal +2 -0
- data/test/data/hello_repo/baobab.journal +2 -0
- data/test/data/hello_repo/cards/foo/hello.card +2 -0
- data/test/data/hello_repo/cards/foo/this_is_not_a_card.txt +1 -0
- data/test/data/hello_repo/cards/foo/vanilla-please.card +2 -0
- data/test/data/hope.card +1 -0
- data/test/data/luck.card +1 -0
- data/test/data/number_repo/anemone.journal +0 -0
- data/test/data/number_repo/cards/eight.card +2 -0
- data/test/data/number_repo/cards/five.card +2 -0
- data/test/data/number_repo/cards/four.card +2 -0
- data/test/data/number_repo/cards/one.card +2 -0
- data/test/data/number_repo/cards/seven.card +2 -0
- data/test/data/number_repo/cards/six.card +2 -0
- data/test/data/number_repo/cards/three.card +2 -0
- data/test/data/number_repo/cards/two.card +2 -0
- data/test/data/vanilla_repo/anemone.journal +0 -0
- data/test/event_test.rb +29 -0
- data/test/helpers.rb +54 -0
- data/test/progress_test.rb +99 -0
- data/test/runtime_test.rb +58 -0
- metadata +138 -0
data/lib/ankit/event.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
|
2
|
+
require 'date'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Ankit
|
6
|
+
class Envelope < Struct.new(:at, :round)
|
7
|
+
def to_json(*a)
|
8
|
+
{ at: self.at.rfc3339, round: self.round }.to_json(*a)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.from_hash(hash)
|
12
|
+
Envelope.new(DateTime.rfc3339(hash["at"]), hash["round"])
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.parse(text) from_hash(JSON.parse(text)); end
|
16
|
+
def self.fresh(round=0); self.new(DateTime.new, round); end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Event
|
20
|
+
attr_reader :envelope, :values
|
21
|
+
|
22
|
+
def verb() @values["verb"]; end
|
23
|
+
def verb=(val) @values["verb"] = val; end
|
24
|
+
def name() @values["name"]; end
|
25
|
+
def type() @values["type"]; end
|
26
|
+
def maturity() @values["maturity"] || 0; end
|
27
|
+
def card?() type == "card"; end
|
28
|
+
def round() @envelope.round or 0; end
|
29
|
+
def next_round() round + 2**maturity; end
|
30
|
+
|
31
|
+
def initialize(env, values)
|
32
|
+
@envelope, @values = env, values
|
33
|
+
end
|
34
|
+
|
35
|
+
def ==(other)
|
36
|
+
@values == other.values && @envelope == other.envelope
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_json(*a) { envelope: @envelope, values: @values }.to_json(*a); end
|
40
|
+
|
41
|
+
def to_passed(env)
|
42
|
+
Event.new(env, @values.merge({ "verb" => "passed", "maturity" => maturity + 1 }))
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_failed(env)
|
46
|
+
Event.new(env, @values.merge({ "verb" => "failed", "maturity" => 0 }))
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.for_card(name, verb, env)
|
50
|
+
self.new(env, { "type" => "card", "verb" => verb, "name" => name, "maturity" => 0 })
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.from_hash(hash) Event.new(Envelope.from_hash(hash["envelope"]), hash["values"]); end
|
54
|
+
def self.parse(text) from_hash(JSON.parse(text)); end
|
55
|
+
end
|
56
|
+
|
57
|
+
module EventFormatting
|
58
|
+
def format_as_score(event)
|
59
|
+
"name:#{event.name}, verb:#{event.verb}, round:#{event.round}, maturity:#{event.maturity}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
require 'ankit/command'
|
3
|
+
require 'ankit/card'
|
4
|
+
|
5
|
+
module Ankit
|
6
|
+
class EventTraversingCommand < Command
|
7
|
+
def each_event(name=nil, &block)
|
8
|
+
runtime.config.journals.each do |j|
|
9
|
+
open(j) do |f|
|
10
|
+
f.each_line do |line|
|
11
|
+
event = Event.parse(line)
|
12
|
+
block.call(event) if nil == name or event.name == name
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# For Command Mixin
|
20
|
+
module EventTraversing
|
21
|
+
class << self
|
22
|
+
include CardNaming
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.find_latest_event_for(runtime, path)
|
26
|
+
#EventTraversingCommand.new(runtime).to_enum(:each_event, to_card_name(path)).sort_by { |x| x.round }[-1]
|
27
|
+
find_latest_event_named(runtime, to_card_name(path))
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.find_latest_event_named(runtime, name)
|
31
|
+
EventTraversingCommand.new(runtime).to_enum(:each_event, name).sort_by { |x| x.round }[-1]
|
32
|
+
end
|
33
|
+
|
34
|
+
def latest_event_for(path) EventTraversing.find_latest_event_for(self.runtime, path); end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
require 'ankit/card_happening_command'
|
3
|
+
|
4
|
+
module Ankit
|
5
|
+
class FailCommand < CardHappeningCommand
|
6
|
+
available
|
7
|
+
EVENT_HAPPENING = :to_failed
|
8
|
+
end
|
9
|
+
|
10
|
+
module Failing
|
11
|
+
include CardHappening
|
12
|
+
def make_failed(card_name); make_happen(FailCommand::EVENT_HAPPENING, card_name); end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
require 'ankit/card'
|
3
|
+
require 'ankit/command'
|
4
|
+
require 'ankit/find_command'
|
5
|
+
|
6
|
+
module Ankit
|
7
|
+
class FindCommand < Command
|
8
|
+
include CardNaming
|
9
|
+
available
|
10
|
+
|
11
|
+
def execute()
|
12
|
+
each_path { |f| runtime.stdout.print("#{f}\n") }
|
13
|
+
end
|
14
|
+
|
15
|
+
def each_path(&block)
|
16
|
+
names.each do |n|
|
17
|
+
found = path_for(n)
|
18
|
+
block.call(found) if found
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def path_for(name)
|
23
|
+
found_in = runtime.config.card_search_paths.find do |p|
|
24
|
+
File.file?(to_card_path(p, name))
|
25
|
+
end
|
26
|
+
|
27
|
+
found_in ? to_card_path(found_in, name) : nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def names; args; end
|
31
|
+
end
|
32
|
+
|
33
|
+
module Finding
|
34
|
+
def find_paths(runtime, names)
|
35
|
+
FindCommand.new(runtime, names).to_enum(:each_path).to_a
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
require 'ankit/command'
|
3
|
+
|
4
|
+
module Ankit
|
5
|
+
class HelloCommand < Command
|
6
|
+
available
|
7
|
+
|
8
|
+
def execute()
|
9
|
+
indent = " "
|
10
|
+
runtime.stdout.print("\n")
|
11
|
+
runtime.stdout.print(indent, "repo: #{runtime.config.repo}\n")
|
12
|
+
runtime.stdout.print(indent, "primary: #{runtime.config.primary_journal}\n")
|
13
|
+
runtime.stdout.print(indent, "location: #{runtime.config.location}\n")
|
14
|
+
|
15
|
+
runtime.config.card_search_paths.each do |p|
|
16
|
+
runtime.stdout.print(indent, "card_search_paths: #{p}\n")
|
17
|
+
end
|
18
|
+
|
19
|
+
runtime.stdout.print("\n")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
require 'ankit/card'
|
3
|
+
require 'ankit/command'
|
4
|
+
|
5
|
+
module Ankit
|
6
|
+
class ListCommand < Command
|
7
|
+
include CardNaming
|
8
|
+
available
|
9
|
+
|
10
|
+
def execute()
|
11
|
+
each_card { |f| runtime.stdout.print("#{f}\n") }
|
12
|
+
end
|
13
|
+
|
14
|
+
def each_card(&block)
|
15
|
+
runtime.config.card_search_paths.each do |p|
|
16
|
+
Dir.glob(card_wildcard_for(p)).each do |f|
|
17
|
+
block.call(f)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def each_card_name(&block)
|
23
|
+
each_card { |path| block.call(to_card_name(path)) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
require 'ankit/text_reading_command'
|
3
|
+
|
4
|
+
module Ankit
|
5
|
+
class NameCommand < TextReadingCommand
|
6
|
+
available
|
7
|
+
define_options { |s, o| superclass.option_spec.call(s, o) }
|
8
|
+
|
9
|
+
def execute()
|
10
|
+
validate_options
|
11
|
+
each_text do |text|
|
12
|
+
runtime.stdout.print("#{Card.parse(text).name}\n")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
require 'ankit/card'
|
3
|
+
#require 'ankit/event_traversing_command'
|
4
|
+
require 'ankit/coming_command'
|
5
|
+
|
6
|
+
module Ankit
|
7
|
+
class RoundCommand < Command
|
8
|
+
available
|
9
|
+
|
10
|
+
def execute()
|
11
|
+
runtime.stdout.print("#{last_round} #{next_round}\n")
|
12
|
+
end
|
13
|
+
|
14
|
+
def next_round
|
15
|
+
found = Coming.existing_events(runtime).first
|
16
|
+
found ? found.next_round : 0
|
17
|
+
end
|
18
|
+
|
19
|
+
def last_round
|
20
|
+
found = Coming.existing_events(runtime).max_by(&:round)
|
21
|
+
found ? found.round : 0
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module RoundCounting
|
26
|
+
def last_round; @last_round ||= RoundCommand.new(self.runtime).last_round; end
|
27
|
+
def next_round; @next_round ||= RoundCommand.new(self.runtime).next_round; end
|
28
|
+
def latest_round; last_round + 1; end
|
29
|
+
def round_proceeded; @last_round = @next_round = nil; end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
|
2
|
+
require 'optparse'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'highline'
|
5
|
+
require 'ankit/command'
|
6
|
+
require 'ankit/add_command'
|
7
|
+
require 'ankit/challenge_command'
|
8
|
+
require 'ankit/coming_command'
|
9
|
+
require 'ankit/fail_command'
|
10
|
+
require 'ankit/find_command'
|
11
|
+
require 'ankit/hello_command'
|
12
|
+
require 'ankit/list_command'
|
13
|
+
require 'ankit/name_command'
|
14
|
+
require 'ankit/pass_command'
|
15
|
+
require 'ankit/round_command'
|
16
|
+
require 'ankit/score_command'
|
17
|
+
|
18
|
+
module Ankit
|
19
|
+
|
20
|
+
class Config
|
21
|
+
DEFAULT_PATH = File.expand_path("~/.ankit")
|
22
|
+
|
23
|
+
attr_writer :repo, :location, :card_paths
|
24
|
+
|
25
|
+
def repo; @repo ||= File.expand_path("~/.ankit.d"); end
|
26
|
+
def location; @location ||= `hostname`.strip; end
|
27
|
+
def card_paths; @card_paths ||= [File.join(repo, "cards")]; end
|
28
|
+
|
29
|
+
# Computed parameters
|
30
|
+
def primary_journal
|
31
|
+
File.join(repo, "#{location}.journal")
|
32
|
+
end
|
33
|
+
|
34
|
+
def journals
|
35
|
+
Dir.glob(File.join(repo, "*.journal")).sort
|
36
|
+
end
|
37
|
+
|
38
|
+
def card_search_paths
|
39
|
+
paths = self.card_paths.dup
|
40
|
+
self.card_paths.each do |path|
|
41
|
+
Dir.glob(File.join(path, "*")).each do |f|
|
42
|
+
paths.push(f) if File.directory?(f)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
paths.sort
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def self.open(path)
|
51
|
+
config = self.new
|
52
|
+
config.instance_eval(File.open(path){ |f| f.read })
|
53
|
+
config
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.prepare_default
|
57
|
+
FileUtils.touch([DEFAULT_PATH]) # TODO: Give same example settings.
|
58
|
+
plain = Config.open(DEFAULT_PATH)
|
59
|
+
(plain.card_paths + [plain.repo]).each { |p| FileUtils.mkdir_p(p) }
|
60
|
+
FileUtils.touch([plain.primary_journal])
|
61
|
+
STDOUT.print("Prepared the default setting. You can edit #{DEFAULT_PATH}\n")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class Runtime
|
66
|
+
attr_writer :line, :stdin, :stdout, :stderr
|
67
|
+
attr_reader :config
|
68
|
+
|
69
|
+
def line; @line ||= HighLine.new; end
|
70
|
+
def stdin; @stdin ||= STDIN; end
|
71
|
+
def stdout; @stdout ||= STDOUT; end
|
72
|
+
def stderr; @stderr ||= STDERR; end
|
73
|
+
|
74
|
+
def self.split_subcommand(args)
|
75
|
+
names = Command.by_name.keys
|
76
|
+
i = args.find_index { |a| names.include?(a) }
|
77
|
+
unless i.nil?
|
78
|
+
{ global: args[0 ... i], subcommand: args[i .. -1] }
|
79
|
+
else
|
80
|
+
{ global: [], subcommand: [] }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.parse_options(args)
|
85
|
+
options = {}
|
86
|
+
OptionParser.new do |spec|
|
87
|
+
spec.on("-c", "--config FILE", "Specifies config file") { |file| options[:config] = file }
|
88
|
+
end.parse(args)
|
89
|
+
|
90
|
+
options[:noconf] = options[:config].nil?
|
91
|
+
options[:config] ||= Config::DEFAULT_PATH
|
92
|
+
options
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.setup(args)
|
96
|
+
options = self.parse_options(args)
|
97
|
+
Config.prepare_default if options[:noconf] and not File.exist?(Config::DEFAULT_PATH)
|
98
|
+
r = self.new(Config.open(options[:config]))
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.run(args)
|
102
|
+
splitted = self.split_subcommand(args)
|
103
|
+
r = self.setup(splitted[:global])
|
104
|
+
if splitted[:subcommand].empty?
|
105
|
+
# TODO: show help
|
106
|
+
else
|
107
|
+
r.dispatch(splitted[:subcommand])
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def initialize(config)
|
112
|
+
@config = config
|
113
|
+
end
|
114
|
+
|
115
|
+
def make_command(args)
|
116
|
+
name = args.shift
|
117
|
+
Command.by_name[name].new(self, args)
|
118
|
+
end
|
119
|
+
|
120
|
+
def dispatch(args)
|
121
|
+
command = make_command(args)
|
122
|
+
command.execute()
|
123
|
+
command
|
124
|
+
end
|
125
|
+
|
126
|
+
# To encourage one-liner
|
127
|
+
def dispatch_then(args)
|
128
|
+
dispatch(args)
|
129
|
+
self
|
130
|
+
end
|
131
|
+
|
132
|
+
def supress_io
|
133
|
+
saved = [@stdin, @stdout, @stderr]
|
134
|
+
["stdin=", "stdout=", "stderr="].each { |m| self.send(m, StringIO.new) }
|
135
|
+
saved
|
136
|
+
end
|
137
|
+
|
138
|
+
def unsupress_io(saved)
|
139
|
+
@stdin, @stdout, @stderr = saved
|
140
|
+
end
|
141
|
+
|
142
|
+
def with_supressing_io(&block)
|
143
|
+
saved = supress_io
|
144
|
+
begin
|
145
|
+
block.call
|
146
|
+
ensure
|
147
|
+
unsupress_io(saved)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
require 'ankit/card'
|
3
|
+
require 'ankit/event'
|
4
|
+
require 'ankit/event_traversing_command'
|
5
|
+
|
6
|
+
module Ankit
|
7
|
+
class ScoreCommand < EventTraversingCommand
|
8
|
+
include CardNaming, EventFormatting
|
9
|
+
available
|
10
|
+
|
11
|
+
define_options do |spec, options|
|
12
|
+
spec.on("-l", "--last") { options[:last] = true }
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute()
|
16
|
+
args.each do |a|
|
17
|
+
list = to_enum(:each_event, to_card_name(a)).to_a
|
18
|
+
(options[:last] ? list.sort_by(&:round).reverse.take(1) : list).each do |e|
|
19
|
+
runtime.stdout.print("#{format_as_score(e)}\n")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
require 'ankit/command'
|
3
|
+
|
4
|
+
module Ankit
|
5
|
+
class TextReadingCommand < Command
|
6
|
+
define_options do |spec, options|
|
7
|
+
spec.on("-i", "--stdin") { options[:stdin] = true }
|
8
|
+
end
|
9
|
+
|
10
|
+
def each_text(&block)
|
11
|
+
if options[:stdin]
|
12
|
+
block.call(runtime.stdin.read)
|
13
|
+
else
|
14
|
+
args.each { |name| open(name) { |f| block.call(f.read) } }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def validate_options
|
19
|
+
raise BadOptions, "--stdin cannot have any fileame" if options[:stdin] and not args.empty?
|
20
|
+
raise BadOptions, "need a fileame" if not options[:stdin] and args.empty?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/ankit.rb
ADDED
data/test/card_test.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
|
2
|
+
require 'ankit/card'
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
class CardTest < Test::Unit::TestCase
|
6
|
+
include Ankit
|
7
|
+
|
8
|
+
def test_parse_empty
|
9
|
+
actual = Card.parse(
|
10
|
+
"""
|
11
|
+
|
12
|
+
|
13
|
+
""")
|
14
|
+
assert_nil(actual)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_parse_empty_comments
|
18
|
+
actual = Card.parse(
|
19
|
+
"""
|
20
|
+
#
|
21
|
+
#
|
22
|
+
""")
|
23
|
+
assert_nil(actual)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_parse_hello
|
27
|
+
actual = Card.parse(
|
28
|
+
"""
|
29
|
+
O: Hello, how are you?
|
30
|
+
T: Konichiwa, Genki?
|
31
|
+
""")
|
32
|
+
|
33
|
+
assert_equal("Konichiwa, Genki?", actual.translation)
|
34
|
+
assert_equal("Hello, how are you?", actual.original)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_name_plain
|
38
|
+
actual = Card.parse(
|
39
|
+
"""
|
40
|
+
O: Hello, how are you?
|
41
|
+
T: Konichiwa, Genki?
|
42
|
+
""")
|
43
|
+
assert_equal("hello-how-are-you", actual.name)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_name_with_a_bracket
|
47
|
+
actual = Card.parse(
|
48
|
+
"""
|
49
|
+
O: [Hello], How are you?
|
50
|
+
T: Konichiwa, Genki?
|
51
|
+
""")
|
52
|
+
assert_equal("hello-how-are-you", actual.name)
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_name_with_brackets
|
56
|
+
actual = Card.parse(
|
57
|
+
"""
|
58
|
+
O: Hello, [How is] your project [going]?
|
59
|
+
T: Konichiwa, Genki?
|
60
|
+
""")
|
61
|
+
assert_equal("hello-how-is-your-project-going", actual.name)
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_guess_id_with_conflict
|
65
|
+
# XXX: will tackle later
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_plain_original
|
69
|
+
assert_equal(Card.new(o: "Hello").plain_original, "Hello")
|
70
|
+
assert_equal(Card.new(o: "Hello, [World].").plain_original, "Hello, World.")
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_match_hello
|
74
|
+
target = Card.new(o: "Hello")
|
75
|
+
assert( target.match?("Hello"))
|
76
|
+
assert_equal(:wrong, target.match?("Bye"))
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_match_bracket
|
80
|
+
target = Card.new(o: "Hello, [World].")
|
81
|
+
assert_equal(:match, target.match?("Hello, World."))
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_match_typo
|
85
|
+
target = Card.new(o: "Hello, [World].")
|
86
|
+
assert_equal(:typo, target.match?("Hallo, World."))
|
87
|
+
assert_equal(:typo, target.match?("Halo, World."))
|
88
|
+
assert_equal(:typo, target.match?("Hello, World!"))
|
89
|
+
assert_equal(:typo, target.match?("World!"))
|
90
|
+
assert_equal(:wrong, target.match?(""))
|
91
|
+
end
|
92
|
+
end
|