22
33module IRuby
44 module Display
5+ DEFAULT_MIME_TYPE_FORMAT_METHODS = {
6+ "text/html" => :to_html ,
7+ "text/markdown" => :to_markdown ,
8+ "image/svg+xml" => :to_svg ,
9+ "image/png" => :to_png ,
10+ "appliation/pdf" => :to_pdf ,
11+ "image/jpeg" => :to_jpeg ,
12+ "text/latex" => [ :to_latex , :to_tex ] ,
13+ # NOTE: Do not include the entry of "application/json" because
14+ # all objects can respond to `to_json` due to json library
15+ # "application/json" => :to_json,
16+ "application/javascript" => :to_javascript ,
17+ nil => :to_iruby ,
18+ "text/plain" => :inspect
19+ } . freeze
20+
521 class << self
622 # @private
723 def convert ( obj , options )
@@ -24,17 +40,38 @@ def display(obj, options = {})
2440 raise 'Invalid mime type' unless exact_mime . include? ( '/' )
2541 end
2642
27- data = { }
43+ data = render_mimebundle ( obj , exact_mime , fuzzy_mime )
44+
45+ # Render by additional formatters
46+ render_by_registry ( data , obj , exact_mime , fuzzy_mime )
47+
48+ # Render by to_xxx methods
49+ DEFAULT_MIME_TYPE_FORMAT_METHODS . each do |mime , methods |
50+ next if mime . nil? && !data . empty? # for to_iruby
2851
29- # Render additional representation
30- render ( data , obj , exact_mime , fuzzy_mime )
52+ next if mime && data . key? ( mime ) # do not overwrite
3153
32- # IPython always requires a text representation
33- render ( data , obj , 'text/plain' , nil ) unless data [ 'text/plain' ]
54+ method = Array ( methods ) . find { |m | obj . respond_to? ( m ) }
55+ next if method . nil?
56+
57+ result = obj . send ( method )
58+ case mime
59+ when nil # to_iruby
60+ case result
61+ when Array
62+ mime , result = result
63+ else
64+ warn ( ( "Ignore the result of to_iruby method of %p because " +
65+ "it does not return a pair of mime-type and formatted representation" ) % obj )
66+ next
67+ end
68+ end
69+ data [ mime ] = result
70+ end
3471
3572 # As a last resort, interpret string representation of the object
3673 # as the given mime type.
37- if exact_mime && data . none? { | m , _ | exact_mime == m }
74+ if exact_mime && ! data . key? ( exact_mime )
3875 data [ exact_mime ] = protect ( exact_mime , obj )
3976 end
4077
@@ -67,7 +104,21 @@ def ascii?(mime)
67104 end
68105 end
69106
70- def render ( data , obj , exact_mime , fuzzy_mime )
107+ private def render_mimebundle ( obj , exact_mime , fuzzy_mime )
108+ data = { }
109+ if obj . respond_to? ( :to_iruby_mimebundle )
110+ include_mime = [ exact_mime ] . compact
111+ formats , metadata = obj . to_iruby_mimebundle ( include : include_mime )
112+ formats . each do |mime , value |
113+ if fuzzy_mime . nil? || mime . include? ( fuzzy_mime )
114+ data [ mime ] = value
115+ end
116+ end
117+ end
118+ data
119+ end
120+
121+ private def render_by_registry ( data , obj , exact_mime , fuzzy_mime )
71122 # Filter matching renderer by object type
72123 renderer = Registry . renderer . select { |r | r . match? ( obj ) }
73124
@@ -88,6 +139,8 @@ def render(data, obj, exact_mime, fuzzy_mime)
88139 # Return first render result which has the right mime type
89140 renderer . each do |r |
90141 mime , result = r . render ( obj )
142+ next if data . key? ( mime )
143+
91144 if mime && result && ( !exact_mime || exact_mime == mime ) && ( !fuzzy_mime || mime . include? ( fuzzy_mime ) )
92145 data [ mime ] = protect ( mime , result )
93146 break
@@ -98,6 +151,19 @@ def render(data, obj, exact_mime, fuzzy_mime)
98151 end
99152 end
100153
154+ private def render_by_to_iruby ( data , obj )
155+ if obj . respond_to? ( :to_iruby )
156+ result = obj . to_iruby
157+ mime , rep = case result
158+ when Array
159+ result
160+ else
161+ [ nil , result ]
162+ end
163+ data [ mime ] = rep
164+ end
165+ end
166+
101167 class Representation
102168 attr_reader :object , :options
103169
@@ -120,6 +186,60 @@ def new(obj, options)
120186 end
121187 end
122188
189+ class FormatMatcher
190+ def initialize ( &block )
191+ @block = block
192+ end
193+
194+ def call ( obj )
195+ @block . ( obj )
196+ end
197+
198+ def inspect
199+ "#{ self . class . name } [%p]" % @block
200+ end
201+ end
202+
203+ class RespondToFormatMatcher < FormatMatcher
204+ def initialize ( name )
205+ super ( ) { |obj | obj . respond_to? ( name ) }
206+ @name = name
207+ end
208+
209+ attr_reader :name
210+
211+ def inspect
212+ "#{ self . class . name } [respond_to?(%p)]" % name
213+ end
214+ end
215+
216+ class TypeFormatMatcher < FormatMatcher
217+ def initialize ( class_block )
218+ super ( ) do |obj |
219+ begin
220+ self . klass === obj
221+ # We have to rescue all exceptions since constant autoloading could fail with a different error
222+ rescue Exception
223+ false
224+ end
225+ end
226+ @class_block = class_block
227+ end
228+
229+ def klass
230+ @class_block . ( )
231+ end
232+
233+ def inspect
234+ klass = begin
235+ @class_block . ( )
236+ rescue Exception
237+ @class_block
238+ end
239+ "#{ self . class . name } [%p]" % klass
240+ end
241+ end
242+
123243 class Renderer
124244 attr_reader :match , :mime , :priority
125245
@@ -159,25 +279,21 @@ def renderer
159279 ]
160280
161281 def match ( &block )
162- @match = block
282+ @match = FormatMatcher . new ( & block )
163283 priority 0
164284 nil
165285 end
166286
167287 def respond_to ( name )
168- match { |obj | obj . respond_to? ( name ) }
288+ @match = RespondToFormatMatcher . new ( name )
289+ priority 0
290+ nil
169291 end
170292
171293 def type ( &block )
172- match do |obj |
173- begin
174- block . call === obj
175- # We have to rescue all exceptions since constant autoloading could fail with a different error
176- rescue Exception
177- rescue #NameError
178- false
179- end
180- end
294+ @match = TypeFormatMatcher . new ( block )
295+ priority 0
296+ nil
181297 end
182298
183299 def priority ( p )
@@ -301,36 +417,17 @@ def format(mime = nil, &block)
301417 type { Gruff ::Base }
302418 format 'image/png' , &:to_blob
303419
304- respond_to :to_html
305- format 'text/html' , &:to_html
306-
307- respond_to :to_latex
308- format 'text/latex' , &:to_latex
309-
310- respond_to :to_tex
311- format 'text/latex' , &:to_tex
312-
313- respond_to :to_javascript
314- format 'text/javascript' , &:to_javascript
315-
316- respond_to :to_svg
420+ type { Rubyvis ::Mark }
317421 format 'image/svg+xml' do |obj |
318- obj . render if defined? ( Rubyvis ) && Rubyvis :: Mark === obj
422+ obj . render
319423 obj . to_svg
320424 end
321425
322- respond_to :to_iruby
323- format ( &:to_iruby )
324-
325426 match { |obj | obj . respond_to? ( :path ) && obj . method ( :path ) . arity == 0 && File . readable? ( obj . path ) }
326427 format do |obj |
327428 mime = MIME ::Types . of ( obj . path ) . first . to_s
328429 [ mime , File . read ( obj . path ) ] if SUPPORTED_MIMES . include? ( mime )
329430 end
330-
331- type { Object }
332- priority ( -1000 )
333- format 'text/plain' , &:inspect
334431 end
335432 end
336433end
0 commit comments