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).
68 # the stream is a Hash, check if the previous element is not a Symbol
69 if Symbol === args[-2]
70 ds = DataStream.new(args.pop)
72 ds = DataStream.new(*args.slice!(-2, 2))
75 # the stream is just whatever else
76 ds = DataStream.new(args.pop)
79 return ds if names.empty?
80 # check if filters exist
81 missing = names - @filters.keys
82 raise "Missing filters: #{missing.join(', ')}" unless missing.empty?
83 fs = @filters.values_at(*names)
84 fs.inject(ds) { |mid, f| mid = f.call(mid) }
87 # This method returns the global filter name for filter _name_
89 def global_filter_name(name, group=nil)
90 (group ? "#{group}.#{name}" : name.to_s).intern
93 # This method checks if the bot has a filter named _name_ (in group
95 def has_filter?(name, group=nil)
96 @filters.key?(global_filter_name(name, group))
99 # This method is used to register a new filter
100 def register_filter(name, group=nil, &block)
101 raise "No block provided" unless block_given?
103 tlkey = global_filter_name(name, group)
105 if has_filter?(tlkey)
106 debug "Overwriting filter #{tlkey}"
108 @filters[tlkey] = DataFilter.new &block
112 @filter_group[gkey] ||= {}
113 if @filter_group[gkey].key?(key)
114 debug "Overwriting filter #{key} in group #{gkey}"
116 @filter_group[gkey][key] = @filters[tlkey]
120 # This method is used to retrieve the filter names (in a given group)
121 def filter_names(group=nil)
124 return [] unless defined? @filter_group and @filter_group.key?(gkey)
125 return @filter_group[gkey].keys
127 return [] unless defined? @filters
132 # This method clears the filter list and installs the identity filter
140 register_filter(:identity) { |stream| stream }