[deploy] Implement ImageResizer module (#9652)

* Create ImageResizer module

* Refactor HtmlVariant

* Refactor CloudCoverUrl

* Fix broken spec

* Refactor PodcastEpisode

* Refactor Message

* Fix broken spec

* Refactor Articles::SocialImage

* Refactor ProfileImage & add spec

* Fix broken spec
This commit is contained in:
Mac Siri 2020-08-07 11:04:31 -04:00 committed by GitHub
parent 1c566e0ec4
commit 3c5b01d490
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 77 additions and 106 deletions

View file

@ -1,30 +1,15 @@
class ProfileImage
include CloudinaryHelper
attr_accessor :resource, :image_link, :backup_link
BACKUP_LINK = "https://thepracticaldev.s3.amazonaws.com/i/99mvlsfu5tfj9m7ku25d.png".freeze
attr_accessor :image_link
def initialize(resource)
@resource = resource
@image_link = resource.profile_image_url
@backup_link = "https://thepracticaldev.s3.amazonaws.com/i/99mvlsfu5tfj9m7ku25d.png"
end
def get(width: 120)
cl_image_path(get_link,
type: "fetch",
crop: "fill",
width: width,
height: width,
quality: "auto",
flags: "progressive",
fetch_format: "auto",
sign_url: true)
end
def get_link
image_link || backup_link
end
def get_external_link
image_link ? "#{ApplicationConfig['APP_PROTOCOL']}#{ApplicationConfig['APP_DOMAIN']}#{image_link}" : backup_link
ImageResizer.call(image_link || BACKUP_LINK, width: width, height: width, crop: "fill")
end
end

22
app/lib/image_resizer.rb Normal file
View file

@ -0,0 +1,22 @@
module ImageResizer
DEFAULT_CL_OPTIONS = {
type: "fetch",
height: nil,
width: nil,
crop: "limit",
quality: "auto",
flags: "progressive",
fetch_format: "auto",
sign_url: true
}.freeze
def self.call(img_src, **kwargs)
options = DEFAULT_CL_OPTIONS.merge(kwargs).reject { |_, v| v.blank? }
if img_src&.include?(".gif")
options[:quality] = 66
end
ActionController::Base.helpers.cl_image_path(img_src, options)
end
end

View file

@ -1,6 +1,4 @@
class HtmlVariant < ApplicationRecord
include CloudinaryHelper
GROUP_NAMES = %w[article_show_below_article_cta badge_landing_page campaign].freeze
validates :html, presence: true
@ -66,7 +64,7 @@ class HtmlVariant < ApplicationRecord
img["src"] = if Giphy::Image.valid_url?(src)
src.gsub("https://media.", "https://i.")
else
img_of_size(src, 420)
ImageResizer.call(src, width: 420).gsub(",", "%2C")
end
end
self.html = doc.to_html
@ -75,20 +73,4 @@ class HtmlVariant < ApplicationRecord
def allowed_image_host?(src)
src.start_with?("https://res.cloudinary.com/")
end
def img_of_size(source, width = 420)
quality = if source && (source.include? ".gif")
66
else
"auto"
end
cl_image_path(source,
type: "fetch",
width: width,
crop: "limit",
quality: quality,
flags: "progressive",
fetch_format: "auto",
sign_url: true).gsub(",", "%2C")
end
end

View file

@ -175,14 +175,7 @@ class Message < ApplicationRecord
# rubocop:enable Rails/OutputSafety
def cl_path(img_src)
ActionController::Base.helpers
.cl_image_path(img_src,
type: "fetch",
width: 725,
crop: "limit",
flags: "progressive",
fetch_format: "auto",
sign_url: true)
ImageResizer.call(img_src, width: 725)
end
def determine_user_validity

View file

@ -113,18 +113,7 @@ class PodcastEpisode < ApplicationRecord
next unless img_src
quality = "auto"
quality = 66 if img_src.include?(".gif")
cloudinary_img_src = ActionController::Base.helpers
.cl_image_path(img_src,
type: "fetch",
width: 725,
crop: "limit",
quality: quality,
flags: "progressive",
fetch_format: "auto",
sign_url: true)
cloudinary_img_src = ImageResizer.call(img_src, width: 725)
self.processed_html = processed_html.gsub(img_src, cloudinary_img_src)
end
end

View file

@ -1,7 +1,6 @@
module Articles
class SocialImage
include Rails.application.routes.url_helpers
include CloudinaryHelper
SOCIAL_PREVIEW_MIGRATION_DATETIME = Time.zone.parse("2019-04-22T00:00:00Z")
@ -15,15 +14,7 @@ module Articles
image = user_defined_image
if image.present?
image = image.split("w_1000/").last if image.include?("w_1000/https://")
return cl_image_path(image,
type: "fetch",
width: width,
height: height,
crop: "imagga_scale",
quality: "auto",
flags: "progressive",
fetch_format: "auto",
sign_url: true)
return ImageResizer.call(image, width: width, height: height, crop: "imagga_scale")
end
return legacy_article_social_image unless use_new_social_url?
@ -41,15 +32,7 @@ module Articles
src = GeneratedImage.new(article).social_image
return src if src.start_with? "https://res.cloudinary.com/"
cl_image_path(src,
type: "fetch",
width: "1000",
height: "500",
crop: "imagga_scale",
quality: "auto",
flags: "progressive",
fetch_format: "auto",
sign_url: true)
ImageResizer.call(src, width: "1000", height: "500", crop: "imagga_scale")
end
end

View file

@ -1,5 +1,4 @@
class CloudCoverUrl
include CloudinaryHelper
include ActionView::Helpers::AssetUrlHelper
def initialize(url)
@ -11,18 +10,9 @@ class CloudCoverUrl
return url if Rails.env.development?
width = 1000
height = 420
quality = "auto"
img_src = url_without_prefix_nesting(url, width)
cl_image_path(url_without_prefix_nesting(url, width),
type: "fetch",
width: width,
height: height,
crop: "imagga_scale",
quality: quality,
flags: "progressive",
fetch_format: "auto",
sign_url: true)
ImageResizer.call(img_src, width: width, height: 420, crop: "imagga_scale")
end
private

View file

@ -47,6 +47,7 @@ describe SocialImageHelper do
url = helper.article_social_image_url(article)
expect(url).to match(/#{article.main_image}/)
expect(url).to include("c_imagga_scale,f_auto,fl_progressive,h_500,q_auto,w_1000/")
end
it "returns older url2png image if already generated" do

View file

@ -0,0 +1,18 @@
require "rails_helper"
RSpec.describe ProfileImage, type: :labor do
describe "#get" do
it "returns user profile_image_url" do
user = build_stubbed(:user)
expect(described_class.new(user).get).to eq(user.profile_image_url)
end
context "when user has no profile_image" do
it "returns backup image prefixed with Cloudinary" do
user = build_stubbed(:user, profile_image: nil)
correct_prefix = "/c_fill,f_auto,fl_progressive,h_120,q_auto,w_120/"
expect(described_class.new(user).get).to include(correct_prefix + described_class::BACKUP_LINK)
end
end
end
end

View file

@ -40,6 +40,6 @@ RSpec.describe HtmlVariant, type: :model do
it "prefixes an image with cloudinary" do
html = "<div><img src='https://devimages.com/image.jpg' /></div>"
html_variant.update(approved: false, html: html)
expect(html_variant.html).to include("cloudinary")
expect(html_variant.html).to include("/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_420/https://devimages.com/image.jpg")
end
end

View file

@ -46,8 +46,11 @@ RSpec.describe Message, type: :model do
message.message_markdown = "hello http://#{ApplicationConfig['APP_DOMAIN']}#{article.path}"
message.validate!
expect(message.message_html).to include(article.title)
expect(message.message_html).to include("sidecar-article")
expect(message.message_html).to include(
article.title,
"sidecar-article",
"/c_limit,f_auto,fl_progressive,q_auto,w_725/",
)
end
it "creates target blank link" do

View file

@ -112,7 +112,10 @@ RSpec.describe PodcastEpisode, type: :model do
image_url = "https://dummyimage.com/10x10"
podcast_episode.body = "<img src=\"#{image_url}\">"
podcast_episode.validate!
expect(podcast_episode.processed_html.include?("res.cloudinary.com")).to be(true)
expect(podcast_episode.processed_html).to include(
"res.cloudinary.com",
"c_limit,f_auto,fl_progressive,q_auto,w_725/https://dummyimage.com/10x10",
)
end
it "chooses the appropriate quality for an image" do

View file

@ -2,27 +2,29 @@ require "rails_helper"
RSpec.describe CloudCoverUrl, type: :view_object do
let(:article) { create(:article) }
let(:cloudinary_prefix) { "https://res.cloudinary.com/TEST-CLOUD/image/fetch/" }
it "returns proper url" do
expect(described_class.new(article.main_image).call).to start_with("https://res.cloudinary.com/TEST-CLOUD/image/fetch/")
expect(described_class.new(article.main_image).call).to include("/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://robohash.org/")
expect(described_class.new(article.main_image).call)
.to start_with(cloudinary_prefix)
.and include("/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://robohash.org/")
end
it "returns proper url when nested cloudinary" do
article.update_column(
:main_image,
"https://res.cloudinary.com/practicaldev/image/fetch/s--A-gun7rr--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://res.cloudinary.com/practicaldev/image/fetch/s--hcD8ZkbP--/c_imagga_scale%2Cf_auto%2Cfl_progressive%2Ch_420%2Cq_auto%2Cw_1000/https://dev-to-uploads.s3.amazonaws.com/i/th93d625o27nuz63oeen.png")
image_url = "https://res.cloudinary.com/practicaldev/image/fetch/s--A-gun7rr--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://res.cloudinary.com/practicaldev/image/fetch/s--hcD8ZkbP--/c_imagga_scale%2Cf_auto%2Cfl_progressive%2Ch_420%2Cq_auto%2Cw_1000/https://dev-to-uploads.s3.amazonaws.com/i/th93d625o27nuz63oeen.png"
expect(described_class.new(article.main_image).call).to start_with("https://res.cloudinary.com/TEST-CLOUD/image/fetch/")
expect(described_class.new(article.main_image).call).to end_with("/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/i/th93d625o27nuz63oeen.png")
article.update_column(:main_image, image_url)
expect(described_class.new(article.main_image).call)
.to start_with(cloudinary_prefix)
.and end_with("/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/i/th93d625o27nuz63oeen.png")
end
it "returns proper url when single cloudinary" do
article.update_column(
:main_image,
"https://res.cloudinary.com/practicaldev/image/fetch/s--hcD8ZkbP--/c_imagga_scale%2Cf_auto%2Cfl_progressive%2Ch_420%2Cq_auto%2Cw_1000/https://dev-to-uploads.s3.amazonaws.com/i/th93d625o27nuz63oeen.png")
image_url = "https://res.cloudinary.com/practicaldev/image/fetch/s--hcD8ZkbP--/c_imagga_scale%2Cf_auto%2Cfl_progressive%2Ch_420%2Cq_auto%2Cw_1000/https://dev-to-uploads.s3.amazonaws.com/i/th93d625o27nuz63oeen.png"
expect(described_class.new(article.main_image).call).to start_with("https://res.cloudinary.com/TEST-CLOUD/image/fetch/")
expect(described_class.new(article.main_image).call).to end_with("/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/i/th93d625o27nuz63oeen.png")
article.update_column(:main_image, image_url)
expect(described_class.new(article.main_image).call)
.to start_with(cloudinary_prefix)
.and end_with("/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/i/th93d625o27nuz63oeen.png")
end
end