millisami-thor 0.14.6

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 (82) hide show
  1. data/.autotest +8 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +9 -0
  4. data/.rspec +1 -0
  5. data/CHANGELOG.rdoc +103 -0
  6. data/Gemfile +11 -0
  7. data/LICENSE.md +20 -0
  8. data/README.md +26 -0
  9. data/Thorfile +13 -0
  10. data/bin/rake2thor +86 -0
  11. data/bin/thor +6 -0
  12. data/lib/thor/actions/create_file.rb +105 -0
  13. data/lib/thor/actions/create_link.rb +57 -0
  14. data/lib/thor/actions/directory.rb +93 -0
  15. data/lib/thor/actions/empty_directory.rb +134 -0
  16. data/lib/thor/actions/file_manipulation.rb +270 -0
  17. data/lib/thor/actions/inject_into_file.rb +109 -0
  18. data/lib/thor/actions.rb +314 -0
  19. data/lib/thor/base.rb +598 -0
  20. data/lib/thor/core_ext/file_binary_read.rb +9 -0
  21. data/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  22. data/lib/thor/core_ext/ordered_hash.rb +100 -0
  23. data/lib/thor/error.rb +30 -0
  24. data/lib/thor/group.rb +276 -0
  25. data/lib/thor/invocation.rb +168 -0
  26. data/lib/thor/parser/argument.rb +67 -0
  27. data/lib/thor/parser/arguments.rb +165 -0
  28. data/lib/thor/parser/option.rb +120 -0
  29. data/lib/thor/parser/options.rb +181 -0
  30. data/lib/thor/parser.rb +4 -0
  31. data/lib/thor/rake_compat.rb +70 -0
  32. data/lib/thor/runner.rb +309 -0
  33. data/lib/thor/shell/basic.rb +302 -0
  34. data/lib/thor/shell/color.rb +108 -0
  35. data/lib/thor/shell/html.rb +121 -0
  36. data/lib/thor/shell.rb +88 -0
  37. data/lib/thor/task.rb +129 -0
  38. data/lib/thor/util.rb +229 -0
  39. data/lib/thor/version.rb +3 -0
  40. data/lib/thor.rb +336 -0
  41. data/spec/actions/create_file_spec.rb +170 -0
  42. data/spec/actions/directory_spec.rb +136 -0
  43. data/spec/actions/empty_directory_spec.rb +98 -0
  44. data/spec/actions/file_manipulation_spec.rb +317 -0
  45. data/spec/actions/inject_into_file_spec.rb +135 -0
  46. data/spec/actions_spec.rb +322 -0
  47. data/spec/base_spec.rb +274 -0
  48. data/spec/core_ext/hash_with_indifferent_access_spec.rb +43 -0
  49. data/spec/core_ext/ordered_hash_spec.rb +115 -0
  50. data/spec/fixtures/application.rb +2 -0
  51. data/spec/fixtures/bundle/execute.rb +6 -0
  52. data/spec/fixtures/bundle/main.thor +1 -0
  53. data/spec/fixtures/doc/%file_name%.rb.tt +1 -0
  54. data/spec/fixtures/doc/README +3 -0
  55. data/spec/fixtures/doc/block_helper.rb +3 -0
  56. data/spec/fixtures/doc/components/.empty_directory +0 -0
  57. data/spec/fixtures/doc/config.rb +1 -0
  58. data/spec/fixtures/doc/config.yaml.tt +1 -0
  59. data/spec/fixtures/group.thor +114 -0
  60. data/spec/fixtures/invoke.thor +112 -0
  61. data/spec/fixtures/path with spaces +0 -0
  62. data/spec/fixtures/script.thor +184 -0
  63. data/spec/fixtures/task.thor +10 -0
  64. data/spec/group_spec.rb +216 -0
  65. data/spec/invocation_spec.rb +100 -0
  66. data/spec/parser/argument_spec.rb +47 -0
  67. data/spec/parser/arguments_spec.rb +65 -0
  68. data/spec/parser/option_spec.rb +202 -0
  69. data/spec/parser/options_spec.rb +329 -0
  70. data/spec/rake_compat_spec.rb +72 -0
  71. data/spec/register_spec.rb +92 -0
  72. data/spec/runner_spec.rb +210 -0
  73. data/spec/shell/basic_spec.rb +223 -0
  74. data/spec/shell/color_spec.rb +41 -0
  75. data/spec/shell/html_spec.rb +27 -0
  76. data/spec/shell_spec.rb +47 -0
  77. data/spec/spec_helper.rb +54 -0
  78. data/spec/task_spec.rb +74 -0
  79. data/spec/thor_spec.rb +362 -0
  80. data/spec/util_spec.rb +163 -0
  81. data/thor.gemspec +25 -0
  82. metadata +241 -0
data/.autotest ADDED
@@ -0,0 +1,8 @@
1
+ Autotest.add_hook :initialize do |at|
2
+ at.clear_mappings
3
+ at.add_exception(/\.git/)
4
+ at.add_exception(/spec\/sandbox/)
5
+ at.add_mapping(%r{^spec/.*_spec}) {|filename,_| at.files_matching %r{#{filename}}}
6
+ at.add_mapping(%r{^bin/thor}) {|_,_| at.files_matching %r{spec/thor_runner_spec}}
7
+ at.add_mapping(%r{}) {|_,_| at.files_matching %r{spec/.*_spec}}
8
+ end
data/.gemtest ADDED
File without changes
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ *.rbc
2
+ .bundle
3
+ .idea
4
+ .rvmrc
5
+ /coverage
6
+ /pkg
7
+ /rdoc
8
+ /spec/sandbox
9
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,103 @@
1
+ == 0.14, released 2010-07-25
2
+
3
+ * Added CreateLink class and #link_file method
4
+ * Made Thor::Actions#run use system as default method for system calls
5
+ * Allow use of private methods from superclass as tasks
6
+ * Added mute(&block) method which allows to run block without any output
7
+ * Removed config[:pretend]
8
+ * Enabled underscores for command line switches
9
+ * Added Thor::Base.basename which is used by both Thor.banner and Thor::Group.banner
10
+ * Deprecated invoke() without arguments
11
+ * Added :only and :except to check_unknown_options
12
+
13
+ == 0.13, released 2010-02-03
14
+
15
+ * Added :lazy_default which is only triggered if a switch is given
16
+ * Added Thor::Shell::HTML
17
+ * Added subcommands
18
+ * Decoupled Thor::Group and Thor, so it's easier to vendor
19
+ * Added check_unknown_options! in case you want error messages to be raised in valid switches
20
+ * run(command) should return the results of command
21
+
22
+ == 0.12, released 2010-01-02
23
+
24
+ * Methods generated by attr_* are automatically not marked as tasks
25
+ * inject_into_file does not add the same content twice, unless :force is set
26
+ * Removed rr in favor to rspec mock framework
27
+ * Improved output for thor -T
28
+ * [#7] Do not force white color on status
29
+ * [#8] Yield a block with the filename on directory
30
+
31
+ == 0.11, released 2009-07-01
32
+
33
+ * Added a rake compatibility layer. It allows you to use spec and rdoc tasks on
34
+ Thor classes.
35
+
36
+ * BACKWARDS INCOMPATIBLE: aliases are not generated automatically anymore
37
+ since it may cause wrong behavior in the invocation system.
38
+
39
+ * thor help now show information about any class/task. All those calls are
40
+ possible:
41
+
42
+ thor help describe
43
+ thor help describe:amazing
44
+
45
+ Or even with default namespaces:
46
+
47
+ thor help :spec
48
+
49
+ * Thor::Runner now invokes the default task if none is supplied:
50
+
51
+ thor describe # invokes the default task, usually help
52
+
53
+ * Thor::Runner now works with mappings:
54
+
55
+ thor describe -h
56
+
57
+ * Added some documentation and code refactoring.
58
+
59
+ == 0.9.8, released 2008-10-20
60
+
61
+ * Fixed some tiny issues that were introduced lately.
62
+
63
+ == 0.9.7, released 2008-10-13
64
+
65
+ * Setting global method options on the initialize method works as expected:
66
+ All other tasks will accept these global options in addition to their own.
67
+ * Added 'group' notion to Thor task sets (class Thor); by default all tasks
68
+ are in the 'standard' group. Running 'thor -T' will only show the standard
69
+ tasks - adding --all will show all tasks. You can also filter on a specific
70
+ group using the --group option: thor -T --group advanced
71
+
72
+ == 0.9.6, released 2008-09-13
73
+
74
+ * Generic improvements
75
+
76
+ == 0.9.5, released 2008-08-27
77
+
78
+ * Improve Windows compatibility
79
+ * Update (incorrect) README and task.thor sample file
80
+ * Options hash is now frozen (once returned)
81
+ * Allow magic predicates on options object. For instance: `options.force?`
82
+ * Add support for :numeric type
83
+ * BACKWARDS INCOMPATIBLE: Refactor Thor::Options. You cannot access shorthand forms in options hash anymore (for instance, options[:f])
84
+ * Allow specifying optional args with default values: method_options(:user => "mislav")
85
+ * Don't write options for nil or false values. This allows, for example, turning color off when running specs.
86
+ * Exit with the status of the spec command to help CI stuff out some.
87
+
88
+ == 0.9.4, released 2008-08-13
89
+
90
+ * Try to add Windows compatibility.
91
+ * BACKWARDS INCOMPATIBLE: options hash is now accessed as a property in your class and is not passed as last argument anymore
92
+ * Allow options at the beginning of the argument list as well as the end.
93
+ * Make options available with symbol keys in addition to string keys.
94
+ * Allow true to be passed to Thor#method_options to denote a boolean option.
95
+ * If loading a thor file fails, don't give up, just print a warning and keep going.
96
+ * Make sure that we re-raise errors if they happened further down the pipe than we care about.
97
+ * Only delete the old file on updating when the installation of the new one is a success
98
+ * Make it Ruby 1.8.5 compatible.
99
+ * Don't raise an error if a boolean switch is defined multiple times.
100
+ * Thor::Options now doesn't parse through things that look like options but aren't.
101
+ * Add URI detection to install task, and make sure we don't append ".thor" to URIs
102
+ * Add rake2thor to the gem binfiles.
103
+ * Make sure local Thorfiles override system-wide ones.
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ platforms :mri_18 do
6
+ gem "ruby-debug", ">= 0.10.3"
7
+ end
8
+
9
+ platforms :mri_19 do
10
+ gem "ruby-debug19"
11
+ end
data/LICENSE.md ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Yehuda Katz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ Thor
2
+ ====
3
+
4
+ Description
5
+ -----------
6
+ Thor is a simple and efficient tool for building self-documenting command line
7
+ utilities. It removes the pain of parsing command line options, writing
8
+ "USAGE:" banners, and can also be used as an alternative to the [Rake][rake]
9
+ build tool. The syntax is Rake-like, so it should be familiar to most Rake
10
+ users.
11
+
12
+ [rake]: https://github.com/jimweirich/rake
13
+
14
+ Installation
15
+ ------------
16
+ gem install thor
17
+
18
+ Usage and documentation
19
+ -----------------------
20
+ Please see [the wiki](https://github.com/wycats/thor/wiki) for basic usage and other documentation on using Thor.
21
+
22
+ License
23
+ -------
24
+ Released under the MIT License. See the [LICENSE][license] file for further details.
25
+
26
+ [license]: https://github.com/wycats/thor/blob/master/LICENSE.md
data/Thorfile ADDED
@@ -0,0 +1,13 @@
1
+ # enconding: utf-8
2
+ $:.unshift File.expand_path("../lib", __FILE__)
3
+
4
+ require 'thor/rake_compat'
5
+
6
+ class Default < Thor
7
+ include Thor::RakeCompat
8
+
9
+ desc "spec", "run the specs"
10
+ def spec
11
+ exec "rspec -cfs spec"
12
+ end
13
+ end
data/bin/rake2thor ADDED
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ruby2ruby'
4
+ require 'parse_tree'
5
+ if Ruby2Ruby::VERSION >= "1.2.0"
6
+ require 'parse_tree_extensions'
7
+ end
8
+ require 'rake'
9
+
10
+ input = ARGV[0] || 'Rakefile'
11
+ output = ARGV[1] || 'Thorfile'
12
+
13
+ $requires = []
14
+
15
+ module Kernel
16
+ def require_with_record(file)
17
+ $requires << file if caller[1] =~ /rake2thor:/
18
+ require_without_record file
19
+ end
20
+ alias_method :require_without_record, :require
21
+ alias_method :require, :require_with_record
22
+ end
23
+
24
+ load input
25
+
26
+ @private_methods = []
27
+
28
+ def file_task_name(name)
29
+ "compile_" + name.gsub('/', '_slash_').gsub('.', '_dot_').gsub(/\W/, '_')
30
+ end
31
+
32
+ def method_for_task(task)
33
+ file_task = task.is_a?(Rake::FileTask)
34
+ comment = task.instance_variable_get('@comment')
35
+ prereqs = task.instance_variable_get('@prerequisites').select(&Rake::Task.method(:task_defined?))
36
+ actions = task.instance_variable_get('@actions')
37
+ name = task.name.gsub(/^([^:]+:)+/, '')
38
+ name = file_task_name(name) if file_task
39
+ meth = ''
40
+
41
+ meth << "desc #{name.inspect}, #{comment.inspect}\n" if comment
42
+ meth << "def #{name}\n"
43
+
44
+ meth << prereqs.map do |pre|
45
+ pre = pre.to_s
46
+ pre = file_task_name(pre) if Rake::Task[pre].is_a?(Rake::FileTask)
47
+ ' ' + pre
48
+ end.join("\n")
49
+
50
+ meth << "\n\n" unless prereqs.empty? || actions.empty?
51
+
52
+ meth << actions.map do |act|
53
+ act = act.to_ruby
54
+ unless act.gsub!(/^proc \{ \|(\w+)\|\n/,
55
+ " \\1 = Struct.new(:name).new(#{name.inspect}) # A crude mock Rake::Task object\n")
56
+ act.gsub!(/^proc \{\n/, '')
57
+ end
58
+ act.gsub(/\n\}$/, '')
59
+ end.join("\n")
60
+
61
+ meth << "\nend"
62
+
63
+ if file_task
64
+ @private_methods << meth
65
+ return
66
+ end
67
+
68
+ meth
69
+ end
70
+
71
+ body = Rake::Task.tasks.map(&method(:method_for_task)).compact.map { |meth| meth.gsub(/^/, ' ') }.join("\n\n")
72
+
73
+ unless @private_methods.empty?
74
+ body << "\n\n private\n\n"
75
+ body << @private_methods.map { |meth| meth.gsub(/^/, ' ') }.join("\n\n")
76
+ end
77
+
78
+ requires = $requires.map { |r| "require #{r.inspect}" }.join("\n")
79
+
80
+ File.open(output, 'w') { |f| f.write(<<END.lstrip) }
81
+ #{requires}
82
+
83
+ class Default < Thor
84
+ #{body}
85
+ end
86
+ END
data/bin/thor ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: ruby -*-
3
+
4
+ require 'thor/runner'
5
+ $thor_runner = true
6
+ Thor::Runner.start
@@ -0,0 +1,105 @@
1
+ require 'thor/actions/empty_directory'
2
+
3
+ class Thor
4
+ module Actions
5
+
6
+ # Create a new file relative to the destination root with the given data,
7
+ # which is the return value of a block or a data string.
8
+ #
9
+ # ==== Parameters
10
+ # destination<String>:: the relative path to the destination root.
11
+ # data<String|NilClass>:: the data to append to the file.
12
+ # config<Hash>:: give :verbose => false to not log the status.
13
+ #
14
+ # ==== Examples
15
+ #
16
+ # create_file "lib/fun_party.rb" do
17
+ # hostname = ask("What is the virtual hostname I should use?")
18
+ # "vhost.name = #{hostname}"
19
+ # end
20
+ #
21
+ # create_file "config/apache.conf", "your apache config"
22
+ #
23
+ def create_file(destination, *args, &block)
24
+ config = args.last.is_a?(Hash) ? args.pop : {}
25
+ data = args.first
26
+ action CreateFile.new(self, destination, block || data.to_s, config)
27
+ end
28
+ alias :add_file :create_file
29
+
30
+ # CreateFile is a subset of Template, which instead of rendering a file with
31
+ # ERB, it gets the content from the user.
32
+ #
33
+ class CreateFile < EmptyDirectory #:nodoc:
34
+ attr_reader :data
35
+
36
+ def initialize(base, destination, data, config={})
37
+ @data = data
38
+ super(base, destination, config)
39
+ end
40
+
41
+ # Checks if the content of the file at the destination is identical to the rendered result.
42
+ #
43
+ # ==== Returns
44
+ # Boolean:: true if it is identical, false otherwise.
45
+ #
46
+ def identical?
47
+ exists? && File.binread(destination) == render
48
+ end
49
+
50
+ # Holds the content to be added to the file.
51
+ #
52
+ def render
53
+ @render ||= if data.is_a?(Proc)
54
+ data.call
55
+ else
56
+ data
57
+ end
58
+ end
59
+
60
+ def invoke!
61
+ invoke_with_conflict_check do
62
+ FileUtils.mkdir_p(File.dirname(destination))
63
+ File.open(destination, 'wb') { |f| f.write render }
64
+ end
65
+ given_destination
66
+ end
67
+
68
+ protected
69
+
70
+ # Now on conflict we check if the file is identical or not.
71
+ #
72
+ def on_conflict_behavior(&block)
73
+ if identical?
74
+ say_status :identical, :blue
75
+ else
76
+ options = base.options.merge(config)
77
+ force_or_skip_or_conflict(options[:force], options[:skip], &block)
78
+ end
79
+ end
80
+
81
+ # If force is true, run the action, otherwise check if it's not being
82
+ # skipped. If both are false, show the file_collision menu, if the menu
83
+ # returns true, force it, otherwise skip.
84
+ #
85
+ def force_or_skip_or_conflict(force, skip, &block)
86
+ if force
87
+ say_status :force, :yellow
88
+ block.call unless pretend?
89
+ elsif skip
90
+ say_status :skip, :yellow
91
+ else
92
+ say_status :conflict, :red
93
+ force_or_skip_or_conflict(force_on_collision?, true, &block)
94
+ end
95
+ end
96
+
97
+ # Shows the file collision menu to the user and gets the result.
98
+ #
99
+ def force_on_collision?
100
+ base.shell.file_collision(destination){ render }
101
+ end
102
+
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,57 @@
1
+ require 'thor/actions/create_file'
2
+
3
+ class Thor
4
+ module Actions
5
+
6
+ # Create a new file relative to the destination root from the given source.
7
+ #
8
+ # ==== Parameters
9
+ # destination<String>:: the relative path to the destination root.
10
+ # source<String|NilClass>:: the relative path to the source root.
11
+ # config<Hash>:: give :verbose => false to not log the status.
12
+ # :: give :symbolic => false for hard link.
13
+ #
14
+ # ==== Examples
15
+ #
16
+ # create_link "config/apache.conf", "/etc/apache.conf"
17
+ #
18
+ def create_link(destination, *args, &block)
19
+ config = args.last.is_a?(Hash) ? args.pop : {}
20
+ source = args.first
21
+ action CreateLink.new(self, destination, source, config)
22
+ end
23
+ alias :add_link :create_link
24
+
25
+ # CreateLink is a subset of CreateFile, which instead of taking a block of
26
+ # data, just takes a source string from the user.
27
+ #
28
+ class CreateLink < CreateFile #:nodoc:
29
+ attr_reader :data
30
+
31
+ # Checks if the content of the file at the destination is identical to the rendered result.
32
+ #
33
+ # ==== Returns
34
+ # Boolean:: true if it is identical, false otherwise.
35
+ #
36
+ def identical?
37
+ exists? && File.identical?(render, destination)
38
+ end
39
+
40
+ def invoke!
41
+ invoke_with_conflict_check do
42
+ FileUtils.mkdir_p(File.dirname(destination))
43
+ # Create a symlink by default
44
+ config[:symbolic] ||= true
45
+ File.unlink(destination) if exists?
46
+ if config[:symbolic]
47
+ File.symlink(render, destination)
48
+ else
49
+ File.link(render, destination)
50
+ end
51
+ end
52
+ given_destination
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,93 @@
1
+ require 'thor/actions/empty_directory'
2
+
3
+ class Thor
4
+ module Actions
5
+
6
+ # Copies recursively the files from source directory to root directory.
7
+ # If any of the files finishes with .tt, it's considered to be a template
8
+ # and is placed in the destination without the extension .tt. If any
9
+ # empty directory is found, it's copied and all .empty_directory files are
10
+ # ignored. Remember that file paths can also be encoded, let's suppose a doc
11
+ # directory with the following files:
12
+ #
13
+ # doc/
14
+ # components/.empty_directory
15
+ # README
16
+ # rdoc.rb.tt
17
+ # %app_name%.rb
18
+ #
19
+ # When invoked as:
20
+ #
21
+ # directory "doc"
22
+ #
23
+ # It will create a doc directory in the destination with the following
24
+ # files (assuming that the `app_name` method returns the value "blog"):
25
+ #
26
+ # doc/
27
+ # components/
28
+ # README
29
+ # rdoc.rb
30
+ # blog.rb
31
+ #
32
+ # ==== Parameters
33
+ # source<String>:: the relative path to the source root.
34
+ # destination<String>:: the relative path to the destination root.
35
+ # config<Hash>:: give :verbose => false to not log the status.
36
+ # If :recursive => false, does not look for paths recursively.
37
+ #
38
+ # ==== Examples
39
+ #
40
+ # directory "doc"
41
+ # directory "doc", "docs", :recursive => false
42
+ #
43
+ def directory(source, *args, &block)
44
+ config = args.last.is_a?(Hash) ? args.pop : {}
45
+ destination = args.first || source
46
+ action Directory.new(self, source, destination || source, config, &block)
47
+ end
48
+
49
+ class Directory < EmptyDirectory #:nodoc:
50
+ attr_reader :source
51
+
52
+ def initialize(base, source, destination=nil, config={}, &block)
53
+ @source = File.expand_path(base.find_in_source_paths(source.to_s))
54
+ @block = block
55
+ super(base, destination, { :recursive => true }.merge(config))
56
+ end
57
+
58
+ def invoke!
59
+ base.empty_directory given_destination, config
60
+ execute!
61
+ end
62
+
63
+ def revoke!
64
+ execute!
65
+ end
66
+
67
+ protected
68
+
69
+ def execute!
70
+ lookup = config[:recursive] ? File.join(source, '**') : source
71
+ lookup = File.join(lookup, '{*,.[a-z]*}')
72
+
73
+ Dir[lookup].sort.each do |file_source|
74
+ next if File.directory?(file_source)
75
+ file_destination = File.join(given_destination, file_source.gsub(source, '.'))
76
+ file_destination.gsub!('/./', '/')
77
+
78
+ case file_source
79
+ when /\.empty_directory$/
80
+ dirname = File.dirname(file_destination).gsub(/\/\.$/, '')
81
+ next if dirname == given_destination
82
+ base.empty_directory(dirname, config)
83
+ when /\.tt$/
84
+ destination = base.template(file_source, file_destination[0..-4], config, &@block)
85
+ else
86
+ destination = base.copy_file(file_source, file_destination, config, &@block)
87
+ end
88
+ end
89
+ end
90
+
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,134 @@
1
+ class Thor
2
+ module Actions
3
+
4
+ # Creates an empty directory.
5
+ #
6
+ # ==== Parameters
7
+ # destination<String>:: the relative path to the destination root.
8
+ # config<Hash>:: give :verbose => false to not log the status.
9
+ #
10
+ # ==== Examples
11
+ #
12
+ # empty_directory "doc"
13
+ #
14
+ def empty_directory(destination, config={})
15
+ action EmptyDirectory.new(self, destination, config)
16
+ end
17
+
18
+ # Class which holds create directory logic. This is the base class for
19
+ # other actions like create_file and directory.
20
+ #
21
+ # This implementation is based in Templater actions, created by Jonas Nicklas
22
+ # and Michael S. Klishin under MIT LICENSE.
23
+ #
24
+ class EmptyDirectory #:nodoc:
25
+ attr_reader :base, :destination, :given_destination, :relative_destination, :config
26
+
27
+ # Initializes given the source and destination.
28
+ #
29
+ # ==== Parameters
30
+ # base<Thor::Base>:: A Thor::Base instance
31
+ # source<String>:: Relative path to the source of this file
32
+ # destination<String>:: Relative path to the destination of this file
33
+ # config<Hash>:: give :verbose => false to not log the status.
34
+ #
35
+ def initialize(base, destination, config={})
36
+ @base, @config = base, { :verbose => true }.merge(config)
37
+ self.destination = destination
38
+ end
39
+
40
+ # Checks if the destination file already exists.
41
+ #
42
+ # ==== Returns
43
+ # Boolean:: true if the file exists, false otherwise.
44
+ #
45
+ def exists?
46
+ ::File.exists?(destination)
47
+ end
48
+
49
+ def invoke!
50
+ invoke_with_conflict_check do
51
+ ::FileUtils.mkdir_p(destination)
52
+ end
53
+ end
54
+
55
+ def revoke!
56
+ say_status :remove, :red
57
+ ::FileUtils.rm_rf(destination) if !pretend? && exists?
58
+ given_destination
59
+ end
60
+
61
+ protected
62
+
63
+ # Shortcut for pretend.
64
+ #
65
+ def pretend?
66
+ base.options[:pretend]
67
+ end
68
+
69
+ # Sets the absolute destination value from a relative destination value.
70
+ # It also stores the given and relative destination. Let's suppose our
71
+ # script is being executed on "dest", it sets the destination root to
72
+ # "dest". The destination, given_destination and relative_destination
73
+ # are related in the following way:
74
+ #
75
+ # inside "bar" do
76
+ # empty_directory "baz"
77
+ # end
78
+ #
79
+ # destination #=> dest/bar/baz
80
+ # relative_destination #=> bar/baz
81
+ # given_destination #=> baz
82
+ #
83
+ def destination=(destination)
84
+ if destination
85
+ @given_destination = convert_encoded_instructions(destination.to_s)
86
+ @destination = ::File.expand_path(@given_destination, base.destination_root)
87
+ @relative_destination = base.relative_to_original_destination_root(@destination)
88
+ end
89
+ end
90
+
91
+ # Filenames in the encoded form are converted. If you have a file:
92
+ #
93
+ # %class_name%.rb
94
+ #
95
+ # It gets the class name from the base and replace it:
96
+ #
97
+ # user.rb
98
+ #
99
+ def convert_encoded_instructions(filename)
100
+ filename.gsub(/%(.*?)%/) do |string|
101
+ instruction = $1.strip
102
+ base.respond_to?(instruction) ? base.send(instruction) : string
103
+ end
104
+ end
105
+
106
+ # Receives a hash of options and just execute the block if some
107
+ # conditions are met.
108
+ #
109
+ def invoke_with_conflict_check(&block)
110
+ if exists?
111
+ on_conflict_behavior(&block)
112
+ else
113
+ say_status :create, :green
114
+ block.call unless pretend?
115
+ end
116
+
117
+ destination
118
+ end
119
+
120
+ # What to do when the destination file already exists.
121
+ #
122
+ def on_conflict_behavior(&block)
123
+ say_status :exist, :blue
124
+ end
125
+
126
+ # Shortcut to say_status shell method.
127
+ #
128
+ def say_status(status, color)
129
+ base.shell.say_status status, relative_destination, color if config[:verbose]
130
+ end
131
+
132
+ end
133
+ end
134
+ end