はじめに
pomeruというサービスを作りました。
Twitter経由でログインして、フォローしてる人/フォロワーの「紹介文」を簡単に送ることができるといった、Webサービスです。リリース当初は、ネットの著名人にも取り上げられ盛り上がりましたが、現在は少し落ち着き、主にブロガーやイラストレーターの方々にご利用いただいています。
バグ修正や、機能アップデートへ勤しんでいるなかで、下記のようなご要望をいただきました。
・(Twitterの)ツイートボタンみたいに、バナーを押すと小ウインドウが現れ、そこで紹介文を作成できるボタンを自分のサイトに埋め込みたい!
・受け取った紹介文の一覧をタイムラインのように自分のブログに表示したい!
ご要望を頂いてから、「Rails 埋め込み」、「ツイートボタンみたいな 実装」などググってみましたが、ベストプラクティスが見つかりませんでした。とはいえ、結果的にいずれの機能もサービスに追加できたので、今回は「ツイートボタン、タイムライン的なものを実装する」を紹介していきたいと思います。
ヒアドキュメントを使用する
結論、Rubyのヒアドキュメントを使うことにしました。
埋め込み系の機能を追加すると考えて、一番最初に頭をよぎったのが「API化」でした。これまでAPI実装の経験がなく、工数的にも負荷がかかりそうだったので、別の方法を探すことにしました。そこで、友人に相談してみたところ、「ヒアドキュメントはどう?」という提案をいただきました。
ヒアドキュメントについて調べてみた結果、実現したいケースを実装するのにヒアドキュメントでなんら問題はなさそうだったので、早速開発することに。
ちなみに、ヒアドキュメントは、「文字列をプログラミングに埋め込むためのもの」で、通常の文字列による表現とは異なり、改行も埋め込むことができるので、よりフレキシブルに文字列を取り扱うことができます。
参考記事:
Ruby のヒアドキュメントすごい
Rubyでヒアドキュメントを使う
ツイートボタン的なものを実装する
早速、ヒアドキュメントをヘルパーに仕込んでいきます。
<<~LIST
からLIST
に囲まれた部分がヒアドキュメントとして認識されます。
リンクはサイトのものをそのまま流用しています。
・・・
def pomeru_post_btn
<<~LIST
<a href="javascript:void(0);" onclick="window.open('https://pomeru.me/users/#{current_user.slug}/buttons/new', 'mywindow4', 'width=400, height=600, menubar=no, toolbar=no, scrollbars=yes');">
<div class="pbutton">
<span>#{current_user.username}</span>さんをポメる!
</div>
</a>
<link rel="stylesheet" href="xxxxxx.scss">
LIST
end
・・・
「ポメルボタン」を押した時に表示される小ウインドウは、新しいコントローラーとビューを用意しました。
:app/views/buttons/new.html.erb
<div class="main">
<div class="pbbox">
<%= form_for(@review) do |f| %>
<h2><%= @other_user.username %>さんを紹介する</h2>
<form>
<%= f.hidden_field :reviewed_id, value: @other_user.id %>
<%= f.hidden_field :reviewer_id, value: current_user.id %>
<div class="field">
<div class="field-item">
<label>関係性を入力してください</label>
<%= f.text_field :relationship, placeholder: "関係性を書いてね" %>
</div>
</div>
<div class="field">
<label>紹介文を入力してください</label>
<%= f.text_area :content, placeholder: "紹介文を書いてね" %>
</div>
<div class="form-btn">
<%= f.submit "ポメる" %>
</div>
</form>
<% end %>
</div>
</div>
最後に、サービスの利用者が埋め込みタグをコピペできるタグを仕込みます。
ここでいう<%= pomeru_post_btn %>
がヘルパーから呼び起こされているものです。
・・・
<div class="embed-code">
<textarea id="" name="" cols="50" rows="10"><%= pomeru_post_btn %></textarea>
</div>
・・・
タイムライン的なものを実装する
タイムラインを実装する際には、まず専用のビューとコントローラーを用意するところからは始めました。
・・
<div class="pbox">
<div class="ptitle">
<p><a href="https://pomeru.me/users/<%= @user.slug %>" target="_blank"><%= @user.username %></a>さんのポメル</p>
</div>
<div class="pbody">
<ul class="plist">
<%= render partial: 'shared/timeline', collection: @reviews %>
</ul>
</div>
</div>
・・・
:app/views/shared/_timeline.html.erb
<a href="https://pomeru.me/users/<%= timeline.reviewed.slug %>/reviews/<%= timeline.id %>" target="_blank">
<li class="pitem">
<div class="phead">
<div class="picon">
<%= image_tag timeline.reviewer.image_url %>
</div>
<div class="pinfo">
<p class="pname"><%= timeline.reviewer.username %></p>
<p class="prelationship">関係性:<%= timeline.relationship %></p>
<div class="content">
<p><%= timeline.content %></p>
</div>
</div>
</div>
</li>
</a>
上記で実装したボタンと同じように、ヘルパーにヒアドキュメントを書いていきます。
サイト上で表示することになるのでiframe
を使いました。
・・・
def pomeru_timeline
<<~LIST
<iframe src="https://pomeru.me/users/#{current_user.slug}/timeline" width="280" height="460" marginwidth="0" marginheight="0" frameborder="0" style="border:none;"></iframe>
LIST
end
・・・
iframe
を使うには、サイト表示を許可する設定をおこなう必要があります。
response.headers['X-Frame-Options'] = 'ALLOWALL'
のみ指定するとサイト全体の脆弱性にかかわるため、タイムラインのみに指定することをお忘れなく。
before_action :set_allow_to_x_frame_options_header, if: :iframe_allowed_controller?
def iframe_allowed_controller?
controller_name == 'timeline'
end
def set_allow_to_x_frame_options_header
response.headers['X-Frame-Options'] = 'ALLOWALL'
end
・・・
さいごに
ヒアドキュメントを使って、埋め込みタグを作るというのは本来のやり方ではないかもしれません。
スタートアップという、リアルタイムでの対応が求められる現場では、正しく開発する以上に、ユーザーが求めているものを実現するためには、どのやり方が一番早いかが正解なのかなと思っています。
というわけで、今回の記事が良いと思ったら、ぜひ僕のポメルに紹介文を送ってください(笑)