1#
2#   notifier.rb - output methods used by irb
3#   	$Release Version: 0.9.6$
4#   	$Revision: 38515 $
5#   	by Keiju ISHITSUKA(keiju@ruby-lang.org)
6#
7# --
8#
9#
10#
11
12require "e2mmap"
13require "irb/output-method"
14
15module IRB
16  # An output formatter used internally by the lexer.
17  module Notifier
18    extend Exception2MessageMapper
19    def_exception :ErrUndefinedNotifier,
20      "undefined notifier level: %d is specified"
21    def_exception :ErrUnrecognizedLevel,
22      "unrecognized notifier level: %s is specified"
23
24    # Define a new Notifier output source, returning a new CompositeNotifier
25    # with the given +prefix+ and +output_method+.
26    #
27    # The optional +prefix+ will be appended to all objects being inspected
28    # during output, using the given +output_method+ as the output source. If
29    # no +output_method+ is given, StdioOuputMethod will be used, and all
30    # expressions will be sent directly to STDOUT without any additional
31    # formatting.
32    def def_notifier(prefix = "", output_method = StdioOutputMethod.new)
33      CompositeNotifier.new(prefix, output_method)
34    end
35    module_function :def_notifier
36
37    # An abstract class, or superclass, for CompositeNotifier and
38    # LeveledNotifier to inherit. It provides several wrapper methods for the
39    # OutputMethod object used by the Notifier.
40    class AbstractNotifier
41      # Creates a new Notifier object
42      def initialize(prefix, base_notifier)
43	@prefix = prefix
44	@base_notifier = base_notifier
45      end
46
47      # The +prefix+ for this Notifier, which is appended to all objects being
48      # inspected during output.
49      attr_reader :prefix
50
51      # A wrapper method used to determine whether notifications are enabled.
52      #
53      # Defaults to +true+.
54      def notify?
55	true
56      end
57
58      # See OutputMethod#print for more detail.
59      def print(*opts)
60	@base_notifier.print prefix, *opts if notify?
61      end
62
63      # See OutputMethod#printn for more detail.
64      def printn(*opts)
65	@base_notifier.printn prefix, *opts if notify?
66      end
67
68      # See OutputMethod#printf for more detail.
69      def printf(format, *opts)
70	@base_notifier.printf(prefix + format, *opts) if notify?
71      end
72
73      # See OutputMethod#puts for more detail.
74      def puts(*objs)
75	if notify?
76	  @base_notifier.puts(*objs.collect{|obj| prefix + obj.to_s})
77	end
78      end
79
80      # Same as #ppx, except it uses the #prefix given during object
81      # initialization.
82      # See OutputMethod#ppx for more detail.
83      def pp(*objs)
84	if notify?
85	  @base_notifier.ppx @prefix, *objs
86	end
87      end
88
89      # Same as #pp, except it concatenates the given +prefix+ with the #prefix
90      # given during object initialization.
91      #
92      # See OutputMethod#ppx for more detail.
93      def ppx(prefix, *objs)
94	if notify?
95	  @base_notifier.ppx @prefix+prefix, *objs
96	end
97      end
98
99      # Execute the given block if notifications are enabled.
100      def exec_if
101	yield(@base_notifier) if notify?
102      end
103    end
104
105    # A class that can be used to create a group of notifier objects with the
106    # intent of representing a leveled notification system for irb.
107    #
108    # This class will allow you to generate other notifiers, and assign them
109    # the appropriate level for output.
110    #
111    # The Notifier class provides a class-method Notifier.def_notifier to
112    # create a new composite notifier. Using the first composite notifier
113    # object you create, sibling notifiers can be initialized with
114    # #def_notifier.
115    class CompositeNotifier<AbstractNotifier
116      # Create a new composite notifier object with the given +prefix+, and
117      # +base_notifier+ to use for output.
118      def initialize(prefix, base_notifier)
119	super
120
121	@notifiers = [D_NOMSG]
122	@level_notifier = D_NOMSG
123      end
124
125      # List of notifiers in the group
126      attr_reader :notifiers
127
128      # Creates a new LeveledNotifier in the composite #notifiers group.
129      #
130      # The given +prefix+ will be assigned to the notifier, and +level+ will
131      # be used as the index of the #notifiers Array.
132      #
133      # This method returns the newly created instance.
134      def def_notifier(level, prefix = "")
135	notifier = LeveledNotifier.new(self, level, prefix)
136	@notifiers[level] = notifier
137	notifier
138      end
139
140      # Returns the leveled notifier for this object
141      attr_reader :level_notifier
142      alias level level_notifier
143
144      # Sets the leveled notifier for this object.
145      #
146      # When the given +value+ is an instance of AbstractNotifier,
147      # #level_notifier is set to the given object.
148      #
149      # When an Integer is given, #level_notifier is set to the notifier at the
150      # index +value+ in the #notifiers Array.
151      #
152      # If no notifier exists at the index +value+ in the #notifiers Array, an
153      # ErrUndefinedNotifier exception is raised.
154      #
155      # An ErrUnrecognizedLevel exception is raised if the given +value+ is not
156      # found in the existing #notifiers Array, or an instance of
157      # AbstractNotifier
158      def level_notifier=(value)
159	case value
160	when AbstractNotifier
161	  @level_notifier = value
162	when Integer
163	  l = @notifiers[value]
164	  Notifier.Raise ErrUndefinedNotifer, value unless l
165	  @level_notifier = l
166	else
167	  Notifier.Raise ErrUnrecognizedLevel, value unless l
168	end
169      end
170
171      alias level= level_notifier=
172    end
173
174    # A leveled notifier is comparable to the composite group from
175    # CompositeNotifier#notifiers.
176    class LeveledNotifier<AbstractNotifier
177      include Comparable
178
179      # Create a new leveled notifier with the given +base+, and +prefix+ to
180      # send to AbstractNotifier.new
181      #
182      # The given +level+ is used to compare other leveled notifiers in the
183      # CompositeNotifier group to determine whether or not to output
184      # notifications.
185      def initialize(base, level, prefix)
186	super(prefix, base)
187
188	@level = level
189      end
190
191      # The current level of this notifier object
192      attr_reader :level
193
194      # Compares the level of this notifier object with the given +other+
195      # notifier.
196      #
197      # See the Comparable module for more information.
198      def <=>(other)
199	@level <=> other.level
200      end
201
202      # Whether to output messages to the output method, depending on the level
203      # of this notifier object.
204      def notify?
205	@base_notifier.level >= self
206      end
207    end
208
209    # NoMsgNotifier is a LeveledNotifier that's used as the default notifier
210    # when creating a new CompositeNotifier.
211    #
212    # This notifier is used as the +zero+ index, or level +0+, for
213    # CompositeNotifier#notifiers, and will not output messages of any sort.
214    class NoMsgNotifier<LeveledNotifier
215      # Creates a new notifier that should not be used to output messages.
216      def initialize
217	@base_notifier = nil
218	@level = 0
219	@prefix = ""
220      end
221
222      # Ensures notifications are ignored, see AbstractNotifier#notify? for
223      # more information.
224      def notify?
225	false
226      end
227    end
228
229    D_NOMSG = NoMsgNotifier.new # :nodoc:
230  end
231end
232