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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +204 -204
  3. data/bin/tj +15 -15
  4. data/lib/theme-juice.rb +46 -46
  5. data/lib/theme-juice/cli.rb +248 -248
  6. data/lib/theme-juice/command.rb +20 -20
  7. data/lib/theme-juice/commands/create.rb +221 -221
  8. data/lib/theme-juice/commands/delete.rb +51 -51
  9. data/lib/theme-juice/commands/deploy.rb +20 -20
  10. data/lib/theme-juice/config.rb +68 -70
  11. data/lib/theme-juice/env.rb +25 -25
  12. data/lib/theme-juice/io.rb +323 -323
  13. data/lib/theme-juice/project.rb +35 -35
  14. data/lib/theme-juice/task.rb +42 -42
  15. data/lib/theme-juice/tasks/create_confirm.rb +33 -33
  16. data/lib/theme-juice/tasks/create_success.rb +42 -42
  17. data/lib/theme-juice/tasks/database.rb +50 -50
  18. data/lib/theme-juice/tasks/delete_confirm.rb +24 -24
  19. data/lib/theme-juice/tasks/delete_success.rb +31 -31
  20. data/lib/theme-juice/tasks/dns.rb +45 -45
  21. data/lib/theme-juice/tasks/dot_env.rb +53 -53
  22. data/lib/theme-juice/tasks/entry.rb +50 -50
  23. data/lib/theme-juice/tasks/hosts.rb +43 -43
  24. data/lib/theme-juice/tasks/import_database.rb +28 -28
  25. data/lib/theme-juice/tasks/landrush.rb +33 -33
  26. data/lib/theme-juice/tasks/list.rb +50 -50
  27. data/lib/theme-juice/tasks/location.rb +23 -23
  28. data/lib/theme-juice/tasks/nginx.rb +51 -51
  29. data/lib/theme-juice/tasks/repo.rb +49 -49
  30. data/lib/theme-juice/tasks/synced_folder.rb +30 -30
  31. data/lib/theme-juice/tasks/theme.rb +34 -34
  32. data/lib/theme-juice/tasks/vm.rb +30 -30
  33. data/lib/theme-juice/tasks/vm_customfile.rb +42 -42
  34. data/lib/theme-juice/tasks/vm_location.rb +32 -32
  35. data/lib/theme-juice/tasks/vm_plugins.rb +32 -32
  36. data/lib/theme-juice/tasks/vm_provision.rb +39 -39
  37. data/lib/theme-juice/tasks/vm_restart.rb +25 -25
  38. data/lib/theme-juice/tasks/wp_cli.rb +52 -52
  39. data/lib/theme-juice/util.rb +45 -45
  40. data/lib/theme-juice/version.rb +5 -5
  41. 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
@@ -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
- if multi_arg_regex =~ cmd
35
- cmd.gsub! multi_arg_regex, args.join(" ")
36
- else
37
- args.to_enum.with_index(1).each do |arg, i|
38
- cmd.gsub! single_arg_regex(i), arg
39
- end
40
- end
41
-
42
- cmd
43
- end
44
-
45
- def config
46
- begin
47
- YAML.load_file Dir["#{@project.location}/*"].select { |f| config_regex =~ File.basename(f) }.last ||
48
- @io.error("Config file not found in '#{@project.location}'")
49
- rescue ::Psych::SyntaxError => err
50
- @io.error "Config file is invalid" do
51
- puts err
52
- end
53
- end
54
- end
55
-
56
- def config_regex
57
- %r{^(((\.)?(tj)|((J|j)uicefile))(.y(a)?ml)?$)}
58
- end
59
-
60
- def multi_arg_regex
61
- %r{(%args%)|(%arguments%)}
62
- end
63
-
64
- def single_arg_regex(i)
65
- %r{(%arg#{i}%)|(%argument#{i}%)}
66
- end
67
-
68
- extend self
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
@@ -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
@@ -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