thor 0.18.1 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +13 -7
- data/Thorfile +4 -5
- data/bin/thor +1 -1
- data/lib/thor.rb +78 -67
- data/lib/thor/actions.rb +57 -56
- data/lib/thor/actions/create_file.rb +33 -35
- data/lib/thor/actions/create_link.rb +2 -3
- data/lib/thor/actions/directory.rb +37 -38
- data/lib/thor/actions/empty_directory.rb +67 -69
- data/lib/thor/actions/file_manipulation.rb +17 -15
- data/lib/thor/actions/inject_into_file.rb +27 -29
- data/lib/thor/base.rb +193 -189
- data/lib/thor/command.rb +20 -23
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +21 -24
- data/lib/thor/core_ext/io_binary_read.rb +2 -4
- data/lib/thor/core_ext/ordered_hash.rb +9 -11
- data/lib/thor/error.rb +5 -1
- data/lib/thor/group.rb +53 -54
- data/lib/thor/invocation.rb +44 -38
- data/lib/thor/line_editor.rb +17 -0
- data/lib/thor/line_editor/basic.rb +35 -0
- data/lib/thor/line_editor/readline.rb +88 -0
- data/lib/thor/parser.rb +4 -4
- data/lib/thor/parser/argument.rb +28 -29
- data/lib/thor/parser/arguments.rb +102 -98
- data/lib/thor/parser/option.rb +26 -22
- data/lib/thor/parser/options.rb +86 -86
- data/lib/thor/rake_compat.rb +9 -10
- data/lib/thor/runner.rb +141 -141
- data/lib/thor/shell.rb +27 -34
- data/lib/thor/shell/basic.rb +91 -63
- data/lib/thor/shell/color.rb +44 -43
- data/lib/thor/shell/html.rb +59 -60
- data/lib/thor/util.rb +24 -27
- data/lib/thor/version.rb +1 -1
- data/spec/actions/create_file_spec.rb +25 -27
- data/spec/actions/create_link_spec.rb +19 -18
- data/spec/actions/directory_spec.rb +31 -31
- data/spec/actions/empty_directory_spec.rb +18 -18
- data/spec/actions/file_manipulation_spec.rb +38 -28
- data/spec/actions/inject_into_file_spec.rb +13 -13
- data/spec/actions_spec.rb +43 -43
- data/spec/base_spec.rb +45 -38
- data/spec/command_spec.rb +13 -14
- data/spec/core_ext/hash_with_indifferent_access_spec.rb +19 -19
- data/spec/core_ext/ordered_hash_spec.rb +6 -6
- data/spec/exit_condition_spec.rb +4 -4
- data/spec/fixtures/invoke.thor +19 -0
- data/spec/fixtures/script.thor +1 -1
- data/spec/group_spec.rb +30 -24
- data/spec/helper.rb +28 -15
- data/spec/invocation_spec.rb +39 -19
- data/spec/line_editor/basic_spec.rb +28 -0
- data/spec/line_editor/readline_spec.rb +69 -0
- data/spec/line_editor_spec.rb +43 -0
- data/spec/parser/argument_spec.rb +12 -12
- data/spec/parser/arguments_spec.rb +11 -11
- data/spec/parser/option_spec.rb +33 -25
- data/spec/parser/options_spec.rb +66 -52
- data/spec/quality_spec.rb +75 -0
- data/spec/rake_compat_spec.rb +10 -10
- data/spec/register_spec.rb +60 -30
- data/spec/runner_spec.rb +67 -62
- data/spec/sandbox/application.rb +2 -0
- data/spec/sandbox/app{1}/README +3 -0
- data/spec/sandbox/bundle/execute.rb +6 -0
- data/spec/sandbox/bundle/main.thor +1 -0
- data/spec/sandbox/command.thor +10 -0
- data/spec/sandbox/doc/%file_name%.rb.tt +1 -0
- data/spec/sandbox/doc/COMMENTER +11 -0
- data/spec/sandbox/doc/README +3 -0
- data/spec/sandbox/doc/block_helper.rb +3 -0
- data/spec/sandbox/doc/config.rb +1 -0
- data/spec/sandbox/doc/config.yaml.tt +1 -0
- data/spec/sandbox/doc/excluding/%file_name%.rb.tt +1 -0
- data/spec/sandbox/enum.thor +10 -0
- data/spec/sandbox/group.thor +128 -0
- data/spec/sandbox/invoke.thor +131 -0
- data/spec/sandbox/path with spaces b/data/spec/sandbox/path with → spaces +0 -0
- data/spec/sandbox/preserve/script.sh +3 -0
- data/spec/sandbox/script.thor +220 -0
- data/spec/sandbox/subcommand.thor +17 -0
- data/spec/shell/basic_spec.rb +107 -86
- data/spec/shell/color_spec.rb +32 -8
- data/spec/shell/html_spec.rb +3 -4
- data/spec/shell_spec.rb +7 -7
- data/spec/subcommand_spec.rb +20 -2
- data/spec/thor_spec.rb +111 -97
- data/spec/util_spec.rb +30 -30
- data/thor.gemspec +14 -14
- metadata +69 -25
@@ -1,8 +1,7 @@
|
|
1
|
-
require
|
1
|
+
require "thor/actions/empty_directory"
|
2
2
|
|
3
3
|
class Thor
|
4
4
|
module Actions
|
5
|
-
|
6
5
|
# Create a new file relative to the destination root with the given data,
|
7
6
|
# which is the return value of a block or a data string.
|
8
7
|
#
|
@@ -25,7 +24,7 @@ class Thor
|
|
25
24
|
data = args.first
|
26
25
|
action CreateFile.new(self, destination, block || data.to_s, config)
|
27
26
|
end
|
28
|
-
|
27
|
+
alias_method :add_file, :create_file
|
29
28
|
|
30
29
|
# CreateFile is a subset of Template, which instead of rendering a file with
|
31
30
|
# ERB, it gets the content from the user.
|
@@ -33,7 +32,7 @@ class Thor
|
|
33
32
|
class CreateFile < EmptyDirectory #:nodoc:
|
34
33
|
attr_reader :data
|
35
34
|
|
36
|
-
def initialize(base, destination, data, config={})
|
35
|
+
def initialize(base, destination, data, config = {})
|
37
36
|
@data = data
|
38
37
|
super(base, destination, config)
|
39
38
|
end
|
@@ -60,46 +59,45 @@ class Thor
|
|
60
59
|
def invoke!
|
61
60
|
invoke_with_conflict_check do
|
62
61
|
FileUtils.mkdir_p(File.dirname(destination))
|
63
|
-
File.open(destination,
|
62
|
+
File.open(destination, "wb") { |f| f.write render }
|
64
63
|
end
|
65
64
|
given_destination
|
66
65
|
end
|
67
66
|
|
68
|
-
|
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
|
67
|
+
protected
|
80
68
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
69
|
+
# Now on conflict we check if the file is identical or not.
|
70
|
+
#
|
71
|
+
def on_conflict_behavior(&block)
|
72
|
+
if identical?
|
73
|
+
say_status :identical, :blue
|
74
|
+
else
|
75
|
+
options = base.options.merge(config)
|
76
|
+
force_or_skip_or_conflict(options[:force], options[:skip], &block)
|
95
77
|
end
|
78
|
+
end
|
96
79
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
80
|
+
# If force is true, run the action, otherwise check if it's not being
|
81
|
+
# skipped. If both are false, show the file_collision menu, if the menu
|
82
|
+
# returns true, force it, otherwise skip.
|
83
|
+
#
|
84
|
+
def force_or_skip_or_conflict(force, skip, &block)
|
85
|
+
if force
|
86
|
+
say_status :force, :yellow
|
87
|
+
block.call unless pretend?
|
88
|
+
elsif skip
|
89
|
+
say_status :skip, :yellow
|
90
|
+
else
|
91
|
+
say_status :conflict, :red
|
92
|
+
force_or_skip_or_conflict(force_on_collision?, true, &block)
|
101
93
|
end
|
94
|
+
end
|
102
95
|
|
96
|
+
# Shows the file collision menu to the user and gets the result.
|
97
|
+
#
|
98
|
+
def force_on_collision?
|
99
|
+
base.shell.file_collision(destination) { render }
|
100
|
+
end
|
103
101
|
end
|
104
102
|
end
|
105
103
|
end
|
@@ -1,8 +1,7 @@
|
|
1
|
-
require
|
1
|
+
require "thor/actions/create_file"
|
2
2
|
|
3
3
|
class Thor
|
4
4
|
module Actions
|
5
|
-
|
6
5
|
# Create a new file relative to the destination root from the given source.
|
7
6
|
#
|
8
7
|
# ==== Parameters
|
@@ -20,7 +19,7 @@ class Thor
|
|
20
19
|
source = args.first
|
21
20
|
action CreateLink.new(self, destination, source, config)
|
22
21
|
end
|
23
|
-
|
22
|
+
alias_method :add_link, :create_link
|
24
23
|
|
25
24
|
# CreateLink is a subset of CreateFile, which instead of taking a block of
|
26
25
|
# data, just takes a source string from the user.
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "thor/actions/empty_directory"
|
2
2
|
|
3
3
|
class Thor
|
4
4
|
module Actions
|
@@ -55,10 +55,10 @@ class Thor
|
|
55
55
|
class Directory < EmptyDirectory #:nodoc:
|
56
56
|
attr_reader :source
|
57
57
|
|
58
|
-
def initialize(base, source, destination=nil, config={}, &block)
|
58
|
+
def initialize(base, source, destination = nil, config = {}, &block)
|
59
59
|
@source = File.expand_path(base.find_in_source_paths(source.to_s))
|
60
60
|
@block = block
|
61
|
-
super(base, destination, {
|
61
|
+
super(base, destination, {:recursive => true}.merge(config))
|
62
62
|
end
|
63
63
|
|
64
64
|
def invoke!
|
@@ -70,50 +70,49 @@ class Thor
|
|
70
70
|
execute!
|
71
71
|
end
|
72
72
|
|
73
|
-
|
73
|
+
protected
|
74
74
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
75
|
+
def execute! # rubocop:disable MethodLength
|
76
|
+
lookup = Util.escape_globs(source)
|
77
|
+
lookup = config[:recursive] ? File.join(lookup, "**") : lookup
|
78
|
+
lookup = file_level_lookup(lookup)
|
79
79
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
80
|
+
files(lookup).sort.each do |file_source|
|
81
|
+
next if File.directory?(file_source)
|
82
|
+
next if config[:exclude_pattern] && file_source.match(config[:exclude_pattern])
|
83
|
+
file_destination = File.join(given_destination, file_source.gsub(source, "."))
|
84
|
+
file_destination.gsub!("/./", "/")
|
85
85
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
end
|
86
|
+
case file_source
|
87
|
+
when /\.empty_directory$/
|
88
|
+
dirname = File.dirname(file_destination).gsub(/\/\.$/, "")
|
89
|
+
next if dirname == given_destination
|
90
|
+
base.empty_directory(dirname, config)
|
91
|
+
when /#{TEMPLATE_EXTNAME}$/
|
92
|
+
base.template(file_source, file_destination[0..-4], config, &@block)
|
93
|
+
else
|
94
|
+
base.copy_file(file_source, file_destination, config, &@block)
|
96
95
|
end
|
97
96
|
end
|
97
|
+
end
|
98
98
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
def files(lookup)
|
105
|
-
Dir[lookup]
|
106
|
-
end
|
107
|
-
else
|
108
|
-
def file_level_lookup(previous_lookup)
|
109
|
-
File.join(previous_lookup, '*')
|
110
|
-
end
|
99
|
+
if RUBY_VERSION < "2.0"
|
100
|
+
def file_level_lookup(previous_lookup)
|
101
|
+
File.join(previous_lookup, "{*,.[a-z]*}")
|
102
|
+
end
|
111
103
|
|
112
|
-
|
113
|
-
|
114
|
-
|
104
|
+
def files(lookup)
|
105
|
+
Dir[lookup]
|
106
|
+
end
|
107
|
+
else
|
108
|
+
def file_level_lookup(previous_lookup)
|
109
|
+
File.join(previous_lookup, "*")
|
115
110
|
end
|
116
111
|
|
112
|
+
def files(lookup)
|
113
|
+
Dir.glob(lookup, File::FNM_DOTMATCH)
|
114
|
+
end
|
115
|
+
end
|
117
116
|
end
|
118
117
|
end
|
119
118
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
class Thor
|
2
2
|
module Actions
|
3
|
-
|
4
3
|
# Creates an empty directory.
|
5
4
|
#
|
6
5
|
# ==== Parameters
|
@@ -11,7 +10,7 @@ class Thor
|
|
11
10
|
#
|
12
11
|
# empty_directory "doc"
|
13
12
|
#
|
14
|
-
def empty_directory(destination, config={})
|
13
|
+
def empty_directory(destination, config = {})
|
15
14
|
action EmptyDirectory.new(self, destination, config)
|
16
15
|
end
|
17
16
|
|
@@ -32,8 +31,8 @@ class Thor
|
|
32
31
|
# destination<String>:: Relative path to the destination of this file
|
33
32
|
# config<Hash>:: give :verbose => false to not log the status.
|
34
33
|
#
|
35
|
-
def initialize(base, destination, config={})
|
36
|
-
@base, @config = base, {
|
34
|
+
def initialize(base, destination, config = {})
|
35
|
+
@base, @config = base, {:verbose => true}.merge(config)
|
37
36
|
self.destination = destination
|
38
37
|
end
|
39
38
|
|
@@ -43,7 +42,7 @@ class Thor
|
|
43
42
|
# Boolean:: true if the file exists, false otherwise.
|
44
43
|
#
|
45
44
|
def exists?
|
46
|
-
::File.
|
45
|
+
::File.exist?(destination)
|
47
46
|
end
|
48
47
|
|
49
48
|
def invoke!
|
@@ -58,80 +57,79 @@ class Thor
|
|
58
57
|
given_destination
|
59
58
|
end
|
60
59
|
|
61
|
-
|
60
|
+
protected
|
62
61
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
62
|
+
# Shortcut for pretend.
|
63
|
+
#
|
64
|
+
def pretend?
|
65
|
+
base.options[:pretend]
|
66
|
+
end
|
68
67
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
end
|
68
|
+
# Sets the absolute destination value from a relative destination value.
|
69
|
+
# It also stores the given and relative destination. Let's suppose our
|
70
|
+
# script is being executed on "dest", it sets the destination root to
|
71
|
+
# "dest". The destination, given_destination and relative_destination
|
72
|
+
# are related in the following way:
|
73
|
+
#
|
74
|
+
# inside "bar" do
|
75
|
+
# empty_directory "baz"
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# destination #=> dest/bar/baz
|
79
|
+
# relative_destination #=> bar/baz
|
80
|
+
# given_destination #=> baz
|
81
|
+
#
|
82
|
+
def destination=(destination)
|
83
|
+
if destination
|
84
|
+
@given_destination = convert_encoded_instructions(destination.to_s)
|
85
|
+
@destination = ::File.expand_path(@given_destination, base.destination_root)
|
86
|
+
@relative_destination = base.relative_to_original_destination_root(@destination)
|
89
87
|
end
|
88
|
+
end
|
90
89
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
end
|
90
|
+
# Filenames in the encoded form are converted. If you have a file:
|
91
|
+
#
|
92
|
+
# %file_name%.rb
|
93
|
+
#
|
94
|
+
# It calls #file_name from the base and replaces %-string with the
|
95
|
+
# return value (should be String) of #file_name:
|
96
|
+
#
|
97
|
+
# user.rb
|
98
|
+
#
|
99
|
+
# The method referenced can be either public or private.
|
100
|
+
#
|
101
|
+
def convert_encoded_instructions(filename)
|
102
|
+
filename.gsub(/%(.*?)%/) do |initial_string|
|
103
|
+
method = $1.strip
|
104
|
+
base.respond_to?(method, true) ? base.send(method) : initial_string
|
107
105
|
end
|
106
|
+
end
|
108
107
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
end
|
119
|
-
|
120
|
-
destination
|
108
|
+
# Receives a hash of options and just execute the block if some
|
109
|
+
# conditions are met.
|
110
|
+
#
|
111
|
+
def invoke_with_conflict_check(&block)
|
112
|
+
if exists?
|
113
|
+
on_conflict_behavior(&block)
|
114
|
+
else
|
115
|
+
say_status :create, :green
|
116
|
+
block.call unless pretend?
|
121
117
|
end
|
122
118
|
|
123
|
-
|
124
|
-
|
125
|
-
def on_conflict_behavior(&block)
|
126
|
-
say_status :exist, :blue
|
127
|
-
end
|
119
|
+
destination
|
120
|
+
end
|
128
121
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
122
|
+
# What to do when the destination file already exists.
|
123
|
+
#
|
124
|
+
def on_conflict_behavior(&block)
|
125
|
+
say_status :exist, :blue
|
126
|
+
end
|
134
127
|
|
128
|
+
# Shortcut to say_status shell method.
|
129
|
+
#
|
130
|
+
def say_status(status, color)
|
131
|
+
base.shell.say_status status, relative_destination, color if config[:verbose]
|
132
|
+
end
|
135
133
|
end
|
136
134
|
end
|
137
135
|
end
|
@@ -1,9 +1,8 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "erb"
|
2
|
+
require "open-uri"
|
3
3
|
|
4
4
|
class Thor
|
5
5
|
module Actions
|
6
|
-
|
7
6
|
# Copies the file from the relative source to the relative destination. If
|
8
7
|
# the destination is not given it's assumed to be equal to the source.
|
9
8
|
#
|
@@ -79,8 +78,8 @@ class Thor
|
|
79
78
|
config = args.last.is_a?(Hash) ? args.pop : {}
|
80
79
|
destination = args.first
|
81
80
|
|
82
|
-
source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~
|
83
|
-
render = open(source) {|input| input.binmode.read }
|
81
|
+
source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ %r{^https?\://}
|
82
|
+
render = open(source) { |input| input.binmode.read }
|
84
83
|
|
85
84
|
destination ||= if block_given?
|
86
85
|
block.arity == 1 ? block.call(render) : block.call
|
@@ -108,13 +107,13 @@ class Thor
|
|
108
107
|
#
|
109
108
|
def template(source, *args, &block)
|
110
109
|
config = args.last.is_a?(Hash) ? args.pop : {}
|
111
|
-
destination = args.first || source.sub(
|
110
|
+
destination = args.first || source.sub(/#{TEMPLATE_EXTNAME}$/, "")
|
112
111
|
|
113
112
|
source = File.expand_path(find_in_source_paths(source.to_s))
|
114
|
-
context = instance_eval(
|
113
|
+
context = instance_eval("binding")
|
115
114
|
|
116
115
|
create_file destination, nil, config do
|
117
|
-
content = ERB.new(::File.binread(source), nil,
|
116
|
+
content = ERB.new(::File.binread(source), nil, "-", "@output_buffer").result(context)
|
118
117
|
content = block.call(content) if block
|
119
118
|
content
|
120
119
|
end
|
@@ -131,7 +130,7 @@ class Thor
|
|
131
130
|
#
|
132
131
|
# chmod "script/server", 0755
|
133
132
|
#
|
134
|
-
def chmod(path, mode, config={})
|
133
|
+
def chmod(path, mode, config = {})
|
135
134
|
return unless behavior == :invoke
|
136
135
|
path = File.expand_path(path, destination_root)
|
137
136
|
say_status :chmod, relative_to_original_destination_root(path), config.fetch(:verbose, true)
|
@@ -231,7 +230,7 @@ class Thor
|
|
231
230
|
unless options[:pretend]
|
232
231
|
content = File.binread(path)
|
233
232
|
content.gsub!(flag, *args, &block)
|
234
|
-
File.open(path,
|
233
|
+
File.open(path, "wb") { |file| file.write(content) }
|
235
234
|
end
|
236
235
|
end
|
237
236
|
|
@@ -284,17 +283,20 @@ class Thor
|
|
284
283
|
# remove_file 'README'
|
285
284
|
# remove_file 'app/controllers/application_controller.rb'
|
286
285
|
#
|
287
|
-
def remove_file(path, config={})
|
286
|
+
def remove_file(path, config = {})
|
288
287
|
return unless behavior == :invoke
|
289
288
|
path = File.expand_path(path, destination_root)
|
290
289
|
|
291
290
|
say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true)
|
292
|
-
::FileUtils.rm_rf(path) if !options[:pretend] && File.
|
291
|
+
::FileUtils.rm_rf(path) if !options[:pretend] && File.exist?(path)
|
293
292
|
end
|
294
|
-
|
293
|
+
alias_method :remove_dir, :remove_file
|
295
294
|
|
296
|
-
private
|
297
295
|
attr_accessor :output_buffer
|
296
|
+
private :output_buffer, :output_buffer=
|
297
|
+
|
298
|
+
private
|
299
|
+
|
298
300
|
def concat(string)
|
299
301
|
@output_buffer.concat(string)
|
300
302
|
end
|
@@ -303,7 +305,7 @@ class Thor
|
|
303
305
|
with_output_buffer { block.call(*args) }
|
304
306
|
end
|
305
307
|
|
306
|
-
def with_output_buffer(buf =
|
308
|
+
def with_output_buffer(buf = "") #:nodoc:
|
307
309
|
self.output_buffer, old_buffer = buf, output_buffer
|
308
310
|
yield
|
309
311
|
output_buffer
|