diff --git a/shard.lock b/shard.lock
index 42a8367..124a443 100644
--- a/shard.lock
+++ b/shard.lock
@@ -60,6 +60,10 @@ shards:
git: https://github.com/luckyframework/lucky_task.git
version: 0.1.0
+ monads:
+ git: https://github.com/alex-lairan/monads.git
+ version: 1.0.0
+
pg:
git: https://github.com/will/crystal-pg.git
version: 0.23.2
diff --git a/shard.yml b/shard.yml
index 64132a9..54fdc4a 100644
--- a/shard.yml
+++ b/shard.yml
@@ -26,6 +26,8 @@ dependencies:
lucky_task:
github: luckyframework/lucky_task
version: ~> 0.1.0
+ monads:
+ github: alex-lairan/monads
development_dependencies:
lucky_flow:
github: luckyframework/lucky_flow
diff --git a/spec/classes/embedded_converter_spec.cr b/spec/classes/embedded_converter_spec.cr
new file mode 100644
index 0000000..4b7ec07
--- /dev/null
+++ b/spec/classes/embedded_converter_spec.cr
@@ -0,0 +1,99 @@
+require "../spec_helper"
+
+include Nodes
+
+describe EmbeddedConverter do
+ context "when the mediaResource has an iframeSrc value" do
+ it "returns an EmbeddedContent node" do
+ paragraph = PostResponse::Paragraph.from_json <<-JSON
+ {
+ "text": "",
+ "type": "IFRAME",
+ "href": null,
+ "layout": "INSET_CENTER",
+ "markups": [],
+ "iframe": {
+ "mediaResource": {
+ "id": "abc123",
+ "href": "https://twitter.com/user/status/1",
+ "iframeSrc": "https://cdn.embedly.com/widgets/...",
+ "iframeWidth": 500,
+ "iframeHeight": 281
+ }
+ },
+ "metadata": null
+ }
+ JSON
+
+ result = EmbeddedConverter.convert(paragraph)
+
+ result.should eq(
+ EmbeddedContent.new(
+ src: "https://cdn.embedly.com/widgets/...",
+ originalWidth: 500,
+ originalHeight: 281,
+ )
+ )
+ end
+ end
+
+ context "when the mediaResource has a blank iframeSrc value" do
+ context "and the href is unknown" do
+ it "returns an EmbeddedLink node" do
+ paragraph = PostResponse::Paragraph.from_json <<-JSON
+ {
+ "text": "",
+ "type": "IFRAME",
+ "href": null,
+ "layout": "INSET_CENTER",
+ "markups": [],
+ "iframe": {
+ "mediaResource": {
+ "id": "abc123",
+ "href": "https://example.com",
+ "iframeSrc": "",
+ "iframeWidth": 0,
+ "iframeHeight": 0
+ }
+ },
+ "metadata": null
+ }
+ JSON
+
+ result = EmbeddedConverter.convert(paragraph)
+
+ result.should eq(EmbeddedLink.new(href: "https://example.com"))
+ end
+ end
+
+ context "and the href is gist.github.com" do
+ it "returns an GithubGist node" do
+ paragraph = PostResponse::Paragraph.from_json <<-JSON
+ {
+ "text": "",
+ "type": "IFRAME",
+ "href": null,
+ "layout": "INSET_CENTER",
+ "markups": [],
+ "iframe": {
+ "mediaResource": {
+ "id": "abc123",
+ "href": "https://gist.github.com/user/someid",
+ "iframeSrc": "",
+ "iframeWidth": 0,
+ "iframeHeight": 0
+ }
+ },
+ "metadata": null
+ }
+ JSON
+
+ result = EmbeddedConverter.convert(paragraph)
+
+ result.should eq(
+ GithubGist.new(href: "https://gist.github.com/user/someid")
+ )
+ end
+ end
+ end
+end
diff --git a/spec/classes/paragraph_converter_spec.cr b/spec/classes/paragraph_converter_spec.cr
index 1d31bbb..bac37f5 100644
--- a/spec/classes/paragraph_converter_spec.cr
+++ b/spec/classes/paragraph_converter_spec.cr
@@ -272,7 +272,10 @@ describe ParagraphConverter do
"markups": [],
"iframe": {
"mediaResource": {
- "href": "https://example.com"
+ "href": "https://example.com",
+ "iframeSrc": "",
+ "iframeWidth": 0,
+ "iframeHeight": 0
}
},
"layout": null,
@@ -312,7 +315,7 @@ describe ParagraphConverter do
Image.new(src: "1*miroimage.png", originalWidth: 618, originalHeight: 682),
FigureCaption.new(children: [Text.new("text")] of Child),
] of Child),
- IFrame.new(href: "https://example.com"),
+ EmbeddedLink.new(href: "https://example.com"),
MixtapeEmbed.new(children: [
Anchor.new(
children: [Text.new("Mixtape")] of Child,
diff --git a/spec/components/page_content_spec.cr b/spec/components/page_content_spec.cr
index bbb5877..6272a43 100644
--- a/spec/components/page_content_spec.cr
+++ b/spec/components/page_content_spec.cr
@@ -152,6 +152,24 @@ describe PageContent do
HTML
end
+ it "renders a GitHub Gist" do
+ page = Page.new(
+ title: "Title",
+ subtitle: nil,
+ author: "Author",
+ created_at: Time.local,
+ nodes: [
+ GithubGist.new(href: "https://gist.github.com/user/some_id"),
+ ] of Child
+ )
+
+ html = PageContent.new(page: page).render_to_string
+
+ html.should eq stripped_html <<-HTML
+
+ HTML
+ end
+
it "renders an H3" do
page = Page.new(
title: "Title",
@@ -210,7 +228,32 @@ describe PageContent do
HTML
end
- it "renders an iframe container" do
+ it "renders embedded content" do
+ page = Page.new(
+ title: "Title",
+ subtitle: nil,
+ author: "Author",
+ created_at: Time.local,
+ nodes: [
+ EmbeddedContent.new(
+ src: "https://example.com",
+ originalWidth: 1000,
+ originalHeight: 600,
+ ),
+ ] of Child
+ )
+
+ html = PageContent.new(page: page).render_to_string
+
+ html.should eq stripped_html <<-HTML
+
+
+
+ HTML
+ end
+
+ it "renders an embedded link container" do
page = Page.new(
title: "Title",
subtitle: nil,
@@ -218,7 +261,7 @@ describe PageContent do
created_at: Time.local,
nodes: [
Paragraph.new(children: [
- IFrame.new(href: "https://example.com"),
+ EmbeddedLink.new(href: "https://example.com"),
] of Child),
] of Child
)
diff --git a/spec/models/nodes_spec.cr b/spec/models/nodes_spec.cr
index f9a43ac..1eb20b1 100644
--- a/spec/models/nodes_spec.cr
+++ b/spec/models/nodes_spec.cr
@@ -1,9 +1,9 @@
require "../spec_helper"
module Nodes
- describe IFrame do
+ describe EmbeddedLink do
it "returns embedded url with subdomains" do
- iframe = IFrame.new(href: "https://dev.example.com/page")
+ iframe = EmbeddedLink.new(href: "https://dev.example.com/page")
iframe.domain.should eq("dev.example.com")
end
@@ -23,4 +23,17 @@ module Nodes
image.src.should eq("https://cdn-images-1.medium.com/fit/c/800/482/image.png")
end
end
+
+ describe EmbeddedContent do
+ it "adjusts the width and height proportionally" do
+ content = EmbeddedContent.new(
+ src: "https://example.com",
+ originalWidth: 1000,
+ originalHeight: 600,
+ )
+
+ content.width.should eq("800")
+ content.height.should eq("480")
+ end
+ end
end
diff --git a/src/classes/embedded_converter.cr b/src/classes/embedded_converter.cr
new file mode 100644
index 0000000..6848bfc
--- /dev/null
+++ b/src/classes/embedded_converter.cr
@@ -0,0 +1,42 @@
+class EmbeddedConverter
+ include Nodes
+
+ GIST_HOST = "https://gist.github.com"
+
+ getter paragraph : PostResponse::Paragraph
+
+ def self.convert(paragraph : PostResponse::Paragraph) : Embedded | Empty
+ new(paragraph).convert
+ end
+
+ def initialize(@paragraph : PostResponse::Paragraph)
+ end
+
+ def convert : Embedded | Empty
+ Monads::Try(PostResponse::IFrame).new(->{ paragraph.iframe })
+ .to_maybe
+ .fmap(->(iframe : PostResponse::IFrame) { iframe.mediaResource })
+ .fmap(->media_to_embedded(PostResponse::MediaResource))
+ .value_or(Empty.new)
+ end
+
+ private def media_to_embedded(media : PostResponse::MediaResource) : Embedded
+ if media.iframeSrc.blank?
+ custom_embed(media)
+ else
+ EmbeddedContent.new(
+ src: media.iframeSrc,
+ originalWidth: media.iframeWidth,
+ originalHeight: media.iframeHeight
+ )
+ end
+ end
+
+ private def custom_embed(media : PostResponse::MediaResource) : Embedded
+ if media.href.starts_with?(GIST_HOST)
+ GithubGist.new(href: media.href)
+ else
+ EmbeddedLink.new(href: media.href)
+ end
+ end
+end
diff --git a/src/classes/paragraph_converter.cr b/src/classes/paragraph_converter.cr
index 0596aff..6070cf6 100644
--- a/src/classes/paragraph_converter.cr
+++ b/src/classes/paragraph_converter.cr
@@ -20,11 +20,7 @@ class ParagraphConverter
node = Heading3.new(children: children)
when PostResponse::ParagraphType::IFRAME
paragraph = paragraphs.shift
- if iframe = paragraph.iframe
- node = IFrame.new(href: iframe.mediaResource.href)
- else
- node = Empty.new
- end
+ node = EmbeddedConverter.convert(paragraph)
when PostResponse::ParagraphType::IMG
paragraph = paragraphs.shift
node = convert_img(paragraph)
diff --git a/src/clients/medium_client.cr b/src/clients/medium_client.cr
index 60fe41a..1898497 100644
--- a/src/clients/medium_client.cr
+++ b/src/clients/medium_client.cr
@@ -43,6 +43,9 @@ class MediumClient
iframe {
mediaResource {
href
+ iframeSrc
+ iframeWidth
+ iframeHeight
}
}
metadata {
diff --git a/src/components/page_content.cr b/src/components/page_content.cr
index 248e544..980b424 100644
--- a/src/components/page_content.cr
+++ b/src/components/page_content.cr
@@ -29,6 +29,26 @@ class PageContent < BaseComponent
raw ""
end
+ def render_child(child : EmbeddedContent)
+ div class: "iframe-wrapper" do
+ iframe(
+ src: child.src,
+ width: child.width,
+ height: child.height,
+ frameborder: "0",
+ allowfullscreen: true,
+ )
+ end
+ end
+
+ def render_child(child : EmbeddedLink)
+ div class: "embedded" do
+ a href: child.href do
+ text "Embedded content at #{child.domain}"
+ end
+ end
+ end
+
def render_child(node : Emphasis)
em { render_children(node.children) }
end
@@ -55,6 +75,10 @@ class PageContent < BaseComponent
end
end
+ def render_child(child : GithubGist)
+ script src: child.src
+ end
+
def render_child(node : Heading2)
h2 { render_children(node.children) }
end
@@ -63,14 +87,6 @@ class PageContent < BaseComponent
h3 { render_children(node.children) }
end
- def render_child(child : IFrame)
- div class: "embedded" do
- a href: child.href do
- text "Embedded content at #{child.domain}"
- end
- end
- end
-
def render_child(child : Image)
img src: child.src, width: child.width
end
diff --git a/src/models/nodes.cr b/src/models/nodes.cr
index 3c141d8..4721e33 100644
--- a/src/models/nodes.cr
+++ b/src/models/nodes.cr
@@ -1,5 +1,6 @@
module Nodes
- alias Leaf = Text | Image | IFrame
+ alias Embedded = EmbeddedLink | EmbeddedContent | GithubGist
+ alias Leaf = Text | Image | Embedded
alias Child = Container | Leaf | Empty
alias Children = Array(Child)
@@ -120,7 +121,40 @@ module Nodes
end
end
- class IFrame
+ class EmbeddedContent
+ MAX_WIDTH = 800
+
+ getter src : String
+
+ def initialize(@src : String, @originalWidth : Int32, @originalHeight : Int32)
+ end
+
+ def width
+ [@originalWidth, MAX_WIDTH].min.to_s
+ end
+
+ def height
+ if @originalWidth > MAX_WIDTH
+ (@originalHeight * ratio).round.to_i.to_s
+ else
+ @originalHeight.to_s
+ end
+ end
+
+ private def ratio
+ MAX_WIDTH / @originalWidth
+ end
+
+ def ==(other : EmbeddedContent)
+ other.src == src && other.width == width && other.height == height
+ end
+
+ def empty?
+ false
+ end
+ end
+
+ class EmbeddedLink
getter href : String
def initialize(@href : String)
@@ -130,7 +164,7 @@ module Nodes
URI.parse(href).host
end
- def ==(other : IFrame)
+ def ==(other : EmbeddedLink)
other.href == href
end
@@ -171,4 +205,21 @@ module Nodes
false
end
end
+
+ class GithubGist
+ def initialize(@href : String)
+ end
+
+ def src
+ "#{@href}.js"
+ end
+
+ def ==(other : GithubGist)
+ other.src == src
+ end
+
+ def empty?
+ false
+ end
+ end
end
diff --git a/src/models/post_response.cr b/src/models/post_response.cr
index d1411de..28c6768 100644
--- a/src/models/post_response.cr
+++ b/src/models/post_response.cr
@@ -82,6 +82,9 @@ class PostResponse
class MediaResource < Base
property href : String
+ property iframeSrc : String
+ property iframeWidth : Int32
+ property iframeHeight : Int32
end
class Metadata < Base
diff --git a/src/shards.cr b/src/shards.cr
index ac3bf5c..30fb81f 100644
--- a/src/shards.cr
+++ b/src/shards.cr
@@ -7,3 +7,4 @@ require "avram"
require "lucky"
require "carbon"
require "authentic"
+require "monads"