]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - lib/rbot/core/utils/filters.rb
Licensing uniformity: dual-license rbot core under MIT+acknowledgement and GPLv2
[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 #
8 # This file collects methods to handle 'stream filters', a generic mechanism
9 # to transform text+attributes into other text+attributes
10
11 module ::Irc
12   class Bot
13
14     # The DataStream class. A DataStream is just a Hash. The :text key has
15     # a special meaning because it's the value that will be used when
16     # converting to String
17     class DataStream < Hash
18
19       # call-seq: new(text, hash)
20       #
21       # Create a new DataStream with text _text_ and attributes held by _hash_.
22       # Either parameter can be missing; if _text_ is missing, the text can be
23       # be defined in the _hash_ with a :text key.
24       #
25       def initialize(*args)
26         self.replace(args.pop) if Hash === args.last
27         self[:text] = args.first if args.length > 0
28       end
29
30       # Returns the :text key
31       def to_s
32         return self[:text]
33       end
34     end
35
36     # The DataFilter class. A DataFilter is a wrapper around a block
37     # that can be run on a DataStream to process it. The block is supposed to
38     # return another DataStream object
39     class DataFilter
40       def initialize(&block)
41         raise "No block provided" unless block_given?
42         @block = block
43       end
44
45       def call(stream)
46         @block.call(stream)
47       end
48       alias :run :call
49       alias :filter :call
50     end
51
52     # call-seq:
53     #   filter(filter1, filter2, ..., filterN, stream) -> stream
54     #   filter(filter1, filter2, ..., filterN, text, hash) -> stream
55     #   filter(filter1, filter2, ..., filterN, hash) -> stream
56     #
57     # This method processes the DataStream _stream_ with the filters <i>filter1</i>,
58     # <i>filter2</i>, ..., _filterN_, in sequence (the output of each filter is used
59     # as input for the next one.
60     # _stream_ can be provided either as a DataStream or as a String and a Hash
61     # (see DataStream.new).
62     #
63     def filter(*args)
64       @filters ||= {}
65       if Hash === args.last
66         # the stream is a Hash, check if the previous element is not a Symbol
67         if Symbol === args[-2]
68           ds = DataStream.new(args.pop)
69         else
70           ds = DataStream.new(*args.slice!(-2, 2))
71         end
72       else
73         # the stream is just whatever else
74         ds = DataStream.new(args.pop)
75       end
76       names = args.dup
77       return ds if names.empty?
78       # check if filters exist
79       missing = names - @filters.keys
80       raise "Missing filters: #{missing.join(', ')}" unless missing.empty?
81       fs = @filters.values_at(*names)
82       fs.inject(ds) { |mid, f| mid = f.call(mid) }
83     end
84
85     # This method returns the global filter name for filter _name_
86     # in group _group_
87     def global_filter_name(name, group=nil)
88       (group ? "#{group}.#{name}" : name.to_s).intern
89     end
90
91     # This method checks if the bot has a filter named _name_ (in group
92     # _group_)
93     def has_filter?(name, group=nil)
94       @filters.key?(global_filter_name(name, group))
95     end
96
97     # This method checks if the bot has a filter group named _name_
98     def has_filter_group?(name)
99       @filter_group.key?(name)
100     end
101
102     # This method is used to register a new filter
103     def register_filter(name, group=nil, &block)
104       raise "No block provided" unless block_given?
105       @filters ||= {}
106       tlkey = global_filter_name(name, group)
107       key = name.to_sym
108       if has_filter?(tlkey)
109         debug "Overwriting filter #{tlkey}"
110       end
111       @filters[tlkey] = DataFilter.new(&block)
112       if group
113         gkey = group.to_sym
114         @filter_group ||= {}
115         @filter_group[gkey] ||= {}
116         if @filter_group[gkey].key?(key)
117           debug "Overwriting filter #{key} in group #{gkey}"
118         end
119         @filter_group[gkey][key] = @filters[tlkey]
120       end
121     end
122
123     # This method is used to retrieve the filter names (in a given group)
124     def filter_names(group=nil)
125       if group
126         gkey = group.to_sym
127         return [] unless defined? @filter_group and @filter_group.key?(gkey)
128         return @filter_group[gkey].keys
129       else
130         return [] unless defined? @filters
131         return @filters.keys
132       end
133     end
134
135     # This method is used to retrieve the filter group names
136     def filter_groups
137       return [] unless defined? @filter_group
138       return @filter_group.keys
139     end
140
141     # This method clears the filter list and installs the identity filter
142     def clear_filters
143       @filters ||= {}
144       @filters.clear
145
146       @filter_group ||= {}
147       @filter_group.clear
148
149       register_filter(:identity) { |stream| stream }
150     end
151   end
152 end
153
154