Module: Thor::Actions

Defined in:
lib/thor/actions.rb,
lib/thor/actions/directory.rb,
lib/thor/actions/create_file.rb,
lib/thor/actions/empty_directory.rb,
lib/thor/actions/inject_into_file.rb,
lib/thor/actions/file_manipulation.rb

Defined Under Namespace

Modules: ClassMethods Classes: CreateFile, Directory, EmptyDirectory, InjectIntoFile

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#behaviorObject

Returns the value of attribute behavior.



9
10
11
# File 'lib/thor/actions.rb', line 9

def behavior
  @behavior
end

Class Method Details

.included(base) ⇒ Object

On inclusion, add some options to base.



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/thor/actions.rb', line 13

def self.included(base) #:nodoc:
  base.extend ClassMethods
  return unless base.respond_to?(:class_option)

  base.class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime,
                              :desc => "Run but do not make any changes"

  base.class_option :force, :type => :boolean, :aliases => "-f", :group => :runtime,
                            :desc => "Overwrite files that already exist"

  base.class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime,
                           :desc => "Skip files that already exist"

  base.class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime,
                            :desc => "Supress status output"
end

Instance Method Details

#action(instance) ⇒ Object

Wraps an action object and call it accordingly to the thor class behavior.



80
81
82
83
84
85
86
# File 'lib/thor/actions.rb', line 80

def action(instance) #:nodoc:
  if behavior == :revoke
    instance.revoke!
  else
    instance.invoke!
  end
end

#append_file(path, data = nil, config = {}, &block) ⇒ Object

Append text to a file.

Parameters

path

path of the file to be changed

data

the data to append to the file, can be also given as a block.

config

give :verbose => false to not log the status.

Example

append_file 'config/environments/test.rb', 'config.gem "rspec"'


137
138
139
140
141
142
# File 'lib/thor/actions/file_manipulation.rb', line 137

def append_file(path, data=nil, config={}, &block)
  return unless behavior == :invoke
  path = File.expand_path(path, destination_root)
  say_status :append, relative_to_original_destination_root(path), config.fetch(:verbose, true)
  File.open(path, 'ab') { |file| file.write(data || block.call) } unless options[:pretend]
end

#apply(path, config = {}) ⇒ Object

Loads an external file and execute it in the instance binding.

Parameters

path

The path to the file to execute. Can be a web address or a relative path from the source root.

Examples

apply "http://gist.github.com/103208"

apply "recipes/jquery.rb"


175
176
177
178
179
180
181
182
183
# File 'lib/thor/actions.rb', line 175

def apply(path, config={})
  verbose = config.fetch(:verbose, true)
  path    = find_in_source_paths(path) unless path =~ /^http\:\/\//

  say_status :apply, path, verbose
  shell.padding += 1 if verbose
  instance_eval(open(path).read)
  shell.padding -= 1 if verbose
end

#chmod(path, mode, config = {}) ⇒ Object

Changes the mode of the given file or directory.

Parameters

mode

the file mode

path

the name of the file to change mode

config

give :verbose => false to not log the status.

Example

chmod "script/*", 0755


96
97
98
99
100
101
# File 'lib/thor/actions/file_manipulation.rb', line 96

def chmod(path, mode, config={})
  return unless behavior == :invoke
  path = File.expand_path(path, destination_root)
  say_status :chmod, relative_to_original_destination_root(path), config.fetch(:verbose, true)
  FileUtils.chmod_R(mode, path) unless options[:pretend]
end

#copy_file(source, destination = nil, config = {}) ⇒ Object

Copies the file from the relative source to the relative destination. If the destination is not given it's assumed to be equal to the source.

Parameters

source

the relative path to the source root.

destination

the relative path to the destination root.

config

give :verbose => false to not log the status.

Examples

copy_file "README", "doc/README"

copy_file "doc/README"


21
22
23
24
25
26
27
28
# File 'lib/thor/actions/file_manipulation.rb', line 21

def copy_file(source, destination=nil, config={})
  destination ||= source
  source = File.expand_path(find_in_source_paths(source.to_s))

  create_file destination, nil, config do
    File.read(source)
  end
end

#create_file(destination, data = nil, config = {}, &block) ⇒ Object Also known as: add_file

Create a new file relative to the destination root with the given data, which is the return value of a block or a data string.

Parameters

destination

the relative path to the destination root.

data<String|NilClass>

the data to append to the file.

config

give :verbose => false to not log the status.

Examples

create_file "lib/fun_party.rb" do
hostname = ask("What is the virtual hostname I should use?")
"vhost.name = #{hostname}"
end

create_file "config/apach.conf", "your apache config"


23
24
25
# File 'lib/thor/actions/create_file.rb', line 23

def create_file(destination, data=nil, config={}, &block)
  action CreateFile.new(self, destination, block || data.to_s, config)
end

#destination_rootObject

Returns the root for this thor class (also aliased as destination root).



90
91
92
# File 'lib/thor/actions.rb', line 90

def destination_root
  @destination_stack.last
end

#destination_root=(root) ⇒ Object

Sets the root for this thor class. Relatives path are added to the directory where the script was invoked and expanded.



97
98
99
100
# File 'lib/thor/actions.rb', line 97

def destination_root=(root)
  @destination_stack ||= []
  @destination_stack[0] = File.expand_path(root || '')
end

#directory(source, destination = nil, config = {}) ⇒ Object

Copies recursively the files from source directory to root directory. If any of the files finishes with .tt, it's considered to be a template and is placed in the destination without the extension .tt. If any empty directory is found, it's copied and all .empty_directory files are ignored. Remember that file paths can also be encoded, let's suppose a doc directory with the following files:

doc/
components/.empty_directory
README
rdoc.rb.tt
app_name%.rb

When invoked as:

directory "doc"

It will create a doc directory in the destination with the following files (assuming that the app_name is "blog"):

doc/
components/
README
rdoc.rb
blog.rb

Parameters

source

the relative path to the source root.

destination

the relative path to the destination root.

config

give :verbose => false to not log the status. If :recursive => false, does not look for paths recursively.

Examples

directory "doc"
directory "doc", "docs", :recursive => false


43
44
45
# File 'lib/thor/actions/directory.rb', line 43

def directory(source, destination=nil, config={})
  action Directory.new(self, source, destination || source, config)
end

#empty_directory(destination, config = {}) ⇒ Object

Creates an empty directory.

Parameters

destination

the relative path to the destination root.

config

give :verbose => false to not log the status.

Examples

empty_directory "doc"


14
15
16
# File 'lib/thor/actions/empty_directory.rb', line 14

def empty_directory(destination, config={})
  action EmptyDirectory.new(self, destination, config)
end

#find_in_source_paths(file) ⇒ Object

Receives a file or directory and search for it in the source paths.



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/thor/actions.rb', line 118

def find_in_source_paths(file)
  relative_root = relative_to_original_destination_root(destination_root, false)

  source_paths.each do |source|
    source_file = File.expand_path(file, File.join(source, relative_root))
    return source_file if File.exists?(source_file)
  end

  if source_paths.empty?
    raise Error, "You don't have any source path defined for class #{self.class.name}. To fix this, " <<
                 "you can define a source_root in your class."
  else
    raise Error, "Could not find #{file.inspect} in source paths."
  end
end

#get(source, destination = nil, config = {}, &block) ⇒ Object

Gets the content at the given address and places it at the given relative destination. If a block is given instead of destination, the content of the url is yielded and used as location.

Parameters

source

the address of the given content.

destination

the relative path to the destination root.

config

give :verbose => false to not log the status.

Examples

get "http://gist.github.com/103208", "doc/README"

get "http://gist.github.com/103208" do |content|
content.split("\n").first
end


47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/thor/actions/file_manipulation.rb', line 47

def get(source, destination=nil, config={}, &block)
  source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ /^http\:\/\//
  render = open(source).read

  destination ||= if block_given?
    block.arity == 1 ? block.call(render) : block.call
  else
    File.basename(source)
  end

  create_file destination, render, config
end

#gsub_file(path, flag, *args, &block) ⇒ Object

Run a regular expression replacement on a file.

Parameters

path

path of the file to be changed

flag<Regexp|String>

the regexp or string to be replaced

replacement

the replacement, can be also given as a block

config

give :verbose => false to not log the status.

Example

gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1'

gsub_file 'README', /rake/, :green do |match|
match << " no more. Use thor!"
end


160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/thor/actions/file_manipulation.rb', line 160

def gsub_file(path, flag, *args, &block)
  return unless behavior == :invoke
  config = args.last.is_a?(Hash) ? args.pop : {}

  path = File.expand_path(path, destination_root)
  say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true)

  unless options[:pretend]
    content = File.read(path)
    content.gsub!(flag, *args, &block)
    File.open(path, 'wb') { |file| file.write(content) }
  end
end

#in_rootObject

Goes to the root and execute the given block.



159
160
161
# File 'lib/thor/actions.rb', line 159

def in_root
  inside(@destination_stack.first) { yield }
end

#initialize(args = [], options = {}, config = {}) ⇒ Object

Extends initializer to add more configuration options.

Configuration

behavior

The actions default behavior. Can be :invoke or :revoke. It also accepts :force, :skip and :pretend to set the behavior and the respective option.

destination_root

The root directory needed for some actions.



63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/thor/actions.rb', line 63

def initialize(args=[], options={}, config={})
  self.behavior = case config[:behavior].to_s
    when "force", "skip"
      _cleanup_options_and_set(options, config[:behavior])
      :invoke
    when "revoke"
      :revoke
    else
      :invoke
  end

  super
  self.destination_root = config[:destination_root]
end

#inject_into_file(destination, *args, &block) ⇒ Object

Injects the given content into a file. Different from append_file, prepend_file and gsub_file, this method is reversible. By this reason, the flag can only be strings. gsub_file is your friend if you need to deal with more complex cases.

Parameters

destination

Relative path to the destination root

data

Data to add to the file. Can be given as a block.

config

give :verbose => false to not log the status and the flag for injection (:after or :before).

Examples

inject_into_file "config/environment.rb", "config.gem thor", :after => "Rails::Initializer.run do |config|\n"

inject_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do
gems = ask "Which gems would you like to add?"
gems.split(" ").map{ |gem| "  config.gem #{gem}" }.join("\n")
end


26
27
28
29
30
31
32
33
34
35
# File 'lib/thor/actions/inject_into_file.rb', line 26

def inject_into_file(destination, *args, &block)
  if block_given?
    data, config = block, args.shift
  else
    data, config = args.shift, args.shift
  end

  log_status = args.empty? || args.pop
  action InjectIntoFile.new(self, destination, data, config)
end

#inside(dir = '', config = {}, &block) ⇒ Object

Do something in the root or on a provided subfolder. If a relative path is given it's referenced from the current root. The full path is yielded to the block you provide. The path is set back to the previous path when the method exits.

Parameters

dir

the directory to move to.

config

give :verbose => true to log and use padding.



143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/thor/actions.rb', line 143

def inside(dir='', config={}, &block)
  verbose = config.fetch(:verbose, false)

  say_status :inside, dir, verbose
  shell.padding += 1 if verbose
  @destination_stack.push File.expand_path(dir, destination_root)

  FileUtils.mkdir_p(destination_root) unless File.exist?(destination_root)
  FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield }

  @destination_stack.pop
  shell.padding -= 1 if verbose
end

#prepend_file(path, data = nil, config = {}, &block) ⇒ Object

Prepend text to a file.

Parameters

path

path of the file to be changed

data

the data to prepend to the file, can be also given as a block.

config

give :verbose => false to not log the status.

Example

prepend_file 'config/environments/test.rb', 'config.gem "rspec"'


114
115
116
117
118
119
120
121
122
123
124
# File 'lib/thor/actions/file_manipulation.rb', line 114

def prepend_file(path, data=nil, config={}, &block)
  return unless behavior == :invoke
  path = File.expand_path(path, destination_root)
  say_status :prepend, relative_to_original_destination_root(path), config.fetch(:verbose, true)

  unless options[:pretend]
    content = data || block.call
    content << File.read(path)
    File.open(path, 'wb') { |file| file.write(content) }
  end
end

#relative_to_original_destination_root(path, remove_dot = true) ⇒ Object

Returns the given path relative to the absolute root (ie, root where the script started).



105
106
107
108
# File 'lib/thor/actions.rb', line 105

def relative_to_original_destination_root(path, remove_dot=true)
  path = path.gsub(@destination_stack[0], '.')
  remove_dot ? (path[2..-1] || '') : path
end

#remove_file(path, config = {}) ⇒ Object Also known as: remove_dir

Removes a file at the given location.

Parameters

path

path of the file to be changed

config

give :verbose => false to not log the status.

Example

remove_file 'README'
remove_file 'app/controllers/application_controller.rb'


185
186
187
188
189
190
191
# File 'lib/thor/actions/file_manipulation.rb', line 185

def remove_file(path, config={})
  return unless behavior == :invoke
  path  = File.expand_path(path, destination_root)

  say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true)
  ::FileUtils.rm_rf(path) if !options[:pretend] && File.exists?(path)
end

#run(command, config = {}) ⇒ Object

Executes a command.

Parameters

command

the command to be executed.

config

give :verbose => false to not log the status. Specify :with to append an executable to command executation.

Example

inside('vendor') do
run('ln -s ~/edge rails')
end


198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/thor/actions.rb', line 198

def run(command, config={})
  return unless behavior == :invoke

  destination = relative_to_original_destination_root(destination_root, false)
  desc = "#{command} from #{destination.inspect}"

  if config[:with]
    desc = "#{File.basename(config[:with].to_s)} #{desc}"
    command = "#{config[:with]} #{command}"
  end

  say_status :run, desc, config.fetch(:verbose, true)
  system(command) unless options[:pretend]
end

#run_ruby_script(command, config = {}) ⇒ Object

Executes a ruby script (taking into account WIN32 platform quirks).

Parameters

command

the command to be executed.

config

give :verbose => false to not log the status.



219
220
221
222
# File 'lib/thor/actions.rb', line 219

def run_ruby_script(command, config={})
  return unless behavior == :invoke
  run "#{command}", config.merge(:with => Thor::Util.ruby_command)
end

#source_pathsObject

Holds source paths in instance so they can be manipulated.



112
113
114
# File 'lib/thor/actions.rb', line 112

def source_paths
  @source_paths ||= self.class.source_paths_for_search
end

#template(source, destination = nil, config = {}) ⇒ Object

Gets an ERB template at the relative source, executes it and makes a copy at the relative destination. If the destination is not given it's assumed to be equal to the source removing .tt from the filename.

Parameters

source

the relative path to the source root.

destination

the relative path to the destination root.

config

give :verbose => false to not log the status.

Examples

template "README", "doc/README"

template "doc/README"


75
76
77
78
79
80
81
82
83
# File 'lib/thor/actions/file_manipulation.rb', line 75

def template(source, destination=nil, config={})
  destination ||= source
  source  = File.expand_path(find_in_source_paths(source.to_s))
  context = instance_eval('binding')

  create_file destination, nil, config do
    ERB.new(::File.read(source), nil, '-').result(context)
  end
end

#thor(task, *args) ⇒ Object

Run a thor command. A hash of options can be given and it's converted to switches.

Parameters

task

the task to be invoked

args

arguments to the task

config

give :verbose => false to not log the status. Other options are given as parameter to Thor.

Examples

thor :install, "http://gist.github.com/103208"
#=> thor install http://gist.github.com/103208

thor :list, :all => true, :substring => 'rails'
#=> thor list --all --substring=rails


241
242
243
244
245
246
247
248
249
250
# File 'lib/thor/actions.rb', line 241

def thor(task, *args)
  config  = args.last.is_a?(Hash) ? args.pop : {}
  verbose = config.key?(:verbose) ? config.delete(:verbose) : true

  args.unshift task
  args.push Thor::Options.to_switches(config)
  command = args.join(' ').strip

  run command, :with => :thor, :verbose => verbose
end