Ruby on Rails Learning Diary

学習したことの整理用です。

【Rails】YoutubeとTwitterをAPIを使わずに記事に埋め込む

実装したいこと

記事投稿アプリで、YoutubeTwitterを記事に埋め込みたい。
URLを入力したら、埋め込み用に変換して、記事に反映させたい。
Slim記法が苦手で、HTMLに反映させるのに手こずってしまいました。。便利ツールを使ってようやくできた。(後述)

データの流れ

  1. ユーザーがYoutubeTwitterを選択し、入力フォームにURLを入力する。
  2. ユーザーの入力した値を受け取り、保存する。
  3. ユーザーが入力した値を記事に埋め込める形式に変換する。
  4. Viewに反映される。

実装の流れ

  1. ユーザーの入力値を受け取るカラムを用意する。
  2. 入力フォームを用意する。
  3. 埋め込みに対応してくれるhtmlを返すviewを用意する。
  4. 受け取ったURLを変換するメソッドを準備する。

カラム準備

ユーザーが入力した値を保存するためのカラムを用意します。

embed_typeYoutubeTwitterを選択します。
identifierでURLを保存します。

class Embed < ApplicationRecord

  enum embed_type: { youtube: 0, twitter: 1 }

  validates :identifier, length: { maximum: 200 }

end

入力フォーム

YoutubeTwitterかを選択し、identifierを入力できるように設定します。
(gem 'simple_form'を使っています。)

(略)
.box-body
    = f.input :embed_type, collect: Embed.embed_types_i18n.invert, include_blank: false
    = f.input :identifier
(略)

View

  1. まず、YoutubeTwitterの公式を調べて、どういう形式のHTMLで表示すれば埋め込みに対応できるのか調べます。
  2. 1で調べたHTMLを返すように、Slim記法で設定します。

Youtube 公式
動画と再生リストを埋め込む - YouTube ヘルプ

Twitter公式
タイムラインを埋め込む方法
Overview | Docs | Twitter Developer Twitter Publish

HTMLをSlimに変換できる
HTML2slim

Youtubeのケース

上記のYoutube公式の動画にも丁寧に解説がありますが、埋め込みたいYoutube動画ページで、「共有」→「<> 埋め込む」を選択すると、埋め込み用のHTMLを用意してくれます。

f:id:Study-Diary:20201017135831p:plain
Youtube用埋め込みHTML

<iframe width="560" height="315" src="https://www.youtube.com/embed/ojdbDYahiCQ" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

私はSlimを使っているので、このHTMLをSlim記法へ変換します。
ただし、とても苦手なので上記のサイトを使っています…

YoutubeのURLを切り取る

Youtubeの埋め込み用URLは以下の構成になっています。

https://www.youtube.com/embed/動画一意のID

最後の/までは固定値として設定し、ユーザーの入力したURLのうち、最後の/以下だけを受け取り、反映させていきます。

最初私は、embed.identifier.last(11)で末尾の11字だけ切り取って実装していたのですが、これだと開始位置を指定したURLを貼り付けられた場合に動かなくなってしまいます。
なので、以下のように、/以下を取得するメソッドを作っておきます。

splitメソッドで、/で区切りをつけた配列を返して、そのlast末尾部分を取得しています。

(略)
  def split_id_from_youtube_url
    # YoutubeならIDのみ抽出
    identifier.split('/').last if youtube?
  end
ruby:
  embed = local_assigns[:embed]
  width = local_assigns[:width] || 853
  height = local_assigns[:height] || 480


.embed-youtube
  = content_tag 'iframe', nil, width: width, height: height, src: "https://www.youtube.com/embed/#{embed.split_id_from_youtube_url}", \
    frameborder: 0, gesture: 'media', allow: 'encrypted-media', allowfullscreen: true


/ Youtube公式に乗っている埋め込み用のHTMLを出力する形にする。
/ #{embed.identifier.last(11)}でIDを切り取ると、開始秒指定されたときにOUT

Twitterのケース

上記の公式に乗っているように進めていきます。
貼り付け用に変換してくれる公式ツールがあるので、ここにツイートを貼り付けて埋め込み用HTMLを生成します。
Twitter Publish

<blockquote class="twitter-tweet">
<p lang="en" dir="ltr">A huge thank you to all of the bosses who are making voting a priority for their employees and giving people the time they need to vote. I’m doing that for my team 😘😘 Please share your voting plans with me!</p>&mdash; Lady Gaga (@ladygaga) 
<a href="https://twitter.com/ladygaga/status/1317133489829937152?ref_src=twsrc%5Etfw">October 16, 2020</a></blockquote> 
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

これの無駄な部分を省いて、この部分があれば良さそう

<blockquote class="twitter-tweet">
<a href="https://twitter.com/ladygaga/status/1317133489829937152?ref_src=twsrc%5Etfw"></a></blockquote>
 <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

これを上記サイトでSlimへ変換して整えればOK

.embed-twitter
  blockquote.twitter-tweet
    a href="#{embed.identifier}"
  script async="" charset="utf-8" src="https://platform.twitter.com/widgets.js"

泥臭くやってしまったけど、もっとスマートな方法があるかもしれないです。。

RSpecメモ

# 更新ボタンが同じページにいくつかあるとき… 
page.all('.box-footer')[0].click_button('更新する')

# Youtubeの埋め込みを確認したいとき
expect(page).to have_selector("iframe[src='https://www.youtube.com/embed/ojdbDYahiCQ']")

使えるRSpec入門・その4「どんなブラウザ操作も自由自在!逆引きCapybara大辞典」 - Qiita