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:
parent
f48f7c2932
commit
05c18f6451
5 changed files with 288 additions and 99 deletions
97
spec/classes/page_converter_spec.cr
Normal file
97
spec/classes/page_converter_spec.cr
Normal 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
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
19
src/classes/page_converter.cr
Normal file
19
src/classes/page_converter.cr
Normal 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
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue