[deploy] Add Reddit Liquid Tag (#8111)
* Implement Reddit liquid tag * Set reddit content in locals * Change Reddit liquid tag content layout I decided a UI similar to the Stack Exchange tag would be better. Now the Reddit tag looks a bit similar. * Add tests for RedditTag * Add reddit liquid tag example to docs Also emphasize notice about compilation of article when using any liquid tag * Parse markdown text reddit content for RedditTag * Display date RedditTag post was published * Remove file location comments * Change order of conditional for Reddit liquid tag Sometimes the attribute `post_hint` might be null and neither condition would apply. * Add example to use Reddit liquid tag * Change reddit svg icon for Reddit liquid tag * handle and verify status of Reddit links Here we make sure the passed link is a valid one in terms of URI composition (e.g. protocol) but also that the URL is one that points to existing content in Reddit. If the URL isn't returning anything but a successful response code (200) then we treat it as an invalid one. * Do not use VCR for RedditTag specs The generated cassette for this tag was really huge (6000+ lines). Using mocks (as in the MediumTag spec) still does the job and without that huge VCR file. * Remove index line from schema.db * Improve RedditJsonFromUrlService - Change User-Agent in requests to one read from app configuration - Remove Reddit URL check by making a GET request - Add regexp to validate URL looks like a Reddit URL * Move RedditJsonFromUrlService methods to RedditTag Also changed the way the request was being mocked. Previously, it was by mocking the service class now I'm mocking HTTParty directly and returning the JSON structure Reddit uses.
This commit is contained in:
parent
fb6685ec1b
commit
3b939f6b9f
8 changed files with 302 additions and 2 deletions
1
app/assets/images/reddit-icon.svg
Normal file
1
app/assets/images/reddit-icon.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="reddit" class="svg-inline--fa fa-reddit fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="#FE5022" d="M201.5 305.5c-13.8 0-24.9-11.1-24.9-24.6 0-13.8 11.1-24.9 24.9-24.9 13.6 0 24.6 11.1 24.6 24.9 0 13.6-11.1 24.6-24.6 24.6zM504 256c0 137-111 248-248 248S8 393 8 256 119 8 256 8s248 111 248 248zm-132.3-41.2c-9.4 0-17.7 3.9-23.8 10-22.4-15.5-52.6-25.5-86.1-26.6l17.4-78.3 55.4 12.5c0 13.6 11.1 24.6 24.6 24.6 13.8 0 24.9-11.3 24.9-24.9s-11.1-24.9-24.9-24.9c-9.7 0-18 5.8-22.1 13.8l-61.2-13.6c-3-.8-6.1 1.4-6.9 4.4l-19.1 86.4c-33.2 1.4-63.1 11.3-85.5 26.8-6.1-6.4-14.7-10.2-24.1-10.2-34.9 0-46.3 46.9-14.4 62.8-1.1 5-1.7 10.2-1.7 15.5 0 52.6 59.2 95.2 132 95.2 73.1 0 132.3-42.6 132.3-95.2 0-5.3-.6-10.8-1.9-15.8 31.3-16 19.8-62.5-14.9-62.5zM302.8 331c-18.2 18.2-76.1 17.9-93.6 0-2.2-2.2-6.1-2.2-8.3 0-2.5 2.5-2.5 6.4 0 8.6 22.8 22.8 87.3 22.8 110.2 0 2.5-2.2 2.5-6.1 0-8.6-2.2-2.2-6.1-2.2-8.3 0zm7.7-75c-13.6 0-24.6 11.1-24.6 24.9 0 13.6 11.1 24.6 24.6 24.6 13.8 0 24.9-11.1 24.9-24.6 0-13.8-11-24.9-24.9-24.9z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -14,3 +14,4 @@
|
|||
@import 'ListingTag';
|
||||
@import 'StackexchangeTag';
|
||||
@import 'WikipediaTag';
|
||||
@import 'RedditTag';
|
||||
|
|
|
|||
98
app/assets/stylesheets/ltags/RedditTag.scss
Normal file
98
app/assets/stylesheets/ltags/RedditTag.scss
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
@import 'variables';
|
||||
|
||||
.ltag__reddit--container {
|
||||
max-width: 620px;
|
||||
border: 1px solid $light-medium-gray;
|
||||
border-radius: 3px;
|
||||
box-shadow: $shadow;
|
||||
margin:1.1em auto 1.30em;
|
||||
padding: 10px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important;
|
||||
overflow: hidden;
|
||||
|
||||
.ltag__reddit--title-container {
|
||||
border-bottom: 1px solid #eaecef;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.ltag__reddit--title {
|
||||
h1 {
|
||||
margin-left: 5px;
|
||||
margin-bottom: 0;
|
||||
margin-top: 8px;
|
||||
font-size: 1.4em;
|
||||
padding-bottom: 0;
|
||||
|
||||
img {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
display: inline-block;
|
||||
vertical-align: -9px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ltag__reddit--post-metadata {
|
||||
font-size: 0.6em;
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
padding: 7px 6px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ltag__reddit--body {
|
||||
padding: 1em 0.5em;
|
||||
max-height: calc(21vw + 165px);
|
||||
overflow: hidden;
|
||||
|
||||
@media screen and (min-width: 430px) {
|
||||
max-height: calc(20vw + 153px);
|
||||
}
|
||||
@media screen and (min-width: 800px) {
|
||||
max-height: 310px;
|
||||
}
|
||||
|
||||
h1,h2,h3,h4,h5,h6 {
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0px;
|
||||
padding: 0px;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ltag__reddit--btn--container {
|
||||
padding: 0.1em 0 1.15em;
|
||||
z-index: 100;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
box-shadow: 0px 0px 60px 42px #fff;
|
||||
}
|
||||
|
||||
.ltag__reddit--btn {
|
||||
color: #0366d6;
|
||||
background-color: #f1f8ff;
|
||||
border-radius: 3px;
|
||||
line-height: 20px;
|
||||
padding: 0.25em 1.2em;
|
||||
opacity: 0.9;
|
||||
font-weight: bold;
|
||||
border: 1px solid #0366d6;
|
||||
font-size: 0.75em;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
73
app/liquid_tags/reddit_tag.rb
Normal file
73
app/liquid_tags/reddit_tag.rb
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
class RedditTag < LiquidTagBase
|
||||
include ActionView::Helpers::SanitizeHelper
|
||||
|
||||
PARTIAL = "liquids/reddit".freeze
|
||||
URL_REGEXP = /\Ahttps\:\/\/(www.)?reddit.com/.freeze
|
||||
|
||||
def initialize(_tag_name, url, _tokens)
|
||||
@url = ActionController::Base.helpers.strip_tags(url).strip
|
||||
@reddit_content = parse_url
|
||||
end
|
||||
|
||||
def render(_context)
|
||||
ActionController::Base.new.render_to_string(
|
||||
partial: PARTIAL,
|
||||
locals: {
|
||||
author: @reddit_content[:author],
|
||||
title: @reddit_content[:title],
|
||||
post_url: @reddit_content[:post_url],
|
||||
created_at: @reddit_content[:created_at],
|
||||
post_hint: @reddit_content[:post_hint],
|
||||
image_url: @reddit_content[:image_url],
|
||||
thumbnail: @reddit_content[:thumbnail],
|
||||
html_text: @reddit_content[:selftext_html],
|
||||
markdown_text: @reddit_content[:selftext]
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_url
|
||||
validate_url
|
||||
|
||||
# Requests to Reddit require a custom `User-Agent` header to prevent 429 errors
|
||||
json = HTTParty.get("#{@url}.json", headers: { "User-Agent" => "#{ApplicationConfig['COMMUNITY_NAME']} (#{URL.url})" })
|
||||
|
||||
# The JSON response is an array with two items.
|
||||
# The first one is the post itself, the second one are the comments
|
||||
data = json.first["data"]["children"][0]["data"]
|
||||
|
||||
{
|
||||
author: data["author"],
|
||||
title: data["title"],
|
||||
post_url: @url,
|
||||
created_at: Time.zone.at(data["created_utc"]).strftime("%b %e '%y"),
|
||||
post_hint: data["post_hint"],
|
||||
image_url: data["url"],
|
||||
thumbnail: data["thumbnail"],
|
||||
selftext: parse_markdown_content(data["selftext"]),
|
||||
selftext_html: data["selftext_html"]
|
||||
}
|
||||
end
|
||||
|
||||
def parse_markdown_content(content)
|
||||
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTMLRouge, REDCARPET_CONFIG)
|
||||
text = markdown.render(content)
|
||||
|
||||
sanitize(HTML_Truncator.truncate(text, 60))
|
||||
end
|
||||
|
||||
def validate_url
|
||||
return true if valid_url?(@url.delete(" ")) && (@url =~ URL_REGEXP)&.zero?
|
||||
|
||||
raise StandardError, "Invalid Reddit link: #{@url}"
|
||||
end
|
||||
|
||||
def valid_url?(url)
|
||||
url = URI.parse(url)
|
||||
url.is_a?(URI::HTTP)
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag("reddit", RedditTag)
|
||||
32
app/views/liquids/_reddit.html.erb
Normal file
32
app/views/liquids/_reddit.html.erb
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<div class="ltag__reddit--container">
|
||||
<div class="ltag__reddit--title-container">
|
||||
<header>
|
||||
<div class="ltag__reddit--title">
|
||||
<h1>
|
||||
<img src="<%= asset_path("reddit-icon.svg") %>" alt="Reddit Logo" />
|
||||
<a href="<%= post_url %>" target="_blank" rel="noopener noreferrer">
|
||||
<%= title %>
|
||||
</a>
|
||||
</h1>
|
||||
<div class="ltag__reddit--post-metadata">
|
||||
<span><%= created_at %></span>
|
||||
<span>Author: <%= author %></span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
<div class="ltag__reddit--body">
|
||||
<p>
|
||||
<% if post_hint == 'image' %>
|
||||
<img src="<%= thumbnail %>" alt="<%= title %>">
|
||||
<% else %>
|
||||
<%= markdown_text %>
|
||||
<% end %>
|
||||
</p>
|
||||
</div>
|
||||
<div class="ltag__reddit--btn--container">
|
||||
<button class="ltag__reddit--btn" type="button">
|
||||
<a href="<%= post_url %>" target="_blank" rel="noopener noreferrer">See Full Post</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -342,6 +342,13 @@
|
|||
<h3><strong>Asciinema Embed</strong></h3>
|
||||
<p>All you need is the Asciinema id:</p>
|
||||
<code>{% asciinema 239367 %}</code>
|
||||
|
||||
<h3><strong>Reddit Tag</strong></h3>
|
||||
<p>Enter the full URL of the post you want to embed</p>
|
||||
<pre>
|
||||
{% reddit https://www.reddit.com/r/aww/comments/ag3s4b/ive_waited_28_years_to_finally_havr_my_first_pet %}
|
||||
</pre>
|
||||
|
||||
<h3><strong>Parsing Liquid Tags as a Code Example</strong></h3>
|
||||
<p>To parse Liquid tags as code, simply wrap it with a single backtick or triple backticks.</p>
|
||||
<p><code>`{% mytag %}{{ site.SOMETHING }}{% endmytag %}`</code></p>
|
||||
|
|
|
|||
|
|
@ -24,8 +24,7 @@ Develop them with that mindset in terms of naming things. They should be
|
|||
documented but also intuitive. They should also be fairly flexible in the
|
||||
arguments they take. Currently, this could use improvements.
|
||||
|
||||
Liquid tags are "compiled" when an article is saved. So you will need to re-save
|
||||
articles to see HTML changes.
|
||||
_Note: Liquid tags are "compiled" when an article is saved. So you will need to re-save articles to see HTML changes._
|
||||
|
||||
Here is a bunch of liquid tags supported on DEV:
|
||||
|
||||
|
|
@ -54,6 +53,7 @@ Here is a bunch of liquid tags supported on DEV:
|
|||
{% blogcast 1234 %}
|
||||
{% kotlin https://pl.kotl.in/owreUFFUG %}
|
||||
{% wikipedia https://en.wikipedia.org/wiki/Wikipedia %}
|
||||
{% reddit https://www.reddit.com/r/aww/comments/ag3s4b/ive_waited_28_years_to_finally_havr_my_first_pet %}
|
||||
```
|
||||
|
||||
## How liquid tags are developed
|
||||
|
|
|
|||
88
spec/liquid_tags/reddit_tag_spec.rb
Normal file
88
spec/liquid_tags/reddit_tag_spec.rb
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe RedditTag, type: :liquid_tag do
|
||||
context "when it is an image post" do
|
||||
describe "#render" do
|
||||
let(:image_post) { "https://www.reddit.com/r/aww/comments/ag3s4b/ive_waited_28_years_to_finally_havr_my_first_pet" }
|
||||
let(:response) do
|
||||
{
|
||||
"author" => "Miaogua007",
|
||||
"title" => "I've waited 28 years to …Everyone, meet Mycroft.",
|
||||
"post_url" => "https://www.reddit.com/r/aww/comments/ag3s4b/ive_waited_28_years_to_finally_havr_my_first_pet",
|
||||
"created_utc" => 1_547_520_425,
|
||||
"post_hint" => "image",
|
||||
"image_url" => "https://i.redd.it/jpqodmc83ia21.jpg",
|
||||
"thumbnail" => "https://b.thumbs.redditm…ge_mn1MHd6uirdou8H3o.jpg",
|
||||
"selftext" => "",
|
||||
"selftext_html" => nil
|
||||
}
|
||||
end
|
||||
|
||||
def generate_reddit_liquid(url)
|
||||
Liquid::Template.register_tag("reddit", RedditTag)
|
||||
Liquid::Template.parse("{% reddit #{url} %}")
|
||||
end
|
||||
|
||||
before do
|
||||
allow(HTTParty).to receive(:get).and_return([
|
||||
{
|
||||
"data" => {
|
||||
"children" => [
|
||||
{ "data" => response },
|
||||
]
|
||||
}
|
||||
},
|
||||
])
|
||||
end
|
||||
|
||||
it "renders reddit content" do
|
||||
reddit_liquid = generate_reddit_liquid(image_post)
|
||||
expect(reddit_liquid.render).to include("ltag__reddit")
|
||||
expect(reddit_liquid.render).to include(response["author"])
|
||||
expect(reddit_liquid.render).to include(response["thumbnail"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when it is a text post" do
|
||||
describe "#render" do
|
||||
let(:text_post) { "https://www.reddit.com/r/IAmA/comments/afvl2w/im_scott_from_scotts_cheap_flights_my_profession" }
|
||||
let(:response) do
|
||||
{
|
||||
"author" => "scottkeyes",
|
||||
"title" => "I'm Scott from Scott's C…r the next 8 hours. AMA",
|
||||
"post_url" => "https://www.reddit.com/r/IAmA/comments/afvl2w/im_scott_from_scotts_cheap_flights_my_profession",
|
||||
"created_utc" => 1_547_470_871,
|
||||
"post_hint" => "self",
|
||||
"image_url" => "",
|
||||
"thumbnail" => "self",
|
||||
"selftext" => "I may have the world’s b…year for cheap flights!!",
|
||||
"selftext_html" => ""
|
||||
}
|
||||
end
|
||||
|
||||
def generate_reddit_liquid(url)
|
||||
Liquid::Template.register_tag("reddit", RedditTag)
|
||||
Liquid::Template.parse("{% reddit #{url} %}")
|
||||
end
|
||||
|
||||
before do
|
||||
allow(HTTParty).to receive(:get).and_return([
|
||||
{
|
||||
"data" => {
|
||||
"children" => [
|
||||
{ "data" => response },
|
||||
]
|
||||
}
|
||||
},
|
||||
])
|
||||
end
|
||||
|
||||
it "renders reddit content" do
|
||||
reddit_liquid = generate_reddit_liquid(text_post)
|
||||
expect(reddit_liquid.render).to include("ltag__reddit")
|
||||
expect(reddit_liquid.render).to include(response["author"])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue