Skip to content

Commit d2038c2

Browse files
committed
Remove the default to_xxx formatters from registry
Use the registry only for type-specific formatting.
1 parent 3a8e0be commit d2038c2

File tree

2 files changed

+132
-63
lines changed

2 files changed

+132
-63
lines changed

lib/iruby/display.rb

Lines changed: 127 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22

33
module 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
362431
end

test/iruby/display_test.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,12 @@ def setup
131131
def test_display
132132
assert_iruby_display({
133133
result: {
134-
"text/html" => "<b>to_iruby</b>",
134+
"text/markdown" => "*markdown*",
135135
"text/plain" => "!!! inspect !!!"
136136
},
137137
to_html_called: false,
138-
to_markdown_called: false,
139-
to_iruby_called: true,
138+
to_markdown_called: true,
139+
to_iruby_called: false,
140140
to_iruby_mimebundle_called: false
141141
})
142142
end
@@ -170,12 +170,12 @@ def setup
170170
def test_display
171171
assert_iruby_display({
172172
result: {
173-
"text/html" => "<b>html</b>",
173+
"text/html" => "<i>html</i>",
174174
"text/markdown" => "**markdown**",
175175
"application/json" => %Q[{"mimebundle": "json"}],
176176
"text/plain" => "!!! inspect !!!"
177177
},
178-
to_html_called: true,
178+
to_html_called: false,
179179
to_markdown_called: false,
180180
to_iruby_called: false,
181181
to_iruby_mimebundle_called: true

0 commit comments

Comments
 (0)