Git のブランチには説明をつけることができる

このツイートの通りです。

使い方

下記のコマンドを実行するとエディタが起動し、説明を書けます。 <branchname> を省略すると、現在のブランチに対してメモが書けます。

$ git branch --edit-description [<branchname>]

メモを表示するコマンドは下記の通りです。 <branchname> はメモを表示したいブランチ名です。

$ git config branch.<branchname>.description

使いづらいけど、仕方ありません。Git のコマンドはそんなものです。

git alias

ブランチの説明を簡単に表示するエイリアスを作ってみました。

[alias]
  # git show-description [<branch_name>]
  show-branch-description = "!f(){ git config branch.$(git rev-parse --abbrev-ref ${1:-@}).description;};f"

これで少し使いやすくなると思います。

注意

ブランチの説明はローカルリポジトリのブランチに対するもので push はできません

本来の用途

ヘルプを読むと、ブランチの説明は format-patchrequest-pullmerge のコマンドで使うようです。

引用元: git help branch

–edit-description Open an editor and edit the text to explain what the branch is for, to be used by various other commands (e.g. format-patch, request-pull, and merge (if enabled)). Multi-line explanations may be used.

引用元: git help merge

merge.branchdesc In addition to branch names, populate the log message with the branch description text associated with them. Defaults to false.

format-patchrequest-pull はあまり使わずよく分からないので、ためしに merge で使ってみました。

merge で使う

まず、マージで使えるようにするために下記の設定をします。

$ git config merge.branchdesc true
$ git config merge.log true

この状態で説明の付いているブランチをマージすると、自動でブランチの説明がコミットメッセージに挿入されます。

Merge branch 'feature' into dev

* feature:
  : ブランチの説明
  2nd commit message
  1st commit message

# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.

なるほど、便利そうな雰囲気あるけど、やっぱり使い所は思い浮かばない。

ブランチをたくさん使う人には便利かもしれません。

2017年 OmniAuth で Qiita 連携を作る際の現状まとめ

Rails で OAuth 連携の機能を作う場合、一般的に OmniAuth を使うことになります。

そして、 OmniAuth の Qiita Strategy が3つあったので、それぞれ状況をまとめました。

各 gem の概要

GitHub rubygems version work?
tmiyamon/omniauth_qiita omniauth_qiita 0.1.0 Yes
kazu69/omniauth-qiita omniauth-qiita 0.0.3 No
increments/omniauth-qiita Not found 0.0.1 No

omniauth-qiita

increments/omniauth-qiita

/auth/qiita のページに遷移後

{"message":"Not found","type":"not_found"}

と画面に表示されてしまい、全く動かない。

公式 wiki の紹介

Provider Notes Author Status Official?
Qiita Qiita OAuth2 Strategy for OmniAuth. tmiyamon Released No

引用元: https://github.com/omniauth/omniauth/wiki/List-of-Strategies

まとめ

omniauth_qiita を使っておくのが良さそう

Ruby/Rails の勉強方法について #omotesandorb

6/1(木) の表参道.rb でLTしてきました。

omotesandorb.connpass.com

資料

「他人がどうやって Ruby/Rails を学んできたのか?」って意外と聞く機会は無いと思うので、自分のふりかえりも兼ねて発表してみました。資料は Qiita で公開してます。

qiita.com

経歴について

  • RESTful を知らずにコード書いてた時代
    • ソシャゲ案件で QuestsController#execute とか書いてた
  • 実は Rails チュートリアル未経験
  • SQLActiveRecord 無しでちゃんと触ったのは最近(3年前くらい)
    • それまで都度、ググっていた

と、恥ずかしい話もありますが、今 Ruby/Rails 勉強している人に何か役立てば…と思って、自分が触ってきた技術的なネタを時系列で紹介してみました。

初心者向けの Ruby/Rails の勉強方法について

帰りの電車内で「Ruby/Rails の勉強方法」について色々と話していたので、自分なりに こうすれば Ruby/Rails の技術力が上がるだろうなー という方法を整理して、ブログで紹介してみます。

下記の順番でやってみるのはどうでしょう?

1. Ruby の基本文法を知っていますか?

「いいえ」という方、何か書籍を1冊買いましょう。他の方の発表だと 初めてのRuby が良書だと紹介されていました。

初めてのRuby

初めてのRuby

書籍を買う理由は下記の2つ。

  • 体系立てて Ruby を勉強、理解できる
  • ググりにくい内容を知ることができる
    • 特殊記号の動き( <=>%w(a b c) などの記法)

2. Rails アプリを 1人で作ったことがありますか?

「いいえ」という方は自分で rails new を実行し、何かアプリを作ってみましょう。私も昔、「Rails によるアジャイルWebアプリケーション開発」の写経をしたことがあります。

RailsによるアジャイルWebアプリケーション開発 第4版

RailsによるアジャイルWebアプリケーション開発 第4版

あと、何人もの方が Rails チュートリアル をオススメしているので、やってみると良いかも*1

3. 仕事で使っている gem の README を全部読んでますか?

「いいえ」という方、まず自分が仕事で使っている gem の README を 全て読むところ から始めましょう。

  • 何をするための gem なのか?
    • 概要は知っておいた方が良い
    • Rails の標準機能なのか、 gem の拡張機能なのかを正しく理解しておいた方が良い
  • 英語の README を日常的に読む癖をつける
    • 日本語のブログを見ないで、公式の英語ドキュメントを最初に読むのが良い
    • インストール方法、初期設定、オプションなどは一度目を通すと理解が早い

余裕があれば、同種の gem の README を比較して読むと面白いです。ファイルアップロードで carrierwaveshrine の gem があるけど、何が違うの?みたいな感じ。

4. 仕事で使っている gem のコードを読んでいますか?

「いいえ」という方は仕事で使っている gem のソースコードから読んでみましょう。最初はなかなか読み進められないと思いますが、だんだんと読めるようになりますし、かなり勉強になります。

  • 汎用的な設計になっていることが多い
  • メタプロを実践的に使っているケースが多い
  • 複数 Ruby/Rails のバージョンに対応していて、学びが多い

コードを読むようになると「機能は少ないけど、コードがシンプル」「多機能でコードも複雑」「メタプロが多くて、読めない」みたいな違いが分かるようになってきます。 deviserails_admin のコードを読むと、便利さとメタプロの辛さを同時に勉強できてオススメです。

5. gem にコントリビュートしたことあります?

「いいえ」という方、まずは使っている gem の Issue やプルリクを読んでみましょう。

  • どんなコードがプルリクで出されているのか?
  • OSS ではどんなコミュニケーションがされているのか?
    • 英語のやりとり、コミットメッセージの定型句を覚えられる
    • リポジトリごとの文化の違いが分かる
  • 最新の Ruby/Rails に対応する方法を知ることができる
    • Ruby 2.5(Rails 5.1)対応のプルリクの diff は非常に勉強になります

6. 最新の Ruby/Rails 情報を自分で追ってみよう

今、私はこのあたりを実践している感じです。

  • id:y_yagi_2 さんの rails commit log流し読み を読む
    • 気になるコミットは diff まで読む
    • 良さそうなものは即仕事のコードに反映する(最近だと rails/rails@0b8441b とか)
  • Ruby, Rails, RuboCop のコミットログを RSS で個人 Slack に流す
    • よくコミットしている人の名前、アイコンを覚える*2
    • 気になるコミットには目を通す(最近だと RuboCop の parallel は楽しみに待ってた)
  • 勉強会で Rails アプリを書いている人と 実践的な設計 について議論する
    • 複雑な要件を、どうやって RESTful にするか?
    • どうすればメンテ性の高いコードを書けるか?
    • ツラいコードの経験の共有

7. そして、Ruby/Rails コントリビューターに

まだ実践できていないけど、Ruby/Rails を使うだけじゃなくて、何か貢献したい。

さいごに

技術力を上げるためには「コードを読んだ量」と「コードを書いた量」が一番大事だと思うので、OSS のコードをたくさん読んで、Ruby/Rails のコードを書き続けるのが上達するための一番の近道だと思います。

この記事が何かしら Ruby/Rails 初心者の参考になれば幸いです。

*1:私は未経験なので詳細は知らないのですが…

*2:その人の gem は信頼できる

Tor: Hidden Service Protocol に対応した Mastodon インスタンスを立てた

巷で話題のマストドンインスタンスを立ててみました。

f:id:sinsoku:20170428015230p:plain

インスタンス概要

タイトルにもあるように、Tor の Hidden Service に対応してます。*1

http://xmstdn2w5vvvd747.onion

普通のドメインもあるので、 Tor を使わない人はこちらをどうぞ。

https://xmstdn.com

どんな人向けか

  • 投稿元IPを隠してトゥートしたい人
  • そんな人が投稿する内容を見たい人
  • トゥートのリンクを安易に踏んだりしない人

など。

今後やりたいこと

Tor ユーザーだけに表示されるトゥートとか作りたい。

*1:というか、これに時間がかかっていたわけですが

Rails アンチパターン - 錆びついたファクトリー (factory_girl)

技術書典2 に行ったら無性に本を書きたくなったけど、本書くのは 面倒 大変です。

というわけで、とりあえずブログに記事を1つ書いてみた。

factory_girl

factory_girl はテスト用データを作成するときに使う gem です。

下記は User のモデルを定義するファクトリーです。

FactoryGirl.define do
  factory :user do
    first_name "John"
    last_name  "Doe"
    admin false
  end
end

このファクトリーから User のテストデータを生成することができます。

FactoryGirl.create(:user)
#=> #<User id: 1, first_name: "John", ...

factory_girl は関連先のレコードを自動生成したり、連番を生成したり、結構多機能だったりします。

このあたりの詳細を知りたい方は公式の README を読むか、英語が苦手なら日本語の紹介記事などを読むと良いです。

錆びついたファクトリー

ここからが本題で、factory_girl で定義したファクトリーは時間が経ったり、作成者が未熟だと 錆びる (=ビルドしづらい、できなくなる)ことがあります。

錆びついたファクトリー例を紹介してみたいと思います。

2回以上ビルドできない

class User
  validates :nickname, uniqueness: true
end

FactoryGirl.define do
  factory :user do
    nickname "sinsoku"
  end
end

これはひどい。 uniqueness 制約により2回目のビルドは失敗します。

FactoryGirl.define do
  factory :user do
    sequence(:nickname) { |n| "nickname_#{n}" }
  end
end

上記のように sequence を使って回避すべきです。

build_stubbed なのにレコードが生成される

クラスの属性だけでテストができる( DB アクセスが不要な)ケースがあります。

class Book
  def published?
    published_at <= Time.current
  end
end

このようなメソッドのテストでは DB アクセスをしないように build_stubbed を使うべきです。*1

しかし、ファクトリーが下記のような定義だとレコードが生成されてしまいます。

FactoryGirl.define do
  factory :book do
    author { create(:user) }
    published_at '2017-04-10'
  end
end

これは association を使って定義すべきです。

FactoryGirl.define do
  factory :book do
    association :author, factory: :user
    published_at '2017-04-10'
  end
end

association を使うことで、 build_stubbed の時にレコードが生成されなくなります。

デフォルトのファクトリーが重い

FactoryGirl.define do
  factory :user do
    name 'serval'

    transient do
      friends_count 10
    end

    after(:create) do |_user, evaluator|
      create_list(:user, evaluator.friends_count)
    end
  end
end

デフォルト値のフレンズが多すぎます><

FactoryGirl.define do
  factory :user do
    name 'serval'

    factory :user_with_friends do
      transient do
        friends_count 10
      end

      after(:create) do |_user, evaluator|
        create_list(:user, evaluator.friends_count)
      end
    end
  end
end

デフォルトのファクトリーはシンプルに保ち、フレンズの多いファクトリーは別にしておきましょう。

条件の必要なファクトリー

class Servant
  belongs_to :master

  validate :must_be_valid_master

  TEAM = {
    'Artoria Pendragon' => 'Shirou Emiya'
  }

  private

  def must_be_valid_master
    errors.add(:master, ' is invalid') unless TEAM[name] == master.name
  end
end

FactoryGirl.define do
  factory :servant do
    association :master, factory: :user
    name 'Artoria Pendragon'
  end
end

このファクトリーは master が ‘Shirou Emiya’ のときだけビルドできます。

user = FactoryGirl.create(:user, name: 'Shirou Emiya')
servant = FactoryGirl.create(:servant, master: user)

このように、ビルド時に必要な関連を渡すようなファクトリーは使いづらいです。

FactoryGirl.define do
  factory :servant do
    association :master, factory: :user, name: 'Shirou Emiya'
    name 'Artoria Pendragon'
  end
end

FactoryGirl.create(:servant) だけでビルドが出来るようにしておくべきです。

その他

時間の経過によりバリデーションが追加・変更されたり、モデルの関連が変わってしまい、ファクトリーが錆びてしまうことがあります。

アンチパターンへの対策

幸い factory_girl には Lint がついています。

# lib/tasks/factory_girl.rake
namespace :factory_girl do
  desc "Verify that all FactoryGirl factories are valid"
  task lint: :environment do
    if Rails.env.test?
      begin
        DatabaseCleaner.start
        FactoryGirl.lint
      ensure
        DatabaseCleaner.clean
      end
    else
      system("bundle exec rake factory_girl:lint RAILS_ENV='test'")
    end
  end
end

上記のような rake タスクを作成し、 CI で常にファクトリーがビルドできるかチェックしておくのが良いでしょう。

おわり

パッと思い出せた factory_girl に関するアンチパターンを書いてみました。

他のアンチパターンはやる気と時間があれば書く…かも?

*1:無駄なレコードを生成するとテストが遅くなってしまいます。

個人 Rails アプリを CircleCI 2.0 で動くようにした

CircleCI 2.0 が使えるようになっていたので、アップグレードしておいた。

CircleCI の設定

今まで circle.yml だったけど、 .circleci/config.yml に変わっている。

Ruby の設定例は Language Guide: Ruby があるので、これを参考にすると良い。

version: 2
jobs:
  build:
    docker:
      - image: ruby:2.4.0
      - image: postgres
      - image: redis
    environment:
      PHANTOMJS_URL: https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2

    working_directory: ~/workspace
    steps:
      - checkout
      - type: cache-restore
        key: phantomjs-2.1.1
      - type: cache-restore
        key: gemfile-{{ checksum "Gemfile.lock" }}
      - run: |
          which phantomjs && exit
          curl --location --silent $PHANTOMJS_URL | tar xj -C /tmp --strip-components=1
          mv /tmp/bin/phantomjs /bin
      - run: apt-get update -qq && apt-get install -y build-essential nodejs
      - run: bin/setup
      - run: rake
      - run: yard -o doc
      - store_artifacts:
          path: doc
          destination: doc
      - type: cache-save
        key: phantomjs-2.1.1
        paths:
          - /bin/phantomjs
      - type: cache-save
        key: gemfile-{{ checksum "Gemfile.lock" }}
        paths:
          - /usr/local/bundle

注意点は デフォルトだと bundler の cache が効かない とか、 PhantomJS が入っていない とかくらい?

bin/setup の実行

Language Guide: Ruby の例に合わせて db:create db:schema:load を実行して db:migrate を実行しない方が少し速くなると思う。
ただ、 bin/setup が動作するのをチェックしたくて CI で毎回動かすようにしている。

Rakefile

Rakefile に下記のような定義をしていて、 rake を実行すると rubucop + parallel:spec が動くようにしている。

# frozen_string_literal: true
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

require File.expand_path('../config/application', __FILE__)

Rails.application.load_tasks

unless Rails.env.production?
  require 'rubocop/rake_task'
  RuboCop::RakeTask.new

  task(:default).clear
  task default: [:rubocop, 'parallel:spec']
end

改善できそうなところ

Using docker-composebackground を使えば、もうちょい改善とか実行速度アップとかできるかも?

まぁ、そのあたりは時間あるときに試す。時間あるときに。

Docker を使って Rust に入門してみた

前々から Rust の良いと聞いていたので、ちょっと入門してみた。

参考のサイト

公式の プログラミング言語Rust が分かりやすそうなので、これを見ながら最初の “Hello, World” まで進めた。

まぁ、環境構築しかやってないってことですね。

環境構築

このコミットの通りで、 Docker を使って Rust の環境を構築した。

新しい言語を触る場合、環境構築のどこでハマるか分からんので、 docker コンテナ使う方が楽。*1

次の記事は…

3日坊主にならないように、定期的に Rust 触ってブログに書きたいですね。そんな気持ちはあります。たぶん。

*1:ローカルで変な問題踏んだりとかもしないし