module Thor::Actions
def self.included(base) #:nodoc:
def self.included(base) #:nodoc: base.extend ClassMethods end
def _cleanup_options_and_set(options, key) #:nodoc:
def _cleanup_options_and_set(options, key) #:nodoc: case options when Array %w(--force -f --skip -s).each { |i| options.delete(i) } options << "--#{key}" when Hash [:force, :skip, "force", "skip"].each { |i| options.delete(i) } options.merge!(key => true) end end
def _shared_configuration #:nodoc:
Allow current root to be shared between invocations.
def _shared_configuration #:nodoc: super.merge!(:destination_root => self.destination_root) end
def action(instance) #:nodoc:
Wraps an action object and call it accordingly to the thor class behavior.
def action(instance) #:nodoc: if behavior == :revoke instance.revoke! else instance.invoke! end end
def append_file(path, *args, &block)
end
'config.gem "rspec"'
append_file 'config/environments/test.rb' do
append_file 'config/environments/test.rb', 'config.gem "rspec"'
==== Example
config
data
path
==== Parameters
Append text to a file. Since it depends on inject_into_file, it's reversible.
def append_file(path, *args, &block) config = args.last.is_a?(Hash) ? args.pop : {} config.merge!(:before => /\z/) inject_into_file(path, *(args << config), &block) end
def apply(path, config={})
apply "recipes/jquery.rb"
apply "http://gist.github.com/103208"
==== Examples
a relative path from the source root.
path
==== Parameters
Loads an external file and execute it in the instance binding.
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 if URI(path).is_a?(URI::HTTP) contents = open(path, "Accept" => "application/x-thor-template") {|io| io.read } else contents = open(path) {|io| io.read } end instance_eval(contents, path) shell.padding -= 1 if verbose end
def chmod(path, mode, config={})
chmod "script/*", 0755
==== Example
config
path
mode
==== Parameters
Changes the mode of the given file or directory.
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
def copy_file(source, destination=nil, config={}, &block)
copy_file "doc/README"
copy_file "README", "doc/README"
==== Examples
config
destination
source
==== Parameters
the destination is not given it's assumed to be equal to the source.
Copies the file from the relative source to the relative destination. If
def copy_file(source, destination=nil, config={}, &block) destination ||= source source = File.expand_path(find_in_source_paths(source.to_s)) create_file destination, nil, config do content = File.binread(source) content = block.call(content) if block content end end
def create_file(destination, data=nil, config={}, &block)
create_file "config/apach.conf", "your apache config"
end
"vhost.name = #{hostname}"
hostname = ask("What is the virtual hostname I should use?")
create_file "lib/fun_party.rb" do
==== Examples
config
data
destination
==== Parameters
which is the return value of a block or a data string.
Create a new file relative to the destination root with the given data,
def create_file(destination, data=nil, config={}, &block) action CreateFile.new(self, destination, block || data.to_s, config) end
def destination_root
Returns the root for this thor class (also aliased as destination root).
def destination_root @destination_stack.last end
def destination_root=(root)
directory where the script was invoked and expanded.
Sets the root for this thor class. Relatives path are added to the
def destination_root=(root) @destination_stack ||= [] @destination_stack[0] = File.expand_path(root || '') end
def directory(source, destination=nil, config={}, &block)
directory "doc", "docs", :recursive => false
directory "doc"
==== Examples
If :recursive => false, does not look for paths recursively.
config
destination
source
==== Parameters
blog.rb
rdoc.rb
README
components/
doc/
files (assuming that the app_name is "blog"):
It will create a doc directory in the destination with the following
directory "doc"
When invoked as:
%app_name%.rb
rdoc.rb.tt
README
components/.empty_directory
doc/
directory with the following files:
ignored. Remember that file paths can also be encoded, let's suppose a doc
empty directory is found, it's copied and all .empty_directory files are
and is placed in the destination without the extension .tt. If any
If any of the files finishes with .tt, it's considered to be a template
Copies recursively the files from source directory to root directory.
def directory(source, destination=nil, config={}, &block) action Directory.new(self, source, destination || source, config, &block) end
def empty_directory(destination, config={})
empty_directory "doc"
==== Examples
config
destination
==== Parameters
Creates an empty directory.
def empty_directory(destination, config={}) action EmptyDirectory.new(self, destination, config) end
def find_in_source_paths(file)
Receives a file or directory and search for it in the source paths.
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: \n#{source_paths.join("\n")}" end end
def get(source, destination=nil, config={}, &block)
end
content.split("\n").first
get "http://gist.github.com/103208" do |content|
get "http://gist.github.com/103208", "doc/README"
==== Examples
config
destination
source
==== Parameters
the url is yielded and used as location.
destination. If a block is given instead of destination, the content of
Gets the content at the given address and places it at the given relative
def get(source, destination=nil, config={}, &block) source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ /^http\:\/\// render = open(source) {|input| input.binmode.read } destination ||= if block_given? block.arity == 1 ? block.call(render) : block.call else File.basename(source) end create_file destination, render, config end
def gsub_file(path, flag, *args, &block)
end
match << " no more. Use thor!"
gsub_file 'README', /rake/, :green do |match|
gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1'
==== Example
config
replacement
flag
path
==== Parameters
Run a regular expression replacement on a file.
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.binread(path) content.gsub!(flag, *args, &block) File.open(path, 'wb') { |file| file.write(content) } end end
def in_root
Goes to the root and execute the given block.
def in_root inside(@destination_stack.first) { yield } end
def initialize(args=[], options={}, config={})
destination_root
and the respective option.
It also accepts :force, :skip and :pretend to set the behavior
behavior
==== Configuration
Extends initializer to add more configuration options.
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
def inject_into_class(path, klass, *args, &block)
end
" filter_parameter :password\n"
inject_into_class "app/controllers/application_controller.rb", ApplicationController do
inject_into_class "app/controllers/application_controller.rb", " filter_parameter :password\n"
==== Examples
config
data
klass
path
==== Parameters
inject_into_file, it's reversible.
Injects text right after the class definition. Since it depends on
def inject_into_class(path, klass, *args, &block) config = args.last.is_a?(Hash) ? args.pop : {} config.merge!(:after => /class #{klass}\n|class #{klass} .*\n/) inject_into_file(path, *(args << config), &block) end
def inject_into_file(destination, *args, &block)
end
gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n")
gems = ask "Which gems would you like to add?"
inject_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do
inject_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n"
==== Examples
insert two or more times the same content.
for injection (:after or :before) or :force => true for
config
data
destination
==== Parameters
method is reversible.
Injects the given content into a file. Different from gsub_file, this
def inject_into_file(destination, *args, &block) if block_given? data, config = block, args.shift else data, config = args.shift, args.shift end action InjectIntoFile.new(self, destination, data, config) end
def inside(dir='', config={}, &block)
config
dir
==== Parameters
the method exits.
to the block you provide. The path is set back to the previous path when
is given it's referenced from the current root. The full path is yielded
Do something in the root or on a provided subfolder. If a relative path
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
def prepend_file(path, *args, &block)
end
'config.gem "rspec"'
prepend_file 'config/environments/test.rb' do
prepend_file 'config/environments/test.rb', 'config.gem "rspec"'
==== Example
config
data
path
==== Parameters
Prepend text to a file. Since it depends on inject_into_file, it's reversible.
def prepend_file(path, *args, &block) config = args.last.is_a?(Hash) ? args.pop : {} config.merge!(:after => /\A/) inject_into_file(path, *(args << config), &block) end
def relative_to_original_destination_root(path, remove_dot=true)
the script started).
Returns the given path relative to the absolute root (ie, root where
def relative_to_original_destination_root(path, remove_dot=true) path = path.gsub(@destination_stack[0], '.') remove_dot ? (path[2..-1] || '') : path end
def remove_file(path, config={})
remove_file 'app/controllers/application_controller.rb'
remove_file 'README'
==== Example
config
path
==== Parameters
Removes a file at the given location.
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
def run(command, config={})
end
run('ln -s ~/edge rails')
inside('vendor') do
==== Example
to append an executable to command executation.
config
command
==== Parameters
Executes a command returning the contents of the command.
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) `#{command}` unless options[:pretend] end
def run_ruby_script(command, config={})
config
command
==== Parameters
Executes a ruby script (taking into account WIN32 platform quirks).
def run_ruby_script(command, config={}) return unless behavior == :invoke run command, config.merge(:with => Thor::Util.ruby_command) end
def source_paths
Holds source paths in instance so they can be manipulated.
def source_paths @source_paths ||= self.class.source_paths_for_search end
def template(source, destination=nil, config={}, &block)
template "doc/README"
template "README", "doc/README"
==== Examples
config
destination
source
==== Parameters
to be equal to the source removing .tt from the filename.
at the relative destination. If the destination is not given it's assumed
Gets an ERB template at the relative source, executes it and makes a copy
def template(source, destination=nil, config={}, &block) destination ||= source source = File.expand_path(find_in_source_paths(source.to_s)) context = instance_eval('binding') create_file destination, nil, config do content = ERB.new(::File.binread(source), nil, '-').result(context) content = block.call(content) if block content end end
def thor(task, *args)
#=> thor list --all --substring=rails
thor :list, :all => true, :substring => 'rails'
#=> thor install http://gist.github.com/103208
thor :install, "http://gist.github.com/103208"
==== Examples
are given as parameter to Thor.
config
args
task
==== Parameters
switches.
Run a thor command. A hash of options can be given and it's converted to
def thor(task, *args) config = args.last.is_a?(Hash) ? args.pop : {} verbose = config.key?(:verbose) ? config.delete(:verbose) : true pretend = config.key?(:pretend) ? config.delete(:pretend) : false args.unshift task args.push Thor::Options.to_switches(config) command = args.join(' ').strip run command, :with => :thor, :verbose => verbose, :pretend => pretend end