Extract tile and subtitle from initial paragraphs

Medium guides each post to have a Title and Subtitle. They are rendered
as the first two paragraphs: H3 and H4 respectively. If they exist, a
new PageConverter class extracts them and sets them on the page.

However, they aren't required. If the first two paragraphs aren't H3
and H4, the PageConverter falls back to using the first paragraph as
the title, and setting the subtitle to blank.

The remaining paragraphs are passed into the ParagraphConverter as
normal.
This commit is contained in:
Edward Loveall 2021-08-14 17:36:10 -04:00
parent f48f7c2932
commit 05c18f6451
No known key found for this signature in database
GPG Key ID: 789A4AE983AC8901
5 changed files with 288 additions and 99 deletions

View File

@ -0,0 +1,97 @@
require "../spec_helper"
include Nodes
describe PageConverter do
it "sets the title and subtitle if present" do
paragraphs = Array(PostResponse::Paragraph).from_json <<-JSON
[
{
"text": "Title",
"type": "H3",
"markups": [],
"href": null,
"iframe": null,
"layout": null,
"metadata": null
},
{
"text": "Subtitle",
"type": "H4",
"markups": [],
"href": null,
"iframe": null,
"layout": null,
"metadata": null
}
]
JSON
page = PageConverter.new.convert(paragraphs)
page.title.should eq "Title"
page.subtitle.should eq "Subtitle"
end
it "sets the title to the first paragraph if no title" do
paragraphs = Array(PostResponse::Paragraph).from_json <<-JSON
[
{
"text": "Not a title",
"type": "P",
"markups": [],
"href": null,
"iframe": null,
"layout": null,
"metadata": null
}
]
JSON
page = PageConverter.new.convert(paragraphs)
page.title.should eq "Not a title"
page.subtitle.should eq nil
end
it "calls ParagraphConverter to convert the remaining paragraph content" do
paragraphs = Array(PostResponse::Paragraph).from_json <<-JSON
[
{
"text": "Title",
"type": "H3",
"markups": [],
"href": null,
"iframe": null,
"layout": null,
"metadata": null
},
{
"text": "Subtitle",
"type": "H4",
"markups": [],
"href": null,
"iframe": null,
"layout": null,
"metadata": null
},
{
"text": "Content",
"type": "P",
"markups": [],
"href": null,
"iframe": null,
"layout": null,
"metadata": null
}
]
JSON
page = PageConverter.new.convert(paragraphs)
page.nodes.should eq [
Paragraph.new([
Text.new("Content"),
] of Child),
]
end
end

View File

@ -4,11 +4,15 @@ include Nodes
describe PageContent do describe PageContent do
it "renders a single parent/child node structure" do it "renders a single parent/child node structure" do
page = Page.new(nodes: [ page = Page.new(
Paragraph.new(children: [ title: "Title",
Text.new(content: "hi"), subtitle: nil,
] of Child), nodes: [
] of Child) Paragraph.new(children: [
Text.new(content: "hi"),
] of Child),
] of Child
)
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
@ -16,22 +20,26 @@ describe PageContent do
end end
it "renders multiple childrens" do it "renders multiple childrens" do
page = Page.new(nodes: [ page = Page.new(
Paragraph.new(children: [ title: "Title",
Text.new(content: "Hello, "), subtitle: nil,
Emphasis.new(children: [ nodes: [
Text.new(content: "World!"), Paragraph.new(children: [
Text.new(content: "Hello, "),
Emphasis.new(children: [
Text.new(content: "World!"),
] of Child),
] of Child), ] of Child),
] of Child), UnorderedList.new(children: [
UnorderedList.new(children: [ ListItem.new(children: [
ListItem.new(children: [ Text.new(content: "List!"),
Text.new(content: "List!"), ] of Child),
ListItem.new(children: [
Text.new(content: "Again!"),
] of Child),
] of Child), ] of Child),
ListItem.new(children: [ ] of Child
Text.new(content: "Again!"), )
] of Child),
] of Child),
] of Child)
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
@ -39,9 +47,13 @@ describe PageContent do
end end
it "renders an anchor" do it "renders an anchor" do
page = Page.new(nodes: [ page = Page.new(
Anchor.new(children: [Text.new("link")] of Child, href: "https://example.com"), title: "Title",
] of Child) subtitle: nil,
nodes: [
Anchor.new(children: [Text.new("link")] of Child, href: "https://example.com"),
] of Child
)
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
@ -49,11 +61,15 @@ describe PageContent do
end end
it "renders a blockquote" do it "renders a blockquote" do
page = Page.new(nodes: [ page = Page.new(
BlockQuote.new(children: [ title: "Title",
Text.new("Wayne Gretzky. Michael Scott."), subtitle: nil,
] of Child), nodes: [
] of Child) BlockQuote.new(children: [
Text.new("Wayne Gretzky. Michael Scott."),
] of Child),
] of Child
)
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
@ -61,11 +77,15 @@ describe PageContent do
end end
it "renders code" do it "renders code" do
page = Page.new(nodes: [ page = Page.new(
Code.new(children: [ title: "Title",
Text.new("foo = bar"), subtitle: nil,
] of Child), nodes: [
] of Child) Code.new(children: [
Text.new("foo = bar"),
] of Child),
] of Child
)
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
@ -73,14 +93,18 @@ describe PageContent do
end end
it "renders empasis" do it "renders empasis" do
page = Page.new(nodes: [ page = Page.new(
Paragraph.new(children: [ title: "Title",
Text.new(content: "This is "), subtitle: nil,
Emphasis.new(children: [ nodes: [
Text.new(content: "neat!"), Paragraph.new(children: [
Text.new(content: "This is "),
Emphasis.new(children: [
Text.new(content: "neat!"),
] of Child),
] of Child), ] of Child),
] of Child), ] of Child
] of Child) )
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
@ -88,14 +112,18 @@ describe PageContent do
end end
it "renders a figure and figure caption" do it "renders a figure and figure caption" do
page = Page.new(nodes: [ page = Page.new(
Figure.new(children: [ title: "Title",
Image.new(src: "image.png", originalWidth: 100, originalHeight: 200), subtitle: nil,
FigureCaption.new(children: [ nodes: [
Text.new("A caption"), Figure.new(children: [
Image.new(src: "image.png", originalWidth: 100, originalHeight: 200),
FigureCaption.new(children: [
Text.new("A caption"),
] of Child),
] of Child), ] of Child),
] of Child), ] of Child
] of Child) )
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
@ -108,11 +136,15 @@ describe PageContent do
end end
it "renders an H3" do it "renders an H3" do
page = Page.new(nodes: [ page = Page.new(
Heading2.new(children: [ title: "Title",
Text.new(content: "Title!"), subtitle: nil,
] of Child), nodes: [
] of Child) Heading2.new(children: [
Text.new(content: "Title!"),
] of Child),
] of Child
)
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
@ -120,11 +152,15 @@ describe PageContent do
end end
it "renders an H4" do it "renders an H4" do
page = Page.new(nodes: [ page = Page.new(
Heading3.new(children: [ title: "Title",
Text.new(content: "In Conclusion..."), subtitle: nil,
] of Child), nodes: [
] of Child) Heading3.new(children: [
Text.new(content: "In Conclusion..."),
] of Child),
] of Child
)
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
@ -132,11 +168,15 @@ describe PageContent do
end end
it "renders an image" do it "renders an image" do
page = Page.new(nodes: [ page = Page.new(
Paragraph.new(children: [ title: "Title",
Image.new(src: "image.png", originalWidth: 100, originalHeight: 200), subtitle: nil,
] of Child), nodes: [
] of Child) Paragraph.new(children: [
Image.new(src: "image.png", originalWidth: 100, originalHeight: 200),
] of Child),
] of Child
)
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
@ -148,11 +188,15 @@ describe PageContent do
end end
it "renders an iframe container" do it "renders an iframe container" do
page = Page.new(nodes: [ page = Page.new(
Paragraph.new(children: [ title: "Title",
IFrame.new(href: "https://example.com"), subtitle: nil,
] of Child), nodes: [
] of Child) Paragraph.new(children: [
IFrame.new(href: "https://example.com"),
] of Child),
] of Child
)
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
@ -166,12 +210,16 @@ describe PageContent do
end end
it "renders an ordered list" do it "renders an ordered list" do
page = Page.new(nodes: [ page = Page.new(
OrderedList.new(children: [ title: "Title",
ListItem.new(children: [Text.new("One")] of Child), subtitle: nil,
ListItem.new(children: [Text.new("Two")] of Child), nodes: [
] of Child), OrderedList.new(children: [
] of Child) ListItem.new(children: [Text.new("One")] of Child),
ListItem.new(children: [Text.new("Two")] of Child),
] of Child),
] of Child
)
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
@ -179,11 +227,15 @@ describe PageContent do
end end
it "renders an preformatted text" do it "renders an preformatted text" do
page = Page.new(nodes: [ page = Page.new(
Paragraph.new(children: [ title: "Title",
Text.new("Hello, world!"), subtitle: nil,
] of Child), nodes: [
] of Child) Paragraph.new(children: [
Text.new("Hello, world!"),
] of Child),
] of Child
)
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
@ -191,11 +243,15 @@ describe PageContent do
end end
it "renders an preformatted text" do it "renders an preformatted text" do
page = Page.new(nodes: [ page = Page.new(
Preformatted.new(children: [ title: "Title",
Text.new("New\nline"), subtitle: nil,
] of Child), nodes: [
] of Child) Preformatted.new(children: [
Text.new("New\nline"),
] of Child),
] of Child
)
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
@ -203,11 +259,15 @@ describe PageContent do
end end
it "renders strong text" do it "renders strong text" do
page = Page.new(nodes: [ page = Page.new(
Strong.new(children: [ title: "Title",
Text.new("Oh yeah!"), subtitle: nil,
] of Child), nodes: [
] of Child) Strong.new(children: [
Text.new("Oh yeah!"),
] of Child),
] of Child
)
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
@ -215,12 +275,16 @@ describe PageContent do
end end
it "renders an unordered list" do it "renders an unordered list" do
page = Page.new(nodes: [ page = Page.new(
UnorderedList.new(children: [ title: "Title",
ListItem.new(children: [Text.new("Apple")] of Child), subtitle: nil,
ListItem.new(children: [Text.new("Banana")] of Child), nodes: [
] of Child), UnorderedList.new(children: [
] of Child) ListItem.new(children: [Text.new("Apple")] of Child),
ListItem.new(children: [Text.new("Banana")] of Child),
] of Child),
] of Child
)
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
@ -228,9 +292,13 @@ describe PageContent do
end end
it "renders a user anchor" do it "renders a user anchor" do
page = Page.new(nodes: [ page = Page.new(
UserAnchor.new(children: [Text.new("Some User")] of Child, userId: "abc123"), title: "Title",
] of Child) subtitle: nil,
nodes: [
UserAnchor.new(children: [Text.new("Some User")] of Child, userId: "abc123"),
] of Child
)
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string

View File

@ -7,10 +7,9 @@ class Articles::Show < BrowserAction
else else
response = MediumClient.post_data(post_id) response = MediumClient.post_data(post_id)
end end
content = ParagraphConverter.new.convert( page = PageConverter.new.convert(
response.data.post.content.bodyModel.paragraphs response.data.post.content.bodyModel.paragraphs
) )
page = Page.new(nodes: content)
html ShowPage, page: page html ShowPage, page: page
end end
end end

View File

@ -0,0 +1,19 @@
class PageConverter
def convert(paragraphs : Array(PostResponse::Paragraph)) : Page
first_two_paragraphs = paragraphs.first(2)
first_two_types = first_two_paragraphs.map(&.type)
if first_two_types == [PostResponse::ParagraphType::H3, PostResponse::ParagraphType::H4]
Page.new(
title: first_two_paragraphs[0].text,
subtitle: first_two_paragraphs[1].text,
nodes: ParagraphConverter.new.convert(paragraphs[2..]),
)
else
Page.new(
title: first_two_paragraphs[0].text,
subtitle: nil,
nodes: ParagraphConverter.new.convert(paragraphs[1..]),
)
end
end
end

View File

@ -1,6 +1,12 @@
class Page class Page
getter nodes : Nodes::Children getter nodes : Nodes::Children
getter title : String
getter subtitle : String?
def initialize(@nodes : Nodes::Children) def initialize(
@title : String,
@subtitle : String?,
@nodes : Nodes::Children = [] of Nodes::Child
)
end end
end end