Injects the given content into a file. Different from #gsub_file, this method is reversible.
Relative path to the destination root
Data to add to the file. Can be given as a block.
give :verbose => false to not log the status and the flag for injection (:after or :before) or :force => true for insert two or more times the same content.
insert_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n" insert_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
Extends initializer to add more configuration options.
The actions default behavior. Can be :invoke or :revoke. It also accepts :force, :skip and :pretend to set the behavior and the respective option.
The root directory needed for some actions.
# File bundler/vendor/thor/lib/thor/actions.rb, line 72 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
Append text to a file. Since it depends on #insert_into_file, it’s reversible.
path of the file to be changed
the data to append to the file, can be also given as a block.
give :verbose => false to not log the status.
append_to_file 'config/environments/test.rb', 'config.gem "rspec"' append_to_file 'config/environments/test.rb' do 'config.gem "rspec"' end
# File bundler/vendor/thor/lib/thor/actions/file_manipulation.rb, line 195 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
Loads an external file and execute it in the instance binding.
The path to the file to execute. Can be a web address or a relative path from the source root.
apply "http://gist.github.com/103208" apply "recipes/jquery.rb"
# File bundler/vendor/thor/lib/thor/actions.rb, line 212 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
Changes the mode of the given file or directory.
the file mode
the name of the file to change mode
give :verbose => false to not log the status.
chmod "script/server", 0755
# File bundler/vendor/thor/lib/thor/actions/file_manipulation.rb, line 148 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
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.
path of the file to be changed
the regexp or string used to decide which lines to comment
give :verbose => false to not log the status.
comment_lines 'config/initializers/session_store.rb', /cookie_store/
# File bundler/vendor/thor/lib/thor/actions/file_manipulation.rb, line 310 def comment_lines(path, flag, *args) flag = flag.respond_to?(:source) ? flag.source : flag gsub_file(path, /^(\s*)([^#\n]*#{flag})/, '\1# \2', *args) end
copy_file "README", "doc/README" copy_file "doc/README"
# File bundler/vendor/thor/lib/thor/actions/file_manipulation.rb, line 21 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
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.
the relative path to the destination root.
the data to append to the file.
give :verbose => false to not log the status.
create_file "lib/fun_party.rb" do hostname = ask("What is the virtual hostname I should use?") "vhost.name = #{hostname}" end create_file "config/apache.conf", "your apache config"
# File bundler/vendor/thor/lib/thor/actions/create_file.rb, line 22 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
Create a new file relative to the destination root from the given source.
the relative path to the destination root.
the relative path to the source root.
give :verbose => false to not log the status.
give :symbolic => false for hard link.
create_link "config/apache.conf", "/etc/apache.conf"
# File bundler/vendor/thor/lib/thor/actions/create_link.rb, line 17 def create_link(destination, *args) config = args.last.is_a?(Hash) ? args.pop : {} source = args.first action CreateLink.new(self, destination, source, config) end
Returns the root for this thor class (also aliased as destination root).
# File bundler/vendor/thor/lib/thor/actions.rb, line 99 def destination_root @destination_stack.last end
Sets the root for this thor class. Relatives path are added to the directory where the script was invoked and expanded.
# File bundler/vendor/thor/lib/thor/actions.rb, line 106 def destination_root=(root) @destination_stack ||= [] @destination_stack[0] = File.expand_path(root || "") end
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. If any file name is wrapped within % signs, the text within the % signs will be executed as a method and replaced with the returned value. 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` method returns the value “blog”):
doc/ components/ README rdoc.rb blog.rb
Encoded path note: Since Bundler::Thor internals use Object#respond_to? to check if it can expand %something%, this `something` should be a public method in the class calling directory. If a method is private, Bundler::Thor stack raises PrivateMethodEncodedError.
the relative path to the source root.
the relative path to the destination root.
give :verbose => false to not log the status. If :recursive => false, does not look for paths recursively. If :mode => :preserve, preserve the file mode from the source. If :exclude_pattern => /regexp/, prevents copying files that match that regexp.
directory "doc" directory "doc", "docs", :recursive => false
# File bundler/vendor/thor/lib/thor/actions/directory.rb, line 49 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
Creates an empty directory.
the relative path to the destination root.
give :verbose => false to not log the status.
empty_directory "doc"
# File bundler/vendor/thor/lib/thor/actions/empty_directory.rb, line 13 def empty_directory(destination, config = {}) action EmptyDirectory.new(self, destination, config) end
Receives a file or directory and search for it in the source paths.
# File bundler/vendor/thor/lib/thor/actions.rb, line 133 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
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.
get
relies on open-uri, so passing application user input
would provide a command injection attack vector.
the address of the given content.
the relative path to the destination root.
give :verbose => false to not log the status.
get "http://gist.github.com/103208", "doc/README" get "http://gist.github.com/103208" do |content| content.split("\n").first end
# File bundler/vendor/thor/lib/thor/actions/file_manipulation.rb, line 79 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
Run a regular expression replacement on a file.
path of the file to be changed
the regexp or string to be replaced
the replacement, can be also given as a block
give :verbose => false to not log the status.
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
# File bundler/vendor/thor/lib/thor/actions/file_manipulation.rb, line 264 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
Goes to the root and execute the given block.
# File bundler/vendor/thor/lib/thor/actions.rb, line 196 def in_root inside(@destination_stack.first) { yield } end
Injects text right after the class definition. Since it depends on #insert_into_file, it’s reversible.
path of the file to be changed
the class to be manipulated
the data to append to the class, can be also given as a block.
give :verbose => false to not log the status.
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
# File bundler/vendor/thor/lib/thor/actions/file_manipulation.rb, line 219 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
Injects text right after the module definition. Since it depends on #insert_into_file, it’s reversible.
path of the file to be changed
the module to be manipulated
the data to append to the class, can be also given as a block.
give :verbose => false to not log the status.
inject_into_module "app/helpers/application_helper.rb", ApplicationHelper, " def help; 'help'; end\n" inject_into_module "app/helpers/application_helper.rb", ApplicationHelper do " def help; 'help'; end\n" end
# File bundler/vendor/thor/lib/thor/actions/file_manipulation.rb, line 242 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
# File bundler/vendor/thor/lib/thor/actions/inject_into_file.rb, line 26 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
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.
the directory to move to.
give :verbose => true to log and use padding.
# File bundler/vendor/thor/lib/thor/actions.rb, line 168 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
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.
the relative path to the source root.
the relative path to the destination root.
give :verbose => false to not log the status.
link_file "README", "doc/README" link_file "doc/README"
# File bundler/vendor/thor/lib/thor/actions/file_manipulation.rb, line 51 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
Prepend text to a file. Since it depends on #insert_into_file, it’s reversible.
path of the file to be changed
the data to prepend to the file, can be also given as a block.
give :verbose => false to not log the status.
prepend_to_file 'config/environments/test.rb', 'config.gem "rspec"' prepend_to_file 'config/environments/test.rb' do 'config.gem "rspec"' end
# File bundler/vendor/thor/lib/thor/actions/file_manipulation.rb, line 173 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
Returns the given path relative to the absolute root (ie, root where the script started).
# File bundler/vendor/thor/lib/thor/actions.rb, line 114 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
Removes a file at the given location.
path of the file to be changed
give :verbose => false to not log the status.
remove_file 'README' remove_file 'app/controllers/application_controller.rb'
# File bundler/vendor/thor/lib/thor/actions/file_manipulation.rb, line 327 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
Executes a command returning the contents of the command.
the command to be executed.
give :verbose => false to not log the status, :capture => true to hide to output. Specify :with to append an executable to command execution.
inside('vendor') do run('ln -s ~/edge rails') end
# File bundler/vendor/thor/lib/thor/actions.rb, line 244 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
Executes a ruby script (taking into account WIN32 platform quirks).
the command to be executed.
give :verbose => false to not log the status.
# File bundler/vendor/thor/lib/thor/actions.rb, line 281 def run_ruby_script(command, config = {}) return unless behavior == :invoke run command, config.merge(:with => Bundler::Thor::Util.ruby_command) end
Holds source paths in instance so they can be manipulated.
# File bundler/vendor/thor/lib/thor/actions.rb, line 127 def source_paths @source_paths ||= self.class.source_paths_for_search 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.
the relative path to the source root.
the relative path to the destination root.
give :verbose => false to not log the status.
template "README", "doc/README" template "doc/README"
# File bundler/vendor/thor/lib/thor/actions/file_manipulation.rb, line 115 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
Run a thor command. A hash of options can be given and it’s converted to switches.
the command to be invoked
arguments to the command
give :verbose => false to not log the status, :capture => true to hide to output. Other options are given as parameter to Bundler::Thor.
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
# File bundler/vendor/thor/lib/thor/actions.rb, line 304 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 Bundler::Thor::Options.to_switches(config) command = args.join(" ").strip run command, :with => :thor, :verbose => verbose, :pretend => pretend, :capture => capture 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.
path of the file to be changed
the regexp or string used to decide which lines to uncomment
give :verbose => false to not log the status.
uncomment_lines 'config/initializers/session_store.rb', /active_record/
# File bundler/vendor/thor/lib/thor/actions/file_manipulation.rb, line 291 def uncomment_lines(path, flag, *args) flag = flag.respond_to?(:source) ? flag.source : flag gsub_file(path, /^(\s*)#[[:blank:]]*(.*#{flag})/, '\1\2', *args) end