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_by_registry ( 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,30 +104,18 @@ def ascii?(mime)
67104 end
68105 end
69106
70- def render ( data , obj , exact_mime , fuzzy_mime )
71- # First examine to_iruby_mimebundle
72-
107+ private def render_mimebundle ( obj , exact_mime , fuzzy_mime )
108+ data = { }
73109 if obj . respond_to? ( :to_iruby_mimebundle )
74- # If the object can respond to to_iruby_mimebundle,
75- # IRuby uses it and ignores all the registered renderer.
76- kwargs = { }
77- include_mime = [ exact_mime , fuzzy_mime ] . compact
78- kwargs [ :include ] = include_mime unless include_mime . empty?
79- # TODO: support `exclude:` keyword argument to specify mime types to be excluded
80-
81- # TODO: should handle metadata correctly
82- formats , metadata = obj . to_iruby_mimebundle ( **kwargs )
83-
84- # formats should be a Hash that maps mime-type string to
85- # the redered data.
110+ include_mime = [ exact_mime ] . compact
111+ formats , metadata = obj . to_iruby_mimebundle ( include : include_mime )
86112 formats . each do |mime , value |
87- data [ mime ] = value unless value . nil?
113+ if fuzzy_mime . nil? || mime . include? ( fuzzy_mime )
114+ data [ mime ] = value
115+ end
88116 end
89117 end
90-
91- unless data . key? ( exact_mime )
92- render_by_registry ( data , obj , exact_mime , fuzzy_mime )
93- end
118+ data
94119 end
95120
96121 private def render_by_registry ( data , obj , exact_mime , fuzzy_mime )
@@ -114,6 +139,8 @@ def render(data, obj, exact_mime, fuzzy_mime)
114139 # Return first render result which has the right mime type
115140 renderer . each do |r |
116141 mime , result = r . render ( obj )
142+ next if data . key? ( mime )
143+
117144 if mime && result && ( !exact_mime || exact_mime == mime ) && ( !fuzzy_mime || mime . include? ( fuzzy_mime ) )
118145 data [ mime ] = protect ( mime , result )
119146 break
@@ -124,6 +151,19 @@ def render(data, obj, exact_mime, fuzzy_mime)
124151 end
125152 end
126153
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+
127167 class Representation
128168 attr_reader :object , :options
129169
@@ -146,6 +186,58 @@ def new(obj, options)
146186 end
147187 end
148188
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+ self . klass === obj
220+ # We have to rescue all exceptions since constant autoloading could fail with a different error
221+ rescue Exception
222+ false
223+ end
224+ @class_block = class_block
225+ end
226+
227+ def klass
228+ @class_block . ( )
229+ end
230+
231+ def inspect
232+ klass = begin
233+ @class_block . ( )
234+ rescue Exception
235+ @class_block
236+ end
237+ "#{ self . class . name } [%p]" % klass
238+ end
239+ end
240+
149241 class Renderer
150242 attr_reader :match , :mime , :priority
151243
@@ -185,25 +277,21 @@ def renderer
185277 ]
186278
187279 def match ( &block )
188- @match = block
280+ @match = FormatMatcher . new ( & block )
189281 priority 0
190282 nil
191283 end
192284
193285 def respond_to ( name )
194- match { |obj | obj . respond_to? ( name ) }
286+ @match = RespondToFormatMatcher . new ( name )
287+ priority 0
288+ nil
195289 end
196290
197291 def type ( &block )
198- match do |obj |
199- begin
200- block . call === obj
201- # We have to rescue all exceptions since constant autoloading could fail with a different error
202- rescue Exception
203- rescue #NameError
204- false
205- end
206- end
292+ @match = TypeFormatMatcher . new ( block )
293+ priority 0
294+ nil
207295 end
208296
209297 def priority ( p )
@@ -327,36 +415,17 @@ def format(mime = nil, &block)
327415 type { Gruff ::Base }
328416 format 'image/png' , &:to_blob
329417
330- respond_to :to_html
331- format 'text/html' , &:to_html
332-
333- respond_to :to_latex
334- format 'text/latex' , &:to_latex
335-
336- respond_to :to_tex
337- format 'text/latex' , &:to_tex
338-
339- respond_to :to_javascript
340- format 'text/javascript' , &:to_javascript
341-
342- respond_to :to_svg
418+ type { Rubyvis ::Mark }
343419 format 'image/svg+xml' do |obj |
344- obj . render if defined? ( Rubyvis ) && Rubyvis :: Mark === obj
420+ obj . render
345421 obj . to_svg
346422 end
347423
348- respond_to :to_iruby
349- format ( &:to_iruby )
350-
351424 match { |obj | obj . respond_to? ( :path ) && obj . method ( :path ) . arity == 0 && File . readable? ( obj . path ) }
352425 format do |obj |
353426 mime = MIME ::Types . of ( obj . path ) . first . to_s
354427 [ mime , File . read ( obj . path ) ] if SUPPORTED_MIMES . include? ( mime )
355428 end
356-
357- type { Object }
358- priority ( -1000 )
359- format 'text/plain' , &:inspect
360429 end
361430 end
362431end
0 commit comments