Fun, Done, Learn

yazawa's tech blog

Rails で新しくプロジェクトを作る時のテンプレート手順

f:id:yazawa_tech:20200203165732p:plain

はじめに

最近個人で公開できるWeb サービスを作りたいと思い、Rails で作ることが多くなりそうなので、最初に実行する内容をまとめてみたいと思います。

目次

手順

プロジェクトの作成

まずローカルホストで雛形プロジェクトを作成します。DB はPostgresql を指定していますが、後々 Heroku Postgres を利用することを想定しています。

jp.heroku.com

bin/rails new project-name -d postgresql
cd project-name 
git remote add origin git@github.com:kj-yazawa/project-name.git
git add -A
git commit -m 'initial commit'
git push -u -origin master
git checkout -b develop
git checkout -b topic-branch

CoffeeScript の削除

いつも CofeeScript を使用しないので削除してしまいます。

vim Gemfile

# -# Use CoffeeScript for .coffee assets and views
# -gem 'coffee-rails', '~> 4.2'

Haml の導入

HTML を簡単に記述することができるようになる Haml を導入します。

haml.info

vim Gemfile

# +gem 'erb2haml'
# +gem 'haml-rails'

RSpec とFactoryBot の導入

テストケースを記述するのに RSpec を使用しているので、それとテストデータ作成のための FactoryBot をインストールします。

github.com

vim Gemfile

# +gem 'rspec-rails'
#  group :test do
# +  gem 'factory_bot_rails'
#  end


# 最後にbundle install
bundle install 

scaffold の作成

bin/rails g scaffold User name:string --no-jbuilder --no-helper --skip-view-specs --skip-controller-specs --skip-routing-specs --skip-request-specs --skip-helper-specs 

#      invoke  active_record
#      create    db/migrate/20200203071021_create_users.rb
#      create    app/models/user.rb
#      invoke    rspec
#      create      spec/models/user_spec.rb
#      invoke  resource_route
#       route    resources :users
#      invoke  scaffold_controller
#      create    app/controllers/users_controller.rb
#      invoke    haml
#      create      app/views/users
#      create      app/views/users/index.html.haml
#      create      app/views/users/edit.html.haml
#      create      app/views/users/show.html.haml
#      create      app/views/users/new.html.haml
#      create      app/views/users/_form.html.haml
#      invoke    rspec
#      invoke      rspec
#      invoke  assets
#      invoke    js
#      create      app/assets/javascripts/users.js
#      invoke    scss
#      create      app/assets/stylesheets/users.scss
#      invoke  scss
#   identical    app/assets/stylesheets/scaffolds.scss

DB 初期化

bin/rails db:create
# Created database 'portfolio_development'
# Created database 'portfolio_test'

bin/rails db:migrate
# == 20200203071021 CreateUsers: migrating ======================================
# -- create_table(:users)
#    -> 0.0038s
# == 20200203071021 CreateUsers: migrated (0.0038s) =============================

サーバー起動

bin/rails s
# ポート指定する場合は -p オプションで指定する
# bin/rails s -p 3003

ここで localhost:3000 (または 3003)にアクセスして以下の画面が出ればOK。

f:id:yazawa_tech:20200203165732p:plain

まとめ

  • Rails で雛形プロジェクトを作る時の手順をまとめてみました。よく使うGem などは決まっているので、これからも導入部分でインストールした方が良いものなどあれば追記していきたいと思います。

【Git】特定のコミットの内容をコピーしたい時に使えるcherry-pick コマンド

f:id:yazawa_tech:20191126141207p:plain

はじめに

今回は、プロジェクトで起きてしまったちょっとしたトラブルから、 cherry-pick コマンドについて調べる機会があったので、まとめてメモしておきます。

目次

ケーススタディ

今回起きてしまった事象は、以下の通り。

  • develop ブランチからcheck ブランチを派生させていた
  • check ブランチは、各々のfeature ブランチをマージして、development 環境にデプロイして動作確認する用のブランチ
  • check ブランチでの動作確認が完了した後に、問題なければ同じコミットログを持ったfeature ブランチからdevelop ブランチに向けてPull Request を作成し、マージする
  • 【今回の事象】check ブランチをdevelop ブランチに誤ってマージしてしまった(その後Revert)
  • check ブランチにマージした履歴が入ったことにより、本来後日develop ブランチに向けて作成されるべき、feature ブランチの持つコミットログが取り込まれてしまった
  • コミットログが取り込まれてしまったことにより、後日feature ブランチからdevelop ブランチに向けてPull Request を作成すると、「差分なし」として認識されてしまう

こんな場合に、「develop ブランチから新たにfeature ブランチを切り直し、check ブランチから自分のコミットログをfeature ブランチにcherry-pickする」ことで解決できる。

cherry-pick とは

Git の公式サイトでは以下のように説明されておりました。

Given one or more existing commits, apply the change each one introduces, recording a new commit for each.

(出典) https://git-scm.com/docs/git-cherry-pick

要約すると、「1つ以上の既存のコミットが与えられた場合、それぞれの変更を適用して、新しいコミットを作る」とのことです。

つまり、差分を新しいコミットで作ってくれるということですね。

サンプルコマンド

仕事でchrry-pick コマンドを使用した時の流れを、サンプルコマンドとしてメモしておきます。

yourname@hostname [11:22:58] [~/env/your-project] [feature/new_branch *]
-> % git checkout feature/old_branch 
# Switched to branch 'feature/old_branch'
# Your branch is up to date with 'origin/feature/old_branch'.
yourname@hostname [11:23:34] [~/env/your-project] [feature/old_branch *]
-> % git log --oneline
### 取り込みたいコミットログ
97ce20c12 (HEAD -> feature/old_branch, origin/feature/old_branch) コミット2
41b0df96b コミット1
yourname@hostname [11:26:54] [~/env/your-project] [feature/new_branch *]
-> % git cherry-pick 41b0df96b 97ce20c12 # 上のコミットIDを引数に渡す(1つ以上)
# [feature/new_branch 4266431c4] コミット1
#  Date: Thu Nov 21 15:15:20 2019 +0900
#  1 file changed, 8 insertions(+), 2 deletions(-)
# [feature/new_branch d520fb748] コミット2
#  Date: Mon Nov 25 10:24:47 2019 +0900
#  2 files changed, 3 insertions(+), 2 deletions(-)
### コミット1とコミット2が新しいブランチに反映された
yourname@hostname [11:27:19] [~/env/your-project] [feature/new_branch *]
-> % git log --oneline
### 古いブランチにあったコミット1とコミット2とはコミットIDが異なっている = 新しいコミットが作られた
d520fb748 (HEAD -> feature/new_branch) コミット2
4266431c4 コミット1
19b8a22be (origin/develop, origin/HEAD, develop) Merge pull request #1111 from master

これで、新しく切り直したブランチに、古いブランチでの差分を反映した新しいコミットを作ることができました。

まとめ

  • cherry-pick コマンドを使うと、特定の差分を別のブランチに取り込める
  • コマンドのみで実行するのが緊張する人は、SourceTree などのGUI を使用すると最初はイメージが湧きやすい
  • cherry-pick する場合はブランチをクリーンにする必要があるが、もし忘れていてコンフリクトが起きてしまったら、上記の公式サイトを参考に解消する

参考サイト

こちらも有名なサイトでイメージ図付きでわかりやすいです。

https://backlog.com/ja/git-tutorial/stepup/31/

【用語】Webhook ってなんだかわからなかったので調べてまとめる

f:id:yazawa_tech:20191125150512p:plain

はじめに

仕事で「そこの処理はWebhook でレスポンスを受け取っているはずなので云々」みたいなことを言われたので、よく知らなかったので改めて調べてみたことをまとめておきます。

目次

Webhook とは?

一言でわかりやすく説明してくれているサイトがあったので、以下に引用します。

Webhook とは、Webアプリケーション同士が連携するときの考え方の一つで、 Webアプリケーションでイベントが実行された際、外部サービスにHTTP で通知する仕組みです。

(出典) コーディングなしで超簡単!kintoneのWebhookでGmailに通知する(https://developer.cybozu.io/hc/ja/articles/115000299143)

developer.cybozu.io

Webhook とは Webアプリケーション同士の連携方法の考え方 であり、 イベントをトリガーとして外部にHTTP 通信をする仕組み だそうです。

Slack にも Incomming Webhooks という便利な仕組みがあるようで、これを利用すると、POSTリクエストを外部からSlack に向けて送ることができるようです。

slack.com

Webhook の便利な使い方

今回は、単純にPOSTリクエストをするというよりも、連携先の外部サービスに、 レスポンス先を指定して、そこに返却してもらう ということをしているみたいです。

調べたのは、 atone という「後払い決済サービス」で、お店がユーザーに対して後払い決済を選択できる仕組みを提供するサービスです。

atone.be

こちらのサービスでは、加盟店の管理サイトで「決済Webhook」と「認証Webhook」という設定項目があり、ここに任意のURL を設定しておくと、「認証処理」や「決済処理」を実施した後に、設定されたURLにPOST リクエストをしてくれるというものみたいでした。

マニュアルページはこちら: https://manual.atone.be/jp/script-and-parameter#chapter-7

まとめ

  • Webhook とは Web アプリケーションでのイベントをトリガーとして、外部サービスにHTTP で通知する仕組み
  • サービスによっては、 事前に設定することで、POST 通信先を設定して、自サービスへのリクエストURLを指定することができる
  • GitHub やSlack など、他のアプリと連携することで力を発揮するアプリケーションにはだいたいこの仕組みが用意されていそう

参考サイト

kintone-blog.cybozu.co.jp

ja.wikipedia.org

developer.ntt.com

【Rails】あるURLを任意のコントローラーのメソッドに紐付ける方法

f:id:yazawa_tech:20191125135214p:plain

はじめに

Railsではconfig/routes.rbに特定のキーワードを使ってルーティングの定義をすることで、URLとコントローラーのメソッドを紐づけてくれる仕組みがあるが、今回は通り、URLを別名のメソッドに紐付けたい時に役に立つ知識をメモしておく

目次

やりたいこと

今回は、ルーティングを定義する際に、 URLとは別名のメソッドにリクエスト先を設定したい 時の記述をメモしておきます。

修正前

以下の例では、 /admins/monthly_reports/fixed というルーティングを定義した時に自動的に生成されるURLが、規約にしたがって controller#method にリクエスト先が設定されている。

config/routes.rb

  namespace :admins do
    resources :monthly_reports, only: %i[index] do
      collection do
        get :fixed
        get :rejected
      end
    end
  end
   fixed_admins_monthly_reports  GET  /admins/monthly_reports/fixed(.:format)     admins/monthly_reports#fixed
rejected_admins_monthly_reports  GET  /admins/monthly_reports/rejected(.:format)  admins/monthly_reports#rejected
         admins_monthly_reports  GET  /admins/monthly_reports(.:format)           admins/monthly_reports#index

これを、URLはそのままに、リクエスト先のコントローラ、またはメソッドを変更したい時は、どのようにすれば良いだろうか。

修正後

以下の例では、 /admins/monthly_reports/fixed のリクエスト先を、 admins/monthly_reports_controllerindex_fixed メソッドに設定している。

config/routes.rb

  namespace :admins do
    resources :monthly_reports, only: %i[index] do
      collection do
        get :fixed, to: 'monthly_reports#index_fixed'
        get :rejected, to: 'monthly_reports#index_rejected'
      end
    end
  end
   fixed_admins_monthly_reports  GET  /admins/monthly_reports/fixed(.:format)     admins/monthly_reports#index_fixed
rejected_admins_monthly_reports  GET  /admins/monthly_reports/rejected(.:format)  admins/monthly_reports#index_rejected
         admins_monthly_reports  GET  /admins/monthly_reports(.:format)           admins/monthly_reports#index

このように、任意のメソッドに設定したい場合は、 get :fixed, to: 'monthly_reports#index_fixed' のように to オプションを使って任意のメソッドに向き先を変更してあげれば良い。

参考サイト

Rails ガイドのルーティングのページに to オプションを使用したコードがいくつか書いてあった。

railsguides.jp

まとめ

  • URLとリクエスト先のコントローラーまたはメソッド名を、 リロースフルでない形式で 設定する場合には、 to オプションを使う

モダンな技術を使ってプロダクトを開発できるようになるための入門手順(バックエンド)

f:id:yazawa_tech:20191120143119p:plain

はじめに

エンジニアとしてフルスタックで技術を身に付けたいと思っており、かつその技術をどのような順番で修得して行くと良いのかという道のりを、メモとして残しておく。 道のりに加えて、その技術を身につける際に参照する情報もまとめておく。

目次

身に付けたいと思っているスキルセット

この中で、今回は AWS Docker CircleCI について、メモしておく。

身に付ける順番

AWS > Docker > CircleCI という順序が良さそう。 AWS の中でも基本的な EC2RDS のような最低限の機能を使いこなせるようになるためであれば、Dockerの知識は不要なので、単独で先行して学んでしまった方が効率がいい。 CircleCIは内部でDockerが利用されているので、Dockerの基礎知識が必要になってくるため、CircleCIに先行して学習しておく必要がある。

身に付けるにあたって参照する書籍・Webページ等

AWS (Amazon Web Service)

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版

ゼロからわかるAmazon Web Services超入門 はじめてのクラウド (かんたんIT基礎講座)

ゼロからわかるAmazon Web Services超入門 はじめてのクラウド (かんたんIT基礎講座)

ゼロからわかるAmazon Web Services超入門 はじめてのクラウド (かんたんIT基礎講座)

(下準備編)世界一丁寧なAWS解説。EC2を利用して、RailsアプリをAWSにあげるまで (Qiita)

qiita.com

Docker

入門 Docker

y-ohgi.com

Docker/Kubernetes 実践コンテナ開発入門 (第3章まで)

Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門

CircleCI

CircleCI 公式チュートリアル

circleci.com

いまさらだけどCircleCIに入門したので分かりやすくまとめてみた (Qiita)

qiita.com

CircleCI-Public/circleci-demo-ruby-rails

github.com

まとめ

これらをベースに自分のポートフォリオに組み込んだり、キャッチアップした結果をブログやLTなどでアウトプットしていけば、間違いなく身についていくし、企業や周りへのアピールにもなるので、積極的にアウトプットしていきたい。 ひとまずここで計画を立てた通り、上から順番にキャッチアップしていきたいと思う。

Vue.jsのディレクティブを何度も使って基本文法をマスターする

f:id:yazawa_tech:20191120090428p:plain

はじめに

仕事で新しくVue.jsを使うようになったので、まずは基本文法をマスターできるよう、復習問題のを含んだブログを書いておく。

目的

1週間後、2週間後、1ヶ月後の自分(または同じような境遇の人)がこれを見て復習できるように。

目次

確認環境

JSFiddle で以下のfiddleをForkして動作確認する。

jsfiddle.net

復習問題

  1. Vueインスタンスのdataオプションのmessageプロパティの内容をMustache構文で表示させてください
  2. v-bindディレクティブを使用して、リンク先がVue.jsの公式サイト(https://jp.vuejs.org/)であるa要素を記述してください
  3. v-ifまたはv-showディレクティブを使用して、toggleプロパティがtrueの場合に上記のリンク先が表示されるように記述してください
  4. v-forディレクティブを使用して、languegesプロパティの値をリスト表示させるような要素を記述してください
  5. v-onディレクティブを使用して、クリックしたらmessageプロパティの値を書き換えるようなボタン要素を記述してください
  6. v-modelディレクティブを使用して、messageプロパティと双方向データバインディングできるinput要素を記述してください

解答サンプル

<div id="app">
  <p>{{ message }}</p>

  <a v-bind:href="url" v-if="toggle">Vue.js</a>

  <ul>
    <li v-for="language in languages">{{ language }} </li>
  </ul>

  <p>
    <button v-on:click="onclick">click</button>
  </p>

  <input type="text" v-model="message">
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello World!',
    url: 'https://jp.vuejs.org',
    toggle: true,
    languages: ['JavaScript', 'Ruby', 'Python'],
  },
  methods: {
    onclick: function() {
      this.message = 'Clicked!';
    }
  }
})

Rails アプリにモデルスペックを導入する

はじめに

今回はRails でアプリケーションを作っている際に、モデルスペックを書くシーンがあったので、「どんなテストを書けば良いのか」という観点でまとめてみた。

目次

参考書籍

今回の記事はこちらの書籍を参考にまとめた (なるべく内容を丸写ししないようにまとめたつもりですが、ご指摘などあればコメントいただけると幸いです)

leanpub.com

モデルスペックを導入する時の手順

  1. 既存のモデルに対してモデルスペックを作る
  2. モデルのバリデーション、クラスメソッド、インスタンスメソッドのテストを書く

モデルスペックに含めるべきテスト

  1. モデルの状態が有効であること(有効なパラメータを与えて初期化等)
  2. モデルの状態が有効でないこと(Not Null などのバリデーションが失敗するようなデータ等)
  3. クラスメソッドとインスタンスメソッドが期待通りに動作すること

一言で言うと、「モデルが有効か無効か」と「各メソッドが期待通りの動きをすること」のテストを記述する。

モデルスペックの書き方

モデルスペックの大枠は以下のようになる。例えばEC サイトで製品を表すproduct モデルがあった場合、以下のようになる。

describe Product do
  it 'is valid with name, price' # 有効な場合のテスト
  it 'is invalid withuot name'   # 有効でない場合のテスト
  it 'is invalid withuot price'  # 有効でない場合のテスト
end
  • describe から始まり、Product モデルへのテストを記述することを宣言している
  • it の後に説明文を書くことで、何に対しての検証か、何を期待するかが明確になっている
  • 【重要】describe の後に続く「モデル名」 + it の後に続く「テストケース」が英語の文章になり、意味が通じるように記述する
    • Product is valid with name, price;

上の状態で bin/rspec コマンドを実行すると、 pending の状態のテストが3つ出来上がる。

1.モデルが有効な場合のテスト

有効であることを検証するためには、例えば以下のようにテストを記述する。

describe Product do
  it 'is valid with name, price' # 有効な場合のテスト
    product = Product.new(
      name: "spoon",
      price: 300
    )
    expect(product).to be_valid
  end
end

expect(product).to be_validproduct.valid? == true を検証しているイメージを持つと、わかりやすい。

2.モデルが有効でない場合のテスト

有効でない場合のテスト、すなわちバリデーションエラーになるようなテストを実行したい場合には、例えば以下のように記述すれば良い。

describe Product do
  it 'is invalid withuot name' do  # 有効でない場合のテスト
    product = Product.new(
      name: nil,
      price: 300
    )
    product.valid?
    expect(product.errors[:name]).to include("can't be blank")
  end
end

product.errors[:name] は 「Product モデルのname 属性に関するエラーメッセージの配列」を返し、その配列が 「"can't be blank"という文字列を含んでいるか」を検証している。product.errors[:name].include?("can't be blank") == true を検証しているイメージを持つと、わかりやすい。

3.クラスメソッドとインスタンスメソッドが期待通りに動作するテスト

インスタンスメソッドのテスト

例えばインスタンスメソッドに以下のようなメソッドがあったとする。

class Product
  def equals?(product)
    (self.name == product.name) && (self.price == product.price)
  end
end

この場合は、モデルが有効な場合や無効な場合と同様に以下のように書ける。

describe Product do
  it 'returns true given product with same name and same price'
    one_product = Product.new(
      name "banana",
      price 500
    )
    another_product = Product.new(
      name "banana",
      price 500
    )
    expect(one_product.equals?(another_product)).to eq true
  end
end

スコープとクラスメソッドのテスト

例えば、製品を金額で条件を指定して検索するスコープが以下のように定義されているとする

scope :upper_than, ->(price) {
  where("price >= ?", price)
}

この場合にモデルのテストを書くとしたら以下のようになる(大枠のみ)

RSpec.describe Product, type: :model do
  it "returns products that match the search price"
    # Product オブジェクトをいくつか作る
    # 検索条件を渡してスコープを実行し、一致するオブジェクトを含むか含まないかをテストする
    # …
    # 失敗するテストも追加する
    # 例えば、検索条件として不適切な値を入れた時に、検索結果が0件の場合のテストなど
end

まとめ

  • 今回はRSpec を使った書き方も含め、モデルスペックを書くときの指標をまとめてみた
  • Rails だけじゃなく、全ての単体テストを書く時にも参考になると思うので、他のアプリでも指標として使いたい
  • システムスペックの指標も自分なりに整理したいので、次回はシステムスペックについて掘り下げてみたいと思う