ing 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/.gitignore +5 -0
  2. data/GENERATORS.md +2 -0
  3. data/LICENSE +18 -0
  4. data/OPTIONS.md +2 -0
  5. data/README.md +251 -0
  6. data/TASKS.md +21 -0
  7. data/bin/ing +5 -0
  8. data/examples/rspec_convert.rb +102 -0
  9. data/ing.gemspec +29 -0
  10. data/ing.rb +102 -0
  11. data/lib/ing.rb +78 -0
  12. data/lib/ing/actions/create_file.rb +105 -0
  13. data/lib/ing/actions/create_link.rb +57 -0
  14. data/lib/ing/actions/directory.rb +98 -0
  15. data/lib/ing/actions/empty_directory.rb +155 -0
  16. data/lib/ing/actions/file_manipulation.rb +308 -0
  17. data/lib/ing/actions/inject_into_file.rb +109 -0
  18. data/lib/ing/commands/boot.rb +76 -0
  19. data/lib/ing/commands/generate.rb +64 -0
  20. data/lib/ing/commands/help.rb +87 -0
  21. data/lib/ing/commands/implicit.rb +59 -0
  22. data/lib/ing/commands/list.rb +108 -0
  23. data/lib/ing/dispatcher.rb +132 -0
  24. data/lib/ing/files.rb +190 -0
  25. data/lib/ing/lib_trollop.rb +782 -0
  26. data/lib/ing/shell.rb +390 -0
  27. data/lib/ing/trollop/parser.rb +17 -0
  28. data/lib/ing/util.rb +61 -0
  29. data/lib/ing/version.rb +3 -0
  30. data/lib/thor/actions/file_manipulation.rb +30 -0
  31. data/lib/thor/shell/basic.rb +44 -0
  32. data/test/acceptance/ing_run_tests.rb +164 -0
  33. data/test/actions/create_file_spec.rb +209 -0
  34. data/test/actions/create_link_spec.rb +90 -0
  35. data/test/actions/directory_spec.rb +167 -0
  36. data/test/actions/empty_directory_spec.rb +146 -0
  37. data/test/actions/file_manipulation_spec.rb +433 -0
  38. data/test/actions/inject_into_file_spec.rb +147 -0
  39. data/test/fixtures/application.rb +2 -0
  40. data/test/fixtures/app{1}/README +3 -0
  41. data/test/fixtures/bundle/execute.rb +6 -0
  42. data/test/fixtures/bundle/main.thor +1 -0
  43. data/test/fixtures/doc/%file_name%.rb.tt +1 -0
  44. data/test/fixtures/doc/COMMENTER +10 -0
  45. data/test/fixtures/doc/README +3 -0
  46. data/test/fixtures/doc/block_helper.rb +3 -0
  47. data/test/fixtures/doc/components/.empty_directory +0 -0
  48. data/test/fixtures/doc/config.rb +1 -0
  49. data/test/fixtures/doc/config.yaml.tt +1 -0
  50. data/test/fixtures/group.ing.rb +76 -0
  51. data/test/fixtures/invok.ing.rb +50 -0
  52. data/test/fixtures/namespace.ing.rb +52 -0
  53. data/test/fixtures/require.ing.rb +7 -0
  54. data/test/fixtures/task.ing.rb +36 -0
  55. data/test/fixtures/task.thor +10 -0
  56. data/test/spec_helper.rb +2 -0
  57. data/test/test_helper.rb +41 -0
  58. data/todo.yml +7 -0
  59. metadata +147 -0
data/lib/ing/shell.rb ADDED
@@ -0,0 +1,390 @@
1
+ require 'fileutils'
2
+ require 'tempfile'
3
+
4
+ module Ing
5
+ module Shell
6
+ class Basic
7
+ attr_accessor :base
8
+ attr_reader :padding
9
+
10
+ # Initialize base, mute and padding to nil.
11
+ #
12
+ def initialize #:nodoc:
13
+ @base, @mute, @padding = nil, false, 0
14
+ end
15
+
16
+ # Mute everything that's inside given block
17
+ #
18
+ def mute
19
+ @mute = true
20
+ yield
21
+ ensure
22
+ @mute = false
23
+ end
24
+
25
+ # Check if base is muted
26
+ #
27
+ def mute?
28
+ @mute
29
+ end
30
+
31
+ # Sets the output padding, not allowing less than zero values.
32
+ #
33
+ def padding=(value)
34
+ @padding = [0, value].max
35
+ end
36
+
37
+ # Asks something to the user and receives a response.
38
+ #
39
+ # If asked to limit the correct responses, you can pass in an
40
+ # array of acceptable answers. If one of those is not supplied,
41
+ # they will be shown a message stating that one of those answers
42
+ # must be given and re-asked the question.
43
+ #
44
+ # ==== Example
45
+ # ask("What is your name?")
46
+ #
47
+ # ask("What is your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"])
48
+ #
49
+ def ask(statement, *args)
50
+ options = args.last.is_a?(Hash) ? args.pop : {}
51
+
52
+ options[:limited_to] ? ask_filtered(statement, options[:limited_to], *args) : ask_simply(statement, *args)
53
+ end
54
+
55
+ # Say (print) something to the user. If the sentence ends with a whitespace
56
+ # or tab character, a new line is not appended (print + flush). Otherwise
57
+ # are passed straight to puts (behavior got from Highline).
58
+ #
59
+ # ==== Example
60
+ # say("I know you knew that.")
61
+ #
62
+ def say(message="", color=nil, force_new_line=(message.to_s !~ /( |\t)$/))
63
+ message = message.to_s
64
+
65
+ message = set_color(message, *color) if color
66
+
67
+ spaces = " " * padding
68
+
69
+ if force_new_line
70
+ stdout.puts(spaces + message)
71
+ else
72
+ stdout.print(spaces + message)
73
+ end
74
+ stdout.flush
75
+ end
76
+
77
+ # Say a status with the given color and appends the message. Since this
78
+ # method is used frequently by actions, it allows nil or false to be given
79
+ # in log_status, avoiding the message from being shown. If a Symbol is
80
+ # given in log_status, it's used as the color.
81
+ #
82
+ def say_status(status, message, log_status=true)
83
+ return if quiet? || log_status == false
84
+ spaces = " " * (padding + 1)
85
+ color = log_status.is_a?(Symbol) ? log_status : :green
86
+
87
+ status = status.to_s.rjust(12)
88
+ status = set_color status, color, true if color
89
+
90
+ stdout.puts "#{status}#{spaces}#{message}"
91
+ stdout.flush
92
+ end
93
+
94
+ # Make a question the to user and returns true if the user replies "y" or
95
+ # "yes".
96
+ #
97
+ def yes?(statement, color=nil)
98
+ !!(ask(statement, color) =~ is?(:yes))
99
+ end
100
+
101
+ # Make a question the to user and returns true if the user replies "n" or
102
+ # "no".
103
+ #
104
+ def no?(statement, color=nil)
105
+ !yes?(statement, color)
106
+ end
107
+
108
+ # Prints values in columns
109
+ #
110
+ # ==== Parameters
111
+ # Array[String, String, ...]
112
+ #
113
+ def print_in_columns(array)
114
+ return if array.empty?
115
+ colwidth = (array.map{|el| el.to_s.size}.max || 0) + 2
116
+ array.each_with_index do |value, index|
117
+ # Don't output trailing spaces when printing the last column
118
+ if ((((index + 1) % (terminal_width / colwidth))).zero? && !index.zero?) || index + 1 == array.length
119
+ stdout.puts value
120
+ else
121
+ stdout.printf("%-#{colwidth}s", value)
122
+ end
123
+ end
124
+ end
125
+
126
+ # Prints a table.
127
+ #
128
+ # ==== Parameters
129
+ # Array[Array[String, String, ...]]
130
+ #
131
+ # ==== Options
132
+ # indent<Integer>:: Indent the first column by indent value.
133
+ # colwidth<Integer>:: Force the first column to colwidth spaces wide.
134
+ #
135
+ def print_table(array, options={})
136
+ return if array.empty?
137
+
138
+ formats, indent, colwidth = [], options[:indent].to_i, options[:colwidth]
139
+ options[:truncate] = terminal_width if options[:truncate] == true
140
+
141
+ formats << "%-#{colwidth + 2}s" if colwidth
142
+ start = colwidth ? 1 : 0
143
+
144
+ colcount = array.max{|a,b| a.size <=> b.size }.size
145
+
146
+ maximas = []
147
+
148
+ start.upto(colcount - 1) do |index|
149
+ maxima = array.map {|row| row[index] ? row[index].to_s.size : 0 }.max
150
+ maximas << maxima
151
+ if index == colcount - 1
152
+ # Don't output 2 trailing spaces when printing the last column
153
+ formats << "%-s"
154
+ else
155
+ formats << "%-#{maxima + 2}s"
156
+ end
157
+ end
158
+
159
+ formats[0] = formats[0].insert(0, " " * indent)
160
+ formats << "%s"
161
+
162
+ array.each do |row|
163
+ sentence = ""
164
+
165
+ row.each_with_index do |column, index|
166
+ maxima = maximas[index]
167
+
168
+ if column.is_a?(Numeric)
169
+ if index == row.size - 1
170
+ # Don't output 2 trailing spaces when printing the last column
171
+ f = "%#{maxima}s"
172
+ else
173
+ f = "%#{maxima}s "
174
+ end
175
+ else
176
+ f = formats[index]
177
+ end
178
+ sentence << f % column.to_s
179
+ end
180
+
181
+ sentence = truncate(sentence, options[:truncate]) if options[:truncate]
182
+ stdout.puts sentence
183
+ end
184
+ end
185
+
186
+ # Prints a long string, word-wrapping the text to the current width of the
187
+ # terminal display. Ideal for printing heredocs.
188
+ #
189
+ # ==== Parameters
190
+ # String
191
+ #
192
+ # ==== Options
193
+ # indent<Integer>:: Indent each line of the printed paragraph by indent value.
194
+ #
195
+ def print_wrapped(message, options={})
196
+ indent = options[:indent] || 0
197
+ width = terminal_width - indent
198
+ paras = message.split("\n\n")
199
+
200
+ paras.map! do |unwrapped|
201
+ unwrapped.strip.gsub(/\n/, " ").squeeze(" ").
202
+ gsub(/.{1,#{width}}(?:\s|\Z)/){($& + 5.chr).
203
+ gsub(/\n\005/,"\n").gsub(/\005/,"\n")}
204
+ end
205
+
206
+ paras.each do |para|
207
+ para.split("\n").each do |line|
208
+ stdout.puts line.insert(0, " " * indent)
209
+ end
210
+ stdout.puts unless para == paras.last
211
+ end
212
+ end
213
+
214
+ # Deals with file collision and returns true if the file should be
215
+ # overwritten and false otherwise. If a block is given, it uses the block
216
+ # response as the content for the diff.
217
+ #
218
+ # ==== Parameters
219
+ # destination<String>:: the destination file to solve conflicts
220
+ # block<Proc>:: an optional block that returns the value to be used in diff
221
+ #
222
+ def file_collision(destination)
223
+ return true if @always_force
224
+ options = block_given? ? "[Ynaqdh]" : "[Ynaqh]"
225
+
226
+ while true
227
+ answer = ask %[Overwrite #{destination}? (enter "h" for help) #{options}]
228
+
229
+ case answer
230
+ when is?(:yes), is?(:force), ""
231
+ return true
232
+ when is?(:no), is?(:skip)
233
+ return false
234
+ when is?(:always)
235
+ return @always_force = true
236
+ when is?(:quit)
237
+ say 'Aborting...'
238
+ raise SystemExit
239
+ when is?(:diff)
240
+ show_diff(destination, yield) if block_given?
241
+ say 'Retrying...'
242
+ else
243
+ say file_collision_help
244
+ end
245
+ end
246
+ end
247
+
248
+ # This code was copied from Rake, available under MIT-LICENSE
249
+ # Copyright (c) 2003, 2004 Jim Weirich
250
+ def terminal_width
251
+ if ENV['THOR_COLUMNS']
252
+ result = ENV['THOR_COLUMNS'].to_i
253
+ else
254
+ result = unix? ? dynamic_width : 80
255
+ end
256
+ (result < 10) ? 80 : result
257
+ rescue
258
+ 80
259
+ end
260
+
261
+ # Called if something goes wrong during the execution. This is used by Thor
262
+ # internally and should not be used inside your scripts. If something went
263
+ # wrong, you can always raise an exception. If you raise a Thor::Error, it
264
+ # will be rescued and wrapped in the method below.
265
+ #
266
+ def error(statement)
267
+ stderr.puts statement
268
+ end
269
+
270
+ # Apply color to the given string with optional bold. Disabled in the
271
+ # Thor::Shell::Basic class.
272
+ #
273
+ def set_color(string, *args) #:nodoc:
274
+ string
275
+ end
276
+
277
+ protected
278
+
279
+ def lookup_color(color)
280
+ return color unless color.is_a?(Symbol)
281
+ self.class.const_get(color.to_s.upcase)
282
+ end
283
+
284
+ def stdout
285
+ $stdout
286
+ end
287
+
288
+ def stdin
289
+ $stdin
290
+ end
291
+
292
+ def stderr
293
+ $stderr
294
+ end
295
+
296
+ def is?(value) #:nodoc:
297
+ value = value.to_s
298
+
299
+ if value.size == 1
300
+ /\A#{value}\z/i
301
+ else
302
+ /\A(#{value}|#{value[0,1]})\z/i
303
+ end
304
+ end
305
+
306
+ def file_collision_help #:nodoc:
307
+ <<HELP
308
+ Y - yes, overwrite
309
+ n - no, do not overwrite
310
+ a - all, overwrite this and all others
311
+ q - quit, abort
312
+ d - diff, show the differences between the old and the new
313
+ h - help, show this help
314
+ HELP
315
+ end
316
+
317
+ def show_diff(destination, content) #:nodoc:
318
+ diff_cmd = ENV['THOR_DIFF'] || ENV['RAILS_DIFF'] || 'diff -u'
319
+
320
+ Tempfile.open(File.basename(destination), File.dirname(destination)) do |temp|
321
+ temp.write content
322
+ temp.rewind
323
+ system %(#{diff_cmd} "#{destination}" "#{temp.path}")
324
+ end
325
+ end
326
+
327
+ def quiet? #:nodoc:
328
+ mute? || (base && base.options[:quiet])
329
+ end
330
+
331
+ # Calculate the dynamic width of the terminal
332
+ def dynamic_width
333
+ @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
334
+ end
335
+
336
+ def dynamic_width_stty
337
+ %x{stty size 2>/dev/null}.split[1].to_i
338
+ end
339
+
340
+ def dynamic_width_tput
341
+ %x{tput cols 2>/dev/null}.to_i
342
+ end
343
+
344
+ def unix?
345
+ RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
346
+ end
347
+
348
+ def truncate(string, width)
349
+ as_unicode do
350
+ chars = string.chars.to_a
351
+ if chars.length <= width
352
+ chars.join
353
+ else
354
+ ( chars[0, width-3].join ) + "..."
355
+ end
356
+ end
357
+ end
358
+
359
+ if "".respond_to?(:encode)
360
+ def as_unicode
361
+ yield
362
+ end
363
+ else
364
+ def as_unicode
365
+ old, $KCODE = $KCODE, "U"
366
+ yield
367
+ ensure
368
+ $KCODE = old
369
+ end
370
+ end
371
+
372
+ def ask_simply(statement, color=nil)
373
+ say("#{statement} ", color)
374
+ stdin.gets.strip
375
+ end
376
+
377
+ def ask_filtered(statement, answer_set, *args)
378
+ correct_answer = nil
379
+ until correct_answer
380
+ answer = ask_simply("#{statement} #{answer_set.inspect}", *args)
381
+ correct_answer = answer_set.include?(answer) ? answer : nil
382
+ answers = answer_set.map(&:inspect).join(", ")
383
+ say("Your response must be one of: [#{answers}]. Please try again.") unless correct_answer
384
+ end
385
+ correct_answer
386
+ end
387
+
388
+ end
389
+ end
390
+ end
@@ -0,0 +1,17 @@
1
+ # Extensions to Trollop::Parser
2
+
3
+ module Trollop
4
+
5
+ class Parser
6
+
7
+ def educate_banner stream=$stdout
8
+ width
9
+ @order.select {|what, _| what == :text}.each do |what, opt|
10
+ stream.puts wrap(opt)
11
+ end
12
+ end
13
+ alias :educate_text :educate_banner
14
+
15
+ end
16
+
17
+ end
data/lib/ing/util.rb ADDED
@@ -0,0 +1,61 @@
1
+ module Ing
2
+ module Util
3
+ extend self
4
+
5
+ def to_class_names(str)
6
+ str.split(':').map {|c| c.gsub(/(?:\A|_+)(\w)/) {$1.upcase} }
7
+ end
8
+ alias decode_class_names to_class_names
9
+
10
+ def encode_class_names(list)
11
+ list.map {|c| c.to_s.gsub(/([A-Z])/) {
12
+ ($`.empty? ? "" : "_") + $1.downcase
13
+ }
14
+ }.join(':')
15
+ end
16
+
17
+ def encode_class(klass)
18
+ encode_class_names(klass.to_s.split('::'))
19
+ end
20
+
21
+ def to_classes(str, base=::Object)
22
+ namespaced_const_get( to_class_names(str), base )
23
+ end
24
+
25
+ def namespaced_const_get(list, base=::Object)
26
+ list.inject(base) {|m, klass| m.const_get(klass, false)}
27
+ end
28
+
29
+ def option?(arg)
30
+ !!(/^-{1,2}/ =~ arg)
31
+ end
32
+
33
+ # not used
34
+ def split_method_args(args)
35
+ if option?(args.first)
36
+ [nil, args]
37
+ else
38
+ [args.first, args[1..-1]]
39
+ end
40
+ end
41
+
42
+ # Returns a string that has had any glob characters escaped.
43
+ # The glob characters are `* ? { } [ ]`.
44
+ #
45
+ # ==== Examples
46
+ #
47
+ # Util.escape_globs('[apps]') # => '\[apps\]'
48
+ #
49
+ # ==== Parameters
50
+ # String
51
+ #
52
+ # ==== Returns
53
+ # String
54
+ #
55
+ def escape_globs(path)
56
+ path.to_s.gsub(/[*?{}\[\]]/, '\\\\\\&')
57
+ end
58
+
59
+ end
60
+
61
+ end