Ratyは、divひとつを星評価の入力欄に変えるJavaScriptプラグインです(npmのパッケージ名はraty-js)。日本語の解説記事の多くは$('#star').raty({ score: 3 })というjQuery記法で書かれていますが、この書き方は2022年10月2日公開のv4.0.0から動きません。jQuery依存が取り除かれ、起動がnew Raty(element, options).init()に変わったためです。
古い記事どおりnpm install raty-jsを実行すると、入るのは最新の4.3.0です。インストールは成功するのに星が出ないので、原因が版の違いにあると気づきにくくなっています。Rails 7以降ではさらに、Turbo Driveのページ遷移で星が消えたり二重に並んだりする問題が重なります。
この記事では、公式changelogとraty-js 4.3.0のソースを根拠に旧記法からの書き換えを整理し、Rails 8.1.4の実アプリで確かめたimportmapでの読み込み、Stimulusでの初期化、評価のAjax保存までを手順で示します。
まとめ
jQuery記法から移行する場合は起動方法、Railsへ組み込む場合は読み込みとTurbo対応の行を確認してください。
| 論点 | 結論(raty-js 4.3.0・Rails 8.1.4で確認) |
|---|---|
| 起動の書き方 | new Raty(el, options).init() |
| jQuery記法が使える最終版 | raty-js 3.1.1(v4系に $.fn.raty は無い) |
| パッケージ名 | raty-js(npmの raty は無関係) |
| Railsでの読み込み | bin/importmap pin raty-js |
| Turbo環境の初期化 | Stimulusの connect() で中身を消してから init() |
| Ajax保存 | click コールバックから X-CSRF-Token 付きで送る |
| 保守状況 | 2024-04-28の4.3.0以降リリースなし(2026-11-02時点) |
既存のコードがjQuery記法なら、まず次の章の書き換え表で失効した箇所を洗い出してください。
v4.0.0で動かなくなったjQuery記法と書き換え表
Ratyは3世代に分かれます。v2系まではjQueryプラグイン、v3系はjQuery依存を残した内部整理、v4系はjQueryを使わない純粋なJavaScriptクラスです。破壊的変更が2回入っているため、世代をまたぐとそのままでは動きません。
$(…).raty is not a functionになる理由
changelogのv4.0.0には「The jQuery dependency was dropped, so the syntax to start Raty was changed」と書かれています。v4系のsrc/raty.jsはclass Ratyを定義してexport default Ratyするだけで、jQuery.fnへの登録処理を持ちません。$.fn.ratyが存在しないので、$('#star').raty()はis not a functionで止まります。
v4.2.0(2023-08-24)ではテストコードからもjQueryが外され、changelogは「Drops jQuery from tests and so from the entire project」と宣言しています。v4.3.0に旧記法は含まれていません。jQuery本体は別に4.0系へ進んでおり、そちらの変更点はjQuery 4.0とは|4.0.0の変更点・CDNとSRI・3系からの移行手順で扱っています。
v2系・v3系・v4.3.0の書き換え対応表
| 対象 | v2系(3.0.0未満) | v3系(3.1.1まで) | v4.3.0 |
|---|---|---|---|
| 起動 | $(el).raty({}) |
同左 | new Raty(el, {}).init() |
| 共通パス | $.raty.path = 'img' |
同左 | { path: 'img' } |
| キャンセル | { cancel: true } |
{ cancelButton: true } |
同左 |
| 関数呼び出し | $(el).raty('score') |
$(el).data('raty').score() |
raty.score() |
| clickの引数 | (score, evt) |
同左 | (score, element, evt) |
見落としやすいのはcancelの改名です。v4に{ cancel: true }を渡してもエラーは出ません。既定オプションにcancelというキーが無く、値がどこからも読まれないためです。キャンセルボタンだけが黙って消えます。v3.0.0ではdestroy・reload・setの3関数も削除されており、代わりのAPIはありません。これらを呼んでいる箇所は、インスタンスを作り直す設計に変えます。
raty-jsの導入と星の表示設定
最初のつまずきはパッケージ名です。npmでratyを引くと、koaベースのNode.jsフレームワーク(1.0.5・2017年公開)という無関係なパッケージが出ます。星評価プラグインはraty-jsです。
npm・CDN・ビルドファイルの選び分け
npm install raty-js
4.3.0のpackage.jsonはmainにsrc/raty.jsを指定しています。これはES Modulesなので、バンドラやimportmap経由なら次の形で読み込めます。
import Raty from 'raty-js';
const raty = new Raty(document.querySelector('#star'), { score: 3 });
raty.init();
scriptタグで読む場合はbuild/配下のファイルを使います。次のスクリプトより前に<div id="star"></div>を置いてください。build/raty.min.jsはクラス宣言を含む通常のスクリプトで、後続のスクリプトからRatyという名前で参照できます。ただしwindow.Ratyはundefinedです。クラス宣言はグローバルオブジェクトのプロパティにならないため、window.Ratyを前提にしたコードは失敗します。
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/raty.min.js"></script>
<script>
new Raty(document.querySelector('#star'), { path: '/raty' }).init();
</script>
UMD版のbuild/raty.umd.min.jsだけは形が違い、globalThis.ratyに名前空間を置きます。このファイルでは素のRatyが未定義で、クラスはwindow.raty.defaultから取ります。AMDやCommonJSと共存させる必要が無ければraty.min.jsを選んでください。
画像版の星表示とpath・PNGの配置
既定のstarTypeはimgで、星をPNG画像で描きます。パッケージのsrc/images/にはstar-on.png・star-off.png・star-half.png・cancel-on.png・cancel-off.pngの5枚が入っています。半星とキャンセルボタンを使わないなら前の2枚で足ります。
pathを省くと、imgのsrcはディレクトリ無しのstar-on.pngになり、ページURLからの相対で解決されます。/products/12のようなURLでは/products/star-on.pngを探しに行き、そこに画像が無ければ404です。画像を置いた場所と一致する配信ディレクトリを指定してください。
フォント版の星表示とstarType: ‘i’の設定
画像を置きたくなければstarType: 'i'を指定します。星はi要素で生成され、star-on-png・star-off-pngといったクラスが付きます。見た目はsrc/raty.cssのWebフォントが担い、大きさの既定はfont-size: 2emです。このCSSは@font-faceのsrcを./fonts/raty.eotのような相対パスで書いているため、CSSだけ置いてフォントを置き忘れると星の位置に何も出ません。raty.cssと同じ階層にfonts/ごと配置してください。
主要オプションとinit()で生成されるDOM
既定オプションは35個あります。実装で触るのは次の一部です。
| オプション | 既定値 | 役割 |
|---|---|---|
score |
undefined |
初期スコア |
scoreName |
'score' |
hidden fieldのname |
number |
5 |
星の数 |
numberMax |
20 |
星の数の上限 |
path |
undefined |
アイコンの配信ディレクトリ |
starType |
'img' |
imgかi |
readOnly |
false |
読み取り専用 |
half |
false |
半星での投票 |
halfShow |
true |
半星での表示 |
cancelButton |
false |
取り消しボタン |
noRatedMsg |
'Not rated yet!' |
未評価時のツールチップ |
hints |
['bad', … 'gorgeous'] |
星ごとのツールチップ |
v4のコンストラクタはオプションを組み立てるだけで、DOMには触れません。描画はinit()の中です。v2系の$(el).raty()は呼んだ瞬間に描画されたので、移行時にinit()を書き忘れるとdivが空のまま、例外も出ません。
init()を通すと、要素の中に星とinput type="hidden"が追加されます。{ score: 3 }ならhidden fieldはname="score" value="3"で、フォーム送信で評価値が飛ぶのはこの要素のおかげです。Railsのreview[rating]形式にしたいときはscoreName: 'review[rating]'を渡します。
注意点として、init()は既存の中身を消さずに追記します。同じ要素で2回呼ぶと星が10個、hidden fieldが2個になります(jsdomとraty-js 4.3.0で確認)。この性質がTurbo環境の不具合の原因になります。
readOnlyによる投稿済み評価の表示
readOnly: trueにすると、v4では要素にpointer-events: noneが当たり、data-read-only="true"が付き、hidden fieldにreadonly属性が入ります。スコア未設定のまま読み取り専用にすると全ての星のツールチップがNot rated yet!になるので、日本語サイトではnoRatedMsg: 'まだ評価がありません'のように差し替えます。
half・halfShow・roundによる半星の入力と表示
表示用のhalfShowと投票用のhalfは別物です。halfShowは既定で有効で、小数スコアを半星のアイコンで描くだけです。半星単位で投票させるならhalf: trueを足します。
丸めの境界はroundで、既定値は{ down: 0.25, full: 0.6, up: 0.76 }です。halfShowが有効なとき、小数部が0.25以下なら切り捨て、0.76以上なら切り上げ、その間は半星です。平均4.2なら星4つ、3.5なら星3つと半星になります。丸めるのは見た目だけで、hidden fieldには小数のまま入ります。
インスタンスから呼ぶ関数
const raty = new Raty(document.querySelector('#star'), { path: '/raty' }).init();
raty.score(2); // スコアを設定
raty.score(); // 現在のスコアを取得(2)
raty.click(4); // 4番目の星をクリックしたことにする
raty.cancel(true); // 評価を取り消す
raty.readOnly(true); // 読み取り専用に切り替える
init()は自身を返すので、変数に受けておけば後から操作できます。cancel()とscore(値)は読み取り専用の間は何もしないため、readOnly(true)は最後に呼びます。
Rails 8.1での星評価レビュー機能の実装
検索結果の上位にあるRailsの手順記事は、どれもjQueryとSprocketsが前提のRails 5.2時代のものです。Rails 8.1.4の既定(importmap・Propshaft・Turbo・Stimulus)で同じ機能を組み立てます。以下は、Productモデルとログイン中のユーザーを返すcurrent_userを持つRails 8.1.4のアプリへの組み込み例で、rails newした検証用アプリで動作を確かめています。ルーティングはresources :products do resources :reviews, only: [:create, :update] endのようにネストさせます。
decimal{3,1}によるrating列の追加
bin/rails generate migration AddReviewsCountToProducts reviews_count:integer
bin/rails generate model Review product:references user:references 'rating:decimal{3,1}'
bin/rails db:migrate
1行目はcounter_cache用の件数列です。生成されたマイグレーションにdefault: 0, null: falseを足しておくと、件数がnilになりません(既存データがあるならProduct.reset_countersで数え直します)。この列が無いままcounter_cache: trueにすると、レビューの保存時にエラーになります。2行目の波括弧の指定でprecision: 3, scale: 1が入ります。rating:decimalだけでは精度が付きません(シェルが波括弧を展開しないよう引用符で囲みます)。ジェネレータの書式はRailsジェネレータ(rails generate)の使い方|生成ファイル一覧と取り消し・カスタム生成【Rails 8.1.4】にまとめています。モデルでは関連と、評価として許可する値を定義します。
class Product < ApplicationRecord
has_many :reviews, dependent: :destroy
end
class Review < ApplicationRecord
belongs_to :product, counter_cache: true
belongs_to :user
validates :rating, presence: true,
inclusion: { in: (1..10).map { |n| n / 2r } } # 0.5〜5.0の0.5刻み
validates :user_id, uniqueness: { scope: :product_id }
end
半星で入力させるので、許可する値を0.5刻みの集合で縛ります。Rails 8.1.4で試すと0.1と5.5は弾かれ、2.5・3・5は通りました。numberで星の数を変えたり半星をやめたりしたら、この集合も合わせます。画面で選べない値がバリデーションだけ通る状態を作らないためです。User側にはhas_many :reviewsを足します。
bin/importmap pin raty-jsによる読み込み設定
bin/importmap pin raty-js
importmap-rails 2.2.3で実行すると、ga.jspm.io/npm:[email protected]/src/raty.jsがvendor/javascript/raty-js.jsに保存され、config/importmap.rbにpin "raty-js" # @4.3.0が追記されました。保存されたファイルはexport{Raty as default}で終わるES Modulesなので、import Raty from "raty-js"で読めます。npmもNode.jsも要りません。app/assets/javascripts/にjquery.raty.jsを置き、Sprocketsの//= requireで結合する手順は、Propshaftではそのまま使えません。
Propshaftのダイジェストと星のPNGの置き場所
Propshaftは通常、配信するアセットのファイル名にダイジェストを付けます(star-on-1a2b3c4d.pngの形)。Ratyのpathはディレクトリにstar-on.pngを連結するだけなので、app/assets/images/に置いた画像はディレクトリ指定では404になります。5枚の静的画像なので、node_modules/raty-js/src/images/またはGitHubからpublic/raty/へコピーし、path: "/raty"で参照するのが簡単です。ダイジェスト付きで配信したい場合はpath: ""にしてstarOn: asset_path("raty/star-on.png")のようにアイコンごとのURLを渡します。Propshaftのコマンドの役割はrails assets:clobberとは?cleanとの違いと消える範囲【Rails 8.1・Propshaft 1.3実測】で整理しています。
評価フォームとStrong Parametersの実装
ビューではtag.divでdata属性を組み立てます。初期化は次章のStimulusコントローラが担うので、data-controller="raty"を付けるだけです。Ratyは要素のdata-*を読み込むので、score_nameはscoreName、halfはhalfオプションとして効きます。
<%= form_with model: [@product, @review] do |f| %>
<%= tag.div(data: { controller: "raty", score_name: "review[rating]",
score: @review.rating&.to_f, half: true }) %>
<%= f.submit "評価を送信" %>
<% end %>
@review.ratingがnilならdata-score属性ごと出力されません。to_fを挟むのは、decimal列がBigDecimalで返り、そのままだとdata-score="0.25e1"という指数表記になるためです。
def create
@review = @product.reviews.build(review_params)
@review.user = current_user
if @review.save
redirect_to @product, notice: "評価を保存しました"
else
render "products/show", status: :unprocessable_content
end
end
private
def review_params
params.expect(review: [:rating])
end
Rack 3.1以降は:unprocessable_entityが非推奨で、Rails 8.1のscaffoldも:unprocessable_contentを生成します。Turbo Driveはフォーム送信の応答に422を返すと画面を描き直すので、バリデーションエラー時はこのステータスが要ります。
商品一覧での平均評価の読み取り専用表示
# app/controllers/products_controller.rb
def index
@products = Product.all
@average_ratings = Review.where(product: @products).group(:product_id).average(:rating)
end
<% @products.each do |product| %>
<% average = @average_ratings[product.id]&.round(1) %>
<%= tag.div(data: { controller: "raty", read_only: true, score: average&.to_f }) %>
<%= average ? "#{average}(#{product.reviews_count}件)" : "評価はまだありません" %>
<% end %>
評価が2件(3.5と4.0)ならdata-score="3.8"と「3.8(2件)」が出ます。平均はgroup(:product_id).averageで商品ごとにまとめて1回で取り、件数はcounter_cacheの列から読むので、商品の数だけAVGやCOUNTを発行せずに済みます。持ち方はcounter_cacheとは?Railsでの実装手順とカウントずれ・後付けの対処を参照してください。
Turbo Driveでの星の消失・重複とStimulusによる対策
Rails 7以降のTurbo Driveを使うアプリでRatyを入れると起こりやすいのが、「最初の画面では星が出るのに、リンクで移動すると出ない」「ブラウザの戻るで星が10個並ぶ」という症状です。どちらもRatyの不具合ではなく、Turbo Driveの仕組みとinit()の追記動作の組み合わせで起きます。
Turbo遷移後にDOMContentLoadedが発火しない理由
古い記事はDOMContentLoadedや$(function(){ … })の中で初期化しています。Turboのハンドブックにあるとおり、Turbo Driveはページ遷移のたびにbodyを差し替えるだけで、DOMContentLoadedは最初の読み込みで1回しか発火しません。2ページ目以降の星評価のdivは空のまま残ります。
戻る操作で星が10個になる仕組み
ではturbo:load(初回と毎回の遷移後に発火)で初期化すればよいかというと、今度は戻る操作で星が二重になります。Turbo 8.0.23のソースでは、ページを離れる前にturbo:before-cacheを発火し、その時点のDOMを複製してキャッシュに入れます。星を描画済みのDOMがそのままキャッシュされ、戻ったときに描画済みの要素へもう一度init()するので、星10個・hidden field 2個になります。
StimulusコントローラによるRatyの初期化
確実なのは、Stimulusのコントローラで初期化し、connect()の最初で要素の中身を消すことです。Stimulusのライフサイクルでは、要素がDOMに入るたびにconnect()が呼ばれます。初回表示でも、Turboがbodyを差し替えたときやキャッシュから復元したときでも、要素が接続されるたびに通ります。ここでreplaceChildren()してからinit()すれば、何度通っても星は5個です。
bin/rails generate stimulus raty
生成されたapp/javascript/controllers/raty_controller.jsを次のように書き換えます。Rails 8.1の既定構成ではcontrollers/index.jsが全コントローラを自動登録するので、追加の登録は要りません。
import { Controller } from "@hotwired/stimulus"
import Raty from "raty-js"
// Connects to data-controller="raty"
export default class extends Controller {
static values = { path: { type: String, default: "/raty" } }
connect() {
const field = this.element.querySelector('input[type="hidden"]')
if (field?.value) this.element.dataset.score = field.value
this.element.replaceChildren()
this.raty = new Raty(this.element, { path: this.pathValue }).init()
}
disconnect() {
this.raty = null
}
}
replaceChildren()の前にhidden fieldの値をdata-scoreへ移しているのは、ユーザーが選び直した評価をキャッシュ復元後も残すためです。消してから作り直すだけだと、サーバーが出力した最初のdata-scoreに戻ってしまいます。jsdomとStimulus 3.2.2で、bodyを複製して差し替える操作(Turboのキャッシュ復元と同じ形)を再現したところ、connect()は2回目も呼ばれ、星5個・hidden field 1個のまま、2から5へ選び直した評価も5で残りました。data-controllerなどRaty宛てでないdata-*もオプションに読み込まれますが、Ratyは知らないキーを使わないので害はありません。Turbo全体の仕組みはTurboとは?Ruby on RailsでHotwireの画面更新を実装する使い方【Turbo 8.0.23】、TurboとStimulusの役割分担はHotwireとは?Turbo・Stimulus・Hotwire Nativeの役割とRails 8.1での使い方で解説しています。
clickによる評価のAjax保存とCSRFトークンの送信
星をクリックした瞬間に保存したい場合は、clickコールバックから送信します。v4のコールバックは(score, element, evt)を受け取り、thisはRatyのインスタンスです。falseを返すとクリックが取り消されます。
Stimulusコントローラへのsaveメソッドの追加
import { Controller } from "@hotwired/stimulus"
import Raty from "raty-js"
export default class extends Controller {
static values = { path: { type: String, default: "/raty" }, url: String }
connect() {
const field = this.element.querySelector('input[type="hidden"]')
if (field?.value) this.element.dataset.score = field.value
this.element.replaceChildren()
const controller = this
this.raty = new Raty(this.element, {
path: this.pathValue,
click(score) {
if (!controller.hasUrlValue) return
if (controller.saving) return false
controller.save(score, this.score())
}
}).init()
}
disconnect() {
this.raty = null
}
async save(score, previous) {
this.saving = true
try {
const token = document.querySelector('meta[name="csrf-token"]')?.content
const response = await fetch(this.urlValue, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
"X-CSRF-Token": token
},
body: JSON.stringify({ review: { rating: score } })
})
if (!response.ok) throw new Error(`HTTP ${response.status}`)
this.element.dataset.score = score
} catch (error) {
this.raty?.score(previous)
alert("評価を保存できたか確認できません。再読み込みして確認してください。")
} finally {
this.saving = false
}
}
}
コールバックの中のthis.score()は、クリックが反映される前の値を返します。これをpreviousとして持っておき、サーバーが失敗を返したときや通信自体が失敗したときはscore(previous)で元の星に戻します。通信が切れた場合はサーバー側だけ保存できていることもあるので、通知の文面は「保存できなかった」と断定しません。送信中のクリックはfalseを返して無視し、応答の順番が入れ替わって画面とサーバーの値がずれるのを防ぎます。jsdomで、4から3をクリックして送信中に5をクリックすると5は無視され、3が422で返ると4に戻り、通信例外でも4に戻り、2を204で保存すると2になることを確認しました。ビューではdata-raty-url-valueに更新先を渡します。
<%= tag.div(data: { controller: "raty", score: review.rating.to_f,
raty_url_value: product_review_path(@product, review) }) %>
updateアクションの成功204・失敗422の応答
def update
@review = current_user.reviews.find(params[:id])
if @review.update(review_params)
head :no_content
else
head :unprocessable_content
end
end
レイアウトのcsrf_meta_tagsが出力するcsrf-tokenをX-CSRF-Tokenヘッダーに入れるのは、Railsガイドが示すfetchでの作法です。Rails 8.1.4のアプリで試すと、ヘッダー無しのPATCHはActionController::InvalidAuthenticityTokenで422、ヘッダー付きは204、範囲外の9を送ると422でした。
評価の所有者確認による不正更新の防止
readOnly: trueはブラウザ上のクリックを止めるだけで、リクエストを直接組み立てれば任意の値を送れます。上のcurrent_user.reviews.findのように、更新対象をログイン中のユーザーの評価に限定するのがサーバー側の守りです。1人1件に保つなら、モデルのvalidates :user_id, uniqueness: { scope: :product_id }に加えて、DBにも[:product_id, :user_id]の複合ユニークインデックスを置きます。認証の組み込みはSorcery gemの使い方|Rails 8.1で動くログイン実装と0.18.0の注意点で比較しています。
星が出ないときの原因切り分け
まずブラウザのコンソールを見ます。エラーが出ていれば記法か参照名、出ていなければinit()・path・Turboを疑います。
| 症状 | 原因 | 対処 |
|---|---|---|
$(...).raty is not a function |
v4に旧記法 | new Raty()へ書き換え |
window.Raty is not a constructor |
window.Ratyを参照 |
素のRatyを使う |
Raty is not defined |
UMD版を読み込み | window.raty.default |
| divが空・エラーなし | init()の呼び忘れ |
生成直後にinit() |
| 画像が壊れて表示 | path未指定・ダイジェスト |
public/ratyとpath |
| 遷移後のページで星が出ない | DOMContentLoadedで初期化 |
Stimulusで初期化 |
| 戻ると星が10個 | キャッシュ復元後に再init() |
replaceChildren()してから |
もう一つ、data属性がJSのオプションに勝つ点も原因になります。src/raty.jsのコンストラクタは既定値・引数・要素のdatasetの順にオブジェクトを展開するので、data-score="5"の要素に{ score: 1 }を渡しても5になります。サーバーが出力したdata属性が共通設定を黙って上書きするので、値が想定と違うときはHTMLのdata属性から確認してください。
Ratyの採用を避ける条件とモデル層との役割分担
Ratyは依存が無く、hidden fieldの生成まで面倒を見てくれます。アクセシビリティと保守体制は採用を見送る判断材料に、集計機能はモデル層との役割分担として確認します。
キーボード操作とスクリーンリーダー対応が要件のフォーム
src/raty.jsにはrole・aria-*・tabindex・keydownのいずれも実装されておらず、リスナーはclick・mouseover(half有効時はmousemove)・mouseleaveで、キーボードイベントには対応していません。キーボードだけでは評価を選べません。アクセシビリティ要件があるなら、ネイティブのinput type="radio"をCSSで星に見せる方が、追加するコードは少なく済みます。
集計・スコープ付き評価をモデル層で扱いたい場合
作者はREADMEで「Rating for Rails?」として別のgem(rating)を案内しています。rating 2.1.0は2026-06-30公開で、平均の集計やスコープ付きの評価をモデル側で扱います。表示はRaty、データはgemという分担が作者の想定です。
継続的な更新を前提にした長期保守
4.3.0は2024-04-28のリリースで、リポジトリへの最終pushも同日です。2026-11-02時点でオープンなissueは52件、pull requestは7件残っています。アーカイブはされておらず、MITライセンスで本体は1ファイルなので自前で保守はできますが、更新が続く前提では選べません。レビュー機能を含む業務システムを長く運用するなら、評価UIを自前のコンポーネントで持つ選択肢も含めて検討してください。要件整理から作り込みまでを任せる場合はフルスクラッチ開発で相談を受けています。
よくある質問
星の数を5個から増やせますか?
numberオプションで変更できます。上限はnumberMaxの既定値20で、{ number: 30 }を渡しても20個で止まります。それ以上ならnumberMaxも引き上げ、サーバー側のバリデーション上限も合わせてください。
星の色や大きさは変えられますか?
starType: 'i'のフォント版にすると、star-on-pngなどのクラスにCSSでcolorとfont-sizeを当てられます。画像版のままなら、starOn・starOff・starHalfに自作PNGのファイル名を渡します。
古いjQuery記法のまま動かし続けられますか?
できます。$.fn.ratyを持つ最後の版はraty-js 3.1.1(2021-09-30公開)で、依存にjQuery ^3.6が入っています。npm install [email protected]で固定すれば既存コードは動きますが、2021年で止まった系列なので延命策と考えてください。
Rails 8で星が表示されないのはなぜですか?
多いのは3つです。Sprockets前提の//= requireでファイルを結合していてPropshaftで読み込まれていない、DOMContentLoadedで初期化していてTurboの遷移後に動かない、画像がダイジェスト付きでpathから見つからない、のいずれかです。bin/importmap pin raty-js・Stimulusでの初期化・public/ratyの3点で解消します。
読み取り専用にすれば評価を改ざんされませんか?
防げません。readOnly: trueが行うのはpointer-events: noneとreadonly属性の付与だけです。評価を更新できるかどうかはコントローラ側で必ず検証してください。