From aef35ac138168738323b0f90a2b2d5cd9cf9a379 Mon Sep 17 00:00:00 2001 From: "Samuel E. Giddins" Date: Sat, 21 Mar 2015 14:48:37 -0700 Subject: [Vendor] Dont change the load path to require vendored gems Properly fixes us uses of autoload in thor Closes #3492 --- Rakefile | 35 +- .../vendor/Molinillo-0.2.1/lib/molinillo.rb | 5 - .../lib/molinillo/dependency_graph.rb | 266 --------- .../vendor/Molinillo-0.2.1/lib/molinillo/errors.rb | 69 --- .../Molinillo-0.2.1/lib/molinillo/gem_metadata.rb | 3 - .../molinillo/modules/specification_provider.rb | 90 --- .../Molinillo-0.2.1/lib/molinillo/modules/ui.rb | 63 -- .../Molinillo-0.2.1/lib/molinillo/resolution.rb | 412 ------------- .../Molinillo-0.2.1/lib/molinillo/resolver.rb | 43 -- .../vendor/Molinillo-0.2.1/lib/molinillo/state.rb | 43 -- lib/bundler/vendor/molinillo/lib/molinillo.rb | 5 + .../molinillo/lib/molinillo/dependency_graph.rb | 266 +++++++++ .../vendor/molinillo/lib/molinillo/errors.rb | 69 +++ .../vendor/molinillo/lib/molinillo/gem_metadata.rb | 3 + .../molinillo/modules/specification_provider.rb | 90 +++ .../vendor/molinillo/lib/molinillo/modules/ui.rb | 63 ++ .../vendor/molinillo/lib/molinillo/resolution.rb | 412 +++++++++++++ .../vendor/molinillo/lib/molinillo/resolver.rb | 43 ++ .../vendor/molinillo/lib/molinillo/state.rb | 43 ++ lib/bundler/vendor/thor-0.19.1/lib/thor.rb | 484 --------------- lib/bundler/vendor/thor-0.19.1/lib/thor/actions.rb | 319 ---------- .../thor-0.19.1/lib/thor/actions/create_file.rb | 103 ---- .../thor-0.19.1/lib/thor/actions/create_link.rb | 59 -- .../thor-0.19.1/lib/thor/actions/directory.rb | 118 ---- .../lib/thor/actions/empty_directory.rb | 135 ----- .../lib/thor/actions/file_manipulation.rb | 316 ---------- .../lib/thor/actions/inject_into_file.rb | 107 ---- lib/bundler/vendor/thor-0.19.1/lib/thor/base.rb | 656 --------------------- lib/bundler/vendor/thor-0.19.1/lib/thor/command.rb | 133 ----- .../thor/core_ext/hash_with_indifferent_access.rb | 77 --- .../lib/thor/core_ext/io_binary_read.rb | 10 - .../thor-0.19.1/lib/thor/core_ext/ordered_hash.rb | 98 --- lib/bundler/vendor/thor-0.19.1/lib/thor/error.rb | 32 - lib/bundler/vendor/thor-0.19.1/lib/thor/group.rb | 281 --------- .../vendor/thor-0.19.1/lib/thor/invocation.rb | 178 ------ .../vendor/thor-0.19.1/lib/thor/line_editor.rb | 17 - .../thor-0.19.1/lib/thor/line_editor/basic.rb | 35 -- .../thor-0.19.1/lib/thor/line_editor/readline.rb | 88 --- lib/bundler/vendor/thor-0.19.1/lib/thor/parser.rb | 4 - .../vendor/thor-0.19.1/lib/thor/parser/argument.rb | 73 --- .../thor-0.19.1/lib/thor/parser/arguments.rb | 175 ------ .../vendor/thor-0.19.1/lib/thor/parser/option.rb | 125 ---- .../vendor/thor-0.19.1/lib/thor/parser/options.rb | 218 ------- .../vendor/thor-0.19.1/lib/thor/rake_compat.rb | 71 --- lib/bundler/vendor/thor-0.19.1/lib/thor/runner.rb | 322 ---------- lib/bundler/vendor/thor-0.19.1/lib/thor/shell.rb | 81 --- .../vendor/thor-0.19.1/lib/thor/shell/basic.rb | 421 ------------- .../vendor/thor-0.19.1/lib/thor/shell/color.rb | 149 ----- .../vendor/thor-0.19.1/lib/thor/shell/html.rb | 126 ---- lib/bundler/vendor/thor-0.19.1/lib/thor/util.rb | 267 --------- lib/bundler/vendor/thor-0.19.1/lib/thor/version.rb | 3 - lib/bundler/vendor/thor/lib/thor.rb | 484 +++++++++++++++ lib/bundler/vendor/thor/lib/thor/actions.rb | 319 ++++++++++ .../vendor/thor/lib/thor/actions/create_file.rb | 103 ++++ .../vendor/thor/lib/thor/actions/create_link.rb | 59 ++ .../vendor/thor/lib/thor/actions/directory.rb | 118 ++++ .../thor/lib/thor/actions/empty_directory.rb | 135 +++++ .../thor/lib/thor/actions/file_manipulation.rb | 316 ++++++++++ .../thor/lib/thor/actions/inject_into_file.rb | 107 ++++ lib/bundler/vendor/thor/lib/thor/base.rb | 656 +++++++++++++++++++++ lib/bundler/vendor/thor/lib/thor/command.rb | 133 +++++ .../thor/core_ext/hash_with_indifferent_access.rb | 77 +++ .../thor/lib/thor/core_ext/io_binary_read.rb | 10 + .../vendor/thor/lib/thor/core_ext/ordered_hash.rb | 98 +++ lib/bundler/vendor/thor/lib/thor/error.rb | 32 + lib/bundler/vendor/thor/lib/thor/group.rb | 281 +++++++++ lib/bundler/vendor/thor/lib/thor/invocation.rb | 178 ++++++ lib/bundler/vendor/thor/lib/thor/line_editor.rb | 17 + .../vendor/thor/lib/thor/line_editor/basic.rb | 35 ++ .../vendor/thor/lib/thor/line_editor/readline.rb | 88 +++ lib/bundler/vendor/thor/lib/thor/parser.rb | 4 + .../vendor/thor/lib/thor/parser/argument.rb | 73 +++ .../vendor/thor/lib/thor/parser/arguments.rb | 175 ++++++ lib/bundler/vendor/thor/lib/thor/parser/option.rb | 125 ++++ lib/bundler/vendor/thor/lib/thor/parser/options.rb | 218 +++++++ lib/bundler/vendor/thor/lib/thor/rake_compat.rb | 71 +++ lib/bundler/vendor/thor/lib/thor/runner.rb | 322 ++++++++++ lib/bundler/vendor/thor/lib/thor/shell.rb | 81 +++ lib/bundler/vendor/thor/lib/thor/shell/basic.rb | 421 +++++++++++++ lib/bundler/vendor/thor/lib/thor/shell/color.rb | 149 +++++ lib/bundler/vendor/thor/lib/thor/shell/html.rb | 126 ++++ lib/bundler/vendor/thor/lib/thor/util.rb | 267 +++++++++ lib/bundler/vendor/thor/lib/thor/version.rb | 3 + lib/bundler/vendored_molinillo.rb | 7 +- lib/bundler/vendored_thor.rb | 8 +- 85 files changed, 6303 insertions(+), 6297 deletions(-) delete mode 100644 lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo.rb delete mode 100644 lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/dependency_graph.rb delete mode 100644 lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/errors.rb delete mode 100644 lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/gem_metadata.rb delete mode 100644 lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/modules/specification_provider.rb delete mode 100644 lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/modules/ui.rb delete mode 100644 lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/resolution.rb delete mode 100644 lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/resolver.rb delete mode 100644 lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/state.rb create mode 100644 lib/bundler/vendor/molinillo/lib/molinillo.rb create mode 100644 lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb create mode 100644 lib/bundler/vendor/molinillo/lib/molinillo/errors.rb create mode 100644 lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb create mode 100644 lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb create mode 100644 lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb create mode 100644 lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb create mode 100644 lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb create mode 100644 lib/bundler/vendor/molinillo/lib/molinillo/state.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/actions.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/actions/create_file.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/actions/create_link.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/actions/directory.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/actions/empty_directory.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/actions/file_manipulation.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/actions/inject_into_file.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/base.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/command.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/hash_with_indifferent_access.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/io_binary_read.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/ordered_hash.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/error.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/group.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/invocation.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor/basic.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor/readline.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/parser.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/parser/argument.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/parser/arguments.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/parser/option.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/parser/options.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/rake_compat.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/runner.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/shell.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/shell/basic.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/shell/color.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/shell/html.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/util.rb delete mode 100644 lib/bundler/vendor/thor-0.19.1/lib/thor/version.rb create mode 100644 lib/bundler/vendor/thor/lib/thor.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/actions.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/actions/create_file.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/actions/create_link.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/actions/directory.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/base.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/command.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/error.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/group.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/invocation.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/line_editor.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/line_editor/basic.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/line_editor/readline.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/parser.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/parser/argument.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/parser/arguments.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/parser/option.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/parser/options.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/rake_compat.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/runner.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/shell.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/shell/basic.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/shell/color.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/shell/html.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/util.rb create mode 100644 lib/bundler/vendor/thor/lib/thor/version.rb diff --git a/Rakefile b/Rakefile index 79bcce1a7d..b69bec89b7 100644 --- a/Rakefile +++ b/Rakefile @@ -27,23 +27,32 @@ module Rake end end +def clean_files(files, regex, replacement = '') + files.each do |file| + contents = File.read(file) + contents.gsub!(regex, replacement) + File.open(file, 'w') { |f| f << contents } + end +end + namespace :molinillo do task :namespace do - files = Dir.glob('lib/bundler/vendor/Molinillo*/**/*.rb') - sh "sed -i.bak 's/Molinillo/Bundler::Molinillo/g' #{files.join(' ')}" - sh "rm #{files.join('.bak ')}.bak" + files = Dir.glob('lib/bundler/vendor/molinillo*/**/*.rb') + clean_files(files, 'Molinillo', 'Bundler::Molinillo') + clean_files(files, /require (["'])molinillo/, 'require \1bundler/vendor/molinillo/lib/molinillo') end task :clean do - files = Dir.glob('lib/bundler/vendor/Molinillo*/*', File::FNM_DOTMATCH).reject { |f| %(. .. lib).include? f.split('/').last } - puts files - sh "rm -r #{files.join(' ')}" + files = Dir.glob('lib/bundler/vendor/molinillo*/*', File::FNM_DOTMATCH).reject { |f| %(. .. lib).include? f.split('/').last } + rm_r files end task :update, [:tag] => [] do |t, args| tag = args[:tag] Dir.chdir 'lib/bundler/vendor' do - `curl -L https://github.com/CocoaPods/molinillo/archive/#{tag}.tar.gz | tar -xz` + rm_rf 'molinillo' + sh "curl -L https://github.com/CocoaPods/molinillo/archive/#{tag}.tar.gz | tar -xz" + sh "mv Molinillo-* molinillo" end Rake::Task['molinillo:namespace'].invoke Rake::Task['molinillo:clean'].invoke @@ -53,20 +62,22 @@ end namespace :thor do task :namespace do files = Dir.glob('lib/bundler/vendor/thor*/**/*.rb') - sh "sed -i.bak 's/Thor/Bundler::Thor/g' #{files.join(' ')}" - sh "rm #{files.join('.bak ')}.bak" + clean_files(files, 'Thor', 'Bundler::Thor') + clean_files(files, /require (["'])thor/, 'require \1bundler/vendor/thor/lib/thor') + clean_files(files, /(autoload\s+[:\w]+,\s+["'])(thor[\w\/]+["'])/, '\1bundler/vendor/thor/lib/\2') end task :clean do files = Dir.glob('lib/bundler/vendor/thor*/*', File::FNM_DOTMATCH).reject { |f| %(. .. lib).include? f.split('/').last } - puts files - sh "rm -r #{files.join(' ')}" + rm_r files end task :update, [:tag] => [] do |t, args| tag = args[:tag] Dir.chdir 'lib/bundler/vendor' do - `curl -L https://github.com/erikhuda/thor/archive/#{tag}.tar.gz | tar -xz` + rm_rf 'thor' + sh "curl -L https://github.com/erikhuda/thor/archive/#{tag}.tar.gz | tar -xz" + sh "mv thor-* thor" end Rake::Task['thor:namespace'].invoke Rake::Task['thor:clean'].invoke diff --git a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo.rb b/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo.rb deleted file mode 100644 index bf740e4848..0000000000 --- a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'molinillo/gem_metadata' -require 'molinillo/errors' -require 'molinillo/resolver' -require 'molinillo/modules/ui' -require 'molinillo/modules/specification_provider' diff --git a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/dependency_graph.rb b/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/dependency_graph.rb deleted file mode 100644 index 4ee5708a56..0000000000 --- a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/dependency_graph.rb +++ /dev/null @@ -1,266 +0,0 @@ -require 'set' -require 'tsort' - -module Bundler::Molinillo - # A directed acyclic graph that is tuned to hold named dependencies - class DependencyGraph - include Enumerable - - # Enumerates through the vertices of the graph. - # @return [Array] The graph's vertices. - def each - vertices.values.each { |v| yield v } - end - - include TSort - - alias_method :tsort_each_node, :each - - def tsort_each_child(vertex, &block) - vertex.successors.each(&block) - end - - # Topologically sorts the given vertices. - # @param [Enumerable] vertices the vertices to be sorted, which must - # all belong to the same graph. - # @return [Array] The sorted vertices. - def self.tsort(vertices) - TSort.tsort( - lambda { |b| vertices.each(&b) }, - lambda { |v, &b| (v.successors & vertices).each(&b) } - ) - end - - # A directed edge of a {DependencyGraph} - # @attr [Vertex] origin The origin of the directed edge - # @attr [Vertex] destination The destination of the directed edge - # @attr [Array] requirements The requirements the directed edge represents - Edge = Struct.new(:origin, :destination, :requirements) - - # @return [{String => Vertex}] vertices that have no {Vertex#predecessors}, - # keyed by by {Vertex#name} - attr_reader :root_vertices - # @return [{String => Vertex}] the vertices of the dependency graph, keyed - # by {Vertex#name} - attr_reader :vertices - # @return [Set] the edges of the dependency graph - attr_reader :edges - - def initialize - @vertices = {} - @edges = Set.new - @root_vertices = {} - end - - # Initializes a copy of a {DependencyGraph}, ensuring that all {#vertices} - # have the correct {Vertex#graph} set - def initialize_copy(other) - super - @vertices = other.vertices.reduce({}) do |vertices, (name, vertex)| - vertices.tap do |hash| - hash[name] = vertex.dup.tap { |v| v.graph = self } - end - end - @root_vertices = Hash[vertices.select { |n, _v| other.root_vertices[n] }] - @edges = other.edges.map do |edge| - Edge.new( - vertex_named(edge.origin.name), - vertex_named(edge.destination.name), - edge.requirements.dup - ) - end - end - - # @return [String] a string suitable for debugging - def inspect - "#{self.class}:#{vertices.values.inspect}" - end - - # @return [Boolean] whether the two dependency graphs are equal, determined - # by a recursive traversal of each {#root_vertices} and its - # {Vertex#successors} - def ==(other) - root_vertices == other.root_vertices - end - - # @param [String] name - # @param [Object] payload - # @param [Array] parent_names - # @param [Object] requirement the requirement that is requiring the child - # @return [void] - def add_child_vertex(name, payload, parent_names, requirement) - is_root = parent_names.include?(nil) - parent_nodes = parent_names.compact.map { |n| vertex_named(n) } - vertex = vertex_named(name) || if is_root - add_root_vertex(name, payload) - else - add_vertex(name, payload) - end - vertex.payload ||= payload - parent_nodes.each do |parent_node| - add_edge(parent_node, vertex, requirement) - end - vertex - end - - # @param [String] name - # @param [Object] payload - # @return [Vertex] the vertex that was added to `self` - def add_vertex(name, payload) - vertex = vertices[name] ||= Vertex.new(self, name, payload) - vertex.tap { |v| v.payload = payload } - end - - # @param [String] name - # @param [Object] payload - # @return [Vertex] the vertex that was added to `self` - def add_root_vertex(name, payload) - add_vertex(name, payload).tap { |v| root_vertices[name] = v } - end - - # Detaches the {#vertex_named} `name` {Vertex} from the graph, recursively - # removing any non-root vertices that were orphaned in the process - # @param [String] name - # @return [void] - def detach_vertex_named(name) - vertex = vertex_named(name) - return unless vertex - successors = vertex.successors - vertices.delete(name) - edges.reject! { |e| e.origin == vertex || e.destination == vertex } - successors.each { |v| detach_vertex_named(v.name) unless root_vertices[v.name] || v.predecessors.any? } - end - - # @param [String] name - # @return [Vertex,nil] the vertex with the given name - def vertex_named(name) - vertices[name] - end - - # @param [String] name - # @return [Vertex,nil] the root vertex with the given name - def root_vertex_named(name) - root_vertices[name] - end - - # Adds a new {Edge} to the dependency graph - # @param [Vertex] origin - # @param [Vertex] destination - # @param [Object] requirement the requirement that this edge represents - # @return [Edge] the added edge - def add_edge(origin, destination, requirement) - if origin == destination || destination.path_to?(origin) - raise CircularDependencyError.new([origin, destination]) - end - Edge.new(origin, destination, [requirement]).tap { |e| edges << e } - end - - # A vertex in a {DependencyGraph} that encapsulates a {#name} and a - # {#payload} - class Vertex - # @return [DependencyGraph] the graph this vertex is a node of - attr_accessor :graph - - # @return [String] the name of the vertex - attr_accessor :name - - # @return [Object] the payload the vertex holds - attr_accessor :payload - - # @return [Arrary] the explicit requirements that required - # this vertex - attr_reader :explicit_requirements - - # @param [DependencyGraph] graph see {#graph} - # @param [String] name see {#name} - # @param [Object] payload see {#payload} - def initialize(graph, name, payload) - @graph = graph - @name = name - @payload = payload - @explicit_requirements = [] - end - - # @return [Array] all of the requirements that required - # this vertex - def requirements - incoming_edges.map(&:requirements).flatten + explicit_requirements - end - - # @return [Array] the edges of {#graph} that have `self` as their - # {Edge#origin} - def outgoing_edges - graph.edges.select { |e| e.origin.shallow_eql?(self) } - end - - # @return [Array] the edges of {#graph} that have `self` as their - # {Edge#destination} - def incoming_edges - graph.edges.select { |e| e.destination.shallow_eql?(self) } - end - - # @return [Set] the vertices of {#graph} that have an edge with - # `self` as their {Edge#destination} - def predecessors - incoming_edges.map(&:origin).to_set - end - - # @return [Set] the vertices of {#graph} that have an edge with - # `self` as their {Edge#origin} - def successors - outgoing_edges.map(&:destination).to_set - end - - # @return [Set] the vertices of {#graph} where `self` is an - # {#ancestor?} - def recursive_successors - successors + successors.map(&:recursive_successors).reduce(Set.new, &:+) - end - - # @return [String] a string suitable for debugging - def inspect - "#{self.class}:#{name}(#{payload.inspect})" - end - - # @return [Boolean] whether the two vertices are equal, determined - # by a recursive traversal of each {Vertex#successors} - def ==(other) - shallow_eql?(other) && - successors == other.successors - end - - # @return [Boolean] whether the two vertices are equal, determined - # solely by {#name} and {#payload} equality - def shallow_eql?(other) - other && - name == other.name && - payload == other.payload - end - - alias_method :eql?, :== - - # @return [Fixnum] a hash for the vertex based upon its {#name} - def hash - name.hash - end - - # Is there a path from `self` to `other` following edges in the - # dependency graph? - # @return true iff there is a path following edges within this {#graph} - def path_to?(other) - successors.include?(other) || successors.any? { |v| v.path_to?(other) } - end - - alias_method :descendent?, :path_to? - - # Is there a path from `other` to `self` following edges in the - # dependency graph? - # @return true iff there is a path following edges within this {#graph} - def ancestor?(other) - predecessors.include?(other) || predecessors.any? { |v| v.ancestor?(other) } - end - - alias_method :is_reachable_from?, :ancestor? - end - end -end diff --git a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/errors.rb b/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/errors.rb deleted file mode 100644 index b828d0c20d..0000000000 --- a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/errors.rb +++ /dev/null @@ -1,69 +0,0 @@ -module Bundler::Molinillo - # An error that occurred during the resolution process - class ResolverError < StandardError; end - - # An error caused by searching for a dependency that is completely unknown, - # i.e. has no versions available whatsoever. - class NoSuchDependencyError < ResolverError - # @return [Object] the dependency that could not be found - attr_accessor :dependency - - # @return [Array] the specifications that depended upon {#dependency} - attr_accessor :required_by - - # @param [Object] dependency @see {#dependency} - # @param [Array] required_by @see {#required_by} - def initialize(dependency, required_by = []) - @dependency = dependency - @required_by = required_by - super() - end - - def message - sources = required_by.map { |r| "`#{r}`" }.join(' and ') - message = "Unable to find a specification for `#{dependency}`" - message << " depended upon by #{sources}" unless sources.empty? - message - end - end - - # An error caused by attempting to fulfil a dependency that was circular - # - # @note This exception will be thrown iff a {Vertex} is added to a - # {DependencyGraph} that has a {DependencyGraph::Vertex#path_to?} an - # existing {DependencyGraph::Vertex} - class CircularDependencyError < ResolverError - # [Set] the dependencies responsible for causing the error - attr_reader :dependencies - - # @param [Array] nodes the nodes in the dependency - # that caused the error - def initialize(nodes) - super "There is a circular dependency between #{nodes.map(&:name).join(' and ')}" - @dependencies = nodes.map(&:payload).to_set - end - end - - # An error caused by conflicts in version - class VersionConflict < ResolverError - # @return [{String => Resolution::Conflict}] the conflicts that caused - # resolution to fail - attr_reader :conflicts - - # @param [{String => Resolution::Conflict}] conflicts see {#conflicts} - def initialize(conflicts) - pairs = [] - conflicts.values.flatten.map(&:requirements).flatten.each do |conflicting| - conflicting.each do |source, conflict_requirements| - conflict_requirements.each do |c| - pairs << [c, source] - end - end - end - - super "Unable to satisfy the following requirements:\n\n" \ - "#{pairs.map { |r, d| "- `#{r}` required by `#{d}`" }.join("\n")}" - @conflicts = conflicts - end - end -end diff --git a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/gem_metadata.rb b/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/gem_metadata.rb deleted file mode 100644 index 0736b89c3c..0000000000 --- a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/gem_metadata.rb +++ /dev/null @@ -1,3 +0,0 @@ -module Bundler::Molinillo - VERSION = '0.2.1' -end diff --git a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/modules/specification_provider.rb b/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/modules/specification_provider.rb deleted file mode 100644 index 79a85e778f..0000000000 --- a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/modules/specification_provider.rb +++ /dev/null @@ -1,90 +0,0 @@ -module Bundler::Molinillo - # Provides information about specifcations and dependencies to the resolver, - # allowing the {Resolver} class to remain generic while still providing power - # and flexibility. - # - # This module contains the methods that users of Bundler::Molinillo must to implement, - # using knowledge of their own model classes. - module SpecificationProvider - # Search for the specifications that match the given dependency. - # The specifications in the returned array will be considered in reverse - # order, so the latest version ought to be last. - # @note This method should be 'pure', i.e. the return value should depend - # only on the `dependency` parameter. - # - # @param [Object] dependency - # @return [Array] the specifications that satisfy the given - # `dependency`. - def search_for(dependency) - [] - end - - # Returns the dependencies of `specification`. - # @note This method should be 'pure', i.e. the return value should depend - # only on the `specification` parameter. - # - # @param [Object] specification - # @return [Array] the dependencies that are required by the given - # `specification`. - def dependencies_for(specification) - [] - end - - # Determines whether the given `requirement` is satisfied by the given - # `spec`, in the context of the current `activated` dependency graph. - # - # @param [Object] requirement - # @param [DependencyGraph] activated the current dependency graph in the - # resolution process. - # @param [Object] spec - # @return [Boolean] whether `requirement` is satisfied by `spec` in the - # context of the current `activated` dependency graph. - def requirement_satisfied_by?(requirement, activated, spec) - true - end - - # Returns the name for the given `dependency`. - # @note This method should be 'pure', i.e. the return value should depend - # only on the `dependency` parameter. - # - # @param [Object] dependency - # @return [String] the name for the given `dependency`. - def name_for(dependency) - dependency.to_s - end - - # @return [String] the name of the source of explicit dependencies, i.e. - # those passed to {Resolver#resolve} directly. - def name_for_explicit_dependency_source - 'user-specified dependency' - end - - # @return [String] the name of the source of 'locked' dependencies, i.e. - # those passed to {Resolver#resolve} directly as the `base` - def name_for_locking_dependency_source - 'Lockfile' - end - - # Sort dependencies so that the ones that are easiest to resolve are first. - # Easiest to resolve is (usually) defined by: - # 1) Is this dependency already activated? - # 2) How relaxed are the requirements? - # 3) Are there any conflicts for this dependency? - # 4) How many possibilities are there to satisfy this dependency? - # - # @param [Array] dependencies - # @param [DependencyGraph] activated the current dependency graph in the - # resolution process. - # @param [{String => Array}] conflicts - # @return [Array] a sorted copy of `dependencies`. - def sort_dependencies(dependencies, activated, conflicts) - dependencies.sort_by do |dependency| - name = name_for(dependency) - [ - activated.vertex_named(name).payload ? 0 : 1, - conflicts[name] ? 0 : 1, - ] - end - end - end -end diff --git a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/modules/ui.rb b/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/modules/ui.rb deleted file mode 100644 index 097c0264ac..0000000000 --- a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/modules/ui.rb +++ /dev/null @@ -1,63 +0,0 @@ -module Bundler::Molinillo - # Conveys information about the resolution process to a user. - module UI - # The {IO} object that should be used to print output. `STDOUT`, by default. - # - # @return [IO] - def output - STDOUT - end - - # Called roughly every {#progress_rate}, this method should convey progress - # to the user. - # - # @return [void] - def indicate_progress - output.print '.' unless debug? - end - - # How often progress should be conveyed to the user via - # {#indicate_progress}, in seconds. A third of a second, by default. - # - # @return [Float] - def progress_rate - 0.33 - end - - # Called before resolution begins. - # - # @return [void] - def before_resolution - output.print 'Resolving dependencies...' - end - - # Called after resolution ends (either successfully or with an error). - # By default, prints a newline. - # - # @return [void] - def after_resolution - output.puts - end - - # Conveys debug information to the user. - # - # @param [Integer] depth the current depth of the resolution process. - # @return [void] - def debug(depth = 0) - if debug? - debug_info = yield - debug_info = debug_info.inspect unless debug_info.is_a?(String) - output.puts debug_info.split("\n").map { |s| ' ' * depth + s } - end - end - - # Whether or not debug messages should be printed. - # By default, whether or not the `MOLINILLO_DEBUG` environment variable is - # set. - # - # @return [Boolean] - def debug? - @debug_mode ||= ENV['MOLINILLO_DEBUG'] - end - end -end diff --git a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/resolution.rb b/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/resolution.rb deleted file mode 100644 index 376e4d4ed2..0000000000 --- a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/resolution.rb +++ /dev/null @@ -1,412 +0,0 @@ -module Bundler::Molinillo - class Resolver - # A specific resolution from a given {Resolver} - class Resolution - # A conflict that the resolution process encountered - # @attr [Object] requirement the requirement that immediately led to the conflict - # @attr [{String,Nil=>[Object]}] requirements the requirements that caused the conflict - # @attr [Object, nil] existing the existing spec that was in conflict with - # the {#possibility} - # @attr [Object] possibility the spec that was unable to be activated due - # to a conflict - # @attr [Object] locked_requirement the relevant locking requirement. - # @attr [Array>] requirement_trees the different requirement - # trees that led to every requirement for the conflicting name. - Conflict = Struct.new( - :requirement, - :requirements, - :existing, - :possibility, - :locked_requirement, - :requirement_trees - ) - - # @return [SpecificationProvider] the provider that knows about - # dependencies, requirements, specifications, versions, etc. - attr_reader :specification_provider - - # @return [UI] the UI that knows how to communicate feedback about the - # resolution process back to the user - attr_reader :resolver_ui - - # @return [DependencyGraph] the base dependency graph to which - # dependencies should be 'locked' - attr_reader :base - - # @return [Array] the dependencies that were explicitly required - attr_reader :original_requested - - # @param [SpecificationProvider] specification_provider - # see {#specification_provider} - # @param [UI] resolver_ui see {#resolver_ui} - # @param [Array] requested see {#original_requested} - # @param [DependencyGraph] base see {#base} - def initialize(specification_provider, resolver_ui, requested, base) - @specification_provider = specification_provider - @resolver_ui = resolver_ui - @original_requested = requested - @base = base - @states = [] - @iteration_counter = 0 - end - - # Resolves the {#original_requested} dependencies into a full dependency - # graph - # @raise [ResolverError] if successful resolution is impossible - # @return [DependencyGraph] the dependency graph of successfully resolved - # dependencies - def resolve - start_resolution - - while state - break unless state.requirements.any? || state.requirement - indicate_progress - if state.respond_to?(:pop_possibility_state) # DependencyState - debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" } - state.pop_possibility_state.tap { |s| states.push(s) if s } - end - process_topmost_state - end - - activated.freeze - ensure - end_resolution - end - - private - - # Sets up the resolution process - # @return [void] - def start_resolution - @started_at = Time.now - - states.push(initial_state) - - debug { "Starting resolution (#{@started_at})" } - resolver_ui.before_resolution - end - - # Ends the resolution process - # @return [void] - def end_resolution - resolver_ui.after_resolution - debug do - "Finished resolution (#{@iteration_counter} steps) " \ - "(Took #{(ended_at = Time.now) - @started_at} seconds) (#{ended_at})" - end - debug { 'Unactivated: ' + Hash[activated.vertices.reject { |_n, v| v.payload }].keys.join(', ') } if state - debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state - end - - require 'molinillo/state' - require 'molinillo/modules/specification_provider' - - # @return [Integer] the number of resolver iterations in between calls to - # {#resolver_ui}'s {UI#indicate_progress} method - attr_accessor :iteration_rate - - # @return [Time] the time at which resolution began - attr_accessor :started_at - - # @return [Array] the stack of states for the resolution - attr_accessor :states - - ResolutionState.new.members.each do |member| - define_method member do |*args, &block| - state.send(member, *args, &block) - end - end - - SpecificationProvider.instance_methods(false).each do |instance_method| - define_method instance_method do |*args, &block| - begin - specification_provider.send(instance_method, *args, &block) - rescue NoSuchDependencyError => error - if state - vertex = activated.vertex_named(name_for error.dependency) - error.required_by += vertex.incoming_edges.map { |e| e.origin.name } - error.required_by << name_for_explicit_dependency_source unless vertex.explicit_requirements.empty? - end - raise - end - end - end - - # Processes the topmost available {RequirementState} on the stack - # @return [void] - def process_topmost_state - if possibility - attempt_to_activate - else - create_conflict if state.is_a? PossibilityState - unwind_for_conflict until possibility && state.is_a?(DependencyState) - end - end - - # @return [Object] the current possibility that the resolution is trying - # to activate - def possibility - possibilities.last - end - - # @return [RequirementState] the current state the resolution is - # operating upon - def state - states.last - end - - # Creates the initial state for the resolution, based upon the - # {#requested} dependencies - # @return [DependencyState] the initial state for the resolution - def initial_state - graph = DependencyGraph.new.tap do |dg| - original_requested.each { |r| dg.add_root_vertex(name_for(r), nil).tap { |v| v.explicit_requirements << r } } - end - - requirements = sort_dependencies(original_requested, graph, {}) - initial_requirement = requirements.shift - DependencyState.new( - initial_requirement && name_for(initial_requirement), - requirements, - graph, - initial_requirement, - initial_requirement && search_for(initial_requirement), - 0, - {} - ) - end - - # Unwinds the states stack because a conflict has been encountered - # @return [void] - def unwind_for_conflict - debug(depth) { "Unwinding for conflict: #{requirement}" } - conflicts.tap do |c| - states.slice!((state_index_for_unwind + 1)..-1) - raise VersionConflict.new(c) unless state - state.conflicts = c - end - end - - # @return [Integer] The index to which the resolution should unwind in the - # case of conflict. - def state_index_for_unwind - current_requirement = requirement - existing_requirement = requirement_for_existing_name(name) - until current_requirement.nil? - current_state = find_state_for(current_requirement) - return states.index(current_state) if state_any?(current_state) - current_requirement = parent_of(current_requirement) - end - - until existing_requirement.nil? - existing_state = find_state_for(existing_requirement) - return states.index(existing_state) if state_any?(existing_state) - existing_requirement = parent_of(existing_requirement) - end - -1 - end - - # @return [Object] the requirement that led to `requirement` being added - # to the list of requirements. - def parent_of(requirement) - return nil unless requirement - seen = false - state = states.reverse_each.find do |s| - seen ||= s.requirement == requirement - seen && s.requirement != requirement && !s.requirements.include?(requirement) - end - state && state.requirement - end - - # @return [Object] the requirement that led to a version of a possibility - # with the given name being activated. - def requirement_for_existing_name(name) - return nil unless activated.vertex_named(name).payload - states.reverse_each.find { |s| !s.activated.vertex_named(name).payload }.requirement - end - - # @return [ResolutionState] the state whose `requirement` is the given - # `requirement`. - def find_state_for(requirement) - return nil unless requirement - states.reverse_each.find { |i| requirement == i.requirement && i.is_a?(DependencyState) } - end - - # @return [Boolean] whether or not the given state has any possibilities - # left. - def state_any?(state) - state && state.possibilities.any? - end - - # @return [Conflict] a {Conflict} that reflects the failure to activate - # the {#possibility} in conjunction with the current {#state} - def create_conflict - vertex = activated.vertex_named(name) - requirements = { - name_for_explicit_dependency_source => vertex.explicit_requirements, - name_for_locking_dependency_source => Array(locked_requirement_named(name)), - } - vertex.incoming_edges.each { |edge| (requirements[edge.origin.payload] ||= []).unshift(*edge.requirements) } - conflicts[name] = Conflict.new( - requirement, - Hash[requirements.select { |_, r| !r.empty? }], - vertex.payload, - possibility, - locked_requirement_named(name), - requirement_trees - ) - end - - # @return [Array>] The different requirement - # trees that led to every requirement for the current spec. - def requirement_trees - activated.vertex_named(name).requirements.map { |r| requirement_tree_for(r) } - end - - # @return [Array] the list of requirements that led to - # `requirement` being required. - def requirement_tree_for(requirement) - tree = [] - while requirement - tree.unshift(requirement) - requirement = parent_of(requirement) - end - tree - end - - # Indicates progress roughly once every second - # @return [void] - def indicate_progress - @iteration_counter += 1 - @progress_rate ||= resolver_ui.progress_rate - if iteration_rate.nil? - if Time.now - started_at >= @progress_rate - self.iteration_rate = @iteration_counter - end - end - - if iteration_rate && (@iteration_counter % iteration_rate) == 0 - resolver_ui.indicate_progress - end - end - - # Calls the {#resolver_ui}'s {UI#debug} method - # @param [Integer] depth the depth of the {#states} stack - # @param [Proc] block a block that yields a {#to_s} - # @return [void] - def debug(depth = 0, &block) - resolver_ui.debug(depth, &block) - end - - # Attempts to activate the current {#possibility} - # @return [void] - def attempt_to_activate - debug(depth) { 'Attempting to activate ' + possibility.to_s } - existing_node = activated.vertex_named(name) - if existing_node.payload - debug(depth) { "Found existing spec (#{existing_node.payload})" } - attempt_to_activate_existing_spec(existing_node) - else - attempt_to_activate_new_spec - end - end - - # Attempts to activate the current {#possibility} (given that it has - # already been activated) - # @return [void] - def attempt_to_activate_existing_spec(existing_node) - existing_spec = existing_node.payload - if requirement_satisfied_by?(requirement, activated, existing_spec) - new_requirements = requirements.dup - push_state_for_requirements(new_requirements) - else - return if attempt_to_swap_possibility - create_conflict - debug(depth) { "Unsatisfied by existing spec (#{existing_node.payload})" } - unwind_for_conflict - end - end - - # Attempts to swp the current {#possibility} with the already-activated - # spec with the given name - # @return [Boolean] Whether the possibility was swapped into {#activated} - def attempt_to_swap_possibility - swapped = activated.dup - swapped.vertex_named(name).payload = possibility - return unless swapped.vertex_named(name).requirements. - all? { |r| requirement_satisfied_by?(r, swapped, possibility) } - attempt_to_activate_new_spec - end - - # Attempts to activate the current {#possibility} (given that it hasn't - # already been activated) - # @return [void] - def attempt_to_activate_new_spec - satisfied = begin - locked_requirement = locked_requirement_named(name) - requested_spec_satisfied = requirement_satisfied_by?(requirement, activated, possibility) - locked_spec_satisfied = !locked_requirement || - requirement_satisfied_by?(locked_requirement, activated, possibility) - debug(depth) { 'Unsatisfied by requested spec' } unless requested_spec_satisfied - debug(depth) { 'Unsatisfied by locked spec' } unless locked_spec_satisfied - requested_spec_satisfied && locked_spec_satisfied - end - if satisfied - activate_spec - else - create_conflict - unwind_for_conflict - end - end - - # @param [String] requirement_name the spec name to search for - # @return [Object] the locked spec named `requirement_name`, if one - # is found on {#base} - def locked_requirement_named(requirement_name) - vertex = base.vertex_named(requirement_name) - vertex && vertex.payload - end - - # Add the current {#possibility} to the dependency graph of the current - # {#state} - # @return [void] - def activate_spec - conflicts.delete(name) - debug(depth) { 'Activated ' + name + ' at ' + possibility.to_s } - vertex = activated.vertex_named(name) - vertex.payload = possibility - require_nested_dependencies_for(possibility) - end - - # Requires the dependencies that the recently activated spec has - # @param [Object] activated_spec the specification that has just been - # activated - # @return [void] - def require_nested_dependencies_for(activated_spec) - nested_dependencies = dependencies_for(activated_spec) - debug(depth) { "Requiring nested dependencies (#{nested_dependencies.map(&:to_s).join(', ')})" } - nested_dependencies.each { |d| activated.add_child_vertex name_for(d), nil, [name_for(activated_spec)], d } - - push_state_for_requirements(requirements + nested_dependencies) - end - - # Pushes a new {DependencyState} that encapsulates both existing and new - # requirements - # @param [Array] new_requirements - # @return [void] - def push_state_for_requirements(new_requirements) - new_requirements = sort_dependencies(new_requirements.uniq, activated, conflicts) - new_requirement = new_requirements.shift - states.push DependencyState.new( - new_requirement ? name_for(new_requirement) : '', - new_requirements, - activated.dup, - new_requirement, - new_requirement ? search_for(new_requirement) : [], - depth, - conflicts.dup - ) - end - end - end -end diff --git a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/resolver.rb b/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/resolver.rb deleted file mode 100644 index 7cfe914d34..0000000000 --- a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/resolver.rb +++ /dev/null @@ -1,43 +0,0 @@ -require 'molinillo/dependency_graph' - -module Bundler::Molinillo - # This class encapsulates a dependency resolver. - # The resolver is responsible for determining which set of dependencies to - # activate, with feedback from the the {#specification_provider} - # - # - class Resolver - require 'molinillo/resolution' - - # @return [SpecificationProvider] the specification provider used - # in the resolution process - attr_reader :specification_provider - - # @return [UI] the UI module used to communicate back to the user - # during the resolution process - attr_reader :resolver_ui - - # @param [SpecificationProvider] specification_provider - # see {#specification_provider} - # @param [UI] resolver_ui - # see {#resolver_ui} - def initialize(specification_provider, resolver_ui) - @specification_provider = specification_provider - @resolver_ui = resolver_ui - end - - # Resolves the requested dependencies into a {DependencyGraph}, - # locking to the base dependency graph (if specified) - # @param [Array] requested an array of 'requested' dependencies that the - # {#specification_provider} can understand - # @param [DependencyGraph,nil] base the base dependency graph to which - # dependencies should be 'locked' - def resolve(requested, base = DependencyGraph.new) - Resolution.new(specification_provider, - resolver_ui, - requested, - base). - resolve - end - end -end diff --git a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/state.rb b/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/state.rb deleted file mode 100644 index 8e394f8672..0000000000 --- a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/state.rb +++ /dev/null @@ -1,43 +0,0 @@ -module Bundler::Molinillo - # A state that a {Resolution} can be in - # @attr [String] name - # @attr [Array] requirements - # @attr [DependencyGraph] activated - # @attr [Object] requirement - # @attr [Object] possibility - # @attr [Integer] depth - # @attr [Set] conflicts - ResolutionState = Struct.new( - :name, - :requirements, - :activated, - :requirement, - :possibilities, - :depth, - :conflicts - ) - - # A state that encapsulates a set of {#requirements} with an {Array} of - # possibilities - class DependencyState < ResolutionState - # Removes a possibility from `self` - # @return [PossibilityState] a state with a single possibility, - # the possibility that was removed from `self` - def pop_possibility_state - PossibilityState.new( - name, - requirements.dup, - activated.dup, - requirement, - [possibilities.pop], - depth + 1, - conflicts.dup - ) - end - end - - # A state that encapsulates a single possibility to fulfill the given - # {#requirement} - class PossibilityState < ResolutionState - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo.rb b/lib/bundler/vendor/molinillo/lib/molinillo.rb new file mode 100644 index 0000000000..50af4a912c --- /dev/null +++ b/lib/bundler/vendor/molinillo/lib/molinillo.rb @@ -0,0 +1,5 @@ +require 'bundler/vendor/molinillo/lib/molinillo/gem_metadata' +require 'bundler/vendor/molinillo/lib/molinillo/errors' +require 'bundler/vendor/molinillo/lib/molinillo/resolver' +require 'bundler/vendor/molinillo/lib/molinillo/modules/ui' +require 'bundler/vendor/molinillo/lib/molinillo/modules/specification_provider' diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb new file mode 100644 index 0000000000..4ee5708a56 --- /dev/null +++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb @@ -0,0 +1,266 @@ +require 'set' +require 'tsort' + +module Bundler::Molinillo + # A directed acyclic graph that is tuned to hold named dependencies + class DependencyGraph + include Enumerable + + # Enumerates through the vertices of the graph. + # @return [Array] The graph's vertices. + def each + vertices.values.each { |v| yield v } + end + + include TSort + + alias_method :tsort_each_node, :each + + def tsort_each_child(vertex, &block) + vertex.successors.each(&block) + end + + # Topologically sorts the given vertices. + # @param [Enumerable] vertices the vertices to be sorted, which must + # all belong to the same graph. + # @return [Array] The sorted vertices. + def self.tsort(vertices) + TSort.tsort( + lambda { |b| vertices.each(&b) }, + lambda { |v, &b| (v.successors & vertices).each(&b) } + ) + end + + # A directed edge of a {DependencyGraph} + # @attr [Vertex] origin The origin of the directed edge + # @attr [Vertex] destination The destination of the directed edge + # @attr [Array] requirements The requirements the directed edge represents + Edge = Struct.new(:origin, :destination, :requirements) + + # @return [{String => Vertex}] vertices that have no {Vertex#predecessors}, + # keyed by by {Vertex#name} + attr_reader :root_vertices + # @return [{String => Vertex}] the vertices of the dependency graph, keyed + # by {Vertex#name} + attr_reader :vertices + # @return [Set] the edges of the dependency graph + attr_reader :edges + + def initialize + @vertices = {} + @edges = Set.new + @root_vertices = {} + end + + # Initializes a copy of a {DependencyGraph}, ensuring that all {#vertices} + # have the correct {Vertex#graph} set + def initialize_copy(other) + super + @vertices = other.vertices.reduce({}) do |vertices, (name, vertex)| + vertices.tap do |hash| + hash[name] = vertex.dup.tap { |v| v.graph = self } + end + end + @root_vertices = Hash[vertices.select { |n, _v| other.root_vertices[n] }] + @edges = other.edges.map do |edge| + Edge.new( + vertex_named(edge.origin.name), + vertex_named(edge.destination.name), + edge.requirements.dup + ) + end + end + + # @return [String] a string suitable for debugging + def inspect + "#{self.class}:#{vertices.values.inspect}" + end + + # @return [Boolean] whether the two dependency graphs are equal, determined + # by a recursive traversal of each {#root_vertices} and its + # {Vertex#successors} + def ==(other) + root_vertices == other.root_vertices + end + + # @param [String] name + # @param [Object] payload + # @param [Array] parent_names + # @param [Object] requirement the requirement that is requiring the child + # @return [void] + def add_child_vertex(name, payload, parent_names, requirement) + is_root = parent_names.include?(nil) + parent_nodes = parent_names.compact.map { |n| vertex_named(n) } + vertex = vertex_named(name) || if is_root + add_root_vertex(name, payload) + else + add_vertex(name, payload) + end + vertex.payload ||= payload + parent_nodes.each do |parent_node| + add_edge(parent_node, vertex, requirement) + end + vertex + end + + # @param [String] name + # @param [Object] payload + # @return [Vertex] the vertex that was added to `self` + def add_vertex(name, payload) + vertex = vertices[name] ||= Vertex.new(self, name, payload) + vertex.tap { |v| v.payload = payload } + end + + # @param [String] name + # @param [Object] payload + # @return [Vertex] the vertex that was added to `self` + def add_root_vertex(name, payload) + add_vertex(name, payload).tap { |v| root_vertices[name] = v } + end + + # Detaches the {#vertex_named} `name` {Vertex} from the graph, recursively + # removing any non-root vertices that were orphaned in the process + # @param [String] name + # @return [void] + def detach_vertex_named(name) + vertex = vertex_named(name) + return unless vertex + successors = vertex.successors + vertices.delete(name) + edges.reject! { |e| e.origin == vertex || e.destination == vertex } + successors.each { |v| detach_vertex_named(v.name) unless root_vertices[v.name] || v.predecessors.any? } + end + + # @param [String] name + # @return [Vertex,nil] the vertex with the given name + def vertex_named(name) + vertices[name] + end + + # @param [String] name + # @return [Vertex,nil] the root vertex with the given name + def root_vertex_named(name) + root_vertices[name] + end + + # Adds a new {Edge} to the dependency graph + # @param [Vertex] origin + # @param [Vertex] destination + # @param [Object] requirement the requirement that this edge represents + # @return [Edge] the added edge + def add_edge(origin, destination, requirement) + if origin == destination || destination.path_to?(origin) + raise CircularDependencyError.new([origin, destination]) + end + Edge.new(origin, destination, [requirement]).tap { |e| edges << e } + end + + # A vertex in a {DependencyGraph} that encapsulates a {#name} and a + # {#payload} + class Vertex + # @return [DependencyGraph] the graph this vertex is a node of + attr_accessor :graph + + # @return [String] the name of the vertex + attr_accessor :name + + # @return [Object] the payload the vertex holds + attr_accessor :payload + + # @return [Arrary] the explicit requirements that required + # this vertex + attr_reader :explicit_requirements + + # @param [DependencyGraph] graph see {#graph} + # @param [String] name see {#name} + # @param [Object] payload see {#payload} + def initialize(graph, name, payload) + @graph = graph + @name = name + @payload = payload + @explicit_requirements = [] + end + + # @return [Array] all of the requirements that required + # this vertex + def requirements + incoming_edges.map(&:requirements).flatten + explicit_requirements + end + + # @return [Array] the edges of {#graph} that have `self` as their + # {Edge#origin} + def outgoing_edges + graph.edges.select { |e| e.origin.shallow_eql?(self) } + end + + # @return [Array] the edges of {#graph} that have `self` as their + # {Edge#destination} + def incoming_edges + graph.edges.select { |e| e.destination.shallow_eql?(self) } + end + + # @return [Set] the vertices of {#graph} that have an edge with + # `self` as their {Edge#destination} + def predecessors + incoming_edges.map(&:origin).to_set + end + + # @return [Set] the vertices of {#graph} that have an edge with + # `self` as their {Edge#origin} + def successors + outgoing_edges.map(&:destination).to_set + end + + # @return [Set] the vertices of {#graph} where `self` is an + # {#ancestor?} + def recursive_successors + successors + successors.map(&:recursive_successors).reduce(Set.new, &:+) + end + + # @return [String] a string suitable for debugging + def inspect + "#{self.class}:#{name}(#{payload.inspect})" + end + + # @return [Boolean] whether the two vertices are equal, determined + # by a recursive traversal of each {Vertex#successors} + def ==(other) + shallow_eql?(other) && + successors == other.successors + end + + # @return [Boolean] whether the two vertices are equal, determined + # solely by {#name} and {#payload} equality + def shallow_eql?(other) + other && + name == other.name && + payload == other.payload + end + + alias_method :eql?, :== + + # @return [Fixnum] a hash for the vertex based upon its {#name} + def hash + name.hash + end + + # Is there a path from `self` to `other` following edges in the + # dependency graph? + # @return true iff there is a path following edges within this {#graph} + def path_to?(other) + successors.include?(other) || successors.any? { |v| v.path_to?(other) } + end + + alias_method :descendent?, :path_to? + + # Is there a path from `other` to `self` following edges in the + # dependency graph? + # @return true iff there is a path following edges within this {#graph} + def ancestor?(other) + predecessors.include?(other) || predecessors.any? { |v| v.ancestor?(other) } + end + + alias_method :is_reachable_from?, :ancestor? + end + end +end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb b/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb new file mode 100644 index 0000000000..b828d0c20d --- /dev/null +++ b/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb @@ -0,0 +1,69 @@ +module Bundler::Molinillo + # An error that occurred during the resolution process + class ResolverError < StandardError; end + + # An error caused by searching for a dependency that is completely unknown, + # i.e. has no versions available whatsoever. + class NoSuchDependencyError < ResolverError + # @return [Object] the dependency that could not be found + attr_accessor :dependency + + # @return [Array] the specifications that depended upon {#dependency} + attr_accessor :required_by + + # @param [Object] dependency @see {#dependency} + # @param [Array] required_by @see {#required_by} + def initialize(dependency, required_by = []) + @dependency = dependency + @required_by = required_by + super() + end + + def message + sources = required_by.map { |r| "`#{r}`" }.join(' and ') + message = "Unable to find a specification for `#{dependency}`" + message << " depended upon by #{sources}" unless sources.empty? + message + end + end + + # An error caused by attempting to fulfil a dependency that was circular + # + # @note This exception will be thrown iff a {Vertex} is added to a + # {DependencyGraph} that has a {DependencyGraph::Vertex#path_to?} an + # existing {DependencyGraph::Vertex} + class CircularDependencyError < ResolverError + # [Set] the dependencies responsible for causing the error + attr_reader :dependencies + + # @param [Array] nodes the nodes in the dependency + # that caused the error + def initialize(nodes) + super "There is a circular dependency between #{nodes.map(&:name).join(' and ')}" + @dependencies = nodes.map(&:payload).to_set + end + end + + # An error caused by conflicts in version + class VersionConflict < ResolverError + # @return [{String => Resolution::Conflict}] the conflicts that caused + # resolution to fail + attr_reader :conflicts + + # @param [{String => Resolution::Conflict}] conflicts see {#conflicts} + def initialize(conflicts) + pairs = [] + conflicts.values.flatten.map(&:requirements).flatten.each do |conflicting| + conflicting.each do |source, conflict_requirements| + conflict_requirements.each do |c| + pairs << [c, source] + end + end + end + + super "Unable to satisfy the following requirements:\n\n" \ + "#{pairs.map { |r, d| "- `#{r}` required by `#{d}`" }.join("\n")}" + @conflicts = conflicts + end + end +end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb b/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb new file mode 100644 index 0000000000..0736b89c3c --- /dev/null +++ b/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb @@ -0,0 +1,3 @@ +module Bundler::Molinillo + VERSION = '0.2.1' +end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb b/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb new file mode 100644 index 0000000000..79a85e778f --- /dev/null +++ b/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb @@ -0,0 +1,90 @@ +module Bundler::Molinillo + # Provides information about specifcations and dependencies to the resolver, + # allowing the {Resolver} class to remain generic while still providing power + # and flexibility. + # + # This module contains the methods that users of Bundler::Molinillo must to implement, + # using knowledge of their own model classes. + module SpecificationProvider + # Search for the specifications that match the given dependency. + # The specifications in the returned array will be considered in reverse + # order, so the latest version ought to be last. + # @note This method should be 'pure', i.e. the return value should depend + # only on the `dependency` parameter. + # + # @param [Object] dependency + # @return [Array] the specifications that satisfy the given + # `dependency`. + def search_for(dependency) + [] + end + + # Returns the dependencies of `specification`. + # @note This method should be 'pure', i.e. the return value should depend + # only on the `specification` parameter. + # + # @param [Object] specification + # @return [Array] the dependencies that are required by the given + # `specification`. + def dependencies_for(specification) + [] + end + + # Determines whether the given `requirement` is satisfied by the given + # `spec`, in the context of the current `activated` dependency graph. + # + # @param [Object] requirement + # @param [DependencyGraph] activated the current dependency graph in the + # resolution process. + # @param [Object] spec + # @return [Boolean] whether `requirement` is satisfied by `spec` in the + # context of the current `activated` dependency graph. + def requirement_satisfied_by?(requirement, activated, spec) + true + end + + # Returns the name for the given `dependency`. + # @note This method should be 'pure', i.e. the return value should depend + # only on the `dependency` parameter. + # + # @param [Object] dependency + # @return [String] the name for the given `dependency`. + def name_for(dependency) + dependency.to_s + end + + # @return [String] the name of the source of explicit dependencies, i.e. + # those passed to {Resolver#resolve} directly. + def name_for_explicit_dependency_source + 'user-specified dependency' + end + + # @return [String] the name of the source of 'locked' dependencies, i.e. + # those passed to {Resolver#resolve} directly as the `base` + def name_for_locking_dependency_source + 'Lockfile' + end + + # Sort dependencies so that the ones that are easiest to resolve are first. + # Easiest to resolve is (usually) defined by: + # 1) Is this dependency already activated? + # 2) How relaxed are the requirements? + # 3) Are there any conflicts for this dependency? + # 4) How many possibilities are there to satisfy this dependency? + # + # @param [Array] dependencies + # @param [DependencyGraph] activated the current dependency graph in the + # resolution process. + # @param [{String => Array}] conflicts + # @return [Array] a sorted copy of `dependencies`. + def sort_dependencies(dependencies, activated, conflicts) + dependencies.sort_by do |dependency| + name = name_for(dependency) + [ + activated.vertex_named(name).payload ? 0 : 1, + conflicts[name] ? 0 : 1, + ] + end + end + end +end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb b/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb new file mode 100644 index 0000000000..097c0264ac --- /dev/null +++ b/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb @@ -0,0 +1,63 @@ +module Bundler::Molinillo + # Conveys information about the resolution process to a user. + module UI + # The {IO} object that should be used to print output. `STDOUT`, by default. + # + # @return [IO] + def output + STDOUT + end + + # Called roughly every {#progress_rate}, this method should convey progress + # to the user. + # + # @return [void] + def indicate_progress + output.print '.' unless debug? + end + + # How often progress should be conveyed to the user via + # {#indicate_progress}, in seconds. A third of a second, by default. + # + # @return [Float] + def progress_rate + 0.33 + end + + # Called before resolution begins. + # + # @return [void] + def before_resolution + output.print 'Resolving dependencies...' + end + + # Called after resolution ends (either successfully or with an error). + # By default, prints a newline. + # + # @return [void] + def after_resolution + output.puts + end + + # Conveys debug information to the user. + # + # @param [Integer] depth the current depth of the resolution process. + # @return [void] + def debug(depth = 0) + if debug? + debug_info = yield + debug_info = debug_info.inspect unless debug_info.is_a?(String) + output.puts debug_info.split("\n").map { |s| ' ' * depth + s } + end + end + + # Whether or not debug messages should be printed. + # By default, whether or not the `MOLINILLO_DEBUG` environment variable is + # set. + # + # @return [Boolean] + def debug? + @debug_mode ||= ENV['MOLINILLO_DEBUG'] + end + end +end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb b/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb new file mode 100644 index 0000000000..cd5191eb7e --- /dev/null +++ b/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb @@ -0,0 +1,412 @@ +module Bundler::Molinillo + class Resolver + # A specific resolution from a given {Resolver} + class Resolution + # A conflict that the resolution process encountered + # @attr [Object] requirement the requirement that immediately led to the conflict + # @attr [{String,Nil=>[Object]}] requirements the requirements that caused the conflict + # @attr [Object, nil] existing the existing spec that was in conflict with + # the {#possibility} + # @attr [Object] possibility the spec that was unable to be activated due + # to a conflict + # @attr [Object] locked_requirement the relevant locking requirement. + # @attr [Array>] requirement_trees the different requirement + # trees that led to every requirement for the conflicting name. + Conflict = Struct.new( + :requirement, + :requirements, + :existing, + :possibility, + :locked_requirement, + :requirement_trees + ) + + # @return [SpecificationProvider] the provider that knows about + # dependencies, requirements, specifications, versions, etc. + attr_reader :specification_provider + + # @return [UI] the UI that knows how to communicate feedback about the + # resolution process back to the user + attr_reader :resolver_ui + + # @return [DependencyGraph] the base dependency graph to which + # dependencies should be 'locked' + attr_reader :base + + # @return [Array] the dependencies that were explicitly required + attr_reader :original_requested + + # @param [SpecificationProvider] specification_provider + # see {#specification_provider} + # @param [UI] resolver_ui see {#resolver_ui} + # @param [Array] requested see {#original_requested} + # @param [DependencyGraph] base see {#base} + def initialize(specification_provider, resolver_ui, requested, base) + @specification_provider = specification_provider + @resolver_ui = resolver_ui + @original_requested = requested + @base = base + @states = [] + @iteration_counter = 0 + end + + # Resolves the {#original_requested} dependencies into a full dependency + # graph + # @raise [ResolverError] if successful resolution is impossible + # @return [DependencyGraph] the dependency graph of successfully resolved + # dependencies + def resolve + start_resolution + + while state + break unless state.requirements.any? || state.requirement + indicate_progress + if state.respond_to?(:pop_possibility_state) # DependencyState + debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" } + state.pop_possibility_state.tap { |s| states.push(s) if s } + end + process_topmost_state + end + + activated.freeze + ensure + end_resolution + end + + private + + # Sets up the resolution process + # @return [void] + def start_resolution + @started_at = Time.now + + states.push(initial_state) + + debug { "Starting resolution (#{@started_at})" } + resolver_ui.before_resolution + end + + # Ends the resolution process + # @return [void] + def end_resolution + resolver_ui.after_resolution + debug do + "Finished resolution (#{@iteration_counter} steps) " \ + "(Took #{(ended_at = Time.now) - @started_at} seconds) (#{ended_at})" + end + debug { 'Unactivated: ' + Hash[activated.vertices.reject { |_n, v| v.payload }].keys.join(', ') } if state + debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state + end + + require 'bundler/vendor/molinillo/lib/molinillo/state' + require 'bundler/vendor/molinillo/lib/molinillo/modules/specification_provider' + + # @return [Integer] the number of resolver iterations in between calls to + # {#resolver_ui}'s {UI#indicate_progress} method + attr_accessor :iteration_rate + + # @return [Time] the time at which resolution began + attr_accessor :started_at + + # @return [Array] the stack of states for the resolution + attr_accessor :states + + ResolutionState.new.members.each do |member| + define_method member do |*args, &block| + state.send(member, *args, &block) + end + end + + SpecificationProvider.instance_methods(false).each do |instance_method| + define_method instance_method do |*args, &block| + begin + specification_provider.send(instance_method, *args, &block) + rescue NoSuchDependencyError => error + if state + vertex = activated.vertex_named(name_for error.dependency) + error.required_by += vertex.incoming_edges.map { |e| e.origin.name } + error.required_by << name_for_explicit_dependency_source unless vertex.explicit_requirements.empty? + end + raise + end + end + end + + # Processes the topmost available {RequirementState} on the stack + # @return [void] + def process_topmost_state + if possibility + attempt_to_activate + else + create_conflict if state.is_a? PossibilityState + unwind_for_conflict until possibility && state.is_a?(DependencyState) + end + end + + # @return [Object] the current possibility that the resolution is trying + # to activate + def possibility + possibilities.last + end + + # @return [RequirementState] the current state the resolution is + # operating upon + def state + states.last + end + + # Creates the initial state for the resolution, based upon the + # {#requested} dependencies + # @return [DependencyState] the initial state for the resolution + def initial_state + graph = DependencyGraph.new.tap do |dg| + original_requested.each { |r| dg.add_root_vertex(name_for(r), nil).tap { |v| v.explicit_requirements << r } } + end + + requirements = sort_dependencies(original_requested, graph, {}) + initial_requirement = requirements.shift + DependencyState.new( + initial_requirement && name_for(initial_requirement), + requirements, + graph, + initial_requirement, + initial_requirement && search_for(initial_requirement), + 0, + {} + ) + end + + # Unwinds the states stack because a conflict has been encountered + # @return [void] + def unwind_for_conflict + debug(depth) { "Unwinding for conflict: #{requirement}" } + conflicts.tap do |c| + states.slice!((state_index_for_unwind + 1)..-1) + raise VersionConflict.new(c) unless state + state.conflicts = c + end + end + + # @return [Integer] The index to which the resolution should unwind in the + # case of conflict. + def state_index_for_unwind + current_requirement = requirement + existing_requirement = requirement_for_existing_name(name) + until current_requirement.nil? + current_state = find_state_for(current_requirement) + return states.index(current_state) if state_any?(current_state) + current_requirement = parent_of(current_requirement) + end + + until existing_requirement.nil? + existing_state = find_state_for(existing_requirement) + return states.index(existing_state) if state_any?(existing_state) + existing_requirement = parent_of(existing_requirement) + end + -1 + end + + # @return [Object] the requirement that led to `requirement` being added + # to the list of requirements. + def parent_of(requirement) + return nil unless requirement + seen = false + state = states.reverse_each.find do |s| + seen ||= s.requirement == requirement + seen && s.requirement != requirement && !s.requirements.include?(requirement) + end + state && state.requirement + end + + # @return [Object] the requirement that led to a version of a possibility + # with the given name being activated. + def requirement_for_existing_name(name) + return nil unless activated.vertex_named(name).payload + states.reverse_each.find { |s| !s.activated.vertex_named(name).payload }.requirement + end + + # @return [ResolutionState] the state whose `requirement` is the given + # `requirement`. + def find_state_for(requirement) + return nil unless requirement + states.reverse_each.find { |i| requirement == i.requirement && i.is_a?(DependencyState) } + end + + # @return [Boolean] whether or not the given state has any possibilities + # left. + def state_any?(state) + state && state.possibilities.any? + end + + # @return [Conflict] a {Conflict} that reflects the failure to activate + # the {#possibility} in conjunction with the current {#state} + def create_conflict + vertex = activated.vertex_named(name) + requirements = { + name_for_explicit_dependency_source => vertex.explicit_requirements, + name_for_locking_dependency_source => Array(locked_requirement_named(name)), + } + vertex.incoming_edges.each { |edge| (requirements[edge.origin.payload] ||= []).unshift(*edge.requirements) } + conflicts[name] = Conflict.new( + requirement, + Hash[requirements.select { |_, r| !r.empty? }], + vertex.payload, + possibility, + locked_requirement_named(name), + requirement_trees + ) + end + + # @return [Array>] The different requirement + # trees that led to every requirement for the current spec. + def requirement_trees + activated.vertex_named(name).requirements.map { |r| requirement_tree_for(r) } + end + + # @return [Array] the list of requirements that led to + # `requirement` being required. + def requirement_tree_for(requirement) + tree = [] + while requirement + tree.unshift(requirement) + requirement = parent_of(requirement) + end + tree + end + + # Indicates progress roughly once every second + # @return [void] + def indicate_progress + @iteration_counter += 1 + @progress_rate ||= resolver_ui.progress_rate + if iteration_rate.nil? + if Time.now - started_at >= @progress_rate + self.iteration_rate = @iteration_counter + end + end + + if iteration_rate && (@iteration_counter % iteration_rate) == 0 + resolver_ui.indicate_progress + end + end + + # Calls the {#resolver_ui}'s {UI#debug} method + # @param [Integer] depth the depth of the {#states} stack + # @param [Proc] block a block that yields a {#to_s} + # @return [void] + def debug(depth = 0, &block) + resolver_ui.debug(depth, &block) + end + + # Attempts to activate the current {#possibility} + # @return [void] + def attempt_to_activate + debug(depth) { 'Attempting to activate ' + possibility.to_s } + existing_node = activated.vertex_named(name) + if existing_node.payload + debug(depth) { "Found existing spec (#{existing_node.payload})" } + attempt_to_activate_existing_spec(existing_node) + else + attempt_to_activate_new_spec + end + end + + # Attempts to activate the current {#possibility} (given that it has + # already been activated) + # @return [void] + def attempt_to_activate_existing_spec(existing_node) + existing_spec = existing_node.payload + if requirement_satisfied_by?(requirement, activated, existing_spec) + new_requirements = requirements.dup + push_state_for_requirements(new_requirements) + else + return if attempt_to_swap_possibility + create_conflict + debug(depth) { "Unsatisfied by existing spec (#{existing_node.payload})" } + unwind_for_conflict + end + end + + # Attempts to swp the current {#possibility} with the already-activated + # spec with the given name + # @return [Boolean] Whether the possibility was swapped into {#activated} + def attempt_to_swap_possibility + swapped = activated.dup + swapped.vertex_named(name).payload = possibility + return unless swapped.vertex_named(name).requirements. + all? { |r| requirement_satisfied_by?(r, swapped, possibility) } + attempt_to_activate_new_spec + end + + # Attempts to activate the current {#possibility} (given that it hasn't + # already been activated) + # @return [void] + def attempt_to_activate_new_spec + satisfied = begin + locked_requirement = locked_requirement_named(name) + requested_spec_satisfied = requirement_satisfied_by?(requirement, activated, possibility) + locked_spec_satisfied = !locked_requirement || + requirement_satisfied_by?(locked_requirement, activated, possibility) + debug(depth) { 'Unsatisfied by requested spec' } unless requested_spec_satisfied + debug(depth) { 'Unsatisfied by locked spec' } unless locked_spec_satisfied + requested_spec_satisfied && locked_spec_satisfied + end + if satisfied + activate_spec + else + create_conflict + unwind_for_conflict + end + end + + # @param [String] requirement_name the spec name to search for + # @return [Object] the locked spec named `requirement_name`, if one + # is found on {#base} + def locked_requirement_named(requirement_name) + vertex = base.vertex_named(requirement_name) + vertex && vertex.payload + end + + # Add the current {#possibility} to the dependency graph of the current + # {#state} + # @return [void] + def activate_spec + conflicts.delete(name) + debug(depth) { 'Activated ' + name + ' at ' + possibility.to_s } + vertex = activated.vertex_named(name) + vertex.payload = possibility + require_nested_dependencies_for(possibility) + end + + # Requires the dependencies that the recently activated spec has + # @param [Object] activated_spec the specification that has just been + # activated + # @return [void] + def require_nested_dependencies_for(activated_spec) + nested_dependencies = dependencies_for(activated_spec) + debug(depth) { "Requiring nested dependencies (#{nested_dependencies.map(&:to_s).join(', ')})" } + nested_dependencies.each { |d| activated.add_child_vertex name_for(d), nil, [name_for(activated_spec)], d } + + push_state_for_requirements(requirements + nested_dependencies) + end + + # Pushes a new {DependencyState} that encapsulates both existing and new + # requirements + # @param [Array] new_requirements + # @return [void] + def push_state_for_requirements(new_requirements) + new_requirements = sort_dependencies(new_requirements.uniq, activated, conflicts) + new_requirement = new_requirements.shift + states.push DependencyState.new( + new_requirement ? name_for(new_requirement) : '', + new_requirements, + activated.dup, + new_requirement, + new_requirement ? search_for(new_requirement) : [], + depth, + conflicts.dup + ) + end + end + end +end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb b/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb new file mode 100644 index 0000000000..9a9d8fd1f8 --- /dev/null +++ b/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb @@ -0,0 +1,43 @@ +require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph' + +module Bundler::Molinillo + # This class encapsulates a dependency resolver. + # The resolver is responsible for determining which set of dependencies to + # activate, with feedback from the the {#specification_provider} + # + # + class Resolver + require 'bundler/vendor/molinillo/lib/molinillo/resolution' + + # @return [SpecificationProvider] the specification provider used + # in the resolution process + attr_reader :specification_provider + + # @return [UI] the UI module used to communicate back to the user + # during the resolution process + attr_reader :resolver_ui + + # @param [SpecificationProvider] specification_provider + # see {#specification_provider} + # @param [UI] resolver_ui + # see {#resolver_ui} + def initialize(specification_provider, resolver_ui) + @specification_provider = specification_provider + @resolver_ui = resolver_ui + end + + # Resolves the requested dependencies into a {DependencyGraph}, + # locking to the base dependency graph (if specified) + # @param [Array] requested an array of 'requested' dependencies that the + # {#specification_provider} can understand + # @param [DependencyGraph,nil] base the base dependency graph to which + # dependencies should be 'locked' + def resolve(requested, base = DependencyGraph.new) + Resolution.new(specification_provider, + resolver_ui, + requested, + base). + resolve + end + end +end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/state.rb b/lib/bundler/vendor/molinillo/lib/molinillo/state.rb new file mode 100644 index 0000000000..8e394f8672 --- /dev/null +++ b/lib/bundler/vendor/molinillo/lib/molinillo/state.rb @@ -0,0 +1,43 @@ +module Bundler::Molinillo + # A state that a {Resolution} can be in + # @attr [String] name + # @attr [Array] requirements + # @attr [DependencyGraph] activated + # @attr [Object] requirement + # @attr [Object] possibility + # @attr [Integer] depth + # @attr [Set] conflicts + ResolutionState = Struct.new( + :name, + :requirements, + :activated, + :requirement, + :possibilities, + :depth, + :conflicts + ) + + # A state that encapsulates a set of {#requirements} with an {Array} of + # possibilities + class DependencyState < ResolutionState + # Removes a possibility from `self` + # @return [PossibilityState] a state with a single possibility, + # the possibility that was removed from `self` + def pop_possibility_state + PossibilityState.new( + name, + requirements.dup, + activated.dup, + requirement, + [possibilities.pop], + depth + 1, + conflicts.dup + ) + end + end + + # A state that encapsulates a single possibility to fulfill the given + # {#requirement} + class PossibilityState < ResolutionState + end +end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor.rb deleted file mode 100644 index 8775d6a3e0..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor.rb +++ /dev/null @@ -1,484 +0,0 @@ -require "set" -require "thor/base" - -class Bundler::Thor # rubocop:disable ClassLength - class << self - # Allows for custom "Command" package naming. - # - # === Parameters - # name - # options - # - def package_name(name, options = {}) - @package_name = name.nil? || name == "" ? nil : name - end - - # Sets the default command when thor is executed without an explicit command to be called. - # - # ==== Parameters - # meth:: name of the default command - # - def default_command(meth = nil) - if meth - @default_command = meth == :none ? "help" : meth.to_s - else - @default_command ||= from_superclass(:default_command, "help") - end - end - alias_method :default_task, :default_command - - # Registers another Bundler::Thor subclass as a command. - # - # ==== Parameters - # klass:: Bundler::Thor subclass to register - # command:: Subcommand name to use - # usage:: Short usage for the subcommand - # description:: Description for the subcommand - def register(klass, subcommand_name, usage, description, options = {}) - if klass <= Bundler::Thor::Group - desc usage, description, options - define_method(subcommand_name) { |*args| invoke(klass, args) } - else - desc usage, description, options - subcommand subcommand_name, klass - end - end - - # Defines the usage and the description of the next command. - # - # ==== Parameters - # usage - # description - # options - # - def desc(usage, description, options = {}) - if options[:for] - command = find_and_refresh_command(options[:for]) - command.usage = usage if usage - command.description = description if description - else - @usage, @desc, @hide = usage, description, options[:hide] || false - end - end - - # Defines the long description of the next command. - # - # ==== Parameters - # long description - # - def long_desc(long_description, options = {}) - if options[:for] - command = find_and_refresh_command(options[:for]) - command.long_description = long_description if long_description - else - @long_desc = long_description - end - end - - # Maps an input to a command. If you define: - # - # map "-T" => "list" - # - # Running: - # - # thor -T - # - # Will invoke the list command. - # - # ==== Parameters - # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given command. - # - def map(mappings = nil) - @map ||= from_superclass(:map, {}) - - if mappings - mappings.each do |key, value| - if key.respond_to?(:each) - key.each { |subkey| @map[subkey] = value } - else - @map[key] = value - end - end - end - - @map - end - - # Declares the options for the next command to be declared. - # - # ==== Parameters - # Hash[Symbol => Object]:: The hash key is the name of the option and the value - # is the type of the option. Can be :string, :array, :hash, :boolean, :numeric - # or :required (string). If you give a value, the type of the value is used. - # - def method_options(options = nil) - @method_options ||= {} - build_options(options, @method_options) if options - @method_options - end - - alias_method :options, :method_options - - # Adds an option to the set of method options. If :for is given as option, - # it allows you to change the options from a previous defined command. - # - # def previous_command - # # magic - # end - # - # method_option :foo => :bar, :for => :previous_command - # - # def next_command - # # magic - # end - # - # ==== Parameters - # name:: The name of the argument. - # options:: Described below. - # - # ==== Options - # :desc - Description for the argument. - # :required - If the argument is required or not. - # :default - Default value for this argument. It cannot be required and have default values. - # :aliases - Aliases for this option. - # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean. - # :banner - String to show on usage notes. - # :hide - If you want to hide this option from the help. - # - def method_option(name, options = {}) - scope = if options[:for] - find_and_refresh_command(options[:for]).options - else - method_options - end - - build_option(name, options, scope) - end - alias_method :option, :method_option - - # Prints help information for the given command. - # - # ==== Parameters - # shell - # command_name - # - def command_help(shell, command_name) - meth = normalize_command_name(command_name) - command = all_commands[meth] - handle_no_command_error(meth) unless command - - shell.say "Usage:" - shell.say " #{banner(command)}" - shell.say - class_options_help(shell, nil => command.options.map { |_, o| o }) - if command.long_description - shell.say "Description:" - shell.print_wrapped(command.long_description, :indent => 2) - else - shell.say command.description - end - end - alias_method :task_help, :command_help - - # Prints help information for this class. - # - # ==== Parameters - # shell - # - def help(shell, subcommand = false) - list = printable_commands(true, subcommand) - Bundler::Thor::Util.thor_classes_in(self).each do |klass| - list += klass.printable_commands(false) - end - list.sort! { |a, b| a[0] <=> b[0] } - - if defined?(@package_name) && @package_name - shell.say "#{@package_name} commands:" - else - shell.say "Commands:" - end - - shell.print_table(list, :indent => 2, :truncate => true) - shell.say - class_options_help(shell) - end - - # Returns commands ready to be printed. - def printable_commands(all = true, subcommand = false) - (all ? all_commands : commands).map do |_, command| - next if command.hidden? - item = [] - item << banner(command, false, subcommand) - item << (command.description ? "# #{command.description.gsub(/\s+/m, ' ')}" : "") - item - end.compact - end - alias_method :printable_tasks, :printable_commands - - def subcommands - @subcommands ||= from_superclass(:subcommands, []) - end - alias_method :subtasks, :subcommands - - def subcommand_classes - @subcommand_classes ||= {} - end - - def subcommand(subcommand, subcommand_class) - subcommands << subcommand.to_s - subcommand_class.subcommand_help subcommand - subcommand_classes[subcommand.to_s] = subcommand_class - - define_method(subcommand) do |*args| - args, opts = Bundler::Thor::Arguments.split(args) - args.unshift("help") if opts.include? "--help" or opts.include? "-h" - invoke subcommand_class, args, opts, :invoked_via_subcommand => true, :class_options => options - end - end - alias_method :subtask, :subcommand - - # Extend check unknown options to accept a hash of conditions. - # - # === Parameters - # options: A hash containing :only and/or :except keys - def check_unknown_options!(options = {}) - @check_unknown_options ||= {} - options.each do |key, value| - if value - @check_unknown_options[key] = Array(value) - else - @check_unknown_options.delete(key) - end - end - @check_unknown_options - end - - # Overwrite check_unknown_options? to take subcommands and options into account. - def check_unknown_options?(config) #:nodoc: - options = check_unknown_options - return false unless options - - command = config[:current_command] - return true unless command - - name = command.name - - if subcommands.include?(name) - false - elsif options[:except] - !options[:except].include?(name.to_sym) - elsif options[:only] - options[:only].include?(name.to_sym) - else - true - end - end - - # Stop parsing of options as soon as an unknown option or a regular - # argument is encountered. All remaining arguments are passed to the command. - # This is useful if you have a command that can receive arbitrary additional - # options, and where those additional options should not be handled by - # Bundler::Thor. - # - # ==== Example - # - # To better understand how this is useful, let's consider a command that calls - # an external command. A user may want to pass arbitrary options and - # arguments to that command. The command itself also accepts some options, - # which should be handled by Bundler::Thor. - # - # class_option "verbose", :type => :boolean - # stop_on_unknown_option! :exec - # check_unknown_options! :except => :exec - # - # desc "exec", "Run a shell command" - # def exec(*args) - # puts "diagnostic output" if options[:verbose] - # Kernel.exec(*args) - # end - # - # Here +exec+ can be called with +--verbose+ to get diagnostic output, - # e.g.: - # - # $ thor exec --verbose echo foo - # diagnostic output - # foo - # - # But if +--verbose+ is given after +echo+, it is passed to +echo+ instead: - # - # $ thor exec echo --verbose foo - # --verbose foo - # - # ==== Parameters - # Symbol ...:: A list of commands that should be affected. - def stop_on_unknown_option!(*command_names) - stop_on_unknown_option.merge(command_names) - end - - def stop_on_unknown_option?(command) #:nodoc: - command && stop_on_unknown_option.include?(command.name.to_sym) - end - - protected - def stop_on_unknown_option #:nodoc: - @stop_on_unknown_option ||= Set.new - end - - # The method responsible for dispatching given the args. - def dispatch(meth, given_args, given_opts, config) #:nodoc: # rubocop:disable MethodLength - meth ||= retrieve_command_name(given_args) - command = all_commands[normalize_command_name(meth)] - - if !command && config[:invoked_via_subcommand] - # We're a subcommand and our first argument didn't match any of our - # commands. So we put it back and call our default command. - given_args.unshift(meth) - command = all_commands[normalize_command_name(default_command)] - end - - if command - args, opts = Bundler::Thor::Options.split(given_args) - if stop_on_unknown_option?(command) && !args.empty? - # given_args starts with a non-option, so we treat everything as - # ordinary arguments - args.concat opts - opts.clear - end - else - args, opts = given_args, nil - command = dynamic_command_class.new(meth) - end - - opts = given_opts || opts || [] - config.merge!(:current_command => command, :command_options => command.options) - - instance = new(args, opts, config) - yield instance if block_given? - args = instance.args - trailing = args[Range.new(arguments.size, -1)] - instance.invoke_command(command, trailing || []) - end - - # The banner for this class. You can customize it if you are invoking the - # thor class by another ways which is not the Bundler::Thor::Runner. It receives - # the command that is going to be invoked and a boolean which indicates if - # the namespace should be displayed as arguments. - # - def banner(command, namespace = nil, subcommand = false) - "#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}" - end - - def baseclass #:nodoc: - Bundler::Thor - end - - def dynamic_command_class #:nodoc: - Bundler::Thor::DynamicCommand - end - - def create_command(meth) #:nodoc: - @usage ||= nil - @desc ||= nil - @long_desc ||= nil - - if @usage && @desc - base_class = @hide ? Bundler::Thor::HiddenCommand : Bundler::Thor::Command - commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options) - @usage, @desc, @long_desc, @method_options, @hide = nil - true - elsif all_commands[meth] || meth == "method_missing" - true - else - puts "[WARNING] Attempted to create command #{meth.inspect} without usage or description. " << - "Call desc if you want this method to be available as command or declare it inside a " << - "no_commands{} block. Invoked from #{caller[1].inspect}." - false - end - end - alias_method :create_task, :create_command - - def initialize_added #:nodoc: - class_options.merge!(method_options) - @method_options = nil - end - - # Retrieve the command name from given args. - def retrieve_command_name(args) #:nodoc: - meth = args.first.to_s unless args.empty? - if meth && (map[meth] || meth !~ /^\-/) - args.shift - else - nil - end - end - alias_method :retrieve_task_name, :retrieve_command_name - - # receives a (possibly nil) command name and returns a name that is in - # the commands hash. In addition to normalizing aliases, this logic - # will determine if a shortened command is an unambiguous substring of - # a command or alias. - # - # +normalize_command_name+ also converts names like +animal-prison+ - # into +animal_prison+. - def normalize_command_name(meth) #:nodoc: - return default_command.to_s.gsub("-", "_") unless meth - - possibilities = find_command_possibilities(meth) - if possibilities.size > 1 - fail AmbiguousTaskError, "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]" - elsif possibilities.size < 1 - meth = meth || default_command - elsif map[meth] - meth = map[meth] - else - meth = possibilities.first - end - - meth.to_s.gsub("-", "_") # treat foo-bar as foo_bar - end - alias_method :normalize_task_name, :normalize_command_name - - # this is the logic that takes the command name passed in by the user - # and determines whether it is an unambiguous substrings of a command or - # alias name. - def find_command_possibilities(meth) - len = meth.to_s.length - possibilities = all_commands.merge(map).keys.select { |n| meth == n[0, len] }.sort - unique_possibilities = possibilities.map { |k| map[k] || k }.uniq - - if possibilities.include?(meth) - [meth] - elsif unique_possibilities.size == 1 - unique_possibilities - else - possibilities - end - end - alias_method :find_task_possibilities, :find_command_possibilities - - def subcommand_help(cmd) - desc "help [COMMAND]", "Describe subcommands or one specific subcommand" - class_eval " - def help(command = nil, subcommand = true); super; end -" - end - alias_method :subtask_help, :subcommand_help - end - - include Bundler::Thor::Base - - map HELP_MAPPINGS => :help - - desc "help [COMMAND]", "Describe available commands or one specific command" - def help(command = nil, subcommand = false) - if command - if self.class.subcommands.include? command - self.class.subcommand_classes[command].help(shell, true) - else - self.class.command_help(shell, command) - end - else - self.class.help(shell, subcommand) - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/actions.rb deleted file mode 100644 index 7edc70f472..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions.rb +++ /dev/null @@ -1,319 +0,0 @@ -require "fileutils" -require "uri" -require "thor/core_ext/io_binary_read" -require "thor/actions/create_file" -require "thor/actions/create_link" -require "thor/actions/directory" -require "thor/actions/empty_directory" -require "thor/actions/file_manipulation" -require "thor/actions/inject_into_file" - -class Bundler::Thor - module Actions - attr_accessor :behavior - - def self.included(base) #:nodoc: - base.extend ClassMethods - end - - module ClassMethods - # Hold source paths for one Bundler::Thor instance. source_paths_for_search is the - # method responsible to gather source_paths from this current class, - # inherited paths and the source root. - # - def source_paths - @_source_paths ||= [] - end - - # Stores and return the source root for this class - def source_root(path = nil) - @_source_root = path if path - @_source_root ||= nil - end - - # Returns the source paths in the following order: - # - # 1) This class source paths - # 2) Source root - # 3) Parents source paths - # - def source_paths_for_search - paths = [] - paths += source_paths - paths << source_root if source_root - paths += from_superclass(:source_paths, []) - paths - end - - # Add runtime options that help actions execution. - # - def add_runtime_options! - class_option :force, :type => :boolean, :aliases => "-f", :group => :runtime, - :desc => "Overwrite files that already exist" - - class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime, - :desc => "Run but do not make any changes" - - class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime, - :desc => "Suppress status output" - - class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime, - :desc => "Skip files that already exist" - end - end - - # Extends initializer to add more configuration options. - # - # ==== Configuration - # behavior:: The actions default behavior. Can be :invoke or :revoke. - # It also accepts :force, :skip and :pretend to set the behavior - # and the respective option. - # - # destination_root:: The root directory needed for some actions. - # - 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 - - # 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 - - # Returns the root for this thor class (also aliased as destination root). - # - 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. - # - def destination_root=(root) - @destination_stack ||= [] - @destination_stack[0] = File.expand_path(root || "") - end - - # Returns the given path relative to the absolute root (ie, root where - # the script started). - # - def relative_to_original_destination_root(path, remove_dot = true) - path = path.dup - if path.gsub!(@destination_stack[0], ".") - remove_dot ? (path[2..-1] || "") : path - else - path - end - end - - # Holds source paths in instance so they can be manipulated. - # - def source_paths - @source_paths ||= self.class.source_paths_for_search - end - - # Receives a file or directory and search for it in the source paths. - # - def find_in_source_paths(file) # rubocop:disable MethodLength - 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. " - - unless self.class.source_root - message << "Please invoke #{self.class.name}.source_root(PATH) with the PATH containing your templates. " - end - - if source_paths.empty? - message << "Currently you have no source paths." - else - message << "Your current source paths are: \n#{source_paths.join("\n")}" - end - - fail Error, message - 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. - # - # ==== Parameters - # dir:: the directory to move to. - # config:: give :verbose => true to log and use padding. - # - 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 - 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 - FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield } - end - - @destination_stack.pop - shell.padding -= 1 if verbose - end - - # Goes to the root and execute the given block. - # - def in_root - inside(@destination_stack.first) { yield } - end - - # Loads an external file and execute it in the instance binding. - # - # ==== Parameters - # path:: The path to the file to execute. Can be a web address or - # a relative path from the source root. - # - # ==== Examples - # - # apply "http://gist.github.com/103208" - # - # apply "recipes/jquery.rb" - # - 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 - - if is_uri - 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 - - # Executes a command returning the contents of the command. - # - # ==== Parameters - # command:: the command to be executed. - # config:: give :verbose => false to not log the status, :capture => true to hide to output. Specify :with - # to append an executable to command execution. - # - # ==== Example - # - # inside('vendor') do - # run('ln -s ~/edge rails') - # end - # - 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) - - unless options[:pretend] - config[:capture] ? `#{command}` : system("#{command}") - end - end - - # Executes a ruby script (taking into account WIN32 platform quirks). - # - # ==== Parameters - # command:: the command to be executed. - # config:: give :verbose => false to not log the status. - # - def run_ruby_script(command, config = {}) - return unless behavior == :invoke - run command, config.merge(:with => Bundler::Thor::Util.ruby_command) - end - - # Run a thor command. A hash of options can be given and it's converted to - # switches. - # - # ==== Parameters - # command:: the command to be invoked - # args:: arguments to the command - # config:: give :verbose => false to not log the status, :capture => true to hide to output. - # Other options are given as parameter to Bundler::Thor. - # - # - # ==== Examples - # - # 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 - # - 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 - - protected - - # Allow current root to be shared between invocations. - # - def _shared_configuration #:nodoc: - super.merge!(:destination_root => destination_root) - end - - 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 - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/create_file.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/create_file.rb deleted file mode 100644 index 711ccb7d7b..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/create_file.rb +++ /dev/null @@ -1,103 +0,0 @@ -require "thor/actions/empty_directory" - -class Bundler::Thor - module Actions - # 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. - # - # ==== Parameters - # destination:: the relative path to the destination root. - # data:: the data to append to the file. - # config:: give :verbose => false to not log the status. - # - # ==== Examples - # - # 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" - # - 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 - alias_method :add_file, :create_file - - # CreateFile is a subset of Template, which instead of rendering a file with - # ERB, it gets the content from the user. - # - class CreateFile < EmptyDirectory #:nodoc: - attr_reader :data - - def initialize(base, destination, data, 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.binread(destination) == render - end - - # Holds the content to be added to the file. - # - def render - @render ||= if data.is_a?(Proc) - data.call - else - data - end - end - - def invoke! - invoke_with_conflict_check do - FileUtils.mkdir_p(File.dirname(destination)) - File.open(destination, "wb") { |f| f.write render } - end - given_destination - 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 diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/create_link.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/create_link.rb deleted file mode 100644 index f633f25c18..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/create_link.rb +++ /dev/null @@ -1,59 +0,0 @@ -require "thor/actions/create_file" - -class Bundler::Thor - module Actions - # Create a new file relative to the destination root from the given source. - # - # ==== Parameters - # destination:: the relative path to the destination root. - # source:: the relative path to the source root. - # config:: give :verbose => false to not log the status. - # :: give :symbolic => false for hard link. - # - # ==== Examples - # - # create_link "config/apache.conf", "/etc/apache.conf" - # - def create_link(destination, *args, &block) - config = args.last.is_a?(Hash) ? args.pop : {} - source = args.first - action CreateLink.new(self, destination, source, config) - end - alias_method :add_link, :create_link - - # CreateLink is a subset of CreateFile, which instead of taking a block of - # data, just takes a source string from the user. - # - class CreateLink < CreateFile #:nodoc: - attr_reader :data - - # 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.identical?(render, destination) - end - - def invoke! - invoke_with_conflict_check do - FileUtils.mkdir_p(File.dirname(destination)) - # Create a symlink by default - config[:symbolic] = true if config[:symbolic].nil? - File.unlink(destination) if exists? - if config[:symbolic] - File.symlink(render, destination) - else - File.link(render, destination) - end - end - given_destination - end - - def exists? - super || File.symlink?(destination) - end - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/directory.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/directory.rb deleted file mode 100644 index 3ed0649c27..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/directory.rb +++ /dev/null @@ -1,118 +0,0 @@ -require "thor/actions/empty_directory" - -class Bundler::Thor - module Actions - # 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. - # - # ==== Parameters - # source:: the relative path to the source root. - # destination:: the relative path to the destination root. - # config:: 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. - # - # ==== Examples - # - # directory "doc" - # directory "doc", "docs", :recursive => false - # - 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 - - class Directory < EmptyDirectory #:nodoc: - attr_reader :source - - def initialize(base, source, destination = nil, config = {}, &block) - @source = File.expand_path(base.find_in_source_paths(source.to_s)) - @block = block - super(base, destination, {:recursive => true}.merge(config)) - end - - def invoke! - base.empty_directory given_destination, config - execute! - end - - def revoke! - execute! - end - - protected - - def execute! # rubocop:disable MethodLength - lookup = Util.escape_globs(source) - lookup = config[:recursive] ? File.join(lookup, "**") : lookup - lookup = file_level_lookup(lookup) - - files(lookup).sort.each do |file_source| - next if File.directory?(file_source) - next if config[:exclude_pattern] && file_source.match(config[:exclude_pattern]) - file_destination = File.join(given_destination, file_source.gsub(source, ".")) - file_destination.gsub!("/./", "/") - - case file_source - when /\.empty_directory$/ - dirname = File.dirname(file_destination).gsub(/\/\.$/, "") - next if dirname == given_destination - base.empty_directory(dirname, config) - when /#{TEMPLATE_EXTNAME}$/ - base.template(file_source, file_destination[0..-4], config, &@block) - else - base.copy_file(file_source, file_destination, config, &@block) - end - end - end - - if RUBY_VERSION < "2.0" - def file_level_lookup(previous_lookup) - File.join(previous_lookup, "{*,.[a-z]*}") - end - - def files(lookup) - Dir[lookup] - end - else - def file_level_lookup(previous_lookup) - File.join(previous_lookup, "*") - end - - def files(lookup) - Dir.glob(lookup, File::FNM_DOTMATCH) - end - end - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/empty_directory.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/empty_directory.rb deleted file mode 100644 index cdc3768b4c..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/empty_directory.rb +++ /dev/null @@ -1,135 +0,0 @@ -class Bundler::Thor - module Actions - # Creates an empty directory. - # - # ==== Parameters - # destination:: the relative path to the destination root. - # config:: give :verbose => false to not log the status. - # - # ==== Examples - # - # empty_directory "doc" - # - def empty_directory(destination, config = {}) - action EmptyDirectory.new(self, destination, config) - end - - # 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:: A Bundler::Thor::Base instance - # source:: Relative path to the source of this file - # destination:: Relative path to the destination of this file - # config:: 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.exist?(destination) - end - - def invoke! - invoke_with_conflict_check do - ::FileUtils.mkdir_p(destination) - end - end - - def revoke! - say_status :remove, :red - ::FileUtils.rm_rf(destination) if !pretend? && exists? - given_destination - 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: - # - # %file_name%.rb - # - # It calls #file_name from the base and replaces %-string with the - # return value (should be String) of #file_name: - # - # user.rb - # - # The method referenced can be either public or private. - # - def convert_encoded_instructions(filename) - filename.gsub(/%(.*?)%/) do |initial_string| - method = $1.strip - base.respond_to?(method, true) ? base.send(method) : initial_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 diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/file_manipulation.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/file_manipulation.rb deleted file mode 100644 index 2bdc78f578..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/file_manipulation.rb +++ /dev/null @@ -1,316 +0,0 @@ -require "erb" -require "open-uri" - -class Bundler::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:: the relative path to the source root. - # destination:: the relative path to the destination root. - # config:: 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:: the relative path to the source root. - # destination:: the relative path to the destination root. - # config:: 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:: the address of the given content. - # destination:: the relative path to the destination root. - # config:: 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:: the relative path to the source root. - # destination:: the relative path to the destination root. - # config:: 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:: the file mode - # path:: the name of the file to change mode - # config:: 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:: path of the file to be changed - # data:: the data to prepend to the file, can be also given as a block. - # config:: 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:: path of the file to be changed - # data:: the data to append to the file, can be also given as a block. - # config:: 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:: path of the file to be changed - # klass:: the class to be manipulated - # data:: the data to append to the class, can be also given as a block. - # config:: 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:: path of the file to be changed - # flag:: the regexp or string to be replaced - # replacement:: the replacement, can be also given as a block - # config:: 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:: path of the file to be changed - # flag:: the regexp or string used to decide which lines to uncomment - # config:: 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:: path of the file to be changed - # flag:: the regexp or string used to decide which lines to comment - # config:: 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:: path of the file to be changed - # config:: 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 diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/inject_into_file.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/inject_into_file.rb deleted file mode 100644 index 45a70701b1..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/inject_into_file.rb +++ /dev/null @@ -1,107 +0,0 @@ -require "thor/actions/empty_directory" - -class Bundler::Thor - module Actions - # Injects the given content into a file. Different from gsub_file, this - # method is reversible. - # - # ==== Parameters - # destination:: Relative path to the destination root - # data:: Data to add to the file. Can be given as a block. - # config:: 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. - # - # ==== Examples - # - # 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 - # - def insert_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 - alias_method :inject_into_file, :insert_into_file - - class InjectIntoFile < EmptyDirectory #:nodoc: - attr_reader :replacement, :flag, :behavior - - def initialize(base, destination, data, config) - super(base, destination, {:verbose => true}.merge(config)) - - @behavior, @flag = if @config.key?(:after) - [:after, @config.delete(:after)] - else - [:before, @config.delete(:before)] - end - - @replacement = data.is_a?(Proc) ? data.call : data - @flag = Regexp.escape(@flag) unless @flag.is_a?(Regexp) - end - - def invoke! - say_status :invoke - - content = if @behavior == :after - '\0' + replacement - else - replacement + '\0' - end - - replace!(/#{flag}/, content, config[:force]) - end - - def revoke! - say_status :revoke - - regexp = if @behavior == :after - content = '\1\2' - /(#{flag})(.*)(#{Regexp.escape(replacement)})/m - else - content = '\2\3' - /(#{Regexp.escape(replacement)})(.*)(#{flag})/m - end - - replace!(regexp, content, true) - end - - protected - - def say_status(behavior) - status = if behavior == :invoke - if flag == /\A/ - :prepend - elsif flag == /\z/ - :append - else - :insert - end - else - :subtract - end - - super(status, config[:verbose]) - end - - # Adds the content to the file. - # - def replace!(regexp, string, force) - unless base.options[:pretend] - content = File.binread(destination) - if force || !content.include?(replacement) - content.gsub!(regexp, string) - File.open(destination, "wb") { |file| file.write(content) } - end - end - end - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/base.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/base.rb deleted file mode 100644 index 56b78ebad6..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/base.rb +++ /dev/null @@ -1,656 +0,0 @@ -require "thor/command" -require "thor/core_ext/hash_with_indifferent_access" -require "thor/core_ext/ordered_hash" -require "thor/error" -require "thor/invocation" -require "thor/parser" -require "thor/shell" -require "thor/line_editor" -require "thor/util" - -class Bundler::Thor - autoload :Actions, "thor/actions" - autoload :RakeCompat, "thor/rake_compat" - autoload :Group, "thor/group" - - # Shortcuts for help. - HELP_MAPPINGS = %w[-h -? --help -D] - - # Bundler::Thor methods that should not be overwritten by the user. - THOR_RESERVED_WORDS = %w[invoke shell options behavior root destination_root relative_root - action add_file create_file in_root inside run run_ruby_script] - - TEMPLATE_EXTNAME = ".tt" - - module Base - attr_accessor :options, :parent_options, :args - - # It receives arguments in an Array and two hashes, one for options and - # other for configuration. - # - # Notice that it does not check if all required arguments were supplied. - # It should be done by the parser. - # - # ==== Parameters - # args:: An array of objects. The objects are applied to their - # respective accessors declared with argument. - # - # options:: An options hash that will be available as self.options. - # The hash given is converted to a hash with indifferent - # access, magic predicates (options.skip?) and then frozen. - # - # config:: Configuration for this Bundler::Thor class. - # - def initialize(args = [], local_options = {}, config = {}) # rubocop:disable MethodLength - parse_options = self.class.class_options - - # The start method splits inbound arguments at the first argument - # that looks like an option (starts with - or --). It then calls - # new, passing in the two halves of the arguments Array as the - # first two parameters. - - command_options = config.delete(:command_options) # hook for start - parse_options = parse_options.merge(command_options) if command_options - if local_options.is_a?(Array) - array_options, hash_options = local_options, {} - else - # Handle the case where the class was explicitly instantiated - # with pre-parsed options. - array_options, hash_options = [], local_options - end - - # Let Bundler::Thor::Options parse the options first, so it can remove - # declared options from the array. This will leave us with - # a list of arguments that weren't declared. - stop_on_unknown = self.class.stop_on_unknown_option? config[:current_command] - opts = Bundler::Thor::Options.new(parse_options, hash_options, stop_on_unknown) - self.options = opts.parse(array_options) - self.options = config[:class_options].merge(options) if config[:class_options] - - # If unknown options are disallowed, make sure that none of the - # remaining arguments looks like an option. - opts.check_unknown! if self.class.check_unknown_options?(config) - - # Add the remaining arguments from the options parser to the - # arguments passed in to initialize. Then remove any positional - # arguments declared using #argument (this is primarily used - # by Bundler::Thor::Group). Tis will leave us with the remaining - # positional arguments. - to_parse = args - to_parse += opts.remaining unless self.class.strict_args_position?(config) - - thor_args = Bundler::Thor::Arguments.new(self.class.arguments) - thor_args.parse(to_parse).each { |k, v| __send__("#{k}=", v) } - @args = thor_args.remaining - end - - class << self - def included(base) #:nodoc: - base.extend ClassMethods - base.send :include, Invocation - base.send :include, Shell - end - - # Returns the classes that inherits from Bundler::Thor or Bundler::Thor::Group. - # - # ==== Returns - # Array[Class] - # - def subclasses - @subclasses ||= [] - end - - # Returns the files where the subclasses are kept. - # - # ==== Returns - # Hash[path => Class] - # - def subclass_files - @subclass_files ||= Hash.new { |h, k| h[k] = [] } - end - - # Whenever a class inherits from Bundler::Thor or Bundler::Thor::Group, we should track the - # class and the file on Bundler::Thor::Base. This is the method responsable for it. - # - def register_klass_file(klass) #:nodoc: - file = caller[1].match(/(.*):\d+/)[1] - Bundler::Thor::Base.subclasses << klass unless Bundler::Thor::Base.subclasses.include?(klass) - - file_subclasses = Bundler::Thor::Base.subclass_files[File.expand_path(file)] - file_subclasses << klass unless file_subclasses.include?(klass) - end - end - - module ClassMethods - def attr_reader(*) #:nodoc: - no_commands { super } - end - - def attr_writer(*) #:nodoc: - no_commands { super } - end - - def attr_accessor(*) #:nodoc: - no_commands { super } - end - - # If you want to raise an error for unknown options, call check_unknown_options! - # This is disabled by default to allow dynamic invocations. - def check_unknown_options! - @check_unknown_options = true - end - - def check_unknown_options #:nodoc: - @check_unknown_options ||= from_superclass(:check_unknown_options, false) - end - - def check_unknown_options?(config) #:nodoc: - !!check_unknown_options - end - - # If true, option parsing is suspended as soon as an unknown option or a - # regular argument is encountered. All remaining arguments are passed to - # the command as regular arguments. - def stop_on_unknown_option?(command_name) #:nodoc: - false - end - - # If you want only strict string args (useful when cascading thor classes), - # call strict_args_position! This is disabled by default to allow dynamic - # invocations. - def strict_args_position! - @strict_args_position = true - end - - def strict_args_position #:nodoc: - @strict_args_position ||= from_superclass(:strict_args_position, false) - end - - def strict_args_position?(config) #:nodoc: - !!strict_args_position - end - - # Adds an argument to the class and creates an attr_accessor for it. - # - # Arguments are different from options in several aspects. The first one - # is how they are parsed from the command line, arguments are retrieved - # from position: - # - # thor command NAME - # - # Instead of: - # - # thor command --name=NAME - # - # Besides, arguments are used inside your code as an accessor (self.argument), - # while options are all kept in a hash (self.options). - # - # Finally, arguments cannot have type :default or :boolean but can be - # optional (supplying :optional => :true or :required => false), although - # you cannot have a required argument after a non-required argument. If you - # try it, an error is raised. - # - # ==== Parameters - # name:: The name of the argument. - # options:: Described below. - # - # ==== Options - # :desc - Description for the argument. - # :required - If the argument is required or not. - # :optional - If the argument is optional or not. - # :type - The type of the argument, can be :string, :hash, :array, :numeric. - # :default - Default value for this argument. It cannot be required and have default values. - # :banner - String to show on usage notes. - # - # ==== Errors - # ArgumentError:: Raised if you supply a required argument after a non required one. - # - def argument(name, options = {}) # rubocop:disable MethodLength - is_thor_reserved_word?(name, :argument) - no_commands { attr_accessor name } - - required = if options.key?(:optional) - !options[:optional] - elsif options.key?(:required) - options[:required] - else - options[:default].nil? - end - - remove_argument name - - arguments.each do |argument| - next if argument.required? - fail ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " << - "the non-required argument #{argument.human_name.inspect}." - end if required - - options[:required] = required - - arguments << Bundler::Thor::Argument.new(name, options) - end - - # Returns this class arguments, looking up in the ancestors chain. - # - # ==== Returns - # Array[Bundler::Thor::Argument] - # - def arguments - @arguments ||= from_superclass(:arguments, []) - end - - # Adds a bunch of options to the set of class options. - # - # class_options :foo => false, :bar => :required, :baz => :string - # - # If you prefer more detailed declaration, check class_option. - # - # ==== Parameters - # Hash[Symbol => Object] - # - def class_options(options = nil) - @class_options ||= from_superclass(:class_options, {}) - build_options(options, @class_options) if options - @class_options - end - - # Adds an option to the set of class options - # - # ==== Parameters - # name:: The name of the argument. - # options:: Described below. - # - # ==== Options - # :desc:: -- Description for the argument. - # :required:: -- If the argument is required or not. - # :default:: -- Default value for this argument. - # :group:: -- The group for this options. Use by class options to output options in different levels. - # :aliases:: -- Aliases for this option. Note: Bundler::Thor follows a convention of one-dash-one-letter options. Thus aliases like "-something" wouldn't be parsed; use either "\--something" or "-s" instead. - # :type:: -- The type of the argument, can be :string, :hash, :array, :numeric or :boolean. - # :banner:: -- String to show on usage notes. - # :hide:: -- If you want to hide this option from the help. - # - def class_option(name, options = {}) - build_option(name, options, class_options) - end - - # Removes a previous defined argument. If :undefine is given, undefine - # accessors as well. - # - # ==== Parameters - # names:: Arguments to be removed - # - # ==== Examples - # - # remove_argument :foo - # remove_argument :foo, :bar, :baz, :undefine => true - # - def remove_argument(*names) - options = names.last.is_a?(Hash) ? names.pop : {} - - names.each do |name| - arguments.delete_if { |a| a.name == name.to_s } - undef_method name, "#{name}=" if options[:undefine] - end - end - - # Removes a previous defined class option. - # - # ==== Parameters - # names:: Class options to be removed - # - # ==== Examples - # - # remove_class_option :foo - # remove_class_option :foo, :bar, :baz - # - def remove_class_option(*names) - names.each do |name| - class_options.delete(name) - end - end - - # Defines the group. This is used when thor list is invoked so you can specify - # that only commands from a pre-defined group will be shown. Defaults to standard. - # - # ==== Parameters - # name - # - def group(name = nil) - if name - @group = name.to_s - else - @group ||= from_superclass(:group, "standard") - end - end - - # Returns the commands for this Bundler::Thor class. - # - # ==== Returns - # OrderedHash:: An ordered hash with commands names as keys and Bundler::Thor::Command - # objects as values. - # - def commands - @commands ||= Bundler::Thor::CoreExt::OrderedHash.new - end - alias_method :tasks, :commands - - # Returns the commands for this Bundler::Thor class and all subclasses. - # - # ==== Returns - # OrderedHash:: An ordered hash with commands names as keys and Bundler::Thor::Command - # objects as values. - # - def all_commands - @all_commands ||= from_superclass(:all_commands, Bundler::Thor::CoreExt::OrderedHash.new) - @all_commands.merge(commands) - end - alias_method :all_tasks, :all_commands - - # Removes a given command from this Bundler::Thor class. This is usually done if you - # are inheriting from another class and don't want it to be available - # anymore. - # - # By default it only remove the mapping to the command. But you can supply - # :undefine => true to undefine the method from the class as well. - # - # ==== Parameters - # name:: The name of the command to be removed - # options:: You can give :undefine => true if you want commands the method - # to be undefined from the class as well. - # - def remove_command(*names) - options = names.last.is_a?(Hash) ? names.pop : {} - - names.each do |name| - commands.delete(name.to_s) - all_commands.delete(name.to_s) - undef_method name if options[:undefine] - end - end - alias_method :remove_task, :remove_command - - # All methods defined inside the given block are not added as commands. - # - # So you can do: - # - # class MyScript < Bundler::Thor - # no_commands do - # def this_is_not_a_command - # end - # end - # end - # - # You can also add the method and remove it from the command list: - # - # class MyScript < Bundler::Thor - # def this_is_not_a_command - # end - # remove_command :this_is_not_a_command - # end - # - def no_commands - @no_commands = true - yield - ensure - @no_commands = false - end - alias_method :no_tasks, :no_commands - - # Sets the namespace for the Bundler::Thor or Bundler::Thor::Group class. By default the - # namespace is retrieved from the class name. If your Bundler::Thor class is named - # Scripts::MyScript, the help method, for example, will be called as: - # - # thor scripts:my_script -h - # - # If you change the namespace: - # - # namespace :my_scripts - # - # You change how your commands are invoked: - # - # thor my_scripts -h - # - # Finally, if you change your namespace to default: - # - # namespace :default - # - # Your commands can be invoked with a shortcut. Instead of: - # - # thor :my_command - # - def namespace(name = nil) - if name - @namespace = name.to_s - else - @namespace ||= Bundler::Thor::Util.namespace_from_thor_class(self) - end - end - - # Parses the command and options from the given args, instantiate the class - # and invoke the command. This method is used when the arguments must be parsed - # from an array. If you are inside Ruby and want to use a Bundler::Thor class, you - # can simply initialize it: - # - # script = MyScript.new(args, options, config) - # script.invoke(:command, first_arg, second_arg, third_arg) - # - def start(given_args = ARGV, config = {}) - config[:shell] ||= Bundler::Thor::Base.shell.new - dispatch(nil, given_args.dup, nil, config) - rescue Bundler::Thor::Error => e - config[:debug] || ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message) - exit(1) if exit_on_failure? - rescue Errno::EPIPE - # This happens if a thor command is piped to something like `head`, - # which closes the pipe when it's done reading. This will also - # mean that if the pipe is closed, further unnecessary - # computation will not occur. - exit(0) - end - - # Allows to use private methods from parent in child classes as commands. - # - # ==== Parameters - # names:: Method names to be used as commands - # - # ==== Examples - # - # public_command :foo - # public_command :foo, :bar, :baz - # - def public_command(*names) - names.each do |name| - class_eval "def #{name}(*); super end" - end - end - alias_method :public_task, :public_command - - def handle_no_command_error(command, has_namespace = $thor_runner) #:nodoc: - if has_namespace - fail UndefinedCommandError, "Could not find command #{command.inspect} in #{namespace.inspect} namespace." - else - fail UndefinedCommandError, "Could not find command #{command.inspect}." - end - end - alias_method :handle_no_task_error, :handle_no_command_error - - def handle_argument_error(command, error, args, arity) #:nodoc: - msg = "ERROR: \"#{basename} #{command.name}\" was called with " - msg << "no arguments" if args.empty? - msg << "arguments " << args.inspect unless args.empty? - msg << "\nUsage: #{banner(command).inspect}" - fail InvocationError, msg - end - - protected - - # Prints the class options per group. If an option does not belong to - # any group, it's printed as Class option. - # - def class_options_help(shell, groups = {}) #:nodoc: - # Group options by group - class_options.each do |_, value| - groups[value.group] ||= [] - groups[value.group] << value - end - - # Deal with default group - global_options = groups.delete(nil) || [] - print_options(shell, global_options) - - # Print all others - groups.each do |group_name, options| - print_options(shell, options, group_name) - end - end - - # Receives a set of options and print them. - def print_options(shell, options, group_name = nil) - return if options.empty? - - list = [] - padding = options.map { |o| o.aliases.size }.max.to_i * 4 - - options.each do |option| - unless option.hide - item = [option.usage(padding)] - item.push(option.description ? "# #{option.description}" : "") - - list << item - list << ["", "# Default: #{option.default}"] if option.show_default? - list << ["", "# Possible values: #{option.enum.join(', ')}"] if option.enum - end - end - - shell.say(group_name ? "#{group_name} options:" : "Options:") - shell.print_table(list, :indent => 2) - shell.say "" - end - - # Raises an error if the word given is a Bundler::Thor reserved word. - def is_thor_reserved_word?(word, type) #:nodoc: - return false unless THOR_RESERVED_WORDS.include?(word.to_s) - fail "#{word.inspect} is a Bundler::Thor reserved word and cannot be defined as #{type}" - end - - # Build an option and adds it to the given scope. - # - # ==== Parameters - # name:: The name of the argument. - # options:: Described in both class_option and method_option. - # scope:: Options hash that is being built up - def build_option(name, options, scope) #:nodoc: - scope[name] = Bundler::Thor::Option.new(name, options) - end - - # Receives a hash of options, parse them and add to the scope. This is a - # fast way to set a bunch of options: - # - # build_options :foo => true, :bar => :required, :baz => :string - # - # ==== Parameters - # Hash[Symbol => Object] - def build_options(options, scope) #:nodoc: - options.each do |key, value| - scope[key] = Bundler::Thor::Option.parse(key, value) - end - end - - # Finds a command with the given name. If the command belongs to the current - # class, just return it, otherwise dup it and add the fresh copy to the - # current command hash. - def find_and_refresh_command(name) #:nodoc: - if commands[name.to_s] - commands[name.to_s] - elsif command = all_commands[name.to_s] # rubocop:disable AssignmentInCondition - commands[name.to_s] = command.clone - else - fail ArgumentError, "You supplied :for => #{name.inspect}, but the command #{name.inspect} could not be found." - end - end - alias_method :find_and_refresh_task, :find_and_refresh_command - - # Everytime someone inherits from a Bundler::Thor class, register the klass - # and file into baseclass. - def inherited(klass) - Bundler::Thor::Base.register_klass_file(klass) - klass.instance_variable_set(:@no_commands, false) - end - - # Fire this callback whenever a method is added. Added methods are - # tracked as commands by invoking the create_command method. - def method_added(meth) - meth = meth.to_s - - if meth == "initialize" - initialize_added - return - end - - # Return if it's not a public instance method - return unless public_method_defined?(meth.to_sym) - - @no_commands ||= false - return if @no_commands || !create_command(meth) - - is_thor_reserved_word?(meth, :command) - Bundler::Thor::Base.register_klass_file(self) - end - - # Retrieves a value from superclass. If it reaches the baseclass, - # returns default. - def from_superclass(method, default = nil) - if self == baseclass || !superclass.respond_to?(method, true) - default - else - value = superclass.send(method) - - # Ruby implements `dup` on Object, but raises a `TypeError` - # if the method is called on immediates. As a result, we - # don't have a good way to check whether dup will succeed - # without calling it and rescuing the TypeError. - begin - value.dup - rescue TypeError - value - end - - end - end - - # A flag that makes the process exit with status 1 if any error happens. - def exit_on_failure? - false - end - - # - # The basename of the program invoking the thor class. - # - def basename - File.basename($PROGRAM_NAME).split(" ").first - end - - # SIGNATURE: Sets the baseclass. This is where the superclass lookup - # finishes. - def baseclass #:nodoc: - end - - # SIGNATURE: Creates a new command if valid_command? is true. This method is - # called when a new method is added to the class. - def create_command(meth) #:nodoc: - end - alias_method :create_task, :create_command - - # SIGNATURE: Defines behavior when the initialize method is added to the - # class. - def initialize_added #:nodoc: - end - - # SIGNATURE: The hook invoked by start. - def dispatch(command, given_args, given_opts, config) #:nodoc: - fail NotImplementedError - end - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/command.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/command.rb deleted file mode 100644 index 72c8348cb6..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/command.rb +++ /dev/null @@ -1,133 +0,0 @@ -class Bundler::Thor - class Command < Struct.new(:name, :description, :long_description, :usage, :options) - FILE_REGEXP = /^#{Regexp.escape(File.dirname(__FILE__))}/ - - def initialize(name, description, long_description, usage, options = nil) - super(name.to_s, description, long_description, usage, options || {}) - end - - def initialize_copy(other) #:nodoc: - super(other) - self.options = other.options.dup if other.options - end - - def hidden? - false - end - - # By default, a command invokes a method in the thor class. You can change this - # implementation to create custom commands. - def run(instance, args = []) - arity = nil - - if private_method?(instance) - instance.class.handle_no_command_error(name) - elsif public_method?(instance) - arity = instance.method(name).arity - instance.__send__(name, *args) - elsif local_method?(instance, :method_missing) - instance.__send__(:method_missing, name.to_sym, *args) - else - instance.class.handle_no_command_error(name) - end - rescue ArgumentError => e - handle_argument_error?(instance, e, caller) ? instance.class.handle_argument_error(self, e, args, arity) : (raise e) - rescue NoMethodError => e - handle_no_method_error?(instance, e, caller) ? instance.class.handle_no_command_error(name) : (fail e) - end - - # Returns the formatted usage by injecting given required arguments - # and required options into the given usage. - def formatted_usage(klass, namespace = true, subcommand = false) - if namespace - namespace = klass.namespace - formatted = "#{namespace.gsub(/^(default)/, '')}:" - end - formatted = "#{klass.namespace.split(':').last} " if subcommand - - formatted ||= "" - - # Add usage with required arguments - formatted << if klass && !klass.arguments.empty? - usage.to_s.gsub(/^#{name}/) do |match| - match << " " << klass.arguments.map { |a| a.usage }.compact.join(" ") - end - else - usage.to_s - end - - # Add required options - formatted << " #{required_options}" - - # Strip and go! - formatted.strip - end - - protected - - def not_debugging?(instance) - !(instance.class.respond_to?(:debugging) && instance.class.debugging) - end - - def required_options - @required_options ||= options.map { |_, o| o.usage if o.required? }.compact.sort.join(" ") - end - - # Given a target, checks if this class name is a public method. - def public_method?(instance) #:nodoc: - !(instance.public_methods & [name.to_s, name.to_sym]).empty? - end - - def private_method?(instance) - !(instance.private_methods & [name.to_s, name.to_sym]).empty? - end - - def local_method?(instance, name) - methods = instance.public_methods(false) + instance.private_methods(false) + instance.protected_methods(false) - !(methods & [name.to_s, name.to_sym]).empty? - end - - def sans_backtrace(backtrace, caller) #:nodoc: - saned = backtrace.reject { |frame| frame =~ FILE_REGEXP || (frame =~ /\.java:/ && RUBY_PLATFORM =~ /java/) || (frame =~ /^kernel\// && RUBY_ENGINE =~ /rbx/) } - saned - caller - end - - def handle_argument_error?(instance, error, caller) - not_debugging?(instance) && (error.message =~ /wrong number of arguments/ || error.message =~ /given \d*, expected \d*/) && begin - saned = sans_backtrace(error.backtrace, caller) - # Ruby 1.9 always include the called method in the backtrace - saned.empty? || (saned.size == 1 && RUBY_VERSION >= "1.9") - end - end - - def handle_no_method_error?(instance, error, caller) - not_debugging?(instance) && - error.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/ - end - end - Task = Command # rubocop:disable ConstantName - - # A command that is hidden in help messages but still invocable. - class HiddenCommand < Command - def hidden? - true - end - end - HiddenTask = HiddenCommand # rubocop:disable ConstantName - - # A dynamic command that handles method missing scenarios. - class DynamicCommand < Command - def initialize(name, options = nil) - super(name.to_s, "A dynamically-generated command", name.to_s, name.to_s, options) - end - - def run(instance, args = []) - if (instance.methods & [name.to_s, name.to_sym]).empty? - super - else - instance.class.handle_no_command_error(name) - end - end - end - DynamicTask = DynamicCommand # rubocop:disable ConstantName -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/hash_with_indifferent_access.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/hash_with_indifferent_access.rb deleted file mode 100644 index 6cf61db812..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/hash_with_indifferent_access.rb +++ /dev/null @@ -1,77 +0,0 @@ -class Bundler::Thor - module CoreExt #:nodoc: - # A hash with indifferent access and magic predicates. - # - # hash = Bundler::Thor::CoreExt::HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true - # - # hash[:foo] #=> 'bar' - # hash['foo'] #=> 'bar' - # hash.foo? #=> true - # - class HashWithIndifferentAccess < ::Hash #:nodoc: - def initialize(hash = {}) - super() - hash.each do |key, value| - self[convert_key(key)] = value - end - end - - def [](key) - super(convert_key(key)) - end - - def []=(key, value) - super(convert_key(key), value) - end - - def delete(key) - super(convert_key(key)) - end - - def values_at(*indices) - indices.map { |key| self[convert_key(key)] } - end - - def merge(other) - dup.merge!(other) - end - - def merge!(other) - other.each do |key, value| - self[convert_key(key)] = value - end - self - end - - # Convert to a Hash with String keys. - def to_hash - Hash.new(default).merge!(self) - end - - protected - - def convert_key(key) - key.is_a?(Symbol) ? key.to_s : key - end - - # Magic predicates. For instance: - # - # options.force? # => !!options['force'] - # options.shebang # => "/usr/lib/local/ruby" - # options.test_framework?(:rspec) # => options[:test_framework] == :rspec - # - def method_missing(method, *args, &block) - method = method.to_s - if method =~ /^(\w+)\?$/ - if args.empty? - !!self[$1] - else - self[$1] == args.first - end - else - self[method] - end - end - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/io_binary_read.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/io_binary_read.rb deleted file mode 100644 index 19f3c3d43e..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/io_binary_read.rb +++ /dev/null @@ -1,10 +0,0 @@ -class IO #:nodoc: - class << self - def binread(file, *args) - fail ArgumentError, "wrong number of arguments (#{1 + args.size} for 1..3)" unless args.size < 3 - File.open(file, "rb") do |f| - f.read(*args) - end - end unless method_defined? :binread - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/ordered_hash.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/ordered_hash.rb deleted file mode 100644 index 7e80672a07..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/ordered_hash.rb +++ /dev/null @@ -1,98 +0,0 @@ -class Bundler::Thor - module CoreExt #:nodoc: - if RUBY_VERSION >= "1.9" - class OrderedHash < ::Hash - end - else - # This class is based on the Ruby 1.9 ordered hashes. - # - # It keeps the semantics and most of the efficiency of normal hashes - # while also keeping track of the order in which elements were set. - # - class OrderedHash #:nodoc: - include Enumerable - - Node = Struct.new(:key, :value, :next, :prev) - - def initialize - @hash = {} - end - - def [](key) - @hash[key] && @hash[key].value - end - - def []=(key, value) - if node = @hash[key] # rubocop:disable AssignmentInCondition - node.value = value - else - node = Node.new(key, value) - - if !defined?(@first) || @first.nil? - @first = @last = node - else - node.prev = @last - @last.next = node - @last = node - end - end - - @hash[key] = node - value - end - - def delete(key) - if node = @hash[key] # rubocop:disable AssignmentInCondition - prev_node = node.prev - next_node = node.next - - next_node.prev = prev_node if next_node - prev_node.next = next_node if prev_node - - @first = next_node if @first == node - @last = prev_node if @last == node - - value = node.value - end - - @hash.delete(key) - value - end - - def keys - map { |k, v| k } - end - - def values - map { |k, v| v } - end - - def each - return unless defined?(@first) && @first - yield [@first.key, @first.value] - node = @first - yield [node.key, node.value] while node = node.next # rubocop:disable AssignmentInCondition - self - end - - def merge(other) - hash = self.class.new - - each do |key, value| - hash[key] = value - end - - other.each do |key, value| - hash[key] = value - end - - hash - end - - def empty? - @hash.empty? - end - end - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/error.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/error.rb deleted file mode 100644 index fc34c11268..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/error.rb +++ /dev/null @@ -1,32 +0,0 @@ -class Bundler::Thor - # Bundler::Thor::Error is raised when it's caused by wrong usage of thor classes. Those - # errors have their backtrace suppressed and are nicely shown to the user. - # - # Errors that are caused by the developer, like declaring a method which - # overwrites a thor keyword, it SHOULD NOT raise a Bundler::Thor::Error. This way, we - # ensure that developer errors are shown with full backtrace. - class Error < StandardError - end - - # Raised when a command was not found. - class UndefinedCommandError < Error - end - UndefinedTaskError = UndefinedCommandError # rubocop:disable ConstantName - - class AmbiguousCommandError < Error - end - AmbiguousTaskError = AmbiguousCommandError # rubocop:disable ConstantName - - # Raised when a command was found, but not invoked properly. - class InvocationError < Error - end - - class UnknownArgumentError < Error - end - - class RequiredArgumentMissingError < InvocationError - end - - class MalformattedArgumentError < InvocationError - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/group.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/group.rb deleted file mode 100644 index 71e7f1c3b8..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/group.rb +++ /dev/null @@ -1,281 +0,0 @@ -require "thor/base" - -# Bundler::Thor has a special class called Bundler::Thor::Group. The main difference to Bundler::Thor class -# is that it invokes all commands at once. It also include some methods that allows -# invocations to be done at the class method, which are not available to Bundler::Thor -# commands. -class Bundler::Thor::Group # rubocop:disable ClassLength - class << self - # The description for this Bundler::Thor::Group. If none is provided, but a source root - # exists, tries to find the USAGE one folder above it, otherwise searches - # in the superclass. - # - # ==== Parameters - # description:: The description for this Bundler::Thor::Group. - # - def desc(description = nil) - if description - @desc = description - else - @desc ||= from_superclass(:desc, nil) - end - end - - # Prints help information. - # - # ==== Options - # short:: When true, shows only usage. - # - def help(shell) - shell.say "Usage:" - shell.say " #{banner}\n" - shell.say - class_options_help(shell) - shell.say desc if desc - end - - # Stores invocations for this class merging with superclass values. - # - def invocations #:nodoc: - @invocations ||= from_superclass(:invocations, {}) - end - - # Stores invocation blocks used on invoke_from_option. - # - def invocation_blocks #:nodoc: - @invocation_blocks ||= from_superclass(:invocation_blocks, {}) - end - - # Invoke the given namespace or class given. It adds an instance - # method that will invoke the klass and command. You can give a block to - # configure how it will be invoked. - # - # The namespace/class given will have its options showed on the help - # usage. Check invoke_from_option for more information. - # - def invoke(*names, &block) # rubocop:disable MethodLength - options = names.last.is_a?(Hash) ? names.pop : {} - verbose = options.fetch(:verbose, true) - - names.each do |name| - invocations[name] = false - invocation_blocks[name] = block if block_given? - - class_eval <<-METHOD, __FILE__, __LINE__ - def _invoke_#{name.to_s.gsub(/\W/, "_")} - klass, command = self.class.prepare_for_invocation(nil, #{name.inspect}) - - if klass - say_status :invoke, #{name.inspect}, #{verbose.inspect} - block = self.class.invocation_blocks[#{name.inspect}] - _invoke_for_class_method klass, command, &block - else - say_status :error, %(#{name.inspect} [not found]), :red - end - end - METHOD - end - end - - # Invoke a thor class based on the value supplied by the user to the - # given option named "name". A class option must be created before this - # method is invoked for each name given. - # - # ==== Examples - # - # class GemGenerator < Bundler::Thor::Group - # class_option :test_framework, :type => :string - # invoke_from_option :test_framework - # end - # - # ==== Boolean options - # - # In some cases, you want to invoke a thor class if some option is true or - # false. This is automatically handled by invoke_from_option. Then the - # option name is used to invoke the generator. - # - # ==== Preparing for invocation - # - # In some cases you want to customize how a specified hook is going to be - # invoked. You can do that by overwriting the class method - # prepare_for_invocation. The class method must necessarily return a klass - # and an optional command. - # - # ==== Custom invocations - # - # You can also supply a block to customize how the option is going to be - # invoked. The block receives two parameters, an instance of the current - # class and the klass to be invoked. - # - def invoke_from_option(*names, &block) # rubocop:disable MethodLength - options = names.last.is_a?(Hash) ? names.pop : {} - verbose = options.fetch(:verbose, :white) - - names.each do |name| - unless class_options.key?(name) - fail ArgumentError, "You have to define the option #{name.inspect} " << - "before setting invoke_from_option." - end - - invocations[name] = true - invocation_blocks[name] = block if block_given? - - class_eval <<-METHOD, __FILE__, __LINE__ - def _invoke_from_option_#{name.to_s.gsub(/\W/, "_")} - return unless options[#{name.inspect}] - - value = options[#{name.inspect}] - value = #{name.inspect} if TrueClass === value - klass, command = self.class.prepare_for_invocation(#{name.inspect}, value) - - if klass - say_status :invoke, value, #{verbose.inspect} - block = self.class.invocation_blocks[#{name.inspect}] - _invoke_for_class_method klass, command, &block - else - say_status :error, %(\#{value} [not found]), :red - end - end - METHOD - end - end - - # Remove a previously added invocation. - # - # ==== Examples - # - # remove_invocation :test_framework - # - def remove_invocation(*names) - names.each do |name| - remove_command(name) - remove_class_option(name) - invocations.delete(name) - invocation_blocks.delete(name) - end - end - - # Overwrite class options help to allow invoked generators options to be - # shown recursively when invoking a generator. - # - def class_options_help(shell, groups = {}) #:nodoc: - get_options_from_invocations(groups, class_options) do |klass| - klass.send(:get_options_from_invocations, groups, class_options) - end - super(shell, groups) - end - - # Get invocations array and merge options from invocations. Those - # options are added to group_options hash. Options that already exists - # in base_options are not added twice. - # - def get_options_from_invocations(group_options, base_options) #:nodoc: # rubocop:disable MethodLength - invocations.each do |name, from_option| - value = if from_option - option = class_options[name] - option.type == :boolean ? name : option.default - else - name - end - next unless value - - klass, _ = prepare_for_invocation(name, value) - next unless klass && klass.respond_to?(:class_options) - - value = value.to_s - human_name = value.respond_to?(:classify) ? value.classify : value - - group_options[human_name] ||= [] - group_options[human_name] += klass.class_options.values.select do |class_option| - base_options[class_option.name.to_sym].nil? && class_option.group.nil? && - !group_options.values.flatten.any? { |i| i.name == class_option.name } - end - - yield klass if block_given? - end - end - - # Returns commands ready to be printed. - def printable_commands(*) - item = [] - item << banner - item << (desc ? "# #{desc.gsub(/\s+/m, ' ')}" : "") - [item] - end - alias_method :printable_tasks, :printable_commands - - def handle_argument_error(command, error, args, arity) #:nodoc: - msg = "#{basename} #{command.name} takes #{arity} argument" - msg << "s" if arity > 1 - msg << ", but it should not." - fail error, msg - end - - protected - - # The method responsible for dispatching given the args. - def dispatch(command, given_args, given_opts, config) #:nodoc: - if Bundler::Thor::HELP_MAPPINGS.include?(given_args.first) - help(config[:shell]) - return - end - - args, opts = Bundler::Thor::Options.split(given_args) - opts = given_opts || opts - - instance = new(args, opts, config) - yield instance if block_given? - - if command - instance.invoke_command(all_commands[command]) - else - instance.invoke_all - end - end - - # The banner for this class. You can customize it if you are invoking the - # thor class by another ways which is not the Bundler::Thor::Runner. - def banner - "#{basename} #{self_command.formatted_usage(self, false)}" - end - - # Represents the whole class as a command. - def self_command #:nodoc: - Bundler::Thor::DynamicCommand.new(namespace, class_options) - end - alias_method :self_task, :self_command - - def baseclass #:nodoc: - Bundler::Thor::Group - end - - def create_command(meth) #:nodoc: - commands[meth.to_s] = Bundler::Thor::Command.new(meth, nil, nil, nil, nil) - true - end - alias_method :create_task, :create_command - end - - include Bundler::Thor::Base - -protected - - # Shortcut to invoke with padding and block handling. Use internally by - # invoke and invoke_from_option class methods. - def _invoke_for_class_method(klass, command = nil, *args, &block) #:nodoc: - with_padding do - if block - case block.arity - when 3 - block.call(self, klass, command) - when 2 - block.call(self, klass) - when 1 - instance_exec(klass, &block) - end - else - invoke klass, command, *args - end - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/invocation.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/invocation.rb deleted file mode 100644 index 684df2c616..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/invocation.rb +++ /dev/null @@ -1,178 +0,0 @@ -class Bundler::Thor - module Invocation - def self.included(base) #:nodoc: - base.extend ClassMethods - end - - module ClassMethods - # This method is responsible for receiving a name and find the proper - # class and command for it. The key is an optional parameter which is - # available only in class methods invocations (i.e. in Bundler::Thor::Group). - def prepare_for_invocation(key, name) #:nodoc: - case name - when Symbol, String - Bundler::Thor::Util.find_class_and_command_by_namespace(name.to_s, !key) - else - name - end - end - end - - # Make initializer aware of invocations and the initialization args. - def initialize(args = [], options = {}, config = {}, &block) #:nodoc: - @_invocations = config[:invocations] || Hash.new { |h, k| h[k] = [] } - @_initializer = [args, options, config] - super - end - - # Make the current command chain accessible with in a Bundler::Thor-(sub)command - def current_command_chain - @_invocations.values.flatten.map(&:to_sym) - end - - # Receives a name and invokes it. The name can be a string (either "command" or - # "namespace:command"), a Bundler::Thor::Command, a Class or a Bundler::Thor instance. If the - # command cannot be guessed by name, it can also be supplied as second argument. - # - # You can also supply the arguments, options and configuration values for - # the command to be invoked, if none is given, the same values used to - # initialize the invoker are used to initialize the invoked. - # - # When no name is given, it will invoke the default command of the current class. - # - # ==== Examples - # - # class A < Bundler::Thor - # def foo - # invoke :bar - # invoke "b:hello", ["Erik"] - # end - # - # def bar - # invoke "b:hello", ["Erik"] - # end - # end - # - # class B < Bundler::Thor - # def hello(name) - # puts "hello #{name}" - # end - # end - # - # You can notice that the method "foo" above invokes two commands: "bar", - # which belongs to the same class and "hello" which belongs to the class B. - # - # By using an invocation system you ensure that a command is invoked only once. - # In the example above, invoking "foo" will invoke "b:hello" just once, even - # if it's invoked later by "bar" method. - # - # When class A invokes class B, all arguments used on A initialization are - # supplied to B. This allows lazy parse of options. Let's suppose you have - # some rspec commands: - # - # class Rspec < Bundler::Thor::Group - # class_option :mock_framework, :type => :string, :default => :rr - # - # def invoke_mock_framework - # invoke "rspec:#{options[:mock_framework]}" - # end - # end - # - # As you noticed, it invokes the given mock framework, which might have its - # own options: - # - # class Rspec::RR < Bundler::Thor::Group - # class_option :style, :type => :string, :default => :mock - # end - # - # Since it's not rspec concern to parse mock framework options, when RR - # is invoked all options are parsed again, so RR can extract only the options - # that it's going to use. - # - # If you want Rspec::RR to be initialized with its own set of options, you - # have to do that explicitly: - # - # invoke "rspec:rr", [], :style => :foo - # - # Besides giving an instance, you can also give a class to invoke: - # - # invoke Rspec::RR, [], :style => :foo - # - def invoke(name = nil, *args) - if name.nil? - warn "[Bundler::Thor] Calling invoke() without argument is deprecated. Please use invoke_all instead.\n#{caller.join("\n")}" - return invoke_all - end - - args.unshift(nil) if args.first.is_a?(Array) || args.first.nil? - command, args, opts, config = args - - klass, command = _retrieve_class_and_command(name, command) - fail "Missing Bundler::Thor class for invoke #{name}" unless klass - fail "Expected Bundler::Thor class, got #{klass}" unless klass <= Bundler::Thor::Base - - args, opts, config = _parse_initialization_options(args, opts, config) - klass.send(:dispatch, command, args, opts, config) do |instance| - instance.parent_options = options - end - end - - # Invoke the given command if the given args. - def invoke_command(command, *args) #:nodoc: - current = @_invocations[self.class] - - unless current.include?(command.name) - current << command.name - command.run(self, *args) - end - end - alias_method :invoke_task, :invoke_command - - # Invoke all commands for the current instance. - def invoke_all #:nodoc: - self.class.all_commands.map { |_, command| invoke_command(command) } - end - - # Invokes using shell padding. - def invoke_with_padding(*args) - with_padding { invoke(*args) } - end - - protected - - # Configuration values that are shared between invocations. - def _shared_configuration #:nodoc: - {:invocations => @_invocations} - end - - # This method simply retrieves the class and command to be invoked. - # If the name is nil or the given name is a command in the current class, - # use the given name and return self as class. Otherwise, call - # prepare_for_invocation in the current class. - def _retrieve_class_and_command(name, sent_command = nil) #:nodoc: - case - when name.nil? - [self.class, nil] - when self.class.all_commands[name.to_s] - [self.class, name.to_s] - else - klass, command = self.class.prepare_for_invocation(nil, name) - [klass, command || sent_command] - end - end - alias_method :_retrieve_class_and_task, :_retrieve_class_and_command - - # Initialize klass using values stored in the @_initializer. - def _parse_initialization_options(args, opts, config) #:nodoc: - stored_args, stored_opts, stored_config = @_initializer - - args ||= stored_args.dup - opts ||= stored_opts.dup - - config ||= {} - config = stored_config.merge(_shared_configuration).merge!(config) - - [args, opts, config] - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor.rb deleted file mode 100644 index 95c848e0e3..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor.rb +++ /dev/null @@ -1,17 +0,0 @@ -require "thor/line_editor/basic" -require "thor/line_editor/readline" - -class Bundler::Thor - module LineEditor - def self.readline(prompt, options = {}) - best_available.new(prompt, options).readline - end - - def self.best_available - [ - Bundler::Thor::LineEditor::Readline, - Bundler::Thor::LineEditor::Basic - ].detect(&:available?) - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor/basic.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor/basic.rb deleted file mode 100644 index b121e95575..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor/basic.rb +++ /dev/null @@ -1,35 +0,0 @@ -class Bundler::Thor - module LineEditor - class Basic - attr_reader :prompt, :options - - def self.available? - true - end - - def initialize(prompt, options) - @prompt = prompt - @options = options - end - - def readline - $stdout.print(prompt) - get_input - end - - private - - def get_input - if echo? - $stdin.gets - else - $stdin.noecho(&:gets) - end - end - - def echo? - options.fetch(:echo, true) - end - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor/readline.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor/readline.rb deleted file mode 100644 index dd39cff35d..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor/readline.rb +++ /dev/null @@ -1,88 +0,0 @@ -begin - require "readline" -rescue LoadError -end - -class Bundler::Thor - module LineEditor - class Readline < Basic - def self.available? - Object.const_defined?(:Readline) - end - - def readline - if echo? - ::Readline.completion_append_character = nil - # Ruby 1.8.7 does not allow Readline.completion_proc= to receive nil. - if complete = completion_proc - ::Readline.completion_proc = complete - end - ::Readline.readline(prompt, add_to_history?) - else - super - end - end - - private - - def add_to_history? - options.fetch(:add_to_history, true) - end - - def completion_proc - if use_path_completion? - proc { |text| PathCompletion.new(text).matches } - elsif completion_options.any? - proc do |text| - completion_options.select { |option| option.start_with?(text) } - end - end - end - - def completion_options - options.fetch(:limited_to, []) - end - - def use_path_completion? - options.fetch(:path, false) - end - - class PathCompletion - attr_reader :text - private :text - - def initialize(text) - @text = text - end - - def matches - relative_matches - end - - private - - def relative_matches - absolute_matches.map { |path| path.sub(base_path, "") } - end - - def absolute_matches - Dir[glob_pattern].map do |path| - if File.directory?(path) - "#{path}/" - else - path - end - end - end - - def glob_pattern - "#{base_path}#{text}*" - end - - def base_path - "#{Dir.pwd}/" - end - end - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/parser.rb deleted file mode 100644 index 74c789b763..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser.rb +++ /dev/null @@ -1,4 +0,0 @@ -require "thor/parser/argument" -require "thor/parser/arguments" -require "thor/parser/option" -require "thor/parser/options" diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/argument.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/argument.rb deleted file mode 100644 index 84957903cd..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/argument.rb +++ /dev/null @@ -1,73 +0,0 @@ -class Bundler::Thor - class Argument #:nodoc: - VALID_TYPES = [:numeric, :hash, :array, :string] - - attr_reader :name, :description, :enum, :required, :type, :default, :banner - alias_method :human_name, :name - - def initialize(name, options = {}) - class_name = self.class.name.split("::").last - - type = options[:type] - - fail ArgumentError, "#{class_name} name can't be nil." if name.nil? - fail ArgumentError, "Type :#{type} is not valid for #{class_name.downcase}s." if type && !valid_type?(type) - - @name = name.to_s - @description = options[:desc] - @required = options.key?(:required) ? options[:required] : true - @type = (type || :string).to_sym - @default = options[:default] - @banner = options[:banner] || default_banner - @enum = options[:enum] - - validate! # Trigger specific validations - end - - def usage - required? ? banner : "[#{banner}]" - end - - def required? - required - end - - def show_default? - case default - when Array, String, Hash - !default.empty? - else - default - end - end - - protected - - def validate! - if required? && !default.nil? - fail ArgumentError, "An argument cannot be required and have default value." - elsif @enum && !@enum.is_a?(Array) - fail ArgumentError, "An argument cannot have an enum other than an array." - end - end - - def valid_type?(type) - self.class::VALID_TYPES.include?(type.to_sym) - end - - def default_banner - case type - when :boolean - nil - when :string, :default - human_name.upcase - when :numeric - "N" - when :hash - "key:value" - when :array - "one two three" - end - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/arguments.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/arguments.rb deleted file mode 100644 index c7bb648e31..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/arguments.rb +++ /dev/null @@ -1,175 +0,0 @@ -class Bundler::Thor - class Arguments #:nodoc: # rubocop:disable ClassLength - NUMERIC = /(\d*\.\d+|\d+)/ - - # Receives an array of args and returns two arrays, one with arguments - # and one with switches. - # - def self.split(args) - arguments = [] - - args.each do |item| - break if item =~ /^-/ - arguments << item - end - - [arguments, args[Range.new(arguments.size, -1)]] - end - - def self.parse(*args) - to_parse = args.pop - new(*args).parse(to_parse) - end - - # Takes an array of Bundler::Thor::Argument objects. - # - def initialize(arguments = []) - @assigns, @non_assigned_required = {}, [] - @switches = arguments - - arguments.each do |argument| - if !argument.default.nil? - @assigns[argument.human_name] = argument.default - elsif argument.required? - @non_assigned_required << argument - end - end - end - - def parse(args) - @pile = args.dup - - @switches.each do |argument| - break unless peek - @non_assigned_required.delete(argument) - @assigns[argument.human_name] = send(:"parse_#{argument.type}", argument.human_name) - end - - check_requirement! - @assigns - end - - def remaining # rubocop:disable TrivialAccessors - @pile - end - - private - - def no_or_skip?(arg) - arg =~ /^--(no|skip)-([-\w]+)$/ - $2 - end - - def last? - @pile.empty? - end - - def peek - @pile.first - end - - def shift - @pile.shift - end - - def unshift(arg) - if arg.kind_of?(Array) - @pile = arg + @pile - else - @pile.unshift(arg) - end - end - - def current_is_value? - peek && peek.to_s !~ /^-/ - end - - # Runs through the argument array getting strings that contains ":" and - # mark it as a hash: - # - # [ "name:string", "age:integer" ] - # - # Becomes: - # - # { "name" => "string", "age" => "integer" } - # - def parse_hash(name) - return shift if peek.is_a?(Hash) - hash = {} - - while current_is_value? && peek.include?(":") - key, value = shift.split(":", 2) - hash[key] = value - end - hash - end - - # Runs through the argument array getting all strings until no string is - # found or a switch is found. - # - # ["a", "b", "c"] - # - # And returns it as an array: - # - # ["a", "b", "c"] - # - def parse_array(name) - return shift if peek.is_a?(Array) - array = [] - array << shift while current_is_value? - array - end - - # Check if the peek is numeric format and return a Float or Integer. - # Check if the peek is included in enum if enum is provided. - # Otherwise raises an error. - # - def parse_numeric(name) - return shift if peek.is_a?(Numeric) - - unless peek =~ NUMERIC && $& == peek - fail MalformattedArgumentError, "Expected numeric value for '#{name}'; got #{peek.inspect}" - end - - value = $&.index(".") ? shift.to_f : shift.to_i - if @switches.is_a?(Hash) && switch = @switches[name] - if switch.enum && !switch.enum.include?(value) - fail MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}" - end - end - value - end - - # Parse string: - # for --string-arg, just return the current value in the pile - # for --no-string-arg, nil - # Check if the peek is included in enum if enum is provided. Otherwise raises an error. - # - def parse_string(name) - if no_or_skip?(name) - nil - else - value = shift - if @switches.is_a?(Hash) && switch = @switches[name] # rubocop:disable AssignmentInCondition - if switch.enum && !switch.enum.include?(value) - fail MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}" - end - end - value - end - end - - # Raises an error if @non_assigned_required array is not empty. - # - def check_requirement! - unless @non_assigned_required.empty? - names = @non_assigned_required.map do |o| - o.respond_to?(:switch_name) ? o.switch_name : o.human_name - end.join("', '") - - class_name = self.class.name.split("::").last.downcase - fail RequiredArgumentMissingError, "No value provided for required #{class_name} '#{names}'" - end - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/option.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/option.rb deleted file mode 100644 index eb893617f4..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/option.rb +++ /dev/null @@ -1,125 +0,0 @@ -class Bundler::Thor - class Option < Argument #:nodoc: - attr_reader :aliases, :group, :lazy_default, :hide - - VALID_TYPES = [:boolean, :numeric, :hash, :array, :string] - - def initialize(name, options = {}) - options[:required] = false unless options.key?(:required) - super - @lazy_default = options[:lazy_default] - @group = options[:group].to_s.capitalize if options[:group] - @aliases = Array(options[:aliases]) - @hide = options[:hide] - end - - # This parse quick options given as method_options. It makes several - # assumptions, but you can be more specific using the option method. - # - # parse :foo => "bar" - # #=> Option foo with default value bar - # - # parse [:foo, :baz] => "bar" - # #=> Option foo with default value bar and alias :baz - # - # parse :foo => :required - # #=> Required option foo without default value - # - # parse :foo => 2 - # #=> Option foo with default value 2 and type numeric - # - # parse :foo => :numeric - # #=> Option foo without default value and type numeric - # - # parse :foo => true - # #=> Option foo with default value true and type boolean - # - # The valid types are :boolean, :numeric, :hash, :array and :string. If none - # is given a default type is assumed. This default type accepts arguments as - # string (--foo=value) or booleans (just --foo). - # - # By default all options are optional, unless :required is given. - # - def self.parse(key, value) # rubocop:disable MethodLength - if key.is_a?(Array) - name, *aliases = key - else - name, aliases = key, [] - end - - name = name.to_s - default = value - - type = case value - when Symbol - default = nil - if VALID_TYPES.include?(value) - value - elsif required = (value == :required) # rubocop:disable AssignmentInCondition - :string - end - when TrueClass, FalseClass - :boolean - when Numeric - :numeric - when Hash, Array, String - value.class.name.downcase.to_sym - end - new(name.to_s, :required => required, :type => type, :default => default, :aliases => aliases) - end - - def switch_name - @switch_name ||= dasherized? ? name : dasherize(name) - end - - def human_name - @human_name ||= dasherized? ? undasherize(name) : name - end - - def usage(padding = 0) - sample = if banner && !banner.to_s.empty? - "#{switch_name}=#{banner}" - else - switch_name - end - - sample = "[#{sample}]" unless required? - - if boolean? - sample << ", [#{dasherize("no-" + human_name)}]" unless name == "force" - end - - if aliases.empty? - (" " * padding) << sample - else - "#{aliases.join(', ')}, #{sample}" - end - end - - VALID_TYPES.each do |type| - class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{type}? - self.type == #{type.inspect} - end - RUBY - end - - protected - - def validate! - fail ArgumentError, "An option cannot be boolean and required." if boolean? && required? - end - - def dasherized? - name.index("-") == 0 - end - - def undasherize(str) - str.sub(/^-{1,2}/, "") - end - - def dasherize(str) - (str.length > 1 ? "--" : "-") + str.gsub("_", "-") - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/options.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/options.rb deleted file mode 100644 index deac6a0c16..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/options.rb +++ /dev/null @@ -1,218 +0,0 @@ -class Bundler::Thor - class Options < Arguments #:nodoc: # rubocop:disable ClassLength - LONG_RE = /^(--\w+(?:-\w+)*)$/ - SHORT_RE = /^(-[a-z])$/i - EQ_RE = /^(--\w+(?:-\w+)*|-[a-z])=(.*)$/i - SHORT_SQ_RE = /^-([a-z]{2,})$/i # Allow either -x -v or -xv style for single char args - SHORT_NUM = /^(-[a-z])#{NUMERIC}$/i - OPTS_END = "--".freeze - - # Receives a hash and makes it switches. - def self.to_switches(options) - options.map do |key, value| - case value - when true - "--#{key}" - when Array - "--#{key} #{value.map { |v| v.inspect }.join(' ')}" - when Hash - "--#{key} #{value.map { |k, v| "#{k}:#{v}" }.join(' ')}" - when nil, false - "" - else - "--#{key} #{value.inspect}" - end - end.join(" ") - end - - # Takes a hash of Bundler::Thor::Option and a hash with defaults. - # - # If +stop_on_unknown+ is true, #parse will stop as soon as it encounters - # an unknown option or a regular argument. - def initialize(hash_options = {}, defaults = {}, stop_on_unknown = false) - @stop_on_unknown = stop_on_unknown - options = hash_options.values - super(options) - - # Add defaults - defaults.each do |key, value| - @assigns[key.to_s] = value - @non_assigned_required.delete(hash_options[key]) - end - - @shorts, @switches, @extra = {}, {}, [] - - options.each do |option| - @switches[option.switch_name] = option - - option.aliases.each do |short| - name = short.to_s.sub(/^(?!\-)/, "-") - @shorts[name] ||= option.switch_name - end - end - end - - def remaining # rubocop:disable TrivialAccessors - @extra - end - - def peek - return super unless @parsing_options - - result = super - if result == OPTS_END - shift - @parsing_options = false - super - else - result - end - end - - def parse(args) # rubocop:disable MethodLength - @pile = args.dup - @parsing_options = true - - while peek - if parsing_options? - match, is_switch = current_is_switch? - shifted = shift - - if is_switch - case shifted - when SHORT_SQ_RE - unshift($1.split("").map { |f| "-#{f}" }) - next - when EQ_RE, SHORT_NUM - unshift($2) - switch = $1 - when LONG_RE, SHORT_RE - switch = $1 - end - - switch = normalize_switch(switch) - option = switch_option(switch) - @assigns[option.human_name] = parse_peek(switch, option) - elsif @stop_on_unknown - @parsing_options = false - @extra << shifted - @extra << shift while peek - break - elsif match - @extra << shifted - @extra << shift while peek && peek !~ /^-/ - else - @extra << shifted - end - else - @extra << shift - end - end - - check_requirement! - - assigns = Bundler::Thor::CoreExt::HashWithIndifferentAccess.new(@assigns) - assigns.freeze - assigns - end - - def check_unknown! - # an unknown option starts with - or -- and has no more --'s afterward. - unknown = @extra.select { |str| str =~ /^--?(?:(?!--).)*$/ } - fail UnknownArgumentError, "Unknown switches '#{unknown.join(', ')}'" unless unknown.empty? - end - - protected - - # Check if the current value in peek is a registered switch. - # - # Two booleans are returned. The first is true if the current value - # starts with a hyphen; the second is true if it is a registered switch. - def current_is_switch? - case peek - when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM - [true, switch?($1)] - when SHORT_SQ_RE - [true, $1.split("").any? { |f| switch?("-#{f}") }] - else - [false, false] - end - end - - def current_is_switch_formatted? - case peek - when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM, SHORT_SQ_RE - true - else - false - end - end - - def current_is_value? - peek && (!parsing_options? || super) - end - - def switch?(arg) - switch_option(normalize_switch(arg)) - end - - def switch_option(arg) - if match = no_or_skip?(arg) # rubocop:disable AssignmentInCondition - @switches[arg] || @switches["--#{match}"] - else - @switches[arg] - end - end - - # Check if the given argument is actually a shortcut. - # - def normalize_switch(arg) - (@shorts[arg] || arg).tr("_", "-") - end - - def parsing_options? - peek - @parsing_options - end - - # Parse boolean values which can be given as --foo=true, --foo or --no-foo. - # - def parse_boolean(switch) - if current_is_value? - if ["true", "TRUE", "t", "T", true].include?(peek) - shift - true - elsif ["false", "FALSE", "f", "F", false].include?(peek) - shift - false - else - true - end - else - @switches.key?(switch) || !no_or_skip?(switch) - end - end - - # Parse the value at the peek analyzing if it requires an input or not. - # - def parse_peek(switch, option) - if parsing_options? && (current_is_switch_formatted? || last?) - if option.boolean? - # No problem for boolean types - elsif no_or_skip?(switch) - return nil # User set value to nil - elsif option.string? && !option.required? - # Return the default if there is one, else the human name - return option.lazy_default || option.default || option.human_name - elsif option.lazy_default - return option.lazy_default - else - fail MalformattedArgumentError, "No value provided for option '#{switch}'" - end - end - - @non_assigned_required.delete(option) - send(:"parse_#{option.type}", switch) - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/rake_compat.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/rake_compat.rb deleted file mode 100644 index fcf6719df6..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/rake_compat.rb +++ /dev/null @@ -1,71 +0,0 @@ -require "rake" -require "rake/dsl_definition" - -class Bundler::Thor - # Adds a compatibility layer to your Bundler::Thor classes which allows you to use - # rake package tasks. For example, to use rspec rake tasks, one can do: - # - # require 'thor/rake_compat' - # require 'rspec/core/rake_task' - # - # class Default < Bundler::Thor - # include Bundler::Thor::RakeCompat - # - # RSpec::Core::RakeTask.new(:spec) do |t| - # t.spec_opts = ['--options', './.rspec'] - # t.spec_files = FileList['spec/**/*_spec.rb'] - # end - # end - # - module RakeCompat - include Rake::DSL if defined?(Rake::DSL) - - def self.rake_classes - @rake_classes ||= [] - end - - def self.included(base) - # Hack. Make rakefile point to invoker, so rdoc task is generated properly. - rakefile = File.basename(caller[0].match(/(.*):\d+/)[1]) - Rake.application.instance_variable_set(:@rakefile, rakefile) - rake_classes << base - end - end -end - -# override task on (main), for compatibility with Rake 0.9 -instance_eval do - alias rake_namespace namespace - - def task(*) - task = super - - if klass = Bundler::Thor::RakeCompat.rake_classes.last # rubocop:disable AssignmentInCondition - non_namespaced_name = task.name.split(":").last - - description = non_namespaced_name - description << task.arg_names.map { |n| n.to_s.upcase }.join(" ") - description.strip! - - klass.desc description, Rake.application.last_description || non_namespaced_name - Rake.application.last_description = nil - klass.send :define_method, non_namespaced_name do |*args| - Rake::Task[task.name.to_sym].invoke(*args) - end - end - - task - end - - def namespace(name) - if klass = Bundler::Thor::RakeCompat.rake_classes.last # rubocop:disable AssignmentInCondition - const_name = Bundler::Thor::Util.camel_case(name.to_s).to_sym - klass.const_set(const_name, Class.new(Bundler::Thor)) - new_klass = klass.const_get(const_name) - Bundler::Thor::RakeCompat.rake_classes << new_klass - end - - super - Bundler::Thor::RakeCompat.rake_classes.pop - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/runner.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/runner.rb deleted file mode 100644 index 5552fe44bd..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/runner.rb +++ /dev/null @@ -1,322 +0,0 @@ -require "thor" -require "thor/group" -require "thor/core_ext/io_binary_read" - -require "fileutils" -require "open-uri" -require "yaml" -require "digest/md5" -require "pathname" - -class Bundler::Thor::Runner < Bundler::Thor #:nodoc: # rubocop:disable ClassLength - map "-T" => :list, "-i" => :install, "-u" => :update, "-v" => :version - - # Override Bundler::Thor#help so it can give information about any class and any method. - # - def help(meth = nil) - if meth && !self.respond_to?(meth) - initialize_thorfiles(meth) - klass, command = Bundler::Thor::Util.find_class_and_command_by_namespace(meth) - self.class.handle_no_command_error(command, false) if klass.nil? - klass.start(["-h", command].compact, :shell => shell) - else - super - end - end - - # If a command is not found on Bundler::Thor::Runner, method missing is invoked and - # Bundler::Thor::Runner is then responsible for finding the command in all classes. - # - def method_missing(meth, *args) - meth = meth.to_s - initialize_thorfiles(meth) - klass, command = Bundler::Thor::Util.find_class_and_command_by_namespace(meth) - self.class.handle_no_command_error(command, false) if klass.nil? - args.unshift(command) if command - klass.start(args, :shell => shell) - end - - desc "install NAME", "Install an optionally named Bundler::Thor file into your system commands" - method_options :as => :string, :relative => :boolean, :force => :boolean - def install(name) # rubocop:disable MethodLength - initialize_thorfiles - - # If a directory name is provided as the argument, look for a 'main.thor' - # command in said directory. - begin - if File.directory?(File.expand_path(name)) - base, package = File.join(name, "main.thor"), :directory - contents = open(base) { |input| input.read } - else - base, package = name, :file - contents = open(name) { |input| input.read } - end - rescue OpenURI::HTTPError - raise Error, "Error opening URI '#{name}'" - rescue Errno::ENOENT - fail Error, "Error opening file '#{name}'" - end - - say "Your Bundler::Thorfile contains:" - say contents - - unless options["force"] - return false if no?("Do you wish to continue [y/N]?") - end - - as = options["as"] || begin - first_line = contents.split("\n")[0] - (match = first_line.match(/\s*#\s*module:\s*([^\n]*)/)) ? match[1].strip : nil - end - - unless as - basename = File.basename(name) - as = ask("Please specify a name for #{name} in the system repository [#{basename}]:") - as = basename if as.empty? - end - - location = if options[:relative] || name =~ %r{^https?://} - name - else - File.expand_path(name) - end - - thor_yaml[as] = { - :filename => Digest::MD5.hexdigest(name + as), - :location => location, - :namespaces => Bundler::Thor::Util.namespaces_in_content(contents, base) - } - - save_yaml(thor_yaml) - say "Storing thor file in your system repository" - destination = File.join(thor_root, thor_yaml[as][:filename]) - - if package == :file - File.open(destination, "w") { |f| f.puts contents } - else - FileUtils.cp_r(name, destination) - end - - thor_yaml[as][:filename] # Indicate success - end - - desc "version", "Show Bundler::Thor version" - def version - require "thor/version" - say "Bundler::Thor #{Bundler::Thor::VERSION}" - end - - desc "uninstall NAME", "Uninstall a named Bundler::Thor module" - def uninstall(name) - fail Error, "Can't find module '#{name}'" unless thor_yaml[name] - say "Uninstalling #{name}." - FileUtils.rm_rf(File.join(thor_root, "#{thor_yaml[name][:filename]}")) - - thor_yaml.delete(name) - save_yaml(thor_yaml) - - puts "Done." - end - - desc "update NAME", "Update a Bundler::Thor file from its original location" - def update(name) - fail Error, "Can't find module '#{name}'" if !thor_yaml[name] || !thor_yaml[name][:location] - - say "Updating '#{name}' from #{thor_yaml[name][:location]}" - - old_filename = thor_yaml[name][:filename] - self.options = options.merge("as" => name) - - if File.directory? File.expand_path(name) - FileUtils.rm_rf(File.join(thor_root, old_filename)) - - thor_yaml.delete(old_filename) - save_yaml(thor_yaml) - - filename = install(name) - else - filename = install(thor_yaml[name][:location]) - end - - unless filename == old_filename - File.delete(File.join(thor_root, old_filename)) - end - end - - desc "installed", "List the installed Bundler::Thor modules and commands" - method_options :internal => :boolean - def installed - initialize_thorfiles(nil, true) - display_klasses(true, options["internal"]) - end - - desc "list [SEARCH]", "List the available thor commands (--substring means .*SEARCH)" - method_options :substring => :boolean, :group => :string, :all => :boolean, :debug => :boolean - def list(search = "") - initialize_thorfiles - - search = ".*#{search}" if options["substring"] - search = /^#{search}.*/i - group = options[:group] || "standard" - - klasses = Bundler::Thor::Base.subclasses.select do |k| - (options[:all] || k.group == group) && k.namespace =~ search - end - - display_klasses(false, false, klasses) - end - -private - - def self.banner(command, all = false, subcommand = false) - "thor " + command.formatted_usage(self, all, subcommand) - end - - def thor_root - Bundler::Thor::Util.thor_root - end - - def thor_yaml - @thor_yaml ||= begin - yaml_file = File.join(thor_root, "thor.yml") - yaml = YAML.load_file(yaml_file) if File.exist?(yaml_file) - yaml || {} - end - end - - # Save the yaml file. If none exists in thor root, creates one. - # - def save_yaml(yaml) - yaml_file = File.join(thor_root, "thor.yml") - - unless File.exist?(yaml_file) - FileUtils.mkdir_p(thor_root) - yaml_file = File.join(thor_root, "thor.yml") - FileUtils.touch(yaml_file) - end - - File.open(yaml_file, "w") { |f| f.puts yaml.to_yaml } - end - - def self.exit_on_failure? - true - end - - # Load the Bundler::Thorfiles. If relevant_to is supplied, looks for specific files - # in the thor_root instead of loading them all. - # - # By default, it also traverses the current path until find Bundler::Thor files, as - # described in thorfiles. This look up can be skipped by supplying - # skip_lookup true. - # - def initialize_thorfiles(relevant_to = nil, skip_lookup = false) - thorfiles(relevant_to, skip_lookup).each do |f| - Bundler::Thor::Util.load_thorfile(f, nil, options[:debug]) unless Bundler::Thor::Base.subclass_files.keys.include?(File.expand_path(f)) - end - end - - # Finds Bundler::Thorfiles by traversing from your current directory down to the root - # directory of your system. If at any time we find a Bundler::Thor file, we stop. - # - # We also ensure that system-wide Bundler::Thorfiles are loaded first, so local - # Bundler::Thorfiles can override them. - # - # ==== Example - # - # If we start at /Users/wycats/dev/thor ... - # - # 1. /Users/wycats/dev/thor - # 2. /Users/wycats/dev - # 3. /Users/wycats <-- we find a Bundler::Thorfile here, so we stop - # - # Suppose we start at c:\Documents and Settings\james\dev\thor ... - # - # 1. c:\Documents and Settings\james\dev\thor - # 2. c:\Documents and Settings\james\dev - # 3. c:\Documents and Settings\james - # 4. c:\Documents and Settings - # 5. c:\ <-- no Bundler::Thorfiles found! - # - def thorfiles(relevant_to = nil, skip_lookup = false) - thorfiles = [] - - unless skip_lookup - Pathname.pwd.ascend do |path| - thorfiles = Bundler::Thor::Util.globs_for(path).map { |g| Dir[g] }.flatten - break unless thorfiles.empty? - end - end - - files = (relevant_to ? thorfiles_relevant_to(relevant_to) : Bundler::Thor::Util.thor_root_glob) - files += thorfiles - files -= ["#{thor_root}/thor.yml"] - - files.map! do |file| - File.directory?(file) ? File.join(file, "main.thor") : file - end - end - - # Load Bundler::Thorfiles relevant to the given method. If you provide "foo:bar" it - # will load all thor files in the thor.yaml that has "foo" e "foo:bar" - # namespaces registered. - # - def thorfiles_relevant_to(meth) - lookup = [meth, meth.split(":")[0...-1].join(":")] - - files = thor_yaml.select do |k, v| - v[:namespaces] && !(v[:namespaces] & lookup).empty? - end - - files.map { |k, v| File.join(thor_root, "#{v[:filename]}") } - end - - # Display information about the given klasses. If with_module is given, - # it shows a table with information extracted from the yaml file. - # - def display_klasses(with_modules = false, show_internal = false, klasses = Bundler::Thor::Base.subclasses) - klasses -= [Bundler::Thor, Bundler::Thor::Runner, Bundler::Thor::Group] unless show_internal - - fail Error, "No Bundler::Thor commands available" if klasses.empty? - show_modules if with_modules && !thor_yaml.empty? - - list = Hash.new { |h, k| h[k] = [] } - groups = klasses.select { |k| k.ancestors.include?(Bundler::Thor::Group) } - - # Get classes which inherit from Bundler::Thor - (klasses - groups).each { |k| list[k.namespace.split(":").first] += k.printable_commands(false) } - - # Get classes which inherit from Bundler::Thor::Base - groups.map! { |k| k.printable_commands(false).first } - list["root"] = groups - - # Order namespaces with default coming first - list = list.sort { |a, b| a[0].sub(/^default/, "") <=> b[0].sub(/^default/, "") } - list.each { |n, commands| display_commands(n, commands) unless commands.empty? } - end - - def display_commands(namespace, list) #:nodoc: - list.sort! { |a, b| a[0] <=> b[0] } - - say shell.set_color(namespace, :blue, true) - say "-" * namespace.size - - print_table(list, :truncate => true) - say - end - alias_method :display_tasks, :display_commands - - def show_modules #:nodoc: - info = [] - labels = %w[Modules Namespaces] - - info << labels - info << ["-" * labels[0].size, "-" * labels[1].size] - - thor_yaml.each do |name, hash| - info << [name, hash[:namespaces].join(", ")] - end - - print_table info - say "" - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/shell.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/shell.rb deleted file mode 100644 index 6a6ec5e0a4..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/shell.rb +++ /dev/null @@ -1,81 +0,0 @@ -require "rbconfig" - -class Bundler::Thor - module Base - class << self - attr_writer :shell - - # Returns the shell used in all Bundler::Thor classes. If you are in a Unix platform - # it will use a colored log, otherwise it will use a basic one without color. - # - def shell - @shell ||= if ENV["THOR_SHELL"] && ENV["THOR_SHELL"].size > 0 - Bundler::Thor::Shell.const_get(ENV["THOR_SHELL"]) - elsif RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ && !ENV["ANSICON"] - Bundler::Thor::Shell::Basic - else - Bundler::Thor::Shell::Color - end - end - end - end - - module Shell - SHELL_DELEGATED_METHODS = [:ask, :error, :set_color, :yes?, :no?, :say, :say_status, :print_in_columns, :print_table, :print_wrapped, :file_collision, :terminal_width] - attr_writer :shell - - autoload :Basic, "thor/shell/basic" - autoload :Color, "thor/shell/color" - autoload :HTML, "thor/shell/html" - - # Add shell to initialize config values. - # - # ==== Configuration - # shell:: An instance of the shell to be used. - # - # ==== Examples - # - # class MyScript < Bundler::Thor - # argument :first, :type => :numeric - # end - # - # MyScript.new [1.0], { :foo => :bar }, :shell => Bundler::Thor::Shell::Basic.new - # - def initialize(args = [], options = {}, config = {}) - super - self.shell = config[:shell] - shell.base ||= self if shell.respond_to?(:base) - end - - # Holds the shell for the given Bundler::Thor instance. If no shell is given, - # it gets a default shell from Bundler::Thor::Base.shell. - def shell - @shell ||= Bundler::Thor::Base.shell.new - end - - # Common methods that are delegated to the shell. - SHELL_DELEGATED_METHODS.each do |method| - module_eval <<-METHOD, __FILE__, __LINE__ - def #{method}(*args,&block) - shell.#{method}(*args,&block) - end - METHOD - end - - # Yields the given block with padding. - def with_padding - shell.padding += 1 - yield - ensure - shell.padding -= 1 - end - - protected - - # Allow shell to be shared between invocations. - # - def _shared_configuration #:nodoc: - super.merge!(:shell => shell) - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/shell/basic.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/shell/basic.rb deleted file mode 100644 index 278ffa3df0..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/shell/basic.rb +++ /dev/null @@ -1,421 +0,0 @@ -require "tempfile" -require "io/console" if RUBY_VERSION > "1.9.2" - -class Bundler::Thor - module Shell - class Basic # rubocop:disable ClassLength - attr_accessor :base - attr_reader :padding - - # Initialize base, mute and padding to nil. - # - def initialize #:nodoc: - @base, @mute, @padding, @always_force = nil, false, 0, false - end - - # Mute everything that's inside given block - # - def mute - @mute = true - yield - ensure - @mute = false - end - - # Check if base is muted - # - def mute? # rubocop:disable TrivialAccessors - @mute - end - - # Sets the output padding, not allowing less than zero values. - # - def padding=(value) - @padding = [0, value].max - end - - # Asks something to the user and receives a response. - # - # If asked to limit the correct responses, you can pass in an - # array of acceptable answers. If one of those is not supplied, - # they will be shown a message stating that one of those answers - # must be given and re-asked the question. - # - # If asking for sensitive information, the :echo option can be set - # to false to mask user input from $stdin. - # - # If the required input is a path, then set the path option to - # true. This will enable tab completion for file paths relative - # to the current working directory on systems that support - # Readline. - # - # ==== Example - # ask("What is your name?") - # - # ask("What is your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"]) - # - # ask("What is your password?", :echo => false) - # - # ask("Where should the file be saved?", :path => true) - # - def ask(statement, *args) - options = args.last.is_a?(Hash) ? args.pop : {} - color = args.first - - if options[:limited_to] - ask_filtered(statement, color, options) - else - ask_simply(statement, color, options) - end - end - - # Say (print) something to the user. If the sentence ends with a whitespace - # or tab character, a new line is not appended (print + flush). Otherwise - # are passed straight to puts (behavior got from Highline). - # - # ==== Example - # say("I know you knew that.") - # - def say(message = "", color = nil, force_new_line = (message.to_s !~ /( |\t)\Z/)) - buffer = prepare_message(message, *color) - buffer << "\n" if force_new_line && !message.to_s.end_with?("\n") - - stdout.print(buffer) - stdout.flush - end - - # Say a status with the given color and appends the message. Since this - # method is used frequently by actions, it allows nil or false to be given - # in log_status, avoiding the message from being shown. If a Symbol is - # given in log_status, it's used as the color. - # - def say_status(status, message, log_status = true) - return if quiet? || log_status == false - spaces = " " * (padding + 1) - color = log_status.is_a?(Symbol) ? log_status : :green - - status = status.to_s.rjust(12) - status = set_color status, color, true if color - - buffer = "#{status}#{spaces}#{message}" - buffer << "\n" unless buffer.end_with?("\n") - - stdout.print(buffer) - stdout.flush - end - - # Make a question the to user and returns true if the user replies "y" or - # "yes". - # - def yes?(statement, color = nil) - !!(ask(statement, color, :add_to_history => false) =~ is?(:yes)) - end - - # Make a question the to user and returns true if the user replies "n" or - # "no". - # - def no?(statement, color = nil) - !!(ask(statement, color, :add_to_history => false) =~ is?(:no)) - end - - # Prints values in columns - # - # ==== Parameters - # Array[String, String, ...] - # - def print_in_columns(array) - return if array.empty? - colwidth = (array.map { |el| el.to_s.size }.max || 0) + 2 - array.each_with_index do |value, index| - # Don't output trailing spaces when printing the last column - if ((((index + 1) % (terminal_width / colwidth))).zero? && !index.zero?) || index + 1 == array.length - stdout.puts value - else - stdout.printf("%-#{colwidth}s", value) - end - end - end - - # Prints a table. - # - # ==== Parameters - # Array[Array[String, String, ...]] - # - # ==== Options - # indent:: Indent the first column by indent value. - # colwidth:: Force the first column to colwidth spaces wide. - # - def print_table(array, options = {}) # rubocop:disable MethodLength - return if array.empty? - - formats, indent, colwidth = [], options[:indent].to_i, options[:colwidth] - options[:truncate] = terminal_width if options[:truncate] == true - - formats << "%-#{colwidth + 2}s" if colwidth - start = colwidth ? 1 : 0 - - colcount = array.max { |a, b| a.size <=> b.size }.size - - maximas = [] - - start.upto(colcount - 1) do |index| - maxima = array.map { |row| row[index] ? row[index].to_s.size : 0 }.max - maximas << maxima - if index == colcount - 1 - # Don't output 2 trailing spaces when printing the last column - formats << "%-s" - else - formats << "%-#{maxima + 2}s" - end - end - - formats[0] = formats[0].insert(0, " " * indent) - formats << "%s" - - array.each do |row| - sentence = "" - - row.each_with_index do |column, index| - maxima = maximas[index] - - if column.is_a?(Numeric) - if index == row.size - 1 - # Don't output 2 trailing spaces when printing the last column - f = "%#{maxima}s" - else - f = "%#{maxima}s " - end - else - f = formats[index] - end - sentence << f % column.to_s - end - - sentence = truncate(sentence, options[:truncate]) if options[:truncate] - stdout.puts sentence - end - end - - # Prints a long string, word-wrapping the text to the current width of the - # terminal display. Ideal for printing heredocs. - # - # ==== Parameters - # String - # - # ==== Options - # indent:: Indent each line of the printed paragraph by indent value. - # - def print_wrapped(message, options = {}) - indent = options[:indent] || 0 - width = terminal_width - indent - paras = message.split("\n\n") - - paras.map! do |unwrapped| - unwrapped.strip.gsub(/\n/, " ").squeeze(" ").gsub(/.{1,#{width}}(?:\s|\Z)/) { ($& + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n") } - end - - paras.each do |para| - para.split("\n").each do |line| - stdout.puts line.insert(0, " " * indent) - end - stdout.puts unless para == paras.last - end - end - - # Deals with file collision and returns true if the file should be - # overwritten and false otherwise. If a block is given, it uses the block - # response as the content for the diff. - # - # ==== Parameters - # destination:: the destination file to solve conflicts - # block:: an optional block that returns the value to be used in diff - # - def file_collision(destination) # rubocop:disable MethodLength - return true if @always_force - options = block_given? ? "[Ynaqdh]" : "[Ynaqh]" - - loop do - answer = ask( - %[Overwrite #{destination}? (enter "h" for help) #{options}], - :add_to_history => false - ) - - case answer - when is?(:yes), is?(:force), "" - return true - when is?(:no), is?(:skip) - return false - when is?(:always) - return @always_force = true - when is?(:quit) - say "Aborting..." - fail SystemExit - when is?(:diff) - show_diff(destination, yield) if block_given? - say "Retrying..." - else - say file_collision_help - end - end - end - - # This code was copied from Rake, available under MIT-LICENSE - # Copyright (c) 2003, 2004 Jim Weirich - def terminal_width - if ENV["THOR_COLUMNS"] - result = ENV["THOR_COLUMNS"].to_i - else - result = unix? ? dynamic_width : 80 - end - result < 10 ? 80 : result - rescue - 80 - end - - # Called if something goes wrong during the execution. This is used by Bundler::Thor - # internally and should not be used inside your scripts. If something went - # wrong, you can always raise an exception. If you raise a Bundler::Thor::Error, it - # will be rescued and wrapped in the method below. - # - def error(statement) - stderr.puts statement - end - - # Apply color to the given string with optional bold. Disabled in the - # Bundler::Thor::Shell::Basic class. - # - def set_color(string, *args) #:nodoc: - string - end - - protected - - def prepare_message(message, *color) - spaces = " " * padding - spaces + set_color(message.to_s, *color) - end - - def can_display_colors? - false - end - - def lookup_color(color) - return color unless color.is_a?(Symbol) - self.class.const_get(color.to_s.upcase) - end - - def stdout - $stdout - end - - def stderr - $stderr - end - - def is?(value) #:nodoc: - value = value.to_s - - if value.size == 1 - /\A#{value}\z/i - else - /\A(#{value}|#{value[0, 1]})\z/i - end - end - - def file_collision_help #:nodoc: - <<-HELP - Y - yes, overwrite - n - no, do not overwrite - a - all, overwrite this and all others - q - quit, abort - d - diff, show the differences between the old and the new - h - help, show this help - HELP - end - - def show_diff(destination, content) #:nodoc: - diff_cmd = ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u" - - Tempfile.open(File.basename(destination), File.dirname(destination)) do |temp| - temp.write content - temp.rewind - system %(#{diff_cmd} "#{destination}" "#{temp.path}") - end - end - - def quiet? #:nodoc: - mute? || (base && base.options[:quiet]) - end - - # Calculate the dynamic width of the terminal - def dynamic_width - @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput) - end - - def dynamic_width_stty - %x(stty size 2>/dev/null).split[1].to_i - end - - def dynamic_width_tput - %x(tput cols 2>/dev/null).to_i - end - - def unix? - RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i - end - - def truncate(string, width) - as_unicode do - chars = string.chars.to_a - if chars.length <= width - chars.join - else - ( chars[0, width - 3].join) + "..." - end - end - end - - if "".respond_to?(:encode) - def as_unicode - yield - end - else - def as_unicode - old, $KCODE = $KCODE, "U" - yield - ensure - $KCODE = old - end - end - - def ask_simply(statement, color, options) - default = options[:default] - message = [statement, ("(#{default})" if default), nil].uniq.join(" ") - message = prepare_message(message, color) - result = Bundler::Thor::LineEditor.readline(message, options) - - return unless result - - result.strip! - - if default && result == "" - default - else - result - end - end - - def ask_filtered(statement, color, options) - answer_set = options[:limited_to] - correct_answer = nil - until correct_answer - answers = answer_set.join(", ") - answer = ask_simply("#{statement} [#{answers}]", color, options) - correct_answer = answer_set.include?(answer) ? answer : nil - say("Your response must be one of: [#{answers}]. Please try again.") unless correct_answer - end - correct_answer - end - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/shell/color.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/shell/color.rb deleted file mode 100644 index 3c2feba4e5..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/shell/color.rb +++ /dev/null @@ -1,149 +0,0 @@ -require "thor/shell/basic" - -class Bundler::Thor - module Shell - # Inherit from Bundler::Thor::Shell::Basic and add set_color behavior. Check - # Bundler::Thor::Shell::Basic to see all available methods. - # - class Color < Basic - # Embed in a String to clear all previous ANSI sequences. - CLEAR = "\e[0m" - # The start of an ANSI bold sequence. - BOLD = "\e[1m" - - # Set the terminal's foreground ANSI color to black. - BLACK = "\e[30m" - # Set the terminal's foreground ANSI color to red. - RED = "\e[31m" - # Set the terminal's foreground ANSI color to green. - GREEN = "\e[32m" - # Set the terminal's foreground ANSI color to yellow. - YELLOW = "\e[33m" - # Set the terminal's foreground ANSI color to blue. - BLUE = "\e[34m" - # Set the terminal's foreground ANSI color to magenta. - MAGENTA = "\e[35m" - # Set the terminal's foreground ANSI color to cyan. - CYAN = "\e[36m" - # Set the terminal's foreground ANSI color to white. - WHITE = "\e[37m" - - # Set the terminal's background ANSI color to black. - ON_BLACK = "\e[40m" - # Set the terminal's background ANSI color to red. - ON_RED = "\e[41m" - # Set the terminal's background ANSI color to green. - ON_GREEN = "\e[42m" - # Set the terminal's background ANSI color to yellow. - ON_YELLOW = "\e[43m" - # Set the terminal's background ANSI color to blue. - ON_BLUE = "\e[44m" - # Set the terminal's background ANSI color to magenta. - ON_MAGENTA = "\e[45m" - # Set the terminal's background ANSI color to cyan. - ON_CYAN = "\e[46m" - # Set the terminal's background ANSI color to white. - ON_WHITE = "\e[47m" - - # Set color by using a string or one of the defined constants. If a third - # option is set to true, it also adds bold to the string. This is based - # on Highline implementation and it automatically appends CLEAR to the end - # of the returned String. - # - # Pass foreground, background and bold options to this method as - # symbols. - # - # Example: - # - # set_color "Hi!", :red, :on_white, :bold - # - # The available colors are: - # - # :bold - # :black - # :red - # :green - # :yellow - # :blue - # :magenta - # :cyan - # :white - # :on_black - # :on_red - # :on_green - # :on_yellow - # :on_blue - # :on_magenta - # :on_cyan - # :on_white - def set_color(string, *colors) - if colors.compact.empty? || !can_display_colors? - string - elsif colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) } - ansi_colors = colors.map { |color| lookup_color(color) } - "#{ansi_colors.join}#{string}#{CLEAR}" - else - # The old API was `set_color(color, bold=boolean)`. We - # continue to support the old API because you should never - # break old APIs unnecessarily :P - foreground, bold = colors - foreground = self.class.const_get(foreground.to_s.upcase) if foreground.is_a?(Symbol) - - bold = bold ? BOLD : "" - "#{bold}#{foreground}#{string}#{CLEAR}" - end - end - - protected - - def can_display_colors? - stdout.tty? - end - - # Overwrite show_diff to show diff with colors if Diff::LCS is - # available. - # - def show_diff(destination, content) #:nodoc: - if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil? - actual = File.binread(destination).to_s.split("\n") - content = content.to_s.split("\n") - - Diff::LCS.sdiff(actual, content).each do |diff| - output_diff_line(diff) - end - else - super - end - end - - def output_diff_line(diff) #:nodoc: - case diff.action - when "-" - say "- #{diff.old_element.chomp}", :red, true - when "+" - say "+ #{diff.new_element.chomp}", :green, true - when "!" - say "- #{diff.old_element.chomp}", :red, true - say "+ #{diff.new_element.chomp}", :green, true - else - say " #{diff.old_element.chomp}", nil, true - end - end - - # Check if Diff::LCS is loaded. If it is, use it to create pretty output - # for diff. - # - def diff_lcs_loaded? #:nodoc: - return true if defined?(Diff::LCS) - return @diff_lcs_loaded unless @diff_lcs_loaded.nil? - - @diff_lcs_loaded = begin - require "diff/lcs" - true - rescue LoadError - false - end - end - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/shell/html.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/shell/html.rb deleted file mode 100644 index 9e28690ad0..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/shell/html.rb +++ /dev/null @@ -1,126 +0,0 @@ -require "thor/shell/basic" - -class Bundler::Thor - module Shell - # Inherit from Bundler::Thor::Shell::Basic and add set_color behavior. Check - # Bundler::Thor::Shell::Basic to see all available methods. - # - class HTML < Basic - # The start of an HTML bold sequence. - BOLD = "font-weight: bold" - - # Set the terminal's foreground HTML color to black. - BLACK = "color: black" - # Set the terminal's foreground HTML color to red. - RED = "color: red" - # Set the terminal's foreground HTML color to green. - GREEN = "color: green" - # Set the terminal's foreground HTML color to yellow. - YELLOW = "color: yellow" - # Set the terminal's foreground HTML color to blue. - BLUE = "color: blue" - # Set the terminal's foreground HTML color to magenta. - MAGENTA = "color: magenta" - # Set the terminal's foreground HTML color to cyan. - CYAN = "color: cyan" - # Set the terminal's foreground HTML color to white. - WHITE = "color: white" - - # Set the terminal's background HTML color to black. - ON_BLACK = "background-color: black" - # Set the terminal's background HTML color to red. - ON_RED = "background-color: red" - # Set the terminal's background HTML color to green. - ON_GREEN = "background-color: green" - # Set the terminal's background HTML color to yellow. - ON_YELLOW = "background-color: yellow" - # Set the terminal's background HTML color to blue. - ON_BLUE = "background-color: blue" - # Set the terminal's background HTML color to magenta. - ON_MAGENTA = "background-color: magenta" - # Set the terminal's background HTML color to cyan. - ON_CYAN = "background-color: cyan" - # Set the terminal's background HTML color to white. - ON_WHITE = "background-color: white" - - # Set color by using a string or one of the defined constants. If a third - # option is set to true, it also adds bold to the string. This is based - # on Highline implementation and it automatically appends CLEAR to the end - # of the returned String. - # - def set_color(string, *colors) - if colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) } - html_colors = colors.map { |color| lookup_color(color) } - "#{string}" - else - color, bold = colors - html_color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol) - styles = [html_color] - styles << BOLD if bold - "#{string}" - end - end - - # Ask something to the user and receives a response. - # - # ==== Example - # ask("What is your name?") - # - # TODO: Implement #ask for Bundler::Thor::Shell::HTML - def ask(statement, color = nil) - fail NotImplementedError, "Implement #ask for Bundler::Thor::Shell::HTML" - end - - protected - - def can_display_colors? - true - end - - # Overwrite show_diff to show diff with colors if Diff::LCS is - # available. - # - def show_diff(destination, content) #:nodoc: - if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil? - actual = File.binread(destination).to_s.split("\n") - content = content.to_s.split("\n") - - Diff::LCS.sdiff(actual, content).each do |diff| - output_diff_line(diff) - end - else - super - end - end - - def output_diff_line(diff) #:nodoc: - case diff.action - when "-" - say "- #{diff.old_element.chomp}", :red, true - when "+" - say "+ #{diff.new_element.chomp}", :green, true - when "!" - say "- #{diff.old_element.chomp}", :red, true - say "+ #{diff.new_element.chomp}", :green, true - else - say " #{diff.old_element.chomp}", nil, true - end - end - - # Check if Diff::LCS is loaded. If it is, use it to create pretty output - # for diff. - # - def diff_lcs_loaded? #:nodoc: - return true if defined?(Diff::LCS) - return @diff_lcs_loaded unless @diff_lcs_loaded.nil? - - @diff_lcs_loaded = begin - require "diff/lcs" - true - rescue LoadError - false - end - end - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/util.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/util.rb deleted file mode 100644 index f4e98fc19f..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/util.rb +++ /dev/null @@ -1,267 +0,0 @@ -require "rbconfig" - -class Bundler::Thor - module Sandbox #:nodoc: - end - - # This module holds several utilities: - # - # 1) Methods to convert thor namespaces to constants and vice-versa. - # - # Bundler::Thor::Util.namespace_from_thor_class(Foo::Bar::Baz) #=> "foo:bar:baz" - # - # 2) Loading thor files and sandboxing: - # - # Bundler::Thor::Util.load_thorfile("~/.thor/foo") - # - module Util - class << self - # Receives a namespace and search for it in the Bundler::Thor::Base subclasses. - # - # ==== Parameters - # namespace:: The namespace to search for. - # - def find_by_namespace(namespace) - namespace = "default#{namespace}" if namespace.empty? || namespace =~ /^:/ - Bundler::Thor::Base.subclasses.detect { |klass| klass.namespace == namespace } - end - - # Receives a constant and converts it to a Bundler::Thor namespace. Since Bundler::Thor - # commands can be added to a sandbox, this method is also responsable for - # removing the sandbox namespace. - # - # This method should not be used in general because it's used to deal with - # older versions of Bundler::Thor. On current versions, if you need to get the - # namespace from a class, just call namespace on it. - # - # ==== Parameters - # constant:: The constant to be converted to the thor path. - # - # ==== Returns - # String:: If we receive Foo::Bar::Baz it returns "foo:bar:baz" - # - def namespace_from_thor_class(constant) - constant = constant.to_s.gsub(/^Bundler::Thor::Sandbox::/, "") - constant = snake_case(constant).squeeze(":") - constant - end - - # Given the contents, evaluate it inside the sandbox and returns the - # namespaces defined in the sandbox. - # - # ==== Parameters - # contents - # - # ==== Returns - # Array[Object] - # - def namespaces_in_content(contents, file = __FILE__) - old_constants = Bundler::Thor::Base.subclasses.dup - Bundler::Thor::Base.subclasses.clear - - load_thorfile(file, contents) - - new_constants = Bundler::Thor::Base.subclasses.dup - Bundler::Thor::Base.subclasses.replace(old_constants) - - new_constants.map! { |c| c.namespace } - new_constants.compact! - new_constants - end - - # Returns the thor classes declared inside the given class. - # - def thor_classes_in(klass) - stringfied_constants = klass.constants.map { |c| c.to_s } - Bundler::Thor::Base.subclasses.select do |subclass| - next unless subclass.name - stringfied_constants.include?(subclass.name.gsub("#{klass.name}::", "")) - end - end - - # Receives a string and convert it to snake case. SnakeCase returns snake_case. - # - # ==== Parameters - # String - # - # ==== Returns - # String - # - def snake_case(str) - return str.downcase if str =~ /^[A-Z_]+$/ - str.gsub(/\B[A-Z]/, '_\&').squeeze("_") =~ /_*(.*)/ - $+.downcase - end - - # Receives a string and convert it to camel case. camel_case returns CamelCase. - # - # ==== Parameters - # String - # - # ==== Returns - # String - # - def camel_case(str) - return str if str !~ /_/ && str =~ /[A-Z]+.*/ - str.split("_").map { |i| i.capitalize }.join - end - - # Receives a namespace and tries to retrieve a Bundler::Thor or Bundler::Thor::Group class - # from it. It first searches for a class using the all the given namespace, - # if it's not found, removes the highest entry and searches for the class - # again. If found, returns the highest entry as the class name. - # - # ==== Examples - # - # class Foo::Bar < Bundler::Thor - # def baz - # end - # end - # - # class Baz::Foo < Bundler::Thor::Group - # end - # - # Bundler::Thor::Util.namespace_to_thor_class("foo:bar") #=> Foo::Bar, nil # will invoke default command - # Bundler::Thor::Util.namespace_to_thor_class("baz:foo") #=> Baz::Foo, nil - # Bundler::Thor::Util.namespace_to_thor_class("foo:bar:baz") #=> Foo::Bar, "baz" - # - # ==== Parameters - # namespace - # - def find_class_and_command_by_namespace(namespace, fallback = true) - if namespace.include?(":") # look for a namespaced command - pieces = namespace.split(":") - command = pieces.pop - klass = Bundler::Thor::Util.find_by_namespace(pieces.join(":")) - end - unless klass # look for a Bundler::Thor::Group with the right name - klass, command = Bundler::Thor::Util.find_by_namespace(namespace), nil - end - if !klass && fallback # try a command in the default namespace - command = namespace - klass = Bundler::Thor::Util.find_by_namespace("") - end - [klass, command] - end - alias_method :find_class_and_task_by_namespace, :find_class_and_command_by_namespace - - # Receives a path and load the thor file in the path. The file is evaluated - # inside the sandbox to avoid namespacing conflicts. - # - def load_thorfile(path, content = nil, debug = false) - content ||= File.binread(path) - - begin - Bundler::Thor::Sandbox.class_eval(content, path) - rescue StandardError => e - $stderr.puts("WARNING: unable to load thorfile #{path.inspect}: #{e.message}") - if debug - $stderr.puts(*e.backtrace) - else - $stderr.puts(e.backtrace.first) - end - end - end - - def user_home # rubocop:disable MethodLength - @@user_home ||= if ENV["HOME"] - ENV["HOME"] - elsif ENV["USERPROFILE"] - ENV["USERPROFILE"] - elsif ENV["HOMEDRIVE"] && ENV["HOMEPATH"] - File.join(ENV["HOMEDRIVE"], ENV["HOMEPATH"]) - elsif ENV["APPDATA"] - ENV["APPDATA"] - else - begin - File.expand_path("~") - rescue - if File::ALT_SEPARATOR - "C:/" - else - "/" - end - end - end - end - - # Returns the root where thor files are located, depending on the OS. - # - def thor_root - File.join(user_home, ".thor").gsub(/\\/, "/") - end - - # Returns the files in the thor root. On Windows thor_root will be something - # like this: - # - # C:\Documents and Settings\james\.thor - # - # If we don't #gsub the \ character, Dir.glob will fail. - # - def thor_root_glob - files = Dir["#{escape_globs(thor_root)}/*"] - - files.map! do |file| - File.directory?(file) ? File.join(file, "main.thor") : file - end - end - - # Where to look for Bundler::Thor files. - # - def globs_for(path) - path = escape_globs(path) - ["#{path}/Bundler::Thorfile", "#{path}/*.thor", "#{path}/tasks/*.thor", "#{path}/lib/tasks/*.thor"] - end - - # Return the path to the ruby interpreter taking into account multiple - # installations and windows extensions. - # - def ruby_command # rubocop:disable MethodLength - @ruby_command ||= begin - ruby_name = RbConfig::CONFIG["ruby_install_name"] - ruby = File.join(RbConfig::CONFIG["bindir"], ruby_name) - ruby << RbConfig::CONFIG["EXEEXT"] - - # avoid using different name than ruby (on platforms supporting links) - if ruby_name != "ruby" && File.respond_to?(:readlink) - begin - alternate_ruby = File.join(RbConfig::CONFIG["bindir"], "ruby") - alternate_ruby << RbConfig::CONFIG["EXEEXT"] - - # ruby is a symlink - if File.symlink? alternate_ruby - linked_ruby = File.readlink alternate_ruby - - # symlink points to 'ruby_install_name' - ruby = alternate_ruby if linked_ruby == ruby_name || linked_ruby == ruby - end - rescue NotImplementedError # rubocop:disable HandleExceptions - # just ignore on windows - end - end - - # escape string in case path to ruby executable contain spaces. - ruby.sub!(/.*\s.*/m, '"\&"') - ruby - end - end - - # Returns a string that has had any glob characters escaped. - # The glob characters are `* ? { } [ ]`. - # - # ==== Examples - # - # Bundler::Thor::Util.escape_globs('[apps]') # => '\[apps\]' - # - # ==== Parameters - # String - # - # ==== Returns - # String - # - def escape_globs(path) - path.to_s.gsub(/[*?{}\[\]]/, '\\\\\\&') - end - end - end -end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/version.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/version.rb deleted file mode 100644 index 74b020a5ab..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/version.rb +++ /dev/null @@ -1,3 +0,0 @@ -class Bundler::Thor - VERSION = "0.19.1" -end diff --git a/lib/bundler/vendor/thor/lib/thor.rb b/lib/bundler/vendor/thor/lib/thor.rb new file mode 100644 index 0000000000..9ed67a44e2 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor.rb @@ -0,0 +1,484 @@ +require "set" +require "bundler/vendor/thor/lib/thor/base" + +class Bundler::Thor # rubocop:disable ClassLength + class << self + # Allows for custom "Command" package naming. + # + # === Parameters + # name + # options + # + def package_name(name, options = {}) + @package_name = name.nil? || name == "" ? nil : name + end + + # Sets the default command when thor is executed without an explicit command to be called. + # + # ==== Parameters + # meth:: name of the default command + # + def default_command(meth = nil) + if meth + @default_command = meth == :none ? "help" : meth.to_s + else + @default_command ||= from_superclass(:default_command, "help") + end + end + alias_method :default_task, :default_command + + # Registers another Bundler::Thor subclass as a command. + # + # ==== Parameters + # klass:: Bundler::Thor subclass to register + # command:: Subcommand name to use + # usage:: Short usage for the subcommand + # description:: Description for the subcommand + def register(klass, subcommand_name, usage, description, options = {}) + if klass <= Bundler::Thor::Group + desc usage, description, options + define_method(subcommand_name) { |*args| invoke(klass, args) } + else + desc usage, description, options + subcommand subcommand_name, klass + end + end + + # Defines the usage and the description of the next command. + # + # ==== Parameters + # usage + # description + # options + # + def desc(usage, description, options = {}) + if options[:for] + command = find_and_refresh_command(options[:for]) + command.usage = usage if usage + command.description = description if description + else + @usage, @desc, @hide = usage, description, options[:hide] || false + end + end + + # Defines the long description of the next command. + # + # ==== Parameters + # long description + # + def long_desc(long_description, options = {}) + if options[:for] + command = find_and_refresh_command(options[:for]) + command.long_description = long_description if long_description + else + @long_desc = long_description + end + end + + # Maps an input to a command. If you define: + # + # map "-T" => "list" + # + # Running: + # + # thor -T + # + # Will invoke the list command. + # + # ==== Parameters + # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given command. + # + def map(mappings = nil) + @map ||= from_superclass(:map, {}) + + if mappings + mappings.each do |key, value| + if key.respond_to?(:each) + key.each { |subkey| @map[subkey] = value } + else + @map[key] = value + end + end + end + + @map + end + + # Declares the options for the next command to be declared. + # + # ==== Parameters + # Hash[Symbol => Object]:: The hash key is the name of the option and the value + # is the type of the option. Can be :string, :array, :hash, :boolean, :numeric + # or :required (string). If you give a value, the type of the value is used. + # + def method_options(options = nil) + @method_options ||= {} + build_options(options, @method_options) if options + @method_options + end + + alias_method :options, :method_options + + # Adds an option to the set of method options. If :for is given as option, + # it allows you to change the options from a previous defined command. + # + # def previous_command + # # magic + # end + # + # method_option :foo => :bar, :for => :previous_command + # + # def next_command + # # magic + # end + # + # ==== Parameters + # name:: The name of the argument. + # options:: Described below. + # + # ==== Options + # :desc - Description for the argument. + # :required - If the argument is required or not. + # :default - Default value for this argument. It cannot be required and have default values. + # :aliases - Aliases for this option. + # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean. + # :banner - String to show on usage notes. + # :hide - If you want to hide this option from the help. + # + def method_option(name, options = {}) + scope = if options[:for] + find_and_refresh_command(options[:for]).options + else + method_options + end + + build_option(name, options, scope) + end + alias_method :option, :method_option + + # Prints help information for the given command. + # + # ==== Parameters + # shell + # command_name + # + def command_help(shell, command_name) + meth = normalize_command_name(command_name) + command = all_commands[meth] + handle_no_command_error(meth) unless command + + shell.say "Usage:" + shell.say " #{banner(command)}" + shell.say + class_options_help(shell, nil => command.options.map { |_, o| o }) + if command.long_description + shell.say "Description:" + shell.print_wrapped(command.long_description, :indent => 2) + else + shell.say command.description + end + end + alias_method :task_help, :command_help + + # Prints help information for this class. + # + # ==== Parameters + # shell + # + def help(shell, subcommand = false) + list = printable_commands(true, subcommand) + Bundler::Thor::Util.thor_classes_in(self).each do |klass| + list += klass.printable_commands(false) + end + list.sort! { |a, b| a[0] <=> b[0] } + + if defined?(@package_name) && @package_name + shell.say "#{@package_name} commands:" + else + shell.say "Commands:" + end + + shell.print_table(list, :indent => 2, :truncate => true) + shell.say + class_options_help(shell) + end + + # Returns commands ready to be printed. + def printable_commands(all = true, subcommand = false) + (all ? all_commands : commands).map do |_, command| + next if command.hidden? + item = [] + item << banner(command, false, subcommand) + item << (command.description ? "# #{command.description.gsub(/\s+/m, ' ')}" : "") + item + end.compact + end + alias_method :printable_tasks, :printable_commands + + def subcommands + @subcommands ||= from_superclass(:subcommands, []) + end + alias_method :subtasks, :subcommands + + def subcommand_classes + @subcommand_classes ||= {} + end + + def subcommand(subcommand, subcommand_class) + subcommands << subcommand.to_s + subcommand_class.subcommand_help subcommand + subcommand_classes[subcommand.to_s] = subcommand_class + + define_method(subcommand) do |*args| + args, opts = Bundler::Thor::Arguments.split(args) + args.unshift("help") if opts.include? "--help" or opts.include? "-h" + invoke subcommand_class, args, opts, :invoked_via_subcommand => true, :class_options => options + end + end + alias_method :subtask, :subcommand + + # Extend check unknown options to accept a hash of conditions. + # + # === Parameters + # options: A hash containing :only and/or :except keys + def check_unknown_options!(options = {}) + @check_unknown_options ||= {} + options.each do |key, value| + if value + @check_unknown_options[key] = Array(value) + else + @check_unknown_options.delete(key) + end + end + @check_unknown_options + end + + # Overwrite check_unknown_options? to take subcommands and options into account. + def check_unknown_options?(config) #:nodoc: + options = check_unknown_options + return false unless options + + command = config[:current_command] + return true unless command + + name = command.name + + if subcommands.include?(name) + false + elsif options[:except] + !options[:except].include?(name.to_sym) + elsif options[:only] + options[:only].include?(name.to_sym) + else + true + end + end + + # Stop parsing of options as soon as an unknown option or a regular + # argument is encountered. All remaining arguments are passed to the command. + # This is useful if you have a command that can receive arbitrary additional + # options, and where those additional options should not be handled by + # Bundler::Thor. + # + # ==== Example + # + # To better understand how this is useful, let's consider a command that calls + # an external command. A user may want to pass arbitrary options and + # arguments to that command. The command itself also accepts some options, + # which should be handled by Bundler::Thor. + # + # class_option "verbose", :type => :boolean + # stop_on_unknown_option! :exec + # check_unknown_options! :except => :exec + # + # desc "exec", "Run a shell command" + # def exec(*args) + # puts "diagnostic output" if options[:verbose] + # Kernel.exec(*args) + # end + # + # Here +exec+ can be called with +--verbose+ to get diagnostic output, + # e.g.: + # + # $ thor exec --verbose echo foo + # diagnostic output + # foo + # + # But if +--verbose+ is given after +echo+, it is passed to +echo+ instead: + # + # $ thor exec echo --verbose foo + # --verbose foo + # + # ==== Parameters + # Symbol ...:: A list of commands that should be affected. + def stop_on_unknown_option!(*command_names) + stop_on_unknown_option.merge(command_names) + end + + def stop_on_unknown_option?(command) #:nodoc: + command && stop_on_unknown_option.include?(command.name.to_sym) + end + + protected + def stop_on_unknown_option #:nodoc: + @stop_on_unknown_option ||= Set.new + end + + # The method responsible for dispatching given the args. + def dispatch(meth, given_args, given_opts, config) #:nodoc: # rubocop:disable MethodLength + meth ||= retrieve_command_name(given_args) + command = all_commands[normalize_command_name(meth)] + + if !command && config[:invoked_via_subcommand] + # We're a subcommand and our first argument didn't match any of our + # commands. So we put it back and call our default command. + given_args.unshift(meth) + command = all_commands[normalize_command_name(default_command)] + end + + if command + args, opts = Bundler::Thor::Options.split(given_args) + if stop_on_unknown_option?(command) && !args.empty? + # given_args starts with a non-option, so we treat everything as + # ordinary arguments + args.concat opts + opts.clear + end + else + args, opts = given_args, nil + command = dynamic_command_class.new(meth) + end + + opts = given_opts || opts || [] + config.merge!(:current_command => command, :command_options => command.options) + + instance = new(args, opts, config) + yield instance if block_given? + args = instance.args + trailing = args[Range.new(arguments.size, -1)] + instance.invoke_command(command, trailing || []) + end + + # The banner for this class. You can customize it if you are invoking the + # thor class by another ways which is not the Bundler::Thor::Runner. It receives + # the command that is going to be invoked and a boolean which indicates if + # the namespace should be displayed as arguments. + # + def banner(command, namespace = nil, subcommand = false) + "#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}" + end + + def baseclass #:nodoc: + Bundler::Thor + end + + def dynamic_command_class #:nodoc: + Bundler::Thor::DynamicCommand + end + + def create_command(meth) #:nodoc: + @usage ||= nil + @desc ||= nil + @long_desc ||= nil + + if @usage && @desc + base_class = @hide ? Bundler::Thor::HiddenCommand : Bundler::Thor::Command + commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options) + @usage, @desc, @long_desc, @method_options, @hide = nil + true + elsif all_commands[meth] || meth == "method_missing" + true + else + puts "[WARNING] Attempted to create command #{meth.inspect} without usage or description. " << + "Call desc if you want this method to be available as command or declare it inside a " << + "no_commands{} block. Invoked from #{caller[1].inspect}." + false + end + end + alias_method :create_task, :create_command + + def initialize_added #:nodoc: + class_options.merge!(method_options) + @method_options = nil + end + + # Retrieve the command name from given args. + def retrieve_command_name(args) #:nodoc: + meth = args.first.to_s unless args.empty? + if meth && (map[meth] || meth !~ /^\-/) + args.shift + else + nil + end + end + alias_method :retrieve_task_name, :retrieve_command_name + + # receives a (possibly nil) command name and returns a name that is in + # the commands hash. In addition to normalizing aliases, this logic + # will determine if a shortened command is an unambiguous substring of + # a command or alias. + # + # +normalize_command_name+ also converts names like +animal-prison+ + # into +animal_prison+. + def normalize_command_name(meth) #:nodoc: + return default_command.to_s.gsub("-", "_") unless meth + + possibilities = find_command_possibilities(meth) + if possibilities.size > 1 + fail AmbiguousTaskError, "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]" + elsif possibilities.size < 1 + meth = meth || default_command + elsif map[meth] + meth = map[meth] + else + meth = possibilities.first + end + + meth.to_s.gsub("-", "_") # treat foo-bar as foo_bar + end + alias_method :normalize_task_name, :normalize_command_name + + # this is the logic that takes the command name passed in by the user + # and determines whether it is an unambiguous substrings of a command or + # alias name. + def find_command_possibilities(meth) + len = meth.to_s.length + possibilities = all_commands.merge(map).keys.select { |n| meth == n[0, len] }.sort + unique_possibilities = possibilities.map { |k| map[k] || k }.uniq + + if possibilities.include?(meth) + [meth] + elsif unique_possibilities.size == 1 + unique_possibilities + else + possibilities + end + end + alias_method :find_task_possibilities, :find_command_possibilities + + def subcommand_help(cmd) + desc "help [COMMAND]", "Describe subcommands or one specific subcommand" + class_eval " + def help(command = nil, subcommand = true); super; end +" + end + alias_method :subtask_help, :subcommand_help + end + + include Bundler::Thor::Base + + map HELP_MAPPINGS => :help + + desc "help [COMMAND]", "Describe available commands or one specific command" + def help(command = nil, subcommand = false) + if command + if self.class.subcommands.include? command + self.class.subcommand_classes[command].help(shell, true) + else + self.class.command_help(shell, command) + end + else + self.class.help(shell, subcommand) + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/actions.rb b/lib/bundler/vendor/thor/lib/thor/actions.rb new file mode 100644 index 0000000000..5a82dfd45f --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/actions.rb @@ -0,0 +1,319 @@ +require "fileutils" +require "uri" +require "bundler/vendor/thor/lib/thor/core_ext/io_binary_read" +require "bundler/vendor/thor/lib/thor/actions/create_file" +require "bundler/vendor/thor/lib/thor/actions/create_link" +require "bundler/vendor/thor/lib/thor/actions/directory" +require "bundler/vendor/thor/lib/thor/actions/empty_directory" +require "bundler/vendor/thor/lib/thor/actions/file_manipulation" +require "bundler/vendor/thor/lib/thor/actions/inject_into_file" + +class Bundler::Thor + module Actions + attr_accessor :behavior + + def self.included(base) #:nodoc: + base.extend ClassMethods + end + + module ClassMethods + # Hold source paths for one Bundler::Thor instance. source_paths_for_search is the + # method responsible to gather source_paths from this current class, + # inherited paths and the source root. + # + def source_paths + @_source_paths ||= [] + end + + # Stores and return the source root for this class + def source_root(path = nil) + @_source_root = path if path + @_source_root ||= nil + end + + # Returns the source paths in the following order: + # + # 1) This class source paths + # 2) Source root + # 3) Parents source paths + # + def source_paths_for_search + paths = [] + paths += source_paths + paths << source_root if source_root + paths += from_superclass(:source_paths, []) + paths + end + + # Add runtime options that help actions execution. + # + def add_runtime_options! + class_option :force, :type => :boolean, :aliases => "-f", :group => :runtime, + :desc => "Overwrite files that already exist" + + class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime, + :desc => "Run but do not make any changes" + + class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime, + :desc => "Suppress status output" + + class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime, + :desc => "Skip files that already exist" + end + end + + # Extends initializer to add more configuration options. + # + # ==== Configuration + # behavior:: The actions default behavior. Can be :invoke or :revoke. + # It also accepts :force, :skip and :pretend to set the behavior + # and the respective option. + # + # destination_root:: The root directory needed for some actions. + # + 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 + + # 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 + + # Returns the root for this thor class (also aliased as destination root). + # + 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. + # + def destination_root=(root) + @destination_stack ||= [] + @destination_stack[0] = File.expand_path(root || "") + end + + # Returns the given path relative to the absolute root (ie, root where + # the script started). + # + def relative_to_original_destination_root(path, remove_dot = true) + path = path.dup + if path.gsub!(@destination_stack[0], ".") + remove_dot ? (path[2..-1] || "") : path + else + path + end + end + + # Holds source paths in instance so they can be manipulated. + # + def source_paths + @source_paths ||= self.class.source_paths_for_search + end + + # Receives a file or directory and search for it in the source paths. + # + def find_in_source_paths(file) # rubocop:disable MethodLength + 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. " + + unless self.class.source_root + message << "Please invoke #{self.class.name}.source_root(PATH) with the PATH containing your templates. " + end + + if source_paths.empty? + message << "Currently you have no source paths." + else + message << "Your current source paths are: \n#{source_paths.join("\n")}" + end + + fail Error, message + 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. + # + # ==== Parameters + # dir:: the directory to move to. + # config:: give :verbose => true to log and use padding. + # + 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 + 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 + FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield } + end + + @destination_stack.pop + shell.padding -= 1 if verbose + end + + # Goes to the root and execute the given block. + # + def in_root + inside(@destination_stack.first) { yield } + end + + # Loads an external file and execute it in the instance binding. + # + # ==== Parameters + # path:: The path to the file to execute. Can be a web address or + # a relative path from the source root. + # + # ==== Examples + # + # apply "http://gist.github.com/103208" + # + # apply "recipes/jquery.rb" + # + 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 + + if is_uri + 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 + + # Executes a command returning the contents of the command. + # + # ==== Parameters + # command:: the command to be executed. + # config:: give :verbose => false to not log the status, :capture => true to hide to output. Specify :with + # to append an executable to command execution. + # + # ==== Example + # + # inside('vendor') do + # run('ln -s ~/edge rails') + # end + # + 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) + + unless options[:pretend] + config[:capture] ? `#{command}` : system("#{command}") + end + end + + # Executes a ruby script (taking into account WIN32 platform quirks). + # + # ==== Parameters + # command:: the command to be executed. + # config:: give :verbose => false to not log the status. + # + def run_ruby_script(command, config = {}) + return unless behavior == :invoke + run command, config.merge(:with => Bundler::Thor::Util.ruby_command) + end + + # Run a thor command. A hash of options can be given and it's converted to + # switches. + # + # ==== Parameters + # command:: the command to be invoked + # args:: arguments to the command + # config:: give :verbose => false to not log the status, :capture => true to hide to output. + # Other options are given as parameter to Bundler::Thor. + # + # + # ==== Examples + # + # 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 + # + 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 + + protected + + # Allow current root to be shared between invocations. + # + def _shared_configuration #:nodoc: + super.merge!(:destination_root => destination_root) + end + + 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 + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb b/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb new file mode 100644 index 0000000000..a0f5640333 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb @@ -0,0 +1,103 @@ +require "bundler/vendor/thor/lib/thor/actions/empty_directory" + +class Bundler::Thor + module Actions + # 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. + # + # ==== Parameters + # destination:: the relative path to the destination root. + # data:: the data to append to the file. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # 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" + # + 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 + alias_method :add_file, :create_file + + # CreateFile is a subset of Template, which instead of rendering a file with + # ERB, it gets the content from the user. + # + class CreateFile < EmptyDirectory #:nodoc: + attr_reader :data + + def initialize(base, destination, data, 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.binread(destination) == render + end + + # Holds the content to be added to the file. + # + def render + @render ||= if data.is_a?(Proc) + data.call + else + data + end + end + + def invoke! + invoke_with_conflict_check do + FileUtils.mkdir_p(File.dirname(destination)) + File.open(destination, "wb") { |f| f.write render } + end + given_destination + 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 diff --git a/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb b/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb new file mode 100644 index 0000000000..be437922b6 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb @@ -0,0 +1,59 @@ +require "bundler/vendor/thor/lib/thor/actions/create_file" + +class Bundler::Thor + module Actions + # Create a new file relative to the destination root from the given source. + # + # ==== Parameters + # destination:: the relative path to the destination root. + # source:: the relative path to the source root. + # config:: give :verbose => false to not log the status. + # :: give :symbolic => false for hard link. + # + # ==== Examples + # + # create_link "config/apache.conf", "/etc/apache.conf" + # + def create_link(destination, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + source = args.first + action CreateLink.new(self, destination, source, config) + end + alias_method :add_link, :create_link + + # CreateLink is a subset of CreateFile, which instead of taking a block of + # data, just takes a source string from the user. + # + class CreateLink < CreateFile #:nodoc: + attr_reader :data + + # 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.identical?(render, destination) + end + + def invoke! + invoke_with_conflict_check do + FileUtils.mkdir_p(File.dirname(destination)) + # Create a symlink by default + config[:symbolic] = true if config[:symbolic].nil? + File.unlink(destination) if exists? + if config[:symbolic] + File.symlink(render, destination) + else + File.link(render, destination) + end + end + given_destination + end + + def exists? + super || File.symlink?(destination) + end + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/actions/directory.rb b/lib/bundler/vendor/thor/lib/thor/actions/directory.rb new file mode 100644 index 0000000000..1a2e25da2f --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/actions/directory.rb @@ -0,0 +1,118 @@ +require "bundler/vendor/thor/lib/thor/actions/empty_directory" + +class Bundler::Thor + module Actions + # 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. + # + # ==== Parameters + # source:: the relative path to the source root. + # destination:: the relative path to the destination root. + # config:: 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. + # + # ==== Examples + # + # directory "doc" + # directory "doc", "docs", :recursive => false + # + 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 + + class Directory < EmptyDirectory #:nodoc: + attr_reader :source + + def initialize(base, source, destination = nil, config = {}, &block) + @source = File.expand_path(base.find_in_source_paths(source.to_s)) + @block = block + super(base, destination, {:recursive => true}.merge(config)) + end + + def invoke! + base.empty_directory given_destination, config + execute! + end + + def revoke! + execute! + end + + protected + + def execute! # rubocop:disable MethodLength + lookup = Util.escape_globs(source) + lookup = config[:recursive] ? File.join(lookup, "**") : lookup + lookup = file_level_lookup(lookup) + + files(lookup).sort.each do |file_source| + next if File.directory?(file_source) + next if config[:exclude_pattern] && file_source.match(config[:exclude_pattern]) + file_destination = File.join(given_destination, file_source.gsub(source, ".")) + file_destination.gsub!("/./", "/") + + case file_source + when /\.empty_directory$/ + dirname = File.dirname(file_destination).gsub(/\/\.$/, "") + next if dirname == given_destination + base.empty_directory(dirname, config) + when /#{TEMPLATE_EXTNAME}$/ + base.template(file_source, file_destination[0..-4], config, &@block) + else + base.copy_file(file_source, file_destination, config, &@block) + end + end + end + + if RUBY_VERSION < "2.0" + def file_level_lookup(previous_lookup) + File.join(previous_lookup, "{*,.[a-z]*}") + end + + def files(lookup) + Dir[lookup] + end + else + def file_level_lookup(previous_lookup) + File.join(previous_lookup, "*") + end + + def files(lookup) + Dir.glob(lookup, File::FNM_DOTMATCH) + end + end + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb b/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb new file mode 100644 index 0000000000..cdc3768b4c --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb @@ -0,0 +1,135 @@ +class Bundler::Thor + module Actions + # Creates an empty directory. + # + # ==== Parameters + # destination:: the relative path to the destination root. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # empty_directory "doc" + # + def empty_directory(destination, config = {}) + action EmptyDirectory.new(self, destination, config) + end + + # 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:: A Bundler::Thor::Base instance + # source:: Relative path to the source of this file + # destination:: Relative path to the destination of this file + # config:: 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.exist?(destination) + end + + def invoke! + invoke_with_conflict_check do + ::FileUtils.mkdir_p(destination) + end + end + + def revoke! + say_status :remove, :red + ::FileUtils.rm_rf(destination) if !pretend? && exists? + given_destination + 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: + # + # %file_name%.rb + # + # It calls #file_name from the base and replaces %-string with the + # return value (should be String) of #file_name: + # + # user.rb + # + # The method referenced can be either public or private. + # + def convert_encoded_instructions(filename) + filename.gsub(/%(.*?)%/) do |initial_string| + method = $1.strip + base.respond_to?(method, true) ? base.send(method) : initial_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 diff --git a/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb new file mode 100644 index 0000000000..2bdc78f578 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb @@ -0,0 +1,316 @@ +require "erb" +require "open-uri" + +class Bundler::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:: the relative path to the source root. + # destination:: the relative path to the destination root. + # config:: 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:: the relative path to the source root. + # destination:: the relative path to the destination root. + # config:: 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:: the address of the given content. + # destination:: the relative path to the destination root. + # config:: 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:: the relative path to the source root. + # destination:: the relative path to the destination root. + # config:: 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:: the file mode + # path:: the name of the file to change mode + # config:: 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:: path of the file to be changed + # data:: the data to prepend to the file, can be also given as a block. + # config:: 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:: path of the file to be changed + # data:: the data to append to the file, can be also given as a block. + # config:: 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:: path of the file to be changed + # klass:: the class to be manipulated + # data:: the data to append to the class, can be also given as a block. + # config:: 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:: path of the file to be changed + # flag:: the regexp or string to be replaced + # replacement:: the replacement, can be also given as a block + # config:: 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:: path of the file to be changed + # flag:: the regexp or string used to decide which lines to uncomment + # config:: 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:: path of the file to be changed + # flag:: the regexp or string used to decide which lines to comment + # config:: 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:: path of the file to be changed + # config:: 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 diff --git a/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb b/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb new file mode 100644 index 0000000000..91ab245ae1 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb @@ -0,0 +1,107 @@ +require "bundler/vendor/thor/lib/thor/actions/empty_directory" + +class Bundler::Thor + module Actions + # Injects the given content into a file. Different from gsub_file, this + # method is reversible. + # + # ==== Parameters + # destination:: Relative path to the destination root + # data:: Data to add to the file. Can be given as a block. + # config:: 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. + # + # ==== Examples + # + # 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 + # + def insert_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 + alias_method :inject_into_file, :insert_into_file + + class InjectIntoFile < EmptyDirectory #:nodoc: + attr_reader :replacement, :flag, :behavior + + def initialize(base, destination, data, config) + super(base, destination, {:verbose => true}.merge(config)) + + @behavior, @flag = if @config.key?(:after) + [:after, @config.delete(:after)] + else + [:before, @config.delete(:before)] + end + + @replacement = data.is_a?(Proc) ? data.call : data + @flag = Regexp.escape(@flag) unless @flag.is_a?(Regexp) + end + + def invoke! + say_status :invoke + + content = if @behavior == :after + '\0' + replacement + else + replacement + '\0' + end + + replace!(/#{flag}/, content, config[:force]) + end + + def revoke! + say_status :revoke + + regexp = if @behavior == :after + content = '\1\2' + /(#{flag})(.*)(#{Regexp.escape(replacement)})/m + else + content = '\2\3' + /(#{Regexp.escape(replacement)})(.*)(#{flag})/m + end + + replace!(regexp, content, true) + end + + protected + + def say_status(behavior) + status = if behavior == :invoke + if flag == /\A/ + :prepend + elsif flag == /\z/ + :append + else + :insert + end + else + :subtract + end + + super(status, config[:verbose]) + end + + # Adds the content to the file. + # + def replace!(regexp, string, force) + unless base.options[:pretend] + content = File.binread(destination) + if force || !content.include?(replacement) + content.gsub!(regexp, string) + File.open(destination, "wb") { |file| file.write(content) } + end + end + end + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/base.rb b/lib/bundler/vendor/thor/lib/thor/base.rb new file mode 100644 index 0000000000..c3667521a5 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/base.rb @@ -0,0 +1,656 @@ +require "bundler/vendor/thor/lib/thor/command" +require "bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access" +require "bundler/vendor/thor/lib/thor/core_ext/ordered_hash" +require "bundler/vendor/thor/lib/thor/error" +require "bundler/vendor/thor/lib/thor/invocation" +require "bundler/vendor/thor/lib/thor/parser" +require "bundler/vendor/thor/lib/thor/shell" +require "bundler/vendor/thor/lib/thor/line_editor" +require "bundler/vendor/thor/lib/thor/util" + +class Bundler::Thor + autoload :Actions, "bundler/vendor/thor/lib/thor/actions" + autoload :RakeCompat, "bundler/vendor/thor/lib/thor/rake_compat" + autoload :Group, "bundler/vendor/thor/lib/thor/group" + + # Shortcuts for help. + HELP_MAPPINGS = %w[-h -? --help -D] + + # Bundler::Thor methods that should not be overwritten by the user. + THOR_RESERVED_WORDS = %w[invoke shell options behavior root destination_root relative_root + action add_file create_file in_root inside run run_ruby_script] + + TEMPLATE_EXTNAME = ".tt" + + module Base + attr_accessor :options, :parent_options, :args + + # It receives arguments in an Array and two hashes, one for options and + # other for configuration. + # + # Notice that it does not check if all required arguments were supplied. + # It should be done by the parser. + # + # ==== Parameters + # args:: An array of objects. The objects are applied to their + # respective accessors declared with argument. + # + # options:: An options hash that will be available as self.options. + # The hash given is converted to a hash with indifferent + # access, magic predicates (options.skip?) and then frozen. + # + # config:: Configuration for this Bundler::Thor class. + # + def initialize(args = [], local_options = {}, config = {}) # rubocop:disable MethodLength + parse_options = self.class.class_options + + # The start method splits inbound arguments at the first argument + # that looks like an option (starts with - or --). It then calls + # new, passing in the two halves of the arguments Array as the + # first two parameters. + + command_options = config.delete(:command_options) # hook for start + parse_options = parse_options.merge(command_options) if command_options + if local_options.is_a?(Array) + array_options, hash_options = local_options, {} + else + # Handle the case where the class was explicitly instantiated + # with pre-parsed options. + array_options, hash_options = [], local_options + end + + # Let Bundler::Thor::Options parse the options first, so it can remove + # declared options from the array. This will leave us with + # a list of arguments that weren't declared. + stop_on_unknown = self.class.stop_on_unknown_option? config[:current_command] + opts = Bundler::Thor::Options.new(parse_options, hash_options, stop_on_unknown) + self.options = opts.parse(array_options) + self.options = config[:class_options].merge(options) if config[:class_options] + + # If unknown options are disallowed, make sure that none of the + # remaining arguments looks like an option. + opts.check_unknown! if self.class.check_unknown_options?(config) + + # Add the remaining arguments from the options parser to the + # arguments passed in to initialize. Then remove any positional + # arguments declared using #argument (this is primarily used + # by Bundler::Thor::Group). Tis will leave us with the remaining + # positional arguments. + to_parse = args + to_parse += opts.remaining unless self.class.strict_args_position?(config) + + thor_args = Bundler::Thor::Arguments.new(self.class.arguments) + thor_args.parse(to_parse).each { |k, v| __send__("#{k}=", v) } + @args = thor_args.remaining + end + + class << self + def included(base) #:nodoc: + base.extend ClassMethods + base.send :include, Invocation + base.send :include, Shell + end + + # Returns the classes that inherits from Bundler::Thor or Bundler::Thor::Group. + # + # ==== Returns + # Array[Class] + # + def subclasses + @subclasses ||= [] + end + + # Returns the files where the subclasses are kept. + # + # ==== Returns + # Hash[path => Class] + # + def subclass_files + @subclass_files ||= Hash.new { |h, k| h[k] = [] } + end + + # Whenever a class inherits from Bundler::Thor or Bundler::Thor::Group, we should track the + # class and the file on Bundler::Thor::Base. This is the method responsable for it. + # + def register_klass_file(klass) #:nodoc: + file = caller[1].match(/(.*):\d+/)[1] + Bundler::Thor::Base.subclasses << klass unless Bundler::Thor::Base.subclasses.include?(klass) + + file_subclasses = Bundler::Thor::Base.subclass_files[File.expand_path(file)] + file_subclasses << klass unless file_subclasses.include?(klass) + end + end + + module ClassMethods + def attr_reader(*) #:nodoc: + no_commands { super } + end + + def attr_writer(*) #:nodoc: + no_commands { super } + end + + def attr_accessor(*) #:nodoc: + no_commands { super } + end + + # If you want to raise an error for unknown options, call check_unknown_options! + # This is disabled by default to allow dynamic invocations. + def check_unknown_options! + @check_unknown_options = true + end + + def check_unknown_options #:nodoc: + @check_unknown_options ||= from_superclass(:check_unknown_options, false) + end + + def check_unknown_options?(config) #:nodoc: + !!check_unknown_options + end + + # If true, option parsing is suspended as soon as an unknown option or a + # regular argument is encountered. All remaining arguments are passed to + # the command as regular arguments. + def stop_on_unknown_option?(command_name) #:nodoc: + false + end + + # If you want only strict string args (useful when cascading thor classes), + # call strict_args_position! This is disabled by default to allow dynamic + # invocations. + def strict_args_position! + @strict_args_position = true + end + + def strict_args_position #:nodoc: + @strict_args_position ||= from_superclass(:strict_args_position, false) + end + + def strict_args_position?(config) #:nodoc: + !!strict_args_position + end + + # Adds an argument to the class and creates an attr_accessor for it. + # + # Arguments are different from options in several aspects. The first one + # is how they are parsed from the command line, arguments are retrieved + # from position: + # + # thor command NAME + # + # Instead of: + # + # thor command --name=NAME + # + # Besides, arguments are used inside your code as an accessor (self.argument), + # while options are all kept in a hash (self.options). + # + # Finally, arguments cannot have type :default or :boolean but can be + # optional (supplying :optional => :true or :required => false), although + # you cannot have a required argument after a non-required argument. If you + # try it, an error is raised. + # + # ==== Parameters + # name:: The name of the argument. + # options:: Described below. + # + # ==== Options + # :desc - Description for the argument. + # :required - If the argument is required or not. + # :optional - If the argument is optional or not. + # :type - The type of the argument, can be :string, :hash, :array, :numeric. + # :default - Default value for this argument. It cannot be required and have default values. + # :banner - String to show on usage notes. + # + # ==== Errors + # ArgumentError:: Raised if you supply a required argument after a non required one. + # + def argument(name, options = {}) # rubocop:disable MethodLength + is_thor_reserved_word?(name, :argument) + no_commands { attr_accessor name } + + required = if options.key?(:optional) + !options[:optional] + elsif options.key?(:required) + options[:required] + else + options[:default].nil? + end + + remove_argument name + + arguments.each do |argument| + next if argument.required? + fail ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " << + "the non-required argument #{argument.human_name.inspect}." + end if required + + options[:required] = required + + arguments << Bundler::Thor::Argument.new(name, options) + end + + # Returns this class arguments, looking up in the ancestors chain. + # + # ==== Returns + # Array[Bundler::Thor::Argument] + # + def arguments + @arguments ||= from_superclass(:arguments, []) + end + + # Adds a bunch of options to the set of class options. + # + # class_options :foo => false, :bar => :required, :baz => :string + # + # If you prefer more detailed declaration, check class_option. + # + # ==== Parameters + # Hash[Symbol => Object] + # + def class_options(options = nil) + @class_options ||= from_superclass(:class_options, {}) + build_options(options, @class_options) if options + @class_options + end + + # Adds an option to the set of class options + # + # ==== Parameters + # name:: The name of the argument. + # options:: Described below. + # + # ==== Options + # :desc:: -- Description for the argument. + # :required:: -- If the argument is required or not. + # :default:: -- Default value for this argument. + # :group:: -- The group for this options. Use by class options to output options in different levels. + # :aliases:: -- Aliases for this option. Note: Bundler::Thor follows a convention of one-dash-one-letter options. Thus aliases like "-something" wouldn't be parsed; use either "\--something" or "-s" instead. + # :type:: -- The type of the argument, can be :string, :hash, :array, :numeric or :boolean. + # :banner:: -- String to show on usage notes. + # :hide:: -- If you want to hide this option from the help. + # + def class_option(name, options = {}) + build_option(name, options, class_options) + end + + # Removes a previous defined argument. If :undefine is given, undefine + # accessors as well. + # + # ==== Parameters + # names:: Arguments to be removed + # + # ==== Examples + # + # remove_argument :foo + # remove_argument :foo, :bar, :baz, :undefine => true + # + def remove_argument(*names) + options = names.last.is_a?(Hash) ? names.pop : {} + + names.each do |name| + arguments.delete_if { |a| a.name == name.to_s } + undef_method name, "#{name}=" if options[:undefine] + end + end + + # Removes a previous defined class option. + # + # ==== Parameters + # names:: Class options to be removed + # + # ==== Examples + # + # remove_class_option :foo + # remove_class_option :foo, :bar, :baz + # + def remove_class_option(*names) + names.each do |name| + class_options.delete(name) + end + end + + # Defines the group. This is used when thor list is invoked so you can specify + # that only commands from a pre-defined group will be shown. Defaults to standard. + # + # ==== Parameters + # name + # + def group(name = nil) + if name + @group = name.to_s + else + @group ||= from_superclass(:group, "standard") + end + end + + # Returns the commands for this Bundler::Thor class. + # + # ==== Returns + # OrderedHash:: An ordered hash with commands names as keys and Bundler::Thor::Command + # objects as values. + # + def commands + @commands ||= Bundler::Thor::CoreExt::OrderedHash.new + end + alias_method :tasks, :commands + + # Returns the commands for this Bundler::Thor class and all subclasses. + # + # ==== Returns + # OrderedHash:: An ordered hash with commands names as keys and Bundler::Thor::Command + # objects as values. + # + def all_commands + @all_commands ||= from_superclass(:all_commands, Bundler::Thor::CoreExt::OrderedHash.new) + @all_commands.merge(commands) + end + alias_method :all_tasks, :all_commands + + # Removes a given command from this Bundler::Thor class. This is usually done if you + # are inheriting from another class and don't want it to be available + # anymore. + # + # By default it only remove the mapping to the command. But you can supply + # :undefine => true to undefine the method from the class as well. + # + # ==== Parameters + # name:: The name of the command to be removed + # options:: You can give :undefine => true if you want commands the method + # to be undefined from the class as well. + # + def remove_command(*names) + options = names.last.is_a?(Hash) ? names.pop : {} + + names.each do |name| + commands.delete(name.to_s) + all_commands.delete(name.to_s) + undef_method name if options[:undefine] + end + end + alias_method :remove_task, :remove_command + + # All methods defined inside the given block are not added as commands. + # + # So you can do: + # + # class MyScript < Bundler::Thor + # no_commands do + # def this_is_not_a_command + # end + # end + # end + # + # You can also add the method and remove it from the command list: + # + # class MyScript < Bundler::Thor + # def this_is_not_a_command + # end + # remove_command :this_is_not_a_command + # end + # + def no_commands + @no_commands = true + yield + ensure + @no_commands = false + end + alias_method :no_tasks, :no_commands + + # Sets the namespace for the Bundler::Thor or Bundler::Thor::Group class. By default the + # namespace is retrieved from the class name. If your Bundler::Thor class is named + # Scripts::MyScript, the help method, for example, will be called as: + # + # thor scripts:my_script -h + # + # If you change the namespace: + # + # namespace :my_scripts + # + # You change how your commands are invoked: + # + # thor my_scripts -h + # + # Finally, if you change your namespace to default: + # + # namespace :default + # + # Your commands can be invoked with a shortcut. Instead of: + # + # thor :my_command + # + def namespace(name = nil) + if name + @namespace = name.to_s + else + @namespace ||= Bundler::Thor::Util.namespace_from_thor_class(self) + end + end + + # Parses the command and options from the given args, instantiate the class + # and invoke the command. This method is used when the arguments must be parsed + # from an array. If you are inside Ruby and want to use a Bundler::Thor class, you + # can simply initialize it: + # + # script = MyScript.new(args, options, config) + # script.invoke(:command, first_arg, second_arg, third_arg) + # + def start(given_args = ARGV, config = {}) + config[:shell] ||= Bundler::Thor::Base.shell.new + dispatch(nil, given_args.dup, nil, config) + rescue Bundler::Thor::Error => e + config[:debug] || ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message) + exit(1) if exit_on_failure? + rescue Errno::EPIPE + # This happens if a thor command is piped to something like `head`, + # which closes the pipe when it's done reading. This will also + # mean that if the pipe is closed, further unnecessary + # computation will not occur. + exit(0) + end + + # Allows to use private methods from parent in child classes as commands. + # + # ==== Parameters + # names:: Method names to be used as commands + # + # ==== Examples + # + # public_command :foo + # public_command :foo, :bar, :baz + # + def public_command(*names) + names.each do |name| + class_eval "def #{name}(*); super end" + end + end + alias_method :public_task, :public_command + + def handle_no_command_error(command, has_namespace = $thor_runner) #:nodoc: + if has_namespace + fail UndefinedCommandError, "Could not find command #{command.inspect} in #{namespace.inspect} namespace." + else + fail UndefinedCommandError, "Could not find command #{command.inspect}." + end + end + alias_method :handle_no_task_error, :handle_no_command_error + + def handle_argument_error(command, error, args, arity) #:nodoc: + msg = "ERROR: \"#{basename} #{command.name}\" was called with " + msg << "no arguments" if args.empty? + msg << "arguments " << args.inspect unless args.empty? + msg << "\nUsage: #{banner(command).inspect}" + fail InvocationError, msg + end + + protected + + # Prints the class options per group. If an option does not belong to + # any group, it's printed as Class option. + # + def class_options_help(shell, groups = {}) #:nodoc: + # Group options by group + class_options.each do |_, value| + groups[value.group] ||= [] + groups[value.group] << value + end + + # Deal with default group + global_options = groups.delete(nil) || [] + print_options(shell, global_options) + + # Print all others + groups.each do |group_name, options| + print_options(shell, options, group_name) + end + end + + # Receives a set of options and print them. + def print_options(shell, options, group_name = nil) + return if options.empty? + + list = [] + padding = options.map { |o| o.aliases.size }.max.to_i * 4 + + options.each do |option| + unless option.hide + item = [option.usage(padding)] + item.push(option.description ? "# #{option.description}" : "") + + list << item + list << ["", "# Default: #{option.default}"] if option.show_default? + list << ["", "# Possible values: #{option.enum.join(', ')}"] if option.enum + end + end + + shell.say(group_name ? "#{group_name} options:" : "Options:") + shell.print_table(list, :indent => 2) + shell.say "" + end + + # Raises an error if the word given is a Bundler::Thor reserved word. + def is_thor_reserved_word?(word, type) #:nodoc: + return false unless THOR_RESERVED_WORDS.include?(word.to_s) + fail "#{word.inspect} is a Bundler::Thor reserved word and cannot be defined as #{type}" + end + + # Build an option and adds it to the given scope. + # + # ==== Parameters + # name:: The name of the argument. + # options:: Described in both class_option and method_option. + # scope:: Options hash that is being built up + def build_option(name, options, scope) #:nodoc: + scope[name] = Bundler::Thor::Option.new(name, options) + end + + # Receives a hash of options, parse them and add to the scope. This is a + # fast way to set a bunch of options: + # + # build_options :foo => true, :bar => :required, :baz => :string + # + # ==== Parameters + # Hash[Symbol => Object] + def build_options(options, scope) #:nodoc: + options.each do |key, value| + scope[key] = Bundler::Thor::Option.parse(key, value) + end + end + + # Finds a command with the given name. If the command belongs to the current + # class, just return it, otherwise dup it and add the fresh copy to the + # current command hash. + def find_and_refresh_command(name) #:nodoc: + if commands[name.to_s] + commands[name.to_s] + elsif command = all_commands[name.to_s] # rubocop:disable AssignmentInCondition + commands[name.to_s] = command.clone + else + fail ArgumentError, "You supplied :for => #{name.inspect}, but the command #{name.inspect} could not be found." + end + end + alias_method :find_and_refresh_task, :find_and_refresh_command + + # Everytime someone inherits from a Bundler::Thor class, register the klass + # and file into baseclass. + def inherited(klass) + Bundler::Thor::Base.register_klass_file(klass) + klass.instance_variable_set(:@no_commands, false) + end + + # Fire this callback whenever a method is added. Added methods are + # tracked as commands by invoking the create_command method. + def method_added(meth) + meth = meth.to_s + + if meth == "initialize" + initialize_added + return + end + + # Return if it's not a public instance method + return unless public_method_defined?(meth.to_sym) + + @no_commands ||= false + return if @no_commands || !create_command(meth) + + is_thor_reserved_word?(meth, :command) + Bundler::Thor::Base.register_klass_file(self) + end + + # Retrieves a value from superclass. If it reaches the baseclass, + # returns default. + def from_superclass(method, default = nil) + if self == baseclass || !superclass.respond_to?(method, true) + default + else + value = superclass.send(method) + + # Ruby implements `dup` on Object, but raises a `TypeError` + # if the method is called on immediates. As a result, we + # don't have a good way to check whether dup will succeed + # without calling it and rescuing the TypeError. + begin + value.dup + rescue TypeError + value + end + + end + end + + # A flag that makes the process exit with status 1 if any error happens. + def exit_on_failure? + false + end + + # + # The basename of the program invoking the thor class. + # + def basename + File.basename($PROGRAM_NAME).split(" ").first + end + + # SIGNATURE: Sets the baseclass. This is where the superclass lookup + # finishes. + def baseclass #:nodoc: + end + + # SIGNATURE: Creates a new command if valid_command? is true. This method is + # called when a new method is added to the class. + def create_command(meth) #:nodoc: + end + alias_method :create_task, :create_command + + # SIGNATURE: Defines behavior when the initialize method is added to the + # class. + def initialize_added #:nodoc: + end + + # SIGNATURE: The hook invoked by start. + def dispatch(command, given_args, given_opts, config) #:nodoc: + fail NotImplementedError + end + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/command.rb b/lib/bundler/vendor/thor/lib/thor/command.rb new file mode 100644 index 0000000000..72c8348cb6 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/command.rb @@ -0,0 +1,133 @@ +class Bundler::Thor + class Command < Struct.new(:name, :description, :long_description, :usage, :options) + FILE_REGEXP = /^#{Regexp.escape(File.dirname(__FILE__))}/ + + def initialize(name, description, long_description, usage, options = nil) + super(name.to_s, description, long_description, usage, options || {}) + end + + def initialize_copy(other) #:nodoc: + super(other) + self.options = other.options.dup if other.options + end + + def hidden? + false + end + + # By default, a command invokes a method in the thor class. You can change this + # implementation to create custom commands. + def run(instance, args = []) + arity = nil + + if private_method?(instance) + instance.class.handle_no_command_error(name) + elsif public_method?(instance) + arity = instance.method(name).arity + instance.__send__(name, *args) + elsif local_method?(instance, :method_missing) + instance.__send__(:method_missing, name.to_sym, *args) + else + instance.class.handle_no_command_error(name) + end + rescue ArgumentError => e + handle_argument_error?(instance, e, caller) ? instance.class.handle_argument_error(self, e, args, arity) : (raise e) + rescue NoMethodError => e + handle_no_method_error?(instance, e, caller) ? instance.class.handle_no_command_error(name) : (fail e) + end + + # Returns the formatted usage by injecting given required arguments + # and required options into the given usage. + def formatted_usage(klass, namespace = true, subcommand = false) + if namespace + namespace = klass.namespace + formatted = "#{namespace.gsub(/^(default)/, '')}:" + end + formatted = "#{klass.namespace.split(':').last} " if subcommand + + formatted ||= "" + + # Add usage with required arguments + formatted << if klass && !klass.arguments.empty? + usage.to_s.gsub(/^#{name}/) do |match| + match << " " << klass.arguments.map { |a| a.usage }.compact.join(" ") + end + else + usage.to_s + end + + # Add required options + formatted << " #{required_options}" + + # Strip and go! + formatted.strip + end + + protected + + def not_debugging?(instance) + !(instance.class.respond_to?(:debugging) && instance.class.debugging) + end + + def required_options + @required_options ||= options.map { |_, o| o.usage if o.required? }.compact.sort.join(" ") + end + + # Given a target, checks if this class name is a public method. + def public_method?(instance) #:nodoc: + !(instance.public_methods & [name.to_s, name.to_sym]).empty? + end + + def private_method?(instance) + !(instance.private_methods & [name.to_s, name.to_sym]).empty? + end + + def local_method?(instance, name) + methods = instance.public_methods(false) + instance.private_methods(false) + instance.protected_methods(false) + !(methods & [name.to_s, name.to_sym]).empty? + end + + def sans_backtrace(backtrace, caller) #:nodoc: + saned = backtrace.reject { |frame| frame =~ FILE_REGEXP || (frame =~ /\.java:/ && RUBY_PLATFORM =~ /java/) || (frame =~ /^kernel\// && RUBY_ENGINE =~ /rbx/) } + saned - caller + end + + def handle_argument_error?(instance, error, caller) + not_debugging?(instance) && (error.message =~ /wrong number of arguments/ || error.message =~ /given \d*, expected \d*/) && begin + saned = sans_backtrace(error.backtrace, caller) + # Ruby 1.9 always include the called method in the backtrace + saned.empty? || (saned.size == 1 && RUBY_VERSION >= "1.9") + end + end + + def handle_no_method_error?(instance, error, caller) + not_debugging?(instance) && + error.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/ + end + end + Task = Command # rubocop:disable ConstantName + + # A command that is hidden in help messages but still invocable. + class HiddenCommand < Command + def hidden? + true + end + end + HiddenTask = HiddenCommand # rubocop:disable ConstantName + + # A dynamic command that handles method missing scenarios. + class DynamicCommand < Command + def initialize(name, options = nil) + super(name.to_s, "A dynamically-generated command", name.to_s, name.to_s, options) + end + + def run(instance, args = []) + if (instance.methods & [name.to_s, name.to_sym]).empty? + super + else + instance.class.handle_no_command_error(name) + end + end + end + DynamicTask = DynamicCommand # rubocop:disable ConstantName +end diff --git a/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb b/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb new file mode 100644 index 0000000000..6cf61db812 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb @@ -0,0 +1,77 @@ +class Bundler::Thor + module CoreExt #:nodoc: + # A hash with indifferent access and magic predicates. + # + # hash = Bundler::Thor::CoreExt::HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true + # + # hash[:foo] #=> 'bar' + # hash['foo'] #=> 'bar' + # hash.foo? #=> true + # + class HashWithIndifferentAccess < ::Hash #:nodoc: + def initialize(hash = {}) + super() + hash.each do |key, value| + self[convert_key(key)] = value + end + end + + def [](key) + super(convert_key(key)) + end + + def []=(key, value) + super(convert_key(key), value) + end + + def delete(key) + super(convert_key(key)) + end + + def values_at(*indices) + indices.map { |key| self[convert_key(key)] } + end + + def merge(other) + dup.merge!(other) + end + + def merge!(other) + other.each do |key, value| + self[convert_key(key)] = value + end + self + end + + # Convert to a Hash with String keys. + def to_hash + Hash.new(default).merge!(self) + end + + protected + + def convert_key(key) + key.is_a?(Symbol) ? key.to_s : key + end + + # Magic predicates. For instance: + # + # options.force? # => !!options['force'] + # options.shebang # => "/usr/lib/local/ruby" + # options.test_framework?(:rspec) # => options[:test_framework] == :rspec + # + def method_missing(method, *args, &block) + method = method.to_s + if method =~ /^(\w+)\?$/ + if args.empty? + !!self[$1] + else + self[$1] == args.first + end + else + self[method] + end + end + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb b/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb new file mode 100644 index 0000000000..19f3c3d43e --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb @@ -0,0 +1,10 @@ +class IO #:nodoc: + class << self + def binread(file, *args) + fail ArgumentError, "wrong number of arguments (#{1 + args.size} for 1..3)" unless args.size < 3 + File.open(file, "rb") do |f| + f.read(*args) + end + end unless method_defined? :binread + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb b/lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb new file mode 100644 index 0000000000..7e80672a07 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb @@ -0,0 +1,98 @@ +class Bundler::Thor + module CoreExt #:nodoc: + if RUBY_VERSION >= "1.9" + class OrderedHash < ::Hash + end + else + # This class is based on the Ruby 1.9 ordered hashes. + # + # It keeps the semantics and most of the efficiency of normal hashes + # while also keeping track of the order in which elements were set. + # + class OrderedHash #:nodoc: + include Enumerable + + Node = Struct.new(:key, :value, :next, :prev) + + def initialize + @hash = {} + end + + def [](key) + @hash[key] && @hash[key].value + end + + def []=(key, value) + if node = @hash[key] # rubocop:disable AssignmentInCondition + node.value = value + else + node = Node.new(key, value) + + if !defined?(@first) || @first.nil? + @first = @last = node + else + node.prev = @last + @last.next = node + @last = node + end + end + + @hash[key] = node + value + end + + def delete(key) + if node = @hash[key] # rubocop:disable AssignmentInCondition + prev_node = node.prev + next_node = node.next + + next_node.prev = prev_node if next_node + prev_node.next = next_node if prev_node + + @first = next_node if @first == node + @last = prev_node if @last == node + + value = node.value + end + + @hash.delete(key) + value + end + + def keys + map { |k, v| k } + end + + def values + map { |k, v| v } + end + + def each + return unless defined?(@first) && @first + yield [@first.key, @first.value] + node = @first + yield [node.key, node.value] while node = node.next # rubocop:disable AssignmentInCondition + self + end + + def merge(other) + hash = self.class.new + + each do |key, value| + hash[key] = value + end + + other.each do |key, value| + hash[key] = value + end + + hash + end + + def empty? + @hash.empty? + end + end + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/error.rb b/lib/bundler/vendor/thor/lib/thor/error.rb new file mode 100644 index 0000000000..fc34c11268 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/error.rb @@ -0,0 +1,32 @@ +class Bundler::Thor + # Bundler::Thor::Error is raised when it's caused by wrong usage of thor classes. Those + # errors have their backtrace suppressed and are nicely shown to the user. + # + # Errors that are caused by the developer, like declaring a method which + # overwrites a thor keyword, it SHOULD NOT raise a Bundler::Thor::Error. This way, we + # ensure that developer errors are shown with full backtrace. + class Error < StandardError + end + + # Raised when a command was not found. + class UndefinedCommandError < Error + end + UndefinedTaskError = UndefinedCommandError # rubocop:disable ConstantName + + class AmbiguousCommandError < Error + end + AmbiguousTaskError = AmbiguousCommandError # rubocop:disable ConstantName + + # Raised when a command was found, but not invoked properly. + class InvocationError < Error + end + + class UnknownArgumentError < Error + end + + class RequiredArgumentMissingError < InvocationError + end + + class MalformattedArgumentError < InvocationError + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/group.rb b/lib/bundler/vendor/thor/lib/thor/group.rb new file mode 100644 index 0000000000..13d168ad62 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/group.rb @@ -0,0 +1,281 @@ +require "bundler/vendor/thor/lib/thor/base" + +# Bundler::Thor has a special class called Bundler::Thor::Group. The main difference to Bundler::Thor class +# is that it invokes all commands at once. It also include some methods that allows +# invocations to be done at the class method, which are not available to Bundler::Thor +# commands. +class Bundler::Thor::Group # rubocop:disable ClassLength + class << self + # The description for this Bundler::Thor::Group. If none is provided, but a source root + # exists, tries to find the USAGE one folder above it, otherwise searches + # in the superclass. + # + # ==== Parameters + # description:: The description for this Bundler::Thor::Group. + # + def desc(description = nil) + if description + @desc = description + else + @desc ||= from_superclass(:desc, nil) + end + end + + # Prints help information. + # + # ==== Options + # short:: When true, shows only usage. + # + def help(shell) + shell.say "Usage:" + shell.say " #{banner}\n" + shell.say + class_options_help(shell) + shell.say desc if desc + end + + # Stores invocations for this class merging with superclass values. + # + def invocations #:nodoc: + @invocations ||= from_superclass(:invocations, {}) + end + + # Stores invocation blocks used on invoke_from_option. + # + def invocation_blocks #:nodoc: + @invocation_blocks ||= from_superclass(:invocation_blocks, {}) + end + + # Invoke the given namespace or class given. It adds an instance + # method that will invoke the klass and command. You can give a block to + # configure how it will be invoked. + # + # The namespace/class given will have its options showed on the help + # usage. Check invoke_from_option for more information. + # + def invoke(*names, &block) # rubocop:disable MethodLength + options = names.last.is_a?(Hash) ? names.pop : {} + verbose = options.fetch(:verbose, true) + + names.each do |name| + invocations[name] = false + invocation_blocks[name] = block if block_given? + + class_eval <<-METHOD, __FILE__, __LINE__ + def _invoke_#{name.to_s.gsub(/\W/, "_")} + klass, command = self.class.prepare_for_invocation(nil, #{name.inspect}) + + if klass + say_status :invoke, #{name.inspect}, #{verbose.inspect} + block = self.class.invocation_blocks[#{name.inspect}] + _invoke_for_class_method klass, command, &block + else + say_status :error, %(#{name.inspect} [not found]), :red + end + end + METHOD + end + end + + # Invoke a thor class based on the value supplied by the user to the + # given option named "name". A class option must be created before this + # method is invoked for each name given. + # + # ==== Examples + # + # class GemGenerator < Bundler::Thor::Group + # class_option :test_framework, :type => :string + # invoke_from_option :test_framework + # end + # + # ==== Boolean options + # + # In some cases, you want to invoke a thor class if some option is true or + # false. This is automatically handled by invoke_from_option. Then the + # option name is used to invoke the generator. + # + # ==== Preparing for invocation + # + # In some cases you want to customize how a specified hook is going to be + # invoked. You can do that by overwriting the class method + # prepare_for_invocation. The class method must necessarily return a klass + # and an optional command. + # + # ==== Custom invocations + # + # You can also supply a block to customize how the option is going to be + # invoked. The block receives two parameters, an instance of the current + # class and the klass to be invoked. + # + def invoke_from_option(*names, &block) # rubocop:disable MethodLength + options = names.last.is_a?(Hash) ? names.pop : {} + verbose = options.fetch(:verbose, :white) + + names.each do |name| + unless class_options.key?(name) + fail ArgumentError, "You have to define the option #{name.inspect} " << + "before setting invoke_from_option." + end + + invocations[name] = true + invocation_blocks[name] = block if block_given? + + class_eval <<-METHOD, __FILE__, __LINE__ + def _invoke_from_option_#{name.to_s.gsub(/\W/, "_")} + return unless options[#{name.inspect}] + + value = options[#{name.inspect}] + value = #{name.inspect} if TrueClass === value + klass, command = self.class.prepare_for_invocation(#{name.inspect}, value) + + if klass + say_status :invoke, value, #{verbose.inspect} + block = self.class.invocation_blocks[#{name.inspect}] + _invoke_for_class_method klass, command, &block + else + say_status :error, %(\#{value} [not found]), :red + end + end + METHOD + end + end + + # Remove a previously added invocation. + # + # ==== Examples + # + # remove_invocation :test_framework + # + def remove_invocation(*names) + names.each do |name| + remove_command(name) + remove_class_option(name) + invocations.delete(name) + invocation_blocks.delete(name) + end + end + + # Overwrite class options help to allow invoked generators options to be + # shown recursively when invoking a generator. + # + def class_options_help(shell, groups = {}) #:nodoc: + get_options_from_invocations(groups, class_options) do |klass| + klass.send(:get_options_from_invocations, groups, class_options) + end + super(shell, groups) + end + + # Get invocations array and merge options from invocations. Those + # options are added to group_options hash. Options that already exists + # in base_options are not added twice. + # + def get_options_from_invocations(group_options, base_options) #:nodoc: # rubocop:disable MethodLength + invocations.each do |name, from_option| + value = if from_option + option = class_options[name] + option.type == :boolean ? name : option.default + else + name + end + next unless value + + klass, _ = prepare_for_invocation(name, value) + next unless klass && klass.respond_to?(:class_options) + + value = value.to_s + human_name = value.respond_to?(:classify) ? value.classify : value + + group_options[human_name] ||= [] + group_options[human_name] += klass.class_options.values.select do |class_option| + base_options[class_option.name.to_sym].nil? && class_option.group.nil? && + !group_options.values.flatten.any? { |i| i.name == class_option.name } + end + + yield klass if block_given? + end + end + + # Returns commands ready to be printed. + def printable_commands(*) + item = [] + item << banner + item << (desc ? "# #{desc.gsub(/\s+/m, ' ')}" : "") + [item] + end + alias_method :printable_tasks, :printable_commands + + def handle_argument_error(command, error, args, arity) #:nodoc: + msg = "#{basename} #{command.name} takes #{arity} argument" + msg << "s" if arity > 1 + msg << ", but it should not." + fail error, msg + end + + protected + + # The method responsible for dispatching given the args. + def dispatch(command, given_args, given_opts, config) #:nodoc: + if Bundler::Thor::HELP_MAPPINGS.include?(given_args.first) + help(config[:shell]) + return + end + + args, opts = Bundler::Thor::Options.split(given_args) + opts = given_opts || opts + + instance = new(args, opts, config) + yield instance if block_given? + + if command + instance.invoke_command(all_commands[command]) + else + instance.invoke_all + end + end + + # The banner for this class. You can customize it if you are invoking the + # thor class by another ways which is not the Bundler::Thor::Runner. + def banner + "#{basename} #{self_command.formatted_usage(self, false)}" + end + + # Represents the whole class as a command. + def self_command #:nodoc: + Bundler::Thor::DynamicCommand.new(namespace, class_options) + end + alias_method :self_task, :self_command + + def baseclass #:nodoc: + Bundler::Thor::Group + end + + def create_command(meth) #:nodoc: + commands[meth.to_s] = Bundler::Thor::Command.new(meth, nil, nil, nil, nil) + true + end + alias_method :create_task, :create_command + end + + include Bundler::Thor::Base + +protected + + # Shortcut to invoke with padding and block handling. Use internally by + # invoke and invoke_from_option class methods. + def _invoke_for_class_method(klass, command = nil, *args, &block) #:nodoc: + with_padding do + if block + case block.arity + when 3 + block.call(self, klass, command) + when 2 + block.call(self, klass) + when 1 + instance_exec(klass, &block) + end + else + invoke klass, command, *args + end + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/invocation.rb b/lib/bundler/vendor/thor/lib/thor/invocation.rb new file mode 100644 index 0000000000..684df2c616 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/invocation.rb @@ -0,0 +1,178 @@ +class Bundler::Thor + module Invocation + def self.included(base) #:nodoc: + base.extend ClassMethods + end + + module ClassMethods + # This method is responsible for receiving a name and find the proper + # class and command for it. The key is an optional parameter which is + # available only in class methods invocations (i.e. in Bundler::Thor::Group). + def prepare_for_invocation(key, name) #:nodoc: + case name + when Symbol, String + Bundler::Thor::Util.find_class_and_command_by_namespace(name.to_s, !key) + else + name + end + end + end + + # Make initializer aware of invocations and the initialization args. + def initialize(args = [], options = {}, config = {}, &block) #:nodoc: + @_invocations = config[:invocations] || Hash.new { |h, k| h[k] = [] } + @_initializer = [args, options, config] + super + end + + # Make the current command chain accessible with in a Bundler::Thor-(sub)command + def current_command_chain + @_invocations.values.flatten.map(&:to_sym) + end + + # Receives a name and invokes it. The name can be a string (either "command" or + # "namespace:command"), a Bundler::Thor::Command, a Class or a Bundler::Thor instance. If the + # command cannot be guessed by name, it can also be supplied as second argument. + # + # You can also supply the arguments, options and configuration values for + # the command to be invoked, if none is given, the same values used to + # initialize the invoker are used to initialize the invoked. + # + # When no name is given, it will invoke the default command of the current class. + # + # ==== Examples + # + # class A < Bundler::Thor + # def foo + # invoke :bar + # invoke "b:hello", ["Erik"] + # end + # + # def bar + # invoke "b:hello", ["Erik"] + # end + # end + # + # class B < Bundler::Thor + # def hello(name) + # puts "hello #{name}" + # end + # end + # + # You can notice that the method "foo" above invokes two commands: "bar", + # which belongs to the same class and "hello" which belongs to the class B. + # + # By using an invocation system you ensure that a command is invoked only once. + # In the example above, invoking "foo" will invoke "b:hello" just once, even + # if it's invoked later by "bar" method. + # + # When class A invokes class B, all arguments used on A initialization are + # supplied to B. This allows lazy parse of options. Let's suppose you have + # some rspec commands: + # + # class Rspec < Bundler::Thor::Group + # class_option :mock_framework, :type => :string, :default => :rr + # + # def invoke_mock_framework + # invoke "rspec:#{options[:mock_framework]}" + # end + # end + # + # As you noticed, it invokes the given mock framework, which might have its + # own options: + # + # class Rspec::RR < Bundler::Thor::Group + # class_option :style, :type => :string, :default => :mock + # end + # + # Since it's not rspec concern to parse mock framework options, when RR + # is invoked all options are parsed again, so RR can extract only the options + # that it's going to use. + # + # If you want Rspec::RR to be initialized with its own set of options, you + # have to do that explicitly: + # + # invoke "rspec:rr", [], :style => :foo + # + # Besides giving an instance, you can also give a class to invoke: + # + # invoke Rspec::RR, [], :style => :foo + # + def invoke(name = nil, *args) + if name.nil? + warn "[Bundler::Thor] Calling invoke() without argument is deprecated. Please use invoke_all instead.\n#{caller.join("\n")}" + return invoke_all + end + + args.unshift(nil) if args.first.is_a?(Array) || args.first.nil? + command, args, opts, config = args + + klass, command = _retrieve_class_and_command(name, command) + fail "Missing Bundler::Thor class for invoke #{name}" unless klass + fail "Expected Bundler::Thor class, got #{klass}" unless klass <= Bundler::Thor::Base + + args, opts, config = _parse_initialization_options(args, opts, config) + klass.send(:dispatch, command, args, opts, config) do |instance| + instance.parent_options = options + end + end + + # Invoke the given command if the given args. + def invoke_command(command, *args) #:nodoc: + current = @_invocations[self.class] + + unless current.include?(command.name) + current << command.name + command.run(self, *args) + end + end + alias_method :invoke_task, :invoke_command + + # Invoke all commands for the current instance. + def invoke_all #:nodoc: + self.class.all_commands.map { |_, command| invoke_command(command) } + end + + # Invokes using shell padding. + def invoke_with_padding(*args) + with_padding { invoke(*args) } + end + + protected + + # Configuration values that are shared between invocations. + def _shared_configuration #:nodoc: + {:invocations => @_invocations} + end + + # This method simply retrieves the class and command to be invoked. + # If the name is nil or the given name is a command in the current class, + # use the given name and return self as class. Otherwise, call + # prepare_for_invocation in the current class. + def _retrieve_class_and_command(name, sent_command = nil) #:nodoc: + case + when name.nil? + [self.class, nil] + when self.class.all_commands[name.to_s] + [self.class, name.to_s] + else + klass, command = self.class.prepare_for_invocation(nil, name) + [klass, command || sent_command] + end + end + alias_method :_retrieve_class_and_task, :_retrieve_class_and_command + + # Initialize klass using values stored in the @_initializer. + def _parse_initialization_options(args, opts, config) #:nodoc: + stored_args, stored_opts, stored_config = @_initializer + + args ||= stored_args.dup + opts ||= stored_opts.dup + + config ||= {} + config = stored_config.merge(_shared_configuration).merge!(config) + + [args, opts, config] + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/line_editor.rb b/lib/bundler/vendor/thor/lib/thor/line_editor.rb new file mode 100644 index 0000000000..ce81a17484 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/line_editor.rb @@ -0,0 +1,17 @@ +require "bundler/vendor/thor/lib/thor/line_editor/basic" +require "bundler/vendor/thor/lib/thor/line_editor/readline" + +class Bundler::Thor + module LineEditor + def self.readline(prompt, options = {}) + best_available.new(prompt, options).readline + end + + def self.best_available + [ + Bundler::Thor::LineEditor::Readline, + Bundler::Thor::LineEditor::Basic + ].detect(&:available?) + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/line_editor/basic.rb b/lib/bundler/vendor/thor/lib/thor/line_editor/basic.rb new file mode 100644 index 0000000000..b121e95575 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/line_editor/basic.rb @@ -0,0 +1,35 @@ +class Bundler::Thor + module LineEditor + class Basic + attr_reader :prompt, :options + + def self.available? + true + end + + def initialize(prompt, options) + @prompt = prompt + @options = options + end + + def readline + $stdout.print(prompt) + get_input + end + + private + + def get_input + if echo? + $stdin.gets + else + $stdin.noecho(&:gets) + end + end + + def echo? + options.fetch(:echo, true) + end + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/line_editor/readline.rb b/lib/bundler/vendor/thor/lib/thor/line_editor/readline.rb new file mode 100644 index 0000000000..dd39cff35d --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/line_editor/readline.rb @@ -0,0 +1,88 @@ +begin + require "readline" +rescue LoadError +end + +class Bundler::Thor + module LineEditor + class Readline < Basic + def self.available? + Object.const_defined?(:Readline) + end + + def readline + if echo? + ::Readline.completion_append_character = nil + # Ruby 1.8.7 does not allow Readline.completion_proc= to receive nil. + if complete = completion_proc + ::Readline.completion_proc = complete + end + ::Readline.readline(prompt, add_to_history?) + else + super + end + end + + private + + def add_to_history? + options.fetch(:add_to_history, true) + end + + def completion_proc + if use_path_completion? + proc { |text| PathCompletion.new(text).matches } + elsif completion_options.any? + proc do |text| + completion_options.select { |option| option.start_with?(text) } + end + end + end + + def completion_options + options.fetch(:limited_to, []) + end + + def use_path_completion? + options.fetch(:path, false) + end + + class PathCompletion + attr_reader :text + private :text + + def initialize(text) + @text = text + end + + def matches + relative_matches + end + + private + + def relative_matches + absolute_matches.map { |path| path.sub(base_path, "") } + end + + def absolute_matches + Dir[glob_pattern].map do |path| + if File.directory?(path) + "#{path}/" + else + path + end + end + end + + def glob_pattern + "#{base_path}#{text}*" + end + + def base_path + "#{Dir.pwd}/" + end + end + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/parser.rb b/lib/bundler/vendor/thor/lib/thor/parser.rb new file mode 100644 index 0000000000..08f80e565d --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/parser.rb @@ -0,0 +1,4 @@ +require "bundler/vendor/thor/lib/thor/parser/argument" +require "bundler/vendor/thor/lib/thor/parser/arguments" +require "bundler/vendor/thor/lib/thor/parser/option" +require "bundler/vendor/thor/lib/thor/parser/options" diff --git a/lib/bundler/vendor/thor/lib/thor/parser/argument.rb b/lib/bundler/vendor/thor/lib/thor/parser/argument.rb new file mode 100644 index 0000000000..84957903cd --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/parser/argument.rb @@ -0,0 +1,73 @@ +class Bundler::Thor + class Argument #:nodoc: + VALID_TYPES = [:numeric, :hash, :array, :string] + + attr_reader :name, :description, :enum, :required, :type, :default, :banner + alias_method :human_name, :name + + def initialize(name, options = {}) + class_name = self.class.name.split("::").last + + type = options[:type] + + fail ArgumentError, "#{class_name} name can't be nil." if name.nil? + fail ArgumentError, "Type :#{type} is not valid for #{class_name.downcase}s." if type && !valid_type?(type) + + @name = name.to_s + @description = options[:desc] + @required = options.key?(:required) ? options[:required] : true + @type = (type || :string).to_sym + @default = options[:default] + @banner = options[:banner] || default_banner + @enum = options[:enum] + + validate! # Trigger specific validations + end + + def usage + required? ? banner : "[#{banner}]" + end + + def required? + required + end + + def show_default? + case default + when Array, String, Hash + !default.empty? + else + default + end + end + + protected + + def validate! + if required? && !default.nil? + fail ArgumentError, "An argument cannot be required and have default value." + elsif @enum && !@enum.is_a?(Array) + fail ArgumentError, "An argument cannot have an enum other than an array." + end + end + + def valid_type?(type) + self.class::VALID_TYPES.include?(type.to_sym) + end + + def default_banner + case type + when :boolean + nil + when :string, :default + human_name.upcase + when :numeric + "N" + when :hash + "key:value" + when :array + "one two three" + end + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb b/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb new file mode 100644 index 0000000000..c7bb648e31 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb @@ -0,0 +1,175 @@ +class Bundler::Thor + class Arguments #:nodoc: # rubocop:disable ClassLength + NUMERIC = /(\d*\.\d+|\d+)/ + + # Receives an array of args and returns two arrays, one with arguments + # and one with switches. + # + def self.split(args) + arguments = [] + + args.each do |item| + break if item =~ /^-/ + arguments << item + end + + [arguments, args[Range.new(arguments.size, -1)]] + end + + def self.parse(*args) + to_parse = args.pop + new(*args).parse(to_parse) + end + + # Takes an array of Bundler::Thor::Argument objects. + # + def initialize(arguments = []) + @assigns, @non_assigned_required = {}, [] + @switches = arguments + + arguments.each do |argument| + if !argument.default.nil? + @assigns[argument.human_name] = argument.default + elsif argument.required? + @non_assigned_required << argument + end + end + end + + def parse(args) + @pile = args.dup + + @switches.each do |argument| + break unless peek + @non_assigned_required.delete(argument) + @assigns[argument.human_name] = send(:"parse_#{argument.type}", argument.human_name) + end + + check_requirement! + @assigns + end + + def remaining # rubocop:disable TrivialAccessors + @pile + end + + private + + def no_or_skip?(arg) + arg =~ /^--(no|skip)-([-\w]+)$/ + $2 + end + + def last? + @pile.empty? + end + + def peek + @pile.first + end + + def shift + @pile.shift + end + + def unshift(arg) + if arg.kind_of?(Array) + @pile = arg + @pile + else + @pile.unshift(arg) + end + end + + def current_is_value? + peek && peek.to_s !~ /^-/ + end + + # Runs through the argument array getting strings that contains ":" and + # mark it as a hash: + # + # [ "name:string", "age:integer" ] + # + # Becomes: + # + # { "name" => "string", "age" => "integer" } + # + def parse_hash(name) + return shift if peek.is_a?(Hash) + hash = {} + + while current_is_value? && peek.include?(":") + key, value = shift.split(":", 2) + hash[key] = value + end + hash + end + + # Runs through the argument array getting all strings until no string is + # found or a switch is found. + # + # ["a", "b", "c"] + # + # And returns it as an array: + # + # ["a", "b", "c"] + # + def parse_array(name) + return shift if peek.is_a?(Array) + array = [] + array << shift while current_is_value? + array + end + + # Check if the peek is numeric format and return a Float or Integer. + # Check if the peek is included in enum if enum is provided. + # Otherwise raises an error. + # + def parse_numeric(name) + return shift if peek.is_a?(Numeric) + + unless peek =~ NUMERIC && $& == peek + fail MalformattedArgumentError, "Expected numeric value for '#{name}'; got #{peek.inspect}" + end + + value = $&.index(".") ? shift.to_f : shift.to_i + if @switches.is_a?(Hash) && switch = @switches[name] + if switch.enum && !switch.enum.include?(value) + fail MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}" + end + end + value + end + + # Parse string: + # for --string-arg, just return the current value in the pile + # for --no-string-arg, nil + # Check if the peek is included in enum if enum is provided. Otherwise raises an error. + # + def parse_string(name) + if no_or_skip?(name) + nil + else + value = shift + if @switches.is_a?(Hash) && switch = @switches[name] # rubocop:disable AssignmentInCondition + if switch.enum && !switch.enum.include?(value) + fail MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}" + end + end + value + end + end + + # Raises an error if @non_assigned_required array is not empty. + # + def check_requirement! + unless @non_assigned_required.empty? + names = @non_assigned_required.map do |o| + o.respond_to?(:switch_name) ? o.switch_name : o.human_name + end.join("', '") + + class_name = self.class.name.split("::").last.downcase + fail RequiredArgumentMissingError, "No value provided for required #{class_name} '#{names}'" + end + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/parser/option.rb b/lib/bundler/vendor/thor/lib/thor/parser/option.rb new file mode 100644 index 0000000000..eb893617f4 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/parser/option.rb @@ -0,0 +1,125 @@ +class Bundler::Thor + class Option < Argument #:nodoc: + attr_reader :aliases, :group, :lazy_default, :hide + + VALID_TYPES = [:boolean, :numeric, :hash, :array, :string] + + def initialize(name, options = {}) + options[:required] = false unless options.key?(:required) + super + @lazy_default = options[:lazy_default] + @group = options[:group].to_s.capitalize if options[:group] + @aliases = Array(options[:aliases]) + @hide = options[:hide] + end + + # This parse quick options given as method_options. It makes several + # assumptions, but you can be more specific using the option method. + # + # parse :foo => "bar" + # #=> Option foo with default value bar + # + # parse [:foo, :baz] => "bar" + # #=> Option foo with default value bar and alias :baz + # + # parse :foo => :required + # #=> Required option foo without default value + # + # parse :foo => 2 + # #=> Option foo with default value 2 and type numeric + # + # parse :foo => :numeric + # #=> Option foo without default value and type numeric + # + # parse :foo => true + # #=> Option foo with default value true and type boolean + # + # The valid types are :boolean, :numeric, :hash, :array and :string. If none + # is given a default type is assumed. This default type accepts arguments as + # string (--foo=value) or booleans (just --foo). + # + # By default all options are optional, unless :required is given. + # + def self.parse(key, value) # rubocop:disable MethodLength + if key.is_a?(Array) + name, *aliases = key + else + name, aliases = key, [] + end + + name = name.to_s + default = value + + type = case value + when Symbol + default = nil + if VALID_TYPES.include?(value) + value + elsif required = (value == :required) # rubocop:disable AssignmentInCondition + :string + end + when TrueClass, FalseClass + :boolean + when Numeric + :numeric + when Hash, Array, String + value.class.name.downcase.to_sym + end + new(name.to_s, :required => required, :type => type, :default => default, :aliases => aliases) + end + + def switch_name + @switch_name ||= dasherized? ? name : dasherize(name) + end + + def human_name + @human_name ||= dasherized? ? undasherize(name) : name + end + + def usage(padding = 0) + sample = if banner && !banner.to_s.empty? + "#{switch_name}=#{banner}" + else + switch_name + end + + sample = "[#{sample}]" unless required? + + if boolean? + sample << ", [#{dasherize("no-" + human_name)}]" unless name == "force" + end + + if aliases.empty? + (" " * padding) << sample + else + "#{aliases.join(', ')}, #{sample}" + end + end + + VALID_TYPES.each do |type| + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{type}? + self.type == #{type.inspect} + end + RUBY + end + + protected + + def validate! + fail ArgumentError, "An option cannot be boolean and required." if boolean? && required? + end + + def dasherized? + name.index("-") == 0 + end + + def undasherize(str) + str.sub(/^-{1,2}/, "") + end + + def dasherize(str) + (str.length > 1 ? "--" : "-") + str.gsub("_", "-") + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/parser/options.rb b/lib/bundler/vendor/thor/lib/thor/parser/options.rb new file mode 100644 index 0000000000..deac6a0c16 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/parser/options.rb @@ -0,0 +1,218 @@ +class Bundler::Thor + class Options < Arguments #:nodoc: # rubocop:disable ClassLength + LONG_RE = /^(--\w+(?:-\w+)*)$/ + SHORT_RE = /^(-[a-z])$/i + EQ_RE = /^(--\w+(?:-\w+)*|-[a-z])=(.*)$/i + SHORT_SQ_RE = /^-([a-z]{2,})$/i # Allow either -x -v or -xv style for single char args + SHORT_NUM = /^(-[a-z])#{NUMERIC}$/i + OPTS_END = "--".freeze + + # Receives a hash and makes it switches. + def self.to_switches(options) + options.map do |key, value| + case value + when true + "--#{key}" + when Array + "--#{key} #{value.map { |v| v.inspect }.join(' ')}" + when Hash + "--#{key} #{value.map { |k, v| "#{k}:#{v}" }.join(' ')}" + when nil, false + "" + else + "--#{key} #{value.inspect}" + end + end.join(" ") + end + + # Takes a hash of Bundler::Thor::Option and a hash with defaults. + # + # If +stop_on_unknown+ is true, #parse will stop as soon as it encounters + # an unknown option or a regular argument. + def initialize(hash_options = {}, defaults = {}, stop_on_unknown = false) + @stop_on_unknown = stop_on_unknown + options = hash_options.values + super(options) + + # Add defaults + defaults.each do |key, value| + @assigns[key.to_s] = value + @non_assigned_required.delete(hash_options[key]) + end + + @shorts, @switches, @extra = {}, {}, [] + + options.each do |option| + @switches[option.switch_name] = option + + option.aliases.each do |short| + name = short.to_s.sub(/^(?!\-)/, "-") + @shorts[name] ||= option.switch_name + end + end + end + + def remaining # rubocop:disable TrivialAccessors + @extra + end + + def peek + return super unless @parsing_options + + result = super + if result == OPTS_END + shift + @parsing_options = false + super + else + result + end + end + + def parse(args) # rubocop:disable MethodLength + @pile = args.dup + @parsing_options = true + + while peek + if parsing_options? + match, is_switch = current_is_switch? + shifted = shift + + if is_switch + case shifted + when SHORT_SQ_RE + unshift($1.split("").map { |f| "-#{f}" }) + next + when EQ_RE, SHORT_NUM + unshift($2) + switch = $1 + when LONG_RE, SHORT_RE + switch = $1 + end + + switch = normalize_switch(switch) + option = switch_option(switch) + @assigns[option.human_name] = parse_peek(switch, option) + elsif @stop_on_unknown + @parsing_options = false + @extra << shifted + @extra << shift while peek + break + elsif match + @extra << shifted + @extra << shift while peek && peek !~ /^-/ + else + @extra << shifted + end + else + @extra << shift + end + end + + check_requirement! + + assigns = Bundler::Thor::CoreExt::HashWithIndifferentAccess.new(@assigns) + assigns.freeze + assigns + end + + def check_unknown! + # an unknown option starts with - or -- and has no more --'s afterward. + unknown = @extra.select { |str| str =~ /^--?(?:(?!--).)*$/ } + fail UnknownArgumentError, "Unknown switches '#{unknown.join(', ')}'" unless unknown.empty? + end + + protected + + # Check if the current value in peek is a registered switch. + # + # Two booleans are returned. The first is true if the current value + # starts with a hyphen; the second is true if it is a registered switch. + def current_is_switch? + case peek + when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM + [true, switch?($1)] + when SHORT_SQ_RE + [true, $1.split("").any? { |f| switch?("-#{f}") }] + else + [false, false] + end + end + + def current_is_switch_formatted? + case peek + when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM, SHORT_SQ_RE + true + else + false + end + end + + def current_is_value? + peek && (!parsing_options? || super) + end + + def switch?(arg) + switch_option(normalize_switch(arg)) + end + + def switch_option(arg) + if match = no_or_skip?(arg) # rubocop:disable AssignmentInCondition + @switches[arg] || @switches["--#{match}"] + else + @switches[arg] + end + end + + # Check if the given argument is actually a shortcut. + # + def normalize_switch(arg) + (@shorts[arg] || arg).tr("_", "-") + end + + def parsing_options? + peek + @parsing_options + end + + # Parse boolean values which can be given as --foo=true, --foo or --no-foo. + # + def parse_boolean(switch) + if current_is_value? + if ["true", "TRUE", "t", "T", true].include?(peek) + shift + true + elsif ["false", "FALSE", "f", "F", false].include?(peek) + shift + false + else + true + end + else + @switches.key?(switch) || !no_or_skip?(switch) + end + end + + # Parse the value at the peek analyzing if it requires an input or not. + # + def parse_peek(switch, option) + if parsing_options? && (current_is_switch_formatted? || last?) + if option.boolean? + # No problem for boolean types + elsif no_or_skip?(switch) + return nil # User set value to nil + elsif option.string? && !option.required? + # Return the default if there is one, else the human name + return option.lazy_default || option.default || option.human_name + elsif option.lazy_default + return option.lazy_default + else + fail MalformattedArgumentError, "No value provided for option '#{switch}'" + end + end + + @non_assigned_required.delete(option) + send(:"parse_#{option.type}", switch) + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/rake_compat.rb b/lib/bundler/vendor/thor/lib/thor/rake_compat.rb new file mode 100644 index 0000000000..60282e2991 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/rake_compat.rb @@ -0,0 +1,71 @@ +require "rake" +require "rake/dsl_definition" + +class Bundler::Thor + # Adds a compatibility layer to your Bundler::Thor classes which allows you to use + # rake package tasks. For example, to use rspec rake tasks, one can do: + # + # require 'bundler/vendor/thor/lib/thor/rake_compat' + # require 'rspec/core/rake_task' + # + # class Default < Bundler::Thor + # include Bundler::Thor::RakeCompat + # + # RSpec::Core::RakeTask.new(:spec) do |t| + # t.spec_opts = ['--options', './.rspec'] + # t.spec_files = FileList['spec/**/*_spec.rb'] + # end + # end + # + module RakeCompat + include Rake::DSL if defined?(Rake::DSL) + + def self.rake_classes + @rake_classes ||= [] + end + + def self.included(base) + # Hack. Make rakefile point to invoker, so rdoc task is generated properly. + rakefile = File.basename(caller[0].match(/(.*):\d+/)[1]) + Rake.application.instance_variable_set(:@rakefile, rakefile) + rake_classes << base + end + end +end + +# override task on (main), for compatibility with Rake 0.9 +instance_eval do + alias rake_namespace namespace + + def task(*) + task = super + + if klass = Bundler::Thor::RakeCompat.rake_classes.last # rubocop:disable AssignmentInCondition + non_namespaced_name = task.name.split(":").last + + description = non_namespaced_name + description << task.arg_names.map { |n| n.to_s.upcase }.join(" ") + description.strip! + + klass.desc description, Rake.application.last_description || non_namespaced_name + Rake.application.last_description = nil + klass.send :define_method, non_namespaced_name do |*args| + Rake::Task[task.name.to_sym].invoke(*args) + end + end + + task + end + + def namespace(name) + if klass = Bundler::Thor::RakeCompat.rake_classes.last # rubocop:disable AssignmentInCondition + const_name = Bundler::Thor::Util.camel_case(name.to_s).to_sym + klass.const_set(const_name, Class.new(Bundler::Thor)) + new_klass = klass.const_get(const_name) + Bundler::Thor::RakeCompat.rake_classes << new_klass + end + + super + Bundler::Thor::RakeCompat.rake_classes.pop + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/runner.rb b/lib/bundler/vendor/thor/lib/thor/runner.rb new file mode 100644 index 0000000000..f0d7bfe2e0 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/runner.rb @@ -0,0 +1,322 @@ +require "bundler/vendor/thor/lib/thor" +require "bundler/vendor/thor/lib/thor/group" +require "bundler/vendor/thor/lib/thor/core_ext/io_binary_read" + +require "fileutils" +require "open-uri" +require "yaml" +require "digest/md5" +require "pathname" + +class Bundler::Thor::Runner < Bundler::Thor #:nodoc: # rubocop:disable ClassLength + map "-T" => :list, "-i" => :install, "-u" => :update, "-v" => :version + + # Override Bundler::Thor#help so it can give information about any class and any method. + # + def help(meth = nil) + if meth && !self.respond_to?(meth) + initialize_thorfiles(meth) + klass, command = Bundler::Thor::Util.find_class_and_command_by_namespace(meth) + self.class.handle_no_command_error(command, false) if klass.nil? + klass.start(["-h", command].compact, :shell => shell) + else + super + end + end + + # If a command is not found on Bundler::Thor::Runner, method missing is invoked and + # Bundler::Thor::Runner is then responsible for finding the command in all classes. + # + def method_missing(meth, *args) + meth = meth.to_s + initialize_thorfiles(meth) + klass, command = Bundler::Thor::Util.find_class_and_command_by_namespace(meth) + self.class.handle_no_command_error(command, false) if klass.nil? + args.unshift(command) if command + klass.start(args, :shell => shell) + end + + desc "install NAME", "Install an optionally named Bundler::Thor file into your system commands" + method_options :as => :string, :relative => :boolean, :force => :boolean + def install(name) # rubocop:disable MethodLength + initialize_thorfiles + + # If a directory name is provided as the argument, look for a 'main.thor' + # command in said directory. + begin + if File.directory?(File.expand_path(name)) + base, package = File.join(name, "main.thor"), :directory + contents = open(base) { |input| input.read } + else + base, package = name, :file + contents = open(name) { |input| input.read } + end + rescue OpenURI::HTTPError + raise Error, "Error opening URI '#{name}'" + rescue Errno::ENOENT + fail Error, "Error opening file '#{name}'" + end + + say "Your Bundler::Thorfile contains:" + say contents + + unless options["force"] + return false if no?("Do you wish to continue [y/N]?") + end + + as = options["as"] || begin + first_line = contents.split("\n")[0] + (match = first_line.match(/\s*#\s*module:\s*([^\n]*)/)) ? match[1].strip : nil + end + + unless as + basename = File.basename(name) + as = ask("Please specify a name for #{name} in the system repository [#{basename}]:") + as = basename if as.empty? + end + + location = if options[:relative] || name =~ %r{^https?://} + name + else + File.expand_path(name) + end + + thor_yaml[as] = { + :filename => Digest::MD5.hexdigest(name + as), + :location => location, + :namespaces => Bundler::Thor::Util.namespaces_in_content(contents, base) + } + + save_yaml(thor_yaml) + say "Storing thor file in your system repository" + destination = File.join(thor_root, thor_yaml[as][:filename]) + + if package == :file + File.open(destination, "w") { |f| f.puts contents } + else + FileUtils.cp_r(name, destination) + end + + thor_yaml[as][:filename] # Indicate success + end + + desc "version", "Show Bundler::Thor version" + def version + require "bundler/vendor/thor/lib/thor/version" + say "Bundler::Thor #{Bundler::Thor::VERSION}" + end + + desc "uninstall NAME", "Uninstall a named Bundler::Thor module" + def uninstall(name) + fail Error, "Can't find module '#{name}'" unless thor_yaml[name] + say "Uninstalling #{name}." + FileUtils.rm_rf(File.join(thor_root, "#{thor_yaml[name][:filename]}")) + + thor_yaml.delete(name) + save_yaml(thor_yaml) + + puts "Done." + end + + desc "update NAME", "Update a Bundler::Thor file from its original location" + def update(name) + fail Error, "Can't find module '#{name}'" if !thor_yaml[name] || !thor_yaml[name][:location] + + say "Updating '#{name}' from #{thor_yaml[name][:location]}" + + old_filename = thor_yaml[name][:filename] + self.options = options.merge("as" => name) + + if File.directory? File.expand_path(name) + FileUtils.rm_rf(File.join(thor_root, old_filename)) + + thor_yaml.delete(old_filename) + save_yaml(thor_yaml) + + filename = install(name) + else + filename = install(thor_yaml[name][:location]) + end + + unless filename == old_filename + File.delete(File.join(thor_root, old_filename)) + end + end + + desc "installed", "List the installed Bundler::Thor modules and commands" + method_options :internal => :boolean + def installed + initialize_thorfiles(nil, true) + display_klasses(true, options["internal"]) + end + + desc "list [SEARCH]", "List the available thor commands (--substring means .*SEARCH)" + method_options :substring => :boolean, :group => :string, :all => :boolean, :debug => :boolean + def list(search = "") + initialize_thorfiles + + search = ".*#{search}" if options["substring"] + search = /^#{search}.*/i + group = options[:group] || "standard" + + klasses = Bundler::Thor::Base.subclasses.select do |k| + (options[:all] || k.group == group) && k.namespace =~ search + end + + display_klasses(false, false, klasses) + end + +private + + def self.banner(command, all = false, subcommand = false) + "thor " + command.formatted_usage(self, all, subcommand) + end + + def thor_root + Bundler::Thor::Util.thor_root + end + + def thor_yaml + @thor_yaml ||= begin + yaml_file = File.join(thor_root, "thor.yml") + yaml = YAML.load_file(yaml_file) if File.exist?(yaml_file) + yaml || {} + end + end + + # Save the yaml file. If none exists in thor root, creates one. + # + def save_yaml(yaml) + yaml_file = File.join(thor_root, "thor.yml") + + unless File.exist?(yaml_file) + FileUtils.mkdir_p(thor_root) + yaml_file = File.join(thor_root, "thor.yml") + FileUtils.touch(yaml_file) + end + + File.open(yaml_file, "w") { |f| f.puts yaml.to_yaml } + end + + def self.exit_on_failure? + true + end + + # Load the Bundler::Thorfiles. If relevant_to is supplied, looks for specific files + # in the thor_root instead of loading them all. + # + # By default, it also traverses the current path until find Bundler::Thor files, as + # described in thorfiles. This look up can be skipped by supplying + # skip_lookup true. + # + def initialize_thorfiles(relevant_to = nil, skip_lookup = false) + thorfiles(relevant_to, skip_lookup).each do |f| + Bundler::Thor::Util.load_thorfile(f, nil, options[:debug]) unless Bundler::Thor::Base.subclass_files.keys.include?(File.expand_path(f)) + end + end + + # Finds Bundler::Thorfiles by traversing from your current directory down to the root + # directory of your system. If at any time we find a Bundler::Thor file, we stop. + # + # We also ensure that system-wide Bundler::Thorfiles are loaded first, so local + # Bundler::Thorfiles can override them. + # + # ==== Example + # + # If we start at /Users/wycats/dev/thor ... + # + # 1. /Users/wycats/dev/thor + # 2. /Users/wycats/dev + # 3. /Users/wycats <-- we find a Bundler::Thorfile here, so we stop + # + # Suppose we start at c:\Documents and Settings\james\dev\thor ... + # + # 1. c:\Documents and Settings\james\dev\thor + # 2. c:\Documents and Settings\james\dev + # 3. c:\Documents and Settings\james + # 4. c:\Documents and Settings + # 5. c:\ <-- no Bundler::Thorfiles found! + # + def thorfiles(relevant_to = nil, skip_lookup = false) + thorfiles = [] + + unless skip_lookup + Pathname.pwd.ascend do |path| + thorfiles = Bundler::Thor::Util.globs_for(path).map { |g| Dir[g] }.flatten + break unless thorfiles.empty? + end + end + + files = (relevant_to ? thorfiles_relevant_to(relevant_to) : Bundler::Thor::Util.thor_root_glob) + files += thorfiles + files -= ["#{thor_root}/thor.yml"] + + files.map! do |file| + File.directory?(file) ? File.join(file, "main.thor") : file + end + end + + # Load Bundler::Thorfiles relevant to the given method. If you provide "foo:bar" it + # will load all thor files in the thor.yaml that has "foo" e "foo:bar" + # namespaces registered. + # + def thorfiles_relevant_to(meth) + lookup = [meth, meth.split(":")[0...-1].join(":")] + + files = thor_yaml.select do |k, v| + v[:namespaces] && !(v[:namespaces] & lookup).empty? + end + + files.map { |k, v| File.join(thor_root, "#{v[:filename]}") } + end + + # Display information about the given klasses. If with_module is given, + # it shows a table with information extracted from the yaml file. + # + def display_klasses(with_modules = false, show_internal = false, klasses = Bundler::Thor::Base.subclasses) + klasses -= [Bundler::Thor, Bundler::Thor::Runner, Bundler::Thor::Group] unless show_internal + + fail Error, "No Bundler::Thor commands available" if klasses.empty? + show_modules if with_modules && !thor_yaml.empty? + + list = Hash.new { |h, k| h[k] = [] } + groups = klasses.select { |k| k.ancestors.include?(Bundler::Thor::Group) } + + # Get classes which inherit from Bundler::Thor + (klasses - groups).each { |k| list[k.namespace.split(":").first] += k.printable_commands(false) } + + # Get classes which inherit from Bundler::Thor::Base + groups.map! { |k| k.printable_commands(false).first } + list["root"] = groups + + # Order namespaces with default coming first + list = list.sort { |a, b| a[0].sub(/^default/, "") <=> b[0].sub(/^default/, "") } + list.each { |n, commands| display_commands(n, commands) unless commands.empty? } + end + + def display_commands(namespace, list) #:nodoc: + list.sort! { |a, b| a[0] <=> b[0] } + + say shell.set_color(namespace, :blue, true) + say "-" * namespace.size + + print_table(list, :truncate => true) + say + end + alias_method :display_tasks, :display_commands + + def show_modules #:nodoc: + info = [] + labels = %w[Modules Namespaces] + + info << labels + info << ["-" * labels[0].size, "-" * labels[1].size] + + thor_yaml.each do |name, hash| + info << [name, hash[:namespaces].join(", ")] + end + + print_table info + say "" + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/shell.rb b/lib/bundler/vendor/thor/lib/thor/shell.rb new file mode 100644 index 0000000000..91afdce2aa --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/shell.rb @@ -0,0 +1,81 @@ +require "rbconfig" + +class Bundler::Thor + module Base + class << self + attr_writer :shell + + # Returns the shell used in all Bundler::Thor classes. If you are in a Unix platform + # it will use a colored log, otherwise it will use a basic one without color. + # + def shell + @shell ||= if ENV["THOR_SHELL"] && ENV["THOR_SHELL"].size > 0 + Bundler::Thor::Shell.const_get(ENV["THOR_SHELL"]) + elsif RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ && !ENV["ANSICON"] + Bundler::Thor::Shell::Basic + else + Bundler::Thor::Shell::Color + end + end + end + end + + module Shell + SHELL_DELEGATED_METHODS = [:ask, :error, :set_color, :yes?, :no?, :say, :say_status, :print_in_columns, :print_table, :print_wrapped, :file_collision, :terminal_width] + attr_writer :shell + + autoload :Basic, "bundler/vendor/thor/lib/thor/shell/basic" + autoload :Color, "bundler/vendor/thor/lib/thor/shell/color" + autoload :HTML, "bundler/vendor/thor/lib/thor/shell/html" + + # Add shell to initialize config values. + # + # ==== Configuration + # shell:: An instance of the shell to be used. + # + # ==== Examples + # + # class MyScript < Bundler::Thor + # argument :first, :type => :numeric + # end + # + # MyScript.new [1.0], { :foo => :bar }, :shell => Bundler::Thor::Shell::Basic.new + # + def initialize(args = [], options = {}, config = {}) + super + self.shell = config[:shell] + shell.base ||= self if shell.respond_to?(:base) + end + + # Holds the shell for the given Bundler::Thor instance. If no shell is given, + # it gets a default shell from Bundler::Thor::Base.shell. + def shell + @shell ||= Bundler::Thor::Base.shell.new + end + + # Common methods that are delegated to the shell. + SHELL_DELEGATED_METHODS.each do |method| + module_eval <<-METHOD, __FILE__, __LINE__ + def #{method}(*args,&block) + shell.#{method}(*args,&block) + end + METHOD + end + + # Yields the given block with padding. + def with_padding + shell.padding += 1 + yield + ensure + shell.padding -= 1 + end + + protected + + # Allow shell to be shared between invocations. + # + def _shared_configuration #:nodoc: + super.merge!(:shell => shell) + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb new file mode 100644 index 0000000000..278ffa3df0 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb @@ -0,0 +1,421 @@ +require "tempfile" +require "io/console" if RUBY_VERSION > "1.9.2" + +class Bundler::Thor + module Shell + class Basic # rubocop:disable ClassLength + attr_accessor :base + attr_reader :padding + + # Initialize base, mute and padding to nil. + # + def initialize #:nodoc: + @base, @mute, @padding, @always_force = nil, false, 0, false + end + + # Mute everything that's inside given block + # + def mute + @mute = true + yield + ensure + @mute = false + end + + # Check if base is muted + # + def mute? # rubocop:disable TrivialAccessors + @mute + end + + # Sets the output padding, not allowing less than zero values. + # + def padding=(value) + @padding = [0, value].max + end + + # Asks something to the user and receives a response. + # + # If asked to limit the correct responses, you can pass in an + # array of acceptable answers. If one of those is not supplied, + # they will be shown a message stating that one of those answers + # must be given and re-asked the question. + # + # If asking for sensitive information, the :echo option can be set + # to false to mask user input from $stdin. + # + # If the required input is a path, then set the path option to + # true. This will enable tab completion for file paths relative + # to the current working directory on systems that support + # Readline. + # + # ==== Example + # ask("What is your name?") + # + # ask("What is your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"]) + # + # ask("What is your password?", :echo => false) + # + # ask("Where should the file be saved?", :path => true) + # + def ask(statement, *args) + options = args.last.is_a?(Hash) ? args.pop : {} + color = args.first + + if options[:limited_to] + ask_filtered(statement, color, options) + else + ask_simply(statement, color, options) + end + end + + # Say (print) something to the user. If the sentence ends with a whitespace + # or tab character, a new line is not appended (print + flush). Otherwise + # are passed straight to puts (behavior got from Highline). + # + # ==== Example + # say("I know you knew that.") + # + def say(message = "", color = nil, force_new_line = (message.to_s !~ /( |\t)\Z/)) + buffer = prepare_message(message, *color) + buffer << "\n" if force_new_line && !message.to_s.end_with?("\n") + + stdout.print(buffer) + stdout.flush + end + + # Say a status with the given color and appends the message. Since this + # method is used frequently by actions, it allows nil or false to be given + # in log_status, avoiding the message from being shown. If a Symbol is + # given in log_status, it's used as the color. + # + def say_status(status, message, log_status = true) + return if quiet? || log_status == false + spaces = " " * (padding + 1) + color = log_status.is_a?(Symbol) ? log_status : :green + + status = status.to_s.rjust(12) + status = set_color status, color, true if color + + buffer = "#{status}#{spaces}#{message}" + buffer << "\n" unless buffer.end_with?("\n") + + stdout.print(buffer) + stdout.flush + end + + # Make a question the to user and returns true if the user replies "y" or + # "yes". + # + def yes?(statement, color = nil) + !!(ask(statement, color, :add_to_history => false) =~ is?(:yes)) + end + + # Make a question the to user and returns true if the user replies "n" or + # "no". + # + def no?(statement, color = nil) + !!(ask(statement, color, :add_to_history => false) =~ is?(:no)) + end + + # Prints values in columns + # + # ==== Parameters + # Array[String, String, ...] + # + def print_in_columns(array) + return if array.empty? + colwidth = (array.map { |el| el.to_s.size }.max || 0) + 2 + array.each_with_index do |value, index| + # Don't output trailing spaces when printing the last column + if ((((index + 1) % (terminal_width / colwidth))).zero? && !index.zero?) || index + 1 == array.length + stdout.puts value + else + stdout.printf("%-#{colwidth}s", value) + end + end + end + + # Prints a table. + # + # ==== Parameters + # Array[Array[String, String, ...]] + # + # ==== Options + # indent:: Indent the first column by indent value. + # colwidth:: Force the first column to colwidth spaces wide. + # + def print_table(array, options = {}) # rubocop:disable MethodLength + return if array.empty? + + formats, indent, colwidth = [], options[:indent].to_i, options[:colwidth] + options[:truncate] = terminal_width if options[:truncate] == true + + formats << "%-#{colwidth + 2}s" if colwidth + start = colwidth ? 1 : 0 + + colcount = array.max { |a, b| a.size <=> b.size }.size + + maximas = [] + + start.upto(colcount - 1) do |index| + maxima = array.map { |row| row[index] ? row[index].to_s.size : 0 }.max + maximas << maxima + if index == colcount - 1 + # Don't output 2 trailing spaces when printing the last column + formats << "%-s" + else + formats << "%-#{maxima + 2}s" + end + end + + formats[0] = formats[0].insert(0, " " * indent) + formats << "%s" + + array.each do |row| + sentence = "" + + row.each_with_index do |column, index| + maxima = maximas[index] + + if column.is_a?(Numeric) + if index == row.size - 1 + # Don't output 2 trailing spaces when printing the last column + f = "%#{maxima}s" + else + f = "%#{maxima}s " + end + else + f = formats[index] + end + sentence << f % column.to_s + end + + sentence = truncate(sentence, options[:truncate]) if options[:truncate] + stdout.puts sentence + end + end + + # Prints a long string, word-wrapping the text to the current width of the + # terminal display. Ideal for printing heredocs. + # + # ==== Parameters + # String + # + # ==== Options + # indent:: Indent each line of the printed paragraph by indent value. + # + def print_wrapped(message, options = {}) + indent = options[:indent] || 0 + width = terminal_width - indent + paras = message.split("\n\n") + + paras.map! do |unwrapped| + unwrapped.strip.gsub(/\n/, " ").squeeze(" ").gsub(/.{1,#{width}}(?:\s|\Z)/) { ($& + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n") } + end + + paras.each do |para| + para.split("\n").each do |line| + stdout.puts line.insert(0, " " * indent) + end + stdout.puts unless para == paras.last + end + end + + # Deals with file collision and returns true if the file should be + # overwritten and false otherwise. If a block is given, it uses the block + # response as the content for the diff. + # + # ==== Parameters + # destination:: the destination file to solve conflicts + # block:: an optional block that returns the value to be used in diff + # + def file_collision(destination) # rubocop:disable MethodLength + return true if @always_force + options = block_given? ? "[Ynaqdh]" : "[Ynaqh]" + + loop do + answer = ask( + %[Overwrite #{destination}? (enter "h" for help) #{options}], + :add_to_history => false + ) + + case answer + when is?(:yes), is?(:force), "" + return true + when is?(:no), is?(:skip) + return false + when is?(:always) + return @always_force = true + when is?(:quit) + say "Aborting..." + fail SystemExit + when is?(:diff) + show_diff(destination, yield) if block_given? + say "Retrying..." + else + say file_collision_help + end + end + end + + # This code was copied from Rake, available under MIT-LICENSE + # Copyright (c) 2003, 2004 Jim Weirich + def terminal_width + if ENV["THOR_COLUMNS"] + result = ENV["THOR_COLUMNS"].to_i + else + result = unix? ? dynamic_width : 80 + end + result < 10 ? 80 : result + rescue + 80 + end + + # Called if something goes wrong during the execution. This is used by Bundler::Thor + # internally and should not be used inside your scripts. If something went + # wrong, you can always raise an exception. If you raise a Bundler::Thor::Error, it + # will be rescued and wrapped in the method below. + # + def error(statement) + stderr.puts statement + end + + # Apply color to the given string with optional bold. Disabled in the + # Bundler::Thor::Shell::Basic class. + # + def set_color(string, *args) #:nodoc: + string + end + + protected + + def prepare_message(message, *color) + spaces = " " * padding + spaces + set_color(message.to_s, *color) + end + + def can_display_colors? + false + end + + def lookup_color(color) + return color unless color.is_a?(Symbol) + self.class.const_get(color.to_s.upcase) + end + + def stdout + $stdout + end + + def stderr + $stderr + end + + def is?(value) #:nodoc: + value = value.to_s + + if value.size == 1 + /\A#{value}\z/i + else + /\A(#{value}|#{value[0, 1]})\z/i + end + end + + def file_collision_help #:nodoc: + <<-HELP + Y - yes, overwrite + n - no, do not overwrite + a - all, overwrite this and all others + q - quit, abort + d - diff, show the differences between the old and the new + h - help, show this help + HELP + end + + def show_diff(destination, content) #:nodoc: + diff_cmd = ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u" + + Tempfile.open(File.basename(destination), File.dirname(destination)) do |temp| + temp.write content + temp.rewind + system %(#{diff_cmd} "#{destination}" "#{temp.path}") + end + end + + def quiet? #:nodoc: + mute? || (base && base.options[:quiet]) + end + + # Calculate the dynamic width of the terminal + def dynamic_width + @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput) + end + + def dynamic_width_stty + %x(stty size 2>/dev/null).split[1].to_i + end + + def dynamic_width_tput + %x(tput cols 2>/dev/null).to_i + end + + def unix? + RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i + end + + def truncate(string, width) + as_unicode do + chars = string.chars.to_a + if chars.length <= width + chars.join + else + ( chars[0, width - 3].join) + "..." + end + end + end + + if "".respond_to?(:encode) + def as_unicode + yield + end + else + def as_unicode + old, $KCODE = $KCODE, "U" + yield + ensure + $KCODE = old + end + end + + def ask_simply(statement, color, options) + default = options[:default] + message = [statement, ("(#{default})" if default), nil].uniq.join(" ") + message = prepare_message(message, color) + result = Bundler::Thor::LineEditor.readline(message, options) + + return unless result + + result.strip! + + if default && result == "" + default + else + result + end + end + + def ask_filtered(statement, color, options) + answer_set = options[:limited_to] + correct_answer = nil + until correct_answer + answers = answer_set.join(", ") + answer = ask_simply("#{statement} [#{answers}]", color, options) + correct_answer = answer_set.include?(answer) ? answer : nil + say("Your response must be one of: [#{answers}]. Please try again.") unless correct_answer + end + correct_answer + end + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/shell/color.rb b/lib/bundler/vendor/thor/lib/thor/shell/color.rb new file mode 100644 index 0000000000..1e2d26cfc5 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/shell/color.rb @@ -0,0 +1,149 @@ +require "bundler/vendor/thor/lib/thor/shell/basic" + +class Bundler::Thor + module Shell + # Inherit from Bundler::Thor::Shell::Basic and add set_color behavior. Check + # Bundler::Thor::Shell::Basic to see all available methods. + # + class Color < Basic + # Embed in a String to clear all previous ANSI sequences. + CLEAR = "\e[0m" + # The start of an ANSI bold sequence. + BOLD = "\e[1m" + + # Set the terminal's foreground ANSI color to black. + BLACK = "\e[30m" + # Set the terminal's foreground ANSI color to red. + RED = "\e[31m" + # Set the terminal's foreground ANSI color to green. + GREEN = "\e[32m" + # Set the terminal's foreground ANSI color to yellow. + YELLOW = "\e[33m" + # Set the terminal's foreground ANSI color to blue. + BLUE = "\e[34m" + # Set the terminal's foreground ANSI color to magenta. + MAGENTA = "\e[35m" + # Set the terminal's foreground ANSI color to cyan. + CYAN = "\e[36m" + # Set the terminal's foreground ANSI color to white. + WHITE = "\e[37m" + + # Set the terminal's background ANSI color to black. + ON_BLACK = "\e[40m" + # Set the terminal's background ANSI color to red. + ON_RED = "\e[41m" + # Set the terminal's background ANSI color to green. + ON_GREEN = "\e[42m" + # Set the terminal's background ANSI color to yellow. + ON_YELLOW = "\e[43m" + # Set the terminal's background ANSI color to blue. + ON_BLUE = "\e[44m" + # Set the terminal's background ANSI color to magenta. + ON_MAGENTA = "\e[45m" + # Set the terminal's background ANSI color to cyan. + ON_CYAN = "\e[46m" + # Set the terminal's background ANSI color to white. + ON_WHITE = "\e[47m" + + # Set color by using a string or one of the defined constants. If a third + # option is set to true, it also adds bold to the string. This is based + # on Highline implementation and it automatically appends CLEAR to the end + # of the returned String. + # + # Pass foreground, background and bold options to this method as + # symbols. + # + # Example: + # + # set_color "Hi!", :red, :on_white, :bold + # + # The available colors are: + # + # :bold + # :black + # :red + # :green + # :yellow + # :blue + # :magenta + # :cyan + # :white + # :on_black + # :on_red + # :on_green + # :on_yellow + # :on_blue + # :on_magenta + # :on_cyan + # :on_white + def set_color(string, *colors) + if colors.compact.empty? || !can_display_colors? + string + elsif colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) } + ansi_colors = colors.map { |color| lookup_color(color) } + "#{ansi_colors.join}#{string}#{CLEAR}" + else + # The old API was `set_color(color, bold=boolean)`. We + # continue to support the old API because you should never + # break old APIs unnecessarily :P + foreground, bold = colors + foreground = self.class.const_get(foreground.to_s.upcase) if foreground.is_a?(Symbol) + + bold = bold ? BOLD : "" + "#{bold}#{foreground}#{string}#{CLEAR}" + end + end + + protected + + def can_display_colors? + stdout.tty? + end + + # Overwrite show_diff to show diff with colors if Diff::LCS is + # available. + # + def show_diff(destination, content) #:nodoc: + if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil? + actual = File.binread(destination).to_s.split("\n") + content = content.to_s.split("\n") + + Diff::LCS.sdiff(actual, content).each do |diff| + output_diff_line(diff) + end + else + super + end + end + + def output_diff_line(diff) #:nodoc: + case diff.action + when "-" + say "- #{diff.old_element.chomp}", :red, true + when "+" + say "+ #{diff.new_element.chomp}", :green, true + when "!" + say "- #{diff.old_element.chomp}", :red, true + say "+ #{diff.new_element.chomp}", :green, true + else + say " #{diff.old_element.chomp}", nil, true + end + end + + # Check if Diff::LCS is loaded. If it is, use it to create pretty output + # for diff. + # + def diff_lcs_loaded? #:nodoc: + return true if defined?(Diff::LCS) + return @diff_lcs_loaded unless @diff_lcs_loaded.nil? + + @diff_lcs_loaded = begin + require "diff/lcs" + true + rescue LoadError + false + end + end + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/shell/html.rb b/lib/bundler/vendor/thor/lib/thor/shell/html.rb new file mode 100644 index 0000000000..e1ea0de599 --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/shell/html.rb @@ -0,0 +1,126 @@ +require "bundler/vendor/thor/lib/thor/shell/basic" + +class Bundler::Thor + module Shell + # Inherit from Bundler::Thor::Shell::Basic and add set_color behavior. Check + # Bundler::Thor::Shell::Basic to see all available methods. + # + class HTML < Basic + # The start of an HTML bold sequence. + BOLD = "font-weight: bold" + + # Set the terminal's foreground HTML color to black. + BLACK = "color: black" + # Set the terminal's foreground HTML color to red. + RED = "color: red" + # Set the terminal's foreground HTML color to green. + GREEN = "color: green" + # Set the terminal's foreground HTML color to yellow. + YELLOW = "color: yellow" + # Set the terminal's foreground HTML color to blue. + BLUE = "color: blue" + # Set the terminal's foreground HTML color to magenta. + MAGENTA = "color: magenta" + # Set the terminal's foreground HTML color to cyan. + CYAN = "color: cyan" + # Set the terminal's foreground HTML color to white. + WHITE = "color: white" + + # Set the terminal's background HTML color to black. + ON_BLACK = "background-color: black" + # Set the terminal's background HTML color to red. + ON_RED = "background-color: red" + # Set the terminal's background HTML color to green. + ON_GREEN = "background-color: green" + # Set the terminal's background HTML color to yellow. + ON_YELLOW = "background-color: yellow" + # Set the terminal's background HTML color to blue. + ON_BLUE = "background-color: blue" + # Set the terminal's background HTML color to magenta. + ON_MAGENTA = "background-color: magenta" + # Set the terminal's background HTML color to cyan. + ON_CYAN = "background-color: cyan" + # Set the terminal's background HTML color to white. + ON_WHITE = "background-color: white" + + # Set color by using a string or one of the defined constants. If a third + # option is set to true, it also adds bold to the string. This is based + # on Highline implementation and it automatically appends CLEAR to the end + # of the returned String. + # + def set_color(string, *colors) + if colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) } + html_colors = colors.map { |color| lookup_color(color) } + "#{string}" + else + color, bold = colors + html_color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol) + styles = [html_color] + styles << BOLD if bold + "#{string}" + end + end + + # Ask something to the user and receives a response. + # + # ==== Example + # ask("What is your name?") + # + # TODO: Implement #ask for Bundler::Thor::Shell::HTML + def ask(statement, color = nil) + fail NotImplementedError, "Implement #ask for Bundler::Thor::Shell::HTML" + end + + protected + + def can_display_colors? + true + end + + # Overwrite show_diff to show diff with colors if Diff::LCS is + # available. + # + def show_diff(destination, content) #:nodoc: + if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil? + actual = File.binread(destination).to_s.split("\n") + content = content.to_s.split("\n") + + Diff::LCS.sdiff(actual, content).each do |diff| + output_diff_line(diff) + end + else + super + end + end + + def output_diff_line(diff) #:nodoc: + case diff.action + when "-" + say "- #{diff.old_element.chomp}", :red, true + when "+" + say "+ #{diff.new_element.chomp}", :green, true + when "!" + say "- #{diff.old_element.chomp}", :red, true + say "+ #{diff.new_element.chomp}", :green, true + else + say " #{diff.old_element.chomp}", nil, true + end + end + + # Check if Diff::LCS is loaded. If it is, use it to create pretty output + # for diff. + # + def diff_lcs_loaded? #:nodoc: + return true if defined?(Diff::LCS) + return @diff_lcs_loaded unless @diff_lcs_loaded.nil? + + @diff_lcs_loaded = begin + require "diff/lcs" + true + rescue LoadError + false + end + end + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/util.rb b/lib/bundler/vendor/thor/lib/thor/util.rb new file mode 100644 index 0000000000..f4e98fc19f --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/util.rb @@ -0,0 +1,267 @@ +require "rbconfig" + +class Bundler::Thor + module Sandbox #:nodoc: + end + + # This module holds several utilities: + # + # 1) Methods to convert thor namespaces to constants and vice-versa. + # + # Bundler::Thor::Util.namespace_from_thor_class(Foo::Bar::Baz) #=> "foo:bar:baz" + # + # 2) Loading thor files and sandboxing: + # + # Bundler::Thor::Util.load_thorfile("~/.thor/foo") + # + module Util + class << self + # Receives a namespace and search for it in the Bundler::Thor::Base subclasses. + # + # ==== Parameters + # namespace:: The namespace to search for. + # + def find_by_namespace(namespace) + namespace = "default#{namespace}" if namespace.empty? || namespace =~ /^:/ + Bundler::Thor::Base.subclasses.detect { |klass| klass.namespace == namespace } + end + + # Receives a constant and converts it to a Bundler::Thor namespace. Since Bundler::Thor + # commands can be added to a sandbox, this method is also responsable for + # removing the sandbox namespace. + # + # This method should not be used in general because it's used to deal with + # older versions of Bundler::Thor. On current versions, if you need to get the + # namespace from a class, just call namespace on it. + # + # ==== Parameters + # constant:: The constant to be converted to the thor path. + # + # ==== Returns + # String:: If we receive Foo::Bar::Baz it returns "foo:bar:baz" + # + def namespace_from_thor_class(constant) + constant = constant.to_s.gsub(/^Bundler::Thor::Sandbox::/, "") + constant = snake_case(constant).squeeze(":") + constant + end + + # Given the contents, evaluate it inside the sandbox and returns the + # namespaces defined in the sandbox. + # + # ==== Parameters + # contents + # + # ==== Returns + # Array[Object] + # + def namespaces_in_content(contents, file = __FILE__) + old_constants = Bundler::Thor::Base.subclasses.dup + Bundler::Thor::Base.subclasses.clear + + load_thorfile(file, contents) + + new_constants = Bundler::Thor::Base.subclasses.dup + Bundler::Thor::Base.subclasses.replace(old_constants) + + new_constants.map! { |c| c.namespace } + new_constants.compact! + new_constants + end + + # Returns the thor classes declared inside the given class. + # + def thor_classes_in(klass) + stringfied_constants = klass.constants.map { |c| c.to_s } + Bundler::Thor::Base.subclasses.select do |subclass| + next unless subclass.name + stringfied_constants.include?(subclass.name.gsub("#{klass.name}::", "")) + end + end + + # Receives a string and convert it to snake case. SnakeCase returns snake_case. + # + # ==== Parameters + # String + # + # ==== Returns + # String + # + def snake_case(str) + return str.downcase if str =~ /^[A-Z_]+$/ + str.gsub(/\B[A-Z]/, '_\&').squeeze("_") =~ /_*(.*)/ + $+.downcase + end + + # Receives a string and convert it to camel case. camel_case returns CamelCase. + # + # ==== Parameters + # String + # + # ==== Returns + # String + # + def camel_case(str) + return str if str !~ /_/ && str =~ /[A-Z]+.*/ + str.split("_").map { |i| i.capitalize }.join + end + + # Receives a namespace and tries to retrieve a Bundler::Thor or Bundler::Thor::Group class + # from it. It first searches for a class using the all the given namespace, + # if it's not found, removes the highest entry and searches for the class + # again. If found, returns the highest entry as the class name. + # + # ==== Examples + # + # class Foo::Bar < Bundler::Thor + # def baz + # end + # end + # + # class Baz::Foo < Bundler::Thor::Group + # end + # + # Bundler::Thor::Util.namespace_to_thor_class("foo:bar") #=> Foo::Bar, nil # will invoke default command + # Bundler::Thor::Util.namespace_to_thor_class("baz:foo") #=> Baz::Foo, nil + # Bundler::Thor::Util.namespace_to_thor_class("foo:bar:baz") #=> Foo::Bar, "baz" + # + # ==== Parameters + # namespace + # + def find_class_and_command_by_namespace(namespace, fallback = true) + if namespace.include?(":") # look for a namespaced command + pieces = namespace.split(":") + command = pieces.pop + klass = Bundler::Thor::Util.find_by_namespace(pieces.join(":")) + end + unless klass # look for a Bundler::Thor::Group with the right name + klass, command = Bundler::Thor::Util.find_by_namespace(namespace), nil + end + if !klass && fallback # try a command in the default namespace + command = namespace + klass = Bundler::Thor::Util.find_by_namespace("") + end + [klass, command] + end + alias_method :find_class_and_task_by_namespace, :find_class_and_command_by_namespace + + # Receives a path and load the thor file in the path. The file is evaluated + # inside the sandbox to avoid namespacing conflicts. + # + def load_thorfile(path, content = nil, debug = false) + content ||= File.binread(path) + + begin + Bundler::Thor::Sandbox.class_eval(content, path) + rescue StandardError => e + $stderr.puts("WARNING: unable to load thorfile #{path.inspect}: #{e.message}") + if debug + $stderr.puts(*e.backtrace) + else + $stderr.puts(e.backtrace.first) + end + end + end + + def user_home # rubocop:disable MethodLength + @@user_home ||= if ENV["HOME"] + ENV["HOME"] + elsif ENV["USERPROFILE"] + ENV["USERPROFILE"] + elsif ENV["HOMEDRIVE"] && ENV["HOMEPATH"] + File.join(ENV["HOMEDRIVE"], ENV["HOMEPATH"]) + elsif ENV["APPDATA"] + ENV["APPDATA"] + else + begin + File.expand_path("~") + rescue + if File::ALT_SEPARATOR + "C:/" + else + "/" + end + end + end + end + + # Returns the root where thor files are located, depending on the OS. + # + def thor_root + File.join(user_home, ".thor").gsub(/\\/, "/") + end + + # Returns the files in the thor root. On Windows thor_root will be something + # like this: + # + # C:\Documents and Settings\james\.thor + # + # If we don't #gsub the \ character, Dir.glob will fail. + # + def thor_root_glob + files = Dir["#{escape_globs(thor_root)}/*"] + + files.map! do |file| + File.directory?(file) ? File.join(file, "main.thor") : file + end + end + + # Where to look for Bundler::Thor files. + # + def globs_for(path) + path = escape_globs(path) + ["#{path}/Bundler::Thorfile", "#{path}/*.thor", "#{path}/tasks/*.thor", "#{path}/lib/tasks/*.thor"] + end + + # Return the path to the ruby interpreter taking into account multiple + # installations and windows extensions. + # + def ruby_command # rubocop:disable MethodLength + @ruby_command ||= begin + ruby_name = RbConfig::CONFIG["ruby_install_name"] + ruby = File.join(RbConfig::CONFIG["bindir"], ruby_name) + ruby << RbConfig::CONFIG["EXEEXT"] + + # avoid using different name than ruby (on platforms supporting links) + if ruby_name != "ruby" && File.respond_to?(:readlink) + begin + alternate_ruby = File.join(RbConfig::CONFIG["bindir"], "ruby") + alternate_ruby << RbConfig::CONFIG["EXEEXT"] + + # ruby is a symlink + if File.symlink? alternate_ruby + linked_ruby = File.readlink alternate_ruby + + # symlink points to 'ruby_install_name' + ruby = alternate_ruby if linked_ruby == ruby_name || linked_ruby == ruby + end + rescue NotImplementedError # rubocop:disable HandleExceptions + # just ignore on windows + end + end + + # escape string in case path to ruby executable contain spaces. + ruby.sub!(/.*\s.*/m, '"\&"') + ruby + end + end + + # Returns a string that has had any glob characters escaped. + # The glob characters are `* ? { } [ ]`. + # + # ==== Examples + # + # Bundler::Thor::Util.escape_globs('[apps]') # => '\[apps\]' + # + # ==== Parameters + # String + # + # ==== Returns + # String + # + def escape_globs(path) + path.to_s.gsub(/[*?{}\[\]]/, '\\\\\\&') + end + end + end +end diff --git a/lib/bundler/vendor/thor/lib/thor/version.rb b/lib/bundler/vendor/thor/lib/thor/version.rb new file mode 100644 index 0000000000..74b020a5ab --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/version.rb @@ -0,0 +1,3 @@ +class Bundler::Thor + VERSION = "0.19.1" +end diff --git a/lib/bundler/vendored_molinillo.rb b/lib/bundler/vendored_molinillo.rb index ce9ef0a3a1..4081f3fa92 100644 --- a/lib/bundler/vendored_molinillo.rb +++ b/lib/bundler/vendored_molinillo.rb @@ -1,5 +1,2 @@ -vendor = File.expand_path('../vendor/Molinillo-0.2.1/lib', __FILE__) -loaded = $:.include?(vendor) -$:.unshift(vendor) unless loaded -require 'molinillo' -$:.delete(vendor) unless loaded +module Bundler; end +require 'bundler/vendor/molinillo/lib/molinillo' diff --git a/lib/bundler/vendored_thor.rb b/lib/bundler/vendored_thor.rb index 2426f0c406..1931b5f278 100644 --- a/lib/bundler/vendored_thor.rb +++ b/lib/bundler/vendored_thor.rb @@ -1,5 +1,3 @@ -vendor = File.expand_path('../vendor/thor-0.19.1/lib', __FILE__) -loaded = $:.include?(vendor) -$:.unshift(vendor) unless loaded -require 'thor' -require 'thor/actions' +module Bundler; end +require 'bundler/vendor/thor/lib/thor' +require 'bundler/vendor/thor/lib/thor/actions' -- cgit v1.2.1