20201116のRailsに関する記事は23件です。

エラーメッセージの重複をなくしたい

エラーメッセージの重複①

form_withメソッドを使って、
form.number_fieldで数値を入力させるフォームを作った。
正しい数値を入力すればエラーは起きないし、バリデーションを超える数値を入力しようとすると、そのタイミングでエラーが出る。
しかし、空のまま入力フォームを送ると、


○○を入力してください
○○は不正な値です
○○は一覧にありません


と、エラーが3つも出てしまう。
空を送信しただけで、3つもいらん!!!

解決方法

model
with_options presence: true do
    validates :grade, format: { with: /\A[0-9]+\z/, allow_blank: true}, inclusion: { in: 1..7, allow_blank: true }
    validates :class_number, format: { with: /\A[0-9]+\z/, allow_blank: true }, inclusion: { in: 1..10, allow_blank: true }
end

formatは正規表現のバリデーション。
inclusionは数値の範囲のバリデーション。

allow_blank: trueは値が空の場合はバリデーションを実行しないメソッド。
冗長だが、これで空のときにはformatとinclusionのバリデーションは反応しない。

改めてエラーメッセージを見ると、
「○○は不正な値です」はformatに反応していて、
「○○は一覧にありません」はinclusionに反応していることがわかる。

エラーメッセージの重複②

外部キーを使っている状態で、そこにpresence: trueのバリデーションをかけると、同じような内容で2つのエラーメッセージが出る。

解決方法

モデルに記述している、外部キーのバリデーションを消す。

【余談】エラーメッセージの順番を変えたい

順番はバリデーションとアソシエーションの記述位置を変える事で解消できる。記述の位置を変えるだけなので、特段問題が起こらないことが有難い。
これは、コードが上から読み込まれていることに起因すると考えられる。ちょっとしたことだけど、入力フォームの順に、エラーメッセージを出してあげたい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

deviseをRailsで扱う

まず、こちらではデバイス導入の一連の流れ(自分が行った)ことを復習として、また忘れないようこちらに記載させていただこうかと思います。
初学者なので間違ったことを伝えていた場合は、都度ご連絡お願い申し上げます。

deviseについて

まず、デバイスの概要ですが、こちらはユーザー管理機能を実装するためのGemです。
新規登録やログインログアウトなんかが簡単に実装できます。

deviseの導入

Gemfileを編集しましょう

gem 'devise'(一番最後の行に追記しましょう)

これで、ローカル、テスト、本番環境でデバイスが扱えます。

ローカルサーバーを再起動

Gemをインストールした後はターミナルで起動しているローカルサーバーを一度再起動しましょう。
インストールしたGemの反映のタイミングが、サーバー起動時のためです。

deviseの設定ファイルを作成

deviseを使用するためには、Gemのインストールに加え、devise専用のコマンドで設定ファイルを作成する必要があります。
こちらのコマンドでファイルを作りましょう

% rails g devise:install

以下のようなログが流れると成功!

      create  config/initializers/devise.rb
      create  config/locales/devise.en.yml
===============================================================================

Depending on your application's configuration some manual setup may be required:

  1. Ensure you have defined default url options in your environments files. Here
     is an example of default_url_options appropriate for a development environment
     in config/environments/development.rb:

       config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

     In production, :host should be set to the actual host of your application.

     * Required for all applications. *

  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:

       root to: "home#index"

     * Not required for API-only Applications *

  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:

       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>

     * Not required for API-only Applications *

  4. You can copy Devise views (for customization) to your app by running:

       rails g devise:views

     * Not required *

===============================================================================

デバイスでUserモデルを作成

deviseを利用する際には、アカウントを作成するためのUserモデルを新しく作成する必要があります。
作成には通常のモデルの作成方法ではなく、deviseのモデル作成用コマンドでUserモデルを作成します。
下記のコマンドでモデルとそれに付随するマイグレーションファイル、ルーティングの設定をまとめて生成でき
routes.rbにはdeviseに関連するパスが追加されます。

 rails g devise user

以下のログにて成功

  invoke  active_record
      create    db/migrate/20200309082300_devise_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml
      insert    app/models/user.rb
       route  devise_for :users

また、routes.rbに以下のルーティングが自動的に追記されます。

Rails.application.routes.draw do
  devise_for :users
  root to: 'tweets#index'
  resources :tweets
end

devise_forは、ユーザー機能に必要な複数のルーティングを一度に生成してくれるdeviseのメソッド。

テーブルの作成

先ほどのコマンドでマイグレーションファイルも生成されたのでマイグレーションを行います。

rails db:migrate

こちらでテーブルの作成が完了しました。

テーブルの情報を変更(マイグレーション)したので、ローカルサーバーを起動時には、再起動しましょう。

deviceのビューファイルの作成

deviseでログイン機能を実装すると、ログイン/サインアップ画面が自動的に生成されますがビューファイルとしては生成されません。
これは、deviseのGem内に存在するビューファイルを読み込んでいるためです。

deviseのビューファイルに変更を加えるためには、deviseのコマンドを利用して、ビューファイルを生成する必要があります。

コマンドを実行してdevise用のビューを作成

% rails g devise:views

こちらで作成されたログイン画面のビュー、新規登録画面のビューファイルを編集して見た目を作っていきます。

こちらでdeviceの基礎的な導入は完了です。
簡単なものであればこちらで大丈夫かなと言う認識ですが、
他にdeviseで用いられる

rails g devise:contoroller users

があったりします。
そちらの使い方は、以下別の記事でまとめたいと思います。
(必要になる基準を理解していないため)

ストロングパラメーターの設置

サインアップ時に入力する情報はパラメーターとしてサーバーに送信されます。deviseを使わない通常のリクエストの場合は、コントローラーにストロングパラメーターを記述し、受け取れるパラメーターを制限していました。

deviseに関しても、同様にストロングパラメーターをコントローラーに記述します。しかし、deviseの処理を行うコントローラーはGem内に記述されているため、編集することができません。
また、deviseでログイン機能を実装した場合は、paramsの他に、paramsとは異なる形のパラメーターも受け取っています。

以上から、deviseのコントローラーにストロングパラメーターを反映する方法と、devise特有のパラメーターを取得する方法が、必要になります。

devise_parameter_sanitizerメソッド

deviseにおけるparamsのようなメソッドです。deviseのUserモデルに関わる「ログイン」「新規登録」などのリクエストからパラメーターを取得できます。

このメソッドとpermitメソッドを組み合わせることにより、deviseに定義されているストロングパラメーターに対し、自分で新しく追加したカラムも指定して含めることができます。

devise_parameter_sanitizerメソッドは、これまでのストロングパラメーターと同じく、新たに定義するプライベートメソッドの中で使用します。deviseの提供元では、新たに定義するメソッド名をconfigure_permitted_parametersと紹介していることから、慣習的にこのメソッド名で定義することが多いです。

private
def configure_permitted_parameters  # メソッド名は慣習
  # deviseのUserモデルにパラメーターを許可
  devise_parameter_sanitizer.permit(:deviseの処理名, keys: [:許可するキー])
end

deviseのpermitは、第一引数にdeviseの処理名、第二引数にkeysというキーに対し、配列でキーを指定することで、許可するパラメーターを追加します。

第一引数の処理名には、deviseですでに設定されているsign_in, sign_up, account_updateが使用でき、それぞれサインイン時やサインアップ時、アカウント情報更新時の処理に対応しています。

処理名   役割
:sign_in        サインイン(ログイン)の処理を行うとき
:sign_up        サインアップ(新規登録)の処理を行うとき
:account_update アカウント情報更新の処理を行うとき

これらの記述をapplication_controller.rbに記述します。

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  private
  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname])
  end
end

before_actionのifというオプションを使用している点に注目してください。
これは、値にメソッド名を指定することで、その戻り値がtrueであったときにのみ処理を実行するよう設定するものです。

今回は:devise_controller?というdeviseのヘルパーメソッド名を指定して、もしdeviseに関するコントローラーの処理であれば、そのときだけconfigure_permitted_parametersメソッドを実行するように設定しています。他のtweetsコントローラーなどでは処理は読み込まれても、実行まではされません。

その他のコマンドについて

rails g migrationコマンド

マイグレーションを生成するコマンドです。
マイグレーションはこれまで、rails g modelコマンドでモデルと一緒に生成されていましたが、すでに作成されたテーブルの内容を変更する際などに使用します。

このコマンドは、指定するファイルの名前によって、どのようなテーブル操作を行うかを自動で記述します。

rails g migration Addカラム名To追加先テーブル名 追加するカラム名:型とすることで、テーブルにカラムを追加する際に必要なコードが記述された状態で、マイグレーションが生成されます。

スネークケースとキャメルケース

スネークケースとキャメルケースは、それぞれ単語の区切り方を表したものです。

スネークケースは、単語の区切りをアンダースコアで表す
キャメルケースは、単語の区切りを大文字で表す
変数名や関数名の単語が連立する場合、単語を区切る方法がいくつかあります。
以下の表をみてください。

表記方法    説明  例
キャメルケース   先頭が小文字で、単語の区切りを大文字で表す adminUserCommentCreator
アッパーキャメルケース   キャメルケースの1つ。先頭から単語の区切りを大文字で表す  AdminUserCommentCreator
スネークケース   単語の区切りをアンダースコアで表す admin_user_comment_creator

Railsの慣習的な命名規則として、下記のように使い分けます。

命名の対象 慣習的な命名規則
クラス名    アッパーキャメルケース
メソッド名 スネークケース
変数名   スネークケース

自分でクラスやメソッドを作成する際には、これらの命名規則は守るようにしましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【アウトプット】データベースについて

データベースの振り返りをしたのでここでも書いていく

ターミナルでテーブルを作るとき
$ rails g model Post content :text
Postはモデル名 contentはカラム名 textはデータ型
textは長い文字列という意味、つまりcontentに入るのはtext(長い文字)ということになる。
「こんにちは」とか

これをターミナルで実行するとmodelとmigrateに2つのファイルが入る
1.migrateには「2020..._create_posts.rb」というものが作られている

ファイルを開くとこんな感じなのがある
dif change
create_table posts do |t|
t.text:content

t.timestamps

2つめはあることをしないと作成されない
rails db:migrate と書く。

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

アソシエーションの理解〜初心者の備忘録〜

アソシエーションの理解〜初心者の備忘録〜

1.アソシエーションとは?

 簡単に表現すると「各モデル同士を関連付ける」ということである。つまり、テーブル同士で関連付けておき、一方のモデルからもう一方のモデルにアクセスできるようにすることである。

例. 1人のuserが複数のコメントを所有するパターン(1対多の関係)
aso0.png
 →「Aさん」とAさんのコメントである「おはよう」、「こんばんは」を結びつける。
 →「Bさん」とBさんのコメントである「こんにちは」、「さようなら」を結びつける。

2.has_manyメソッドとbelongs_toメソッド

では、どのようにアソシエーションをコードで再現するのか示してみる。
上記の1人のuserが複数のコメントを所有するパターン(1対多の関係)で考えてみる。

(1) has_many

aso01.png

  ※上記の図はAさんAさんが所有するコメントの関係であるが、Bさんも上記の図のように考える。

#Userモデル  
class User < ApplicationRecord
 has_many :comments       #1人のuserが複数のcommentを所有するので複数形のcommentsになる。
end

(2) belongs_to

aso02.png
  ※上記の図は「おはよう」のコメントAさんに所有されている関係であるが、その他のコメントも上記の図のように考える。

#Commentモデル
class Comment < ApplicationRecord
 belongs_to :user      #1つcommentは1人のuserが所有するので単数形のuserになる。
end

3.さいごに

 アソシエーションの1対多における基本的な考え方を説明しました。今後も、多対多の関係など少し応用的な投稿にも継続的にしていきたいと思います。また、。誤り等ご指摘ありましたらコメントお願いします!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

アソシエーション(1対多の関係)

アソシエーション(1対多の関係)

1.アソシエーションとは?

 簡単に表現すると「各モデル同士を関連付ける」ということである。つまり、テーブル同士で関連付けておき、一方のモデルからもう一方のモデルにアクセスできるようにすることである。

例. 1人のuserが複数のコメントを所有するパターン(1対多の関係)
aso0.png
 →「Aさん」とAさんのコメントである「おはよう」、「こんばんは」を結びつける。
 →「Bさん」とBさんのコメントである「こんにちは」、「さようなら」を結びつける。

2.has_manyメソッドとbelongs_toメソッド

では、どのようにアソシエーションをコードで再現するのか示してみる。
上記の1人のuserが複数のコメントを所有するパターン(1対多の関係)で考えてみる。

(1) has_many

aso01.png

  ※上記の図はAさんAさんが所有するコメントの関係であるが、Bさんも上記の図のように考える。

#Userモデル  
class User < ApplicationRecord
 has_many :comments       #1人のuserが複数のcommentを所有するので複数形のcommentsになる。
end

(2) belongs_to

aso02.png
  ※上記の図は「おはよう」のコメントAさんに所有されている関係であるが、その他のコメントも上記の図のように考える。

#Commentモデル
class Comment < ApplicationRecord
 belongs_to :user      #1つcommentは1人のuserが所有するので単数形のuserになる。
end

3.さいごに

 アソシエーションの1対多における基本的な考え方を説明しました。今後も、多対多の関係など少し応用的な投稿にも継続的にしていきたいと思います。また、。誤り等ご指摘ありましたらコメントお願いします!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【RSpec】WebMockを利用して外部APIを利用したロジックのテストをする

【Ruby】faradayを利用した外部API連携クラスの作成手順の記事で、Faradayを利用した外部APIとの連携方法について紹介しました。

外部APIを利用したロジックのテストコードでは、実際のAPIを利用するのではなくHTTPリクエストをスタブ化するのが一般的です。
Railsではwebmockというgemを利用することでHTTPリクエストのスタブ化ができます。

今回はWebMockを利用して、外部APIと連携したロジックのテストコードを作成する方法について紹介します。

テストコードの対象となるクラス

今回はQiitaClientAPIというQiita APIと連携するクラスのテストコードを作成します。
クラスにはget_itemsという記事一覧をQiitaから取得するメソッドが実装されています。

具体的なソースコードは以下の通りです。

lib/qiita_api_client.rb
class QiitaApiClient
  class HTTPError < StandardError
    def initialize(message)
      super "connection failed: #{message}"
    end
  end

  class << self
    def connection
      Faraday::Connection.new('https://qiita.com') do |builder|
        builder.authorization :Bearer, "#{Rails.application.credentials.qiita[:token]}"
        builder.request  :url_encoded # リクエストパラメータを URL エンコードする
        builder.response :logger # レスポンスを標準出力する
        builder.adapter Faraday.default_adapter # アダプターの選択。デフォルトはNet::HTTP
        builder.response :json, :content_type => "application/json" # レスポンスボディをJSONパースする
      end
    end

    def get_items
      begin
        response = connection.get(
          '/api/v2/items'
        )
        response.body
      rescue Faraday::ConnectionFailed => e
        raise QiitaApiClient::HTTPError.new(e.message)
      end
    end
  end
end

下準備

テストコードを実行するための準備を行ます。

RSpecのセットアップ

今回はテスティングフレームワークにRSpecを利用します。
rspec-railsをGemfileに追加します。

Gemfile
group :development, :test do
  gem 'rspec-rails'
end

gemのインストールとRSpecのセットアップを行ます。

$ bundle install
$ rails generate rspec:install

webmockのインストール

webmockをGemfileに追加し、インストールします。

Gemfile
gem 'webmock'
$ bundle install

テストコードの実装

以下ではWebMockを利用したテストコードの記述方法について紹介します。

正常系のテスト

正常系のテストコードは以下の通りです。

spec/lib/qiita_api_client_spec.rb
require 'rails_helper'

describe 'QiitaApiClient' do
  before do
    WebMock.enable! # WebMockの有効化
    allow(Rails.application.credentials).to receive(:qiita).and_return({token: '123'})
  end
  describe '.get_items' do
    let(:response_body) { [{ "title": "test" }].to_json }
    before do
      WebMock.stub_request(:get, "https://qiita.com/api/v2/items").
        with(
          headers: {'Authorization' => "Bearer 123"}
        ).
        to_return(
          body: response_body,
          status: 200,
          headers: { 'Content-Type' => 'application/json' }
        )
    end
    it 'データが取得できること' do
      expect(QiitaApiClient.get_items).to eq(JSON.parse(response_body))
    end
  end
end

WebMock.enable!でWebMockを有効化し、WebMock.stub_requestの箇所でHTTPリクエストのスタブを作成しています。

WebMockを利用することで「どういったリクエストに対して、どういったレスポンスを返す」という挙動が記述できます。これにより、外部API経由でデータのやりとりを行う状況が擬似的に作成できます。

なおbeforeに記載されているallow(Rails.application.credentials)の箇所はアプリケーションコードに記載されているRails.application.credentials.qiita[:token]に対してダミー値をセットするためのものです。

異常系(例外発生)のテスト

アプリケーションコードでは外部APIと正常に通信できなかった場合、エラーメッセージを添えてQiitaApiClient::HTTPErrorという例外を発生させています。

例外が発生したことを確認する異常系のテストコードは以下の通りです。

spec/lib/qiita_api_client_spec.rb
require 'rails_helper'

describe 'QiitaApiClient' do

  before do
    WebMock.enable!
    allow(Rails.application.credentials).to receive(:qiita).and_return({token: '123'})
  end

  describe '.get_items' do
    let(:response_body) { { "message": "error_message", "type": "error_type" }.to_json }
    before do
      WebMock.stub_request(:get, "https://qiita.com/api/v2/items").
        to_raise(Faraday::ConnectionFailed.new("some error"))
    end
    it '例外が発生すること' do
      # 例外のテストはexpect()ではなくexpect{}なので注意
      expect{QiitaApiClient.get_items}.to raise_error(QiitaApiClient::HTTPError, "connection failed: some error")
    end
  end
end

WebMockのto_raiseで例外が発生するスタブを作成し、expectのraise_errorで例外が発生していることを確認しています。

最終的なテストコード

今回紹介した正常系と異常系をまとめた最終的なテストコードは以下のようになります。

spec/lib/qiita_api_client_spec.rb
require 'rails_helper'

describe 'QiitaApiClient' do
  let(:qiita_base_uri) { 'https://qiita.com/api/v2' }
  before do
    WebMock.enable!
    allow(Rails.application.credentials).to receive(:qiita).and_return({token: '123'})
  end

  describe '.get_items' do
    subject { QiitaApiClient.get_items }
    context '成功' do
      let(:response_body) { [{ "title": "test" }].to_json }
      before do
        WebMock.stub_request(:get, "#{qiita_base_uri}/items").
          with(
            headers: {'Authorization' => "Bearer 123"}
          ).
          to_return(
            body: response_body,
            status: 200,
            headers: { 'Content-Type' => 'application/json' }
          )
      end
      it 'データが取得できること' do
        expect(subject).to eq(JSON.parse(response_body))
      end
    end
    context '失敗' do
      before do
        WebMock.stub_request(:get, "#{qiita_base_uri}/items").
          to_raise(Faraday::ConnectionFailed.new("some error"))
      end
      it '例外が発生すること' do
        expect{subject}.to raise_error(QiitaApiClient::HTTPError, "connection failed: some error")
      end
    end
  end
end

まとめ

WebMockを利用して、外部APIと連携したロジックのテストコードを作成する方法について紹介します。

WebMockを利用したテストコードのフローは以下のようになります。

  1. Webmock.enable!でWebMockを有効化する
  2. WebMock.stub_requestでHTTPリクエストのスタブを作成する
  3. 外部APIを利用するメソッドを実行し、実行結果の検証を行う

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Webデザイン 色のイメージ

はじめに

現在オリジナルアプリを作成中です。トップページのフロント実装時に色合いの考え方を調べました。忘れても思い出せるよう書き記します。

開発環境
ruby 2.6.5
Rails 6.0.3.4

目次

1.色の効果
2.オリジナルアプリの配色

1.色の効果

Webデザインで使う色は目的別で選ぶ必要がある。Webサイトを利用するユーザー目線に立つことが重要である。

赤系

熱い、生命力、警告

オレンジ系

近親感、陽気

黄系

好奇心、協力

緑系

自然、リラックス

青系

冷たい、静けさ、誠実

紫系

後期、威厳

ピンク系

柔らかい、可愛い

茶色系

安定、信頼、歴史

2.オリジナルアプリの配色

オリジナルアプリは大きく分けて下記2つの機能を有する。トップページからそれぞれリンク先に遷移できるように実装した。
・ユーザーへの掃除提案
・掃除したデスク画像の共有

「ユーザーへの掃除提案」リンクを赤系にした。これは清掃を促す(警告)するためである。「掃除したデスク画像の共有」は青にした。これは清掃後のデスクを想定したため、清潔感をイメージする青を選択した。
image.png

参考
1冊ですべて身につくHTML-CSSとWebデザイン入門講座
Qiitaのマークダウンで色をつける方法[140色]

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsとClean Architectureに関する考察

Railsのアプリケーションの設計について考える機会があったのでその過程で考えたこととをまとめておきます。

背景

Railsをメインで使用している企業で新規のWebアプリケーションを0ベースで作ることになった。
その企業はRailsでかなり大規模なアプリを運用しており、Rails自体の知見は十分。

一方で伝統的なRailsでの開発手法に大規模開発の限界も感じており、この機会に設計のPrcaticeを模索したいという要望もあります。

筆者の方はアプリケーションの設計にはそれなりに自信がありますが、普段使用している言語はScalaやNodeJS(TypeScript)がメインです。

Railsでの開発は何度もやっており、特に開発に不自由することはありませんが、フレームワークの細かい癖などはあまり把握していません。

今回の要件では元々のチームのスキルを活かすために、Rails自体を別の言語やフレームワークに置き換えることは考えません。

Railsの良さを活かしつつモダンな設計手法を取り入れるにはどうすれば良いかを考察するのがこの文書の主目的になります。

基本設計方針

Clean Architectureを下敷きにします。

Clean Architecture(以下CA)はここ数年に急速に人気の出てきた設計手法で、日本でも日本語版の書籍発行をきっかけ多くのエンジニアがそれに言及するようになりました。

筆者自身も2、3、CAをベースとした開発を経験(自分で設計したものも、人の設計に乗っかって開発したものもあります)しており、このやり方はとてもイケてると感じています。

一方でCAを既存のWebアプリケーションフレームワークに載せる際の課題もいくつか感じており、そこに考察を加えて自分なりの勝ちパターンを見つけたい、と考えたこともこれをわざわざ文章化しようと思った動機の一つです。

ちなみに今の今、この節を書いている時点ではまだモヤモヤしているポイントがいくつかあるので、それらが文章化することによって整理されると良いなという願いもあります。

CAとRailsの相性の悪さ

さて、RailsにCAのエッセンスを適用とするにはどうすれば良いのか?という命題についてこうして考え始めたわけですが、いくらもしないうちにその相性の悪さに躓くことになります。

これはよく考えると当たりまえの話です。

何故なら、Clean Architectureは「使用するフレームワークに関わらず通用する普遍的な設計手法」を目指したものであり、一方でRailsなどのアプリケーションフレームワークは「フレームワークに依存させることによって開発コストを最小にする」ことを目指したものだからです。

つまり、最初から目指している方向が真逆なので相性が良いはずがありません。

特にRailsは「フレームワークに依存させることによって開発コストを最小にする」ことを極限まで追求したような化け物フレームワークなので相性は最悪と言えます。

CAではこの問題に対する解として、同心円の内側(EntityとUsecase)ではフレームワークの機能を使わないで実装すべし、としています。

実際これまで関わったプロジェクトでは(完全ではないにしても)そういうアプローチを取っていました。

Railsでも同じアプローチを取ることはできますが、そうするとRailsの良さを完全にスポイルすることになるので、それが良いとは正直言い切れません。

実際、Rails Wayでやれば3行で済むような処理がCAの作法にのっとると複数ファイルにまたがって数十行のコードが必要になったりすることもあるので、それがコストに見合っているかどうかは疑問です。

ていうか、自分ひとりで開発するのであれば間違いなくRails Wayを選択すると思うんですよね、この場合。。。。

おそらく世のRailsプログラマも同じように感じる人は多いはずです。
予想ですが完全にCA準拠したやりかたはRailsエンジニアには受け入れられない可能性が高い。

つまり、この場合Railsの良さを残しつつCAのエッセンスを取り入れる新しい方法を考える必要がある、というのがこの節の結論です。
(CA準拠のやり方を採用するのであればそれを納得させるだけの強い理由が必要と思いますが、ちょっと思いつきません。前述の通り目指しているところが違うので、各々の軸における優位性を並べることはできてもそれは一種の宗教戦争のようなもので、他方を納得させることができる気はしないです。また、個人的にもどちらか一方が正しくて他方が間違っているとは思いません。)

CA EntityとActiveRecord

RailsといえばActiveRecordです。
Railsの利便性はActiveRecordの存在によるところが大きく、ActiveRecordを使わないのであればRailsを使う意味がないと言っても過言ではありません。

CAに則るならEntityはActiveRecordとは無関係な形で再定義するべきですが、RailsにおいてはここでActiveRecordを排除するのは得策ではないと思っています。

動的型付け言語であるRubyではinterfaceは作れず、DuckTypingだよりでObjectを扱うだけなので、単純なモデルの場合は再定義したところで、結局ActiveRecordと交換可能なオブジェクトになる可能性が高い気がするんですよね。。。

メソッドに引数として渡すオブジェクトがCAのEntityであってもActiveRecordであっても動いて、かつそれを制限する方法もないのであれば、再定義が徒労感が伴うだけの苦痛な作業になってしまうことを危惧します。

であれば、この際単純なテーブルマッピングで済むようなEntityはActiveRecordのモデルをそのまま使うのもアリではないかと。

ただし、ActiveRecordに直接ビジネスロジックに関わるメソッドを定義するのが良いとは思わないので、そうしたメソッド定義が必要な場合は再定義します。

また、例えば請求書のようなDB上は複数のテーブルにまたがって保存されるけれどビジネス上のエンティティとしては不可分なものも再定義した方が良いと思っています。

再定義が必要なものとそうでないものの境界がイマイチあいまいですが、とりあえずは最初はActiveRecordを直接使っても良くて、必要を感じた時に再定義するというルールでスタートしてもまぁ良いかと思っています。(これは開始時点のプロジェクトメンバーが少ないことを踏まえての選択です。最初からメンバーの多いプロジェクトの場合はもう少し慎重に考えた方が良いとも思いますが、そんな大規模のプロジェクトとはそもそも縁がないのでこれ以上の考察はここではしません。)

再定義する場合も完全にActiveRecordを排除する必要はなく、ラップする形で定義すれば十分です。
例えばこんな感じ

# 請求書
class Bill

  initialize(ar_model)
    @ar_model = ar_model
  end

  def corp_name
    @ar_model.corp_name
  end

  # 請求明細
  def bill_details
    # Relationを表に出したくないのでto_aする
    # 必要ならコンストラクタで再定義したEntityにmapする用に修正する
    @ar_model.bill_details.to_a 
  end
end

問題点はActiveRecordに山盛りある副作用を伴うメソッド(saveとかupdateなど)が、どこからでも呼び出せてしまうことですが、これもとりあえずは「repository以外のファイルからは副作用のあるメソッドの使用を禁止する」をルール化していおけば十分な気がしています。(これも少人数ならではの選択です。実際にやるかどうかは別としてこのルールはLintでチェックすることも可能だと思います。)

正直ここはやってるうちに気が変わるかも、と思わないでもないですがとりあえずスタートはこんな感じで。(少人数以下略)

ディレクトリ構成

RailsではないプロジェクトでCA構成をやった時のディレクトリ構成は以下のような感じでした。

- domain
  - <ドメイン名1>
    - <Entityファイル>
    - <Entityファイル>
  - <ドメイン名2>
    - <Entityファイル>
    - <Entityファイル>
  - ...
- repository
  - <ドメイン名1>
    - <Repositoryファイル>
  - <ドメイン名2>
    - ...
- usecase
  - <ドメイン名1>
    - <Usecaseファイル>
    - <Usecaseファイル>
  - <ドメイン名2>
    - ...

ドメイン名のところには「User」とか「Order」のようなアプリケーションが扱う領域を大きく区切った名前が入ります。

Scalaのプロジェクトの場合はサブプロジェクトを使うことによって、domain層のファイルが絶対にusecaseやrepositoryに依存できないようにもできるのでこの構成には意義があります。

では、Railsの場合はどうか?

残念ながらRailsではそのような依存関係の制限はできないように思います。

問題点はもう一つあって、Railsではautoloadに沿った命名が強く推奨されているので上記構成の場合、Module名+Class名が

  • Domain1::Entity1
  • Domain1::Domain1Repository
  • Domain1::XXXXUsecase

のようにEntity, Repository, Usecaseのすべてが同じ名前空間になってしまいます。
これは非常によろしくない。

もう一段、階層をさげて、

  • Entity::Domain1::Entity1
  • Repository::Domain1::Domain1Repository
  • Usecase::Domain1::XXXXUsecase

としても良いですが、日本語話者的には主たる修飾が前に来る方が自然なので、

  • Domain1::Entity::Entity1
  • Domain1::Repository::Domain1Repository
  • Domain1::Usecase::XXXXUsecase

の方がしっくりきます。

なので、これを踏まえてディレクトリ構成は以下のようにしてはどうかと思っています。

- app
  - domain
    - <ドメイン名1>
      - entity
        - <Entityファイル>
      - repository
        - <Repositoryファイル>
      - usecase
        - <Usecaseファイル>
    - <ドメイン名2>

この方針を取ると仮定して、事前に思いついた問題点と回答は以下です。

「User」とか「Order」のようなありふれた名前がトップレベルのModule名になる。

扱う領域名がトップレベルに現れること自体はわかりやすいのでむしろ良いことだと思っています。

ただ、Railsでは慣習的にActiveRecordをmodels直下に定義することが多く、「User」や「Order」などは思いっきり名前がかぶりそうなので、ActiveRecord側を「AR::User」とするなど、module配下に置く必要があります。

ドメイン定義の方を「Domain::User::Entity::User」とする、という案もあるんですが、「DomainのUser」ではなく、「DomainがUser」なので、修飾としてしっくりこず冗長な気がしています。(個人的にはこうした日本語的にしっくりくる、みたいな感覚は大事にしています。外国人メンバがいる場合はまた別ですが。)

あと、ActiveRecordのモデルがそれに属することがわかる命名であることも優位があると思うので、可能であればActiveRecord側の命名を変える方が良いです。

ちなみに前節で「ActiveRecordを直接DomainのEntityとして使うのもアリ」と書きましたが、ActiveRecord自体は一覧性を重視して従来どおりmodels以下に置くつもりです。
(これを書いている時点ですでに、やっぱりDomain/Entityとして薄いラッパーを必ず定義するようにした方が良いのでは。。。と思い始めてますが、そこは作りながら考えます。)

Usecaseは複数のドメインオブジェクトをまたがって扱うことがある

そうですね。別にいいんじゃないでしょうか。

EntityとRepositoryはそのドメイン内で完結するべきと思いますが、Usecaseは複数ドメインを扱っても特に問題はないと思います。

ファイルの置き場所としてしっくりこないなら、EntityやRepsitoryの無い別ドメインを定義してUsecaseだけを置いても良いです。

蛇足ですが、Repositoryでは同じドメインのEntityモジュールをincludeしても良いと思っています。
ですが、Usecaseはダメです。(というか、コンストラクタで各種Repositoryを渡すならばModule名修飾の名前が出てくる場面はほぼ無いとも思います。)

簡便化

以下、CAの教科書的ではないですが開発速度をあげるためにやろうと思っている内容です。

IDでのEntity取得はドメインModuleのmodule functionにして良い

こんな感じ

module Order extend self

  order_repository
    @order_repository ||= Order::Repository::OrderRepository.new
  end

  module_function
  def get_order_by_id(order_id)
    order_repository.get(order_id)
  end
end

ID指定でのEntity取得は、デバッグの際にも使用したい場面があるので必要ならそのショートカットを作っても良いというルールです。(すべてのEntityに対して必ず作るということではありません。)

routesのGET /XXXXs/:idに相当する処理が、これで間に合うのであればUsecaseを定義する必要すらないと思いますが、実際にはほとんどの場合Permissionのチェック(上記Orderの場合は自分のOrder以外は不可視)が必要になると思うので、その場合はUsecaseが必要です。

usecaseをドメインModuleのmodule functionとして定義する

一般にUsecaseはそのコンストラクタで各種Repositoryを引数に取ります。
これはテスト時にRepositoryを差し替えられないとテストが書きにくいからです。

しかし、ControllerからUsecaseを使う際に毎回Repositoryを明示するのは面倒なので、ドメインModuleにそのショートカットを作ります。

こんな感じ

module Order extend self

  order_repository
    @order_repository ||= Order::Repository::OrderRepository.new
  end

  module_function
  def create_order_usecase
    Order::Usecase::CreateOrder.new(
      order_repository
    )
  end
end

使う側は

  val res = Order.create_order_usecase.run(...)

となって、やりたい内容が明快です。

Usecase側でデフォルト引数を指定するでも良いと思いますが、そこはまぁ好みの問題です。

テスト

正直Railsでのテストの知見はあまりなく、手探り状態です。
また、工数的にもフルでテストを書く余裕はなく、必要なところから部分的に書いていく、という形になると思っています。

なので基本的には、

  • ロジックのあるEntity
  • Usecase

を中心にテストが書ける形になってさえいれば、実際にテストを書くのは後回しになっても良いと割り切ります。
Repositoryが単純なActiveRecordのラッパーであるならそのテストは優先度最下位で良いです。
(外部サービスに依存するものであれば別途テストやMockが必要です。)

Entityのテストは特に問題になるところはないでしょう。
Usecaseの方は物によってはテストを書くのは面倒なケースがありますが、一般にUsecaseは

  • validate - 入力パラメータのValidation
  • collect - 必要なEntityをRepositoryから収集
  • execute - Usecaseで行いたい処理の実行

の3ステップからなることが多く、テストしたいのはexecuteの部分だけだったりもするので必要に応じてexecuteの部分だけテストできるようになっていれば良いと思います。

こんな感じ

class Domain1::Usecase::HogeHogeUsecase

  initialize(domain1_repository)
    @domain1_repository = domain1_repository
  end

  def run(params)
    error = validate(params)
    return ErrorCase.new(error) if error

    [v1, v2, error] = collect(params)
    return ErrorCase.new(error) if error

    return execute(v1, v2)
  end

  def validate(params)
    ...
  end

  def collect(params)
    ...
  end

  def execute(v1, v2)
    ...
  end
end

一応これで永続化されたデータの状態を気にせずテストを書けるようになるはずです。

まとめ

以上、開発初期に考えたことをまとめてみました。
文章化したことによって自分の中での整理が進んだことが最大の収穫ですかね。

自分のRails力は決して高くはないし、賛否の分かれる部分もあると思います。
でも、まぁそんなのは割とどうでも良いです。現場での設計は万人受けする必要はないのです。

ただチームのメンバにこのプロジェクトは「こういう方針で作っている」という思想が共有され、一貫性が保たれていればそれで十分だと思います。

正解を求めるのは学問の分野の仕事で、現場のエンジニアがそれを求めてしまうとドツボにはまるだけだとも思います。

すべてを肯定的に捉えることは無理でも、何かしら参考になる点があれば幸いです。

作っている過程で、何か変える部分がでてくればまた追記します。
(何故なら、この文書こそがチームメンバに「こういう方針で作っている」を共有するためのドキュメントそのものだからです。)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Git/GitHub 共同開発】知っておきたいコンフリクト解消とコードレビューの基本

概要

現在所属中のオンラインサロン「転職クエスト」にて、Railsアプリ共同開発を行っています。

恐れながらファシリテーターを務めさせてもらっているのですが、開発をすすめる上で全員のネックになっていたことがありました。

Git/GitHubの共同開発的な使い方がわからない。。。

なので、開発を進めるファシリテーターとして、Git/GitHubをこうやって使おうぜ!というマニュアルを用意しておこうと考えました。

本記事では共同開発 初心者さんたちに向けたGit/GitHubのフローについて紹介します!

想定読者
1. アプリの個人開発のためにGit/GitHubを使ったことはある
2. コンフリクト解消のやり方を知りたい!
3. コードレビューのやり方を知りたい!


そもそも、どこがわかっていない?

Git/GitHubを使って共同開発をする上で、ネックになる部分はどこか。
考えるために、これまで行ってきたGit/GitHubの使い方を振り返ってみます。

個人開発でのGit/GitHubの使い方
1. ローカルのmasterでブランチ切る
2. ブランチで作業しコミット
3. リモートにブランチをプッシュして、プルリク作成
4. GitHub上でひとりLGTMを出して、ブランチをmasterにマージ
5. リモートのmasterをローカルのmasterにプル
6. 1~5を繰り返す

さて、このフローでしか開発をしてこなかった人が、共同開発でGit/GitHubを使う際に、初めて経験するのはどんなことでしょうか?

僕は、次のことだと考えました。

初めて共同開発をする人がやったことがないこと
1. 他の人のプルリクに対するコードレビュー
2. リモートのmasterの変更分を、ローカルの作業中ブランチにマージ
3. その際に起きるコンフリクト解消

特に2, 3については、最新に更新されたmasterからしかブランチを切ったことがないというのが不安の理由として挙げられます。
共同開発では、ブランチのマージによって常にmasterが更新されていきますので、その更新分を自分の作業中ブランチに反映する必要があります。

ここに焦点を当てて、共同開発でのコンフリクト解消、コードレビューの手順を紹介していきます!


コンフリクト解消の例

場面設定

設定
masterから同じタイミングで2つのブランチを切る
  user1.branch1 : git checkout -b new,createアクションを実装
  user2.branch2 : git checkout -b edit,updateアクションを実装

ちなみに今回は「ルーティングの設定」のみを取り扱っていきますのであしからず。


1. [user1] branch1でnew,createのルーティングを設定

branch1→routes.rb
+ resources :tweets, only: [:new, :create]


2. [user1] ローカルでコミット→リモートにブランチをプッシュ

branch1(new,createアクションを実装)
% git add .
% git commit -m "ルーティングにnew,createを追加"
% git push origin new,createアクションを実装


3. [user1] GitHubでプルリク作成→LGTM→マージ

ここは省略します。


4. [user2] branch2でedit,updateのルーティングを設定

branch2→routes.rb
+ resources :tweets, only: [:edit, :update]


5. [user2] branch2でコミット

branch2(edit,updateアクションを定義)
% git add .
% git commit -m "ルーティングにedit,updateを追加"

※次にmasterブランチに切り替えますが、その前に必ずここでコミットをしてください!


6. [user2] リモートmaster(branch1 merged)→ローカルmasterにプル

# masterブランチに移動
% git checkout master

# リモートのmasterをローカルのmasterに反映
% git pull origin master

【今はこういう状態】

  • master → new, createアクション
  • branch2 → edit, updateアクション demo


7. [user2] ⚠コンフリクト ローカルmaster→branch2にマージ

# 作業中ブランチに移動
% git checkout edit,updateアクションを定義

# ローカルのmasterを作業中ブランチにマージ
% git merge master

【コンフリクト発生の様子】

  • Head : 現在、作業中のブランチ (branch2)
  • master : マージしたローカルのmasterブランチ

demo


8-1. [user2] コンフリクトの解消

masterを基準に修正し、コンフリクト解消したらコミット
# 編集後
% git add .
% git commit
(デフォルト名 : Merge branch 'master' into edit,updateアクションを定義)

基本的に、masterと作業中ブランチのログをどちらも残し、必要な形に変えればそれでOKです。
コミットするとデフォルトでこのブランチがマージされたよ!というコミット名になります。

【※コンフリクト解消後のGitHub Desktopの画面】
▶commit Mergeを押せば、デフォルトのコミット名でコミットされる...はず
demo


8-2. [user2] わからないのでとりあえずmergeを取り消したい!

もし「コンフリクトこわい! 一旦戻したい!」という方にはこちら。

merge取り消し方法
% git merge --abort

【merge取り消しの様子】
▶コンフリクト状態がリセットされます
demo


9. [user2] branch2をリモートにプッシュ→プルリク

% git push origin edit,updateアクションを定義


小括 : コンフリクト解消

  • ローカルの作業中ブランチで最新の変更をコミット
  • 誰かがブランチをマージしたリモートのmasterをローカルのmasterにプル
  • ブランチを切り替え、masterをマージ
  • 落ち着いてコンフリクト解消→修正したらコミット

続いて、コードレビューの仕方についてです。

branch2「edit,updateアクションを定義」のプルリクに対してコードレビューをしましょう!


コードレビューの例

1. [レビュアー] プルリクURL→File changedタブ

ここで、ファイルの変更差分について確認します。
demo


2. [レビュアー] 変更希望箇所をクリック(複数行を選択も可能)

直してほしい部分を選択して、コメントをします。
demo


3. [レビュアー] コメントをする→Start a review

選択した部分についてのコメントを記載して下さい。


4. [レビュアー] 複数ファイルにコメント→Finish your review

そのプルリクで気になった箇所にコメントを残し終えたら、Finish your reviewを押して下さい。
demo

  • conversationsに戻るとレビューが反映されています。
  • これに従って、プルリク依頼者はローカルで編集 → push → 再レビュー依頼 demo

▶今回、レビュアー側として「showアクションを追加して下さい」とコメントをしてみました。これに沿って「プルリク依頼者側」での対応をしてみましょう。


5. [依頼者側] ローカルで編集してpush

routes.rb
#showを追加
+ resources :tweets, only: [:new, :create, :show, :edit, :update]
local-branch2
% git add .
% git commit -m "showアクションを追加"
% git push origin edit,updateアクションを定義


6.[依頼者側] 修正報告

レビューに対して「showアクションを追加しました!」とコメントしましょう。
demo


7. [レビュアー] LGTM→Resolve conversation(レビュー完了)

  • File changedを確認
  • OKなら(コメントした後に)Resolve conversation demo


8. 全部でLGTMならマージ

以上のフローを繰り返し、LGTMをもらえたらマージしましょう!


9.ローカルのmasterにプル

% git checkout master
% git pull origin master

masterの変更分 (mergeした部分) が次のように反映されます。
demo


小括 : コードレビュー

  • プルリクのFile Changedで変更差分を確認
  • 必要に応じてコメント
  • コメントに対してローカルブランチで作業し再レビュー
  • LGTMならマージ

以上が、GitHub flowによるコードレビューの基本手順です。


まとめ

  • Git/GitHubを共同開発で使うときは、最新masterを作業中ブランチにマージする場面がくる
  • 変わった部分を冷静に判断し、コンフリクトを解消
  • プルリクに対してはファイルの変更差分を確認し、レビューを行う

個人とは違う部分が多く、例を出して説明するのに苦労しました。
しかし、この機会に学んでみるとGitってすごいな!と改めて思いました。

引き続き、共同開発がスムーズに進むよう、学習を進めていきたいです!


参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

この問題地味に難しい...(Ruby)

0から9までのカウント(Ruby編)

問題

0から9までを数えるカウンタを考えます。

0 → 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 → 9

このカウンタの拡張として、任意の数から始められるカウンタを考えました。

2 → 3 → 4 → 5 → 6 → 7 → 8 → 9 → 0 → 1
(2から始める例、カウンタは9まで進んだら0に戻ります)

入力から数字nを受け取り、nからカウンタをスタートさせて、

カウンタの値を順番に10個出力するプログラムを実装してください。

入力される値

入力は以下のフォーマットで与えられます

n

・nはカウンタの最初の値

期待する出力

nから始まるカウンタの10個の値を、順番に改行区切りで出力してください。

入力例1

0

出力例1

0
1
2
3
4
5
6
7
8
9

入力例2

2

出力例2

2
3
4
5
6
7
8
9
0
1

私の回答(結局解けなかった)

n = 2
# nが10まで進んだら0にリセットするそれ以外はパスして10個出力
for i in n..9 do
if n == 9
   n = 0
   puts i
   n += 1
end
end

色々弄りすぎて多分2時間くらいやってたので最終的には諦めたコードになります笑

なんせ 10にいこうとしたら0に戻ってリスタートさせる的なコードが必要なのですが、そのコードを書こうともがいたのが n ==9 の部分です。こういうところがプログラミング脳が足りてないところなんですよね。。。そしてなんと言ってもググる際の言語化がこういうときかなり難しい。

以上!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails 正規表現での^,$

郵便番号を正規表現でチェックする

エラーが出た実装

user.rb
class User < ActiveRecord::Base
  validates :zip_code, format: { with: /^\d{3}\-?\d{4}$/ }
end

これでアプリケーションを実行すると以下のエラーが出ました。

The provided regular expression is using multiline anchors (^ or $), which may present a security risk. Did you mean to use \A and \z, or forgot to add the :multiline => true option?

セキュリティリスクがあるので

  • 行頭の ^\A
  • 行末の $\z

を使え、と言われた。

修正した実装

user.rb
class User < ActiveRecord::Base
  validates :zip_code, format: { with: /\A\d{3}\-?\d{4}\z/ }
end

これでエラーは出なくなりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EC2に自動デプロイ後、変更が反映されていないときの対処法

内容としてはタイトルにある通りですが、EC2に自動デプロイを行った際に変更が反映されておらず、めちゃめちゃ焦りました。
今後同じことがあったときに焦ることがないよう、備忘録として残しておきます。

環境

AWS EC2
Ruby 2.6.5
Rails 6.0.3.3
capistrano

結論

EC2インスタンスを再起動する。
自動デプロイを複数回行うと変更が反映されないことがあるためその際はインスタンスの再起動を行います。
再起動手順についてはこちらの記事にわかりやすく解説されています。

最後に

インスタンスの再起動を行った際はデータベースとNginxの再起動も忘れずに行うこと!

1.EC2インスタンスにログイン

ターミナル
hoge@MacBook ~ % cd .ssh  
hoge@MacBook .ssh % ssh -i ダウンロードした鍵の名前.pem ec2-user@該当EC2インスタンスと紐付けたElastic IP

2.データーベース(今回はmariaDB)とNginxの再起動を行う

ターミナル
[ec2-user@ip-173-41-45-198 ~]$ sudo systemctl restart mariadb 
[ec2-user@ip-173-41-45-198 ~]$ sudo systemctl restart nginx
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby On Rails】ActionView::Template::Error (ActiveStorage::InvariableError)の原因と対処法(仮説・検証)

備忘録です。
ActionView::Template::Error (ActiveStorage::InvariableError)の原因と、その対処法について仮説・検証しました。

エラー背景・経緯

デプロイしたアプリの動作確認を行うことにしました。今回は画像がアップロードされているかどうかを、簡易的に手動で確認したいため、単体テストや結合テストを行わずに動作確認を行うことにしました。

今回は周りの友達に動作確認テストの協力をしてもらいました。
協力内容としては、画像を投稿できるフォームにファイルを添付して、投稿したファイルがビューに表示できるかどうかを確認して欲しいと依頼しました。

エラー内容

協力者である友達から、画像ファイルを添付したと報告をもらったので、デプロイしたアプリの詳細画面を確認しました。すると、"We're sorry but something went wrong"と表示されてしまいました。

まずは、ターミナルでログとそのエラー内容を確認しました。(今回はheroku)

% heroku logs --tail

(中略)

ActionView::Template::Error (ActiveStorage::InvariableError)
    34: <div>
    35:   画像
    36: </div>
    37: <%= image_tag @question.image.variant(resize: '200x200'), class: 'question-image' if @question.image.attached? %>
    38: <% end %>
    39:
    40: <%= render 'shared/error_messages', model: @comment %>
app/views/questions/show.html.erb:37

調べたこと

エラー内容に関する記事を調べてみました。このエラーを解決する鍵は「variable?メソッドを使って、blobが変数であるかどうかを区別する」とのことでした。
ここで言う変数は添付される画像ということになりますが、正しい変数(画像)が与えられているかが鍵となることがわかりました。

「そもそもvariable?とはなんぞや」

ということでネットで調べてみると、variable?を使ってTrueの時のみ画像を呼び出せる様にする時に使うとのこと。そして、ActiveStorage.variable_content_typesメソッドと組み合わせることで、指定した拡張子のみの画像を添付できる様に制限をかけることがわかりました。

仮説

そもそも、友達が投稿したファイルは画像ファイルではない、もしくはactive_storageがサポートしている拡張子の画像ではない。という仮説を立てました。

行ったこと

友達に画像以外を載せた可能性を聞いてみました。すると、「画像も他の拡張子のファイルも混在しているファイルの中からテキトーに確認せずに添付した」と回答が返ってきました。つまり、画像以外が添付されている可能性大です。

友達から聞いた内容と、自分で立てた仮説をもとに、自分で誤った拡張子のファイルを添付し保存ができるかどうか、詳細ページはエラーになるかどうかを試しました。

結果

友達の時と同様に、全く関係のない拡張子でデータの保存ができました。そして、詳細ページに遷移すると同様に"We're sorry but something went wrong"という表示になりました。

つまり「正しい拡張子のファイルが送信されていなかった」ということがわかりました。

今後は特定の拡張子以外の保存ははじけるように、ActiveStorage.variable_content_typesメソッド等を使用して、今回の様なエラーが起きない様にします。

補足

テストをしていく段階で、画像の拡張子が.heicだとアップロードできない場合がありました(できる場合もありましたが、原因は不明です)。
参考記事2番目の記事に記載されている通りですが、現在は.heic拡張子のファイルのサポートはしていないみたいですので、避けた方が吉 or heicだけをはじく記述をした方が良いかもしれないですね。

英語の記事読むのはかなり重要ですね

参考記事

https://edgeapi.rubyonrails.org/classes/ActiveStorage/InvariableError.html
https://stackoverflow.com/questions/60686249/activestorageinvariableerror-in-homeindex
https://qiita.com/xusaku_/items/36a61e35b6cd863bbf9d

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

日本時刻設定と表示方法まとめ

はじめに

自分用の備忘録
忘れがちなのでまとめてみる

手順

日本時刻設定

アプリケーションファイル編集

config/application.rb
# 中略

module App
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 6.0 

    # 日本語の言語設定
    config.i18n.default_locale = :ja
    # 日本時刻の設定
    config.time_zone = 'Tokyo'

    # 中略

時刻のフォーマットを設定

config/locales/ja.yml => 作成
config/locales/ja.yml
ja:
  time:
    formats:
      default: "%Y/%m/%d %H:%M:%S"
      # あくまでデフォルトなのでよく使いそうなパターンで設定

時刻の表示

l(エル)メソッド

sample.rb
l sample.created_at => デフォルトの設定で表示

表示方法を指定

sample.rb
sample.created_at.strftime('20%y年%m月') => "20xx年yy月"

おわりに

l(エル)メソッドって響きがかっこいいと思うのは私だけでしょうか

✔︎

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby on Railsでのテーブル作成→レコード作成、削除→テーブル削除の一連の流れ

はじめに

Ruby on RailsのDB操作に慣れるための覚え書きも兼ねた完結型の筋トレセットです。

テーブル削除の手順などは自分も含めた初心者にとって少し複雑だと思いますので、繰り返し練習などでお役に立てば幸いです。

すでにお使いのRailsアプリにデータベースがインストールされていることを前提にしているので、データベース作成がまだの方は「rails db:create」でデータベースをご用意ください。

環境

Ruby 2.7.2
Rails 6.0.3

テーブルの新規作成

まず、ターミナルからRailsアプリのあるディレクトリに移動する。

console
> cd (フォルダのアドレス)

モデル作成のためのマイグレーションファイルを作成。
(例:モデル名「User」、フィールド名「name」、「address」、「age」)

console
> rails g model User name:text address:text age:integer

この時点で「User」モデルを定義したクラスファイルとマイグレーションファイルが作成されますが、テーブルは生成されません。

テーブルの生成にはマイグレーションを行う必要があります。

console
> rails db:migrate

これで「User」モデルを基にした「users」テーブルが作成されます。

※テーブル名がモデル名と違うのはrailsの命名規則に拠る
参考:「RailsのDBモデルの命名規則をまとめてみた」https://qiita.com/seri1234/items/8ca4b52d82390929195f

レコード追加~削除まで

Railsコンソールを起動します。

Railsコンソール
> rails console

新しい行にレコードを追加します。

Railsコンソール
irb(main):001:0> user = User.new
irb(main):002:0> user.name = "John Smith"
irb(main):003:0> user.save

レコードが追加されていることを確認します。

Railsコンソール
irb(main):004:0> users = User.all

確認ができたら、作成したレコードを削除します。
(find()の引数はレコードのインデックス)

Railsコンソール
irb(main):005:0> User.find(1).destroy

レコードを削除したら、Railsコンソールを終了してターミナルに戻ります。

Railsコンソール
irb(main):006:0> exit

モデルの削除からテーブルの削除まで

まず、モデル作成時に作られたファイルを削除します。

console
> rails destroy model User

これにより「User」モデルが削除されます。

この時点ではまだ「users」テーブルが残っていることに注意して下さい。

次にテーブル削除用マイグレーションファイルを作成します。
(「drop_table_users」は任意の名前でOK)

console
> rails generate migration drop_table_users

「アプリ名/db/migrate/」以下に先頭に数字が並んだ「~drop_table_users.rb」というマイグレーションファイルが作られます。

このファイルをテキストエディタで開き、「def change」内にテーブルを削除するための命令を追加して上書き保存する。

1**********9drop_table_users.rb
class DropTableUsers < ActiveRecord::Migration[6.0]
  def change
    # 以下1行を追加
    drop_table :users
  end
end

マイグレーションを実行します。

console
> rails db:migrate

テーブルが削除され、これで一連の流れが完了となります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby編]等差数列

[Ruby編]等差数列を解いてみる

問題

以下のような数列を出力するプログラムを作りましょう。

5 8 11 14 17 20 23 26 29 32

最初の数字が5で、3ずつ増加していく事がわかります。
一般的にこれを初項5、公差3の等差数列といいます。

このような数列を出力するプログラムを作りましょう。
初項 m が与えられ、公差 n が与えられるので 10番目までの数字をスペース区切りで出力するプログラムを作成してください。

入力される値

入力は以下のような初項 m、公差 nが半角スペース区切りのフォーマットで与えられます。

入力値最終行の末尾に改行が1つ入ります。

m n

期待する出力

初項 m ,公差 n の等差数列を1〜10番目までスペース区切りで出力して下さい。

入力例1

3 3

出力例1

3 6 9 12 15 18 21 24 27 30

入力例2

5 10

出力例2

5 15 25 35 45 55 65 75 85 95

入力例3

1 3

出力例3

1 4 7 10 13 16 19 22 25 28

私の答え

num = gets.chomp.split(" ").map(&:to_i)
x = num[0]
i = 1
array = []
while i <= 10
  array << x
  x = x + num[1]
  i += 1
end
print array.join(" ")

今回のポイント

1行目の入力値はスペース区切りがあるので split と map で[3, 3]のような配列にする

2行目で先ほどの配列の一部を指定して x変数に代入しておく

3行目で i = 1 としておく事で1回ずつカウントさせる

4行目でスコープ外でもarrayを使えるようにし、また繰り返し結果を配列に格納する

5~9行目で i が10回を超えるまで繰り返される。 array[] に x = num[0]を配列演算子 << を使って連結している。while文の場合、 i += 1 の自己代入演算子を忘れてはいけない

最終行でjoinメソッドを用いて (" ") とする事で配列間にスペースを開けられる

以上!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

downcase,sliceメソッドの応用

「a=abc」,「b=HiAbc」という二つの変数がある時、a,bいずれかの文字列がもう片方の文字列の最後にある場合にTrueを返すプログラムの作成方法です。
今回の場合、aのabcがbの後ろ三文字に存在するためTrueとなります。

以下二つのメソッドを使用することで作成することができます。

メソッドの使用例

  • 大文字を小文字に変換するためにはdowncaseメソッド
ruby
a = "AbC"
a_down = a.downcase
// これにより a_down には abc が入る
  • 特定の位置にある文字列などを切り抜きたい場合はsliceメソッド
ruby
a = "AbC"
a_slice = a.slice(1)
// これにより a_slice には b が入る

回答例

ruby
def compare(a, b)
  a_down = a.downcase
  b_down = b.downcase
  a_len = a_down.length
  b_len = b_down.length
  if b_down.slice(-(a_len)..- 1) == a_down || a_down.slice(-(b_len)..- 1) == b_down
    puts "True"
  else
    puts "False"
  end
end

解説

2~5行目でこの後に使用する変数の定義をしています。

ruby
a_down = a.downcase    // aの文字列を全て小文字化してa_downに代入
b_down = b.downcase    // bの文字列を全て小文字化してa_downに代入
a_len = a_down.length  // a_downの文字列の長さをa_lenに代入
b_len = b_down.length  // a_downの文字列の長さをa_lenに代入

その後、以下のif文で判定しています。

ruby
if b_down.slice(-(a_len)..- 1) == a_down || a_down.slice(-(b_len)..- 1) == b_down
  puts "True"
else
  puts "False"
end

「b_down.slice(-(a_len)..-1 == a_down」を日本語訳すると、「b_downの右から1〜a_len部分を切り取る」となります。
仮にa_lenが「3」、b_downが「hiabc」という5文字だったとして、「hiabcの右から1〜3文字を切り取る」となります。すると切り取った文字列は「abc」となります。
sliceメソッドは正の値を渡すと先頭から計算し、負の値を渡すと末尾から計算しますので、これを覚えておいてください。

b_downの場合とa_downの条件を「または」という演算子を使っていることで、どちらの変数からも確認できるようにしています。(正直最初見たときは訳せなくて落ち込みました。)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]redirect_toとrenderの違いについて[初心者]

はじめに

いきなりですが、redirect_torenderって、初めて見たときは、両者の違いがよく分からなかったですよね。
深掘りして、一緒に学んでいきましょう!

redirect_toとrenderの違い

redirect_to: HTTPリクエストをサーバーに送り、ユーザーはそこから返ってくるHTMLが表示される。
render: アクションの中で、呼び出すViewファイルを指定するメソッド。

言葉だけだと、分かりづらい部分があると思うので、それぞれの処理の流れについて解説します。

それぞれの処理の流れについて

redirect_toの処理の流れ
controllerの処理でredirect_toを実行
redirect_toの引数で指定したURLHTTPリクエスト(GET!!)を実行
HTTPリクエスト(GET!!)されたURLに対応するルーティング処理を実行
④ルーティング処理に対応したcontroller、アクションが呼び出され処理を実行
⑤処理に応じたviewのレンダリングを実行

①controller(redirect_to) → ②HTTPリクエスト(GET!!) → ③ルーティング → ④controller → ⑤view

renderの処理の流れ
controllerの処理でrenderを実行
renderのオプションで指定したviewファイルを表示する

①controller(render) → ②view

ここで注意が必要なのは、redirect_toの指定先は、必ずGETメソッドのルーティングになるということです。

redirect_toやrenderの指定がない場合

たまに見かけますよね。
下の状態のようなコントローラーを。

controllers/books.rb
class BooksController < ApplicationController
  def index
  end
end

アクション内に、何も記載がない場合は、自動的にrenderが実行されます!!
つまり、上の場合だと、render 'index'が省略されているものと考えられ、このアクションからviews/books/index.html.erbが呼び出されます。

よく見る記載方法

それではここで、よく使われているredirect_toとrenderの記述例について見てみましょう!

controllers/books.rb
class BooksController < ApplicationController
  def create
    @book = Book.new(book_params)
    @book.user_id = current_user.id
    if @book.save
      redirect_to books_path, flash[:notice] = "投稿成功!!"
    else
      flash.now[:alert] = "投稿失敗!!"
      render 'new'
    end
  end
end

投稿を行う際に、saveメソッドの結果がtrueならば投稿一覧ページにリダイレクトし、falseなら新規投稿用のページを再表示するように設定しています。
render 'アクション名'で同じコントローラの別アクションのViewを表示できます。
 コントローラ名を指定することで、他のコントローラのViewも表示できます。

なぜfalseの場合はrenderを使用するのか

renderを利用して、viewファイルを表示する際に、アクションを呼び出していないことにお気づきでしょうか。
↑のrender 'new'は、books/new.html.erbを表示させているだけであり、newアクションを経由しているわけではないのです。

今回のパターンだと、投稿に失敗しているからnewページを表示させたいのですが、redirect_toを使用してしまうと、newアクションを呼び込み、インスタンス変数に空のメソッドを渡してしまうことで、newアクションがリセットされた状態になるので、ユーザーが打ち込んだデータが消えます。
よって、エラーメッセージを打ち出す為の情報も消えてしまう為、ユーザーは「なぜ投稿できなかったのか」を窺い知ることができなくなります。

ややこしいですが、大事な分岐点を決めるものなので、腹落ちするまで向き合ってもらえればと思います。

(補足)render時のフラッシュメッセージについて

本筋とは離れてしまうので、詳細は述べませんが、表示したビュー内にメッセージを出したい場合、以下のように記述します。

redirect_toの場合
flash[:notice] = "メッセージ"
 または
notice: "メッセージ"
renderの場合
flash.now[:alert] = "メッセージ"  #nowがついている!!!!

おわりに

初心者の方が間違いやすいredirect_toとrenderの違いについて説明しました!!
解説している中で、自分自身勉強になることが多く、アウトプットの重要性を痛感しています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails6 チェックボックスにチェックが付いている値を配列にしたい

フォームのチェックボックスにチェックが付いている値を取得して配列にする。

ActionController::Parameters {"authenticity_token"=>"DoVsq/+dLxgEzm0kXJUo4SWmHx5CBR1GmeV+yNaAeYcDmyMg8nWcMDNSWeMY**************==", "hogehoge"=><ActionController::Parameters {"aaa"=>"0", "bbb"=>"1", "ccc"=>"1", "ddd"=>"1",} permitted: false>, "commit"=>"検索", "controller"=>"distributions", "action"=>"search"} permitted: false>

こんな感じでフォームがくるので

params[:hogehoge].permit!.select { |key, value| value == "1" }.keys
["bbb","ccc", "ddd"]

として取得できました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GoogleMapに複数のマーカーを表示する

GoogleMapに対象店舗と周辺店舗を表示させたいと思ったため調べたことを記録します。
当記事は、主にJavascriptの記述についての記載になるためGoogleMapのAPIキー取得等に関する記述は省略します。
条件としては以下のとおりです。

・地図の中心に対象店舗が表示される。(マーカーを立てる)
・対象店舗を中心として半径5km以内にある店舗にもマーカーを表示する。
・対象店舗と周辺店舗はマーカーの色を分ける。
・周辺店舗のマーカーをクリックしたときには吹き出しが表示される。
・吹き出しには店舗名が入っており、店名をクリックすると該当店舗の詳細ページに遷移する。

Gif.gif

開発環境

ruby 2.6.5
Ruby on Rails 6.0.3.3
OS: macOS Catalina

はじめに

GoogleMapApiの使い方に関してこちらの記事がわかりやすくまとめてあり参考にさせていただきました。
Google Maps APIの使い方

実装コード

コードは以下のとおりです。

  let map;
  let mainMarker;
  let marker =[];
  let infoWindow = [];

function initMap(){
  let markerData = gon.places;
  let latlng = {lat: gon.latitude, lng: gon.longitude};
  // 初期位置の指定
  map = new google.maps.Map(document.getElementById('map'), {
  center: latlng,
  zoom: 14
  });

  // 初期位置にマーカーを立てる
  mainMarker = new google.maps.Marker({
    map: map,
    position: latlng
  });

  // 近隣店舗にマーカーを立てる
  for (var i = 0; i < markerData.length; i++) {
    const image = "https://maps.google.com/mapfiles/ms/icons/yellow-dot.png";
    const id = markerData[i]['id'];
    // 緯度経度のデータを作成
    let markerLatLng = new google.maps.LatLng({lat: markerData[i]['latitude'], lng: markerData[i]['longitude']});
    // マーカーの追加
    marker[i] = new google.maps.Marker({
      position: markerLatLng,
      map: map,
      icon: image,
    });

    // 吹き出しの追加
    infoWindow[i] = new google.maps.InfoWindow({
    // 吹き出しに店舗詳細ページへのリンクを追加
    content: `<a href=/laundries/${id}>${markerData[i]['name']}</a>`
    });

    markerEvent(i); 
  }

  // マーカークリック時に吹き出しを表示する
  function markerEvent(i) {
    marker[i].addListener('click', function() {
      infoWindow[i].open(map, marker[i]);
    });
  }
}

document.addEventListener('turbolinks:load', function(){
  initMap();
});

1つずつ見ていきます。
1.変数の定義

 let map;
 let mainMarker;
 let marker =[];
 let infoWindow = [];

対象店舗と周辺店舗のマーカーを分けたかったため2つ変数を用意しました。markerには周辺店舗の情報が複数入るため配列にしています。infoWindowも同様に、各店舗のマーカーに対して吹き出しを付けたいため配列にしています。

2.地図の作成〜マーカーを立てる

  let markerData = gon.places;
  let latlng = {lat: gon.latitude, lng: gon.longitude};
  // 初期位置の指定
  map = new google.maps.Map(document.getElementById('map'), {
  center: latlng,
  zoom: 14
  });

  // 初期位置にマーカーを立てる
  mainMarker = new google.maps.Marker({
    map: map,
    position: latlng
  });

latlngという変数を定義して店舗の緯度と経度を指定しています。
gonというのはgemの1つでrailsで定義した変数を、javascriptで使える変数に変換してくれます。
(https://github.com/gazay/gon)
new google.maps.Mapとすることで新しく地図を作成し、オプションで中心地やズームレベルを指定します。
地図の作成に関してはLet'sプログラミングやMaps JavaScriptAPIのドキュメントを参考にさせていただきました。

次に作成した地図にマーカーを立てていきます。
こちらもnew google.maps.Markerでマーカーを作成してどのmapにマーカーをたてるか、どこにマーカーを立てるかを指定することでマーカーを立てることができます。

3.周辺店舗にマーカーを立てる + 吹き出しを追加する

for (var i = 0; i < markerData.length; i++) {
    const image = "https://maps.google.com/mapfiles/ms/icons/yellow-dot.png";
    const id = markerData[i]['id'];
    // 緯度経度のデータを作成
    let markerLatLng = new google.maps.LatLng({lat: markerData[i]['latitude'], lng: markerData[i]['longitude']});
    // マーカーの追加
    marker[i] = new google.maps.Marker({
      position: markerLatLng,
      map: map,
      icon: image,
    });

    // 吹き出しの追加
    infoWindow[i] = new google.maps.InfoWindow({
    // 吹き出しに店舗詳細ページへのリンクを追加
    content: `<a href=/laundries/${id}>${markerData[i]['name']}</a>`
    });

    markerEvent(i); 
  }

  // マーカークリック時に吹き出しを表示する
  function markerEvent(i) {
    marker[i].addListener('click', function() {
      infoWindow[i].open(map, marker[i]);
    });
  }

行っていることとしてはlet markerData = gon.places;で定義した変数の値を1つずつ取り出してマーカーと吹き出しを追加しています。
そしてマーカーの色を変えたかったためimageという定数にアイコンを定義してicon: imageで定義したアイコンを使用しています。
new google.maps.LatLng({lat: markerData[i]['latitude'], lng: markerData[i]['longitude']});
とすることでそれぞれの要素の緯度経度に対してマーカーを立てるように指定します。

続いて吹き出しを追加します。
contentで吹き出しの中に入れる文字を指定できます。
今回は各店舗名を表示+クリックすることで詳細ページに遷移をさせたかったため、aタグを使いました。
最後に吹き出しを作成しただけでは表示されないためaddListenerを利用してマーカーがクリックされたときに吹き出しが開くようにしました。

以上、GoogleMapに複数マーカーを表示する方法でした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

文字のだるま落とし(ruby編)

[Ruby編]文字のだるま落とし

問題

だるま落としは 5 段の木片からなり、その木片には 1 つにつき 1 つの英字小文字が書いてあります。
木片を飛ばす前の文字列と、飛ばしたのが何段目の木片であったかが与えられるので、木片を飛ばした後の文字列を求めるプログラムを作成してください。

入力される値

入力は以下のフォーマットで与えられます。

S
N
・1 行目には、木片を落とす前の文字列を表す S が与えられます。
・2 行目には、何段目の木片を落とすかを表す整数 N が与えられます。
・入力は全部で 2 行となり、最後に改行が一つ入ります。

期待する出力

木片を落下させた後にできる文字列を出力してください。

末尾に改行を入れ、余計な文字、空行を含んではいけません。

入力例1

apple
2

出力例1

aple

入力例2

water
5

出力例2

wate

私の答え

a = gets.chomp
b = gets.to_i
str = a.slice!(b - 1)
print a

今回のポイント

1行目で文字列の呼び出し

2行目で数値の呼び出し

3行目でsliceメソッドを用いて b - 1で文字列を指定して呼び出している

4行目でstrにすると a という文字列のみ出力されるので print a でsliceした文字列を除外した文字列を出力した

以上!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

deviseでログアウト時に確認ページに遷移させる

はじめに

railsでdeviseをしようしたログインログアウト機能を実装していて、
ログアウトした際に確認ページに遷移するように実装していたところ
つまずいたので記事にしようと思います。
さらに良い方法とかがあればコメント欄か編集リクエストに記載願います。:bow_tone2:

バージョン情報

  • ruby 2.6.5
  • rails 6.0.3.3

前提条件

  • hamlでの記載(gem 'haml-rails')
  • deviseが導入済みでログインができている

コントローラーの変更

sessions_controllerのrespond_to_on_destroyがデフォルトで記述されていて
root_pathに遷移されるようになっていました。
なのでここのコードを削除します。
そこにrender '遷移先URL'を記述することでURL先のページに遷移することができます。

# controllers/users/sessions_controller.rb

def destroy
  signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
  set_flash_message! :notice, :signed_out if signed_out
  yield if block_given?
  respond_to_on_destroy #デフォルトの記述 ← コード削除
  render '遷移先URL' #renderで遷移先のURLを記述  コード記入
end

Viewの作成

私はusers/destoroyにdestroy.html.hamlファイルを作成しました。

%h1 ログアウトしました。
= link_to "マイページに戻る", "/"

destroy.png

コントローラーのカスタマイズ

sesstions: "users/sessions"を追記して、sessionsをカスタマイズしました!!

  devise_for :users, controllers: {
    registrations: "users/registrations",
    sessions: "users/sessions" #カスタマイズ
  }

ルーティングのカスタマイズ

ルーティングもカスタマイズします!
HTTPメソッド:delete, URL, コントローラー名#アクション名

  devise_scope :user do
    get 'profiles', to: 'users/registrations#new_profile'
    post 'profiles', to: 'users/registrations#create_profile'
    get 'addresses', to: 'users/registrations#new_address'
    post 'addresses', to: 'users/registrations#create_address'
    delete 'users/destroy', to: 'devise/sessions#destroy' #追記
  end

二つをあわせて
routes.rb

  devise_for :users, controllers: {
    registrations: "users/registrations",
    sessions: "users/sessions" #カスタマイズ
  }
  devise_scope :user do
    get 'profiles', to: 'users/registrations#new_profile'
    post 'profiles', to: 'users/registrations#create_profile'
    get 'addresses', to: 'users/registrations#new_address'
    post 'addresses', to: 'users/registrations#create_address'
    delete 'users/destroy', to: 'devise/sessions#destroy' #追記
  end

まとめ

まだまだMVCの理解がないと感じました。
冷静になって考えたらすぐに解決できた問題でした。
そして、マークダウン の書き方にも苦戦してます。
記事書くのにすごい時間かかってしまっているので早くなれなければ…
もっとアウトプットしていきます。

しょうま

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Git ブランチ作成・削除・便利コマンド【Gitコマンド】

ブランチとは

ブランチ(branch)は、1つのプロジェクトから分岐させることにより、プロジェクト本体に影響を与えずに開発を行える機能のことを言います。つまり変更などの履歴を分岐してそれぞれ記録していくということですね!
分岐したブランチはほかのブランチの影響を受けないので、同じレポジトリの中で複数の変更(実装)を同時に進めていくことができます。
image.png

このように複数のブランチで開発を行い、それぞれのブランチで異なる機能の実装を行い実装が完了したタイミングなど(チームや作業者で異なります)で、統合(merge)して開発を進めていきます。

ブランチの作成関連

※ブランチを移動する前にはcommitが必要になります。commitせずに移動しようとするとcommitしてください的なerrorが出るのでその際は潔くcommitしてから移動しましょう。
※[ ]は省いて入力してください

#現在いる自分のブランチを確認(出力された中の*がついているブランチが現在のブランチです)
$ git branch

#ローカル間でのブランチの移動
$ git checkout [移動したい先のブランチ名]

#新規ローカルブランチの作成
$ git branch -b [作成したいブランチ名]

#リモートにあるブランチを元に新しくローカルブランチを作成
$ git branch [新しいブランチ名] [元にするブランチ名]

ブランチの削除

1⃣マージ済みのブランチの削除(マージしてないとエラーが出ます、してない場合は2⃣)
$ git branch -d [ブランチ名]

2⃣ローカルブランチを問答無用で削除
$ git branch -D [ブランチ名]

ブランチ名の変更

#ローカルのブランチ名の変更(hogeからfugaなら[hoge] [fuga])
$ git branch -m [現状のブランチ名] [新しいブランチ名]

#現在使用しているブランチ名の変更(fugaにしたければ[fuga])
$ git branch -m [新しいブランチ名]                                

統合ブランチへのプルリク&マージ後のローカルへの反映

#1⃣反映させたいブランチへ移動(統合ブランチがhogeなら[ブランチ名]はhoge)
$ git checkout [ブランチ名]

#2⃣リモートの最新版をローカルにプルする(1⃣に合わせると[ブランチ名]はhoge)
$ git pull origin [ブランチ名]

#3⃣自分の作業ブランチへの反映(1⃣2⃣に合わせると[ブランチ名]はhoge)
$ git checkout [自分の作業ブランチ名]
$ git merge origin/[ブランチ名]

※2⃣でプルした後に新しくブランチを作成したい場合
$ git checkout -b [作成したいブランチ名]

空コミット(筆者は良く忘れるコマンドなのでメモ代わりに、、)

空コミットは新規ファイルや修正ファイルが皆無でもコミットすることができます。
ブランチを確認するときなどに使います。

$ git add -A

$ git commit --allow-empty -m "コミットメッセージ"

#リモートレポジトリにプッシュ
$ git push origin [ブランチ名]

最後に

今回は初心者である筆者による筆者のための備忘録要素がハンパないですが、参考になれば嬉しいです。
他にもよく使うコマンドがあれば追加していきたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む