Skip to content

Commit

Permalink
Render message for bad OEmbed responses
Browse files Browse the repository at this point in the history
When a requested OEmbed object returns an error, render a message for
the user explaining that there was an error with the embedded element.

For this, standardize the `OEbedResolver::Base#source_url` method and
add a `#name` method which will be included in the text for the user.

The text for the user will be rendered within the `<figure>` element
within a nested `span` with the class `unavailable-embed`. This also
allows the consumer to have custom CSS for this case.
  • Loading branch information
nicolas-fricke committed Nov 7, 2017
1 parent abff6f6 commit ef03d4e
Show file tree
Hide file tree
Showing 17 changed files with 160 additions and 32 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## WIP
- Handle non-successful OEmbed responses

## 0.2.0 - 2017/11/03
In this second release we **added support** to:
- Export AMP along with required libraries for AMP rendering
Expand Down
5 changes: 5 additions & 0 deletions lib/article_json/elements/embed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ def oembed_data
oembed_resolver&.oembed_data
end

# @return [Array[ArticleJSON::Elements::Text]|nil]
def oembed_unavailable_message
oembed_resolver&.unavailable_message
end

private

# @return [ArticleJSON::Utils::OEmbedResolver::Base]
Expand Down
12 changes: 10 additions & 2 deletions lib/article_json/export/common/html/elements/embed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,22 @@ def export

def embed_node
create_element(:div, class: 'embed') do |div|
div.add_child(embedded_object) if embedded_object
div.add_child(embedded_object)
end
end

def embedded_object
return unless @element.oembed_data
return unavailable_node unless @element.oembed_data
Nokogiri::HTML.fragment(@element.oembed_data[:html])
end

def unavailable_node
create_element(:span, class: 'unavailable-embed') do |span|
@element.oembed_unavailable_message.each do |element|
span.add_child(base_class.build(element).export)
end
end
end
end
end
end
Expand Down
22 changes: 22 additions & 0 deletions lib/article_json/utils/o_embed_resolver/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,28 @@ def oembed_data
resolver.parsed_api_response
end

# In case that there was an error with requesting the OEmbed data from
# the endpoint (e.g. because the URL is unavailable), this message can
# be rendered to let the user know about the issue
# @return [Array[ArticleJSON::Elements::Text]|nil]
def unavailable_message
[
ArticleJSON::Elements::Text.new(content: "The #{name} "),
ArticleJSON::Elements::Text.new(content: source_url,
href: source_url),
ArticleJSON::Elements::Text.new(content: ' is not available.'),
]
end

def name
raise NotImplementedError,
'`#name` needs to be implemented by the subclass'
end

def source_url
raise NotImplementedError,
'`#source_url` needs to be implemented by the subclass'
end

protected

Expand Down
12 changes: 8 additions & 4 deletions lib/article_json/utils/o_embed_resolver/facebook_video.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@ module ArticleJSON
module Utils
module OEmbedResolver
class FacebookVideo < Base
# Human readable name of the resolver
# @return [String]
def name
'Facebook video'
end

# The URL for the oembed API call
# @return [String]
def oembed_url
"https://www.facebook.com/plugins/video/oembed.json?url=#{video_url}"
"https://www.facebook.com/plugins/video/oembed.json?url=#{source_url}"
end

private

# The video URL of the element
# @return [String]
def video_url
def source_url
"https://www.facebook.com/facebook/videos/#{@element.embed_id}"
end
end
Expand Down
13 changes: 9 additions & 4 deletions lib/article_json/utils/o_embed_resolver/slideshare.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@ module ArticleJSON
module Utils
module OEmbedResolver
class Slideshare < Base
# Human readable name of the resolver
# @return [String]
def name
'Slideshare deck'
end

# The URL for the oembed API call
# @return [String]
def oembed_url
"https://www.slideshare.net/api/oembed/2?format=json&url=#{slide_url}"
'https://www.slideshare.net/api/oembed/2?format=json&url='\
"#{source_url}"
end

private

# The URL of the slideshow
# @return [String]
def slide_url
def source_url
handle, slug = @element.embed_id.split('/', 2)
"https://www.slideshare.net/#{handle}/#{slug}"
end
Expand Down
12 changes: 8 additions & 4 deletions lib/article_json/utils/o_embed_resolver/tweet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@ module ArticleJSON
module Utils
module OEmbedResolver
class Tweet < Base
# Human readable name of the resolver
# @return [String]
def name
'Tweet'
end

# The URL for the oembed API call
# @return [String]
def oembed_url
'https://api.twitter.com/1/statuses/oembed.json?align=center' \
"&url=#{tweet_url}"
"&url=#{source_url}"
end

private

# The URL of the tweet
# @return [String]
def tweet_url
def source_url
handle, tweet_id = @element.embed_id.split('/', 2)
"https://twitter.com/#{handle}/status/#{tweet_id}"
end
Expand Down
12 changes: 8 additions & 4 deletions lib/article_json/utils/o_embed_resolver/vimeo_video.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@ module ArticleJSON
module Utils
module OEmbedResolver
class VimeoVideo < Base
# Human readable name of the resolver
# @return [String]
def name
'Vimeo video'
end

# The URL for the oembed API call
# @return [String]
def oembed_url
"https://vimeo.com/api/oembed.json?url=#{video_url}"
"https://vimeo.com/api/oembed.json?url=#{source_url}"
end

private

# The video URL of the element
# @return [String]
def video_url
def source_url
"https://vimeo.com/#{@element.embed_id}"
end
end
Expand Down
12 changes: 8 additions & 4 deletions lib/article_json/utils/o_embed_resolver/youtube_video.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@ module ArticleJSON
module Utils
module OEmbedResolver
class YoutubeVideo < Base
# Human readable name of the resolver
# @return [String]
def name
'Youtube video'
end

# The URL for the oembed API call
# @return [String]
def oembed_url
"http://www.youtube.com/oembed?format=json&url=#{video_url}"
"http://www.youtube.com/oembed?format=json&url=#{source_url}"
end

private

# The video URL of the element
# @return [String]
def video_url
def source_url
"https://www.youtube.com/watch?v=#{@element.embed_id}"
end
end
Expand Down
35 changes: 27 additions & 8 deletions spec/article_json/export/html/elements/embed_spec.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
describe ArticleJSON::Export::HTML::Elements::Embed do
subject(:element) { described_class.new(source_element) }

let(:embed_type) { :something }
let(:source_element) do
ArticleJSON::Elements::Embed.new(
embed_type: :something,
embed_type: embed_type,
embed_id: 666,
caption: [ArticleJSON::Elements::Text.new(content: 'Foo Bar')],
tags: %w(test)
Expand All @@ -12,14 +13,32 @@

describe '#export' do
subject { element.export.to_html(save_with: 0) }
let(:expected_html) do
'<figure><div class="embed">Embedded Object: something-666</div>' \
'<figcaption>Foo Bar</figcaption></figure>'

context 'when the endpoint successfully returns OEmbed data' do
let(:expected_html) do
'<figure><div class="embed">Embedded Object: something-666</div>' \
'<figcaption>Foo Bar</figcaption></figure>'
end
let(:oembed_data) { { html: 'Embedded Object: something-666' } }
before do
allow(source_element).to receive(:oembed_data).and_return(oembed_data)
end
it { should eq expected_html }
end
let(:oembed_data) { { html: 'Embedded Object: something-666' } }
before do
allow(source_element).to receive(:oembed_data).and_return(oembed_data)

context 'when the endpoint does not return OEmbed data' do
let(:embed_type) { :youtube_video }
let(:expected_html) do
'<figure><div class="embed"><span class="unavailable-embed">'\
'The Youtube video <a href="https://www.youtube.com/watch?v=666">'\
'https://www.youtube.com/watch?v=666</a> is not available.</span>'\
'</div><figcaption>Foo Bar</figcaption></figure>'
end
let(:oembed_data) { { html: 'Embedded Object: something-666' } }
before do
allow(source_element).to receive(:oembed_data).and_return(nil)
end
it { should eq expected_html }
end
it { should eq expected_html }
end
end
37 changes: 35 additions & 2 deletions spec/article_json/utils/o_embed_resolver/base_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
describe ArticleJSON::Utils::OEmbedResolver::Base do
subject(:resolver) { described_class.new(element) }

let(:embed_id) { 'ABC' }
let(:embed_type) { :foobar }
let(:element) do
ArticleJSON::Elements::Embed.new(
embed_type: embed_type,
Expand All @@ -12,8 +14,6 @@
describe '#oembed_data' do
subject { resolver.oembed_data }

let(:embed_id) { 'ABC' }
let(:embed_type) { :foobar }
let(:something_resolver) { double('something resolver') }
let(:oembed_data) { { foo: :bar } }

Expand All @@ -27,6 +27,39 @@
it { should eq oembed_data }
end

describe '#unavailable_message' do
subject { resolver.unavailable_message }
let(:name) { 'Embed type' }
let(:source_url) { 'https://example.com' }
before do
allow(resolver).to receive(:name).and_return(name)
allow(resolver).to receive(:source_url).and_return(source_url)
end
it { should_not be_nil }

it 'should return an Array of Text elements' do
is_expected.to be_an Array
is_expected.to all be_a ArticleJSON::Elements::Text
expect(subject.size).to eq 3
expect(subject[0].content).to include name
expect(subject[1].content).to eq source_url
expect(subject[1].href).to eq source_url
expect(subject[2].content).to include 'not available'
end
end

describe '#name' do
it 'should raise a NotImplementedError' do
expect { resolver.name }.to raise_error NotImplementedError
end
end

describe '#source_url' do
it 'should raise a NotImplementedError' do
expect { resolver.source_url }.to raise_error NotImplementedError
end
end

describe '.build' do
subject { described_class.build(element) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@
let(:oembed_response) do
File.read('spec/fixtures/facebook_video_oembed.json')
end
let(:expected_name) { 'Facebook video' }
end
end
12 changes: 12 additions & 0 deletions spec/article_json/utils/o_embed_resolver/oembed_resolver_shared.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
shared_context 'for a successful oembed resolution' do
subject(:resolver) { described_class.new(element) }

describe '#name' do
subject { resolver.name }
it { should be_a String }
it { should eq expected_name }
end

describe '#oembed_url' do
subject { resolver.oembed_url }
it { should eq expected_oembed_url }
Expand Down Expand Up @@ -32,4 +38,10 @@
it { should eq nil }
end
end

describe '#source_url' do
subject { resolver.source_url }
it { should be_a String }
it { should start_with 'http' }
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@
'https://www.slideshare.net/Devex/the-best-global-development-quotes-of-2012'
end
let(:oembed_response) { File.read('spec/fixtures/slideshare_oembed.json') }
let(:expected_name) { 'Slideshare deck' }
end
end
1 change: 1 addition & 0 deletions spec/article_json/utils/o_embed_resolver/tweet_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@
let(:oembed_response) do
File.read('spec/fixtures/tweet_oembed.json')
end
let(:expected_name) { 'Tweet' }
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
'https://vimeo.com/api/oembed.json?url=https://vimeo.com/42315417'
end
let(:oembed_response) { File.read('spec/fixtures/vimeo_video_oembed.json') }
let(:expected_name) { 'Vimeo video' }
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@
let(:oembed_response) do
File.read('spec/fixtures/youtube_video_oembed.json')
end
let(:expected_name) { 'Youtube video' }
end
end

0 comments on commit ef03d4e

Please sign in to comment.