list-tool 1.0.3 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -1
- data/lib/list_tool/app/commands/add_item_command.rb +20 -23
- data/lib/list_tool/app/commands/add_list_command.rb +19 -17
- data/lib/list_tool/app/commands/clear_list_command.rb +31 -0
- data/lib/list_tool/app/commands/command.rb +50 -0
- data/lib/list_tool/app/commands/delete_item_command.rb +19 -23
- data/lib/list_tool/app/commands/delete_list_command.rb +18 -19
- data/lib/list_tool/app/commands/help_command.rb +15 -13
- data/lib/list_tool/app/commands/rename_list_command.rb +24 -25
- data/lib/list_tool/app/commands/replace_item_command.rb +20 -21
- data/lib/list_tool/app/commands/show_items_command.rb +18 -20
- data/lib/list_tool/app/commands/show_lists_command.rb +16 -14
- data/lib/list_tool/app/commands/unknown_command.rb +16 -14
- data/lib/list_tool/app/commands/use_command.rb +18 -18
- data/lib/list_tool/app/commands/version_command.rb +16 -13
- data/lib/list_tool/app/commands.rb +3 -1
- data/lib/list_tool/list.rb +5 -0
- data/lib/list_tool/lister.rb +35 -23
- data/lib/list_tool/lister_data.rb +4 -0
- data/lib/list_tool/version.rb +1 -1
- data/spec/list_tool/app/commands/add_list_command_spec.rb +0 -6
- data/spec/list_tool/app/commands/clear_list_command_spec.rb +89 -0
- data/spec/list_tool/app/commands/command_spec.rb +101 -0
- data/spec/list_tool/app/commands_spec.rb +1 -1
- data/spec/list_tool/list_spec.rb +8 -0
- data/spec/list_tool/lister_data_spec.rb +8 -0
- data/spec/list_tool/lister_spec.rb +75 -61
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be84730bfcdb5b3ac055f621aa41cc3ebc931806
|
4
|
+
data.tar.gz: 8aca87d46e3b310f25e4e1a506af4327e1f5e643
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa3eb60d41ef20b9234efdcc728a48840e50d671270b8b301ee93c266050448b2114d94f8d96a4608090e0eb4b10ab802d2c033110a8d40580adb318a878421a
|
7
|
+
data.tar.gz: 8a4696d902bf523bbaf66a5a4db2080cc24542839175b842601bc61074b39944e29ccbd66649f3bb9c8697daa5b9e7b18136e434791f952c29922f904a425336
|
data/README.md
CHANGED
@@ -39,8 +39,10 @@ Require list-tool with `require 'list_tool'` command
|
|
39
39
|
lister.lists # => { 'todolist' => 3, 'wishlist' => 2 }
|
40
40
|
# (digits for item quantitiy in a list)
|
41
41
|
lister.list(list_index=nil) # => {name: 'list_name', items: ['item1', 'item2']}
|
42
|
+
lister.default_list # => {name: 'list_name', items: ['item1', 'item2']}
|
42
43
|
|
43
44
|
For **#list** method, if list index is not specified, it will return contents of default list.
|
45
|
+
For **#default_list** method, if default list is not set, it will return nil.
|
44
46
|
|
45
47
|
#### List management methods
|
46
48
|
|
@@ -71,7 +73,7 @@ ListerData class responds to all list management methods (**\*\_list**) and also
|
|
71
73
|
|
72
74
|
#### Errors
|
73
75
|
|
74
|
-
- all lister.\*_item methods
|
76
|
+
- all lister.\*_item methods raise **ListTool::ListNotFoundError** if list was not found.
|
75
77
|
- lister.list and lister.\*_list methods raise **ListTool::NoDefaultListError** if list index is not specified and default list is not set
|
76
78
|
- #save and #load methods may return **ListTool::FileAccessError** or **ListTool::FileNotFoundError**
|
77
79
|
- other errors are generally **ArgumentError** s
|
@@ -90,6 +92,7 @@ This gem provides console tool named '**clt**' ('console list tool'), which allo
|
|
90
92
|
al, add-list NAME Create list with NAME
|
91
93
|
rl, rename-list LIST, NAME Set LIST name to NAME
|
92
94
|
dl, del-list LIST Delete given LIST
|
95
|
+
cl, clear-list LIST Clear given LIST
|
93
96
|
sl, show-lists Print list of existing lists
|
94
97
|
u, use LIST Set default list
|
95
98
|
-h, --help Print this message
|
@@ -1,38 +1,35 @@
|
|
1
1
|
module ListTool
|
2
2
|
module App
|
3
3
|
|
4
|
-
class AddItemCommand
|
4
|
+
class AddItemCommand < Command
|
5
|
+
class << self
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
def match? arg
|
8
|
+
['a', 'add-item'].include? arg
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
def parse argv
|
12
|
+
fail_if_not_an_array(argv)
|
12
13
|
|
13
|
-
|
14
|
-
|
14
|
+
item, list = argv.shift(2)
|
15
|
+
ensure_existence_of('item text' => item)
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
out[:list] = Integer(list) - 1 rescue raise(ArgumentError, 'list number must be an integer')
|
20
|
-
raise ArgumentError, "list number can't be less than 1" if out[:list] < 0
|
17
|
+
out = {text: item}
|
18
|
+
out[:list] = parse_list_number(list) if list
|
19
|
+
out
|
21
20
|
end
|
22
21
|
|
23
|
-
|
24
|
-
|
22
|
+
def execute options, lister
|
23
|
+
args = [ options.delete(:text) ]
|
24
|
+
args << options if options[:list]
|
25
|
+
raise(ListNotFoundError, 'no list with given number') if lister.add_item(*args).nil?
|
26
|
+
end
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
raise(ListNotFoundError, 'no list with given number') if lister.add_item(*args).nil?
|
30
|
-
end
|
28
|
+
def help
|
29
|
+
" a, add-item TEXT [LIST]\tAdd item with TEXT to given or default list"
|
30
|
+
end
|
31
31
|
|
32
|
-
def self.help
|
33
|
-
" a, add-item TEXT [LIST]\tAdd item with TEXT to given or default list"
|
34
32
|
end
|
35
|
-
|
36
33
|
end
|
37
34
|
|
38
35
|
end
|
@@ -1,29 +1,31 @@
|
|
1
1
|
module ListTool
|
2
2
|
module App
|
3
3
|
|
4
|
-
class AddListCommand
|
4
|
+
class AddListCommand < Command
|
5
|
+
class << self
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
def match? arg
|
8
|
+
['al', 'add-list'].include? arg
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
name = argv.shift
|
13
|
-
raise ArgumentError, 'list name not specified' if name.nil?
|
14
|
-
raise ArgumentError, 'name is not a string' unless name.is_a? String
|
11
|
+
def parse argv
|
12
|
+
fail_if_not_an_array(argv)
|
15
13
|
|
16
|
-
|
17
|
-
|
14
|
+
name = argv.shift
|
15
|
+
ensure_existence_of("list name" => name)
|
18
16
|
|
19
|
-
|
20
|
-
|
21
|
-
end
|
17
|
+
{name: name}
|
18
|
+
end
|
22
19
|
|
23
|
-
|
24
|
-
|
25
|
-
|
20
|
+
def execute options, lister
|
21
|
+
raise(RuntimeError, "list creation failed") if lister.add_list(options[:name]).nil?
|
22
|
+
end
|
23
|
+
|
24
|
+
def help
|
25
|
+
" al, add-list NAME\t\tCreate list with NAME"
|
26
|
+
end
|
26
27
|
|
28
|
+
end
|
27
29
|
end
|
28
30
|
|
29
31
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ListTool
|
2
|
+
module App
|
3
|
+
|
4
|
+
class ClearListCommand < Command
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def match? arg
|
8
|
+
['cl', 'clear-list'].include? arg
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse argv
|
12
|
+
fail_if_not_an_array(argv)
|
13
|
+
|
14
|
+
list = argv.shift
|
15
|
+
|
16
|
+
{list: parse_list_number!(list)}
|
17
|
+
end
|
18
|
+
|
19
|
+
def execute options, lister
|
20
|
+
raise(ListNotFoundError, 'no list with given number') if lister.clear_list(options[:list]).nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
def help
|
24
|
+
" cl, clear-list LIST\t\tClear given LIST"
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ListTool
|
2
|
+
module App
|
3
|
+
|
4
|
+
class Command
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def parse_number str, name
|
9
|
+
index = Integer(str) - 1 rescue raise(ArgumentError, "#{name} number must be an integer")
|
10
|
+
raise ArgumentError, "#{name} number can't be less than 1" if index < 0
|
11
|
+
index
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
def parse_item_number str
|
16
|
+
parse_number(str, "item")
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse_list_number str
|
20
|
+
parse_number(str, "list")
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def parse_item_number! str
|
25
|
+
raise ArgumentError, 'item number not specified' if str.nil?
|
26
|
+
parse_item_number(str)
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse_list_number! str
|
30
|
+
raise ArgumentError, 'list number not specified' if str.nil?
|
31
|
+
parse_list_number(str)
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def ensure_existence_of hash
|
36
|
+
hash.each do |key, value|
|
37
|
+
raise ArgumentError, "#{key} not specified" if value.nil?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def fail_if_not_an_array arg
|
42
|
+
raise ArgumentError, "expected argument to be an array, #{arg.class} given" unless arg.is_a? Array
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -1,38 +1,34 @@
|
|
1
1
|
module ListTool
|
2
2
|
module App
|
3
3
|
|
4
|
-
class DeleteItemCommand
|
4
|
+
class DeleteItemCommand < Command
|
5
|
+
class << self
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
def match? arg
|
8
|
+
['d', 'del-item'].include? arg
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
def parse argv
|
12
|
+
fail_if_not_an_array(argv)
|
12
13
|
|
13
|
-
|
14
|
-
raise ArgumentError, 'item number not specified' if item.nil?
|
14
|
+
item, list = argv.shift(2)
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
out[:list] = Integer(list) - 1 rescue raise(ArgumentError, 'list number must be an integer')
|
20
|
-
raise ArgumentError, "list number can't be less than 1" if out[:list] < 0
|
16
|
+
out = { item: parse_item_number!(item) }
|
17
|
+
out[:list] = parse_list_number(list) if list
|
18
|
+
out
|
21
19
|
end
|
22
20
|
|
23
|
-
|
24
|
-
|
21
|
+
def execute options, lister
|
22
|
+
args = [ options[:item] ]
|
23
|
+
args << {list: options[:list]} if options[:list]
|
24
|
+
raise(ListNotFoundError, 'no list with given number') if lister.delete_item(*args).nil?
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
raise(ListNotFoundError, 'no list with given number') if lister.delete_item(*args).nil?
|
30
|
-
end
|
27
|
+
def help
|
28
|
+
" d, del-item ITEM [LIST]\tDelete ITEM from given or default list"
|
29
|
+
end
|
31
30
|
|
32
|
-
def self.help
|
33
|
-
" d, del-item ITEM [LIST]\tDelete ITEM from given or default list"
|
34
31
|
end
|
35
|
-
|
36
32
|
end
|
37
33
|
|
38
34
|
end
|
@@ -1,31 +1,30 @@
|
|
1
1
|
module ListTool
|
2
2
|
module App
|
3
3
|
|
4
|
-
class DeleteListCommand
|
4
|
+
class DeleteListCommand < Command
|
5
|
+
class << self
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
def match? arg
|
8
|
+
['dl', 'del-list'].include? arg
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
def parse argv
|
12
|
+
fail_if_not_an_array(argv)
|
13
|
+
|
14
|
+
list = argv.shift
|
15
|
+
|
16
|
+
{list: parse_list_number!(list)}
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
-
|
19
|
+
def execute options, lister
|
20
|
+
raise(ListNotFoundError, 'no list with given number') if lister.delete_list(options[:list]).nil?
|
21
|
+
end
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
-
|
23
|
+
def help
|
24
|
+
" dl, del-list LIST\t\tDelete given LIST"
|
25
|
+
end
|
24
26
|
|
25
|
-
def self.help
|
26
|
-
" dl, del-list LIST\t\tDelete given LIST"
|
27
27
|
end
|
28
|
-
|
29
28
|
end
|
30
29
|
|
31
30
|
end
|
@@ -1,24 +1,26 @@
|
|
1
1
|
module ListTool
|
2
2
|
module App
|
3
3
|
|
4
|
-
class HelpCommand
|
4
|
+
class HelpCommand < Command
|
5
|
+
class << self
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
def match? arg
|
8
|
+
['h', '-h', 'help', '--help'].include? arg
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
def parse argv
|
12
|
+
{}
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
def execute options, lister
|
16
|
+
Printer.print_usage
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
def help
|
20
|
+
" -h, --help\t\t\tPrint this message"
|
21
|
+
end
|
21
22
|
|
23
|
+
end
|
22
24
|
end
|
23
25
|
end
|
24
26
|
end
|
@@ -1,33 +1,32 @@
|
|
1
1
|
module ListTool
|
2
2
|
module App
|
3
3
|
|
4
|
-
class RenameListCommand
|
4
|
+
class RenameListCommand < Command
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def match? arg
|
8
|
+
['rl', 'rename-list'].include? arg
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse argv
|
12
|
+
fail_if_not_an_array(argv)
|
13
|
+
|
14
|
+
list, name = argv.shift(2)
|
15
|
+
ensure_existence_of('new name' => name)
|
16
|
+
|
17
|
+
{ name: name, list: parse_list_number!(list) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def execute options, lister
|
21
|
+
args = [ options[:list], options[:name] ]
|
22
|
+
raise(ListNotFoundError, 'no list with given number') if lister.rename_list(*args).nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
def help
|
26
|
+
" rl, rename-list LIST, NAME\tSet LIST name to NAME"
|
27
|
+
end
|
5
28
|
|
6
|
-
def self.match? arg
|
7
|
-
['rl', 'rename-list'].include? arg
|
8
29
|
end
|
9
|
-
|
10
|
-
def self.parse argv
|
11
|
-
raise ArgumentError, "expected argument to be an array, #{argv.class} given" unless argv.is_a? Array
|
12
|
-
list, name = argv.shift(2)
|
13
|
-
raise ArgumentError, 'list number not specified' if list.nil?
|
14
|
-
raise ArgumentError, 'new name not specified' if name.nil?
|
15
|
-
|
16
|
-
list = Integer(list) - 1 rescue raise(ArgumentError, 'list number must be an integer')
|
17
|
-
raise ArgumentError, "list number can't be less than 1" if list < 0
|
18
|
-
|
19
|
-
{name: name, list: list}
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.execute options, lister
|
23
|
-
args = [ options[:list], options[:name] ]
|
24
|
-
raise(ListNotFoundError, 'no list with given number') if lister.rename_list(*args).nil?
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.help
|
28
|
-
" rl, rename-list LIST, NAME\tSet LIST name to NAME"
|
29
|
-
end
|
30
|
-
|
31
30
|
end
|
32
31
|
|
33
32
|
end
|
@@ -1,33 +1,32 @@
|
|
1
1
|
module ListTool
|
2
2
|
module App
|
3
3
|
|
4
|
-
class ReplaceItemCommand
|
4
|
+
class ReplaceItemCommand < Command
|
5
|
+
class << self
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
def match? arg
|
8
|
+
['r', 'replace-item'].include? arg
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
item, name = argv.shift(2)
|
13
|
-
raise ArgumentError, 'item number not specified' if item.nil?
|
14
|
-
raise ArgumentError, 'new name not specified' if name.nil?
|
15
|
-
|
16
|
-
item = Integer(item) - 1 rescue raise(ArgumentError, 'item number must be an integer')
|
17
|
-
raise ArgumentError, "item number can't be less than 1" if item < 0
|
11
|
+
def parse argv
|
12
|
+
fail_if_not_an_array(argv)
|
18
13
|
|
19
|
-
|
20
|
-
|
14
|
+
item, name = argv.shift(2)
|
15
|
+
ensure_existence_of('new name' => name)
|
21
16
|
|
22
|
-
|
23
|
-
|
24
|
-
raise(ItemNotFoundError, 'no item with given number') if lister.change_item(*args).nil?
|
25
|
-
end
|
17
|
+
{ name: name, item: parse_item_number!(item) }
|
18
|
+
end
|
26
19
|
|
27
|
-
|
28
|
-
|
29
|
-
|
20
|
+
def execute options, lister
|
21
|
+
args = [ options[:item], options[:name] ]
|
22
|
+
raise(ItemNotFoundError, 'no item with given number') if lister.change_item(*args).nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
def help
|
26
|
+
" r, replace-item ITEM, TEXT\tSet ITEM text to TEXT"
|
27
|
+
end
|
30
28
|
|
29
|
+
end
|
31
30
|
end
|
32
31
|
|
33
32
|
end
|
@@ -1,34 +1,32 @@
|
|
1
1
|
module ListTool
|
2
2
|
module App
|
3
3
|
|
4
|
-
class ShowItemsCommand
|
4
|
+
class ShowItemsCommand < Command
|
5
|
+
class << self
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
def match? arg
|
8
|
+
['s', 'show-items'].include? arg
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
def parse argv
|
12
|
+
fail_if_not_an_array(argv)
|
13
|
+
|
14
|
+
list = argv.shift
|
13
15
|
|
14
|
-
|
15
|
-
list = Integer( list ) - 1 rescue raise(ArgumentError, 'list number must be an integer')
|
16
|
-
raise ArgumentError, "list number can't be less than 1" if list < 0
|
16
|
+
list ? {list: parse_list_number(list)} : {}
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
def execute options, lister
|
20
|
+
items = options[:list].nil? ? lister.list() : lister.list(options[:list])
|
21
|
+
raise ListNotFoundError if items.nil?
|
22
|
+
Printer.print_items( items )
|
23
|
+
end
|
21
24
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
Printer.print_items( items )
|
26
|
-
end
|
25
|
+
def help
|
26
|
+
" s, show-items [LIST]\t\tPrint contents of default or given list"
|
27
|
+
end
|
27
28
|
|
28
|
-
def self.help
|
29
|
-
" s, show-items [LIST]\t\tPrint contents of default or given list"
|
30
29
|
end
|
31
|
-
|
32
30
|
end
|
33
31
|
|
34
32
|
end
|
@@ -1,25 +1,27 @@
|
|
1
1
|
module ListTool
|
2
2
|
module App
|
3
3
|
|
4
|
-
class ShowListsCommand
|
4
|
+
class ShowListsCommand < Command
|
5
|
+
class << self
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
def match? arg
|
8
|
+
['sl', 'show-lists'].include? arg
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
def parse argv
|
12
|
+
{}
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
def execute options, lister
|
16
|
+
lists = lister.lists
|
17
|
+
Printer.print_lists(lists)
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
def help
|
21
|
+
" sl, show-lists\t\tPrint list of existing lists"
|
22
|
+
end
|
22
23
|
|
24
|
+
end
|
23
25
|
end
|
24
26
|
|
25
27
|
end
|
@@ -1,25 +1,27 @@
|
|
1
1
|
module ListTool
|
2
2
|
module App
|
3
3
|
|
4
|
-
class UnknownCommand
|
4
|
+
class UnknownCommand < Command
|
5
|
+
class << self
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
def match? arg
|
8
|
+
@unknown_command = arg
|
9
|
+
true
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
def parse argv
|
13
|
+
{}
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
def execute options, lister
|
17
|
+
raise UnknownCommandError, "unknown command: '#{@unknown_command}'"
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
def help
|
21
|
+
""
|
22
|
+
end
|
22
23
|
|
24
|
+
end
|
23
25
|
end
|
24
26
|
|
25
27
|
end
|