4 # :title: Stream filters
6 # Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
7 # Copyright:: (C) 2008 Giuseppe Bilotta
10 # This file collects methods to handle 'stream filters', a generic mechanism
11 # to transform text+attributes into other text+attributes
16 # The DataStream class. A DataStream is just a Hash. The :text key has
17 # a special meaning because it's the value that will be used when
18 # converting to String
19 class DataStream < Hash
21 # call-seq: new(text, hash)
23 # Create a new DataStream with text _text_ and attributes held by _hash_.
24 # Either parameter can be missing; if _text_ is missing, the text can be
25 # be defined in the _hash_ with a :text key.
28 self.replace(args.pop) if Hash === args.last
29 self[:text] = args.first if args.length > 0
32 # Returns the :text key
38 # The DataFilter class. A DataFilter is a wrapper around a block
39 # that can be run on a DataStream to process it. The block is supposed to
40 # return another DataStream object
42 def initialize(&block)
43 raise "No block provided" unless block_given?
55 # filter(filter1, filter2, ..., filterN, stream) -> stream
56 # filter(filter1, filter2, ..., filterN, text, hash) -> stream
57 # filter(filter1, filter2, ..., filterN, hash) -> stream
59 # This method processes the DataStream _stream_ with the filters <i>filter1</i>,
60 # <i>filter2</i>, ..., _filterN_, in sequence (the output of each filter is used
61 # as input for the next one.
62 # _stream_ can be provided either as a DataStream or as a String and a Hash
63 # (see DataStream.new).
69 # the stream is an actual DataStream
72 # the stream is just plain text
73 ds = DataStream.new(args.pop)
75 # the stream is a Hash, check if the previous element is a String
76 if String === args[-2]
77 ds = DataStream.new(*args.slice!(-2, 2))
79 ds = DataStream.new(args.pop)
82 raise "Unknown DataStream class #{args.last.class}"
85 return ds if names.empty?
86 # check if filters exist
87 missing = names - @filters.keys
88 raise "Missing filters: #{missing.join(', ')}" unless missing.empty?
89 fs = @filters.values_at(*names)
90 fs.inject(ds) { |mid, f| mid = f.call(mid) }
93 # This method is used to register a new filter
94 def register_filter(name, &block)
95 raise "No block provided" unless block_given?
97 @filters[name.to_sym] = DataFilter.new &block
100 # This method clears the filter list and installs the identity filter
104 register_filter(:identity) { |stream| stream }