/
chained_filter.rb
116 lines (99 loc) · 3.01 KB
/
chained_filter.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
module Rake::Pipeline::Web::Filters
# Implement the FileWrapper API. Because filters are defined
# in terms of the FileWrapper API, we can implement an
# alternative that doesn't write to disk but still utilizes
# the same filter definitions.
class MemoryFileWrapper < Struct.new(:root, :path, :encoding, :body)
def with_encoding(new_encoding)
self.class.new(root, path, new_encoding, body)
end
def fullpath
File.join(root, path)
end
def create
self.body = ""
yield
end
alias read body
def write(contents)
self.body << contents
end
end
# The purpose of ChainedFilter is to enable filters to
# be applied to files based upon their file extensions.
#
# Filters are applied repeatedly to files for each
# extension.
#
# @example
#
# filter ChainedFilter, :types => {
# :erb => ErbFilter,
# :coffee => CoffeeFilter,
# :scss => ScssFilter
# }
#
# In this example, files with the extensions +erb+,
# +coffee+, and +scss+ will be processed using the
# specified filters. If a file has multiple extensions,
# all of the filters will be applied.
#
# For example, with the above filter specification,
# a file like +application.js.coffee.erb+ will first
# apply the +ErbFilter+, then the +CoffeeFilter+, and
# then output +application.js+.
#
# This filter is largely designed for use with the
# {ProjectHelpers#register register} helper, which
# will transparently add a ChainedFilter before each
# input block with the registered extensions.
class ChainedFilter < Rake::Pipeline::Filter
attr_reader :filters
# @param [Hash] options
# @option options [Hash] :types
# A hash of file extensions and their associated
# filters. See the class description for more
# information.
def initialize(options={}, &block)
@filters = options[:types]
keys = @filters.keys
pattern = keys.map { |key| "\\.#{key}" }.join("|")
@pattern = Regexp.new("(#{pattern})*$", "i")
block ||= proc do |input|
input.sub(@pattern, '')
end
super(&block)
end
# @private
#
# Implement +generate_output+
def generate_output(inputs, output)
inputs.each do |input|
output.write process_filters(input)
end
end
# @private
#
# Process an input file by applying the filter for each
# extension in the file.
def process_filters(input)
keys = input.path.match(@pattern)[0].scan(/(?<=\.)\w+/)
filters = keys.reverse_each.map do |key|
@filters[key.to_sym]
end
filters.each do |filter|
input = process_with_filter(input, filter)
end
input.read
end
# @private
#
# Process an individual file with a filter.
def process_with_filter(input, filter_class)
filter = filter_class.new
output = MemoryFileWrapper.new("/output", input.path, "UTF-8")
output.create { filter.generate_output([input], output) }
output
end
end
end