Ruby on Railsでデータベースの情報が記載されている重要なファイルにdatabase.ymlがあります。
その中には次のような記述があります。
development: &default
adapter: postgresql
database: test-app_development
test:
<<: *default
database: test-app_test
production:
<<: *default
database: test-app_production
「&default」や「<<」を使った「<<: *default」という記述があります。
ここではそれぞれの記述が何をしているかについて解説してます。
ここではdatabase.ymlを例として取り上げていますが、全ての拡張子「.yml」または「.yaml」のファイルに共通する書き方なので、他のファイルでも応用できます。
「&default」と「<<: *default」が何をしているか
結論からいうと、「&default」はそれ以降の記述を「default」というエイリアスに代入しています。いわゆる変数への代入のようなものです。
そして、「<<: *default」はその内容を呼び出しています。
つまり、以下の記述によって、
development: &default
adapter: postgresql
database: test-app_development
「default」というエイリアスの中に、
adapter: postgresql
database: test-app_development
が代入されます。
そして、
production:
<<: *default
database: test-app_production
とすることで、「<<: *default」の部分にその内容を呼び出しています。つまり、上記は以下と同じになります。
production:
adapter: postgresql
database: test-app_development
database: test-app_production
「&」「*」「<<」の概要
.ymlファイルでは「&」で任意の名前を指定して、その後ろに値を記述することで、指定した名前の中に値を代入することができます。
いわゆる変数のようなものです。この名前を「エイリアス」と呼びます。「エイリアス」とは日本語で「別名」という意味です。
そして、そのエイリアスの内容を「*」で呼び出すことができます。
「<<」はその応用で、エイリアスをオブジェクトの中でマージするために使います。
以下では、基本となる「&」と「*」の使い方と、応用となる「<<」の使い方を実例で解説しています。
「&」と「*」とは何か?
ヤムル(.ymlや.yaml)では「&(アンド/アンパサンド)」を使って名前を付け、「*(アスタリスク)」でアンカー名を指定して対象の値を呼び出すことができます。
「&」指定した名前の目印をつけるため「アンカー」(またはアンカリング)とよび、つけた名前を「エイリアス」と呼びます。エイリアスとは「別名」という意味です。
「&」の使い方
単一の値を指定する場合
「&」を使うときは、「&」の後ろに空白を開けずに、エイリアス名を記載します。その後ろに空白を空けて値を記述します。
&エイリアス 値
これで、値が指定したエイリアスの中に入ります。このエイリアスが変数的な働きになります。
オブジェクトの中の値を指定する場合
オブジェクトの中の値をエイリアスに代入したい場合は以下のようにします。
プロパティ名: &エイリアス
値1
値2
,,,,
「*」の使い方
「*」を使うときは、「*」の後ろに空白を開けずに、呼び出したいエイリアス(変数名)を記載します。
*エイリアス
すると、「&」で指定したエイリアスに対応する値を呼び出すことができます。
実例:単一データの場合
エイリアス「ankor」に1を代入して、「*ankor」で呼び出す場合は以下のようになります。
a:
- &ankor 1
- *ankor
↓ JSON形式にコンパイル
{ "a": [ 1, 1 ] }
実例:単一のオブジェクトの場合
エイリアス「x」に「a: 1」を代入して、「*x」で呼び出す場合は以下のようになります。
- &x a:1
- *x
↓ JSON形式にコンパイル
[ "a": 1, "a": 1 ]
実例:オブジェクトの値として呼び出す場合
エイリアス「x」に「1」を代入して、プロパティ名「b」の値の一つとして呼び出す場合は以下のようになります。
a:
- &x 1
- 2
b:
- 3
- *x
↓ JSON形式にコンパイル
{ "a": [ 1, 2 ], "b": [ 3, 1 ] }
実例:オブジェクトの中の複数データを代入する場合
オブジェクトの中の複数データをエイリアスに代入する場合は、プロパティ名の後ろに「&エイリアス」を記述して、その下に改行して値を記述します。
エイリアス「x」に[1, 2]を代入して、プロパティ名「b」の値として呼び出す場合は以下のようになります。
a: &x
- 1
- 2
b: *x
↓ JSON形式にコンパイル
{ "a": [ 1, 2 ], "b": [ 1, 2 ] }
エラー例:配列自体を代入することはできない
配列そのものをエイリアスに代入することはできません。
&x
- 1
- 2
*x
↓ JSON形式にコンパイル
Error : Unable to parse.
Line : 1 &x
「Unable to parse.(解析できません)」というエラーが発生します。
エラー例:オブジェクト自体を代入することはできない
オブジェクトそのものをエイリアスに代入することはできません。
&x :
- 1
- 2
*x
↓ JSON形式にコンパイル
Error : Unable to parse.
Line : 4 *x
「Unable to parse.(解析できません)」というエラーが発生します。
エラー例: 「*」でマージすることはできない
「*」ではオブジェクトの値の中に別の要素をマージすることはできません。(要素の一つとして指定したエイリアスを呼び出すことができません)
例えば、以下のようにするとエイリアス「x」に「c:1 d:2」を代入して、プロパティ名「b」の値として呼び出すことができます。
a: &x
c: 1
d: 2
b: *x
↓ JSON形式にコンパイル
{
"a": {
"c": 1,
"d": 2
},
"b": {
"c": 1,
"d": 2
}
}
しかし、このエイリアス「x」と「e: 3」をbの中の要素として呼び出そうとするとエラーが発生します。
a: &x
c: 1
d: 2
b:
*x
e: 3
↓ JSON形式にコンパイル
Error : Unable to parse.
Line : 5 *x
「Unable to parse.(解析できません)」というエラーが発生します。
上記のようにオブジェクトの値をマージさせたい場合は「<<」を使う必要があります。
<<(インジェクト)の使い方
「<<」とは何か?
「<<」はオブジェクトの中に値をマージするための記述です。
オブジェクトの値の中に注入するので、「インジェクト」と呼ばれます。
「<<」の使い方
「<<」は「&」と「*」と合わせて使います。
オブジェクトをマージする
以下のようにあるオブジェクトの中に、オブジェクト形式の複数の値が入っている場合に、その値をエイリアスとして設定する場合は「&」を使います。
オブジェクト名: &エイリアス
プロパティ名1: 値1
プロパティ名2: 値2
,,,,
これを、他のオブジェクトの中で呼び出して、他の要素とマージさせるためには、「<<: *エイリアス」を記述します。
オブジェクト名':
<<: *エイリアス
プロパティ名: 値
要素は「<<: *エイリアス」で指定した場所に入ります。
配列をマージする
以下のようにあるオブジェクトの中に、配列形式の複数の値が入っている場合に、その値をエイリアスとして設定する場合は「&」を使います。
オブジェクト名: &エイリアス
- 値1
- 値2
,,,,
この配列となっている値も、「<<: *エイリアス」を使うことで、他のオブジェクトの中で呼び出して、他の要素とマージさせることができます。
オブジェクト名':
<<: *エイリアス
プロパティ名: 値
要素は「<<: *エイリアス」で指定した場所に入ります。
その際、配列の要素のプロパティ名は「0」「1」というように配列番号が入ります。
実例:一番上に挿入する場合
a: &x
b: 1
c: 2
d:
<<: *x
e: 3
↓ JSON形式にコンパイル
{ "a": { "b": 1, "c": 2 }, "b": { "b": 1, "c": 2, "e": 3 } }
実例:間に挿入する場合
a: &x
b: 1
c: 2
d:
e: 3
<<: *x
f: 4
↓ JSON形式にコンパイル
{ "a": { "b": 1, "c": 2 }, "b": { "e": 3, "b": 1, "c": 2, "f": 4 } }
実例:配列をマージする
a: &x
- 1
- 2
d:
<<: *x
c: 3
↓ JSON形式にコンパイル
{ "a": { "b": 1, "c": 2 }, "b": { "0": 1, "1": 2, "c": 3 }
エラー例:「<<」を使わないとエラーになる
a: &x
b: 1
c: 2
d:
*x
d: 3
↓ JSON形式にコンパイル
Error : Unable to parse.
Line : 6 *x
コメントアウトの使い方
他にもdatabase.ymlの中には「#」を使った記述が大量にあります。これはコメントアウトです。
なお、JSON形式ではコメントアウトはないので、JSONにコンパイルするとコメントアウトは無視されてなくなります。
実例
#コメントアウト
a: &x
#bを定義
b: 1
#cを定義
c: 2
d: *x
↓ JSON形式にコンパイル
{ "a": { "b": 1, "c": 2 }, "b": { "b": 1, "c": 2 } }
database.ymlのコンパイル例
database.ymlをjsonに変換するとどのように表示されるかの例です。
大量のコメントアウトがなくなります。
# PostgreSQL. Versions 9.3 and up are supported.
#
# Install the pg driver:
# gem install pg
# On macOS with Homebrew:
# gem install pg -- --with-pg-config=/usr/local/bin/pg_config
# On macOS with MacPorts:
# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config
# On Windows:
# gem install pg
# Choose the win32 build.
# Install PostgreSQL and put its /bin directory on your path.
#
# Configure Using Gemfile
# gem 'pg'
#
default: &default
adapter: postgresql
encoding: unicode
# For details on connection pooling, see Rails configuration guide
# https://guides.rubyonrails.org/configuring.html#database-pooling
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
database: rails6_docker_alpine_development
# The specified database role being used to connect to postgres.
# To create additional roles in postgres see `$ createuser --help`.
# When left blank, postgres will use the default role. This is
# the same name as the operating system user running Rails.
#username: rails6_docker_alpine
# The password associated with the postgres role (username).
#password:
# Connect on a TCP socket. Omitted by default since the client uses a
# domain socket that doesn't need configuration. Windows does not have
# domain sockets, so uncomment these lines.
#host: localhost
# The TCP port the server listens on. Defaults to 5432.
# If your server runs on a different port number, change accordingly.
#port: 5432
# Schema search path. The server defaults to $user,public
#schema_search_path: myapp,sharedapp,public
# Minimum log levels, in increasing order:
# debug5, debug4, debug3, debug2, debug1,
# log, notice, warning, error, fatal, and panic
# Defaults to warning.
#min_messages: notice
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: rails6_docker_alpine_test
# As with config/credentials.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password or a full connection URL as an environment
# variable when you boot the app. For example:
#
# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase"
#
# If the connection URL is provided in the special DATABASE_URL environment
# variable, Rails will automatically merge its configuration values on top of
# the values provided in this file. Alternatively, you can specify a connection
# URL environment variable explicitly:
#
# production:
# url: <%= ENV['MY_APP_DATABASE_URL'] %>
#
# Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full overview on how database connection configuration can be specified.
#
production:
<<: *default
database: rails6_docker_alpine_production
username: rails6_docker_alpine
password: <%= ENV['RAILS6_DOCKER_ALPINE_DATABASE_PASSWORD'] %>
↓ JSON形式にコンパイル
{
"default": {
"adapter": "postgresql",
"encoding": "unicode",
"pool": "<%= ENV.fetch(\"RAILS_MAX_THREADS\") { 5 } %>"
},
"development": {
"adapter": "postgresql",
"encoding": "unicode",
"pool": "<%= ENV.fetch(\"RAILS_MAX_THREADS\") { 5 } %>",
"database": "rails6_docker_alpine_development"
},
"test": {
"adapter": "postgresql",
"encoding": "unicode",
"pool": "<%= ENV.fetch(\"RAILS_MAX_THREADS\") { 5 } %>",
"database": "rails6_docker_alpine_test"
},
"production": {
"adapter": "postgresql",
"encoding": "unicode",
"pool": "<%= ENV.fetch(\"RAILS_MAX_THREADS\") { 5 } %>",
"database": "rails6_docker_alpine_production",
"username": "rails6_docker_alpine",
"password": "<%= ENV['RAILS6_DOCKER_ALPINE_DATABASE_PASSWORD'] %>"
}
}
&default
が<<: *default
で共通部として呼び出されていることがわかります。
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>