非同期処理及びPromise、async/awaitについてのまとめ

非同期処理及びPromise、async/awaitについて理解した為、備忘録として残します。

非同期処理って何?

複数の処理をする際に順番を待たないで次の処理を実行する処理の事です。また、処理を順番通りに行う事を同期処理と言います。

Javascriptでは、通常1行目から順に処理が実行されていきます。(同期処理)

    console.log("1行目");
    console.log("2行目");
    console.log("3行目");
    // 以下、出力結果
    // 1行目
    // 2行目
    // 3行目
  

しかし、Javascriptの非同期処理で代表的なメソッドsetTimeOut()メソッドを利用すると、以下のようになります。

    console.log("1行目");
    setTimeout(() => {
      console.log("2行目");
    }, 1000);
    console.log("3行目");
    // 以下、出力結果
    // 1行目
    // 3行目
    // 2行目
  

2行目より先に3行目が実行されていますね。これは、2行目のsetTimeOutで1秒後に処理を実行するように設定されており、2行目の処理を実行している間に3行目を並行して処理を実行した為です。

Promiseとは

非同期処理を行う手法の1つになります。

Promise オブジェクトは、成功時にはresolve、失敗時にはrejectを返します。以下に例を示します。

    function examResults(totalTestScore) {
      return new Promise((resolve, reject) => {
        if (totalTestScore > 400) {
          resolve("受験結果:合格");
        } else {
          reject("受験結果:不合格");
        }
      });
    }

    function postExamProcessing() {
      console.log("examResultsメソッドと並行に処理を実行");
    }
    
    examResults(450)
    .then(data => {
      console.log(data);
    })
    .catch(error => {
      console.log(error);
    });
    postExamProcessing();
    // 以下、出力結果
    // "examResultsメソッドと並行に処理を実行"
    // "受験結果:合格"
  

examResultsのtotalTestScoreに450を渡した結果、resolve("受験結果:合格")が返却され、examResultsのthenからログが出力されました。

また、Promiseオブジェクトは非同期処理である為、examResults よりも先に postExamProcessing の処理が完了し、ログが出力されています。

async/awaitとは

非同期処理を同期処理のように動作させることができる手法です。

以下に例を示します。

    function examresults(totaltestscore) {
      return new Promise((resolve, reject) => {
        if (totaltestscore > 400) {
          resolve("受験結果:合格");
        } else {
          reject("受験結果:不合格");
        }
      });
    }

    function postExamProcessing() {
      console.log("examResultsメソッドと並行に処理を実行しません。");
    }
    
    async function printExamResults() {
      try {
        const result = await examresults(450);
        console.log(result);
        postExamProcessing()
      } catch (error) {
        console.log(error);
      }
    }

    printExamResults();
    // 以下、出力結果
    // "受験結果:合格"
    // "examResultsメソッドと並行に処理を実行しません。"
  

今回は、examresultsメソッドの処理が完了してから、postExamProcessingメソッドが処理されている事がわかりますね。

このasync/awaitは非常に便利で、サーバー通信の処理を書いた後にその後のコードではサーバー通信で返却された値を利用したい場合、サーバー通信の処理を待つようにする事ができます。

最後に

この記事では、非同期処理及びPromise、async/awaitについてまとめました。最初は全く聞き覚えのない言葉で理解することに手間取りました。笑

まだ理解できていないことは大量にあるので、少しずつ身につけていきたいと思います。最後まで読んでくださりありがとうございました〜

参考資料

間違えてgit add及びコミットをしてしまった際の取り消し方法

間違えてgit addgit commitをしてしまった際の取り消し方法をまとめる。

git add を取り消す

全てのファイルを取り消したい場合は、以下のコマンドを叩く。

    git rm --cached -r .
  

一部のファイルを取り消したい場合は、以下のコマンドを叩く。

    git rm --cached -r [ディレクトリ及びファイルのパス]
  

パスを調べたい場合は、以下のコマンドを入力して、git addしているディレクトリなどの情報を取得しましょう。

    git status
  

直前のコミットの取り消し

    git reset --soft HEAD^
  
  • HEAD^(又はHEAD~@^:直前のコミットを指す。
  • HEAD~{n}:n個前のコミットを指す。
  • HEAD~{n}:n個前のコミットを指す。
  • --soft:インデックス・ワーキングツリーはそのままで、コミットのみ取り消し。
  • --hard:インデックス・ワーキングツリーはコミットの一つ前の状態に戻した上でコミットを取り消し。

最後に

間違えてコミットしても上記のコマンドを活用すれば、取り消せるのでご安心を。

ここまで読んで下さりありがとうございました〜

参考資料

RubyAPI3、Rails7、Mysql8、ReactでDockerにて環境構築する方法

SPAアプリケーションを作るために、環境構築をしました。今回の記事は環境構築の備忘録です。

バージョン

アプリケーションの基盤を作成

RailsとReact用のディレクトリを作成します。Railsにはbackendディレクトリ、Reactにはfrontendディレクトリを作成します。

    mkdir practiceApp
    cd practiceApp
    mkdir backend
    mkdir frontend
    touch docker-compose.yml
    touch backend/Gemfile
    touch backend/Gemfile.lock
    touch backend/entrypoint.sh
    touch backend/Dockerfile
    touch frontend/Dockerfile

Rails用の設定を作成

以下のファイルにRailsの設定を追記します。

  • entrypoint.sh
    #!/bin/bash
    set -e

    # Remove a potentially pre-existing server.pid for Rails.
    rm -f /myapp/tmp/pids/server.pid

    # Then exec the container's main process (what's set as CMD in the Dockerfile).
    exec "$@"
  • Dockerfile
    FROM ruby:3.2.1
    RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs

    WORKDIR /myapp
    COPY Gemfile /myapp/Gemfile
    COPY Gemfile.lock /myapp/Gemfile.lock
    RUN bundle install
    COPY . /myapp

    # Add a script to be executed every time the container starts.
    COPY entrypoint.sh /usr/bin/
    RUN chmod +x /usr/bin/entrypoint.sh
    ENTRYPOINT ["entrypoint.sh"]
    EXPOSE 3000

    # Start the main process.
    CMD ["rails", "server", "-b", "0.0.0.0"]
  • Gemfile
    source "https://rubygems.org"
    ruby "3.2.1"

    # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
    gem "rails", "~> 7.0.4"
  
  • docker-compose.yml
    version: '3.8'

    services:
      db:
        image: mysql:8.0
        platform: linux/x86_64
        command: --default-authentication-plugin=mysql_native_password
        ports:
          - "4306:3306"
        volumes:
          - db:/var/lib/mysql
        environment:
          MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
        security_opt:
          - seccomp:unconfined
      backend:
        build:
          context: ./backend/
          dockerfile: Dockerfile
        stdin_open: true
        tty: true
        volumes:
          - ./backend:/myapp
          - bundle:/usr/local/bundle
        command: bash -c "rm -rf tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
        depends_on:
          - db
        ports:
          - "3001:3000"
        environment:
          TZ: Asia/Tokyo
    volumes:
      db:
        driver: local
      bundle:
        driver: local

上記のファイルを作成したら、以下のコマンドを実行して下さい。

docker-compose run --no-deps backend rails new . --force -d mysql --api --skip-test

dbという名前でデータベース環境を作成している為、以下のファイルのdefaulthost: dbを追加

  • database.yml
    default: &default
    adapter: mysql2
    encoding: utf8mb4
    pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
    username: root
    password:
    host: db // この箇所を修正

React用の設定を作成

  • practiceApp/frontend/Dockerfile
    FROM node:14.17.1-alpine
    WORKDIR /usr/src/app
  • docker-compose.yml

元々あるdocker-compose.ymlservices:frontend:を追加

    version: '3.8'

    services:
      db:
        image: mysql:8.0
        platform: linux/x86_64
        command: --default-authentication-plugin=mysql_native_password
        ports:
          - "4306:3306"
        volumes:
          - db:/var/lib/mysql
        environment:
          MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
        security_opt:
          - seccomp:unconfined
      backend:
        build:
          context: ./backend/
          dockerfile: Dockerfile
        stdin_open: true
        tty: true
        volumes:
          - ./backend:/myapp
          - bundle:/usr/local/bundle
        command: bash -c "rm -rf tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
        depends_on:
          - db
        ports:
          - "3001:3000"
        environment:
          TZ: Asia/Tokyo
      frontend: // このfrontendを追加
        build:
          context: ./frontend/
          dockerfile: Dockerfile
        volumes:
          - ./frontend:/usr/src/app
        command: sh -c "cd app && npm start"
        ports:
          - "3000:3000"
    volumes:
      db:
        driver: local
      bundle:
        driver: local

以下のコマンドでDockerのイメージを実行して、Reactのテンプレートを作成

docker-compose run --rm frontend sh -c "npx create-react-app app"

動作確認

以下のコマンドで今回作成したDockerを起動させましょう。

docker-compose up

Dockerを起動させた後、まだRailsのデータベースが作成されていないので、以下のコマンドでデータベースを作成して下さい。

docker-compose exec backend rails db:create

ブラウザでhttp://localhost:3000/及びhttp://localhost:3001/にアクセスしてみて下さい。

それぞれReactの画面とRailsの画面が表示されたら、環境構築の出来上がりです。

Git管理

このままでは、RailasとReactでGit管理されてしまう為、以下のコマンドを実行して下さい。

    rm -rf backend/.git
    rm -rf frontend/.git
    git init

最後に

環境構築って結構骨が折れますよねw 今回は他の方の記事を参考に現時点でのRailsやReactなどでの最新の環境を作るようにしてみました。

自分は環境構築あまり好きじゃないですが、とにかく苦手意識を持たずにならしていこうかと思います。

この後は環境構築せっかくしたので、色々触ってみようと思います。

ここまで読んで下さりありがとうございました〜

参考資料

Dockerコンテナ使用時に何故サーバーを立てる際にIPアドレス0.0.0.0を指定するのか

概要

Dockerを利用する際に何故rails sでバインドするIPアドレス0.0.0.0で指定するのか気になった為、備忘録として残す。

何故バインドするIPアドレスlocalhost(127.0.0.1)ではなく、0.0.0.0を指定するのか

ローカルマシンで127.0.0.1にアクセスしようとしてもローカルにlistenするのではなく、Dockerコンテナにlistenしており、アクセスができない為、localhost(127.0.0.1)は指定しません。

そこで何故0.0.0.0を指定するのかと言うと、Dockerで0.0.0.0ワイルドカードIPアドレスとして公開しているからです。

このワイルドカードIPアドレスによって、ホストマシン(自分のMacなど)で到達可能な全てのIPアドレスが対象になっている為、ローカルマシンで127.0.0.10.0.0.0にアクセスしようとしても問題なくlistenしてくれます。

最後に

この知識が皆さんの参考になれば嬉しいです。何か気になる箇所や間違ってる箇所があればコメントして頂けると嬉しいです。ここまで読んで下さりありがとうございました〜

参考資料

ホスト上にコンテナのポートを割り当て [Docker]0.0.0.0でサーバーを立てる理由(Python)

Javascriptでのreplaceメソッドの第二引数の関数が理解できた件について

なぜ記事を書こうと思ったか

開発の際にJavascriptのreplaceメソッドで第一引数に正規表現、第二引数にインライン関数を使用する際のインライン関数の引数に何の情報が入っているか分からず開発が進まない事があった為、今回replace関数の引数に入る情報をまとめたいと思います。

replaceメソッドはどのような処理?

replaceメソッドは簡単に言うと、パターンに一致した部分文字列の一部またはすべてを置換文字列で置き換えた新しい文字列を返します。

簡単な例だと以下のような形です。

const greeting = "初めまして、私は田中太郎です。このクラスの田中健太君と友達です。よろしくお願いします。";

// 第一引数が文字列の場合
console.log(greeting.replace("田中", "佐藤"));
// コンソール結果("初めまして、私は佐藤太郎です。このクラスの田中健太君と友達です。よろしくお願いします。")

// 第一引数が正規表現の場合
console.log(greeting.replace(/田中/i, "佐藤"));
// コンソール結果("初めまして、私は佐藤太郎です。このクラスの田中健太君と友達です。よろしくお願いします。")

上記のreplaceメソッドは簡単に理解できると思います。自分にとってはこの後の内容が理解に苦しみました。(こんな事すぐ分かって当然だろ!って思う方もいると思いますが、そっと目を瞑って温かい目でご覧になって下さい)

replaceメソッドの第二引数に関数を指定した場合について

以下にreplaceメソッドの第二引数に関数を指定した場合の例を記載致します。

function replacer(match, regexpMatch1, regexpMatch2, offset, string) {
  return "{小池徹平}";
}
const introduction = "初めまして、私は田中太郎です。このクラスの{佐藤健}と友達です。よろしくお願いします。";
const newIntroduction = introduction.replace(/({佐藤([^}}]*)})/g, replacer);
console.log(newIntroduction);
// コンソール結果("初めまして、私は田中太郎です。このクラスの{小池徹平}と友達です。よろしくお願いします。")

このreplaceメソッドの第二引数のreplacerの引数に何が入っているのか分かりませんでした。

簡単に以下にreplacer関数の引数にどのような情報が入っているかまとめます。

  • 第一引数(match)

replaceメソッドの第一引数の正規表現に該当する文字列を格納しています。今回の場合は"{佐藤健}"が該当します。

  • 第二・第三引数(regexpMatch1・regexpMatch2)

replaceメソッドの第一引数の正規表現の()に囲まれた部分(サブパターン)を格納します。regexpMatch1には正規表現の"({佐藤([^}}]*)})"に該当する"{佐藤健}"が格納され、regexpMatch2には正規表現の"([^}}]*)"に該当する"健"が格納されています。

この第二・第三引数については、正規表現のサブパターンを指定しない場合は存在せず、今回よりも多くのサブパターンを指定する場合は第四引数、第五引数・・・と増えていきます。

  • 第四引数(offset)

変数introductionの文字列で何文字目の後に正規表現が合致するかを格納します。今回の場合は21が該当します。

  • 第五引数(string)

replaceメソッドをしている変数の値を格納します。今回の場合は"初めまして、私は田中太郎です。このクラスの{佐藤健}と友達です。よろしくお願いします。"が該当します。

 

上記に書いた事で引数にはどのような情報が格納されているか理解できると思います。

最後に

正直他の方の記事のreplaceメソッドの第二引数に関数を指定した場合の説明は、自分には全く分からなかったです笑(自分が頭悪いだけです、、すみません、、)

今回の記事は丁寧に書いているので、恐らく初心者の方でも理解できる内容になっていると思います。最後まで読んでいただきありがとうございました〜

参考資料

MDN/String.prototype.replace()

ストロングパラメーターの基本的な書き方と対応できないオブジェクト例

なぜ記事を書こうと思ったか

Railsアプリ開発している際に、ストロングパラメーターで対応できないオブジェクトがあることを知った為、今回ストロングパラメータの基本的な書き方と一緒にこの記事にまとめようと思います。

ストロングパラメーターの書き方

基本的なオブジェクト

{
  name: "田中",
  age: 23
}
params.permit(:name, :age)

配列が入っている場合

{
  name: "田中",
  hobbies: [
    "サッカー", "映画鑑賞"
  ],
  age: 23,
  favorite_food: [
    "ラーメン", "寿司"
  ]
}
値に配列が入っているプロパティはストロングパラメーターで最後に記述する
params.permit(:name, :age, hobbies: [], favorite_food: [])

オブジェクトがネストした場合

{
  name: "田中",
  setting: {
    age: 23,
    department: "営業部"
  }
}
ネストしたプロパティもストロングパラメーターで最後に記述する
params.permit(:name, setting: [:age, department])

ストロングパラメーターで対応できないオブジェクトについて

ストロングパラメーターで全てのオブジェクトを対応している訳ではなく、直接、値を取得しないといけない場合もある。

二次元配列が入っている場合

{
  person: [
    "田中", "佐藤"
  ],
  setting: [
    [
      23, "営業部"
    ],
    [
      28, "開発部"
    ]
  ]
}
settingsをストロングパラメーターで取得しようとすると、エラーになる。
params.permit(person: [], setting: [[]])
settingsは直接取得する必要がある。
params.permit(person: [])
params[:setting]

最後に

正直、二次元配列のようにストロングパラメータで書く事ができないオブジェクトがあることは知りませんでした笑(開発中にたまたま見つけました)

もし他にストロングパラメーターで書くことができないオブジェクトや他のストロングパラメーターの書き方がある場合は、コメントで教えていただけると嬉しいです。読んで頂きありがとうございました。

参考資料

Rails及びDocker環境でのpry-railsのデバッグ方法

なぜ記事を書こうと思ったか

Rails及びDocker環境でpry-railsでのデバッグをおこなったので、そのやり方を記載致します。

これまで、行っていたデバッグよりもより細かくコードを確認できるので、すごく便利に感じたので、多くの新人の方が使ってくれると嬉しいです。

Rails及びDocker環境でpry-railsでのデバッグ手順

※Dockerは構築して、起動してある事を前提として記載致します。

デバッグ手順

1.Railsのコードで動作を止めて、確認したいコードの直後にbinding.pryを追加します。

今回は例として、createメソッドのコードに付け足したいと思います。

def create
  permit_parameters = params.permit(:name, :age, :sex).to_h
  binding.pry

  user = User.create!(
           name: permit_parameters[:name].to_i,
           age: permit_parameters[:age],
           sex: permit_parameters[:sex]
         )

  return_json = { "#{ user.name }さんの登録を受け付けました。" }
  render json: return_json
end

上記のようにするだけで、permit_parameters変数の行までしか実行されないようになります。

2.docker ps -aでDockerで起動しているコンテナの一覧を表示し、Railsサーバーを立ち上げているコンテナのIDを確認します。

3.docker attach コンテナIDを入力し、Railsサーバーを立ち上げているコンテナに入ります。

4.今回の場合はcreateメソッドにデータをPOSTします。

ログと共に、以下のようなログも表示されます。

  1:      def create
  2:        permit_parameters = params.permit(:name, :age, :sex).to_h
  3:        binding.pry
  4:
=>5:        user = User.create!(
  6:                 name: permit_parameters[:name],
  7:                 age: permit_parameters[:age],
  8:                 sex: permit_parameters[:sex]
  9:               )
  10:
  11:       return_json = { "#{ user.name }さんの登録を受け付けました。" }
  12:       render json: return_json
  13:     end

上記のログでは、=>が書いてある行以前まで、コードが動いている事を表します。

また、[1] pry(#)>railsコンソールの時と同様の操作ができます。

[1] pry(#)> params[:name]
=> "田中太郎"

5.次の行のコードまで動作させたい場合は、nextと入力する。

[2] pry(#)> next

  1:      def create
  2:        permit_parameters = params.permit(:name, :age, :sex).to_h
  3:        binding.pry
  4:
  5:        user = User.create!(
  6:                 name: permit_parameters[:name],
  7:                 age: permit_parameters[:age],
  8:                 sex: permit_parameters[:sex]
  9:               )
  10:
=>11:       return_json = { "#{ user.name }さんの登録を受け付けました。" }
  12:       render json: return_json
  13:     end

[3] pry(#)>

先ほどまでは4行まで動作していましたが、今回は10行まで動作した状態になります。

このような作業をするだけで各コードが適切に動作しているか確認することができます。

デバッグ終了方法

1.continueを入力して、止まっていたコードを全て動作させる。

[3] pry(#)> continue

~~~ログが流れる~~~

2.controlボタンを押しながら、pの後にqを押す。

こうすることで、コンテナから出ることができます。もし、exitcontorol + cをした場合は、サーバーも停止されます。

3.binding.pry`を削除します。

最後に

自分は、logger.debugやログで動作状況を確認していたので、このデバッグ方法は非常に便利に感じました。是非、やった事がない方はやってみる事をお勧めします。もし、もっと良いデバッグ方法がある場合は、コメントで教えていただけると嬉しいです。読んで頂きありがとうございました。