mirror of
https://github.com/github/rails.git
synced 2026-04-26 03:00:59 -04:00
Update vendored Thor to have latest actions updates.
This commit is contained in:
108
railties/lib/vendor/thor-0.11.1/lib/thor/actions.rb
vendored
108
railties/lib/vendor/thor-0.11.1/lib/thor/actions.rb
vendored
@@ -159,24 +159,6 @@ class Thor
|
||||
inside(@destination_stack.first) { yield }
|
||||
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/*", 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
|
||||
|
||||
# Executes a command.
|
||||
#
|
||||
# ==== Parameters
|
||||
@@ -237,96 +219,6 @@ class Thor
|
||||
run "thor #{command}", :verbose => false
|
||||
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.exists?(path)
|
||||
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.read(path)
|
||||
content.gsub!(flag, *args, &block)
|
||||
File.open(path, 'wb') { |file| file.write(content) }
|
||||
end
|
||||
end
|
||||
|
||||
# Append text to a file.
|
||||
#
|
||||
# ==== 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_file 'config/environments/test.rb', 'config.gem "rspec"'
|
||||
#
|
||||
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
|
||||
|
||||
# Prepend text to a file.
|
||||
#
|
||||
# ==== 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_file 'config/environments/test.rb', 'config.gem "rspec"'
|
||||
#
|
||||
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
|
||||
|
||||
protected
|
||||
|
||||
# Allow current root to be shared between invocations.
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
require 'thor/actions/templater'
|
||||
|
||||
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.
|
||||
#
|
||||
# ==== Examples
|
||||
#
|
||||
# copy_file "README", "doc/README"
|
||||
#
|
||||
# copy_file "doc/README"
|
||||
#
|
||||
def copy_file(source, destination=nil, config={})
|
||||
action CopyFile.new(self, source, destination || source, config)
|
||||
end
|
||||
|
||||
class CopyFile < Templater #:nodoc:
|
||||
|
||||
def render
|
||||
@render ||= ::File.read(source)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'thor/actions/templater'
|
||||
require 'thor/actions/empty_directory'
|
||||
|
||||
class Thor
|
||||
module Actions
|
||||
@@ -28,14 +28,25 @@ class Thor
|
||||
# AddFile is a subset of Template, which instead of rendering a file with
|
||||
# ERB, it gets the content from the user.
|
||||
#
|
||||
class CreateFile < Templater #:nodoc:
|
||||
class CreateFile < EmptyDirectory #:nodoc:
|
||||
attr_reader :data
|
||||
|
||||
def initialize(base, destination, data, config={})
|
||||
super(base, nil, destination, config)
|
||||
@data = data
|
||||
super(base, destination, config)
|
||||
end
|
||||
|
||||
# Checks if the content of the file at the destination is identical to the rendered result.
|
||||
#
|
||||
# ==== Returns
|
||||
# Boolean:: true if it is identical, false otherwise.
|
||||
#
|
||||
def identical?
|
||||
exists? && File.read(destination) == render
|
||||
end
|
||||
|
||||
# Holds the content to be added to the file.
|
||||
#
|
||||
def render
|
||||
@render ||= if data.is_a?(Proc)
|
||||
data.call
|
||||
@@ -44,6 +55,48 @@ class Thor
|
||||
end
|
||||
end
|
||||
|
||||
def invoke!
|
||||
invoke_with_conflict_check do
|
||||
FileUtils.mkdir_p(File.dirname(destination))
|
||||
File.open(destination, 'w'){ |f| f.write render }
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Now on conflict we check if the file is identical or not.
|
||||
#
|
||||
def on_conflict_behavior(&block)
|
||||
if identical?
|
||||
say_status :identical, :blue
|
||||
else
|
||||
options = base.options.merge(config)
|
||||
force_or_skip_or_conflict(options[:force], options[:skip], &block)
|
||||
end
|
||||
end
|
||||
|
||||
# If force is true, run the action, otherwise check if it's not being
|
||||
# skipped. If both are false, show the file_collision menu, if the menu
|
||||
# returns true, force it, otherwise skip.
|
||||
#
|
||||
def force_or_skip_or_conflict(force, skip, &block)
|
||||
if force
|
||||
say_status :force, :yellow
|
||||
block.call unless pretend?
|
||||
elsif skip
|
||||
say_status :skip, :yellow
|
||||
else
|
||||
say_status :conflict, :red
|
||||
force_or_skip_or_conflict(force_on_collision?, true, &block)
|
||||
end
|
||||
end
|
||||
|
||||
# Shows the file collision menu to the user and gets the result.
|
||||
#
|
||||
def force_on_collision?
|
||||
base.shell.file_collision(destination){ render }
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'thor/actions/templater'
|
||||
require 'thor/actions/empty_directory'
|
||||
|
||||
class Thor
|
||||
module Actions
|
||||
@@ -44,9 +44,12 @@ class Thor
|
||||
action Directory.new(self, source, destination || source, config)
|
||||
end
|
||||
|
||||
class Directory < Templater #:nodoc:
|
||||
class Directory < EmptyDirectory #:nodoc:
|
||||
attr_reader :source
|
||||
|
||||
def initialize(base, source, destination=nil, config={})
|
||||
super(base, source, destination, { :recursive => true }.merge(config))
|
||||
@source = File.expand_path(base.find_in_source_paths(source.to_s))
|
||||
super(base, destination, { :recursive => true }.merge(config))
|
||||
end
|
||||
|
||||
def invoke!
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'thor/actions/templater'
|
||||
|
||||
class Thor
|
||||
module Actions
|
||||
|
||||
@@ -14,17 +12,122 @@ class Thor
|
||||
# empty_directory "doc"
|
||||
#
|
||||
def empty_directory(destination, config={})
|
||||
action EmptyDirectory.new(self, nil, destination, config)
|
||||
action EmptyDirectory.new(self, destination, config)
|
||||
end
|
||||
|
||||
class EmptyDirectory < Templater #:nodoc:
|
||||
# Class which holds create directory logic. This is the base class for
|
||||
# other actions like create_file and directory.
|
||||
#
|
||||
# This implementation is based in Templater actions, created by Jonas Nicklas
|
||||
# and Michael S. Klishin under MIT LICENSE.
|
||||
#
|
||||
class EmptyDirectory #:nodoc:
|
||||
attr_reader :base, :destination, :given_destination, :relative_destination, :config
|
||||
|
||||
# Initializes given the source and destination.
|
||||
#
|
||||
# ==== Parameters
|
||||
# base<Thor::Base>:: A Thor::Base instance
|
||||
# source<String>:: Relative path to the source of this file
|
||||
# destination<String>:: Relative path to the destination of this file
|
||||
# config<Hash>:: give :verbose => false to not log the status.
|
||||
#
|
||||
def initialize(base, destination, config={})
|
||||
@base, @config = base, { :verbose => true }.merge(config)
|
||||
self.destination = destination
|
||||
end
|
||||
|
||||
# Checks if the destination file already exists.
|
||||
#
|
||||
# ==== Returns
|
||||
# Boolean:: true if the file exists, false otherwise.
|
||||
#
|
||||
def exists?
|
||||
::File.exists?(destination)
|
||||
end
|
||||
|
||||
def invoke!
|
||||
invoke_with_options!(base.options.merge(config)) do
|
||||
invoke_with_conflict_check do
|
||||
::FileUtils.mkdir_p(destination)
|
||||
end
|
||||
end
|
||||
|
||||
def revoke!
|
||||
say_status :remove, :red
|
||||
::FileUtils.rm_rf(destination) if !pretend? && exists?
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Shortcut for pretend.
|
||||
#
|
||||
def pretend?
|
||||
base.options[:pretend]
|
||||
end
|
||||
|
||||
# Sets the absolute destination value from a relative destination value.
|
||||
# It also stores the given and relative destination. Let's suppose our
|
||||
# script is being executed on "dest", it sets the destination root to
|
||||
# "dest". The destination, given_destination and relative_destination
|
||||
# are related in the following way:
|
||||
#
|
||||
# inside "bar" do
|
||||
# empty_directory "baz"
|
||||
# end
|
||||
#
|
||||
# destination #=> dest/bar/baz
|
||||
# relative_destination #=> bar/baz
|
||||
# given_destination #=> baz
|
||||
#
|
||||
def destination=(destination)
|
||||
if destination
|
||||
@given_destination = convert_encoded_instructions(destination.to_s)
|
||||
@destination = ::File.expand_path(@given_destination, base.destination_root)
|
||||
@relative_destination = base.relative_to_original_destination_root(@destination)
|
||||
end
|
||||
end
|
||||
|
||||
# Filenames in the encoded form are converted. If you have a file:
|
||||
#
|
||||
# %class_name%.rb
|
||||
#
|
||||
# It gets the class name from the base and replace it:
|
||||
#
|
||||
# user.rb
|
||||
#
|
||||
def convert_encoded_instructions(filename)
|
||||
filename.gsub(/%(.*?)%/) do |string|
|
||||
instruction = $1.strip
|
||||
base.respond_to?(instruction) ? base.send(instruction) : string
|
||||
end
|
||||
end
|
||||
|
||||
# Receives a hash of options and just execute the block if some
|
||||
# conditions are met.
|
||||
#
|
||||
def invoke_with_conflict_check(&block)
|
||||
if exists?
|
||||
on_conflict_behavior(&block)
|
||||
else
|
||||
say_status :create, :green
|
||||
block.call unless pretend?
|
||||
end
|
||||
|
||||
destination
|
||||
end
|
||||
|
||||
# What to do when the destination file already exists.
|
||||
#
|
||||
def on_conflict_behavior(&block)
|
||||
say_status :exist, :blue
|
||||
end
|
||||
|
||||
# Shortcut to say_status shell method.
|
||||
#
|
||||
def say_status(status, color)
|
||||
base.shell.say_status status, relative_destination, color if config[:verbose]
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
194
railties/lib/vendor/thor-0.11.1/lib/thor/actions/file_manipulation.rb
vendored
Normal file
194
railties/lib/vendor/thor-0.11.1/lib/thor/actions/file_manipulation.rb
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
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.
|
||||
#
|
||||
# ==== Examples
|
||||
#
|
||||
# copy_file "README", "doc/README"
|
||||
#
|
||||
# copy_file "doc/README"
|
||||
#
|
||||
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
|
||||
|
||||
# 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, 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
|
||||
|
||||
# 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, 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
|
||||
|
||||
# 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/*", 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.
|
||||
#
|
||||
# ==== 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_file 'config/environments/test.rb', 'config.gem "rspec"'
|
||||
#
|
||||
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
|
||||
|
||||
# Append text to a file.
|
||||
#
|
||||
# ==== 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_file 'config/environments/test.rb', 'config.gem "rspec"'
|
||||
#
|
||||
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
|
||||
|
||||
# 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.read(path)
|
||||
content.gsub!(flag, *args, &block)
|
||||
File.open(path, 'wb') { |file| file.write(content) }
|
||||
end
|
||||
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.exists?(path)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -1,58 +0,0 @@
|
||||
require 'thor/actions/templater'
|
||||
require 'open-uri'
|
||||
|
||||
class Thor
|
||||
module Actions
|
||||
|
||||
# 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, destination=nil, config={}, &block)
|
||||
action Get.new(self, source, block || destination, config)
|
||||
end
|
||||
|
||||
class Get < Templater #:nodoc:
|
||||
|
||||
def render
|
||||
@render ||= open(source).read
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def source=(source)
|
||||
if source =~ /^http\:\/\//
|
||||
@source = source
|
||||
else
|
||||
super(source)
|
||||
end
|
||||
end
|
||||
|
||||
def destination=(destination)
|
||||
destination = if destination.nil?
|
||||
File.basename(source)
|
||||
elsif destination.is_a?(Proc)
|
||||
destination.arity == 1 ? destination.call(render) : destination.call
|
||||
else
|
||||
destination
|
||||
end
|
||||
|
||||
super(destination)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,5 @@
|
||||
require 'thor/actions/empty_directory'
|
||||
|
||||
class Thor
|
||||
module Actions
|
||||
|
||||
@@ -32,13 +34,12 @@ class Thor
|
||||
action InjectIntoFile.new(self, destination, data, config)
|
||||
end
|
||||
|
||||
class InjectIntoFile #:nodoc:
|
||||
attr_reader :base, :destination, :relative_destination, :flag, :replacement, :config
|
||||
class InjectIntoFile < EmptyDirectory
|
||||
attr_reader :flag, :replacement
|
||||
|
||||
def initialize(base, destination, data, config)
|
||||
@base, @config = base, { :verbose => true }.merge(config)
|
||||
super(base, destination, { :verbose => true }.merge(config))
|
||||
|
||||
self.destination = destination
|
||||
data = data.call if data.is_a?(Proc)
|
||||
|
||||
@replacement = if @config.key?(:after)
|
||||
@@ -51,33 +52,17 @@ class Thor
|
||||
end
|
||||
|
||||
def invoke!
|
||||
say_status :inject
|
||||
say_status :inject, config[:verbose]
|
||||
replace!(flag, replacement)
|
||||
end
|
||||
|
||||
def revoke!
|
||||
say_status :deinject
|
||||
say_status :deinject, config[:verbose]
|
||||
replace!(replacement, flag)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Sets the destination value from a relative destination value. The
|
||||
# relative destination is kept to be used in output messages.
|
||||
#
|
||||
def destination=(destination)
|
||||
if destination
|
||||
@destination = ::File.expand_path(destination.to_s, base.destination_root)
|
||||
@relative_destination = base.relative_to_original_destination_root(@destination)
|
||||
end
|
||||
end
|
||||
|
||||
# Shortcut to say_status shell method.
|
||||
#
|
||||
def say_status(status)
|
||||
base.shell.say_status status, relative_destination, config[:verbose]
|
||||
end
|
||||
|
||||
# Adds the content to the file.
|
||||
#
|
||||
def replace!(regexp, string)
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
require 'thor/actions/templater'
|
||||
require 'erb'
|
||||
|
||||
class Thor
|
||||
module Actions
|
||||
|
||||
# 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, destination=nil, config={})
|
||||
destination ||= source.gsub(/.tt$/, '')
|
||||
action Template.new(self, source, destination, config)
|
||||
end
|
||||
|
||||
class Template < Templater #:nodoc:
|
||||
|
||||
def render
|
||||
@render ||= begin
|
||||
context = base.instance_eval('binding')
|
||||
ERB.new(::File.read(source), nil, '-').result(context)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,194 +0,0 @@
|
||||
class Thor
|
||||
module Actions
|
||||
|
||||
# This is the base class for templater actions, ie. that copies something
|
||||
# from some directory (source) to another (destination).
|
||||
#
|
||||
# This implementation is completely based in Templater actions, created
|
||||
# by Jonas Nicklas and Michael S. Klishin under MIT LICENSE.
|
||||
#
|
||||
class Templater #:nodoc:
|
||||
attr_reader :base, :source, :destination, :given_destination, :relative_destination, :config
|
||||
|
||||
# Initializes given the source and destination.
|
||||
#
|
||||
# ==== Parameters
|
||||
# base<Thor::Base>:: A Thor::Base instance
|
||||
# source<String>:: Relative path to the source of this file
|
||||
# destination<String>:: Relative path to the destination of this file
|
||||
# config<Hash>:: give :verbose => false to not log the status.
|
||||
#
|
||||
def initialize(base, source, destination, config={})
|
||||
@base, @config = base, { :verbose => true }.merge(config)
|
||||
self.source = source
|
||||
self.destination = destination
|
||||
end
|
||||
|
||||
# Returns the contents of the source file as a String. If render is
|
||||
# available, a diff option is shown in the file collision menu.
|
||||
#
|
||||
# ==== Returns
|
||||
# String:: The source file.
|
||||
#
|
||||
# def render
|
||||
# end
|
||||
|
||||
# Checks if the destination file already exists.
|
||||
#
|
||||
# ==== Returns
|
||||
# Boolean:: true if the file exists, false otherwise.
|
||||
#
|
||||
def exists?
|
||||
::File.exists?(destination)
|
||||
end
|
||||
|
||||
# Checks if the content of the file at the destination is identical to the rendered result.
|
||||
#
|
||||
# ==== Returns
|
||||
# Boolean:: true if it is identical, false otherwise.
|
||||
#
|
||||
def identical?
|
||||
exists? && (is_not_comparable? || ::File.read(destination) == render)
|
||||
end
|
||||
|
||||
# Invokes the action. By default it adds to the file the content rendered,
|
||||
# but you can modify in the subclass.
|
||||
#
|
||||
def invoke!
|
||||
invoke_with_options!(base.options.merge(config)) do
|
||||
::FileUtils.mkdir_p(::File.dirname(destination))
|
||||
::File.open(destination, 'w'){ |f| f.write render }
|
||||
end
|
||||
end
|
||||
|
||||
# Revokes the action.
|
||||
#
|
||||
def revoke!
|
||||
say_status :remove, :red
|
||||
::FileUtils.rm_rf(destination) if !pretend? && exists?
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Shortcut for pretend.
|
||||
#
|
||||
def pretend?
|
||||
base.options[:pretend]
|
||||
end
|
||||
|
||||
# A templater is comparable if responds to render. In such cases, we have
|
||||
# to show the conflict menu to the user unless the files are identical.
|
||||
#
|
||||
def is_not_comparable?
|
||||
!respond_to?(:render)
|
||||
end
|
||||
|
||||
# Sets the absolute source value from a relative source value. Notice
|
||||
# that we need to take into consideration both the source_root as the
|
||||
# relative_root.
|
||||
#
|
||||
# Let's suppose that we are on the directory "dest", with source root set
|
||||
# to "source" and with the following scenario:
|
||||
#
|
||||
# inside "bar" do
|
||||
# copy_file "baz.rb"
|
||||
# end
|
||||
#
|
||||
# In this case, the user wants to copy the file at "source/bar/baz.rb"
|
||||
# to "dest/bar/baz.rb". If we don't take into account the relative_root
|
||||
# (in this case, "bar"), it would copy the contents at "source/baz.rb".
|
||||
#
|
||||
def source=(source)
|
||||
if source
|
||||
@source = ::File.expand_path(base.find_in_source_paths(source.to_s))
|
||||
end
|
||||
end
|
||||
|
||||
# Sets the absolute destination value from a relative destination value.
|
||||
# It also stores the given and relative destination. Let's suppose our
|
||||
# script is being executed on "dest", it sets the destination root to
|
||||
# "dest". The destination, given_destination and relative_destination
|
||||
# are related in the following way:
|
||||
#
|
||||
# inside "bar" do
|
||||
# empty_directory "baz"
|
||||
# end
|
||||
#
|
||||
# destination #=> dest/bar/baz
|
||||
# relative_destination #=> bar/baz
|
||||
# given_destination #=> baz
|
||||
#
|
||||
def destination=(destination)
|
||||
if destination
|
||||
@given_destination = convert_encoded_instructions(destination.to_s)
|
||||
@destination = ::File.expand_path(@given_destination, base.destination_root)
|
||||
@relative_destination = base.relative_to_original_destination_root(@destination)
|
||||
end
|
||||
end
|
||||
|
||||
# Filenames in the encoded form are converted. If you have a file:
|
||||
#
|
||||
# %class_name%.rb
|
||||
#
|
||||
# It gets the class name from the base and replace it:
|
||||
#
|
||||
# user.rb
|
||||
#
|
||||
def convert_encoded_instructions(filename)
|
||||
filename.gsub(/%(.*?)%/) do |string|
|
||||
instruction = $1.strip
|
||||
base.respond_to?(instruction) ? base.send(instruction) : string
|
||||
end
|
||||
end
|
||||
|
||||
# Receives a hash of options and just execute the block if some
|
||||
# conditions are met.
|
||||
#
|
||||
def invoke_with_options!(options, &block)
|
||||
if exists?
|
||||
if is_not_comparable?
|
||||
say_status :exist, :blue
|
||||
elsif identical?
|
||||
say_status :identical, :blue
|
||||
else
|
||||
force_or_skip_or_conflict(options[:force], options[:skip], &block)
|
||||
end
|
||||
else
|
||||
say_status :create, :green
|
||||
block.call unless pretend?
|
||||
end
|
||||
|
||||
destination
|
||||
end
|
||||
|
||||
# If force is true, run the action, otherwise check if it's not being
|
||||
# skipped. If both are false, show the file_collision menu, if the menu
|
||||
# returns true, force it, otherwise skip.
|
||||
#
|
||||
def force_or_skip_or_conflict(force, skip, &block)
|
||||
if force
|
||||
say_status :force, :yellow
|
||||
block.call unless pretend?
|
||||
elsif skip
|
||||
say_status :skip, :yellow
|
||||
else
|
||||
say_status :conflict, :red
|
||||
force_or_skip_or_conflict(force_on_collision?, true, &block)
|
||||
end
|
||||
end
|
||||
|
||||
# Shows the file collision menu to the user and gets the result.
|
||||
#
|
||||
def force_on_collision?
|
||||
base.shell.file_collision(destination){ render }
|
||||
end
|
||||
|
||||
# Shortcut to say_status shell method.
|
||||
#
|
||||
def say_status(status, color)
|
||||
base.shell.say_status status, relative_destination, color if config[:verbose]
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user