You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@buildr.apache.org by vb...@apache.org on 2008/09/23 21:12:23 UTC
svn commit: r698288 - /incubator/buildr/trunk/lib/buildr/core/filter.rb
Author: vborja
Date: Tue Sep 23 12:12:23 2008
New Revision: 698288
URL: http://svn.apache.org/viewvc?rev=698288&view=rev
Log:
BUILDR-135. Abstracted content mapping logic from Filter into Filter::Mapper
Modified:
incubator/buildr/trunk/lib/buildr/core/filter.rb
Modified: incubator/buildr/trunk/lib/buildr/core/filter.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/lib/buildr/core/filter.rb?rev=698288&r1=698287&r2=698288&view=diff
==============================================================================
--- incubator/buildr/trunk/lib/buildr/core/filter.rb (original)
+++ incubator/buildr/trunk/lib/buildr/core/filter.rb Tue Sep 23 12:12:23 2008
@@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations under
# the License.
-
module Buildr
# A filter knows how to copy files from one directory to another, applying mappings to the
@@ -57,6 +56,7 @@
@include = []
@exclude = []
@sources = FileList[]
+ @mapper = Mapper.new
self
end
@@ -110,10 +110,14 @@
end
# The mapping. See #using.
- attr_accessor :mapping
+ def mapping #:nodoc:
+ @mapper.config
+ end
# The mapper to use. See #using.
- attr_accessor :mapper
+ def mapper #:nodoc:
+ @mapper.mapper_type
+ end
# :call-seq:
# using(mapping) => self
@@ -127,6 +131,7 @@
# * :ant -- Map <code>@key@</code>.
# * :maven -- Map <code>${key}</code> (default).
# * :ruby -- Map <code>#{key}</code>.
+ # * :erb -- Map <code><%= key %></code>.
# * Regexp -- Maps the matched data (e.g. <code>/=(.*?)=/</code>
#
# For example:
@@ -138,20 +143,10 @@
# to return the mapped content.
#
# Without any mapping, all files are copied as is.
+ #
+ # To register new mapping type see the Mapper class.
def using(*args, &block)
- case args.first
- when Hash # Maven hash mapping
- using :maven, *args
- when Symbol # Mapping from a method
- raise ArgumentError, 'Expected mapper type followed by mapping hash' unless args.size == 2 && Hash === args[1]
- @mapper, @mapping = *args
- when Regexp # Mapping using a regular expression
- raise ArgumentError, 'Expected regular expression followed by mapping hash' unless args.size == 2 && Hash === args[1]
- @mapper, @mapping = *args
- else
- raise ArgumentError, 'Expected proc, method or a block' if args.size > 1 || (args.first && block)
- @mapping = args.first || block
- end
+ @mapper.using(*args, &block)
self
end
@@ -185,24 +180,12 @@
mkpath dest
else
mkpath File.dirname(dest)
- case mapping
- when Proc, Method # Call on input, accept output.
- mapped = mapping.call(path, File.open(source, 'rb') { |file| file.read })
+ if @mapper.mapper_type
+ mapped = @mapper.result(File.open(source, 'rb') { |file| file.read }, path)
File.open(dest, 'wb') { |file| file.write mapped }
- when Hash # Map ${key} to value
- content = File.open(source, 'rb') { |file| file.read }
- if Symbol === @mapper
- mapped = send("#{@mapper}_mapper", content) { |key| mapping[key] }
- else
- mapped = regexp_mapper(content) { |key| mapping[key] }
- end
- #gsub(/\$\{[^}]*\}/) { |str| mapping[str[2..-2]] || str }
- File.open(dest, 'wb') { |file| file.write mapped }
- when nil # No mapping.
+ else # no mapping
cp source, dest
File.chmod(0664, dest)
- else
- fail "Filter can be a hash (key=>value), or a proc/method; I don't understand #{mapping}"
end
end
end
@@ -216,23 +199,145 @@
@target.to_s
end
- private
+ # This class implements content replacement logic for Filter.
+ #
+ # To register a new template engine @:foo@, extend this class with a method like:
+ #
+ # def foo_result(content, path = nil)
+ # # if this method yields a key, the value comes from the mapping hash
+ # content.gsub(/world/) { |str| yield :bar }
+ # end
+ #
+ # Then you can use :foo mapping type on a Filter
+ #
+ # filter.using :foo, :bar => :baz
+ #
+ # Or all by your own, simply
+ #
+ # Mapper.new(:foo, :bar => :baz).result("Hello world") # => "Hello baz"
+ #
+ # You can handle configuration arguments by providing a @*_config@ method like:
+ #
+ # # The return value of this method is available with the :config accessor.
+ # def moo_config(*args, &block)
+ # raise ArgumentError, "Expected moo block" unless block_given?
+ # { :moos => args, :callback => block }
+ # end
+ #
+ # def moo_result(content, path = nil)
+ # content.gsub(/moo+/i) do |str|
+ # moos = yield :moos # same than config[:moos]
+ # moo = moos[str.size - 3] || str
+ # config[:callback].call(moo)
+ # end
+ # end
+ #
+ # Usage for the @:moo@ mapper would be something like:
+ #
+ # mapper = Mapper.new(:moo, 'ooone', 'twoo') do |str|
+ # i = nil; str.capitalize.gsub(/\w/) { |s| s.send( (i = !i) ? 'upcase' : 'downcase' ) }
+ # end
+ # mapper.result('Moo cow, mooo cows singing mooooo') # => 'OoOnE cow, TwOo cows singing MoOoOo'
+ class Mapper
- def maven_mapper(content)
- content.gsub(/\$\{.*?\}/) { |str| yield(str[2..-2]) || str }
- end
+ attr_reader :mapper_type, :config
- def ant_mapper(content)
- content.gsub(/@.*?@/) { |str| yield(str[1..-2]) || str }
- end
+ def initialize(*args, &block) #:nodoc:
+ using(*args, &block)
+ end
+
+ def using(*args, &block)
+ case args.first
+ when Hash # Maven hash mapping
+ using :maven, *args
+ when Binding # Erb binding
+ using :erb, *args
+ when Symbol # Mapping from a method
+ raise ArgumentError, "Unknown mapping type: #{args.first}" unless respond_to?("#{args.first}_result", true)
+ configure(*args, &block)
+ when Regexp # Mapping using a regular expression
+ raise ArgumentError, 'Expected regular expression followed by mapping hash' unless args.size == 2 && Hash === args[1]
+ @mapper_type, @config = *args
+ else
+ unless args.empty? && block.nil?
+ raise ArgumentError, 'Expected proc, method or a block' if args.size > 1 || (args.first && block)
+ @mapper_type = :callback
+ config = args.first || block
+ raise ArgumentError, 'Expected proc, method or callable' unless config.respond_to?(:call)
+ @config = config
+ end
+ end
+ self
+ end
- def ruby_mapper(content)
- content.gsub(/#\{.*?\}/) { |str| yield(str[2..-2]) || str }
- end
+ def result(content, path = nil)
+ type = Regexp === mapper_type ? :regexp : mapper_type
+ raise ArgumentError, "Invalid mapper type: #{type.inspect}" unless respond_to?("#{type}_result", true)
+ self.__send__("#{type}_result", content, path) { |key| config[key] || config[key.to_s.to_sym] }
+ end
- def regexp_mapper(content)
- content.gsub(@mapper) { |str| yield(str.scan(@mapper).join) || str }
- end
+ private
+ def configure(mapper_type, *args, &block)
+ configurer = method("#{mapper_type}_config") rescue nil
+ if configurer
+ @config = configurer.call(*args, &block)
+ else
+ raise ArgumentError, "Missing hash argument after :#{mapper_type}" unless args.size == 1 && Hash === args[0]
+ @config = *args
+ end
+ @mapper_type = mapper_type
+ end
+
+ def maven_result(content, path = nil)
+ content.gsub(/\$\{.*?\}/) { |str| yield(str[2..-2]) || str }
+ end
+
+ def ant_result(content, path = nil)
+ content.gsub(/@.*?@/) { |str| yield(str[1..-2]) || str }
+ end
+
+ def ruby_result(content, path = nil)
+ content.gsub(/#\{.*?\}/) { |str| yield(str[2..-2]) || str }
+ end
+
+ def regexp_result(content, path = nil)
+ content.gsub(mapper_type) { |str| yield(str.scan(mapper_type).join) || str }
+ end
+
+ def callback_result(content, path = nil)
+ config.call(path, content)
+ end
+
+ def erb_result(content, path = nil)
+ case config
+ when Binding, Proc
+ bnd = config
+ when Method
+ bnd = config.to_proc
+ when Hash
+ bnd = OpenStruct.new
+ table = config.inject({}) { |h, e| h[e.first.to_sym] = e.last; h }
+ bnd.instance_variable_set(:@table, table)
+ bnd = bnd.instance_eval { binding }
+ else
+ bnd = config.instance_eval { binding }
+ end
+ require 'erb'
+ ERB.new(content).result(bnd)
+ end
+
+ def erb_config(*args, &block)
+ if block_given?
+ raise ArgumentError, "Expected block or single argument, but both given." unless args.empty?
+ block
+ elsif args.size > 1
+ raise ArgumentError, "Expected block or single argument."
+ else
+ args.first
+ end
+ end
+
+ end # class Mapper
end