theme-juice 0.7.7 → 0.7.8
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.
- checksums.yaml +4 -4
- data/README.md +204 -204
- data/bin/tj +15 -15
- data/lib/theme-juice.rb +46 -46
- data/lib/theme-juice/cli.rb +248 -248
- data/lib/theme-juice/command.rb +20 -20
- data/lib/theme-juice/commands/create.rb +221 -221
- data/lib/theme-juice/commands/delete.rb +51 -51
- data/lib/theme-juice/commands/deploy.rb +20 -20
- data/lib/theme-juice/config.rb +68 -70
- data/lib/theme-juice/env.rb +25 -25
- data/lib/theme-juice/io.rb +323 -323
- data/lib/theme-juice/project.rb +35 -35
- data/lib/theme-juice/task.rb +42 -42
- data/lib/theme-juice/tasks/create_confirm.rb +33 -33
- data/lib/theme-juice/tasks/create_success.rb +42 -42
- data/lib/theme-juice/tasks/database.rb +50 -50
- data/lib/theme-juice/tasks/delete_confirm.rb +24 -24
- data/lib/theme-juice/tasks/delete_success.rb +31 -31
- data/lib/theme-juice/tasks/dns.rb +45 -45
- data/lib/theme-juice/tasks/dot_env.rb +53 -53
- data/lib/theme-juice/tasks/entry.rb +50 -50
- data/lib/theme-juice/tasks/hosts.rb +43 -43
- data/lib/theme-juice/tasks/import_database.rb +28 -28
- data/lib/theme-juice/tasks/landrush.rb +33 -33
- data/lib/theme-juice/tasks/list.rb +50 -50
- data/lib/theme-juice/tasks/location.rb +23 -23
- data/lib/theme-juice/tasks/nginx.rb +51 -51
- data/lib/theme-juice/tasks/repo.rb +49 -49
- data/lib/theme-juice/tasks/synced_folder.rb +30 -30
- data/lib/theme-juice/tasks/theme.rb +34 -34
- data/lib/theme-juice/tasks/vm.rb +30 -30
- data/lib/theme-juice/tasks/vm_customfile.rb +42 -42
- data/lib/theme-juice/tasks/vm_location.rb +32 -32
- data/lib/theme-juice/tasks/vm_plugins.rb +32 -32
- data/lib/theme-juice/tasks/vm_provision.rb +39 -39
- data/lib/theme-juice/tasks/vm_restart.rb +25 -25
- data/lib/theme-juice/tasks/wp_cli.rb +52 -52
- data/lib/theme-juice/util.rb +45 -45
- data/lib/theme-juice/version.rb +5 -5
- metadata +6 -7
@@ -1,51 +1,51 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
module ThemeJuice
|
4
|
-
module Commands
|
5
|
-
class Delete < Command
|
6
|
-
|
7
|
-
def initialize(opts = {})
|
8
|
-
super
|
9
|
-
|
10
|
-
@project.name = @opts.fetch("name") { name }
|
11
|
-
@project.url = @opts.fetch("url") { url }
|
12
|
-
@project.db_drop = @opts.fetch("db_drop", false)
|
13
|
-
@project.vm_restart = @opts.fetch("vm_restart", false)
|
14
|
-
@project.vm_root = vm_root
|
15
|
-
@project.vm_location = vm_location
|
16
|
-
@project.vm_srv = vm_srv
|
17
|
-
|
18
|
-
runner do |tasks|
|
19
|
-
tasks << Tasks::DeleteConfirm.new
|
20
|
-
tasks << Tasks::Database.new
|
21
|
-
tasks << Tasks::VMLocation.new
|
22
|
-
tasks << Tasks::SyncedFolder.new
|
23
|
-
tasks << Tasks::DNS.new
|
24
|
-
tasks << Tasks::DeleteSuccess.new
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
def name
|
31
|
-
name = @io.prompt "What's the project name?"
|
32
|
-
|
33
|
-
unless @list.projects.include? name
|
34
|
-
@io.error "Project '#{name}' doesn't exist"
|
35
|
-
end
|
36
|
-
|
37
|
-
name
|
38
|
-
end
|
39
|
-
|
40
|
-
def url
|
41
|
-
url = @io.prompt "What is the project's development url?", :default => "#{@project.name}.dev"
|
42
|
-
|
43
|
-
unless @list.urls.include? url
|
44
|
-
@io.notice "Project url '#{url}' doesn't exist within DNS records. Skipping..."
|
45
|
-
end
|
46
|
-
|
47
|
-
url
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module ThemeJuice
|
4
|
+
module Commands
|
5
|
+
class Delete < Command
|
6
|
+
|
7
|
+
def initialize(opts = {})
|
8
|
+
super
|
9
|
+
|
10
|
+
@project.name = @opts.fetch("name") { name }
|
11
|
+
@project.url = @opts.fetch("url") { url }
|
12
|
+
@project.db_drop = @opts.fetch("db_drop", false)
|
13
|
+
@project.vm_restart = @opts.fetch("vm_restart", false)
|
14
|
+
@project.vm_root = vm_root
|
15
|
+
@project.vm_location = vm_location
|
16
|
+
@project.vm_srv = vm_srv
|
17
|
+
|
18
|
+
runner do |tasks|
|
19
|
+
tasks << Tasks::DeleteConfirm.new
|
20
|
+
tasks << Tasks::Database.new
|
21
|
+
tasks << Tasks::VMLocation.new
|
22
|
+
tasks << Tasks::SyncedFolder.new
|
23
|
+
tasks << Tasks::DNS.new
|
24
|
+
tasks << Tasks::DeleteSuccess.new
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def name
|
31
|
+
name = @io.prompt "What's the project name?"
|
32
|
+
|
33
|
+
unless @list.projects.include? name
|
34
|
+
@io.error "Project '#{name}' doesn't exist"
|
35
|
+
end
|
36
|
+
|
37
|
+
name
|
38
|
+
end
|
39
|
+
|
40
|
+
def url
|
41
|
+
url = @io.prompt "What is the project's development url?", :default => "#{@project.name}.dev"
|
42
|
+
|
43
|
+
unless @list.urls.include? url
|
44
|
+
@io.notice "Project url '#{url}' doesn't exist within DNS records. Skipping..."
|
45
|
+
end
|
46
|
+
|
47
|
+
url
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -1,20 +1,20 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
module ThemeJuice
|
4
|
-
module Commands
|
5
|
-
class Deploy < Command
|
6
|
-
|
7
|
-
def initialize(opts = {})
|
8
|
-
super
|
9
|
-
|
10
|
-
@project.vm_root = vm_root
|
11
|
-
@project.vm_location = vm_location
|
12
|
-
@project.vm_srv = vm_srv
|
13
|
-
|
14
|
-
runner do |tasks|
|
15
|
-
@io.error "Not implemented"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module ThemeJuice
|
4
|
+
module Commands
|
5
|
+
class Deploy < Command
|
6
|
+
|
7
|
+
def initialize(opts = {})
|
8
|
+
super
|
9
|
+
|
10
|
+
@project.vm_root = vm_root
|
11
|
+
@project.vm_location = vm_location
|
12
|
+
@project.vm_srv = vm_srv
|
13
|
+
|
14
|
+
runner do |tasks|
|
15
|
+
@io.error "Not implemented"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/theme-juice/config.rb
CHANGED
@@ -1,70 +1,68 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
module ThemeJuice
|
4
|
-
module Config
|
5
|
-
@env = Env
|
6
|
-
@io = IO
|
7
|
-
@project = Project
|
8
|
-
@util = Util.new
|
9
|
-
|
10
|
-
def method_missing(method, *args, &block)
|
11
|
-
@project.location ||= Dir.pwd
|
12
|
-
|
13
|
-
begin
|
14
|
-
config.fetch("commands", {})
|
15
|
-
.fetch("#{method}") { @io.error("Command '#{method}' not found in config") }
|
16
|
-
.each { |cmd| run format_command(cmd, *args) }
|
17
|
-
rescue ::NoMethodError => err
|
18
|
-
@io.error "Config file is invalid" do
|
19
|
-
puts err
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def run(command)
|
27
|
-
@util.inside @project.location do
|
28
|
-
@util.run command, :verbose => @env.verbose
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def format_command(cmd, args = [])
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
70
|
-
end
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module ThemeJuice
|
4
|
+
module Config
|
5
|
+
@env = Env
|
6
|
+
@io = IO
|
7
|
+
@project = Project
|
8
|
+
@util = Util.new
|
9
|
+
|
10
|
+
def method_missing(method, *args, &block)
|
11
|
+
@project.location ||= Dir.pwd
|
12
|
+
|
13
|
+
begin
|
14
|
+
config.fetch("commands", {})
|
15
|
+
.fetch("#{method}") { @io.error("Command '#{method}' not found in config") }
|
16
|
+
.each { |cmd| run format_command(cmd, *args) }
|
17
|
+
rescue ::NoMethodError => err
|
18
|
+
@io.error "Config file is invalid" do
|
19
|
+
puts err
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def run(command)
|
27
|
+
@util.inside @project.location do
|
28
|
+
@util.run command, :verbose => @env.verbose
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def format_command(cmd, args = [])
|
33
|
+
if multi_arg_regex =~ cmd
|
34
|
+
cmd.gsub! multi_arg_regex, args.join(" ")
|
35
|
+
else
|
36
|
+
args.to_enum.with_index(1).each do |arg, i|
|
37
|
+
cmd.gsub! single_arg_regex(i), arg
|
38
|
+
end
|
39
|
+
end
|
40
|
+
cmd
|
41
|
+
end
|
42
|
+
|
43
|
+
def config
|
44
|
+
begin
|
45
|
+
YAML.load_file Dir["#{@project.location}/*"].select { |f| config_regex =~ File.basename(f) }.last ||
|
46
|
+
@io.error("Config file not found in '#{@project.location}'")
|
47
|
+
rescue ::Psych::SyntaxError => err
|
48
|
+
@io.error "Config file contains invalid YAML" do
|
49
|
+
puts err
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def config_regex
|
55
|
+
%r{^(((\.)?(tj)|((J|j)uicefile))(.y(a)?ml)?$)}
|
56
|
+
end
|
57
|
+
|
58
|
+
def multi_arg_regex
|
59
|
+
%r{(%args%)|(%arguments%)}
|
60
|
+
end
|
61
|
+
|
62
|
+
def single_arg_regex(i)
|
63
|
+
%r{(%arg#{i}%)|(%argument#{i}%)}
|
64
|
+
end
|
65
|
+
|
66
|
+
extend self
|
67
|
+
end
|
68
|
+
end
|
data/lib/theme-juice/env.rb
CHANGED
@@ -1,25 +1,25 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
module ThemeJuice
|
4
|
-
module Env
|
5
|
-
attr_accessor :vm_path
|
6
|
-
attr_accessor :vm_ip
|
7
|
-
attr_accessor :yolo
|
8
|
-
attr_accessor :boring
|
9
|
-
attr_accessor :no_unicode
|
10
|
-
attr_accessor :no_colors
|
11
|
-
attr_accessor :no_animations
|
12
|
-
attr_accessor :no_landrush
|
13
|
-
attr_accessor :vm_prefix
|
14
|
-
attr_accessor :verbose
|
15
|
-
attr_accessor :dryrun
|
16
|
-
|
17
|
-
def inspect
|
18
|
-
res = []
|
19
|
-
self.instance_variables.each { |k, _| res << "#{k[1..-1]}: #{instance_variable_get(k)}" }
|
20
|
-
res.sort
|
21
|
-
end
|
22
|
-
|
23
|
-
extend self
|
24
|
-
end
|
25
|
-
end
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module ThemeJuice
|
4
|
+
module Env
|
5
|
+
attr_accessor :vm_path
|
6
|
+
attr_accessor :vm_ip
|
7
|
+
attr_accessor :yolo
|
8
|
+
attr_accessor :boring
|
9
|
+
attr_accessor :no_unicode
|
10
|
+
attr_accessor :no_colors
|
11
|
+
attr_accessor :no_animations
|
12
|
+
attr_accessor :no_landrush
|
13
|
+
attr_accessor :vm_prefix
|
14
|
+
attr_accessor :verbose
|
15
|
+
attr_accessor :dryrun
|
16
|
+
|
17
|
+
def inspect
|
18
|
+
res = []
|
19
|
+
self.instance_variables.each { |k, _| res << "#{k[1..-1]}: #{instance_variable_get(k)}" }
|
20
|
+
res.sort
|
21
|
+
end
|
22
|
+
|
23
|
+
extend self
|
24
|
+
end
|
25
|
+
end
|
data/lib/theme-juice/io.rb
CHANGED
@@ -1,323 +1,323 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
module ThemeJuice
|
4
|
-
module IO
|
5
|
-
include Thor::Shell
|
6
|
-
|
7
|
-
ICONS = {
|
8
|
-
:success => "✓", # "\u2713",
|
9
|
-
:error => "↑", # "\u2191",
|
10
|
-
:notice => "→", # "\u2192",
|
11
|
-
:question => "•", # "\u2022",
|
12
|
-
:general => "›", # "\u203A",
|
13
|
-
:log => "…", # "\u2026",
|
14
|
-
:restart => "↪", # "\u21AA",
|
15
|
-
:selected => "•", # "\u2022",
|
16
|
-
:unselected => "○", # "\u25CB",
|
17
|
-
:fallback_success => "+",
|
18
|
-
:fallback_error => "!",
|
19
|
-
:fallback_notice => "!",
|
20
|
-
:fallback_question => "?",
|
21
|
-
:fallback_general => "-",
|
22
|
-
:fallback_log => "...",
|
23
|
-
:fallback_restart => "!",
|
24
|
-
:fallback_selected => "[x]",
|
25
|
-
:fallback_unselected => "[ ]",
|
26
|
-
}
|
27
|
-
|
28
|
-
KEYS = {
|
29
|
-
"\e[A" => "up",
|
30
|
-
"\e[B" => "down",
|
31
|
-
"\e[C" => "right",
|
32
|
-
"\e[D" => "left",
|
33
|
-
"\003" => "ctrl+c",
|
34
|
-
"\004" => "ctrl+d",
|
35
|
-
"\e" => "escape",
|
36
|
-
"\n" => "linefeed",
|
37
|
-
"\r" => "return",
|
38
|
-
" " => "space",
|
39
|
-
}
|
40
|
-
|
41
|
-
@state = nil
|
42
|
-
@env = Env
|
43
|
-
|
44
|
-
def speak(message, opts = {})
|
45
|
-
format_message message, opts
|
46
|
-
output_message
|
47
|
-
end
|
48
|
-
|
49
|
-
def prompt(question, *opts)
|
50
|
-
format_message question, {
|
51
|
-
:color => :blue,
|
52
|
-
:icon => :question
|
53
|
-
}
|
54
|
-
|
55
|
-
opts.each do |opt|
|
56
|
-
|
57
|
-
# if opt[:default]
|
58
|
-
# opt[:default] = set_color(opt[:default], :black, :bold) unless @env.no_colors
|
59
|
-
# end
|
60
|
-
|
61
|
-
if opt[:indent]
|
62
|
-
with(question) { |str| (" " * opt[:indent]) << str }
|
63
|
-
end
|
64
|
-
|
65
|
-
break
|
66
|
-
end
|
67
|
-
|
68
|
-
ask("#{question} :", *opts).gsub /\e\[\d+m/, ""
|
69
|
-
end
|
70
|
-
|
71
|
-
def agree?(question, opts = {})
|
72
|
-
format_message question, {
|
73
|
-
:color => opts.fetch("color", :blue),
|
74
|
-
:icon => :question
|
75
|
-
}
|
76
|
-
|
77
|
-
if opts[:simple]
|
78
|
-
yes? " :", opts.fetch("color", {})
|
79
|
-
else
|
80
|
-
yes? "#{question} (y/N) :"
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def log(message)
|
85
|
-
speak message, {
|
86
|
-
:color => :yellow,
|
87
|
-
:icon => :log
|
88
|
-
}
|
89
|
-
end
|
90
|
-
|
91
|
-
def success(message)
|
92
|
-
speak message, {
|
93
|
-
:color => [:black, :on_green, :bold],
|
94
|
-
:icon => :success,
|
95
|
-
:row => true
|
96
|
-
}
|
97
|
-
end
|
98
|
-
|
99
|
-
def notice(message)
|
100
|
-
speak message, {
|
101
|
-
:color => [:black, :on_yellow],
|
102
|
-
:icon => :notice,
|
103
|
-
:row => true
|
104
|
-
}
|
105
|
-
end
|
106
|
-
|
107
|
-
def error(message)
|
108
|
-
speak message, {
|
109
|
-
:color => [:white, :on_red],
|
110
|
-
:icon => :error,
|
111
|
-
:row => true
|
112
|
-
}
|
113
|
-
|
114
|
-
yield if block_given?
|
115
|
-
|
116
|
-
exit 1
|
117
|
-
end
|
118
|
-
|
119
|
-
def hello(opts = {})
|
120
|
-
speak "Welcome to Theme Juice!", {
|
121
|
-
:color => [:black, :on_green, :bold],
|
122
|
-
:row => true
|
123
|
-
}.merge(opts)
|
124
|
-
end
|
125
|
-
|
126
|
-
def goodbye(opts = {})
|
127
|
-
|
128
|
-
# Have some fun?
|
129
|
-
goodbyes = [
|
130
|
-
"Bye, bye, bye",
|
131
|
-
"Adios, muchachos",
|
132
|
-
"See ya later, alligator",
|
133
|
-
"Peace...",
|
134
|
-
"Later, homes",
|
135
|
-
"I'll be back",
|
136
|
-
"Victory is ours!",
|
137
|
-
"May the force be with you",
|
138
|
-
"Take a break, man...",
|
139
|
-
"It's not me, it's you",
|
140
|
-
"Go home, developer, you're drunk",
|
141
|
-
"Okay, this is getting a little out of hand...",
|
142
|
-
"I don't like it when you press my buttons",
|
143
|
-
"Ouch!",
|
144
|
-
":(",
|
145
|
-
]
|
146
|
-
|
147
|
-
speak goodbyes.sample, {
|
148
|
-
:color => :yellow,
|
149
|
-
:newline => true
|
150
|
-
}.merge(opts)
|
151
|
-
|
152
|
-
exit 130
|
153
|
-
end
|
154
|
-
|
155
|
-
def open_project(url)
|
156
|
-
speak "Do you want to open up your new project at 'http://#{url}' now? (y/N)", {
|
157
|
-
:color => [:black, :on_blue],
|
158
|
-
:icon => :restart,
|
159
|
-
:row => true
|
160
|
-
}
|
161
|
-
|
162
|
-
if agree? "", { :simple => true }
|
163
|
-
OS.open_file_command "http://#{url}"
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
def list(header, color, list)
|
168
|
-
speak header, {
|
169
|
-
:color => [:black, :"on_#{color}"],
|
170
|
-
:icon => :notice,
|
171
|
-
:row => true
|
172
|
-
}
|
173
|
-
|
174
|
-
list.each do |item|
|
175
|
-
speak item, {
|
176
|
-
:color => :"#{color}",
|
177
|
-
:icon => :general
|
178
|
-
}
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
def choose(header, color, list)
|
183
|
-
if OS.windows?
|
184
|
-
ask header, {
|
185
|
-
:limited_to => list,
|
186
|
-
:color => color
|
187
|
-
}
|
188
|
-
else
|
189
|
-
speak "#{header} (use arrow keys and press enter)", {
|
190
|
-
:color => :"#{color}",
|
191
|
-
:icon => :question
|
192
|
-
}
|
193
|
-
|
194
|
-
print "\n" * list.size
|
195
|
-
|
196
|
-
selected = 0
|
197
|
-
update_list_selection(list, color, selected)
|
198
|
-
|
199
|
-
loop do
|
200
|
-
key = read_key
|
201
|
-
case key
|
202
|
-
when "up"
|
203
|
-
selected -= 1
|
204
|
-
selected = list.size - 1 if selected < 0
|
205
|
-
update_list_selection(list, color, selected)
|
206
|
-
when "down"
|
207
|
-
selected += 1
|
208
|
-
selected = 0 if selected > list.size - 1
|
209
|
-
update_list_selection(list, color, selected)
|
210
|
-
when "return", "linefeed", "space"
|
211
|
-
return list[selected]
|
212
|
-
when "esc", "ctrl+c"
|
213
|
-
goodbye(:newline => false)
|
214
|
-
# else
|
215
|
-
# speak key.inspect, { :color => :yellow }
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
private
|
222
|
-
|
223
|
-
def update_list_selection(list, color, selected = 0)
|
224
|
-
print "\e[#{list.size}A"
|
225
|
-
|
226
|
-
list.each_with_index do |item, i|
|
227
|
-
icon = i == selected ? "selected" : "unselected"
|
228
|
-
speak "#{item}", {
|
229
|
-
:color => :"#{color}",
|
230
|
-
:icon => :"#{icon}",
|
231
|
-
:indent => 2
|
232
|
-
}
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
#
|
237
|
-
# @see http://www.alecjacobson.com/weblog/?p=75
|
238
|
-
#
|
239
|
-
def read_key
|
240
|
-
save_stty_state
|
241
|
-
raw_stty_mode
|
242
|
-
|
243
|
-
key = STDIN.getc.chr
|
244
|
-
|
245
|
-
if key == "\e"
|
246
|
-
thread = Thread.new { key += STDIN.getc.chr + STDIN.getc.chr }
|
247
|
-
thread.join(0.001)
|
248
|
-
thread.kill
|
249
|
-
end
|
250
|
-
|
251
|
-
KEYS[key] || key
|
252
|
-
ensure
|
253
|
-
restore_stty_state
|
254
|
-
end
|
255
|
-
|
256
|
-
def save_stty_state
|
257
|
-
@state = %x(stty -g)
|
258
|
-
end
|
259
|
-
|
260
|
-
def raw_stty_mode
|
261
|
-
%x(stty raw -echo)
|
262
|
-
end
|
263
|
-
|
264
|
-
def restore_stty_state
|
265
|
-
%x(stty #{@state})
|
266
|
-
end
|
267
|
-
|
268
|
-
def format_message(message, opts = {})
|
269
|
-
@message, @opts = message, opts
|
270
|
-
|
271
|
-
format_message_icon
|
272
|
-
format_message_newline
|
273
|
-
format_message_row
|
274
|
-
format_message_width
|
275
|
-
format_message_color
|
276
|
-
format_message_indent
|
277
|
-
|
278
|
-
@message
|
279
|
-
end
|
280
|
-
|
281
|
-
def with(string)
|
282
|
-
str = yield(string); string.clear; string << str
|
283
|
-
end
|
284
|
-
|
285
|
-
def format_message_icon
|
286
|
-
icon = @env.no_unicode ? "fallback_#{@opts[:icon]}" : "#{@opts[:icon]}"
|
287
|
-
|
288
|
-
if @opts[:icon]
|
289
|
-
with(@message) { |msg| "#{ICONS[:"#{icon}"]}" << (@opts[:empty] ? nil : " #{msg}") }
|
290
|
-
else
|
291
|
-
with(@message) { |msg| "" << msg }
|
292
|
-
end
|
293
|
-
end
|
294
|
-
|
295
|
-
def format_message_newline
|
296
|
-
with(@message) { |msg| "\n" << msg } if @opts[:newline]
|
297
|
-
end
|
298
|
-
|
299
|
-
def format_message_color
|
300
|
-
unless @env.no_colors
|
301
|
-
with(@message) { |msg| set_color(msg, *@opts[:color]) } if @opts[:color]
|
302
|
-
end
|
303
|
-
end
|
304
|
-
|
305
|
-
def format_message_row
|
306
|
-
with(@message) { |msg| msg.ljust(terminal_width) } if @opts[:row]
|
307
|
-
end
|
308
|
-
|
309
|
-
def format_message_width
|
310
|
-
with(@message) { |msg| msg.ljust(@opts[:width]) } if @opts[:width]
|
311
|
-
end
|
312
|
-
|
313
|
-
def format_message_indent
|
314
|
-
with(@message) { |str| (" " * @opts[:indent]) << str } if @opts[:indent]
|
315
|
-
end
|
316
|
-
|
317
|
-
def output_message
|
318
|
-
@opts[:quiet] ? @message : say(@message)
|
319
|
-
end
|
320
|
-
|
321
|
-
extend self
|
322
|
-
end
|
323
|
-
end
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module ThemeJuice
|
4
|
+
module IO
|
5
|
+
include Thor::Shell
|
6
|
+
|
7
|
+
ICONS = {
|
8
|
+
:success => "✓", # "\u2713",
|
9
|
+
:error => "↑", # "\u2191",
|
10
|
+
:notice => "→", # "\u2192",
|
11
|
+
:question => "•", # "\u2022",
|
12
|
+
:general => "›", # "\u203A",
|
13
|
+
:log => "…", # "\u2026",
|
14
|
+
:restart => "↪", # "\u21AA",
|
15
|
+
:selected => "•", # "\u2022",
|
16
|
+
:unselected => "○", # "\u25CB",
|
17
|
+
:fallback_success => "+",
|
18
|
+
:fallback_error => "!",
|
19
|
+
:fallback_notice => "!",
|
20
|
+
:fallback_question => "?",
|
21
|
+
:fallback_general => "-",
|
22
|
+
:fallback_log => "...",
|
23
|
+
:fallback_restart => "!",
|
24
|
+
:fallback_selected => "[x]",
|
25
|
+
:fallback_unselected => "[ ]",
|
26
|
+
}
|
27
|
+
|
28
|
+
KEYS = {
|
29
|
+
"\e[A" => "up",
|
30
|
+
"\e[B" => "down",
|
31
|
+
"\e[C" => "right",
|
32
|
+
"\e[D" => "left",
|
33
|
+
"\003" => "ctrl+c",
|
34
|
+
"\004" => "ctrl+d",
|
35
|
+
"\e" => "escape",
|
36
|
+
"\n" => "linefeed",
|
37
|
+
"\r" => "return",
|
38
|
+
" " => "space",
|
39
|
+
}
|
40
|
+
|
41
|
+
@state = nil
|
42
|
+
@env = Env
|
43
|
+
|
44
|
+
def speak(message, opts = {})
|
45
|
+
format_message message, opts
|
46
|
+
output_message
|
47
|
+
end
|
48
|
+
|
49
|
+
def prompt(question, *opts)
|
50
|
+
format_message question, {
|
51
|
+
:color => :blue,
|
52
|
+
:icon => :question
|
53
|
+
}
|
54
|
+
|
55
|
+
opts.each do |opt|
|
56
|
+
|
57
|
+
# if opt[:default]
|
58
|
+
# opt[:default] = set_color(opt[:default], :black, :bold) unless @env.no_colors
|
59
|
+
# end
|
60
|
+
|
61
|
+
if opt[:indent]
|
62
|
+
with(question) { |str| (" " * opt[:indent]) << str }
|
63
|
+
end
|
64
|
+
|
65
|
+
break
|
66
|
+
end
|
67
|
+
|
68
|
+
ask("#{question} :", *opts).gsub /\e\[\d+m/, ""
|
69
|
+
end
|
70
|
+
|
71
|
+
def agree?(question, opts = {})
|
72
|
+
format_message question, {
|
73
|
+
:color => opts.fetch("color", :blue),
|
74
|
+
:icon => :question
|
75
|
+
}
|
76
|
+
|
77
|
+
if opts[:simple]
|
78
|
+
yes? " :", opts.fetch("color", {})
|
79
|
+
else
|
80
|
+
yes? "#{question} (y/N) :"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def log(message)
|
85
|
+
speak message, {
|
86
|
+
:color => :yellow,
|
87
|
+
:icon => :log
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
def success(message)
|
92
|
+
speak message, {
|
93
|
+
:color => [:black, :on_green, :bold],
|
94
|
+
:icon => :success,
|
95
|
+
:row => true
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
def notice(message)
|
100
|
+
speak message, {
|
101
|
+
:color => [:black, :on_yellow],
|
102
|
+
:icon => :notice,
|
103
|
+
:row => true
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
def error(message)
|
108
|
+
speak message, {
|
109
|
+
:color => [:white, :on_red],
|
110
|
+
:icon => :error,
|
111
|
+
:row => true
|
112
|
+
}
|
113
|
+
|
114
|
+
yield if block_given?
|
115
|
+
|
116
|
+
exit 1
|
117
|
+
end
|
118
|
+
|
119
|
+
def hello(opts = {})
|
120
|
+
speak "Welcome to Theme Juice!", {
|
121
|
+
:color => [:black, :on_green, :bold],
|
122
|
+
:row => true
|
123
|
+
}.merge(opts)
|
124
|
+
end
|
125
|
+
|
126
|
+
def goodbye(opts = {})
|
127
|
+
|
128
|
+
# Have some fun?
|
129
|
+
goodbyes = [
|
130
|
+
"Bye, bye, bye",
|
131
|
+
"Adios, muchachos",
|
132
|
+
"See ya later, alligator",
|
133
|
+
"Peace...",
|
134
|
+
"Later, homes",
|
135
|
+
"I'll be back",
|
136
|
+
"Victory is ours!",
|
137
|
+
"May the force be with you",
|
138
|
+
"Take a break, man...",
|
139
|
+
"It's not me, it's you",
|
140
|
+
"Go home, developer, you're drunk",
|
141
|
+
"Okay, this is getting a little out of hand...",
|
142
|
+
"I don't like it when you press my buttons",
|
143
|
+
"Ouch!",
|
144
|
+
":(",
|
145
|
+
]
|
146
|
+
|
147
|
+
speak goodbyes.sample, {
|
148
|
+
:color => :yellow,
|
149
|
+
:newline => true
|
150
|
+
}.merge(opts)
|
151
|
+
|
152
|
+
exit 130
|
153
|
+
end
|
154
|
+
|
155
|
+
def open_project(url)
|
156
|
+
speak "Do you want to open up your new project at 'http://#{url}' now? (y/N)", {
|
157
|
+
:color => [:black, :on_blue],
|
158
|
+
:icon => :restart,
|
159
|
+
:row => true
|
160
|
+
}
|
161
|
+
|
162
|
+
if agree? "", { :simple => true }
|
163
|
+
OS.open_file_command "http://#{url}"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def list(header, color, list)
|
168
|
+
speak header, {
|
169
|
+
:color => [:black, :"on_#{color}"],
|
170
|
+
:icon => :notice,
|
171
|
+
:row => true
|
172
|
+
}
|
173
|
+
|
174
|
+
list.each do |item|
|
175
|
+
speak item, {
|
176
|
+
:color => :"#{color}",
|
177
|
+
:icon => :general
|
178
|
+
}
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def choose(header, color, list)
|
183
|
+
if OS.windows?
|
184
|
+
ask header, {
|
185
|
+
:limited_to => list,
|
186
|
+
:color => color
|
187
|
+
}
|
188
|
+
else
|
189
|
+
speak "#{header} (use arrow keys and press enter)", {
|
190
|
+
:color => :"#{color}",
|
191
|
+
:icon => :question
|
192
|
+
}
|
193
|
+
|
194
|
+
print "\n" * list.size
|
195
|
+
|
196
|
+
selected = 0
|
197
|
+
update_list_selection(list, color, selected)
|
198
|
+
|
199
|
+
loop do
|
200
|
+
key = read_key
|
201
|
+
case key
|
202
|
+
when "up"
|
203
|
+
selected -= 1
|
204
|
+
selected = list.size - 1 if selected < 0
|
205
|
+
update_list_selection(list, color, selected)
|
206
|
+
when "down"
|
207
|
+
selected += 1
|
208
|
+
selected = 0 if selected > list.size - 1
|
209
|
+
update_list_selection(list, color, selected)
|
210
|
+
when "return", "linefeed", "space"
|
211
|
+
return list[selected]
|
212
|
+
when "esc", "ctrl+c"
|
213
|
+
goodbye(:newline => false)
|
214
|
+
# else
|
215
|
+
# speak key.inspect, { :color => :yellow }
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
private
|
222
|
+
|
223
|
+
def update_list_selection(list, color, selected = 0)
|
224
|
+
print "\e[#{list.size}A"
|
225
|
+
|
226
|
+
list.each_with_index do |item, i|
|
227
|
+
icon = i == selected ? "selected" : "unselected"
|
228
|
+
speak "#{item}", {
|
229
|
+
:color => :"#{color}",
|
230
|
+
:icon => :"#{icon}",
|
231
|
+
:indent => 2
|
232
|
+
}
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
#
|
237
|
+
# @see http://www.alecjacobson.com/weblog/?p=75
|
238
|
+
#
|
239
|
+
def read_key
|
240
|
+
save_stty_state
|
241
|
+
raw_stty_mode
|
242
|
+
|
243
|
+
key = STDIN.getc.chr
|
244
|
+
|
245
|
+
if key == "\e"
|
246
|
+
thread = Thread.new { key += STDIN.getc.chr + STDIN.getc.chr }
|
247
|
+
thread.join(0.001)
|
248
|
+
thread.kill
|
249
|
+
end
|
250
|
+
|
251
|
+
KEYS[key] || key
|
252
|
+
ensure
|
253
|
+
restore_stty_state
|
254
|
+
end
|
255
|
+
|
256
|
+
def save_stty_state
|
257
|
+
@state = %x(stty -g)
|
258
|
+
end
|
259
|
+
|
260
|
+
def raw_stty_mode
|
261
|
+
%x(stty raw -echo)
|
262
|
+
end
|
263
|
+
|
264
|
+
def restore_stty_state
|
265
|
+
%x(stty #{@state})
|
266
|
+
end
|
267
|
+
|
268
|
+
def format_message(message, opts = {})
|
269
|
+
@message, @opts = message, opts
|
270
|
+
|
271
|
+
format_message_icon
|
272
|
+
format_message_newline
|
273
|
+
format_message_row
|
274
|
+
format_message_width
|
275
|
+
format_message_color
|
276
|
+
format_message_indent
|
277
|
+
|
278
|
+
@message
|
279
|
+
end
|
280
|
+
|
281
|
+
def with(string)
|
282
|
+
str = yield(string); string.clear; string << str
|
283
|
+
end
|
284
|
+
|
285
|
+
def format_message_icon
|
286
|
+
icon = @env.no_unicode ? "fallback_#{@opts[:icon]}" : "#{@opts[:icon]}"
|
287
|
+
|
288
|
+
if @opts[:icon]
|
289
|
+
with(@message) { |msg| "#{ICONS[:"#{icon}"]}" << (@opts[:empty] ? nil : " #{msg}") }
|
290
|
+
else
|
291
|
+
with(@message) { |msg| "" << msg }
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
def format_message_newline
|
296
|
+
with(@message) { |msg| "\n" << msg } if @opts[:newline]
|
297
|
+
end
|
298
|
+
|
299
|
+
def format_message_color
|
300
|
+
unless @env.no_colors
|
301
|
+
with(@message) { |msg| set_color(msg, *@opts[:color]) } if @opts[:color]
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
def format_message_row
|
306
|
+
with(@message) { |msg| msg.ljust(terminal_width) } if @opts[:row]
|
307
|
+
end
|
308
|
+
|
309
|
+
def format_message_width
|
310
|
+
with(@message) { |msg| msg.ljust(@opts[:width]) } if @opts[:width]
|
311
|
+
end
|
312
|
+
|
313
|
+
def format_message_indent
|
314
|
+
with(@message) { |str| (" " * @opts[:indent]) << str } if @opts[:indent]
|
315
|
+
end
|
316
|
+
|
317
|
+
def output_message
|
318
|
+
@opts[:quiet] ? @message : say(@message)
|
319
|
+
end
|
320
|
+
|
321
|
+
extend self
|
322
|
+
end
|
323
|
+
end
|