ruby-zoom 3.5.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/z +189 -147
- data/bin/zc +189 -147
- data/bin/zf +189 -147
- data/bin/zg +189 -147
- data/bin/zl +189 -147
- data/bin/zr +189 -147
- data/lib/zoom.rb +48 -604
- data/lib/zoom/cache.rb +253 -0
- data/lib/zoom/cache/result.rb +49 -0
- data/lib/zoom/config.rb +156 -0
- data/lib/zoom/editor.rb +121 -0
- data/lib/zoom/error.rb +6 -6
- data/lib/zoom/error/{executable_not_found_error.rb → executable_not_found.rb} +1 -3
- data/lib/zoom/error/invalid_color.rb +5 -0
- data/lib/zoom/error/{invalid_tag_error.rb → invalid_tag.rb} +1 -3
- data/lib/zoom/error/{profile_class_unknown_error.rb → profile_class_unknown.rb} +1 -3
- data/lib/zoom/error/{profile_does_not_exist_error.rb → profile_does_not_exist.rb} +1 -3
- data/lib/zoom/error/profile_not_named.rb +7 -0
- data/lib/zoom/profile.rb +162 -66
- data/lib/zoom/profile/ack.rb +5 -30
- data/lib/zoom/profile/ag.rb +2 -37
- data/lib/zoom/profile/find.rb +2 -25
- data/lib/zoom/profile/grep.rb +2 -44
- data/lib/zoom/profile/passwords.rb +27 -59
- data/lib/zoom/profile/pt.rb +2 -26
- data/lib/zoom/profile_manager.rb +49 -0
- data/lib/zoom/wish/add_wish.rb +58 -0
- data/lib/zoom/wish/color_wish.rb +147 -0
- data/lib/zoom/wish/copy_wish.rb +57 -0
- data/lib/zoom/wish/delete_wish.rb +51 -0
- data/lib/zoom/wish/edit_wish.rb +130 -0
- data/lib/zoom/wish/editor_wish.rb +31 -0
- data/lib/zoom/wish/list_wish.rb +58 -0
- data/lib/zoom/wish/rename_wish.rb +69 -0
- data/lib/zoom/wish/reset_wish.rb +40 -0
- data/lib/zoom/wish/use_wish.rb +61 -0
- metadata +118 -24
- data/lib/string.rb +0 -48
- data/lib/zoom/error/profile_already_exists_error.rb +0 -8
- data/lib/zoom/error/profile_can_not_be_modified_error.rb +0 -8
data/lib/zoom/profile/ack.rb
CHANGED
@@ -1,36 +1,11 @@
|
|
1
|
-
require "shellwords"
|
2
|
-
require "zoom/profile"
|
3
|
-
|
4
1
|
class Zoom::Profile::Ack < Zoom::Profile
|
5
|
-
def
|
6
|
-
'ACK_COLOR_LINENO=white ACK_COLOR_MATCH="black on_white"'
|
7
|
-
end
|
8
|
-
|
9
|
-
def exe(args, pattern)
|
10
|
-
if (pattern.nil? || pattern.empty?)
|
11
|
-
system(
|
12
|
-
"#{self.to_s} --pager \"#{@pager}\" #{args} " \
|
13
|
-
"#{self.append}"
|
14
|
-
)
|
15
|
-
else
|
16
|
-
system(
|
17
|
-
"#{self.to_s} --pager \"#{@pager}\" #{args} " \
|
18
|
-
"#{pattern.shellescape} #{self.append}"
|
19
|
-
)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def initialize(
|
24
|
-
operator = nil,
|
25
|
-
flags = "--smart-case",
|
26
|
-
envprepend = "",
|
27
|
-
append = ""
|
28
|
-
)
|
2
|
+
def initialize(n, o = "ack", f = "--smart-case", b = "", a = "")
|
29
3
|
# Special case because of debian
|
30
|
-
|
31
|
-
|
4
|
+
if ((o == "ack") && ScoobyDoo.where_are_you("ack-grep"))
|
5
|
+
o = "ack-grep"
|
6
|
+
end
|
32
7
|
|
33
|
-
super(
|
8
|
+
super(n, o, f, b, a)
|
34
9
|
@taggable = true
|
35
10
|
end
|
36
11
|
end
|
data/lib/zoom/profile/ag.rb
CHANGED
@@ -1,41 +1,6 @@
|
|
1
|
-
require "shellwords"
|
2
|
-
require "zoom/profile"
|
3
|
-
|
4
1
|
class Zoom::Profile::Ag < Zoom::Profile
|
5
|
-
def
|
6
|
-
|
7
|
-
end
|
8
|
-
|
9
|
-
def exe(args, pattern)
|
10
|
-
if (pattern.nil? || pattern.empty?)
|
11
|
-
system(
|
12
|
-
"#{self.to_s} --pager \"#{@pager}\" #{args} " \
|
13
|
-
"#{self.append}"
|
14
|
-
)
|
15
|
-
else
|
16
|
-
system(
|
17
|
-
"#{self.to_s} --pager \"#{@pager}\" #{args} " \
|
18
|
-
"#{pattern.shellescape} #{self.append}"
|
19
|
-
)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def initialize(
|
24
|
-
operator = nil,
|
25
|
-
flags = "",
|
26
|
-
envprepend = "",
|
27
|
-
append = ""
|
28
|
-
)
|
29
|
-
super("ag", flags, envprepend, append)
|
2
|
+
def initialize(n, o = "ag", f = "-S", b = "", a = "")
|
3
|
+
super(n, o, f, b, a)
|
30
4
|
@taggable = true
|
31
5
|
end
|
32
|
-
|
33
|
-
def to_s
|
34
|
-
[
|
35
|
-
self["prepend"],
|
36
|
-
self["operator"],
|
37
|
-
self.colors,
|
38
|
-
self["flags"]
|
39
|
-
].join(" ").strip
|
40
|
-
end
|
41
6
|
end
|
data/lib/zoom/profile/find.rb
CHANGED
@@ -1,29 +1,6 @@
|
|
1
|
-
require "shellwords"
|
2
|
-
require "zoom/profile"
|
3
|
-
|
4
1
|
class Zoom::Profile::Find < Zoom::Profile
|
5
|
-
def
|
6
|
-
|
7
|
-
system(
|
8
|
-
"#{self.to_s} #{args} \"*\" #{self.append} | " \
|
9
|
-
"#{@pager}"
|
10
|
-
)
|
11
|
-
else
|
12
|
-
system(
|
13
|
-
"#{self.to_s} #{args} \"#{pattern}\" " \
|
14
|
-
"#{self.append} | #{@pager}"
|
15
|
-
)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def initialize(
|
20
|
-
operator = nil,
|
21
|
-
flags = ". -name",
|
22
|
-
envprepend = "",
|
23
|
-
append = ""
|
24
|
-
)
|
25
|
-
super("find", flags, envprepend, append)
|
26
|
-
@immutable = true
|
2
|
+
def initialize(n, o = "find", f = ". -name", b = "", a = "")
|
3
|
+
super(n, o, f, b, a)
|
27
4
|
@taggable = true
|
28
5
|
end
|
29
6
|
end
|
data/lib/zoom/profile/grep.rb
CHANGED
@@ -1,48 +1,6 @@
|
|
1
|
-
require "shellwords"
|
2
|
-
require "zoom/profile"
|
3
|
-
|
4
1
|
class Zoom::Profile::Grep < Zoom::Profile
|
5
|
-
def
|
6
|
-
|
7
|
-
'GREP_COLORS="',
|
8
|
-
"fn=1;32:",
|
9
|
-
"ln=0;37:",
|
10
|
-
"ms=47;1;30:",
|
11
|
-
"mc=47;1;30:",
|
12
|
-
"sl=:cx=:bn=:se=",
|
13
|
-
'"'
|
14
|
-
].join.strip
|
15
|
-
end
|
16
|
-
|
17
|
-
def exe(args, pattern)
|
18
|
-
# Emulate ag/ack as much as possible
|
19
|
-
if (pattern.nil? || pattern.empty?)
|
20
|
-
system(
|
21
|
-
"#{self.to_s} #{args} #{self.append} | " \
|
22
|
-
"sed \"s|\\[K[:-]|\\[K\\n|\" | #{@pager}"
|
23
|
-
)
|
24
|
-
else
|
25
|
-
system(
|
26
|
-
"#{self.to_s} #{args} #{pattern.shellescape} " \
|
27
|
-
"#{self.append} | sed \"s|\\[K[:-]|\\[K\\n|\" | " \
|
28
|
-
"#{@pager}"
|
29
|
-
)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def initialize(
|
34
|
-
operator = nil,
|
35
|
-
flags = [
|
36
|
-
"--color=always",
|
37
|
-
"-EHIinR",
|
38
|
-
"--exclude-dir=.bzr",
|
39
|
-
"--exclude-dir=.git",
|
40
|
-
"--exclude-dir=.svn"
|
41
|
-
].join(" ").strip,
|
42
|
-
envprepend = "",
|
43
|
-
append = "."
|
44
|
-
)
|
45
|
-
super("grep", flags, envprepend, append)
|
2
|
+
def initialize(n, o = "grep", f = "-Ii", b = "", a = ".")
|
3
|
+
super(n, o, f, b, a)
|
46
4
|
@taggable = true
|
47
5
|
end
|
48
6
|
end
|
@@ -1,72 +1,40 @@
|
|
1
|
-
require "zoom/
|
2
|
-
require "zoom/profile/ag"
|
3
|
-
require "zoom/profile/ack"
|
4
|
-
require "zoom/profile/grep"
|
1
|
+
require "zoom/profile_manager"
|
5
2
|
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
case Zoom::ProfileManager.default_profile
|
4
|
+
when /^ack(-grep)?$/
|
5
|
+
class Zoom::Profile::Passwords < Zoom::Profile::Ack
|
9
6
|
end
|
10
|
-
|
11
|
-
|
12
|
-
@profile.exe(args, pattern)
|
7
|
+
when "ag"
|
8
|
+
class Zoom::Profile::Passwords < Zoom::Profile::Ag
|
13
9
|
end
|
14
|
-
|
15
|
-
|
16
|
-
[
|
17
|
-
"Class : #{self.class.to_s}",
|
18
|
-
"Prepend : #{@profile.prepend}",
|
19
|
-
"Operator: #{@profile.operator}",
|
20
|
-
"Flags : #{@profile.flags}",
|
21
|
-
"Append : #{@profile.append}"
|
22
|
-
].join("\n").strip
|
10
|
+
when "pt"
|
11
|
+
class Zoom::Profile::Passwords < Zoom::Profile::Pt
|
23
12
|
end
|
13
|
+
else
|
14
|
+
class Zoom::Profile::Passwords < Zoom::Profile::Grep
|
15
|
+
end
|
16
|
+
end
|
24
17
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
envprepend = "",
|
29
|
-
append = ""
|
30
|
-
)
|
31
|
-
@passwd_regex = "\"pass(word|wd)?[^:=,>]? *[:=,>]\""
|
18
|
+
class Zoom::Profile::Passwords
|
19
|
+
def initialize(n, o = nil, f = "", b = "", a = "")
|
20
|
+
op = Zoom::ProfileManager.default_profile
|
32
21
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
)
|
40
|
-
elsif (
|
41
|
-
ScoobyDoo.where_are_you("ack") ||
|
42
|
-
ScoobyDoo.where_are_you("ack-grep")
|
43
|
-
)
|
44
|
-
@profile = Zoom::Profile::Ack.new(
|
45
|
-
nil,
|
46
|
-
"--smart-case",
|
47
|
-
"",
|
48
|
-
@passwd_regex
|
22
|
+
case op
|
23
|
+
when /^ack(-grep)?$/
|
24
|
+
super(
|
25
|
+
n,
|
26
|
+
op,
|
27
|
+
"--smart-case --ignore-dir=test --ignore-dir=tests"
|
49
28
|
)
|
29
|
+
when "ag"
|
30
|
+
super(n, op, "-Su --ignore=\"\/*test*\/\"")
|
31
|
+
when "pt"
|
32
|
+
super(n, op, "-SU --hidden --ignore=\"\/*test*\/\"")
|
50
33
|
else
|
51
|
-
|
52
|
-
nil,
|
53
|
-
"--color=always -EHinR",
|
54
|
-
"",
|
55
|
-
@passwd_regex
|
56
|
-
)
|
34
|
+
super(n, op, "-ai --exclude-dir=test --exclude-dir=tests")
|
57
35
|
end
|
58
36
|
|
59
|
-
|
60
|
-
@profile.operator,
|
61
|
-
@profile.flags,
|
62
|
-
@profile.prepend,
|
63
|
-
@profile.append
|
64
|
-
)
|
65
|
-
@immutable = true
|
37
|
+
@pattern = "pass(word|wd)?[^:=,>]? *[:=,>]"
|
66
38
|
@taggable = true
|
67
39
|
end
|
68
|
-
|
69
|
-
def to_s
|
70
|
-
return @porfile.to_s
|
71
|
-
end
|
72
40
|
end
|
data/lib/zoom/profile/pt.rb
CHANGED
@@ -1,30 +1,6 @@
|
|
1
|
-
require "shellwords"
|
2
|
-
require "zoom/profile"
|
3
|
-
|
4
1
|
class Zoom::Profile::Pt < Zoom::Profile
|
5
|
-
def
|
6
|
-
|
7
|
-
if (pattern.nil? || pattern.empty?)
|
8
|
-
system(
|
9
|
-
"#{self.to_s} #{args} #{self.append} | " \
|
10
|
-
"sed \"s|\\[K[:-]|\\[K\\n|\" | #{@pager}"
|
11
|
-
)
|
12
|
-
else
|
13
|
-
system(
|
14
|
-
"#{self.to_s} #{args} #{pattern.shellescape} " \
|
15
|
-
"#{self.append} | sed \"s|\\[K[:-]|\\[K\\n|\" | " \
|
16
|
-
"#{@pager}"
|
17
|
-
)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def initialize(
|
22
|
-
operator = nil,
|
23
|
-
flags = "--color -Se",
|
24
|
-
envprepend = "",
|
25
|
-
append = ""
|
26
|
-
)
|
27
|
-
super("pt", flags, envprepend, append)
|
2
|
+
def initialize(n, o = "pt", f = "-S", b = "", a = "")
|
3
|
+
super(n, o, f, b, a)
|
28
4
|
@taggable = true
|
29
5
|
end
|
30
6
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require "scoobydoo"
|
3
|
+
|
4
|
+
# Load custom profiles
|
5
|
+
config_dir = Pathname.new("~/.config/zoom").expand_path
|
6
|
+
if (config_dir.exist?)
|
7
|
+
Dir["#{config_dir}/*.rb"].each do |file|
|
8
|
+
require_relative file
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Zoom::ProfileManager
|
13
|
+
@@ranking = [
|
14
|
+
["ag", "Zoom::Profile::Ag", "-Su"],
|
15
|
+
["pt", "Zoom::Profile::Pt", "-SU --hidden"],
|
16
|
+
["ack", "Zoom::Profile::Ack", ""],
|
17
|
+
["ack-grep", "Zoom::Profile::Ack", ""],
|
18
|
+
["grep", "Zoom::Profile::Grep", "-ai"],
|
19
|
+
["find", "Zoom::Profile::Find", ""]
|
20
|
+
]
|
21
|
+
|
22
|
+
def self.default_profile
|
23
|
+
@@ranking.each do |op, clas, all|
|
24
|
+
return op if (ScoobyDoo.where_are_you(op))
|
25
|
+
end
|
26
|
+
return nil # shouldn't happen
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.default_profiles
|
30
|
+
profiles = Hash.new
|
31
|
+
|
32
|
+
@@ranking.each do |op, clas, all|
|
33
|
+
if (ScoobyDoo.where_are_you(op))
|
34
|
+
name = op.gsub("-grep", "")
|
35
|
+
obj = Zoom::Profile.profile_by_name(clas)
|
36
|
+
profiles[name] = obj.new(name)
|
37
|
+
if (!all.empty?)
|
38
|
+
profiles["all"] ||= obj.new("all", name, all)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
profiles["passwords"] = Zoom::Profile::Passwords.new(
|
44
|
+
"passwords"
|
45
|
+
)
|
46
|
+
|
47
|
+
return profiles
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "djinni"
|
2
|
+
|
3
|
+
class AddWish < Djinni::Wish
|
4
|
+
def aliases
|
5
|
+
return ["add", "new"]
|
6
|
+
end
|
7
|
+
|
8
|
+
def description
|
9
|
+
return "Create a new profile"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(args, djinni_env = {})
|
13
|
+
if (args.split(" ").length != 2)
|
14
|
+
usage
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
18
|
+
config = djinni_env["config"]
|
19
|
+
c, n = args.split(" ")
|
20
|
+
|
21
|
+
if (config.has_profile?(n))
|
22
|
+
puts "Profile already exists: #{n}"
|
23
|
+
elsif (!@classes.has_key?(c))
|
24
|
+
puts "Class does not exist: #{c}"
|
25
|
+
else
|
26
|
+
profiles = config.get_profiles
|
27
|
+
profiles[n] = Zoom::Profile.profile_by_name(c).new(n)
|
28
|
+
config.set_profiles(profiles)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
@classes = Hash.new
|
34
|
+
[Zoom::Profile].concat(Zoom::Profile.subclasses).each do |c|
|
35
|
+
@classes[c.to_s] = c.new(c.to_s).to_s.split("\n")[1].strip
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def tab_complete(input, djinni_env = {})
|
40
|
+
return [{}, "", ""] if (input.include?(" "))
|
41
|
+
|
42
|
+
completions = @classes.select do |clas, desc|
|
43
|
+
clas.downcase.start_with?(input.downcase)
|
44
|
+
end
|
45
|
+
|
46
|
+
return [completions, input, " "]
|
47
|
+
end
|
48
|
+
|
49
|
+
def usage
|
50
|
+
puts "#{aliases.join(", ")} <class> <name>"
|
51
|
+
puts " #{description}."
|
52
|
+
puts
|
53
|
+
puts "CLASSES"
|
54
|
+
@classes.each do |clas, desc|
|
55
|
+
puts " #{clas}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require "djinni"
|
2
|
+
|
3
|
+
class ColorWish < Djinni::Wish
|
4
|
+
def aliases
|
5
|
+
return ["color"]
|
6
|
+
end
|
7
|
+
|
8
|
+
def description
|
9
|
+
return "Configure colors"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(args, djinni_env = {})
|
13
|
+
f, found, c = args.rpartition(" ")
|
14
|
+
if (f.include?(" "))
|
15
|
+
usage
|
16
|
+
return
|
17
|
+
end
|
18
|
+
|
19
|
+
config = djinni_env["config"]
|
20
|
+
|
21
|
+
if (found.empty?)
|
22
|
+
f = c
|
23
|
+
|
24
|
+
case f
|
25
|
+
when "off"
|
26
|
+
config.hilight(false)
|
27
|
+
when "on"
|
28
|
+
config.hilight(true)
|
29
|
+
else
|
30
|
+
usage
|
31
|
+
end
|
32
|
+
|
33
|
+
return
|
34
|
+
end
|
35
|
+
|
36
|
+
case f
|
37
|
+
when "off", "on"
|
38
|
+
usage
|
39
|
+
else
|
40
|
+
c.split(".").each do |color|
|
41
|
+
if (!@colors.include?(color))
|
42
|
+
puts "Invalid color: #{color}"
|
43
|
+
return
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
config.send("color_#{f}", c)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize
|
52
|
+
@colors = [
|
53
|
+
"black",
|
54
|
+
"blue",
|
55
|
+
"cyan",
|
56
|
+
"default",
|
57
|
+
"green",
|
58
|
+
"magenta",
|
59
|
+
"red",
|
60
|
+
"white",
|
61
|
+
"yellow",
|
62
|
+
"light_black",
|
63
|
+
"light_blue",
|
64
|
+
"light_cyan",
|
65
|
+
"light_green",
|
66
|
+
"light_magenta",
|
67
|
+
"light_red",
|
68
|
+
"light_white",
|
69
|
+
"light_yellow",
|
70
|
+
"on_black",
|
71
|
+
"on_blue",
|
72
|
+
"on_cyan",
|
73
|
+
"on_green",
|
74
|
+
"on_magenta",
|
75
|
+
"on_red",
|
76
|
+
"on_white",
|
77
|
+
"on_yellow",
|
78
|
+
"on_light_black",
|
79
|
+
"on_light_blue",
|
80
|
+
"on_light_cyan",
|
81
|
+
"on_light_green",
|
82
|
+
"on_light_magenta",
|
83
|
+
"on_light_red",
|
84
|
+
"on_light_white",
|
85
|
+
"on_light_yellow"
|
86
|
+
]
|
87
|
+
@fields = {
|
88
|
+
"filename" => "Configure filename color (default: green)",
|
89
|
+
"lineno" =>
|
90
|
+
"Configure line number color (default: white)",
|
91
|
+
"match" =>
|
92
|
+
"Configure match color (default: black.on_white)",
|
93
|
+
"off" => "Turn off colorized output",
|
94
|
+
"on" => "Turn on colorized output (default)",
|
95
|
+
"tag" => "Configure tag color (default: red)"
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
def tab_complete(input, djinni_env = {})
|
100
|
+
f, found, c = input.rpartition(" ")
|
101
|
+
|
102
|
+
return [{}, "", ""] if (f.include?(" "))
|
103
|
+
|
104
|
+
if (found.empty?)
|
105
|
+
f = c
|
106
|
+
|
107
|
+
completions = @fields.select do |field, desc|
|
108
|
+
field.downcase.start_with?(f.downcase)
|
109
|
+
end
|
110
|
+
|
111
|
+
append = " "
|
112
|
+
append = "" if (f.start_with?("o"))
|
113
|
+
return [completions, f, append]
|
114
|
+
else
|
115
|
+
case f
|
116
|
+
when "off", "on"
|
117
|
+
return [{}, "", ""]
|
118
|
+
else
|
119
|
+
last = c.rpartition(".")[-1]
|
120
|
+
|
121
|
+
completions = Hash.new
|
122
|
+
@colors.select do |color|
|
123
|
+
color.downcase.start_with?(last.downcase)
|
124
|
+
end.each do |color|
|
125
|
+
completions[color] = ""
|
126
|
+
end
|
127
|
+
|
128
|
+
return [completions, last, ""]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def usage
|
134
|
+
puts "#{aliases.join(", ")} <field> [color]"
|
135
|
+
puts " #{description}."
|
136
|
+
puts
|
137
|
+
puts "FIELDS"
|
138
|
+
@fields.each do |field, desc|
|
139
|
+
puts " #{field}"
|
140
|
+
end
|
141
|
+
puts
|
142
|
+
puts "COLORS"
|
143
|
+
@colors.each do |color|
|
144
|
+
puts " #{color}"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|