module Thor::Actions

def self.included(base) #:nodoc:

:nodoc:
def self.included(base) #:nodoc:
  super(base)
  base.extend ClassMethods
end

def _cleanup_options_and_set(options, key) #:nodoc:

: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:

:nodoc:

Allow current root to be shared between invocations.
def _shared_configuration #:nodoc:
  super.merge!(:destination_root => destination_root)
end

def action(instance) #:nodoc:

: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_to_file(path, *args, &block)


end
'config.gem "rspec"'
append_to_file 'config/environments/test.rb' do

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

==== Example

config:: give :verbose => false to not log the status.
data:: the data to append to the file, can be also given as a block.
path:: path of the file to be changed
==== Parameters

Append text to a file. Since it depends on insert_into_file, it's reversible.
def append_to_file(path, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  config[:before] = /\z/
  insert_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:: The path to the file to execute. Can be a web address or
==== Parameters

Loads an external file and execute it in the instance binding.
def apply(path, config = {})
  verbose = config.fetch(:verbose, true)
  is_uri  = path =~ %r{^https?\://}
  path    = find_in_source_paths(path) unless is_uri
  say_status :apply, path, verbose
  shell.padding += 1 if verbose
  contents = if is_uri
    require "open-uri"
    open(path, "Accept" => "application/x-thor-template", &:read)
  else
    open(path, &:read)
  end
  instance_eval(contents, path)
  shell.padding -= 1 if verbose
end

def capture(*args)

def capture(*args)
  with_output_buffer { yield(*args) }
end

def chmod(path, mode, config = {})


chmod "script/server", 0755

==== Example

config:: give :verbose => false to not log the status.
path:: the name of the file to change mode
mode:: the file 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)
  unless options[:pretend]
    require "fileutils"
    FileUtils.chmod_R(mode, path)
  end
end

def comment_lines(path, flag, *args)


comment_lines 'config/initializers/session_store.rb', /cookie_store/

==== Example

config:: give :verbose => false to not log the status.
flag:: the regexp or string used to decide which lines to comment
path:: path of the file to be changed
==== Parameters

a single space after the comment hash.
which existed before the beginning of the line in tact and will insert
Comment all lines matching a given regex. It will leave the space
def comment_lines(path, flag, *args)
  flag = flag.respond_to?(:source) ? flag.source : flag
  gsub_file(path, /^(\s*)([^#\n]*#{flag})/, '\1# \2', *args)
end

def concat(string)

def concat(string)
  @output_buffer.concat(string)
end

def copy_file(source, *args, &block)


copy_file "doc/README"

copy_file "README", "doc/README"

==== Examples
def copy_file(source, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  destination = args.first || source
  source = File.expand_path(find_in_source_paths(source.to_s))
  resulting_destination = create_file destination, nil, config do
    content = File.binread(source)
    content = yield(content) if block
    content
  end
  if config[:mode] == :preserve
    mode = File.stat(source).mode
    chmod(resulting_destination, mode, config)
  end
end

def create_file(destination, *args, &block)


create_file "config/apache.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:: give :verbose => false to not log the status.
data:: the data to append to the file.
destination:: the relative path to the destination root.
==== 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, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  data = args.first
  action CreateFile.new(self, destination, block || data.to_s, config)
end

def create_link(destination, *args)


create_link "config/apache.conf", "/etc/apache.conf"

==== Examples

:: give :symbolic => false for hard link.
config:: give :verbose => false to not log the status.
source:: the relative path to the source root.
destination:: the relative path to the destination root.
==== Parameters

Create a new file relative to the destination root from the given source.
def create_link(destination, *args)
  config = args.last.is_a?(Hash) ? args.pop : {}
  source = args.first
  action CreateLink.new(self, destination, source, 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, *args, &block)


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

==== Examples

If :exclude_pattern => /regexp/, prevents copying files that match that regexp.
If :mode => :preserve, preserve the file mode from the source.
If :recursive => false, does not look for paths recursively.
config:: give :verbose => false to not log the status.
destination:: the relative path to the destination root.
source:: the relative path to the source root.
==== Parameters

#directory. If a method is private, Thor stack raises PrivateMethodEncodedError.
expand %something%, this `something` should be a public method in the class calling
Encoded path note: Since Thor internals use Object#respond_to? to check if it can

blog.rb
rdoc.rb
README
components/
doc/

files (assuming that the `app_name` method returns the value "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/

value. Let's suppose a doc directory with the following files:
the % signs will be executed as a method and replaced with the returned
ignored. If any file name is wrapped within % signs, the text within
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, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  destination = args.first || source
  action Directory.new(self, source, destination || source, config, &block)
end

def empty_directory(destination, config = {})


empty_directory "doc"

==== Examples

config:: give :verbose => false to not log the status.
destination:: the relative path to the destination root.
==== 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)
  possible_files = [file, file + TEMPLATE_EXTNAME]
  relative_root = relative_to_original_destination_root(destination_root, false)
  source_paths.each do |source|
    possible_files.each do |f|
      source_file = File.expand_path(f, File.join(source, relative_root))
      return source_file if File.exist?(source_file)
    end
  end
  message = "Could not find #{file.inspect} in any of your source paths. ".dup
  unless self.class.source_root
    message << "Please invoke #{self.class.name}.source_root(PATH) with the PATH containing your templates. "
  end
  message << if source_paths.empty?
               "Currently you have no source paths."
             else
               "Your current source paths are: \n#{source_paths.join("\n")}"
             end
  raise Error, message
end

def get(source, *args, &block)


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

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

==== Examples

config:: give :verbose => false to not log the status.
destination:: the relative path to the destination root.
source:: the address of the given content.
==== Parameters

a command injection attack vector.
+get+ relies on open-uri, so passing application user input would provide

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, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  destination = args.first
  render = if source =~ %r{^https?\://}
    require "open-uri"
    URI.send(:open, source) { |input| input.binmode.read }
  else
    source = File.expand_path(find_in_source_paths(source.to_s))
    open(source) { |input| input.binmode.read }
  end
  destination ||= if block_given?
    block.arity == 1 ? yield(render) : yield
  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:: give :verbose => false to not log the status.
replacement:: the replacement, can be also given as a block
flag:: the regexp or string to be replaced
path:: path of the file to be changed
==== 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:: The root directory needed for some actions.

and the respective option.
It also accepts :force, :skip and :pretend to set the behavior
behavior:: The actions default behavior. Can be :invoke or :revoke.
==== 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", ApplicationController, " filter_parameter :password\n"

==== Examples

config:: give :verbose => false to not log the status.
data:: the data to append to the class, can be also given as a block.
klass:: the class to be manipulated
path:: path of the file to be changed
==== Parameters

insert_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[:after] = /class #{klass}\n|class #{klass} .*\n/
  insert_into_file(path, *(args << config), &block)
end

def inject_into_module(path, module_name, *args, &block)


end
" def help; 'help'; end\n"
inject_into_module "app/helpers/application_helper.rb", ApplicationHelper do

inject_into_module "app/helpers/application_helper.rb", ApplicationHelper, " def help; 'help'; end\n"

==== Examples

config:: give :verbose => false to not log the status.
data:: the data to append to the class, can be also given as a block.
module_name:: the module to be manipulated
path:: path of the file to be changed
==== Parameters

insert_into_file, it's reversible.
Injects text right after the module definition. Since it depends on
def inject_into_module(path, module_name, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  config[:after] = /module #{module_name}\n|module #{module_name} .*\n/
  insert_into_file(path, *(args << config), &block)
end

def insert_into_file(destination, *args, &block)

def insert_into_file(destination, *args, &block)
  data = block_given? ? block : args.shift
  config = args.shift || {}
  config[:after] = /\z/ unless config.key?(:before) || config.key?(:after)
  action InjectIntoFile.new(self, destination, data, config)
end

def inside(dir = "", config = {}, &block)


config:: give :verbose => true to log and use padding.
dir:: the directory to move to.
==== 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)
  pretend = options[:pretend]
  say_status :inside, dir, verbose
  shell.padding += 1 if verbose
  @destination_stack.push File.expand_path(dir, destination_root)
  # If the directory doesnt exist and we're not pretending
  if !File.exist?(destination_root) && !pretend
    require "fileutils"
    FileUtils.mkdir_p(destination_root)
  end
  if pretend
    # In pretend mode, just yield down to the block
    block.arity == 1 ? yield(destination_root) : yield
  else
    require "fileutils"
    FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield }
  end
  @destination_stack.pop
  shell.padding -= 1 if verbose
end

def link_file(source, *args)


link_file "doc/README"

link_file "README", "doc/README"

==== Examples

config:: give :verbose => false to not log the status.
destination:: the relative path to the destination root.
source:: the relative path to the source root.
==== Parameters

the destination is not given it's assumed to be equal to the source.
Links the file from the relative source to the relative destination. If
def link_file(source, *args)
  config = args.last.is_a?(Hash) ? args.pop : {}
  destination = args.first || source
  source = File.expand_path(find_in_source_paths(source.to_s))
  create_link destination, source, config
end

def prepend_to_file(path, *args, &block)


end
'config.gem "rspec"'
prepend_to_file 'config/environments/test.rb' do

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

==== Example

config:: give :verbose => false to not log the status.
data:: the data to prepend to the file, can be also given as a block.
path:: path of the file to be changed
==== Parameters

Prepend text to a file. Since it depends on insert_into_file, it's reversible.
def prepend_to_file(path, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  config[:after] = /\A/
  insert_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)
  root = @destination_stack[0]
  if path.start_with?(root) && [File::SEPARATOR, File::ALT_SEPARATOR, nil, ''].include?(path[root.size..root.size])
    path = path.dup
    path[0...root.size] = '.'
    remove_dot ? (path[2..-1] || "") : path
  else
    path
  end
end

def remove_file(path, config = {})


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

==== Example

config:: give :verbose => false to not log the status.
path:: path of the file to be changed
==== 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)
  if !options[:pretend] && File.exist?(path)
    require "fileutils"
    ::FileUtils.rm_rf(path)
  end
end

def run(command, config = {})


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

==== Example

to append an executable to command execution.
config:: give :verbose => false to not log the status, :capture => true to hide to output. Specify :with
command:: the command to be executed.
==== 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)
  return if options[:pretend]
  env_splat = [config[:env]] if config[:env]
  if config[:capture]
    require "open3"
    result, status = Open3.capture2e(*env_splat, command.to_s)
    success = status.success?
  else
    result = system(*env_splat, command.to_s)
    success = result
  end
  abort if !success && config.fetch(:abort_on_failure, self.class.exit_on_failure?)
  result
end

def run_ruby_script(command, config = {})


config:: give :verbose => false to not log the status.
command:: the command to be executed.
==== 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, *args, &block)


template "doc/README"

template "README", "doc/README"

==== Examples

config:: give :verbose => false to not log the status.
destination:: the relative path to the destination root.
source:: the relative path to the source root.
==== 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, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  destination = args.first || source.sub(/#{TEMPLATE_EXTNAME}$/, "")
  source  = File.expand_path(find_in_source_paths(source.to_s))
  context = config.delete(:context) || instance_eval("binding")
  create_file destination, nil, config do
    match = ERB.version.match(/(\d+\.\d+\.\d+)/)
    capturable_erb = if match && match[1] >= "2.2.0" # Ruby 2.6+
      CapturableERB.new(::File.binread(source), :trim_mode => "-", :eoutvar => "@output_buffer")
    else
      CapturableERB.new(::File.binread(source), nil, "-", "@output_buffer")
    end
    content = capturable_erb.tap do |erb|
      erb.filename = source
    end.result(context)
    content = yield(content) if block
    content
  end
end

def thor(command, *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


Other options are given as parameter to Thor.
config:: give :verbose => false to not log the status, :capture => true to hide to output.
args:: arguments to the command
command:: the command to be invoked
==== Parameters

switches.
Run a thor command. A hash of options can be given and it's converted to
def thor(command, *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
  capture = config.key?(:capture) ? config.delete(:capture) : false
  args.unshift(command)
  args.push Thor::Options.to_switches(config)
  command = args.join(" ").strip
  run command, :with => :thor, :verbose => verbose, :pretend => pretend, :capture => capture
end

def uncomment_lines(path, flag, *args)


uncomment_lines 'config/initializers/session_store.rb', /active_record/

==== Example

config:: give :verbose => false to not log the status.
flag:: the regexp or string used to decide which lines to uncomment
path:: path of the file to be changed
==== Parameters

between the comment hash and the beginning of the line.
which existed before the comment hash in tact but will remove any spacing
Uncomment all lines matching a given regex. It will leave the space
def uncomment_lines(path, flag, *args)
  flag = flag.respond_to?(:source) ? flag.source : flag
  gsub_file(path, /^(\s*)#[[:blank:]]*(.*#{flag})/, '\1\2', *args)
end

def with_output_buffer(buf = "".dup) #:nodoc:

:nodoc:
def with_output_buffer(buf = "".dup) #:nodoc:
  raise ArgumentError, "Buffer can not be a frozen object" if buf.frozen?
  old_buffer = output_buffer
  self.output_buffer = buf
  yield
  output_buffer
ensure
  self.output_buffer = old_buffer
end