317 lines
11 KiB
Ruby
317 lines
11 KiB
Ruby
|
require 'erb'
|
||
|
require 'open-uri'
|
||
|
|
||
|
class Thor
|
||
|
module Actions
|
||
|
# 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<String>:: the relative path to the source root.
|
||
|
# destination<String>:: the relative path to the destination root.
|
||
|
# config<Hash>:: give :verbose => false to not log the status, and
|
||
|
# :mode => :preserve, to preserve the file mode from the source.
|
||
|
|
||
|
#
|
||
|
# ==== Examples
|
||
|
#
|
||
|
# copy_file "README", "doc/README"
|
||
|
#
|
||
|
# copy_file "doc/README"
|
||
|
#
|
||
|
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))
|
||
|
|
||
|
create_file destination, nil, config do
|
||
|
content = File.binread(source)
|
||
|
content = block.call(content) if block
|
||
|
content
|
||
|
end
|
||
|
if config[:mode] == :preserve
|
||
|
mode = File.stat(source).mode
|
||
|
chmod(destination, mode, config)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# Links 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<String>:: the relative path to the source root.
|
||
|
# destination<String>:: the relative path to the destination root.
|
||
|
# config<Hash>:: give :verbose => false to not log the status.
|
||
|
#
|
||
|
# ==== Examples
|
||
|
#
|
||
|
# link_file "README", "doc/README"
|
||
|
#
|
||
|
# link_file "doc/README"
|
||
|
#
|
||
|
def link_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))
|
||
|
|
||
|
create_link destination, source, config
|
||
|
end
|
||
|
|
||
|
# 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<String>:: the address of the given content.
|
||
|
# destination<String>:: the relative path to the destination root.
|
||
|
# config<Hash>:: 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
|
||
|
#
|
||
|
def get(source, *args, &block)
|
||
|
config = args.last.is_a?(Hash) ? args.pop : {}
|
||
|
destination = args.first
|
||
|
|
||
|
source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ %r{^https?\://}
|
||
|
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
|
||
|
|
||
|
# 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<String>:: the relative path to the source root.
|
||
|
# destination<String>:: the relative path to the destination root.
|
||
|
# config<Hash>:: give :verbose => false to not log the status.
|
||
|
#
|
||
|
# ==== Examples
|
||
|
#
|
||
|
# template "README", "doc/README"
|
||
|
#
|
||
|
# template "doc/README"
|
||
|
#
|
||
|
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 = instance_eval('binding')
|
||
|
|
||
|
create_file destination, nil, config do
|
||
|
content = ERB.new(::File.binread(source), nil, '-', '@output_buffer').result(context)
|
||
|
content = block.call(content) if block
|
||
|
content
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# Changes the mode of the given file or directory.
|
||
|
#
|
||
|
# ==== Parameters
|
||
|
# mode<Integer>:: the file mode
|
||
|
# path<String>:: the name of the file to change mode
|
||
|
# config<Hash>:: give :verbose => false to not log the status.
|
||
|
#
|
||
|
# ==== Example
|
||
|
#
|
||
|
# chmod "script/server", 0755
|
||
|
#
|
||
|
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
|
||
|
|
||
|
# Prepend text to a file. Since it depends on insert_into_file, it's reversible.
|
||
|
#
|
||
|
# ==== Parameters
|
||
|
# path<String>:: path of the file to be changed
|
||
|
# data<String>:: the data to prepend to the file, can be also given as a block.
|
||
|
# config<Hash>:: give :verbose => false to not log the status.
|
||
|
#
|
||
|
# ==== Example
|
||
|
#
|
||
|
# prepend_to_file 'config/environments/test.rb', 'config.gem "rspec"'
|
||
|
#
|
||
|
# prepend_to_file 'config/environments/test.rb' do
|
||
|
# 'config.gem "rspec"'
|
||
|
# end
|
||
|
#
|
||
|
def prepend_to_file(path, *args, &block)
|
||
|
config = args.last.is_a?(Hash) ? args.pop : {}
|
||
|
config.merge!(:after => /\A/)
|
||
|
insert_into_file(path, *(args << config), &block)
|
||
|
end
|
||
|
alias_method :prepend_file, :prepend_to_file
|
||
|
|
||
|
# Append text to a file. Since it depends on insert_into_file, it's reversible.
|
||
|
#
|
||
|
# ==== Parameters
|
||
|
# path<String>:: path of the file to be changed
|
||
|
# data<String>:: the data to append to the file, can be also given as a block.
|
||
|
# config<Hash>:: give :verbose => false to not log the status.
|
||
|
#
|
||
|
# ==== Example
|
||
|
#
|
||
|
# append_to_file 'config/environments/test.rb', 'config.gem "rspec"'
|
||
|
#
|
||
|
# append_to_file 'config/environments/test.rb' do
|
||
|
# 'config.gem "rspec"'
|
||
|
# end
|
||
|
#
|
||
|
def append_to_file(path, *args, &block)
|
||
|
config = args.last.is_a?(Hash) ? args.pop : {}
|
||
|
config.merge!(:before => /\z/)
|
||
|
insert_into_file(path, *(args << config), &block)
|
||
|
end
|
||
|
alias_method :append_file, :append_to_file
|
||
|
|
||
|
# Injects text right after the class definition. Since it depends on
|
||
|
# insert_into_file, it's reversible.
|
||
|
#
|
||
|
# ==== Parameters
|
||
|
# path<String>:: path of the file to be changed
|
||
|
# klass<String|Class>:: the class to be manipulated
|
||
|
# data<String>:: the data to append to the class, can be also given as a block.
|
||
|
# config<Hash>:: give :verbose => false to not log the status.
|
||
|
#
|
||
|
# ==== Examples
|
||
|
#
|
||
|
# inject_into_class "app/controllers/application_controller.rb", ApplicationController, " filter_parameter :password\n"
|
||
|
#
|
||
|
# inject_into_class "app/controllers/application_controller.rb", ApplicationController do
|
||
|
# " filter_parameter :password\n"
|
||
|
# end
|
||
|
#
|
||
|
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/)
|
||
|
insert_into_file(path, *(args << config), &block)
|
||
|
end
|
||
|
|
||
|
# Run a regular expression replacement on a file.
|
||
|
#
|
||
|
# ==== Parameters
|
||
|
# path<String>:: path of the file to be changed
|
||
|
# flag<Regexp|String>:: the regexp or string to be replaced
|
||
|
# replacement<String>:: the replacement, can be also given as a block
|
||
|
# config<Hash>:: 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
|
||
|
#
|
||
|
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
|
||
|
|
||
|
# Uncomment all lines matching a given regex. It will leave the space
|
||
|
# which existed before the comment hash in tact but will remove any spacing
|
||
|
# between the comment hash and the beginning of the line.
|
||
|
#
|
||
|
# ==== Parameters
|
||
|
# path<String>:: path of the file to be changed
|
||
|
# flag<Regexp|String>:: the regexp or string used to decide which lines to uncomment
|
||
|
# config<Hash>:: give :verbose => false to not log the status.
|
||
|
#
|
||
|
# ==== Example
|
||
|
#
|
||
|
# uncomment_lines 'config/initializers/session_store.rb', /active_record/
|
||
|
#
|
||
|
def uncomment_lines(path, flag, *args)
|
||
|
flag = flag.respond_to?(:source) ? flag.source : flag
|
||
|
|
||
|
gsub_file(path, /^(\s*)#[[:blank:]]*(.*#{flag})/, '\1\2', *args)
|
||
|
end
|
||
|
|
||
|
# Comment all lines matching a given regex. It will leave the space
|
||
|
# which existed before the beginning of the line in tact and will insert
|
||
|
# a single space after the comment hash.
|
||
|
#
|
||
|
# ==== Parameters
|
||
|
# path<String>:: path of the file to be changed
|
||
|
# flag<Regexp|String>:: the regexp or string used to decide which lines to comment
|
||
|
# config<Hash>:: give :verbose => false to not log the status.
|
||
|
#
|
||
|
# ==== Example
|
||
|
#
|
||
|
# comment_lines 'config/initializers/session_store.rb', /cookie_store/
|
||
|
#
|
||
|
def comment_lines(path, flag, *args)
|
||
|
flag = flag.respond_to?(:source) ? flag.source : flag
|
||
|
|
||
|
gsub_file(path, /^(\s*)([^#|\n]*#{flag})/, '\1# \2', *args)
|
||
|
end
|
||
|
|
||
|
# Removes a file at the given location.
|
||
|
#
|
||
|
# ==== Parameters
|
||
|
# path<String>:: path of the file to be changed
|
||
|
# config<Hash>:: give :verbose => false to not log the status.
|
||
|
#
|
||
|
# ==== Example
|
||
|
#
|
||
|
# remove_file 'README'
|
||
|
# remove_file 'app/controllers/application_controller.rb'
|
||
|
#
|
||
|
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.exist?(path)
|
||
|
end
|
||
|
alias_method :remove_dir, :remove_file
|
||
|
|
||
|
attr_accessor :output_buffer
|
||
|
private :output_buffer, :output_buffer=
|
||
|
|
||
|
private
|
||
|
|
||
|
def concat(string)
|
||
|
@output_buffer.concat(string)
|
||
|
end
|
||
|
|
||
|
def capture(*args, &block)
|
||
|
with_output_buffer { block.call(*args) }
|
||
|
end
|
||
|
|
||
|
def with_output_buffer(buf = '') #:nodoc:
|
||
|
self.output_buffer, old_buffer = buf, output_buffer
|
||
|
yield
|
||
|
output_buffer
|
||
|
ensure
|
||
|
self.output_buffer = old_buffer
|
||
|
end
|
||
|
end
|
||
|
end
|