Yuki's Tech Blog

仕事で得た知見や勉強した技術を書きます。

【TS】分割代入引数についてざっくりまとめてみた

目次

背景

分割代入と分割代入引数がごっちゃになっていたので、まとめます。

分割代入引数とは

まず、おさらいとして分割代入とは、配列の要素やオブジェクトのプロパティの値を別の変数として定義できる構文のことです。 分割代入引数とは、関数の1つの仮引数を分割代入で表現したものです。 実引数として渡すオブジェクトのプロパティ名と、分割代入引数の引数名は一致させる必要があります(分割代入なので、別名をつけることもできる)。 関数の仮引数に分割代入引数を使うことで、オブジェクトのあるプロパティのみや、配列のある要素のみを取得できます。やっていることはシンプルで、1つの仮引数を分割代入で書いているだけです。関数の実引数にはオブジェクトや配列を指定します。

■分割代入引数を使わない場合

type User = {
  first_name: string;
  last_name: string;
};

const getFullName = (user: User) => `${user.last_name} ${user.first_name}`; 

const loginUser = {
  first_name: "yuki",
  last_name: "hoge",
};

console.log(getFullName(loginUser)); // => "hoge yuki"

■分割代入引数を使う場合

type User = {
  first_name: string;
  last_name: string;
};

const getFullName = ({ first_name, last_name }: User) => `${last_name} ${first_name}`;

const loginUser = {
  first_name: "yuki",
  last_name: "hoge"
};

console.log(getFullName(loginUser)); // => "hoge yuki"

分割代入引数をオプショナルにする

分割代入引数をオプショナルにするには、オブジェクト型の型定義をオプショナルにすれば良いだけです。

type User = {
  first_name: string;
  last_name?: string;
};

const getFullName = ({ first_name, last_name }: User) => `${last_name} ${first_name}`;

const loginUser = {
  first_name: "yuki",
};

console.log(getFullName(loginUser)); // => "undefined yuki"

しかし、対応するプロパティがないとundefinedが渡ってしまうので、オプショナルなプロパティにデフォルト値を、事前に設定しておきます。

type User = {
  first_name: string;
  last_name?: string;
};

const getFullName = ({ first_name, last_name = "sample" }: User) => `${last_name} ${first_name}`;

const loginUser = {
  first_name: "yuki",
};

console.log(getFullName(loginUser)); // => "sample yuki"

分割代入引数の全体の既定値

分割代入引数の全体の既定値を指定する場合、分割代入構文の後に=と既定値を書きます。引数全体がない場合に、採用されます。

type User = {
  first_name: string;
  last_name: string;
};

const getFullName = ({
  first_name, 
  last_name,
}: Partial<User> = { first_name: "hoge", last_name: "huga"}) => `${last_name} ${first_name}`;

console.log(getFullName()); // => "huga hoge" 

参考記事

【TS】分割代入の代入先でスプレッド構文を使ってみた - Yuki's Tech Blog

分割代入引数 (destructuring assignment parameter) | TypeScript入門『サバイバルTypeScript』

オブジェクト型のオプションプロパティ (optional property) | TypeScript入門『サバイバルTypeScript』

【TypeScript】【Ruby】TypeScriptとRubyのクラスの書き方を比較してみた part1

目次

背景

TypeScriptとRubyのクラスをよく書くのですが、ごっちゃになる時があるのでまとめます。

そもそもクラスとは

こちらの記事にまとめてます。

クラスとは、あるオブジェクトに関する変数と、あるオブジェクトに関する処理を一つにまとめたものです。

【オブジェクト指向設計実践ガイド】第2 章 単一責任のクラスを設計するを読んで学んだことをざっくりまとめてみた - Yuki's Tech Blog

クラスの定義方法

TS

TypeScriptでは classキーワードを用いてクラスを定義できます。

class User {};

const user = new User();
console.log(user); // => {}

Ruby

Rubyでも、同様にclassキーワードを用いてクラスを定義します。

class User
end

user = User.new
p user # => #<User:0xc7a>

インスタンスの生成方法

TS

TypeScriptでは、new演算子を用いてインスタンスを生成します。 また、クラスを定義すると、クラスと同名の型も定義されます。インスタンスを代入する変数に型アノテーションをしたい場合、このクラスの型を使用します。

Ruby

Rubyでは、Class#newを用いてインスタンスを生成します。

# [[c:Class]] クラスのインスタンス、C クラスを生成
C = Class.new   # => C

# [[c:Class]] クラスのインスタンス、C クラスのインスタンスを生成
C.new           # => #<C:0x00005623f8b4e458>

コンストラク

TS

コンストラクタは、クラスからインスタンスを生成するときに実行される関数です。コンストラクタには、インスタンスプロパティを初期化する処理を実装します。 TypeScriptでコンストラクタを使用するためには、constructorメソッドを定義します。new演算子でクラスを生成するときに、クラスの実引数に指定した値が、コンストラクタに渡されます。

class Person {

  // コンストラクタの本来の使い方ではないがこんな使い方もできる
  // コンストラクタの本来の使い方(メンバ変数の初期化処理)をしたい場合、事前にクラス内にメンバ変数を定義しておく
  constructor() {
    console.log("hoge");
  }
};

const user = new Person(); // => hoge

Ruby

Rubyではinitializeメソッドを利用します。

class User
  def initialize(name:)
    @name = name;
  end
end

user = User.new(name: "hoge")
p user # => #<User:0xe08 @name="hoge">

メンバ

TS

メンバとは、クラスやオブジェクト(インスタンス)の持つ変数や関数のことです。一般的には、インスタンスの変数や振る舞いは、メンバ変数、メンバ関数と呼ばれ、クラス自身の変数や振る舞いは、静的メンバ変数、静的メンバ関数と呼ばれます。言語によってはメンバ変数をプロパティ、メンバ関数をメソッドと呼んだりします。メンバは英語のmemberであり、構成員という意味です。つまり、メンバ変数とは、クラスに所属する構成員の変数という意味で解釈できます。

TypeScriptでインスタンスにプロパティを持たせたい場合、クラスの最初にメンバ変数を宣言する必要があります。しかし、メンバ変数を定義しただけだとエラーが出ます。理由は、new Person()を実行した際に、メンバ変数の値がundefinedになってしまい、メンバ変数がstring型の値ではないので整合性が失われてしまうからです。

class Person {
  // Property 'name' has no initializer and is not definitely assigned in the constructor.
  name: string
};

const user = new Person();
console.log(user); // => {}

そのため、整合性を保つためにもクラスにメンバ変数を宣言する際には、コンストラクタも定義しましょう。そうすることで、コンストラクタを通してメンバ変数を初期化できるので、このエラーを回避できます。

class Person {
  name: string;

  constructor(name: string) {
    this.name = name;
  }
}

const user = new Person("yuki");
const new_user = new Person(); // => Expected 1 arguments, but got 0.(2554)
console.log(user);

ちなみに、コンストラクタを定義する以外にもundefinedとの共用体型を定義するか、初期値を事前に設定するかのどちらかで、このエラーを回避できます。

■メンバ変数の型をundefinedとの共用体型にする

class Person {
  name: string | undefined;
}

const user = new Person();
console.log(user); // => Person: {}

■メンバ変数に初期値を設定する

class Person {
  firstName: string = "yuki";
  lastName: string = "haga";
}

class Product {
  // 初期値から型を推論できる場合、メンバ変数に型アノテーションをしなくても良い
  name = "fish";
  price = 800;
}

const user = new Person();
console.log(user);
// => [LOG]: Person: {
//      "firstName": "yuki",
//      "lastName": "haga"
//    } 

user.firstName = "hoge";
console.log(user);
// => [LOG]: Person: {
//      "firstName": "hoge",
//      "lastName": "haga"
//    } 


const fish = new Product();
console.log(fish);
// => [LOG]: Product: {
//      "name": "fish",
//      "price": 800
//    } 

これらの方法でもエラーを回避できます。しかし、インスタンス生成時に動的に初期値を指定できないので、やはりクラスにコンストラクタを定義したほうが嬉しいでしょう。

ちなみに、現状のコンストラクタの実装だと、メンバ変数が増えるたびにコンストラクタを修正する必要があります。これは以下のように、Object.assign()を利用することで防ぐことができます。Object.assign()は第一引数で指定したオブジェクトに、第二引数以降のオブジェクトの各プロパティを破壊的に追加・上書きしていくメソッドです。つまり、コンストラクタ内でthisを用いてインスタンスにアクセスして、そのインスタンスを、呼び出し時に指定したデータで上書きします。

type PersonType = {
  firstName: string;
  lastName: string;
  age: number;
}

class Person {
  firstName = "";
  lastName = "";
  age = 0;

  constructor(data: PersonType) {
    console.log(this); // メンバ変数に指定した初期値を持つインスタンスにアクセスできる(つまり、コンストラクタの処理より初期値の方が実行されるタイミングが早い)
    Object.assign(this, data);
  }
}

const user = new Person({ firstName: "yuki", lastName: "haga", age: 26 });

ちなみにですが、クラスから生成したインスタンスのメンバ変数を動的に増やしたりとかはできません(できないのが挙動として正しいので注意しましょう)。

// こういうのはできない
class User {
  // Property 'name' has no initializer and is not definitely assigned in the constructor.
  name: string = "";

  constructor(data: Partial<User> = {}) {
    Object.assign(this, data);
  }
};

const user = new User({ name: "hoge" });
console.log(user); // => { "name": "hoge" }
const new_user = new User({ name: "hoge", age: 26 });
// => Argument of type '{ name: string; age: number; }' is not assignable to parameter of type 'Partial<User>'.
//    Object literal may only specify known properties, and 'age' does not exist in type 'Partial<User>'.(2345)

Ruby

Rubyではメンバを事前に定義する必要はないですが、初期化時にどのメンバにどんな値を入れるかをinitializeで定義しておいた方が、わかりやすいです。余談ですが、関数を定義する際に、キーワード引数と普通の引数を併用することもできます。

class User
  def initialize(name, age: 0)
    @name = name;
    @age = age
  end
end

user = User.new("hoge")
p user # => #<User:0xe48 @name="hoge" @age=0>
user_1 = User.new("fuga", age: 20);
p user_1 # => # <User:0xf76 @name="fuga" @age=20>

メソッド

TS

以下のようにして、クラスにメソッドを定義できます。このクラスに定義したメソッドは、インスタンスの動作を表します。constructorメソッドもメソッドなので、メソッドはconstructorメソッドと同じように定義します。メソッド内でインスタンスにアクセスするためには、constructorメソッドと同じくthisを使います。このクラスに定義するメソッドは全てのインスタンスが持つメソッドなので、インスタンスが持つべきではない振る舞いをメソッドとして定義したり、ある状況特有の処理をメソッドとして定義したり、しないようにしましょう。また、メソッド呼び出し時は、関数呼び出しと同じように()をつける必要があるので、その点を注意しましょう。

type PersonType = {
  firstName: string;
  lastName: string;
}

class Person {
  firstName: string;
  lastName: string;

  constructor({ firstName, lastName }: PersonType) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
}

const user = new Person({ firstName: "yuki", lastName: "haga" });
console.log(user.fullName()); // => "yuki haga"

Ruby

Rubyではdefでメソッドを定義します。メソッド呼び出し時に引数を一つも指定しない場合、括弧を省略するのが一般的なので、注意しましょう。

class User
  attr_reader :first_name, :last_name
  def initialize(first_name, last_name:, age: 0)
    @first_name = first_name
    @last_name = last_name
    @age = age
  end
  
  def full_name
    "#{last_name} #{first_name}"
  end
end

user = User.new("yuki", last_name: "hoge");
p user.full_name # => "hoge yuki"

アクセス修飾子

TS

アクセス修飾子を用いることで、メンバ変数やメソッドに対してのアクセスを制限することができます。

アクセス修飾子 意味
(宣言なし) publicと同等
public どこからでもアクセス可能
protected 自身のクラスとサブクラスからアクセス可能
private 自身のクラスのみアクセス可能
type PersonType = {
  firstName: string;
  lastName: string;
}


class Person {
  private firstName: string;
  public lastName: string;

  constructor({ firstName, lastName }: PersonType) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
}

const user = new Person({ firstName: "yuki", lastName: "haga" });
console.log(user.lastName); // => "haga"
console.log(user.firstName);
// Property 'firstName' is private and only accessible within class 'Person'.(2341)

Ruby

Rubyにはメソッドに対してpublic, private, protectedなどがありますが、メンバに対してのアクセス修飾子はなかったような気がします(もちろんatttr_readerを定義しないとか、そういうやり方ならあります)。

privateメソッドをメソッドに指定することで、

  • クラスの内部でのみ使えるメソッド
  • レシーバを指定しないで呼び出すメソッド

になります。

メンバ変数のreadonly修飾子

TS

メンバ変数には、アクセス修飾子以外にも、readonly修飾子をつけることができます。readonly修飾子をつけることで、読み取り専用なメンバ変数を定義できます。読み取り専用なメンバ変数は、読み取り専用なので、メンバ変数宣言時に初期値を指定するか、コンストラクタで値を代入するかの2通りでしか値を代入できません

type PersonType = {
  firstName: string;
}

class Person {
  readonly firstName = "";

  constructor(data: PersonType) {
     Object.assign(this, data);
  }
}

const user = new Person({ firstName: "yuki" });
console.log(user.firstName); // => yuki
// publicだと代入できる
// Cannot assign to 'firstName' because it is a read-only property.(2540)
user.firstName = "hoge"; 

Ruby

Rubyでは、attr_readerを使えば読み取り限定のメンバとして定義できます。

getter

元々ゲッターとは、メンバ変数にアクセスするためのメソッドのことです。Typescriptではgetを使って、ゲッターを定義します。普通にメソッドを定義して、メソッド名の先頭にgetを指定するだけです。

個人的には、インスタンスのプロパティとして存在してもおかしくないようなものを、メソッドではなくゲッターとして定義すると良いのかなと思っています。 fullnameのようなインスタンスのプロパティのようなメソッドはgetterとして定義した方が、自然なのかなと思います。

class User {
  firstName: string = "";
  lastName: string = "";

  constructor(data: Partial<User> = {}) {
    Object.assign(this, data);
  }

  get fullName() {
    return `${this.lastName} ${this.firstName}`;
  }
};

const user = new User({ firstName: "yuki", lastName: "hoge" });
console.log(user.fullName); // => "hoge yuki" 

Ruby

Rubyでは、attr_readerを使えば読み取り限定のメンバとして定義でき、同時にゲッターも定義されます。full_nameのようなオリジナルのゲッターを定義したい場合、メソッドを定義すればOKです。

class User
  attr_reader :first_name, :last_name
  def initialize(first_name:, last_name:)
    @first_name = first_name;
    @last_name = last_name;
  end
 
  def full_name
    "#{last_name} #{first_name}"
  end
  
end

user = User.new(first_name: "yuki", last_name: "hoge")
user.full_name # => hoge yuki

参考記事

クラス変数 - Wikipedia

メンバとは - 意味をわかりやすく - IT用語辞典 e-Words

静的メンバ

アクセス修飾子 (access modifier) | TypeScript入門『サバイバルTypeScript』

Class#new (Ruby 3.2 リファレンスマニュアル)

【オブジェクト指向設計実践ガイド】第2 章 単一責任のクラスを設計するを読んで学んだことをざっくりまとめてみた

目次

クラスとは

クラスとは、あるオブジェクトに関する変数と、あるオブジェクトに関する処理を一つにまとめたものです。

クラスを定義するメリット

個人的には以下のメリットがあるのかなと考えました。

  • クラスを定義することで、仕様変更に対する影響範囲(変数の追加や、処理の変更)をクラスに局所化できる。
  • クラスを定義することで、同じ性質のオブジェクトを簡単に作ることができて、再利用性が高くなる。同じようなコードをもう一度書かなくて良くなる。
  • クラスを定義することで、コードのまとまりを意味のあるまとまりとして管理することができる。

ドメインとは何か

ドメインとは、アプリケーションが対象とする問題領域のことです。問題領域とは、解決すべき問題が潜んでいる領域のことです。ドメインを分析して構成概念を抽出することをモデリングと呼びます。モデリングの結果、得られた概念のことをドメインモデルと呼びます。ドメインモデルは、その概念に関連する属性と振る舞いを持ったオブジェクトとして定義されます。

このドメインモデルをコードで表現する際に、クラスを用います。

メソッド、手続き、振る舞いの違いとは

メソッドと手続き、振る舞い(オブジェクト指向における振る舞いは、オブジェクトの動作の仕方を表している)はどれも、一定の処理をまとめて抜き出したものです。そのため、概念的には同じなので、特に違いを意識する必要はないでしょう。

DRY原則の本質

私がRailsを勉強してたとき、「繰り返しはよくない」と散々言われてきて、そのようなこともあり、DRY原則は「同じようなコードを繰り返しは書くな」という意味だと思っていました。似たようなコードがあった場合、ひたすら共通化していました。

しかし、DRY原則の本当の意味は、「同じようなコードを排除することで、コードの変更箇所を一箇所に限定すること」ってのを最近知りました。つまり、「同じようなコードを排除してコード量を減らすこと」が目的なのではなくて、「変更箇所を一箇所に限定して、変更コストを最小限にすること」が目的なのです。

あと、DRY原則を適用させる上で注意すべきことは、何でもかんでもDRY原則を適用させるべきではないということです。DRY原則を適用させるということは、つまり、コードを共通化するということです。コードを共通化することによって、逆に変更コストが高くつく場合もあります(コードを共通化するということは、使われる場所に応じた独自の処理を追加できないということを意味します)。そのため、状況に応じて、メリットデメリットを考えて、DRY原則を適用させるかを判断するようにしましょう。

メッセージを送り出すとは?

凝縮度とは

単一責任の原則とは

参考記事

クラスとは - 意味をわかりやすく - IT用語辞典 e-Words

第13回 クラスはプログラミングを楽にする道具(1) | gihyo.jp

[プログラミング]クラスとは なぜ重要?動機やメリットは? | スモビュ!

ドメイン駆動設計入門【DDDをわかりやすく解説】 | 楽水

ドメインとかモデルとかよく分かってない - Neo's World

関数と手続きとメソッドとサブルーチンの違いを教えてください。 - 厳密な... - Yahoo!知恵袋

振舞(ふるまう)とは? 意味や使い方 - コトバンク

DRY原則とは - 意味をわかりやすく - IT用語辞典 e-Words

【オブジェクト指向設計実践ガイド】第1章 オブジェクト指向設計を読んで学んだことをざっくりまとめてみた

目次

背景

変化に強いソフトウェアを作れるようになりたいなと思い、以前から気になっていたオブジェクト指向設計に入門しました。

そもそもオブジェクトとは

プログラミングにおけるオブジェクトとは、「データと自分自身の手続き(処理手順)を1つのまとまりとして定義したもの」です。オブジェクトは何かしらのクラスから生成されているので、クラスに定義してあるメソッドや、そのクラスの継承元のクラスに定義してあるメソッドを使用することができます。オブジェクトは自分自身のデータと自分自身の振る舞いを持ちます。

オブジェクト指向とは何か、オブジェクト指向プログラミング、オブジェクト指向言語との違いは何か

オブジェクト指向とは、さまざまなオブジェクトを組み合わせてソフトウェアを開発しようとする考え方のことです。 オブジェクト指向では、世界を、オブジェクト間で受け渡されるメッセージの連続としてモデル化しています。手続型では、世界を、手続きの集まりとしてモデル化しています。

オブジェクト指向という言葉を聞くと、オブジェクト指向プログラミングって何?となります。オブジェクト指向プログラミングとは、オブジェクト指向の考え方をプログラミングに応用したプログラム記述手法のことです。つまり、オブジェクト指向プログラミングは、オブジェクトの定義やオブジェクト間の相互関係、相互作用を記述することでプログラムを構築していく手法のことです。

また、オブジェクト指向言語とは何?ともなりますが、オブジェクト指向言語とは、オブジェクト指向プログラミングに必要な機能(クラスやプロトタイプ、メソッド、インスタンス化等々)を言語仕様として備えている言語のことです。

オブジェクト指向でソフトウェアを開発すると何が嬉しいのか?

個人的な考えになりますが、以下の点でメリットがあるのかなと思いました。

  1. オブジェクトがデータと手続きを持っているので、変更箇所が局所化されている。そのため、変更を加えやすい
  2. オブジェクトがデータと手続きを持っているので、そのオブジェクトを再利用することで、プログラムを効率的に作れる

ただ、これらのメリットは、あくまで適切なオブジェクト指向設計をした際に得られるものだと私は思います。

オブジェクト指向設計とは何か

オブジェクト指向設計とは、ざっくりいうと、オブジェクトが変更を許容できるように、オブジェクト間の依存関係をどのように管理するかを決定することです。オブジェクト指向設計をする際に、SOLID、DRY、デメテルの法則といった原理原則やデザインパターンを使用します。

オブジェクトがお互いのことを知ることで、オブジェクト間に依存関係が作られます。そういう依存関係が大量にあることで、1つのオブジェクトを修正することで、全てのオブジェクトを修正する必要があるでしょう。 また、オブジェクトが本来持つべきではない情報を持つことによって、その状況でしか利用できない再利用性のないオブジェクトが作られます。これはReactコンポーネントでも同様です。

このような依存関係を管理するには、適切なオブジェクト指向設計をする必要があります。

良い設計とは何か

ソフトウェア開発における良い設計とは、未来を予測するのではなく、未来を受け入れるための選択肢を保護することです。拡張の余地を残すことで、変更に強いソフトウェアを作ることができます。

この考え方では、未来に何が起こるのかを予測するのではなく、未来に何かが起こることを認めています。未来に何が起こるのかを予想して設計すると、もし未来で想定したことが起きなかった場合、変更コストが高くつきます。未来を想定するのではなく、未来に何かが起こることを認めて、拡張の余地を残すことが良い設計と言えます。

参考記事

オブジェクト指向言語(オブジェクト指向プログラミング言語)とは - 意味をわかりやすく - IT用語辞典 e-Words

設計とは - 意味をわかりやすく - IT用語辞典 e-Words

オブジェクト指向プログラミング(OOP)とは - 意味をわかりやすく - IT用語辞典 e-Words

オブジェクト指向言語(オブジェクト指向プログラミング言語)とは - 意味をわかりやすく - IT用語辞典 e-Words

オブジェクト指向 - Wikipedia

オブジェクト指向分析設計 - Wikipedia

【SQL】CRUD処理のSQL文ついてざっくりまとめてみた

目次

背景

SQLCRUD処理をパッとかけなかったので、ブログにまとめようと思います。

参照(SELECT文)

SELECT文は、テーブルからデータを参照したいときに使います。 以下のSELECT文は、SELECT句、FROM句、WHERE句で構成されています。

/* WHEREのイコールはカラムと値が等しいかどうかを表している */
/* プログラミングの代入を表すイコールとは違う */
SELECT name, price AS product_price
FROM products
WHERE id = 20;

登録(INSERT文)

INSERT文は、テーブルにレコードを作成したいときに使います。基本的に1回で1レコード挿入します。テーブルにレコードを挿入とイメージすると分かりやすいです。VALUES句に指定した値リストを列リストで指定したカラムに挿入します。 列名や値をカンマで区切って外側を括弧で括った形式をリストと呼びます。INSERT文では、列リストと値リストを使います。

INSERT INTO テーブル名
(列1, 列2, 列3 .....)
VALUES (値1, 値2, 値3......);

以下のINSERT文を実行すると、productsテーブルに1件の商品レコードが作成されます。

INSERT INTO products
(id, name, price)
VALUES (1, "Water", 500);

INSERT文を使う際、以下の点に注意しましょう。

  • 列リストと値リストの数が一致していないとエラーが出る
  • 列リスト「(列1, 列2, 列3 .....)」を省略することもできますが、その場合、VALUES句の値が左側から順に各列に割り当てられる
  • カラムにdefault値が設定されている場合、デフォルト値を設定しているカラムを列リストと値リストから省略すると、カラムにデフォルト値を設定できる(またはどちらも省略せず、値リストのカラムの部分にDEFAULTと書いてもOK)
  • デフォルト値を設定していないカラムを列リストと値リストから省略するとNULLが割り当てられる

以下のINSERT文を実行すると、productsテーブルにidカラムがデフォルト値のデータが作成されます。

INSERT INTO products
(id, name, price)
VALUES (DEFAULT, "Water", 500);

削除(DELETE文)

DELETE文は、テーブルを消さずに、テーブルから全てのレコードを削除する場合に使用します。INSERT文もDELETE文もUPDATE文も、レコード単位で処理を行うので、SELECT文のように列を指定しなくて大丈夫です。あと、DELETE テーブル名のように書かないのは削除対象がテーブルではなく、テーブルに含まれる全ての行だからです。

DELETE FROM テーブル名;

例えば以下のクエリを実行した場合、productsテーブルは消えないが、productsテーブルに保存されている全てのレコードが削除されます。

DELETE FROM products;

テーブルの全行だけではなく、一部の行だけを削除する場合、WHERE句で条件を指定します。削除対象を制限したDELETE文のことを「探索型DELETE」と呼びます。

DELETE FROM products
WHERE id = 1;

更新(UPDATE文)

UPDATE文は、テーブルの全てのレコードを更新する際に使います。 あまり正確には分かっていないですが、UPDATE文にFROM句がない理由としては、あるテーブルの全てのレコードを更新することは、テーブル全体を更新しているようなものなので、FROMがついていなくても意味的にOKなのかなと思いました(UPDATEの目的語がテーブルでも意味が成立する)。DELETEの場合、テーブルを削除するわけではないので、FROM句は必要なのかなと思いました。

UPDATE文では、更新対象の列と更新後の値は、SET句に記述します。 このUPDATE文を実行すると、全てのレコードの列が式で更新されてしまうので注意しましょう。

UPDATE テーブル名
SET 列名 = 式;

テーブルの全行を更新するのではなく、一部の行だけを更新したい場合、WHERE句を使用します。更新対象を制限したUPDATE文のことを「探索型UPDATE」と呼びます。

UPDATE テーブル名
SET 列名 = 式
WHERE 条件;

複数列を更新したい場合、列をカンマ区切りで並べます。

UPDATE テーブル名
SET 列名 = 式,
    列名 = 式, 
    .....
WHERE 条件;

以下のUPDATE文を実行すると、productsテーブルのidが5のレコードのnameカラムとpriceカラムを指定した値に更新します。

UPDATE products
SET name = "clean water",
    price = 321
WHERE id = 5;

参考記事

「SQL」と「クエリ」の違い|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

SELECT (SQL) - Wikipedia

【SQL】結合についてざっくりまとめてみた

目次

背景

結合のSQLがパッと書けなかったので、記事にまとめようと思います。

結合とは

結合とは、複数のテーブルを組み合わせる処理のことです。あるテーブルの外部キーに紐づくレコードを取得したい場合などに、結合を使用します。結合によって新たなテーブルができているとイメージすると、理解しやすいです(実際にテーブルができるわけではない)。

内部結合と外部結合の違い

結合には大きく分けて、内部結合と外部結合の2種類が存在します。 内部結合は、2つのテーブルの両方にデータが存在する場合にレコード同士を結合します。

外部結合では、内部結合のような結合をしつつ、メインで指定したテーブルのデータはすべて表示します。そのため、メインのテーブルにしか存在しないデータのレコードは、カラムにNULLが入ったレコードと結合することになります。

元のテーブルにない情報をテーブルの外部から持ってくるという意味で、外部結合と呼ばれています。内部結合はテーブルの内部だけから情報を持ってくる結合なので、内部結合と呼ばれています。

多くの場合、内部結合を使うことが多いでしょう。次に左外部結合が使われます。

外部結合の種類について

外部結合には以下の3種類が存在します。この3種類の総称が外部結合です。

  • 左外部結合(LEFT OUTER JOIN)
  • 右外部結合(RIGHT OUTER JOIN)
  • 完全外部結合(FULL OUTER JOIN)

多くの場合、左外部結合と右外部結合を利用するので、その2つに絞って話を進めます。左外部結合の場合、FROM句でLEFTの左側に書いたテーブルをメインのテーブルとします。右外部結合の場合、RIGHTの右側のテーブルをメインのテーブルとします。

内部結合の構文

内部結合をする場合、FROM句にINNER JOINを書きます。INNER JOINの左側に指定したテーブルと右側に指定したテーブルを結合します。そして、ON句に結合条件を書きます。この結合条件で、2つのテーブルを結びつける列(結合キー)を指定します。

/* 内部結合 */
SELECT S.name, P.name, P.price
FROM Stores AS S
INNER JOIN Products AS P
ON S.product_id = P.id;

外部結合の構文

外部結合の場合、INNER JOINではなく、LEFT OUTER JOINやRIGHT OUTER JOINを書きます。

/* 左外部結合 */
/* Storesをメインのテーブルとする */
SELECT S.name, P.name, P.price
FROM Stores AS S
LEFT OUTER JOIN Products AS P
ON S.product_id = P.id;
/* 右外部結合 */
/* Productsをメインのテーブルとする */
SELECT S.name, P.name, P.price
FROM Stores AS S
RIGHT OUTER JOIN Products AS P
ON S.product_id = P.id;

参考記事

SELECT (SQL) - Wikipedia

外部結合とは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

【Ruby】Rubyの復習を通して知ったことをざっくりまとめてみた

目次

背景

最近Rubyだったり、TypeScriptだったりを実務で触って、いろんな概念がごっちゃになっているなと感じたので、とりあえずRubyを復習しました。

Rubyで知ったこと

コマンドライン引数

lsコマンドに渡す -lやcpコマンドに渡すファイル名などは、コマンドライン引数と呼ばれます。Rubyでは、コマンドライン引数をARGVという配列変数が保持しています。

puts "count = #{ARGV.count}" // => 3

ARGV.each do |i|
  puts i
end
# => ruby1
# => ruby2
# => -l

上記のコードをコマンドライン引数を渡して実行すると、コマンドライン引数が出力されていることが確認できます。

initializeメソッド

initializeメソッドは、クラスからオブジェクトを作成するnewメソッドを呼び出した際に呼ばれるメソッドです。initializeメソッドは、クラスの中に定義します。initializeメソッドはコンストラクタの役割を担います。

class User
  attr_accessor :name
  attr_accessor :age

  def initialize(name:, age:)
    @name = name
    @age = age
  end
end

user = User.new(name: "yuki", age: 26)

# userのゲッターが呼び出されてる
p user.name # => "yuki"
p user.age # => 26

インスタンス変数とローカル変数の違い

インスタンス変数とは、クラスの内部で定義できる変数のことです。インスタンス変数は、インスタンス(オブジェクト)のデータを保持するための変数です。インスタンスの内部で共有できる変数なので、インスタンスが呼び出せるメソッドで利用することができます。インスタンス(オブジェクト)が破棄された時に、インスタンス変数も破棄されます。インスタンス変数の変数名には、先頭に@をつけます。

ローカル変数とは、メソッドやブロックの内部で作成される変数です。ローカル変数はメソッドやブロックの内部でのみ有効であり、メソッドやブロックの実行が終了すると破棄されます。メソッドやブロックを呼び出すたびに毎回作り直されます。ローカル変数の変数名には、アルファベットの小文字または、アンダースコアで始めます。

末尾がイコールで終わるメソッドは、半角スペースを開けることができる

name=メソッドのような末尾が=で終わるメソッドは、eと=の間に半角スペースを開けることができます。

class User
  def initialize(name:, age:)
    @name = name
    @age = age
  end

  def name
    @name
  end

  def name=(name)
    @name = name
  end
end

user = User.new(name: "yuki", age: 26)

p user.name # => "yuki"

user.name=("hoge")
p user.name # => "hoge"

# 括弧は省略できる(特殊なケースを除いて本来括弧を省略するのは良くない)
user.name= "huga"
p user.name # => "huga"

user.name = "yuki"
p user.name # => "yuki"

オブジェクトについて

Rubyは全てのデータがオブジェクトです。そもそもプログラミングにおけるオブジェクトとは、「データと手続き(処理手順)を1つのまとまりとして定義したもの」です。オブジェクトは何かしらのクラスから生成されているので、クラスに定義してあるメソッドや、そのクラスの継承元のクラスに定義してあるメソッドを使用することができます。このデータ型だから、このメソッドが使えるという認識は違っていて、このオブジェクトはこのクラスに属しているから、このメソッドが使えるという認識が正しいです。これはJSのようなオブジェクト指向言語でも同じです。

余談ですが、JSには広義のオブジェクトと狭義のオブジェクトがあります。広義のオブジェクトとは、プリミティブ型の値以外の全てです。もっと具体的にいうと、最終的な継承元がObject(標準組み込みオブジェクト)であるような値のことです。狭義のオブジェクトとは、プロパティの集まりのことです。JSでは、プログラミングにおけるオブジェクトのことか、広義、狭義のオブジェクトなのかを文脈で判断する必要があるので注意しましょう。

データ型について

Rubyにはデータ型という概念は存在しません。「~は〇〇型である」という表現もしません。データ型はデータの種類を表しているので、Rubyの場合、オブジェクトが属すクラスがある意味データ型のように、データの種類を表していると考えることができます。Rubyは動的型付け言語なので実行時に型が決まりますが、このようなクラスが型の変わりをしていると考えることができます。

ちなみにJSのデータ型はプリミティブ型とオブジェクト型です。プリミティブ型は7種類存在していて、Boolean型、Number型、BigInt型、String型、Symbol型、Null型、Undefined型です。オブジェクト型の値はオブジェクトなので、メソッドを使うことができます。プリミティブ型の値はオブジェクトではないので、メソッドを使うことはできません。しかし、undefinedとnull以外のプリミティブ型の値はメソッド呼び出し時にラッパーオブジェクトに変換されるので、メソッドを呼び出すことができます。

つまり、JSは全てがオブジェクトというわけではなく、「全てがオブジェクトのよう見える」というのが正しい認識です。そう考えるとRubyは全てがオブジェクトなので、すごいなと感じます。

擬似変数

擬似変数とは、代入ができない参照専用の変数です。オブジェクト自身を表すselfや、現在のソースファイル名を表すFILEなどが擬似変数です。

組み込みライブラリ、標準ライブラリ、gemの違いについて

Rubyでは多くのクラスやモジュールが標準ライブラリとして最初から用意されています。標準ライブラリの中でもよく使うクラスやモジュールは、組み込みライブラリとして提供されています。この組み込みライブラリは、requireを書かなくても利用できます。

組み込みライブラリ以外の標準ライブラリを使用したい場合、requireでインポートする必要があります。以下の例では、Dateクラスを利用するために、dateライブラリをインポートしています。dateライブラリ内にはDateクラスや様々なクラスが定義されていて、それらをdateライブラリをインポートすることで利用することができます。

require "date"

day = Date.new()

p day # => #<Date: -4712-01-01 ((0j,0s,0n),+0s,2299161j)>

有志の開発者が作成した外部ライブラリは、Rubyではgemという形式でパッケージングされます。gemと呼ばれたら、Rubyの外部ライブラリのことを指していると思っておけば大丈夫です。gemを利用する場合、別途インストールが必要です。gemも利用する場合は、requireでインポートします。Railsではデフォルトの設定のおかげでgemをrequireしなくても使うことができます。

Railsにおいてrequireがなぜ要らないのかを追ってみた(Gemfile編)

gem install と bundle installの違い

gem installコマンドでgemをインストールすることができます。インストール先は、gem environment gemdirで確認することができます。

# インストール先
/home/ubuntu/.rbenv/versions/3.0.5/lib/ruby/gems/3.0.0

この3.0.0というディレクトリの配下にgemsというディレクトリがあって、そこにgem installでどんなgemをインストールしたのかを確認できます。3.00ディレクトリ配下にインストールされたgemは、グローバルインストールされたという意味であり、gemが開発マシン内のどこでも使えることを意味します。gem install時に依存しているgemもインストールしてくれます

例として、aというgemをgem installすると、gemsディレクトリでaを確認できるので、aがグローバルにインストールされたことが分かります。

../
./
abbrev-0.1.0/
abbrev-0.1.1/
base64-0.1.0/
base64-0.1.1/
benchmark-0.1.1/
benchmark-0.2.1/
bigdecimal-3.0.0/
bigdecimal-3.1.3/
bundler-2.2.33/
bundler-2.4.3/
../
./
a-0.2.8/
abbrev-0.1.0/
abbrev-0.1.1/
base64-0.1.0/
base64-0.1.1/
benchmark-0.1.1/
benchmark-0.2.1/
bigdecimal-3.0.0/
bigdecimal-3.1.3/
bundler-2.2.33/
bundler-2.4.3/

bundle installでも同様にgemをインストールすることができます。bundle installのインストール先は、デフォルトではgem installのインストール先と同じです。bundlerの設定でインストール先をvendor/bundleにしたりできるそうです。しかし、グローバルにインストールしてもBundlerがgem同士の依存関係をハンドリングしてくれるので、Bundlerを使っているプロジェクトでgemをbundler経由でインストールする場合、bundle config set pathでわざわざvendor/bundleのようなパスを指定しなくても良いそうです。gem installでgemをインストールするよりも、bundlerを利用した方がメリットが多いです。

bundlerでgemをインストールするメリットは、

  1. Gemfileにインストールしたいgemを指定してbundle installを実行するので、何回もgem installコマンドを実行しなくて良い
  2. Gemfileを見れば、どんなgemをインストールするかを確認できる。Gemfileのようなファイルがないと、このプロジェクトではどんなgemがインストールされているかが分かりづらい

  3. 依存関係を解決した結果がGemfile.lockに書いている。gem installでgemをインストールした場合、gemのバージョンを指定してても、依存gemはある範囲のバージョンを満たしていれば良いため、正常に動いている環境で使われている依存gemよりバージョンアップした依存gemがインストールされることがある。その結果、依存関係が原因でアプリが正常に動かなくなったりする。また、人間がそれらの依存関係を調べて解決しないといけないのが辛い

bundle installを実行することで、Gemfile内に指定されたgemを依存先も込みでインストールします(gem installでも依存先込みでインストールします)。インストール後、パッケージごとの依存関係やどのバージョンを使用しているかが書かれているGemfile.lockが作成されます。Gemfile.lockが存在している場合、Gemfile.lockの内容から必要なgemをインストールします。Gemfile.lockにないgemをGemfileから見つけた場合、そのgemもインストールします。そのため、Gemfile.lockを共有することで、他の人の環境でも簡単に同じgem(依存gemも同じ)で開発することができます。

Bundlerとは何か

Bundlerとは、gem同士の依存関係やどのバージョンのgemがインストールされたかを管理するためのgemです。Bundler自体は、gem installでインストールします。以降は、以下のような手順でBundler経由でgemをインストールします。

  1. Bundler経由でgemをインストールしたいプロジェクトに移動する
  2. Bundlerでgemを管理するためには、Gemfileを作成する必要があるので、bundle initコマンドを実行してGemfileを作成する
  3. Gemfileにインストールしたいgemを書いてbundle installでgemをインストールする
  4. Gemfile.lockが作成される。Gemfile.lockにはbundlerがインストールしたgemの依存関係だったりどのバージョンのgemをインストールしたのかが書いてあるので、依存関係やインストールされたバージョンを簡単に管理できる

余談ですが、bundle exec コマンド名を実行するとBundlerでインストールされているgemを利用してコマンドを実行します。

ちなみに rails newを実行する前に、プロジェクトでGemfileが存在していた場合、rails newを実行するタイミングでGemfileを上書きしていいかを聞かれるそうです。こちらは未検証なので、rails newをするタイミングで調べてみます。

rbenvとは何か

rbenvとは、1つのマシン上で複数のバージョンのRubyを扱うためのツールです。アプリケーションごとに適切なRubyバージョンに切り替えることができます。rbenvがあることによって、1つのマシンでプロジェクトごとに異なるRubyで開発することができます。rbenvがない場合、プロジェクトを切り替えるごとにRubyを都度インストールし直すか仮想マシンを使うかなどの必要性が出てきます。

rbenvを利用するためには、GitHubリポジトリからインストールしてきます。インストールしたrbenvは~/.rbenvに格納します。

git clone https://github.com/rbenv/rbenv.git ~/.rbenv

その後、rbenv initをしたり、ruby-buildをインストールすれば基本的な設定は完了です。

ruby-buildとは何か

ruby-buildとは、rbenvのプラグインです。ruby-buildをインストールすることによって、rbenv installなどのコマンドを利用することができます。rbenv installコマンドを実行することで、特定のバージョンのRuby~/.rbenv/versions 配下にインストールすることができます。

git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build

ruby-buildは、rbenvをインストールしたディレクトリ配下のプラグイン用のディレクトリ(plugins)に配置します。

rbenvでどのRubyバージョンがインストール可能かは以下のコマンドで確認できます。

# --list オプションでは各Ruby実装の最新安定版しか表示されない
rbenv install --list

# より網羅的にインストールできるバージョンを確認したい場合
rbenv install --list-all

その後、バージョンを指定してRubyをインストールします。

rbenv install 3.2.0

rbenv versionsでインストールしたRubyのバージョンを一覧で確認できます。

# この*がついているバージョンが現在有効なRubyバージョン
rbenv versions
  2.7.7
* 3.0.5 (set by /home/ubuntu/.rbenv/version)

rbenv versionでも現在有効なRubyのバージョンを確認できます。

rbenv version
3.0.5 (set by /home/ubuntu/.rbenv/version)

rbenvでどのRubyバージョンを使うかを指定しない場合、デフォルトでグローバルなRubyバージョンが指定されます。グローバルなRubyバージョンとしてバージョンを指定すると、Rubyをインストールしたマシン全体でそのバージョンをデフォルトのバージョンとして利用することができます。グローバルでRubyバージョンを指定する場合、以下のコマンドを実行します。

rbenv global 2.7.7

グローバル単位でバージョンが指定されていることが確認できました。

# ~/.rbenv/version => グローバルなRubyバージョン
# ~/.rbenv/version ファイルにバージョン情報を書き込む
rbenv version
2.7.7 (set by /home/ubuntu/.rbenv/version)

ディレクトリ単位でRubyバージョンを指定したい場合、指定したディレクトリに移動して、以下のコマンドを実行します。

rbenv local 3.0.5

ディレクトリ単位で指定されていることが確認できました。

# .ruby-version => ローカルなRubyバージョン
# .ruby-version ファイルにバージョン情報を書き込む
rbenv version
3.0.5 (set by /home/ubuntu/repos/practice1/ruby-version/.ruby-version)

ruby -vでも同様に確認できますが、グローバルで指定されているバージョンなのか、ローカルで指定されているバージョンなのかを確認することはできません。rbenv versionを使うことで、グローバルで指定されているバージョンなのか、ローカルで指定されているバージョンなのかを知ることができます。

ruby -v
ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c5) [x86_64-linux]

基本的には、rbenv、ruby-build、bundlerがあればRubyの環境構築は完了です。

Gemfileの~> について

Gemfileの ~>には、基本的にパッチバージョンまで指定します。 ~> 1.0.0は、マイナーバージョンを維持しつつ、指定したパッチバージョン以上の最新版を入れるという意味です。 つまり、~> 1.0.0は、1.1.0 > x >= 1.0.0という意味になります。

ちなみにマイナーバージョンとかパッチバージョンを忘れていたので、以下にまとめます。

バージョン 意味
パッチバージョン バージョン番号の一番右側のナンバー。後方互換性を伴うバグ修正をした場合にこちらのバージョンを上げる。
マイナーバージョン バージョン番号の中央のナンバー。後方互換性があり機能性を追加した場合にこちらのバージョンを上げる。マイナーアップデートを行った際は、パッチバージョンを0にリセットする。
メジャーバージョン バージョン番号の一番左のナンバー。APIの変更に互換性のない場合、一つ以上の破壊的な変更を含む場合にこちらのバージョンを上げる。メジャーアップデートを行った際は、マイナーバージョンとパッチバージョンを0にリセットする。

HTMLで知ったこと

HTMLのformタグで指定できるHTTPメソッドは、GETとPOSTの2つのみ

HTMLのformタグで指定できるHTTPメソッドは、GETとPOSTの2つのみです。あくまでHTMLのformタグに指定できるHTTPメソッドが2種類なだけであって、ブラウザはその他のHTTPメソッドにも対応しています。featch APIとかで、patchリクエストをしたりできます。formタグにmethodを指定しなかった場合、デフォルトではgetを指定したことになります。actionには送信先を指定します。

<form method="post" action="/users">
  <label for="name">name</label>
  <input type="text" id="name">
  <button type="submit">送信</button>
</form>

reactのformの場合、onSubmitでサブミット時に便乗して実行する関数を指定します。この関数内に送信処理を書きます。

HTMLのformタグでputリクエストやdeleteリクエストをしたい場合

HTMLのformタグでputリクエストやdeleteリクエストをしたい場合、postを指定しつつ以下の2つの方法のどちらかを使います。この方法を用いてpostリクエストを行うことで、postリクエストをサーバー側にしているが、サーバー側でdeleteリクエストとして扱うことができます。どちらの方法もサーバー側でリクエストを判別する処理が実装されている必要があるので、その点を注意しましょう。RailsSinatraではデフォルトで_methodを採用しています。

  1. _methodパラメータを使う
  2. X-HTTP-Method-Overrideヘッダーを使う

個人的にはRailsでも採用されている1番のやり方が簡単です。 以下が実装例です。type="hidden"は、ユーザーが見たり編集したりできないデータをフォームに含めたい時に使用します。nameはmethod、valueにはHTTPメソッドを指定します。サーバー側でmethodパラメータを見て、postリクエストをdeleteリクエストとして扱ってくれます。Railsのform_withなどでは最初から実装されています。

<form method="post" action="/users">
  <label for="name">name</label>
  <input type="text" id="name">
  <button type="submit">送信</button>
  <input type="hidden" name="_method" value="delete">
</form>

ReactだとonSubmitで送信処理を指定できるので、このような方法を使うことはありません。

JSを使わずにHTMLのみでDELETEリクエストのボタンを作るにはフォームを使う

aタグはhrefで指定したURLにGETリクエストを出します。GETリクエストしか出せないので、もしReactなどのフロントエンドライブラリを使っていないHTMLの場合、formを使うことでDELETEリクエストを発行するボタンを作成できます。

<div>
  <form method="post" action="/users/1">
    <input type="hidden" name="id" value="1">
    <input type="hidden" name="_method" value="delete">
    <button type="submit">delete</button>
  </form>
</div>

ただし、同期的にリクエストが行われるので、画面がリロードされるのがデメリットです(Reactでは非同期リクエストを出すのでそのようなことが起きない)。

input type="button"と、buttonタグの違い

input type="button"には、valueにボタンのラベルを指定します。buttonタグの場合、buttonタグで囲った要素がラベルになります。buttonタグの方がラベルの自由度が高いので、buttonタグを使うことがMDNでも推奨されています。

Rails

database.ymlとは

database.ymlとは、Rubyプログラムからデータベースにアクセスする際の設定を書くファイルのことです。一番簡単に使うためにはadapterとdatabaseを指定します。adapterにはデータベースの種類、databaseにはデータベース名を指定します。&defaultの部分はアンカーと呼びdefaultの設定を他の部分でも利用することができます。アンカーを呼び出すには、*をつけます。<<:は、右側にあるものを該当部にまとめるという意味です。

↓ database.yml

default: &default
  adapter: sqlite3

development: 
  <<: *default
  database: development.db

Rubyyamlライブラリを使えば、このアンカーがちゃんと該当部にまとめられていることを確認できます。

require 'yaml'
d = YAML.load_file("database.yml")
puts d
bundle exec ruby app2.rb
{"default"=>{"adapter"=>"sqlite3"}, "development"=>{"adapter"=>"sqlite3", "database"=>"development.db"}}

ActiveRecord::Base.table_name= で、使用するテーブル名を指定できる

クラス名が単数形のProductの場合、テーブル名は複数形のproductsになります。もし、テーブル名をクラス名の複数形にしたくない場合、ActiveRecord::Base.table_name=を使用します。

class Product < ApplicationRecord
  self.table_name = "my_products"
end

Web関係で知ったこと

クッキーについて

■そもそもクッキーとは
クッキーには2つの意味があります

  1. HTTP通信をステートフルにするための通信プロトコル
  2. サーバーから送られてきてブラウザ上に一定期間保存されるデータ

一般的には、2番の意味でクッキーという言葉が使われることが多いと感じます。

■クッキーの仕組み
以下のような手順でクッキーは利用されます。

  1. クッキーはHTTPレスポンスヘッダを利用して、サーバー側からブラウザに送られます。
  2. ブラウザはクッキーを指定された期間だけ保持します。
  3. 以後、クッキーを送ってきたサーバーにリクエストを送るときは、すべてのHTTPリクエストでHTTPリクエストヘッダにクッキー情報を含めます。

■クッキーでどのような値を管理しているのか
多くの場合、クッキーではセッションidなどのセッション情報を保持するための値を管理しています。また、クッキーがいつまで有効かの期限なども管理しています。

■クッキーは何を解決したのか
クッキーという仕組みができたことで、HTTP通信でリクエスト元を特定できて、ステートフルな通信をすることができます。

■クッキーの問題点
セッションidをクッキーに管理すると、そのセッションidが盗まれたらセッションハイジャックなどの攻撃を受ける可能性があります。これを防ぐためにはCookieにsecure属性を追加して、HTTPS通信の時にしかクッキーを送らないように設定します。

POSTリクエストのリクエストボディのフォーマットについて

POSTリクエストを送る際は、リクエストボディのフォーマットを指定する必要があります。リクエストボディのフォーマットを示すContent-TypeというヘッダーとMIMEタイプ(リソースの種類を表す)をリクエストに含めることで、リクエストボディのフォーマットを指定することができます。 以下はPOSTリクエストのリクエストボディの代表的なフォーマット(MIMEタイプ)です。

  1. application/x-www-form-urlencoded
    <form>タグのenctype属性を指定しなかった場合に使われるデフォルトのフォーマットです。
  2. multipart/form-data
    <form>タグのenctype属性に指定します。バイナリデータを送りたい場合やバイナリデータとテキストデータを混在して送りたい場合に利用します。
  3. application/json

publicディレクトリについて

(Railsアプリケーションのルート)/publicディレクトリ配下に画像等の静的なコンテンツを置いた場合、外部からアクセスすることができます。ブラウザからは、「http://ドメイン/静的コンテンツのファイル名」でアクセスできます。

Homebrewとは何か? Homebrewでインストールしたパッケージはどこに格納されるのか

HomebrewはmacOS上で動作するパッケージ管理ツールです。Homebrewを使ってパッケージをインストールすることで、公式サイトに行ってダウンロードをクリック → zipファイルを解凍 → 開いてインストールしなくても、コマンド一つでインストールできます。また、アンインストールやアップグレードもコマンドで簡単に行うことができます。

Homebrewのパッケージは、以下のディレクトリ配下にインストールされます。

  • コマンド実体は、/usr/local/Cellar
  • コマンドのエイリアスは、/usr/local/bin

PATHを通すとは

PATHを通すとは、PATHという環境変数にコマンドのパスを登録することです。環境変数とはPC環境についての変数のことです。ターミナルでenvを実行すると、設定されているすべての環境変数が表示されます。PATHという環境変数には、さまざまなパスが代入されています。それぞれのパスはコロンで区切られています。コンピュータはファイルパスを指定されなかった場合、PATHという環境変数に代入したパスの配下にファイルがあるかを確認していきます。この確認作業はコロンで区切ったパスごとに行われます。そのため、PATHを通すことで、コマンドを実行する際にフルパスを指定しなくてもファイル名でコマンドを実行することができます。

認証周りで知ったこと

認証方法の種類

以下の認証方法がメジャーです。

  • Basic認証(一番簡単に作れるけど、セキュリティ弱め)
  • ダイジェスト認証
  • セッションベースの認証(deviseを使うやつ)
  • トークン認証
  • OAuth(Auth0とかGoogleログインとか)

社内向けのサービスで、セキュリティリスクそこまで考えてなくて、いち早く認証機能を実装したい場合、Basic認証でも良いと思います。 今後toB向けのサービスとして展開していくなら、セッションベースか、トークンか、OAuthが良いです。

ウェブサイトのさまざまな「認証方式」をまとめて比較した結果がコレ - GIGAZINE

ソルトとは

以下の説明がわかりやすかったので、引用させていただきます。

ソルトとは、認証システムがパスワードなどを保存する際に付け加えるランダムな符号列のこと。“salt” の原義は「塩」。

ソルトとは - 意味をわかりやすく - IT用語辞典 e-Words

つまり、パスワード保存時に付け加えるランダムな文字列のことをソルトと言います。ソルトとパスワードを合体させた文字列をハッシュ関数で変換します。変換した値(ハッシュ値)とソルトをDBに保存します。パスワードのみをハッシュ化して保存していると、パスワードを複数システムで利用している場合に、一回でもパスワードがバレると他のシステムでも簡単にアクセスできてしまう恐れがあります。。ソルトはパスワード保存ごとに毎回ランダムに生成されます。そのため、同じパスワードを保存してもハッシュ値は毎回異なるため、仮にパスワードがバレても簡単に解読できないようになっています。

Authorizationヘッダー

Authorizationヘッダーは認証時にクライアントからサーバーに対して送るヘッダーのことです。このヘッダーに認証情報を含めます。Basic認証、Digest認証、トークン認証(Bearer認証)等のHTTP認証で利用します。

参考記事

Ruby用語集 (Ruby 3.0 リファレンスマニュアル)

オブジェクトとは - 意味をわかりやすく - IT用語辞典 e-Words

オブジェクト指向 - Wikipedia

ラッパーオブジェクト · JavaScript Primer #jsprimer

Ruby には型が無い,のか? - Qiita

Primitive (プリミティブ) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN

コンストラクタとは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

新規Railsプロジェクトの作成手順まとめ - Qiita

ruby - How to find where gem files are installed - Stack Overflow

ライブラリ

gem installしたgemのパスを知りたい - Eggshell

bundlerのpathを指定しない場合のインストール先 - コード日進月歩

bundle install時に--path vendor/bundleを付ける必要性は本当にあるのか、もう一度よく考えてみよう - Qiita

【Ruby】 gemの仕組みを図解形式で学ぼう | Pikawaka

【Rails】 結局bundlerって何?bundlerの仕組みを図解形式で徹底解説 | Pikawaka

【Rails】 rails newの使い方とは? | Pikawaka

Ruby 歴 10 年の私が【絶対に】 gem install rails コマンドを実行しない理由 - Qiita

rbenv+ruby-buildをインストールする手順 (CentOS/RedHat) |

バージョン表記の各数字の意味をイラストで理解する | DevelopersIO

Gemfileで使われる ~> はなにを意味するか

ちゃんと理解するrbenv : (2) 基本的な使い方

ruby-build | rbenv日本語リファレンス | Ruby STUDIO

GitHub - rbenv/rbenv: Manage your app's Ruby environment

HTTP リクエストメソッド - HTTP | MDN

<form>: フォーム要素 - HTML: HyperText Markup Language | MDN

HTMLのフォーム(form)の使い方・コンプリートガイド

なぜリンクをクリックすると自動的にGETメソッドになるのですか?

HTML button tag

<input type="button"> - HTML: HyperText Markup Language | MDN

【YAML】Railsのdatabase.ymlについてなんとなく分かった気になっていた記法・意味まとめ - Qiita

MySQL用のデータベース設定ファイル(database.yml) - Ruby on Rails入門

POSTパラメータを扱えるようにする|伸び悩んでいる3年目Webエンジニアのための、Python Webアプリケーション自作入門

MIME タイプ (IANA メディアタイプ) - HTTP | MDN

静的コンテンツの表示 - Ruby on Rails入門

【完全版】Homebrewとはなんぞや

PATHを通すとは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

HTTP認証 - Wikipedia

「HTTP」の仕組みをおさらいしよう(その4):リトライ! 触って学ぶTCP/IP(5)(1/2 ページ) - @IT