【Rails基礎】Active Recordとは？


はじめに
---
ActiveRecordsについてあまり理解していないので、今回深掘りしてみました。

**Active Record**とは**Rails**における**Object/Relational Mapping (ORM)** です。
※訳：オブジェクト関係マッピング 


**Object/Relational Mapping (ORM)とは？🤔**

以下Wekipediaより
>オブジェクト関係マッピング（英: Object-relational mapping、O/RM、ORM）とは、データベースとオブジェクト指向プログラミング言語の間の非互換なデータを変換するプログラミング技法である

**つまり、RubyでDBを操作するための技法ということですね！！👏**

RailsアプリでDBに変更を加えるとき、Rubyのコードで直感的にDBを操作することができるのもActive Record様のおかげ

また、ActiveRecord様のおかげでDBの種類(MySQL, PostgreSQL等)に関わらず同じコードで操作可能になります！


Active Recordはどのように機能している？
---

では、お馴染みのrails g modelをして、DBにテーブルを追加して行きます！

```
rails g model Post title:string
```
```
create	db/migrate/20220826_create_post.rb
create	app/models/post.rb
```

このファイルはどこから来ているのでしょうか？👀
これはrails g modelコマンドを実行時にrailsの[model_generator.rb](https://github.com/rails/rails/blob/main/activerecord/lib/rails/generators/active_record/model/model_generator.rb#L18)が呼び出されています！

コードを確認してみます！

```ruby
....
# creates the migration file for the model.
      def create_migration_file
        return if skip_migration_creation?
        attributes.each { |a| a.attr_options.delete(:index) if a.reference? && !a.has_index? } if options[:indexes] == false
        migration_template "../../migration/templates/create_table_migration.rb", File.join(db_migrate_path, "create_#{table_name}.rb")
      end

      def create_model_file
        generate_abstract_class if database && !custom_parent?
        template "model.rb", File.join("app/models", class_path, "#{file_name}.rb")
      end
....
```

1つ目のメソッドはマイグレーションファイル、
2つ目のメソッドはモデルファイルを生成していますね！！

下記の記述をさら見てみます
```ruby
migration_template "../../migration/templates/create_table_migration.rb", File.join(db_migrate_path, "create_#{table_name}.rb")
```

[create_table_migration.rb](https://github.com/rails/rails/blob/main/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt)

```:create_table_migration.rb
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
  def change
    create_table :<%= table_name %><%= primary_key_type %> do |t|
<% attributes.each do |attribute| -%>
<% if attribute.password_digest? -%>
      t.string :password_digest<%= attribute.inject_options %>
<% elsif attribute.token? -%>
      t.string :<%= attribute.name %><%= attribute.inject_options %>
<% elsif attribute.reference? -%>
      t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %><%= foreign_key_type %>
<% elsif !attribute.virtual? -%>
      t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %>
<% end -%>
<% end -%>
<% if options[:timestamps] %>
      t.timestamps
<% end -%>
    end
<% attributes.select(&:token?).each do |attribute| -%>
    add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>, unique: true
<% end -%>
<% attributes_with_index.each do |attribute| -%>
    add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
<% end -%>
  end
end
```

なんかみたことある形....

これは、、
rails g modelのときに生成されるマイグレーションファイルと同じフォーマットですね！！！
```
class CreatePost < ActiveRecord::Migration
    def change
        create_table :post do |t|
            t.integer :title
 
            t.timestamps
        end
    end
end
```

ということは、rails g model実行時に渡したモデル名やカラムが
ファイルを生成時に使われていることがわかりますね！

次にrailsdb:migrateを実行し、DBにテーブルを作ります



ということは、rails g model実行時に渡したモデル名やカラムが
ファイルを生成時に使われていることがわかりますね！

次にrailsdb:migrateを実行し、DBにテーブルを作ります。
では、データを入れて行きましょう！


`post = Post.new title: "This is title"`

Postのインスタンスを生成してみます。
次にDBに保存するために、`post.save`をします。

```
INSERT INTO "post" ("title", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?)  [["title", "This is title"], ["created_at", Fri, 26 Aug 2022 18:20:12 UTC +00:00], ["updated_at", Fri, 26 Aug 2022 18:20:12 UTC +00:00]]
```
DBに保存時[このメソッド](https://github.com/rails/rails/blob/main/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb#L139)を使用しています。
```:rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb 
# Executes an INSERT query and returns the new record's ID
#
# +id_value+ will be returned unless the value is +nil+, in
# which case the database will attempt to calculate the last inserted
# id and return that value.
#
# If the next id was calculated in advance (as in Oracle), it should be
# passed in as +id_value+.
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
  sql, binds = to_sql_and_binds(arel, binds)
    value = exec_insert(sql, name, binds, pk, sequence_name)
    id_value || last_inserted_id(value)
  end
```

まとめ
---
Active Recordのおかげで、直感的にDB操作することが可能になることがわかりました。

Active Recordに関わらず、Railsはあまり深い理解をせずとも動いてくれるので、
裏でどのようになっているかはしっかりと理解する必要があると感じました。
今後もどのようなロジックで動いているのかしっかり調べていこうと思いました。