summaryrefslogtreecommitdiff
path: root/lib/irb/notifier.rb
blob: 20a5b15738e3b81547f7c14ae5438c9ca3d57e34 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#
#   notifier.rb - output methods used by irb
#   	$Release Version: 0.9.6$
#   	$Revision$
#   	by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
# --
#
#
#

require "e2mmap"
require "irb/output-method"

module IRB
  # An output formatter used internally by the lexer.
  module Notifier
    extend Exception2MessageMapper
    def_exception :ErrUndefinedNotifier,
      "undefined notifier level: %d is specified"
    def_exception :ErrUnrecognizedLevel,
      "unrecognized notifier level: %s is specified"

    # Define a new Notifier output source, returning a new CompositeNotifier
    # with the given +prefix+ and +output_method+.
    #
    # The optional +prefix+ will be appended to all objects being inspected
    # during output, using the given +output_method+ as the output source. If
    # no +output_method+ is given, StdioOuputMethod will be used, and all
    # expressions will be sent directly to STDOUT without any additional
    # formatting.
    def def_notifier(prefix = "", output_method = StdioOutputMethod.new)
      CompositeNotifier.new(prefix, output_method)
    end
    module_function :def_notifier

    # An abstract class, or superclass, for CompositeNotifier and
    # LeveledNotifier to inherit. It provides several wrapper methods for the
    # OutputMethod object used by the Notifier.
    class AbstractNotifier
      # Creates a new Notifier object
      def initialize(prefix, base_notifier)
	@prefix = prefix
	@base_notifier = base_notifier
      end

      # The +prefix+ for this Notifier, which is appended to all objects being
      # inspected during output.
      attr_reader :prefix

      # A wrapper method used to determine whether notifications are enabled.
      #
      # Defaults to +true+.
      def notify?
	true
      end

      # See OutputMethod#print for more detail.
      def print(*opts)
	@base_notifier.print prefix, *opts if notify?
      end

      # See OutputMethod#printn for more detail.
      def printn(*opts)
	@base_notifier.printn prefix, *opts if notify?
      end

      # See OutputMethod#printf for more detail.
      def printf(format, *opts)
	@base_notifier.printf(prefix + format, *opts) if notify?
      end

      # See OutputMethod#puts for more detail.
      def puts(*objs)
	if notify?
	  @base_notifier.puts(*objs.collect{|obj| prefix + obj.to_s})
	end
      end

      # Same as #ppx, except it uses the #prefix given during object
      # initialization.
      # See OutputMethod#ppx for more detail.
      def pp(*objs)
	if notify?
	  @base_notifier.ppx @prefix, *objs
	end
      end

      # Same as #pp, except it concatenates the given +prefix+ with the #prefix
      # given during object initialization.
      #
      # See OutputMethod#ppx for more detail.
      def ppx(prefix, *objs)
	if notify?
	  @base_notifier.ppx @prefix+prefix, *objs
	end
      end

      # Execute the given block if notifications are enabled.
      def exec_if
	yield(@base_notifier) if notify?
      end
    end

    # A class that can be used to create a group of notifier objects with the
    # intent of representing a leveled notification system for irb.
    #
    # This class will allow you to generate other notifiers, and assign them
    # the appropriate level for output.
    #
    # The Notifier class provides a class-method Notifier.def_notifier to
    # create a new composite notifier. Using the first composite notifier
    # object you create, sibling notifiers can be initialized with
    # #def_notifier.
    class CompositeNotifier<AbstractNotifier
      # Create a new composite notifier object with the given +prefix+, and
      # +base_notifier+ to use for output.
      def initialize(prefix, base_notifier)
	super

	@notifiers = [D_NOMSG]
	@level_notifier = D_NOMSG
      end

      # List of notifiers in the group
      attr_reader :notifiers

      # Creates a new LeveledNotifier in the composite #notifiers group.
      #
      # The given +prefix+ will be assigned to the notifier, and +level+ will
      # be used as the index of the #notifiers Array.
      #
      # This method returns the newly created instance.
      def def_notifier(level, prefix = "")
	notifier = LeveledNotifier.new(self, level, prefix)
	@notifiers[level] = notifier
	notifier
      end

      # Returns the leveled notifier for this object
      attr_reader :level_notifier
      alias level level_notifier

      # Sets the leveled notifier for this object.
      #
      # When the given +value+ is an instance of AbstractNotifier,
      # #level_notifier is set to the given object.
      #
      # When an Integer is given, #level_notifier is set to the notifier at the
      # index +value+ in the #notifiers Array.
      #
      # If no notifier exists at the index +value+ in the #notifiers Array, an
      # ErrUndefinedNotifier exception is raised.
      #
      # An ErrUnrecognizedLevel exception is raised if the given +value+ is not
      # found in the existing #notifiers Array, or an instance of
      # AbstractNotifier
      def level_notifier=(value)
	case value
	when AbstractNotifier
	  @level_notifier = value
	when Integer
	  l = @notifiers[value]
	  Notifier.Raise ErrUndefinedNotifier, value unless l
	  @level_notifier = l
	else
	  Notifier.Raise ErrUnrecognizedLevel, value unless l
	end
      end

      alias level= level_notifier=
    end

    # A leveled notifier is comparable to the composite group from
    # CompositeNotifier#notifiers.
    class LeveledNotifier<AbstractNotifier
      include Comparable

      # Create a new leveled notifier with the given +base+, and +prefix+ to
      # send to AbstractNotifier.new
      #
      # The given +level+ is used to compare other leveled notifiers in the
      # CompositeNotifier group to determine whether or not to output
      # notifications.
      def initialize(base, level, prefix)
	super(prefix, base)

	@level = level
      end

      # The current level of this notifier object
      attr_reader :level

      # Compares the level of this notifier object with the given +other+
      # notifier.
      #
      # See the Comparable module for more information.
      def <=>(other)
	@level <=> other.level
      end

      # Whether to output messages to the output method, depending on the level
      # of this notifier object.
      def notify?
	@base_notifier.level >= self
      end
    end

    # NoMsgNotifier is a LeveledNotifier that's used as the default notifier
    # when creating a new CompositeNotifier.
    #
    # This notifier is used as the +zero+ index, or level +0+, for
    # CompositeNotifier#notifiers, and will not output messages of any sort.
    class NoMsgNotifier<LeveledNotifier
      # Creates a new notifier that should not be used to output messages.
      def initialize
	@base_notifier = nil
	@level = 0
	@prefix = ""
      end

      # Ensures notifications are ignored, see AbstractNotifier#notify? for
      # more information.
      def notify?
	false
      end
    end

    D_NOMSG = NoMsgNotifier.new # :nodoc:
  end
end