⚙️

Dev Containerで作るSinatra開発環境(MySQL対応)

2024/12/21に公開

はじめに

Dev Containerとは、開発環境をコンテナ化することで、開発者が簡単に統一された環境を利用できるようにする仕組みです。詳細は以下の記事をご参照ください。

https://zenn.dev/takayuu/articles/dev-container-80f6900ce9eb8c

本記事では、Dev Containerを使って以下の環境を構築する手順を解説します。

  • Sinatra・MySQL開発環境
    Rubyの実行環境を整え、Bundlerを用いて依存関係を管理します。
    さらに、Sinatra環境を構築し、MySQLとの接続を設定します。

  • コード品質管理環境
    Prettier、Rubocop、RubyLSPを導入し、コード整形と品質管理を自動化します。

Dev ContainerによるSinatra・MySQL開発環境の構成

準備

必要なツールをインストール

必要な設定項目と確認項目

以下の設定項目を順に確認しながら、進めていきます。

項目 確認内容
Dev Containerの起動 コンテナが正常に起動し、VS Code上で作業可能か確認
Node.js・Yarn・Ruby・Bundlerの確認 それぞれがインストールされているか確認
Sinatraの導入 Sinatraが正常に動作することを確認
MySQLの導入 MySQLが正常に起動することを確認
SinatraからMySQLにアクセス Sinatra経由でMySQLに接続できることを確認
データベース作成 Rakeタスクを作成し、データベースが作成されることを確認
テーブル・データ作成 Rakeタスクを作成し、テーブルとデータが作成されることを確認
データの表示 ブラウザ上でデータが表示されることを確認

Dev Containerの起動

Dev Containerで作るRuby開発環境(Prettier & Rubocop & RubyLSP対応)を参考に、必要な設定を行い、Dev Containerの起動およびNode.js・Yarn・Ruby・Bundlerが正しくインストールされていることを確認

Sinatraの導入

  1. GemfileにSinatraを追加、依存関係をインストール

    Gemfile
    ...
    gem 'sinatra'
    
    bundle install
    
  2. ルートにindex.rbを新規作成し、アプリケーション実行

    index.rb
    # frozen_string_literal: true
    
    require 'sinatra'
    
    get '/' do
      'Hello, world!'
    end
    
    bundle exec ruby index.rb
    
  3. ブラウザでhttp://localhost:4567を開き、表示確認

MySQLの導入

  1. devcontainer.jsonを修正し、devcontainer.jsonと同じ階層にdocker-compose.ymlを追加・MySQLコンテナの設定追加

    devcontainer.json
    {
       "name": "ruby-web",
       "dockerComposeFile": "docker-compose.yml",
       "service": "web",
       "workspaceFolder": "/workspace",
       ...
     }
    
    docker-compose.yml
    services:
      web:
        build:
          context: ..
          dockerfile: .devcontainer/Dockerfile
        volumes:
          - ..:/workspace
          - node_modules_cache:/workspace/node_modules
          - gem_cache:/workspace/vendor/bundle
        command: ['sleep', 'infinity']
    
      db:
        image: mysql:8
        environment:
          MYSQL_ROOT_PASSWORD: password
        volumes:
          - db_data:/var/lib/mysql
    
    volumes:
      node_modules_cache:
      gem_cache:
      db_data:
    
  2. コンテナを再起動し、コンテナ内からMySQLコンテナへの接続確認

    mysql -h db -u root -ppassword
    

SinatraからMySQLにアクセス

  1. docker-compose.ymlに環境変数を追加

    docker-compose.yml
    services:
      web:
        ...
        environment:
          DATABASE_HOST: db
          DATABASE_USER: root
          DATABASE_PASSWORD: password
          DATABASE_NAME: template-sinatra_development
    
  2. GemfileにMySQL関連のGemを追加・インストール

    Gemfile
    ...
    gem "mysql2", "~> 0.5.6"
    
    bundle install
    
  3. index.rbに接続確認用のルートを追加し、アプリケーション実行

    index.rb
    get '/health_check' do
      Mysql2::Client.new(
        host: ENV['DATABASE_HOST'],
        username: ENV['DATABASE_USER'],
        password: ENV['DATABASE_PASSWORD']
      )
      'OK'
    rescue StandardError => e
      status 500
      e.message
    end
    
    bundle exec ruby index.rb
    
  4. ブラウザでhttp://localhost:4567/health_checkを開き、表示確認

データベース作成

  1. ルートにRakefileを新規作成し、データベースを作成するためのタスク追加

    require 'rake'
    require 'mysql2'
    
    namespace :db do
      desc 'データベースを作成します'
      task :create do
       begin
        client = Mysql2::Client.new(
          host: ENV['DATABASE_HOST'],
          username: ENV['DATABASE_USER'],
          password: ENV['DATABASE_PASSWORD']
        )
    
        db_name = ENV['DATABASE_NAME']
        client.query("CREATE DATABASE IF NOT EXISTS #{db_name}")
        puts "データベースを作成しました: #{db_name}"
       rescue Mysql2::Error => e
        puts "エラーが発生しました: #{e.message}"
       ensure
        client&.close
       end
      end
    
  2. タスクを実行し、データベースが作成されるのを確認

    bundle exec rake db:create
    

テーブル・データ作成

  1. Rakefileにテーブルとデータを作成するためのタスク追加

    ...
    namespace :db do
      ...
      desc 'データを追加します'
      task :seed do
       begin
        client = Mysql2::Client.new(
          host: ENV['DATABASE_HOST'],
          username: ENV['DATABASE_USER'],
          password: ENV['DATABASE_PASSWORD']
          database: ENV['DATABASE_NAME']
        )
    
        client.query(<<~SQL)
          CREATE TABLE IF NOT EXISTS users (
           id INT AUTO_INCREMENT PRIMARY KEY,
           name VARCHAR(255),
           email VARCHAR(255)
          );
        SQL
    
        client.query("INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')")
        client.query("INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com')")
        puts 'サンプルデータを追加しました'
       rescue Mysql2::Error => e
        puts "エラーが発生しました: #{e.message}"
       ensure
        client&.close
       end
      end
    
  2. タスクを実行し、テーブル・データが作成されるのを確認

    bundle exec rake db:seed
    

データの表示

  1. 以下のコードをindex.rbに追記、アプリケーション実行

    index.rb
    DB_CLIENT = Mysql2::Client.new(
      host: ENV['DATABASE_HOST'],
      username: ENV['DATABASE_USER'],
      password: ENV['DATABASE_PASSWORD'],
      database: ENV['DATABASE_NAME']
    )
    
    DB_CLIENT.query_options.merge!(symbolize_keys: true)
    
    get '/users' do
      users = DB_CLIENT.query('SELECT * FROM users')
    
      response = <<~HTML
        <h1>Users List</h1>
        <ul>
      HTML
    
      users.each do |user|
        response += "<li>#{user[:name]} (#{user[:email]})</li>"
      end
    
      response += '</ul>'
      response
    end
    
    bundle exec ruby index.rb
    
  2. ブラウザで http://localhost:4567/users を開き、登録データが表示されることを確認

最終フォルダ構成

/.devcontainer/
  ├── devcontainer.json
  ├── docker-compose.json
  └── Dockerfile
/.gitignore
/package.json
/yarn.lock
/Gemfile
/Gemfile.lock
/.prettierrc.json
/rubocop.yml
/index.rb
/Rakefile

以上で、Sinatra開発環境(MySQL対応)の構築が完了しました。

サンプルコード

これまでの内容に加え、以下を実装したテンプレートプロジェクトです。

  • Rspecを使ったテスト
  • dotenvを使用した環境変数の切り替え対応
  • Sequelを使用したマイグレーション機能
  • GitHub Actionsを利用したCI/CDの設定

https://github.com/yuuu-takahashi/template-sinatra

Discussion