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.
- data/.autotest +8 -0
- data/.gemtest +0 -0
- data/.gitignore +9 -0
- data/.rspec +1 -0
- data/CHANGELOG.rdoc +103 -0
- data/Gemfile +11 -0
- data/LICENSE.md +20 -0
- data/README.md +26 -0
- data/Thorfile +13 -0
- data/bin/rake2thor +86 -0
- data/bin/thor +6 -0
- data/lib/thor/actions/create_file.rb +105 -0
- data/lib/thor/actions/create_link.rb +57 -0
- data/lib/thor/actions/directory.rb +93 -0
- data/lib/thor/actions/empty_directory.rb +134 -0
- data/lib/thor/actions/file_manipulation.rb +270 -0
- data/lib/thor/actions/inject_into_file.rb +109 -0
- data/lib/thor/actions.rb +314 -0
- data/lib/thor/base.rb +598 -0
- data/lib/thor/core_ext/file_binary_read.rb +9 -0
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
- data/lib/thor/core_ext/ordered_hash.rb +100 -0
- data/lib/thor/error.rb +30 -0
- data/lib/thor/group.rb +276 -0
- data/lib/thor/invocation.rb +168 -0
- data/lib/thor/parser/argument.rb +67 -0
- data/lib/thor/parser/arguments.rb +165 -0
- data/lib/thor/parser/option.rb +120 -0
- data/lib/thor/parser/options.rb +181 -0
- data/lib/thor/parser.rb +4 -0
- data/lib/thor/rake_compat.rb +70 -0
- data/lib/thor/runner.rb +309 -0
- data/lib/thor/shell/basic.rb +302 -0
- data/lib/thor/shell/color.rb +108 -0
- data/lib/thor/shell/html.rb +121 -0
- data/lib/thor/shell.rb +88 -0
- data/lib/thor/task.rb +129 -0
- data/lib/thor/util.rb +229 -0
- data/lib/thor/version.rb +3 -0
- data/lib/thor.rb +336 -0
- data/spec/actions/create_file_spec.rb +170 -0
- data/spec/actions/directory_spec.rb +136 -0
- data/spec/actions/empty_directory_spec.rb +98 -0
- data/spec/actions/file_manipulation_spec.rb +317 -0
- data/spec/actions/inject_into_file_spec.rb +135 -0
- data/spec/actions_spec.rb +322 -0
- data/spec/base_spec.rb +274 -0
- data/spec/core_ext/hash_with_indifferent_access_spec.rb +43 -0
- data/spec/core_ext/ordered_hash_spec.rb +115 -0
- data/spec/fixtures/application.rb +2 -0
- data/spec/fixtures/bundle/execute.rb +6 -0
- data/spec/fixtures/bundle/main.thor +1 -0
- data/spec/fixtures/doc/%file_name%.rb.tt +1 -0
- data/spec/fixtures/doc/README +3 -0
- data/spec/fixtures/doc/block_helper.rb +3 -0
- data/spec/fixtures/doc/components/.empty_directory +0 -0
- data/spec/fixtures/doc/config.rb +1 -0
- data/spec/fixtures/doc/config.yaml.tt +1 -0
- data/spec/fixtures/group.thor +114 -0
- data/spec/fixtures/invoke.thor +112 -0
- data/spec/fixtures/path with spaces +0 -0
- data/spec/fixtures/script.thor +184 -0
- data/spec/fixtures/task.thor +10 -0
- data/spec/group_spec.rb +216 -0
- data/spec/invocation_spec.rb +100 -0
- data/spec/parser/argument_spec.rb +47 -0
- data/spec/parser/arguments_spec.rb +65 -0
- data/spec/parser/option_spec.rb +202 -0
- data/spec/parser/options_spec.rb +329 -0
- data/spec/rake_compat_spec.rb +72 -0
- data/spec/register_spec.rb +92 -0
- data/spec/runner_spec.rb +210 -0
- data/spec/shell/basic_spec.rb +223 -0
- data/spec/shell/color_spec.rb +41 -0
- data/spec/shell/html_spec.rb +27 -0
- data/spec/shell_spec.rb +47 -0
- data/spec/spec_helper.rb +54 -0
- data/spec/task_spec.rb +74 -0
- data/spec/thor_spec.rb +362 -0
- data/spec/util_spec.rb +163 -0
- data/thor.gemspec +25 -0
- 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
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
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
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,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
|