lib/core/common.rb in buildr-1.1.3 vs lib/core/common.rb in buildr-1.2.0
- old
+ new
@@ -1,12 +1,88 @@
require "tempfile"
require "pathname"
require "core/transports"
+require "open-uri"
+require "uri/open-sftp"
+
+class Hash
+
+ # :call-seq:
+ # only(keys*) => hash
+ #
+ # Returns a new hash with only the specified keys.
+ #
+ # For example:
+ # { :a=>1, :b=>2, :c=>3, :d=>4 }.only(:a, :c)
+ # => { :b=>2, :d=>4 }
+ def only(*keys)
+ self.inject({}) { |hash, pair| hash[pair[0]] = pair[1] if keys.include?(pair[0]) ; hash }
+ end
+
+
+ # :call-seq:
+ # except(keys*) => hash
+ #
+ # Returns a new hash without the specified keys.
+ #
+ # For example:
+ # { :a=>1, :b=>2, :c=>3, :d=>4 }.except(:a, :c)
+ # => { :a=>1, :c=>3 }
+ def except(*keys)
+ self.inject({}) { |hash, pair| hash[pair[0]] = pair[1] unless keys.include?(pair[0]) ; hash }
+ end
+
+end
+
+
module Buildr
+ # Collection of options for controlling Buildr. For example for running builds without running
+ # test cases, using a proxy server, JVM arguments, etc. You access this object by calling options,
+ # for example:
+ # options.proxy.http = "http://proxy.acme.com:8080"
+ # options.java_args = "-Xmx512M"
+ class Options
+
+ # :call-seq:
+ # proxy() => options
+ #
+ # Returns the proxy options. Currently supported options are:
+ # * :http -- HTTP proxy for use when downloading.
+ #
+ # For example:
+ # options.proxy.http = "http://proxy.acme.com:8080"
+ # You can also set it using the environment variable HTTP_PROXY.
+ def proxy()
+ @proxy ||= Struct.new(:http).new(ENV['HTTP_PROXY'] || ENV['http_proxy'])
+ end
+
+ end
+
+ class << self
+
+ # :call-seq:
+ # options() => Options
+ #
+ # Returns the Buildr options. See Options.
+ def options()
+ @options ||= Options.new
+ end
+
+ end
+
# :call-seq:
+ # options() => Options
+ #
+ # Returns the Buildr options. See Options.
+ def options()
+ Buildr.options
+ end
+
+
+ # :call-seq:
# struct(hash) => Struct
#
# Convenience method for creating an anonymous Struct.
#
# For example:
@@ -74,31 +150,34 @@
# checksums on the server it will verify the download before saving it.
#
# For example:
# download "image.jpg"=>"http://example.com/theme/image.jpg"
def download(args)
- if String === args || URI === args
+ args = URI.parse(args) if String === args
+ if URI === args
# Given only a download URL, download into a temporary file.
# You can infer the file from task name.
- temp = Tempfile.new(File.basename(args.to_s))
- task = file_create(temp.path) do |task|
- Transports.download task.source, task.name
+ temp = Tempfile.open(File.basename(args.to_s))
+ file(temp.path).tap do |task|
+ # Since temporary file exists, force a download.
+ class << task ; def needed?() ; true ; end ; end
+ task.sources << args
+ task.enhance { args.download temp, :proxy=>Buildr.options.proxy }
end
- task.sources << args
else
# Download to a file created by the task.
fail unless args.keys.size == 1
- url = args.values.first
- task = file_create(args.keys.first) do |task|
- mkpath File.dirname(task.name), :verbose=>false
- Transports.download task.source, task.name
+ uri = URI.parse(args.values.first.to_s)
+ file_create(args.keys.first).tap do |task|
+ task.sources << uri
+ task.enhance { uri.download task.name, :proxy=>Buildr.options.proxy }
end
- task.sources << url
end
- task
+
end
+
# A filter knows how to copy files from one directory to another, applying mappings to the
# contents of these files.
#
# You can specify the mapping using a Hash, and it will map ${key} fields found in each source
# file into the appropriate value in the target file. For example:
@@ -186,76 +265,63 @@
# Without any mapping, all files are copied as is.
#
# For example:
# filter.using "version"=>"1.2"
# will replace all occurrences of "${version}" with "1.2".
- def using(mapping, &block)
+ def using(mapping = nil, &block)
self.mapping = mapping || block
self
end
+ # :call-seq:
+ # run() => boolean
+ #
# Runs the filter.
def run()
- if needed?
- unless copy_map.empty?
- verbose(Rake.application.options.trace || false) do
- mkpath target.to_s
- copy_map do |dest, src|
- mkpath File.dirname(dest) rescue nil
- case mapping
- when Proc, Method # Call on input, accept output.
- mapped = mapping.call(src, File.open(src, "rb") { |file| file.read })
- File.open(dest, "wb") { |file| file.write mapped }
- when Hash # Map ${key} to value
- mapped = File.open(src, "rb") { |file| file.read }.
- gsub(/\$\{.*\}/) { |str| mapping[str[2..-2]] || str }
- File.open(dest, "wb") { |file| file.write mapped }
- when nil # No mapping.
- cp src, dest
- else
- fail "Filter can be a hash (key=>value), or a proc/method; I don't understand #{mapping}"
- end
- end
- touch target.to_s
+ raise "No source directory specified, where am I going to find the files to filter?" if source.nil?
+ raise "Source directory #{source} doesn't exist" unless File.exist?(source.to_s)
+ raise "No target directory specified, where am I going to copy the files to?" if target.nil?
+
+ includes = @include.empty? ? ["*"] : @include
+ src_base = Pathname.new(source.to_s)
+ copy_map = Dir[File.join(source.to_s, "**/*")].reject { |file| File.directory?(file) }.
+ map { |src| Pathname.new(src).relative_path_from(src_base).to_s }.
+ select { |file| includes.any? { |pattern| File.fnmatch(pattern, file) } }.
+ reject { |file| @exclude.any? { |pattern| File.fnmatch(pattern, file) } }.
+ map { |file| [File.expand_path(file, target.to_s), File.expand_path(file, source.to_s)] }.
+ select { |dest, src| !File.exist?(dest) || File.stat(src).mtime > File.stat(dest).mtime }
+ return false if copy_map.empty?
+
+ verbose(Rake.application.options.trace || false) do
+ mkpath target.to_s
+ copy_map.each do |dest, src|
+ mkpath File.dirname(dest) rescue nil
+ case mapping
+ when Proc, Method # Call on input, accept output.
+ relative = Pathname.new(src).relative_path_from(src_base).to_s
+ mapped = mapping.call(relative, File.open(src, "rb") { |file| file.read })
+ File.open(dest, "wb") { |file| file.write mapped }
+ when Hash # Map ${key} to value
+ mapped = File.open(src, "rb") { |file| file.read }.
+ gsub(/\$\{[^}]*\}/) { |str| mapping[str[2..-2]] || str }
+ File.open(dest, "wb") { |file| file.write mapped }
+ when nil # No mapping.
+ cp src, dest
+ else
+ fail "Filter can be a hash (key=>value), or a proc/method; I don't understand #{mapping}"
end
end
+ touch target.to_s
end
+ true
end
# Returns the target directory.
def to_s()
@target.to_s
end
- private
-
- def needed?()
- return false if target.nil? || source.nil? || !File.exist?(source.to_s)
- return true unless File.exist?(target.to_s)
- !copy_map.empty?
- end
-
- # Return a copy map of all the files that need copying: the key is the file to copy to,
- # the value is the source file. If called with a block, yields with each dest/source pair.
- def copy_map(&block)
- unless @copy_map
- @include = ["*"] if @include.empty?
- base = Pathname.new(source.to_s)
- @copy_map = Dir[File.join(source.to_s, "**/*")].reject { |file| File.directory?(file) }.
- map { |src| Pathname.new(src).relative_path_from(base).to_s }.
- select { |file| @include.any? { |pattern| File.fnmatch(pattern, file) } }.
- reject { |file| @exclude.any? { |pattern| File.fnmatch(pattern, file) } }.
- map { |file| [File.expand_path(file, target.to_s), File.expand_path(file, source.to_s)] }.
- select { |dest, src| !File.exist?(dest) || File.stat(src).mtime > File.stat(dest).mtime }
- end
- if block_given?
- @copy_map.each(&block)
- else
- @copy_map
- end
- end
-
end
# :call-seq:
# filter(source) => Filter
#
@@ -271,7 +337,40 @@
# date/time:
# filter("src/files").into("target/classes").include("*.txt").using("build"=>Time.now).run
def filter(source)
Filter.new.from(source)
end
-
+
end
+
+
+# Add a touch of colors (red) to warnings.
+HighLine.use_color = PLATFORM !~ /win32/
+module Kernel #:nodoc:
+
+ def warn_with_color(message)
+ warn_without_color $terminal.color(message.to_s, :red)
+ end
+ alias_method_chain :warn, :color
+
+ # :call-seq:
+ # warn_deprecated(message)
+ #
+ # Use with deprecated methods and classes. This method automatically adds the file name and line number,
+ # and the text "Deprecated" before the message, and eliminated duplicate warnings. It only warns when
+ # running in verbose mode.
+ #
+ # For example:
+ # warn_deprecated "Please use new_foo instead of foo."
+ def warn_deprecated(message) #:nodoc:
+ return unless verbose
+ "#{caller[1]}: Deprecated: #{message}".tap do |message|
+ @deprecated ||= {}
+ unless @deprecated[message]
+ @deprecated[message] = true
+ warn message
+ end
+ end
+ end
+
+end
+