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 returns the global filter name for filter _name_
95 def global_filter_name(name, group=nil)
96 (group ? "#{group}.#{name}" : name.to_s).intern
99 # This method checks if the bot has a filter named _name_ (in group
101 def has_filter?(name, group=nil)
102 @filters.key?(global_filter_name(name, group))
105 # This method is used to register a new filter
106 def register_filter(name, group=nil, &block)
107 raise "No block provided" unless block_given?
109 tlkey = global_filter_name(name, group)
111 if has_filter?(tlkey)
112 debug "Overwriting filter #{tlkey}"
114 @filters[tlkey] = DataFilter.new &block
118 @filter_group[gkey] ||= {}
119 if @filter_group[gkey].key?(key)
120 debug "Overwriting filter #{key} in group #{gkey}"
122 @filter_group[gkey][key] = @filters[tlkey]
126 # This method is used to retrieve the filter names (in a given group)
127 def filter_names(group=nil)
130 return [] unless defined? @filter_group and @filter_group.key?(gkey)
131 return @filter_group[gkey].keys
133 return [] unless defined? @filters
138 # This method clears the filter list and installs the identity filter
146 register_filter(:identity) { |stream| stream }