Rails6 のちょい足しな新機能を試す76（delete_by destroy_by 編）

# はじめに

（多分）Rails 6 に追加された新機能を試す第76段。 今回は、 `delete_by destroy_by` 編です。
Rails 6 では、 ActvieRecord に `delete_by`  と `destroy_by` が追加されました。
引数に条件を指定して、レコードを削除することができるようになりました。

Ruby 2.6.3, Rails 6.0.0.rc2 で確認しました。

Rails 6.0.0.rc2 は `gem install rails -v 6.0.0rc2 --prerelease` でインストールできます。
(確認は、Rails 6.0.0 リリース前に実施したものです。)

```shell
$ rails --version
Rails 6.0.0.rc2
```

# プロジェクトを作る

```shell
$ bin/rails new rails_sandbox
$ cd rails_sandbox
```

# Library と Book モデルを作る

Library は複数の Book オブジェクトを保持します。

```shell
$ bin/rails g model library name
$ bin/rails g model book title libray:references
```

# Library モデルを修正する

`has_many` 関係を追加します。

```ruby:app/models/library.rb
class Library < ApplicationRecord
  has_many :books, dependent: :destroy
end
```

# seed データを作る

Library, Book ともに複数のデータを作成します。

```ruby:db/seeds.rb
Library.create(
  [
    {
      name: 'Tokyo',
      books: Book.create(
        [
          { title: 'Programming Ruby' },
          { title: 'The Pragmatic Programmer' },
          { title: 'Agile Web Development with Rails' }
        ]
      )
    },
    {
      name: 'Kanagawa',
      books: Book.create(
        [
          { title: 'Programming Ruby' }
        ]
      )
    },
    {
      name: 'Chiba',
      books: Book.create(
        [
          { title: 'Programming Ruby' },
          { title: 'The Pragmatic Programmer' }
        ]
      )
    }
  ]
)
```

# seed データの投入

DB マイグレーションを実行しデータを登録します。

```shell
bin/rails db:create db:migrate db:seed
```

# Rails Console で確認する

`rails console` で確認します。

Kanagawa Library の Book を `delete_by` で削除します。

まずは、 `Kanagawa` で Library を検索します。

```irb
irb(main):001:0> library = Library.find_by(name: 'Kanagawa')
  Library Load (0.2ms)  SELECT "libraries".* FROM "libraries" WHERE "libraries"."name" = $1 LIMIT $2  [["name", "Kanagawa"], ["LIMIT", 1]]
=> #<Library id: 2, name: "Kanagawa", created_at: "2019-08-17 00:04:21", updated_at: "2019-08-17 00:04:21">
```

`delete_by` で title を条件に指定してデータを削除します。

```irb
irb(main):002:0> library.books.delete_by(title: 'Programming Ruby')
  Book Destroy (7.4ms)  DELETE FROM "books" WHERE "books"."library_id" = $1 AND "books"."title" = $2  [["library_id", 2], ["title", "Programming Ruby"]]
=> 1
```

`delete_by` で Library も削除します。

```irb
irb(main):003:0> Library.delete_by(name: 'Kanagawa')
  Library Destroy (8.1ms)  DELETE FROM "libraries" WHERE "libraries"."name" = $1  [["name", "Kanagawa"]]
=> 1
```

Tokyo Library の Book を `destroy_by` で削除します。

まずは、 Tokyo の Library を検索します。

```irb
irb(main):004:0> library = Library.find_by(name: 'Tokyo')
  Library Load (0.7ms)  SELECT "libraries".* FROM "libraries" WHERE "libraries"."name" = $1 LIMIT $2  [["name", "Tokyo"], ["LIMIT", 1]]
=> #<Library id: 1, name: "Tokyo", created_at: "2019-08-17 00:04:21", updated_at: "2019-08-17 00:04:21">
```

`destroy_by` を使って Book を削除します。

```irb
irb(main):005:0> library.books.destroy_by(title: 'The Pragmatic Programmer')
  Book Load (0.2ms)  SELECT "books".* FROM "books" WHERE "books"."library_id" = $1 AND "books"."title" = $2  [["library_id", 1], ["title", "The Pragmatic Programmer"]]
   (0.2ms)  BEGIN
  Book Destroy (0.3ms)  DELETE FROM "books" WHERE "books"."id" = $1  [["id", 2]]
   (3.3ms)  COMMIT
=> [#<Book id: 2, title: "The Pragmatic Programmer", library_id: 1, created_at: "2019-08-17 00:04:21", updated_at: "2019-08-17 00:04:21">]
```

Library を `destroy_by` で削除します。

```irb
irb(main):006:0> Library.destroy_by(name: 'Chiba')
  Library Load (0.6ms)  SELECT "libraries".* FROM "libraries" WHERE "libraries"."name" = $1  [["name", "Chiba"]]
   (0.3ms)  BEGIN
  Book Load (0.6ms)  SELECT "books".* FROM "books" WHERE "books"."library_id" = $1  [["library_id", 3]]
  Book Destroy (0.6ms)  DELETE FROM "books" WHERE "books"."id" = $1  [["id", 5]]
  Book Destroy (0.5ms)  DELETE FROM "books" WHERE "books"."id" = $1  [["id", 6]]
  Library Destroy (0.8ms)  DELETE FROM "libraries" WHERE "libraries"."id" = $1  [["id", 3]]
   (7.3ms)  COMMIT
=> [#<Library id: 3, name: "Chiba", created_at: "2019-08-17 00:04:21", updated_at: "2019-08-17 00:04:21">]
```

# 動作としては

`delete_by(*args)` は、 `where(*arg).delete_all` と
`destroy_by(*arg)` は、 `where(*arg).destroy_all` と

同じです。

# 試したソース

試したソースは以下にあります。
https://github.com/suketa/rails_sandbox/tree/try076_delete_by_destroy_by


# 参考情報

* [What is new in Rails 6.0](https://bogdanvlviv.com/posts/ruby/rails/what-is-new-in-rails-6_0.html)
* [Introduce delete_by and destroy_by methods to ActiveRecord::Relation](https://github.com/rails/rails/pull/35316)