]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - lib/rbot/core/utils/filters.rb
filters: don't force data types in @bot.filter()
[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       if Hash === args.last
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)
71         else
72           ds = DataStream.new(*args.slice!(-2, 2))
73         end
74       else
75         # the stream is just whatever else
76         ds = DataStream.new(args.pop)
77       end
78       names = args.dup
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) }
85     end
86
87     # This method returns the global filter name for filter _name_
88     # in group _group_
89     def global_filter_name(name, group=nil)
90       (group ? "#{group}.#{name}" : name.to_s).intern
91     end
92
93     # This method checks if the bot has a filter named _name_ (in group
94     # _group_)
95     def has_filter?(name, group=nil)
96       @filters.key?(global_filter_name(name, group))
97     end
98
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?
102       @filters ||= {}
103       tlkey = global_filter_name(name, group)
104       key = name.to_sym
105       if has_filter?(tlkey)
106         debug "Overwriting filter #{tlkey}"
107       end
108       @filters[tlkey] = DataFilter.new &block
109       if group
110         gkey = group.to_sym
111         @filter_group ||= {}
112         @filter_group[gkey] ||= {}
113         if @filter_group[gkey].key?(key)
114           debug "Overwriting filter #{key} in group #{gkey}"
115         end
116         @filter_group[gkey][key] = @filters[tlkey]
117       end
118     end
119
120     # This method is used to retrieve the filter names (in a given group)
121     def filter_names(group=nil)
122       if group
123         gkey = group.to_sym
124         return [] unless defined? @filter_group and @filter_group.key?(gkey)
125         return @filter_group[gkey].keys
126       else
127         return [] unless defined? @filters
128         return @filters.keys
129       end
130     end
131
132     # This method clears the filter list and installs the identity filter
133     def clear_filters
134       @filters ||= {}
135       @filters.clear
136
137       @filter_group ||= {}
138       @filter_group.clear
139
140       register_filter(:identity) { |stream| stream }
141     end
142   end
143 end
144
145