]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - lib/rbot/core/utils/filters.rb
filters: @bot.filter can now be called with a list of filters and with any form of...
[user/henk/code/ruby/rbot.git] / lib / rbot / core / utils / filters.rb
1 #-- vim:sw=2:et
2 #++
3 #
4 # :title: Stream filters
5 #
6 # Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
7 # Copyright:: (C) 2008 Giuseppe Bilotta
8 # License:: GPL v2
9 #
10 # This file collects methods to handle 'stream filters', a generic mechanism
11 # to transform text+attributes into other text+attributes
12
13 module ::Irc
14   class Bot
15
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
20
21       # call-seq: new(text, hash)
22       #
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.
26       #
27       def initialize(*args)
28         self.replace(args.pop) if Hash === args.last
29         self[:text] = args.first if args.length > 0
30       end
31
32       # Returns the :text key
33       def to_s
34         return self[:text]
35       end
36     end
37
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
41     class DataFilter
42       def initialize(&block)
43         raise "No block provided" unless block_given?
44         @block = block
45       end
46
47       def call(stream)
48         @block.call(stream)
49       end
50       alias :run :call
51       alias :filter :call
52     end
53
54     # call-seq:
55     #   filter(filter1, filter2, ..., filterN, stream) -> stream
56     #   filter(filter1, filter2, ..., filterN, text, hash) -> stream
57     #   filter(filter1, filter2, ..., filterN, hash) -> stream
58     #
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).
64     #
65     def filter(*args)
66       @filters ||= {}
67       case args.last
68       when DataStream
69         # the stream is an actual DataStream
70         ds = args.pop
71       when String
72         # the stream is just plain text
73         ds = DataStream.new(args.pop)
74       when Hash
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))
78         else
79           ds = DataStream.new(args.pop)
80         end
81       else
82         raise "Unknown DataStream class #{args.last.class}"
83       end
84       names = args.dup
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) }
91     end
92
93     # This method is used to register a new filter
94     def register_filter(name, &block)
95       raise "No block provided" unless block_given?
96       @filters ||= {}
97       @filters[name.to_sym] = DataFilter.new &block
98     end
99
100     # This method clears the filter list and installs the identity filter
101     def clear_filters
102       @filters ||= {}
103       @filters.clear
104       register_filter(:identity) { |stream| stream }
105     end
106   end
107 end
108
109