【WIP】Rails7.1での変更点

こんにちは。プログラミングスクール[Happiness Chain](https://happiness-chain.com/)でメンターをしているryoです。
今回は、最近リリースされたRails7.1の変更点に関して、記載していきます。

※以下公式リリースノートの和訳と、個人的な感想を少し記載しています。

https://rubyonrails.org/2023/10/5/Rails-7-1-0-has-been-released

※ こちら執筆途中になりますので、ご了承ください。


## Railsアプリ新規作成時に、Dockerfileの自動生成

`rails new sample-project`をすると以下のDockerfileが自動生成される。

```Dockerfile
# syntax = docker/dockerfile:1

# Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile
ARG RUBY_VERSION=3.2.2
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base

# Rails app lives here
WORKDIR /rails

# Set production environment
ENV RAILS_ENV="production" \
    BUNDLE_DEPLOYMENT="1" \
    BUNDLE_PATH="/usr/local/bundle" \
    BUNDLE_WITHOUT="development"


# Throw-away build stage to reduce size of final image
FROM base as build

# Install packages needed to build gems
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y build-essential git libvips pkg-config

# Install application gems
COPY Gemfile Gemfile.lock ./
RUN bundle install && \
    rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
    bundle exec bootsnap precompile --gemfile

# Copy application code
COPY . .

# Precompile bootsnap code for faster boot times
RUN bundle exec bootsnap precompile app/ lib/

# Precompiling assets for production without requiring secret RAILS_MASTER_KEY
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile


# Final stage for app image
FROM base

# Install packages needed for deployment
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y curl libsqlite3-0 libvips && \
    rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Copy built artifacts: gems, application
COPY --from=build /usr/local/bundle /usr/local/bundle
COPY --from=build /rails /rails

# Run and own only the runtime files as a non-root user for security
RUN useradd rails --create-home --shell /bin/bash && \
    chown -R rails:rails db log storage tmp
USER rails:rails

# Entrypoint prepares the database.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]

# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
CMD ["./bin/rails", "server"]

```


本番環境にチューニングされたチューニングされたDockerfile（適切なlayer、image sizeを最小化するためにmulti-stage buildの利用）ということで、実際にrails newしましたけど、multi-stage buildの記載になってたり、bootsnapでのpre compileとassetのprecompileなど既に記載されてて、そこまでDockerfileをいじる必要なさそう。


# 独自の認証機能の改善された

`has_secure_password`を保管するための機能として以下の通り。

- `normalizes`
    - カラムを保存時に正規化してくれる（検索時も）
```rb
user = User.create(email: " CRUISE-CONTROL@EXAMPLE.COM\n")
user.email                  # => "cruise-control@example.com"

user = User.find_by(email: "\tCRUISE-CONTROL@EXAMPLE.COM ")
user.email                  # => "cruise-control@example.com"
```

- `authenticate_by`
  - パスワード以外のカラムでレコードを見つけ、パスワードで認証し、成功の場合は、レコードを返し、失敗の場合nilを返す（受け取ったパスワードは常にdigest化する）
  - これにより、ブルーフォース攻撃の軽減となる
```rb
class User < ActiveRecord::Base
  has_secure_password
end

User.create(name: "John Doe", email: "jdoe@example.com", password: "abc123")

User.authenticate_by(email: "jdoe@example.com", password: "abc123").name # => "John Doe" (in 373.4ms)
User.authenticate_by(email: "jdoe@example.com", password: "wrong")       # => nil (in 373.9ms)
```

- `generates_token_for`
    - passwordをリセットする際に使うようなtokenを発行することができる。
```rb
class User < ApplicationRecord
  has_secure_password

  generates_token_for :password_reset, expires_in: 15.minutes do
    # Last 10 characters of password salt, which changes when password is updated:
    password_salt&.last(10)
  end
end

user = User.first

token = user.generate_token_for(:password_reset)
User.find_by_token_for(:password_reset, token) # => user
# 16 minutes later...
User.find_by_token_for(:password_reset, token) # => nil

has_secure_passwordに関しては、
パスワード更新時に、自動的に検証できるようになった
（passwordに新しいパスワード、password_challengeに現在のパスワードを渡すことで、パスワードの更新が可能）
# Schema: User(name:string, password_digest:string)
class User < ActiveRecord::Base
  has_secure_password
end

user = User.new(name: "rafael", password: "railsrocks", password_confirmation: "railsrocks")
user.save                                                                      # => true
user.update(password: "pwn3d", password_challenge: "")                         # => false, challenge doesn't authenticate
user.update(password: "railsGetsEvenBetter", password_challenge: "railsrocks") # => true
```

- `has_secure_password`
    - パスワード更新時に、自動的に検証できるようになった
    （passwordに新しいパスワード、password_challengeに現在のパスワードを渡すことで、パスワードの更新が可能）
```rb
# Schema: User(name:string, password_digest:string)
class User < ActiveRecord::Base
  has_secure_password
end

user = User.new(name: "rafael", password: "railsrocks", password_confirmation: "railsrocks")
user.save                                                                      # => true
user.update(password: "pwn3d", password_challenge: "")                         # => false, challenge doesn't authenticate
user.update(password: "railsGetsEvenBetter", password_challenge: "railsrocks") # => true
```

rails標準の認証が、以前よりも楽にセキュアに書けるようになった印象。

# 非同期クエリサポートの拡張

遅いクエリ、特に計算に重点を置いたクエリを、非同期で実行できるようになった。
追加されたメソッドは以下の通り。
- async_count
- async_sum
- async_minimum
- async_maximum
- async_average
- async_pluck
- async_pick
- async_ids
- async_find_by_sql
- async_count_by_sql

これはとてもありがたい。


# 現在執筆中です🙇