Audiaブログ
Audiaの最新情報を掲載しています。
https://www.audia.jp/blog
2022-02-21T09:00:00+09:00
Kohei Hayashi
Ruby on Rails 6.0から6.1へのアップグレード
https://www.audia.jp/blog/upgrading-rails-from-6-0-to-6-1/
2022-02-21T09:00:00+09:00
2022-02-24T11:08:06+09:00
Kohei Hayashi
<nav aria-label="breadcrumb" class="pt-3">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">ホーム</a></li>
<li class="breadcrumb-item"><a href="/blog/">ブログ</a></li>
<li class="breadcrumb-item active" aria-current="page">
Ruby on Rails 6.0から6.1へのアップグレード
</li>
</ol>
</nav>
<article>
<header class="mb-3">
<h1>Ruby on Rails 6.0から6.1へのアップグレード</h1>
<address class="d-inline">Posted by Kohei Hayashi</address> on <time datetime="2022-02-21">2022/02/21</time>
</header>
<div>
<p>
<a href="/blog/upgrading-rails-from-5-2-to-6-0/">前回の記事</a>ではRuby on Railsを5.2から6.0までのアップグレード方法を解説しましたが、今回の記事では6.0から6.1へのアップグレード方法を解説します。すでに6.0は通常のセキュリティーパッチ対象外となっているため、セキュリティー的には6.0までアップグレードしただけでは不十分で、6.1まで上げる必要があります。
</p>
<h2>GemfileでRailsバージョン更新</h2>
<p>
以下のようにGemfileを更新し、 <code>bundle update</code>を実行し、新しいRailsをダウンロードします。
</p>
<pre>
<code>
-gem 'rails', '~> 6.0.0'
+gem 'rails', '~> 6.1.0'
</code>
</pre>
<h2>app:updateコマンドの実行</h2>
<p><a href="/blog/upgrading-rails-from-5-2-to-6-0/">Rails6.0へのアップブレード記事</a>で紹介したように、Railsバージョンアップグレードコマンドの<code>rails app:update</code>を実行して設定ファイルの更新・追加を行います。追加、変更対象となるファイルは以下の通りです。
<ul class="text-break">
<li>bin/rails</li>
<li>bin/rake</li>
<li>bin/setup</li>
<li>bin/spring</li>
<li>bin/yarn</li>
<li>config.ru</li>
<li>config/application.rb</li>
<li>config/boot.rb</li>
<li>config/environment.rb</li>
<li>config/environments/development.rb</li>
<li>config/environments/production.rb</li>
<li>config/environments/test.rb</li>
<li>config/initializers/backtrace_silencers.rb</li>
<li>config/initializers/filter_parameter_logging.rb</li>
<li>config/initializers/new_framework_defaults_6_1.rb</li>
<li>config/initializers/permissions_policy.rb</li>
<li>config/puma.rb</li>
<li>db/migrate/20220126080120_add_service_name_to_active_storage_blobs.active_storage.rb</li>
<li>db/migrate/20220126080121_create_active_storage_variant_records.active_storage.rb</li>
</ul>
<p>
各ファイルごとにどのような処理を行なうかを尋ねられますので、新しいバージョンで上書き、既存バージョンをキープ、diff、マージなどを選択します。差分を見て、自身のカスタマイズがなければ上書き、あればマージツールを使って手動でマージ処理を行なう方法が良いかと思います。
</p>
<h2>ActiveRecord::Persistenceのupdate_attributesのupdateへの変更</h2>
<p>ActiveRecord::Persistenceのupdate_attributesとupdate_attributes!メソッドは6.0へのアップグレードの段階で既にdeprecation対象となっていましたが、6.1では完全にコードベースから削除されました。もしまだ使っているようでしたら、それぞれupdate及びupdate!に変更します。</p>
<pre>
<code>
# Rails6.0
model.update_attributes params
model2.update_attributes! params
# Rails6.1
model.update params
model2.update! params
</code>
</pre>
<h2>トランザクションブロック内でのreturn、break、throwのdeprecation</h2>
<p>
Rails6.1からは、ActiveRecord::Base.transactionブロック内でのreturn、break、throwがdeprecation対象となりました。
<br>
以前のバージョンでは以下のようなコードでトランザクション内でearly returnが出来ましたが、Rails6.1ではearly returnをトランザクションブロックの外に移すなどの工夫が必要になります。
</p>
<pre>
<code>
# Rails 6.0
ActiveRecord::Base.transaction do
return if condition
# main logic
end
# Rails 6.1
return if condition
ActiveRecord::Base.transaction do
# main logic
end
</code>
</pre>
<h2>form_withのリモートフォーム設定変更</h2>
<p>Rails6.0以前ではform_withを使う場合はデフォルトでリモートフォームとなり、通常のフォームにしたい場合は<code>local: true</code>の設定を追加していましたが、Rails6.1では通常フォームがデフォルトとなりました。(後述の<code>config.action_view.form_with_generates_remote_forms = false</code>設定に該当)。そのため、もしリモートフォームを使っているviewがあった場合、明示的に設定するようにします。逆に通常フォームは<code>local: true</code>の設定は不要となりますので、そのままでも害はありませんが外してしまっても良いでしょう。
<pre>
<code>
# 6.1リモートフォーム
form_with model: model, local: false do |f|
# 6.1通常フォーム
form_with model: model do |f|
</code>
</pre>
<h2>initializerを使った6.1デフォルト設定適用</h2>
<p>通常のアップグレードでは、config/application.rbで<code>config.load_defaults 6.0</code>のように、6.0のデフォルト設定を保持しつつ、config/initializers/new_framework_defaults_6_1.rbを使ってデフォルト設定を一つずつ上書きして動作を確認していくのですが、今回はいくつかの設定だとその方法がうまくいきませんでした。特にActiveRecordの設定は、デフォルト設定で一度設定するとActiveRecordクラスにその設定が記憶され、後の段階でinitializerでconfig設定を上書きしてもActiveRecordはその設定を再度読み込むことはしないので、6.1の設定で上書きしたと思っていても実は6.0のデフォルト設定で動いていることになり大変危険です。これはRailsフレームワークコードのバグですが、対処方法としてはconfig/initializers/new_framework_defaults_6_1.rbで上書きするのではなく、config/application.rbで以下のように直接上書きすることで防げます。
</p>
<pre>
<code>
# config/application.rb
config.load_defaults 6.0
config.active_record.has_many_inversing = true
config.active_storage.track_variants = true
config.active_job.retry_jitter = 0.15
config.active_job.skip_after_callbacks_if_terminated = true
ActiveSupport.utc_to_local_returns_utc_offset_times = true
config.action_dispatch.ssl_default_redirect_status = 308
config.active_record.legacy_connection_handling = false
config.action_view.form_with_generates_remote_forms = false
config.active_storage.queues.analysis = nil
config.active_storage.queues.purge = nil
config.action_mailbox.queues.incineration = nil
config.action_mailbox.queues.routing = nil
config.action_mailer.deliver_later_queue_name = nil
config.action_view.preload_links_header = true
</code>
</pre>
<p>なお、以下の2つの設定についてはあえて上書き対象から外しています。この2つの設定は一度本番に適用してしまうと不可逆な変更なため、他の設定で本番運用を試してから適用した方が良いためです。
<ul>
<li><code>config.action_dispatch.cookies_same_site_protection = :lax</code></li>
<li><code>config.action_controller.urlsafe_csrf_tokens = true</code></li>
</ul>
<p>
ローカルとステージング環境で動作が問題なく、すべて自動テストがパスするようになったら本番にリリースし、問題が起こらないかしばらく観察します。<br>
もし問題が起きた場合は6.0のコードにロールバックするか、バグ修正をすばやく行います。<br>
特に問題がない場合は、以下のようにデフォルト設定を6.1に変更し、設定の上書きコードを全て削除して再度本番にリリースします。
</p>
<pre>
<code>
# config/application.rb
config.load_defaults 6.1
</code>
</pre>
<p>
これでRails6.1へのアップグレードは完了となります。<br>
もしまだ6.0以前のバージョンで運用している方は、ぜひ参考にしてみてください。
</p>
<div>
---
<br>
<br>
弊社のRuby on Rails開発サービスにご興味がある方は
<a href="https://docs.google.com/forms/d/e/1FAIpQLSeFGSFIVLy2HXwabKzdnPDtO4S2buXMN5sNlIN7vwF5ZAUSgw/viewform" target="_blank" rel="noopener">
お問い合わせフォーム
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-box-arrow-up-right" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5z"/>
<path fill-rule="evenodd" d="M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0v-5z"/>
</svg>
</a>
からお問い合わせください。
</div>
</div>
</article>
Ruby on Rails 5.2から6.0へのアップグレード
https://www.audia.jp/blog/upgrading-rails-from-5-2-to-6-0/
2022-02-15T09:00:00+09:00
2022-02-17T10:28:46+09:00
Kohei Hayashi
<nav aria-label="breadcrumb" class="pt-3">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">ホーム</a></li>
<li class="breadcrumb-item"><a href="/blog/">ブログ</a></li>
<li class="breadcrumb-item active" aria-current="page">
Ruby on Rails 5.2から6.0へのアップグレード
</li>
</ol>
</nav>
<article>
<header class="mb-3">
<h1>Ruby on Rails 5.2から6.0へのアップグレード</h1>
<address class="d-inline">Posted by Kohei Hayashi</address> on <time datetime="2022-02-15">2022/02/15</time>
</header>
<div>
<p>
先日Rails7.0がリリースされたことに伴い、バージョン5.2がセキュリティーパッチの対象から外れました。そのため、まだ5.2で動いているアプリケーションはセキュリティーリスク回避のために6.xバージョンへのなるべく早い移行が望まれます。<br>
当社でも5.2を6.0にアップグレードする機会がありましたので、方法を記載しておきたいと思います。
</p>
<h2>GemfileでRailsとRubyのバージョン更新</h2>
<p>
以下のようにGemfileを更新します。<br>
</p>
<pre>
<code>
-ruby '2.6.3'
-gem 'rails', '~> 5.2.6'
+ruby '2.7.5'
+gem 'rails', '~> 6.0.0'
</code>
</pre>
<p>
Rails6はrubyのバージョンが2.5.0以上の必要がありますので、それ未満だった場合は必要に応じてRubyのバージョンも更新します。今回のプロジェクトではRubyは2.6でしたのでRailsでは更新の必要はありませんでしたが、EOLが2022-03-31と近づいていたので2.7までアップグレードしています。<br>
ローカル開発環境でのRubyアップグレードは、RVMやDockerなどを使って行います。<br>
Gemfileの更新、及び新しいRubyのインストール後、<code>bundle update</code>を実行し、新しいRailsをダウンロードします。
<h2>app:updateコマンドの実行</h2>
<p>
Railsにはアップグレード用のコマンドが用意されているので、基本的にはこのコマンドを実行して設定ファイルを更新していきます。
コマンドを実行すると各ファイルについて、Rails6の新しい設定ファイルで既存ファイルを上書きするか、既存ファイルをキープするか、マージするかなど、いくつか選択肢が表示されるのですが、マージを行うためには事前にマージツールをTHOR_MERGEという環境変数で指定しておく必要があります。
Ubuntuで行う場合、~/.profileファイルに以下のように記載します。私の場合はエディターにvimを使っているのでvimdiffを指定していますが、使っているエディターに合わせて自身が一番使いやすいものを設定します。
</p>
<pre>
<code>
# ~/.profile
export THOR_MERGE="/usr/bin/vimdiff"
</code>
</pre>
準備が出来たら<code>rails app:update</code>コマンドを実行します。<br>
このコマンドで更新・追加されるファイルには以下が含まれます(全てではないので、他にも対象ファイルが出てくるかもしれません)。
<ul class="text-break">
<li>bin/setup</li>
<li>config/application.rb</li>
<li>config/environments/development.rb</li>
<li>config/environments/production.rb</li>
<li>config/environments/test.rb</li>
<li>config/initializers/content_security_policy.rb</li>
<li>config/initializers/new_framework_defaults_6_0.rb</li>
<li>config/locales/en.yml</li>
<li>config/puma.rb</li>
<li>config/routes.rb</li>
<li>config/spring.rb</li>
</ul>
<p>各ファイル更新の際に選択肢が表示されますが、dを選択すると以前のファイルと6.0のデフォルトファイルとのdiffを確認出来ます。mを選択すると設定したマージツールが起動します。マージツールを使ってマージを行なう際は、config/environments/development.rbのようにオリジナルファイル名が表示されている方に保存を行ないます。config/environments/development.rb20220106-42674-d5roiz.rbのように表示されているものは一時ファイルのため、マージ作業が終わると消去されてしまいますので気をつけましょう。</p>
<p>もしコンフリクトが発生した場合は、以前のRailsバージョンの設定ファイルテンプレート(例:<span class="text-break">railties-5.2.6/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt</span>)を参照すると良いと思います。もし削除されるコードが以前のバージョンのテンプレートファイル由来のものであれば、更新コマンドで上書きしてしまっても大丈夫でしょう。逆にもし削除対象のコードが自分が書いた設定ならば、おそらくバージョンアップ後もキープすべきです。</p>
<h2>ホスト名設定</h2>
<p>もし開発環境でlocalhost以外のカスタムドメインを使っている場合は、config/environments/development.rbで以下のように設定を追加します。
<pre>
<code>
config.hosts << "your dev hostname"
</code>
</pre>
<h2>CoffeeScript削除</h2>
<p>もし以下のようなdepreciation warningが表示されてCoffeeScriptを必要としない場合は、Gemfileからcoffee-railsを削除して<code>bundle install</code>を実行すれば解消します。</p>
<pre>
<code>
DEPRECATION WARNING: Single arity template handlers are deprecated. Template handlers must
now accept two parameters, the view object and the source for the view object.
Change:
>> Coffee::Rails::TemplateHandler.call(template)
To:
>> Coffee::Rails::TemplateHandler.call(template, source)
(called from <top (required)> at config/environment.rb:5)
</code>
</pre>
<h2>routes.rbでの複数root</h2>
<p>
もしroutes.rbでサブドメイン対応などで複数のrootを設定していた場合、5.2では許可されていましたが6.0ではエラーになってしまいます。<br>
解消するためには以下のように、それぞれのrootにユニークな名前を設定します。
</p>
<pre>
<code>
root 'your_controller#method', as: 'unique_root_name'
</code>
</pre>
<h2>routes.rbでのformatによる制限</h2>
<p>もしroutes.rbでconstraintを使ってformat制限をしている場合、以下のようにlambdaに書き換えないと制限が効かなくなります。
<pre>
<code>
# 5.2
resources :things, constraints: { format: [:html, :js] }
# 6.0
resources :things, constraints: lambda { |req| ['html', 'js'].include? req.format }
</code>
</pre>
<h2>ActiveRecord::Baseのupdate_attributesのdeprecation</h2>
<p>次のバージョンのRails6.1ではActiveRecord::Persistenceupdate_attributes及びupdate_attributes!メソッドが無くなりますので、以下のようなdeprecation warningが表示されたら事前にupdate及びupdate!に書き換えておくのが良いでしょう。</p>
<p>
CATION WARNING: update_attributes! is deprecated and will be removed from Rails 6.1 (please, use update! instead)
</p>
<h2>request.media_typeの使用</h2>
<p>request.content_typeを使用していた場合、6.0からはメディアタイプにキャラクターセット情報が追加されますので、5.2と同様にメディアタイプ情報のみを取得したい場合はrequest.media_typeに変更します。
<h2>Zeitwerk</h2>
<p>
Rails6.0からはzeitwerkというオートローダーが採用されており、5.2以前のオートローダーはclassicモードと呼ばれるようになっています。まだ更新作業の途中段階ではconfig/application.rbのconfig.load_defaultsは5.2となっており5.2のclassicオートローダーが適用されてしまうため、以下のようにzeitwerkモードに切り替えます。
</p>
<pre>
<code>
# config/application.rb
config.autoloader = :zeitwerk
</code>
</pre>
<p>
Railsを再起動するとZeitwerkモードに切り替わりますので、<code>rails zeitwerk:check</code>コマンドで問題がないかチェックします。<br>
大抵の問題は、頭字語を変更することで解決出来ます。例えば、5.2ではvat_mailer.rbというファイルがあった場合、VATMailerというクラスを定義しても許可されていましたが、ZeiwerkではVatMailerのようにキャメルケースになっていないとエラーとなります。デフォルト動作を変更してVATMailerクラスを使い続けることも出来ますが、特別な理由がないのであれば素直にデフォルト設定に従いクラス名を変更した方が無難です。</p>
<p>
また、concernディレクトリがオートロードパスに含まれることになったことが原因でエラーが起こることがあります。そのような場合は、以下のようにConcernネームスペースをモジュール定義と呼び出しから削除することで解決出来ます。
</p>
<pre>
<code>
# 5.2 concerns/vat_decorator.rb
module Concerns::VatDecorator
# 5.2 呼び出し
include Concerns::VatDecorator
# 6.0 concerns/vat_decorator.rb
module VatDecorator
# 6.0 呼び出し
include VatDecorator
</code>
</pre>
<h2>initializerを使った6.0デフォルト設定適用</h2>
<p>この時点では、config/application.rbで<code>config.load_defaults 5.2</code>となっているため、6.0のコードベースに対して5.2のデフォルト設定が適用された状態でRailsが動作しています。<br>
そこで、次は6.0のデフォルト設定を1つづつ試していくために、<code>rails app:update</code>コマンドで生成された<span class="text-break">config/initializers/new_framework_defaults_6_1.rb</span>というファイルで各設定のコメントアウトを外し、動作に問題ないか確認します。但し、以下の2つの設定は一度適用すると、エラーが起こったときなどに5.2のコードベースに戻すことが難しくなってしまうため、他の設定を本番に適用して動作が安定していることを確認してから適用した方が安全です。1つ目はcookieに関する設定のため、クライアントブラウザーに保存されたcookieが5.2で解析出来なくなってしまうため、2つ目の設定ではメール送信の際に6.0にしか存在しないクラスを使うため、このクラスがActiveJobでredisに保存されたままで5.2に戻すとクラスが存在しないエラーとなるためです。(ちなみに5.2ではメール送信にActionMailer::DeliveryJobというクラスが使われていましたが、6.0ではdeprecateされています。6.1では完全に削除されるため、6.1移行前にredisに保存されているジョブが全て新しいクラスに置き換わっていることを確認する必要があります。)
<pre>
<code>
Rails.application.config.action_dispatch.use_cookies_with_metadata = true
Rails.application.config.action_mailer.delivery_job = "ActionMailer::MailDeliveryJob"
</code>
</pre>
<p>自動テストが全てパスし、マニュアルテストでも動作が問題ないことを確認したら、本番にリリースします。しばらく運用し特に問題がないようであれば、上記2つの設定も本番適用します。その場合は、全てのデフォルト設定が6.0になり、initializerで設定を上書きする必要はなくなりますので、以下のようにconfig/application.rbでデフォルト設定を6.0にし、config/initializers/new_framework_defaults_6_1.rbは削除します。
<pre>
<code>config.load_defaults 6.0</code>
</pre>
<p>以上で、Rails5.2から6.0へのアップグレードは完了となります。次回の記事では、6.0から6.1への移行も解説したいと思います。</p>
<div>
---
<br>
<br>
弊社のRuby on Rails開発サービスにご興味がある方は
<a href="https://docs.google.com/forms/d/e/1FAIpQLSeFGSFIVLy2HXwabKzdnPDtO4S2buXMN5sNlIN7vwF5ZAUSgw/viewform" target="_blank" rel="noopener">
お問い合わせフォーム
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-box-arrow-up-right" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5z"/>
<path fill-rule="evenodd" d="M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0v-5z"/>
</svg>
</a>
からお問い合わせください。
</div>
</div>
</article>
ZendeskウェブウィジェットをTurbolinksで使う方法
https://www.audia.jp/blog/zendesk-web-widget-on-turbolinks/
2022-02-06T09:00:00+09:00
2022-02-07T11:14:13+09:00
Kohei Hayashi
<nav aria-label="breadcrumb" class="pt-3">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">ホーム</a></li>
<li class="breadcrumb-item"><a href="/blog/">ブログ</a></li>
<li class="breadcrumb-item active" aria-current="page">
ZendeskウェブウィジェットをTurbolinksで使う方法
</li>
</ol>
</nav>
<article>
<header class="mb-3">
<h1>ZendeskウェブウィジェットをTurbolinksで使う方法</h1>
<address class="d-inline">Posted by Kohei Hayashi</address> on <time datetime="2022-02-06">2022/02/06</time>
</header>
<div>
<p>
ZendeskのオンラインチャットやFAQをウェブサイト上に表示したい時、Zendeskが提供しているウェブウィジェットが非常に便利なのですが、Ruby on RailsのサイトでTurbolinksを使用している場合、そのままでは使えません。最終的には表示することが出来たのですが、解決に至るまでに様々な方法を試したので備忘もかねて記載してみました。解決方法のみ知りたい方は文章の一番下までスクロールください。
<h2 class="mt-5">Zendesk公式サイトの方法: head内にスクリプトを記述</h2>
<p>
Turbolinksを使用していない通常のウェブサイトですと、Zendeskの説明にあるようにdocumentのhead部分にスクリプトタグを埋め込むだけで動作します。
</p>
<pre>
<code>
<head>
<!-- Start of Zendesk Widget script -->
<script id='ze-snippet' src='https://static.zdassets.com/ekr/snippet.js?key=your_api_key'></script>
<!-- End of Zendesk Widget script -->
</head>
</code>
</pre>
<p>但し、turbolinksが有効になっているサイトの場合、最初のページには表示されますが、2ページ目以降では表示されなくなります。turbolinksはページ遷移の際にDOMのbodyのみを上書きし、headは最初のページロード以降は変化しないので、ここまではturbolinksの特性上、予想可能な事象かと思います。実際、turbolinksを導入すると、Zendeskだけではなくほぼ全てのスクリプトが同じ問題に直面します。この問題は大抵はスクリプトの位置の変更で解決することが出来るので、試してみました。
</p>
<h2 class="mt-5">スクリプトをheadからbody内に移動</h2>
<p>
Turbolinksはページ遷移するごとにbodyの内容を入れ替えるので、スクリプトロード時に実行されるスクリプトであれば、body内に記述することでページ遷移ごとに毎回実行され問題解決となります。
<br>
ZendeskのスクリプトもIIFE(即時実行関数)の形となっておりロードごとに実行されるのでこの方法で解決するかと思いましたが、試したところZendeskの場合はこの方法でもウィジェットが表示されませんでした。
</p>
<pre>
<code>
<!-- Start of Zendesk Widget script -->
<script id='ze-snippet' src='https://static.zdassets.com/ekr/snippet.js?key=your_api_key'></script>
<!-- End of Zendesk Widget script -->
</body>
</code>
</pre>
<h2>動的にスクリプトをロード</h2>
<p>
Google tag managerなどで使われている方法です。
turbolinks:loadイベント内で、スクリプトタグを動的に生成すれば良いのではないかと思い、以下のようなコードも試してみましたが、この方法でも2ページ目以降はうまく表示出来ませんでした。
<pre>
<code>
(function(w,d,s,i) {
var f = d.getElementsByTagName(s)[0],
j = d.createElement(s);
j.id = 'ze-snippet';
j.src = 'https://static.zdassets.com/ekr/snippet.js?key=' + i;
f.parentNode.insertBefore(j, f);
})(window, document, 'script', 'your_api_key');
</code>
</pre>
<h2>スクリプトが行っている処理を全てカスタム開発</h2>
<p>エンジニアチームからこのような提案もあり、実際にネットで検索するとそのようなソリューションもあるようですが、多大な開発コストがかかる上に、将来的に仕様が変わった際に動く保証がどこにもないので、この案はボツになりました。</p>
<h2>Zendeskのドキュメンテーションに記載されている関数を使用してみる</h2>
ZendeskのAPI関数を呼び出せば表示出来るのではと思い、ドキュメンテーションに記載されている以下のような関数を試してみました。
<pre>
<code>
/* Open */
zE('webWidget', 'open');
/* Show */
zE('webWidget', 'show');
/* Toggle */
zE('webWidget', 'toggle');
</code>
</pre>
<p>
ただ、上記3つの関数でも、結局2ページ目以降では表示されませんでした。
</p>
<h2>iframe内でウィジェットを表示</h2>
<p>あまり美しい方法ではありませんが、bodyのウィジェットを表示したい箇所にiframeを使い、iframe中のhtmlのheadにウィジェットスクリプトを記述する方法はどうかと思い、以下のようなコードも試してみました。
<pre>
<code>
<iframe>
<html>
<head>
<script id='ze-snippet' src='https://static.zdassets.com/ekr/snippet.js?key=your_api_key'></script>
</head>
<body>
</body>
</html>
</iframe>
</code>
</pre>
<p>この方法だと2ページ目以降に遷移しても表示することが出来ましたが、既存のページのレイアウトと整合性を取るのが非常に困難なため、結局この案もボツとなりました。</p>
<h2>最終的な解決方法</h2>
<p>実際にデバッガーを動かしてスクリプトが行っている処理を一行一行解析してみると、以下のような条件分岐があることに気が付きました。</p>
<pre>
<code>
if (window.zEACLoaded)
return;
</code>
</pre>
<p>
このグローバル変数が悪さをしているのではないかと思い、以下のようなコードを書いてみたところ、2ページ目以降もZendeskウィジェットが表示されるようになりました。
<br>
*stimulus.jsを使い、<code>disconnect()</code>でページ遷移直前に処理を行っています。
</p>
<pre>
<code>
disconnect() {
window.zEACLoaded = false;
}
</code>
</pre>
<p>
要はグローバル変数がスクリプトによって設定されていたため、通常のウェブサイトならページ遷移ごとに全てリセットされますが、turbolinksではページ遷移後も値がtrueのままで保持されてしまっており、処理が中断保持されていたのが原因です。
<br>
コーディングをしていて行き詰まった時は、結局のところソースコードを読むのが遠回りなようなようで実は一番速くて確実だという良い例かと思います。
</p>
<div>
---
<br>
<br>
弊社のRuby on Rails開発サービスにご興味がある方は
<a href="https://docs.google.com/forms/d/e/1FAIpQLSeFGSFIVLy2HXwabKzdnPDtO4S2buXMN5sNlIN7vwF5ZAUSgw/viewform" target="_blank" rel="noopener">
お問い合わせフォーム
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-box-arrow-up-right" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5z"/>
<path fill-rule="evenodd" d="M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0v-5z"/>
</svg>
</a>
からお問い合わせください。
</div>
</div>
</article>
Ruby on Railsアクティブレコード更新メソッドの比較
https://www.audia.jp/blog/rubyonrails-activerecord-update-method-comparison/
2022-01-10T09:00:00+09:00
2022-01-11T17:08:08+09:00
Kohei Hayashi
<nav aria-label="breadcrumb" class="pt-3">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">ホーム</a></li>
<li class="breadcrumb-item"><a href="/blog/">ブログ</a></li>
<li class="breadcrumb-item active" aria-current="page">
Ruby on Railsアクティブレコード更新メソッドの比較
</li>
</ol>
</nav>
<article>
<h1>Ruby on Railsアクティブレコード更新メソッドの比較</h1>
<header class="my-3">
<address class="d-inline">Posted by Kohei Hayashi</address> on <time datetime="2022-01-10">2022/01/10</time>
</header>
<div>
<p>
Ruby on RailsのActiveRecord::Persistenceモジュールにはアクティブレコード更新のために様々なメソッドが用意されていますが、似たような名前のメソッドが多く、それぞれの違いがわからなかったり、どれを使うのが良いのか迷ったりすることがあります。
<br>
そこで、更新メソッドの違いを比較することで、それぞれをどのような場面で使うのが適切なのかを見ていきたいと思います。
</p>
<h2 class="mt-3">update</h2>
<p>
更新メソッドの中で最も頻繁に使うのは<a href="https://docsho.com/rails/ActiveRecord/Persistence/update/" target="_blank" rel="noopener">update</a>でしょう。
<br>
<code>@thing.update(thing_params)</code>のようにコントローラーのupdateアクションで使うのが一般的です。
<br>
モデルのバリデーション、コールバック共に実行されます。
<br>
もしバリデーションでエラーになった場合はfalse、データベースに無事に保存されればtrueが戻り値になりますので、if..elseで使うのが適しています。
</p>
<pre>
<code>
class ThingsController < ApplicationController
def update
if @thing.update thing_params
redirect_to @thing
else
render 'edit'
end
end
end
</code>
</pre>
<h2 class="mt-3">update!</h2>
<p>
<a href="https://docsho.com/rails/ActiveRecord/Persistence/update21/" target="_blank" rel="noopener">update!</a>ではupdateと同じくバリデーション、コールバック共に実行されます。但しバリデーションがエラーになった場合は、例外となります。
</p>
<pre>
<code>
def update
@thing.update! thing_params
redirect_to @thing
rescue => e
render 'edit'
end
</code>
</pre>
<p>
updateではなくupdate!を使用すべき場面としては、複数の異なるモデルの保存を一括で行わなければならず、どれか一つでもモデル保存がエラーになったら例外を投げてデータベーストランザクションをロールバックしたい時が当てはまります。
</p>
<pre>
<code>
def update
@thing.transaction do
@thing.update!
@foo.update!
end
redirect_to @thing
rescue
render 'edit'
end
</code>
</pre>
<p>
同じコードをupdateを使って書くと以下のように自身で例外を起こさなければなりません。
</p>
<pre>
<code>
def update
@thing.transaction do
raise ActiveRecord::RecordInvalid.new 'thing failed' unless @thing.update
raise ActiveRecord::RecordInvalid.new 'foo failed' unless @foo.update
end
redirect_to @thing
rescue ActiveRecord::RecordInvalid => e
render 'edit'
end
</code>
</pre>
<h2>update_attribute</h2>
<p>
<a href="https://docsho.com/rails/ActiveRecord/Persistence/update_attribute/" target="_blank" rel="noopener">update_attribute</a>は1つのモデルattributeのみを更新します。バリデーションはスキップされますが、コールバックは実行されます。
<br>
使用例としては、データベースレコードの論理削除などがあります。バリデーションを気にせず確実にdeleted_atにタイムスタンプを記録した後、コールバックで処理完了の通知を送りたい場合などです。
<br>
updateを使うとバリデーションで引っかかってしまうリスクがありますし、後述のupdate_columnだとコールバックが呼ばれません。ソースコードの裏側ではsave(validation: false)が実行されます。なので、もしメソッド実行以前に他のattributeがdirtyになっている場合は一緒に更新されてしまうので、注意が必要です。
</p>
<pre>
<code>
@user.update_attribute deleted_at: Time.current
</code>
</pre>
<h2>update_attributes</h2>
<p><a href="https://docsho.com/rails/ActiveRecord/Persistence/update_attributes/" target="_blank" rel="noopener">update_attributes</a>は、一見update_attributeの複数形なので、複数のattributesをバリデーションなしで更新するためのメソッドに見えますが、実はupdateのエイリアスです。updateとどちらを使うかはエンジニアの好み、もしくは文脈により意図がよりはっきり伝わる方を選べば良いかと思います。</p>
<h2>save</h2>
<p><a href="https://docsho.com/rails/ActiveRecord/Persistence/save/" target="_blank" rel="noopener">save</a>でupdateと同じことをしようとすると、以下のようにアサインと保存を分けることになります。実際、updateのソースコードではassign_attributesとsaveが呼ばれています。
<pre>
<code>
@thing.assign_attributes thing_params
@thing.save
</code>
</pre>
<p>
通常はupdateを使って一行で済ませますが、アサインメントと保存の間に処理を加えたい場合は、saveを使います。例えば@thingにparamsをアサイン後、@thingインスタンスのプロパティ値を変更したりする場合です。
</p>
<pre>
<code>
@thing.assign_attributes thing_params
@thing.modify_some_attributes
@thing.save
</code>
</pre>
<p>
バリデーションエラーやコールバックでの例外が起こった場合はfalse, データベースで無事に更新された場合はtrueが戻り値になります。
</p>
<h2>save!</h2>
<p>
<a href="https://docsho.com/rails/ActiveRecord/Persistence/save21/" target="_blank" rel="noopener">save!</a>はsaveと似ていますが、バリデーションエラーやコールバック中に例外が起こると、例外が起こります。もしエラーがない場合、trueを返します。
</p>
<pre>
<code>
def update
@thing.transaction do
@thing.assign_attributes thing_params
@thing.modify_some_attributes
@thing.save!
@foo.save!
end
redirect_to @thing
rescue
render 'edit'
end
</code>
</pre>
<h2>toggle</h2>
<p>
<a href="https://docsho.com/rails/ActiveRecord/Persistence/toggle/" target="_blank" rel="noopener">toggle</a>はブーリアン型のattributeを反対の値にします。つまり、元の値がtrueだったらfalse, falseならtrueになります。データベースへの保存は行われず、オブジェクトメモリーの値が変化します。
<br>
戻り値はselfのため、メソッドチェインが可能です。
</p>
<pre>
<code>
def toggle_enabled
if @thing.toggle(:enabled).save
redirect_to @thing
else
render 'edit'
end
end
</code>
</pre>
<h2>toggle!</h2>
<p><a href="https://docsho.com/rails/ActiveRecord/Persistence/toggle21/" target="_blank" rel="noopener">toggle!</a>はtoggleと同じようにブーリアン型のattributeを反対の値にしますが、データベースへの保存がされます。ソースコードでは、toggleとupdate_attributeのメソッドチェインとなっています。ゆえに、戻り値もselfではなくブーリアンとなります。</p>
<pre>
<code>
def toggle_enabled
if @thing.toggle!(:enabled)
redirect_to @thing
else
render 'edit'
end
end
</code>
</pre>
<h2>touch</h2>
<p>
<a href="https://docsho.com/rails/ActiveRecord/Persistence/touch/" target="_blank" rel="noopener">touch</a>はupdated_at/updated_on、及び任意のタイムスタンプ・カラムを現在時刻で更新します。
<br>
after_touch, after_commit/after_rollbackののコールバックしか実行されないので、 タイムスタンプカラムの更新で通常のafter_saveなどのコールバックを実行したくない時に使えます。
</p>
<pre>
<code>
@user.touch(:deleted_at)
</code>
</pre>
<h2>update_column</h2>
<p><a href="https://docsho.com/rails/ActiveRecord/Persistence/update_column/" target="_blank" rel="noopener">update_column</a>は後述のupdate_columnsと挙動は一緒ですが、複数ではなく単数のattributeを更新したい場合に使用します。</p>
<pre>
<code>
@thing.update_column :status, 'active'
</code>
</pre>
<p>
以下のようにupdate_columnsを使って単数のattributeを更新しても挙動は同じですが、update_columnを使ったほうが単数カラムの更新というコードの意図がよりはっきりします。
</p>
<pre>
<code>
@thing.update_columns status: 'active'
</code>
</pre>
<h2>update_columns</h2>
<p>
<a href="https://docsho.com/rails/ActiveRecord/Persistence/update_columns/" target="_blank" rel="noopener">update_columns</a>は複数のattributesを更新します。バリデーション、コールバック共にスキップされ、updated_atも更新されないため、生のUPDATE SQLと同義です。
<br>
使用例としては、新規レコード作成後のActiveRecordコールバックの中で、アサインされたプライマリーidを基に他のコラムを更新したい時があります。また、バグなどにより本番データベースレコードの整合性を修正したいが、生のSQLで間違って別のレコードを更新するようなミスを防ぎたいようなケースが該当します。
</p>
<pre>
<code>
@thing.update_columns status: 'active', amount: 100
</code>
</pre>
<h2>どのメソッドを使うべきかの判断</h2>
<p>
前述の各更新メソッドの特徴をまとめると、以下の表の通りとなります。
</p>
<table class="table table-responsive">
<caption>更新メソッド比較表</caption>
<thead>
<tr>
<th>メソッド</th>
<th>バリデーション</th>
<th>コールバック実行</th>
<th>戻り値</th>
<th>エラーの場合</th>
<th>更新されるattributeの数</th>
<th>updated_at更新有無</th>
</tr>
</thead>
<tbody>
<tr>
<td>update<br>(update_attributes)</td>
<td>あり</td>
<td>あり</td>
<td>ブーリアン</td>
<td>戻り値がfalse</td>
<td>複数</td>
<td>あり</td>
</tr>
<tr>
<td>update!</td>
<td>あり</td>
<td>あり</td>
<td>ブーリアン</td>
<td>例外</td>
<td>複数</td>
<td>あり</td>
</tr>
<tr>
<td>update_attribute</td>
<td>なし</td>
<td>あり</td>
<td>ブーリアン</td>
<td>戻り値がfalse</td>
<td>単数</td>
<td>あり</td>
</tr>
<tr>
<td>save</td>
<td>あり</td>
<td>あり</td>
<td>ブーリアン</td>
<td>戻り値がfalse</td>
<td>複数</td>
<td>あり</td>
</tr>
<tr>
<td>save!</td>
<td>あり</td>
<td>あり</td>
<td>ブーリアン</td>
<td>例外</td>
<td>複数</td>
<td>あり</td>
</tr>
<tr>
<td>toggle</td>
<td>なし</td>
<td>なし</td>
<td>self</td>
<td>n/a</td>
<td>単数</td>
<td>なし</td>
</tr>
<tr>
<td>toggle!</td>
<td>なし</td>
<td>あり</td>
<td>ブーリアン</td>
<td>戻り値がfalse</td>
<td>単数</td>
<td>あり</td>
</tr>
<tr>
<td>touch</td>
<td>なし</td>
<td>あり</td>
<td>ブーリアン</td>
<td>n/a</td>
<td>複数</td>
<td>あり</td>
</tr>
<tr>
<td>update_column</td>
<td>なし</td>
<td>なし</td>
<td>ブーリアン</td>
<td>n/a</td>
<td>単数</td>
<td>なし</td>
</tr>
<tr>
<td>update_columns</td>
<td>なし</td>
<td>なし</td>
<td>ブーリアン</td>
<td>n/a</td>
<td>複数</td>
<td>なし</td>
</tr>
</tbody>
</table>
<p>
上記の表で条件にあうメソッドを絞り込んで決定しても良いですし、以下のようなディシジョンツリーを使ってどのメソッドを使うべきかを判断しても良いかと思います。
<br>
<img src="/images/blog/2022-01-10/decision_tree_on_rails_update_methods.png" alt="アクティブレコード更新メソッドのディシジョンツリー" class="img-fluid" />
</p>
<h2 class="mt-5">補足:更新メソッド間の依存関係</h2>
<p>
各メソッドのソースコードを読むと、いくつかのメソッドは他のメソッドのラッパーメソッドであることが確認出来ます。例えば、updateのソースコードではsaveが呼ばれているため、updateの基本的な挙動はsaveに準拠することがわかります。以下の図は、どのメソッドがどのメソッドに依存しているかをまとめたものですが、この依存関係を頭に入れておくと、saveやupdate_columnsなどベースとなる数個のメソッドの特徴を覚えるだけで、他のメソッドの動作も理解出来るはずです。
<br>
<img src="/images/blog/2022-01-10/update_method_dependencies.png" alt="アクティブレコード更新メソッドの依存関係" class="img-fluid" />
</p>
<h2 class="mt-5">最後に</h2>
<p>
このように、Ruby on Railsではモデルの更新を行うのに似たようなメソッドがいくつもあるので混乱しがちですが、異なった文脈に応じて適切なメソッドを使うことでrubyらしく人間に取って読みやすいコードを書くことが出来ます。
</p>
</div>
<div>
<a href="/blog/feed.xml"><img src="/images/feed.png" alt="コンテンツの配信" title="Audiaブログ" width="16" height="16" /></a>
</div>
</article>
Ruby on RailsでHMAC-SHA256を使ったリクエスト署名を検証する方法
https://www.audia.jp/blog/verify-hmac-sha256-signature-ruby-on-rails/
2021-12-02T09:00:00+09:00
2021-12-06T09:42:14+09:00
Kohei Hayashi
<nav aria-label="breadcrumb" class="pt-3">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">ホーム</a></li>
<li class="breadcrumb-item"><a href="/blog/">ブログ</a></li>
<li class="breadcrumb-item active" aria-current="page">
Ruby on RailsでHMAC-SHA256を使ったリクエスト署名を検証する方法
</li>
</ol>
</nav>
<article>
<header class="mb-3">
<h1>Ruby on RailsでHMAC-SHA256を使ったリクエスト署名を検証する方法</h1>
<address class="d-inline">Posted by Kohei Hayashi</address> on <time datetime="2021-12-02">2021/12/02</time>
</header>
<div>
<p>
ウェブフックなどを使って自社のウェブAPIでHTTPリクエストを受信する際、そのリクエストが本当に正規の相手から送信されたかどうかを検証する必要があります。<br>この記事ではRuby on Railsを使ってリクエストに含まれた署名を検証する方法を紹介します。
</p>
<p>
前提<br>
</p>
<ul>
<li>署名にはSHA256ハッシュ関数を使ったHMACアルゴリズムが使われている。</li>
<li>署名ダイジェストはHTTPリクエストボディを元に生成される。</li>
<li>署名ダイジェストはHTTPヘッダーにてbase64エンコードされて送信される。</li>
</ul>
<p>
このようなリクエストは、RailsのControllerで以下のようなコードを書くことによって検証することが出来ます。
</p>
<pre>
<code>
class ExamplesController < ActionController::API
before_action :verify_signature
private
def verify_signature
body = request.body.read
digest = Base64.strict_encode64(OpenSSL::HMAC.digest('SHA256', ENV['SHARED_KEY'], body))
return if digest == request.headers['X-Signature']
render status: 403, json: { message: 'Forbidden' }
end
end
</code>
</pre>
<p>
シンプルなコードですが、一行ずつ解説をしていきます。<br>
<br>
まず、RailsのcontrollerではrequestオブジェクトにHTTPリクエストの情報が格納されています。署名の対象がrequest.bodyなので、その情報を取得しますが、StringIOオブジェクトのままだと使えないため、readメソッドを使って読み込む必要があります。<br>
<br>
次に、リクエストボディのダイジェスト値を計算します。リクエストの送信側がHMAC-SHA256を使って署名を付与しているので、まったく同じ方法でダイジェスト値を求めます。<br>
<br>
環境変数のSHARED_KEYはリクエストの送信側と受信側で事前に共有した鍵となります。(rubyのSecureRandom.hex()メソッドなどの乱数ジェネレーターを使って簡単に作成できます。)<br>
<br>
作られたダイジェストはバイナリーのため、ヘッダーの署名と同様にbase64でエンコードします。ここでBase64.encode64()ではなくstrict_encode64()を使っているのは、前者はMIME用のメソッドのため、文字列の最後に改行コード(\n)が入ってしまうからです。後者を使うと改行コードなしのbase64文字列を生成できます。<br>
<br>
生成されたbase64ダイジェストを、ヘッダーに含まれる署名と比較します。値が同一ならばリクエストが鍵の持ち主によって送られ、通信中の改竄も行われていないことが証明出来るので、early returnをしてリクエストの処理を続けます。<br>
<br>
もしダイジェスト値が一致しない、もしくはヘッダーが存在しない場合は、悪意のある第三者によるリクエストの可能性が高いので、HTTPステータス403(Forbidden)を返しリクエスト処理をストップします。(Railsではbefore_action等のリクエストフィルターでrenderもしくはredirectを行うと、リクエスト処理が停止されます。)
</p>
<p>
いかがでしたでしょうか。もしリクエストの署名検証なしにリクエストを処理している場合、URLがわかっただけで第三者が自社アプリケーションにアクセス出来てしまうため、ぜひ署名検証の仕組みを導入してみてください。
</p>
<div>
---
<br>
<br>
弊社のRuby on Rails開発サービスにご興味がある方は
<a href="https://docs.google.com/forms/d/e/1FAIpQLSeFGSFIVLy2HXwabKzdnPDtO4S2buXMN5sNlIN7vwF5ZAUSgw/viewform" target="_blank" rel="noopener">
お問い合わせフォーム
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-box-arrow-up-right" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5z"/>
<path fill-rule="evenodd" d="M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0v-5z"/>
</svg>
</a>
からお問い合わせください。
</div>
</div>
<div>
<a href="/blog/feed.xml"><img src="/images/feed.png" alt="コンテンツの配信" title="Audiaブログ" width="16" height="16" /></a>
</div>
</article>
Cagamee - 簿記初級 - 簿記とは
https://www.audia.jp/blog/cagamee-bookkeeping-class-s-what-is-bookkeeping/
2020-11-30T09:00:00+09:00
2021-12-06T09:48:30+09:00
Kohei Hayashi
<nav aria-label="breadcrumb" class="pt-3">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">ホーム</a></li>
<li class="breadcrumb-item"><a href="/blog/">ブログ</a></li>
<li class="breadcrumb-item active" aria-current="page">
Cagamee - 簿記初級 - 簿記とは
</li>
</ol>
</nav>
<article>
<header class="mb-3">
<h1>Cagamee - 簿記初級 - 簿記とは</h1>
<address class="d-inline">Posted by Kohei Hayashi</address> on <time datetime="2020-11-30">2020/11/30</time>
</header>
<div>
<p>
初級レベルの簿記について、<a href="https://cagamee.com/ja" target="_blank" rel="noopener">Cagamee</a>を使って解説するブログ記事を書きました。
<br>
<a href="https://cagamee.com/ja/blog/bookkeeping-class-s-what-is-bookkeeping" target="_blank" rel="noopener">
簿記初級 - 簿記とは
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-box-arrow-up-right" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5z"/>
<path fill-rule="evenodd" d="M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0v-5z"/>
</svg>
</a>
</p>
</div>
<div>
<a href="/blog/feed.xml"><img src="/images/feed.png" alt="コンテンツの配信" title="Audiaブログ" width="16" height="16" /></a>
</div>
</article>