give IT a try

プログラミング、リモートワーク、田舎暮らし、音楽、etc.

Rails + Sassでimage-urlを使うのは古い。画像の指定はurlだけでOK

TL; DR(最初に結論)

2024年にrails newするなら、dartsass-railsを使おう。

そして、sass/scssファイルに画像のURLを指定する場合はimage-urlではなくurlを使おう。

/* app/assets/stylesheets/foo.scss */
.my-image {
  /* image-urlではなくurlを使う */
  background-image: url('bg.png');
}

はじめに

Qiitaに書こうと思ったけど、記事としてきれいにまとまらないのでこっちに雑にまとめます。

たとえばRailsアプリで背景画像を出したいとき、かつSassを使っているとき、画像のURLはimage-urlで指定していました。

/* app/assets/stylesheets/foo.scss */
.my-image {
  background-image: image-url('bg.png');
}

ちなみにbg.pngはapp/assets/images/bg.pngに配置されている前提です。

上のscssファイルはプリコンパイルされるとダイジェスト付きのURLに変わります。(image-urlurlに変わる)

.my-image {
  background-image: url(/assets/bg-31822...4a19b.png);
}

が、image-urlはsass-railsやsassc-railsが提供している関数です。 そして、sass-railsやsassc-railsはすでに開発が止まっていて、現在開発が継続しているのはdartsass-railsです。

ただし、dartsass-railsではimage-urlは使えません。 冒頭のscssファイルをプリコンパイルするとurlの中身はダイジェスト付きのパスになるものの、image-urlimage-urlのままになり、結果として無効なCSSになります。

.my-image {
  /* dartsass-railsだとimage-urlがそのまま残る */
  background-image: image-url(/assets/bg-31822...4a19b.png);
}

じゃあどうしたらいいのかというと、scssにurlを指定します。

/* app/assets/stylesheets/foo.scss */
.my-image {
  background-image: url('bg.png');
}

こうすると正しいCSSが出力されます。

.my-image {
  background-image: url(/assets/bg-31822...4a19b.png);
}

なお、この記事ではimage-urlを対象にしていますが、asset-url, font-url, video-url, audio-urlも考え方は同じです。

コラム:Railsで使えるSassのgemは3種類

歴史的経緯により、Railsで使えるSassのgemは、sass-rails、sassc-rails、dartsass-railsの3種類です。

さらにややこしいことに、sass-rails 6はsassc-railsのラッパーです。

This gem is now only just a wrapper around sassc-rails.

https://github.com/rails/sass-rails/releases/tag/v6.0.0

よって、これら3つのgemを古いものから順に並べると、以下のようになります。

  • sass-rails 5以下(Rubyで実装されたオリジナルのSass。開発終了)
  • sass-rails 6 = sassc-rails(Cで実装されたSass。開発終了)
  • dartsass-rails(Dartで実装されたSass)

このほかにも、gemを使わずにnpmとして提供されているSassを使う、というアプローチもあります。 いろいろあってややこしいですね。

僕の疑問

上記のような挙動を知って、僕は以下のような疑問を持ちました。

  • image-urlってそもそもどこで定義されてるの??
  • url(...)って標準のCSSなのに、なんでdartsass-railsだとプリコンパイルされたらダイジェスト付きのパスに変わるの??

それぞれ調査した結果を以下にまとめます。

セルフアンサー:image-urlはどこで定義されてるの??

image-urlはsprockets gemのimage_urlメソッドにマッピングされます。

# https://github.com/rails/sprockets/blob/v3.7.3/lib/sprockets/sass_processor.rb
def image_url(path)
  asset_url(path, type: :image)
end

asset_urlメソッドはsassc-railsに定義されていて、これがさらにasset_pathメソッドを呼び出します。 こうした一連の流れにより、image-urlの引数として指定したパスはasset_pathメソッドでダイジェスト付きのパスに変換されます。

# https://github.com/sass/sassc-rails/blob/v2.1.2/lib/sassc/rails/template.rb
def asset_path(path, options = {})
  path = path.value

  path, _, query, fragment = URI.split(path)[5..8]
  path     = sprockets_context.asset_path(path, options)
  query    = "?#{query}" if query
  fragment = "##{fragment}" if fragment

  ::SassC::Script::Value::String.new("#{path}#{query}#{fragment}", :string)
end

def asset_url(path, options = {})
  ::SassC::Script::Value::String.new("url(#{asset_path(path, options).value})")
end

しかし、dartsass-railsではasset_urlメソッドやasset_pathメソッドに相当する処理がありません。

セルフアンサー:なんでurlがダイジェスト付きのパスに変わるの??

これはsprockets-rails 3.3でurlに指定したパスをダイジェスト付きのパスに変換する機能が導入されたためです。

github.com

つまり、これはdartsass-railsではなく、sprockets-railsの機能です。

この機能は正規表現で実現されています。

# https://github.com/rails/sprockets-rails/blob/v3.5.1/lib/sprockets/rails/asset_url_processor.rb
module Sprockets
  module Rails
    # Resolve assets referenced in CSS `url()` calls and replace them with the digested paths
    class AssetUrlProcessor
      REGEX = /url\(\s*["']?(?!(?:\#|data|http))(?<relativeToCurrentDir>\.\/)?(?<path>[^"'\s)]+)\s*["']?\)/
      def self.call(input)
        context = input[:environment].context_class.new(input)
        data    = input[:data].gsub(REGEX) do |_match|
          path = Regexp.last_match[:path]
          "url(#{context.asset_path(path)})"
        end

        context.metadata.merge(data: data)
      end
    end
  end
end

実装上はurl(...)みたいな文字列を置換するだけなので、image-url(...)でもhoge-url(...)でも、正規表現にマッチした文字列なら何でも()内のパスがダイジェスト付きのパスに変換されます。

/* app/assets/stylesheets/foo.scss */
.my-image {
  /* hoge-urlはCSSとして無効 */
  background-image: hoge-url('bg.png');
}
.my-image {
  /* だが、url(...)の形式ならimage-urlでもhoge-urlでも何でも変換される */
  background-image: hoge-url(/assets/bg-31822...4a19b.png);
}

また、sprockets-railsが提供している機能なので、SassではないプレーンなCSSでもアセットプリコンパイルの対象になっていればurlがダイジェスト付きのパスに変換されます。

/* app/assets/stylesheets/foo.css */
.my-image {
  /* scssやsassではなく、プレーンなCSSとしてurlを指定する */
  background-image: url(bg.png);
}
.my-image {
  /* アセットプリコンパイルの対象になっていればダイジェスト付きのパスに変換される */
  background-image: url(/assets/bg-31822...4a19b.png);
}

ちなみにsprocets-rails 3.2以前ではurlはプリコンパイルしても何も変わらず、そのまま出力されます*1

/* sprockets-sass 3.2ではプリコンパイルしても何も変化なし */
.my-image {
  background-image: url(bg.png);
}

まとめ

わかったことをまとめるとこんな感じです。

  • image-urlはsass-railsやsassc-railsが提供している関数
  • image-urlはdartsass-railsには存在しない
  • バージョン3.3以降のsprockets-railsはurlをダイジェスト付きのパスに変換してくれる
  • dartsass-railsはurlを使うしかない
  • sass-railsやsassc-railsはsprockets-rails 3.3以上がインストールされていれば、image-urlurlも両方使える

表にするとこんな感じでしょうか。

組み合わせ
sass-rails, sassc-rails Y Y
dartsass-rails Y
sprockets-rails 3.2以下 Y
sprockets-rails 3.3以上 Y Y
image-url ❌ 2
url ❌ 1
  • ❌ 1 = ()内のパスは変化しない(ダイジェスト付きにならない)
  • ❌ 2 = パスにダイジェストは付くものの、image-urlimage-urlのまま残るため、無効なCSSになる

2024年現在でrails newする場合、Sassを使いたいならdartsass-railsを使うことになるはずです(なぜならsass-railsやsassc-railsは開発が終了しているから)。

また、sprockets-rails 3.3がリリースされたのは2021年11月なので、3.2以前のsprockets-railsがインストールされることはまずないでしょう。

ということはSassで画像URLを指定する場合はimage-urlではなくurlを使う、というのが望ましい方法になりますね。

/* app/assets/stylesheets/foo.scss */
.my-image {
  background-image: url('bg.png');
}

参考文献

github.com

おまけ

Stack Overflowに同じような質問があり、まだ誰も回答していなかったので、僕が回答してみました(2013年の質問ですが・・・)。

stackoverflow.com

あわせて読みたい

アセットプリコンパイル周りは初心者泣かせのややこしい挙動が満載です。画像がうまく表示できないときはこちらの記事も参考にしてみてください。

qiita.com

*1:これが原因でローカルでは画像が表示されるが、Herokuにデプロイすると画像が表示されない、というトラブルがよく起きていた

2021年式VOLVO XC60のワイパーゴムを自分で交換する

はじめに

愛車のVOLVO XC60(2021年式)のワイパーゴムが弱ってきたので交換することにしました。
ディーラーで交換すると車を持っていくのが面倒だし、費用も高そうなので自分で交換できないかチャレンジしてみました。
結果としては無事に交換できたのですが、意外とネット上に情報がなく、「どのゴムを使えばいいの?」、「どういう手順で交換すればいいの?」と右往左往したので、最終的に判明した交換方法をこのブログにまとめておきます。


免責事項

このブログの内容は基本的に自分用の備忘録です。
ワイパーゴムの交換は自己責任でお願いします。
自信がなければディーラーで交換してもらいましょう。

用意するもの

新品のワイパーゴム

今回は近所のカー用品店で売っていた、こちらのワイパーゴムを購入しました。
2つ併せて3160円でした。

ただし、助手席側のワイパーゴムは500mm(呼番147)ではなく475mm(呼番146)がベストです。
475mmは売ってなかったので500mmを自分で475mmにカットしました。

その他

マイナスドライバーとタオルや雑巾を2枚用意します。

交換手順

ワイパーをサービス位置に移動させる

メニューから「ワイパーのサービス位置」を選択します。

するとワイパーがピョコッと写真の位置に移動して止まります。

この状態でいったんエンジンを停止してください。

ワイパーブレードを外す

ワイパーの背中にある四角いボタン(?)を押し込みながらワイパーブレードを上に引っ張ると、ワイパーブレードが抜けます。
指で押し込むのが難しいときは、マイナスドライバーを突っこみながら引っ張るのもありです。


もしくは上端のキャップだけを外す

運転席側のワイパーブレードは上記の方法で外せたのですが、助手席側はなぜかいくら引っ張っても外れませんでした。
下手に力を入れて壊すのもイヤなので、ワイパーブレードを引っこ抜くのは諦めて、上端のキャップ部分だけを外すことにしました。

ちょっとわかりづらいですが、キャップには小さなロック用のツメがあります。
これをマイナスドライバーなどでワイパーの外側に向かってにカチッと移動させるとロックが解除され、上端のキャップを外せます。

キャップを外すとワイパーゴムだけスルスルッと引っ張り出せるようになるので、古いワイパーゴムを抜いておきます。

ワイパーとフロントガラスの間にタオルを挟む

作業中、何かの拍子にワイパーゴムの付いてないワイパーがパチン!とフロントガラスにぶつかるとガラスが割れてしまうかもしれないため、あらかじめワイパーを倒してタオルを挟み込んでおきましょう。


運転席側のワイパーゴムを交換する

運転席側はワイパーブレードごと外したので、このワイパーゴムを交換します。
まず、先ほど書いた手順で上端のキャップを外します。
それから古いワイパーゴムを引っ張って抜き出します。

写真ではわかりにくいですが、黄色い枠の部分にワイパーゴムを通す溝が切ってあるので、ここに新しいワイパーゴムを通します。

こんな感じで通していきます。

最後まで通しました。

あとはさっきと逆の手順で上端のキャップを取り付けます。
キャップ部分にもワイパーゴムを通す溝があるので、ワイパーゴムがちゃんとキャップの溝に収まるように注意しながらキャップを取り付けてください。
キャップは最後までぐっと押し込むと、カチッと音がして自動的にロック用のツメがロック位置に移動します。

助手席側のワイパーゴムを交換する

助手席側はワイパーブレードが外れなかったので、ワイパーブレードを車に取り付けたまま新しいゴムを取り付けます。
手順は運転席側と同じですが、ワイパーブレードが車に付いたままだと、若干無理な体勢で作業を強いられるので少し時間がかかりました。

ワイパーブレードを取り付ける

運転席側はワイパーブレードを外したときと逆の手順でワイパーブレードを取り付けます。
ただし、XC60のワイパーブレードはウォッシャー液がワイパーから出てくるようになっているせいか、作りが若干複雑です。
無理に押し込まず、丁寧に取り付けてください。

うまくはまればカチッと音がしてきれいにワイパーブレードが元の状態に戻ります。


ワイパーを元の位置に移動させる

エンジンを始動し、メニューから「ワイパーのサービス位置」を選択して、ワイパーを元の位置に戻します。

ワイパーの試運転

ウォッシャー液を出しながらフロントワイパーを動作させます。
ちゃんとウォッシャー液がワイパーから出てくるか?ワイパーの動きにおかしな点はないか?不自然な拭き残しがないか?等々のチェックをしましょう。

これで終わりです。
お疲れ様でした!

ちなみに:このワイパーは使えません!

Amazonで「代表適合車種 ボルボ: C30/ S40/ S80/ V50/ V70/ XC60/ XC70」と書いてあるワイパー(ワイパーブレード)があったので、最初はこのBOSCH A089Sを買ったのですが、全然ダメでした!


パッと見は似てるんですが、ワイパーの長さも違うし、ブレードの取り付け溝も形状が違うし、ウォッシャー液を出す穴とかもないし、まったく互換性がなかったです!

ワイパーブレードは互換性がなくても、実はワイパーゴムだけは互換性があったりする?と思いましたが、ワイパーゴムの幅や形状も違ったため、ワイパーゴムだけ利用する作戦もダメでした(4600円が無駄になった😭)。

もしかすると初代(2009-2017年製)XC60用だったのかもしれませんね。。
オーナーのみなさんはお気を付けください。

まとめ

というわけで今回は2021年式VOLVO XC60のワイパーゴムを自分で交換する方法を紹介してみました。
やろうと思えば自分で交換できることがわかったので、今後もワイパーゴムは自分で変えようと思います。

「プロを目指す人のためのRuby入門 改訂2版」が増刷されました🎉&Ruby 3.0で学習すべきかどうかについて

お知らせ

先日拙著「プロを目指す人のためのRuby入門 改訂2版(通称・チェリー本)」が増刷されました!🎉

いやあ、嬉しい!いやあ、めでたい!
本書を買っていただいたみなさんのおかげです。どうもありがとうございます!

そもそも「増刷」って何なん?

増刷(ぞうさつ)というのは、簡単に言うと「本の在庫がなくなってきたから、在庫を補充するために追加で印刷すること」です。
「え、それだけ?」と思われるかもしれませんが、商業出版の場合、一回につき、かなりまとまった冊数を印刷します。
つまり「増刷される=それだけ本がたくさん売れている」ということを意味するので、「増刷しますよ」の連絡は本の著者にとって、大変嬉しいニュースになります😄

ところで、Ruby 3.0で学習を進めてもいいの?

さて、話は変わりますが、本書の対象バージョンはRuby 3.0です。
出版当時(2021年)は最新バージョンでしたが、Rubyは毎年12月25日バージョンが上がります。
2024年5月現在の最新バージョンはRuby 3.3です。

そして、定期的に古いバージョンからサポートが切れていきます。
Ruby 3.0は2024年4月23日にリリースされたバージョン3.0.7が最終バージョンとなり、現在はサポートが切れた状態(EOL = End of Life)になっています。


では、このサポートが切れたRuby 3.0でRubyの学習を進めてもいいのでしょうか?

筆者の答え:Ruby 3.0でもいいし、Ruby 3.1以上でもOK!

最初に結論を言うと、学習目的で使用するならRuby 3.0でもいいし、Ruby 3.1以上でも構いません。
詳しい話は以下に書きます。

Ruby 3.0を使う場合

公式サポートが切れると、Ruby本体のバグ修正やセキュリティパッチのリリースがなくなります。
もしRuby 3.0を使って、外部に公開するようなアプリケーションを開発している場合はこれが深刻な問題につながる可能性があります。

ですが、チェリー本のサンプルプログラムはローカルマシン内に閉じた簡単なものばかりです。
なので、学習用として使うのであれば、Ruby 3.0を使い続けても問題はありません。

Ruby 3.0の問題点

ただし、Rubyのバージョンが古くなっていくと、「最新の開発環境にうまく対応できなくて、インストールにすごく苦労する」という現象がときどき発生します。
今はすんなりインストールできると思いますが、数年すると「Ruby 3.0をインストールしようとしたらエラーが出た」みたいな問題が起きるかもしれません。
その際は無理に頑張らず、新しいバージョンのRubyをインストールして学習することを検討してください。

また、最新のRubyでは本書の説明と異なる部分もいくつかあるため、業務で新しいバージョンのRubyを使ったときに「あれ、こんな構文あったっけ?」と疑問に思うことが出てくるかもしれません。
そうならないように、Ruby 3.0で学習が終わったら今度は次の項で紹介する説明記事を参考にして、Ruby 3.1以上の新機能や新構文もチェックしておきましょう!

Ruby 3.1以上を使う場合

Ruby本体はバージョンが上がっても、大半の部分で後方互換性が維持されています。
ですので、Ruby 3.1以上のバージョンでチェリー本を勉強したとしても、9割以上の内容は有効です。

ただ、そうは言っても「100%同じ」というわけにはいきません。
そんなに数は多くないものの、中には「チェリー本に書いてある内容と、実際に動かしたときの挙動が異なる」という部分もところどころに出てきます。

そういうときのために、Ruby 3.1、3.2、3.3の各バージョンについて、チェリー本の説明との差異をまとめています。

Ruby 3.1以上のバージョンを使って学習する場合は、事前にこれらの記事に目を通しておくと、安心して学習が進められると思います。

このような最新バージョンとの差異の説明は、今後も引き続き行っていく予定です。
なので、書籍のRubyバージョンが多少古くても気にせず本書を手に取っていただけるとありがたいです!

チェリー本に関する書評ブログあれこれ

最後に、読者のみなさんが書いてくださった書評ブログ(一部)を掲載させてもらいます。
まだ本書を持ってない方はこちらの書評も参考にしてみてください。

engineer-umd.hatenablog.com
torihazi.hateblo.jp
engineer.crowdworks.jp
qiita.com
neco3s.hatenablog.com

あなたの書評もお待ちしています!

書評を書いてくださったみなさん、どうもありがとうございます!
どんな内容であれ、書評を書いてもらえるのは著者としてとても嬉しいです。
簡単な内容でも良いので、みなさんぜひ「プロを目指す人のためのRuby入門」を読んだ感想を教えてください!

まとめ

というわけで、このエントリでは「プロを目指す人のためのRuby入門 改訂2版」が増刷されたお知らせと、サポートの切れたRuby 3.0で学習を進めても良いかどうかについて書いてみました。

これからもたくさんの人に本書を読んでもらえると嬉しいです。
引き続き「プロを目指す人のためのRuby入門」をよろしくお願いします!

あわせて読みたい

blog.jnito.com
blog.jnito.com