Rubyは1つのことを実行するにも様々な書き方ができることが特徴の言語です。これは他の言語からRubyへ移ってくる際に学習コストを下げて参入障壁を下げる効果もありますが、複数名でソースコードを共有してプロダクトを開発する場合は、このメリットがデメリットに転じるケースがあります。

複数名でコードを共有するときに各々が思い思いの書き方をした場合、他の開発者が書いたコードを参考にするために読む場合や、コードレビューを実施するタイミングで自分の書き方に再解釈し直すプロセスを経る必要があり、可読性が下がってしまいます。最悪の場合はコード内容の誤認を誘発するケースがあります。これにより、コードレビューやプルリク承認時のコード確認に時間がかかったり見落としが起きることが起きてきます。

そこで、そういった可読性や誤認識といったところから来る開発効率の低下を防ぐため、一定のコーディング規約を定めてそれを遵守することが、全体的な効率化や品質向上につながります。

その一定のコーディング規約としてよく利用されるのが、下記のルールです。

昔からあるRuby Style Guideに加え、昨今ではRailsやRSpecに対する標準ルール的なものも規定されつつあります。

「いやいや、テストコードまで縛られるのはちょっと・・・」と思う方もいるかもしれませんが、テストコード自体の正当性もプルリクやレビューの時に確認するものですし、もしそこがしっかり書けていなかった場合、その後の試験に対する信頼性を揺るがすことにもなりかねません。そのため、実はプロダクトコードよりも試験コードを重要視している人もいます。

・・・とは言っても、毎回全てのコードを目で見て、人手でチェックすることは非現実的なため、昨今ではツールを使ってコーディング規約の遵守状況のチェックをします。

ツールとしてはrubocopを使ってプロジェクトの全体を評価するといったことがデファクトスタンダードになってきていますが、これで評価できるのは上記のRuby Style GuideRails Style Guideの一部のみとなっています。

「ではRSpec Style Guideに対応しているか確認するには、すべて人がチェックする必要があるのか?」

答えは、Noです。

これについても既にツールが出てきていて、rubocopを拡張する形で実現されています。

これはGemfileに追加してbundle installした後、.rubocop.ymlに追記するか、rubocopコマンド実行時に引数で指定するかの2パターンで実行することができます。もちろんポリシーの有効・無効や、オプションの指定は.rubocop.ymlに合わせて記載することができます。

今回は.rubocop.ymlに追記する方法を紹介します。

  1. あなたのRailsプロジェクトにあるGemfileに下記を追記します。(rubocopのバージョンは、各プロジェクトで使用するバージョンを決めて固定してください)
     group :development do
       gem 'rubocop', '0.60.0'
       gem 'rubocop-rspec'
     end
    
  2. .rubocop.ymlを規定の設定から取得します
     cp $(bundle show rubocop)/config/default.yml .rubocop.yml
    
  3. その後、.rubocop.ymlの最初に下記を追記します
     require: rubocop-rspec
        
    
  4. 後はいつも通りrubocopを実行します
     bundle exec rubocop -R -D -c .rubocop.yml
    

これでRSpecも含めたコーディング規約チェックができます。

このうちプロジェクトで不要なチェックがあった場合、.rubocop.ymlで無効化します。 最初から作るプロジェクトの場合はそのままでも問題ないかもしれませんが、今回実験したプロジェクトでは.rubocop.yml下記のような設定を施しています。

RSpec/NestedGroups:
  Enabled: false

RSpec/ContextWording:
  Enabled: false

RSpec/ExampleWording:
  Enabled: false

RSpec/MessageChain:
  Enabled: false

RSpec/NamedSubject:
  Enabled: false

RSpec/MultipleExpectations:
  Enabled: false

RSpec/ExampleLength:
  Max: 10

ただ、この設定はあくまでも現段階の一例でしかなく、プロジェクトメンバーの力量によって値をチューニングしながら適用していく必要があります。

今年は色々あってrubocopのサンプルをいくつか作ったので、もしよければ活用してみてください。

https://github.com/tk-hamaguchi/docs/tree/master/rubocop%E3%81%AB%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF

Istioにはリクエストパスごとに割り振るサービスを設定することができます。 例えば、/api/v1/hoge はAのサービスに、/api/v2/fuga はBのサービスに割り振るといった設定です。

これらの機能について、前回の記事『 helmパッケージ化されたアプリをKubernetes+Istioを使って公開する 』で作った環境とhelmパッケージを使って実現方法をまとめていきます。

まず前回のおさらいですが、今の所全てのリクエストがfoolish-penguin-sampleか、peeking-rabbit-sampleのサービスに流れる設定になっています。

for i in `seq 0 9` ; do curl 127.0.0.1.xip.io ; done

コマンド実行結果

もちろんこれは/hoge/fugaに対するアクセスにも適用されます。

コマンド実行結果

今回はこの/hoge/配下のに対するリクエストを他のサービスに割り振る設定をしていきます。

シンプルなHTTPルーティングの実現

HTTPルーティングは、VirtualServiceのリソースを使って設定します。

まずはルーティング先のServiceを作ります。 前回作成したsampleのチャートが含まれているフォルダに移動し、新規にデプロイします。 今回も説明の都合上、リリース名をangry-swanに固定します。

cd ./sample
helm install --name=angry-swan .

コマンド実行結果

次にsample-gatewayのフォルダに移動し、VirtualServerに設定を書き加えます。

cd ../sample-gateway
vi templates/virtualservice.yaml
vi Chart.yaml
git diff
git add .
git commit -m "Added http routing for angry-swan."
git tag 0.2.0

コマンド実行結果

書き加えた設定を反映させるため、sample-gatewayをアップグレードします

helm upgrade exciting-lemur .

コマンド実行結果

これで期待通りのルーティングになっているか確認していきます。 確認には前回同様のcurlに加えて、今回確認したい/hoge/fugaに対するリクエストを実行し、さらに無関係な/mokeにリクエストを実行して確認していきます。

for i in `seq 0 9` ; do curl 127.0.0.1.xip.io ; done
curl 127.0.0.1.xip.io/hoge/fuga
curl 127.0.0.1.xip.io/moke

コマンド実行結果

結果の通り、/へのリクエストはfoolish-penguin-samplepeeking-rabbit-sampleに、/hoge/fugaangry-swan-sampleに、/moke/のマッチが適用されてfoolish-penguin-samplepeeking-rabbit-sampleに振り分けられている事が確認できました。

ポッドに対するログも見ていきます。

export POD_NAME=$(kubectl get pods --namespace default -l "app=sample,release=angry-swan" -o jsonpath="{.items[0].metadata.name}")
kubectl logs -f $POD_NAME sample | grep -v kube-probe

コマンド実行結果

ログを読むとangry-swan-sampleからのリクエストがそのまま/hoge/fugaに対して実行されていることが確認できます。 このログのフォローもCtrl+cで終了する事ができます。

パスのリライトを含んだHTTPルーティング

サービスに対してリクエストをルーティングする際、パスを書き換えて実行する機能がIstioには備わっています。 例えば、/api/hoge/v1/fugaはAのサービスにルーティングするけど、その際にリクエストパスを/api/fugaに変更してサービスに渡すといった事ができるようになります。

今回は例の通り、/api/hoge/v1/fugaに対するリクエストをangry-swan-sampleに、パスを/api/fugaに変更しつつ渡す設定をします。

リライトの設定はVirtualServiceに対して入れていきます。

vi templates/virtualservice.yaml
vi Chart.yaml
git diff
git add .
git commit -m "Added path rewrite from '/api/hoge/v1/fuga' to '/api/fuga'."
git tag 0.2.1

コマンド実行結果

その後書き加えた設定を反映させるため、sample-gatewayをアップグレードします

helm upgrade exciting-lemur .

コマンド実行結果

実際にリクエストを投げてangry-swan-sampleにリクエストが流れていることを確認します。

curl 127.0.0.1.xip.io/api/hoge/v1/fuga

コマンド実行結果

先ほどと同様にポッドに対するログも見ていきます。

export POD_NAME=$(kubectl get pods --namespace default -l "app=sample,release=angry-swan" -o jsonpath="{.items[0].metadata.name}")
kubectl logs -f $POD_NAME sample | grep -v kube-probe

コマンド実行結果

ログの最後の一行(最新のリクエストに対するログ)を読むとリクエストパスがリライトされ、/api/fugaにリクエストが飛んでいる事が確認できます。

加えてその配下に対するアクセスに対してもリライトされた後に実行されます。

curl 127.0.0.1.xip.io/api/hoge/v1/fuga/aaaa
kubectl logs -f $POD_NAME sample | grep -v kube-probe

コマンド実行結果

上記の例では/api/hoge/v1/fuga/aaaに対するリクエストがリライトされ、/api/fuga/aaaに飛んでいる事が確認できます。

今回は以上のように、Istioでリクエストパスを使ったHTTPルーティングを設定し、動きを確認する事ができました。

これまでkubernetes上でアプリを動かす時に基本的にhelmにまとめてデプロイしてきましたが、upgradeするたびに瞬断する問題の解決と、カナリアリリースを利用したブルーグリーンデプロイメントを実現するために、既存のhelmを使ったワークロードにIstioを絡めて利用する方法を書いていきます。

IstioはKubernetes上に展開したマイクロサービスの接続をマネージメントしてくれるコンポーネントで、簡単に言うとIngressの高機能版です。

環境

今回のKubernetes環境としてはDocker for Mac with Kubernetesで動きを見ていきます。

バージョン確認

アプリは、hashicorp/http-echo を使って構築していきます。これは簡単なWebアプリケーションで、コンテナの起動引数を使ってレスポンスボディを指定できるものになります。

まずはイメージの動作確認を兼ねてdockerコマンドでコンテナを作成・起動してみます。 実行方法はUSAGEの通りです。

docker run -p 5678:5678 hashicorp/http-echo -text="hello world"

起動するとターミナルは下記のような状態になります。

docker runの実行結果

この状態でブラウザで http://localhost:5678 にアクセスすると、起動時に指定したhello worldが表示されてているはずです。

ブラウザでの表示結果

docker runで動かしたコンテナを停止する場合ははCtrl+Cを使用します。

Ctrl+Cの実行結果

helmパッケージの作成とデプロイ

次にこのイメージをkubernetes上に展開するためにhelm chartを作成して、gitのバージョン管理配下に置きます。

helm create sample
cd sample/
git init
git add .
git commit -m 'first commit'

コマンドの実行結果

作成したhelmパッケージに対し、先ほどdockerコマンドで立ち上げたようにhashicorp/http-echoを立ち上げる設定をします。編集するファイルはChart.yamlvalues.yamltemplates/deployment.yamltemplates/NOTES.txtの4つです。

vi Chart.yaml
vi values.yaml
vi templates/deployment.yaml
vi templates/NOTES.txt

コマンドの実行結果

編集内容は下記の通りです。

git diff

コマンドの実行結果

編集が完了したらコミットし、タグを付けます。

git commit -a -m "bump version to 1.0.0"
git tag 1.0.0

コマンドの実行結果

作成したhelmパッケージをインストールしていきます。今回は説明の都合上、release nameをfoolish-penguinに固定します。

helm install --name=foolish-penguin .

コマンドの実行結果

表示されたNOTESを元にポートフォワーディングを設定してインストールされたpodにアクセスできるかどうか確認します。

コマンドの実行結果

ブラウザで http://localhost:5678 にアクセスし、"hello world v1"と表示されればOKです。

ブラウザの表示結果

このポートフォワーディングもdocker run同様Ctrl+Cで終了することができます。

コマンドの実行結果

ちなみに今回はDeploymentを作成しているので、Deploymentに対してポートフォワーディングを設定することもできます。

kubectl port-forward deployment/foolish-penguin-sample 8081:5678

コマンドの実行結果 ブラウザの表示結果

Istioを利用する場合はServiceに対してルーティングすることになるので、Serviceへもポートフォワーディングを設定して動作を確認しておきます。

kubectl port-forward svc/foolish-penguin-sample 8082:80

コマンドの実行結果 ブラウザの表示結果

一通り確認し終わったらCtrl+Cで終了し、1つ上のディレクトリまで戻ります。

cd ..

コマンドの実行結果

Istioのダウンロードとhelmを使ったインストール

次にIstioの最新版をダウンロードし、ローカルに作成されだディレクトリの中へ移動します。 この記事をまとめている時点では1.0.1が最新になっています。 (参考:https://istio.io/docs/setup/kubernetes/download-release/ )

curl -L https://git.io/getLatestIstio | sh -
cd istio-*/

コマンドの実行結果

helmを実行するためのサービスアカウントをcluster-adminにアップデートし、Istioのインストールを行います。

kubectl apply -f install/kubernetes/helm/helm-service-account.yaml
helm init --service-account tiller --upgrade
helm install install/kubernetes/helm/istio --name istio --namespace istio-system

Istioを構成する様々なリソースがインストールされます。インストールされたリソースは下記のコマンドで確認することができます。

kubectl -n istio-system get all

コマンドの実行結果

インストールが完了したら1つ上のディレクトリまで戻ります。

cd ..

コマンドの実行結果

helmパッケージのIstio対応

ベースとなるhelmパッケージを作成し、同様にGitの管理下に置きます。

helm create sample-gateway
cd sample-gateway/
git init
git add .
git commit -a -m 'first commit'

コマンドの実行結果

その後不要なファイルを削除し、IstioのGatewayとVirtualServiceを作成します。 この時、VirtualServiceの destination -> host の値を上記で作成したサービスの名前にするのがポイントです。

rm -f templates/*.yaml templates/NOTES.txt

cat << 'EOF' > values.yaml
nameOverride: ""
fullnameOverride: ""

istio:
  hosts:
    - 127.0.0.1.xip.io
EOF

cat << 'EOF' > templates/gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: {{ include "sample-gateway.fullname" . }}
  labels:
    app: {{ include "sample-gateway.name" . }}
    chart: {{ include "sample-gateway.chart" . }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  selector:
    istio: ingressgateway # use Istio default gateway implementation
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    {{- range .Values.istio.hosts }}
    - {{ . | quote }}
    {{- end }}
EOF

cat << 'EOF' > templates/virtualservice.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: {{ include "sample-gateway.fullname" . }}
  labels:
    app: {{ include "sample-gateway.name" . }}
    chart: {{ include "sample-gateway.chart" . }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  hosts:
  {{- range .Values.istio.hosts }}
  - {{ . | quote }}
  {{- end }}
  gateways:
  - {{ include "sample-gateway.fullname" . }}
  http:
  - route:
    - destination:
        host: foolish-penguin-sample
EOF

コマンドの実行結果

そしてChart.yamlを編集し、変更内容のコミットとタグ打ちをします。

vi Chart.yaml
git diff Chart.yaml
git add .
git commit -a -m "bump version to 0.1.0"
git tag 0.1.0

コマンドの実行結果

helmコマンドを使ってデプロイします。今回も説明の都合上、リリース名をexciting-lemurに固定します。

helm install --name=exciting-lemur .

コマンドの実行結果

Istioの自動注入機能を有効にするため、istio-injectionをdefaultネームスーペースに対して有効にする

kubectl get namespace -L istio-injection
kubectl label namespace default istio-injection=enabled
kubectl get namespace -L istio-injection

コマンドの実行結果

既存のhelmパッケージをIstioで利用するためには上記の注入機能を有効にした上でデプロイする必要があります。アップデートついでにレスポンスボディも変えておきます。

cd ../sample
vi Chart.yaml
vi templates/deployment.yaml
git diff
git commit -a -m "bump version to 1.0.1"
git tag 1.0.1

コマンドの実行結果

更新したパッケージをデプロイします。

helm upgrade foolish-penguin .

コマンドの実行結果

ブラウザで http://127.0.0.1.xip.io:80 を開き、更新されたサービスにistio経由で表示されていることを確認します。この時先ほどのアップデートが適用されていることをレスポンスボディを使って判断します。

ブラウザの表示結果

アプリケーションのバージョンアップとカナリアリリース

本来はイメージのタグを変えるが今回はスキップしてレスポンスボディの変更で代用します。

vi Chart.yaml
git diff
git commit -a -m "bump version to 1.1.0"
git tag 1.1.0

コマンドの実行結果

新しいバージョンのアプリケーションをデプロイします。 この時新しいリリース名をつけてインストールします。 今回も説明の都合上、リリース名をpeeking-rabbitに固定します。

helm install --name=peeking-rabbit .

コマンドの実行結果

もちろんこの時点で http://127.0.0.1.xip.io:80 に変化はありません。

ブラウザの表示結果

新しいバージョンのServiceに対してポートフォワーディングを設定し、デプロイできているか確認して見ます。

kubectl port-forward svc/peeking-rabbit-sample 8083:80

コマンドの実行結果

ブラウザで http://localhost:8083 にアクセスし、hello world peeking-rabbitと表示されていれば成功です。

ブラウザの表示結果

一通り確認し終わったらCtrl+Cで終了し、sample-gatewayのディレクトリまで戻ります。

cd ../sample-gateway

コマンドの実行結果

新しいバージョンにリクエストの半分を割り当てるよう設定を追加し、バージョンを上げます。

vi Chart.yaml
vi templates/virtualservice.yaml
git diff
git commit -a -m "bump version to 0.1.1"
git tag 0.1.1

コマンドの実行結果

Istioの設定を更新するためにhelmコマンドでデプロイします。

helm upgrade exciting-lemur .

コマンドの実行結果

その後ブラウザで http://127.0.0.1.xip.io:80 に何度かアクセスし、表示されているページが変化することを確認できたら完了です。

ブラウザの表示結果 ブラウザの表示結果

試しにcurlでHTTPリクエストを10回実行すると、ほぼ指定したウェイト通りに分配されていることが確認できます。

for i in `seq 0 9` ; do curl 127.0.0.1.xip.io ; done

コマンドの実行結果

以上でhelmパッケージのIstio対応とカナリアリリースの設定が完了しました。

MacでRaspberryPiのインストールと初期設定を行う手順のメモです。

主にMacのターミナルからのコマンドベースの設定になります。

気がついたらRaspbianもsystemdベースになっていたので、以前作ったRaspberryPi用のRecipeをベースにいくつか追加しています。

Raspbianをダウンロードする

RaspbianのダウンロードサイトからRASPBIAN JESSIE LITEのZipをダウンロードします

Safariでダウンロードした場合、ダウンロード完了後に解凍されたものが~/Download配下に作成されたはずです。

bash-3.2$ ls ~/Downloads/2016-11-25-raspbian-jessie-lite.img

SDカードへイメージを書き込む

RaspberryPiで使うMicroSDをメモリーカードリーダーを使ってMacに接続し、フォーマットを実行します

まずMac上でdiskutilコマンドを実行し、MicroSDの状態とパスを確認します。

bash-3.2$ diskutil list 
/dev/disk0 (internal, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *960.2 GB   disk0
   1:                        EFI EFI                     209.7 MB   disk0s1
   2:                  Apple_HFS Macintosh SSD           839.3 GB   disk0s2
   3:                 Apple_Boot Recovery HD             650.0 MB   disk0s3
   4:       Microsoft Basic Data BOOTCAMP                120.0 GB   disk0s4
/dev/disk1 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:                                                   *31.7 GB    disk1
bash-3.2$ 

上記の場合/dev/disk1にマッピングされています。

もしディスクがマウントされている場合は下記の手順でアンマウントします。

bash-3.2$ sudo diskutil unmountDisk /dev/disk1

続けてRaspbianのイメージを書き込みます

bash-3.2$ sudo dd bs=1m if=~/2016-11-25-raspbian-jessie-lite.img  of=/dev/disk1
1326+0 records in
1326+0 records out
1390411776 bytes transferred in 1103.233323 secs (1260306 bytes/sec)
bash-3.2$ 

書き込み完了後、MicroSDをMacから抜き出し、RaspberryPiへ差し込みます。

RaspberryPiへのシリアル接続する

まずGPIO経由でRaspberryPiとMacをシリアル接続します。

自分の場合はSWITCH SCIENCEのFTDI USBシリアル変換アダプターを使って下記のように配線しています。

RaspberryPi側 USBシリアル変換アダプター側
6 : Ground GND
8 : GPIO14 (TXD) RX
10 : GPIO15 (RXD) TX

リンク先の通りFTDIのドライバが必要になるので、Macにインストールしておく必要があります。

そしてその後MicroUSBケーブルでUSBシリアル変換アダプターとMacを繋ぎます。

シリアルへの接続は、Macの場合、screenコマンドを利用して接続します。

bash-3.2$ sudo screen  /dev/tty.usbserial-* 115200

RaspberryPiの電源を入れると、screen上に起動シーケンスが表示され、ログインプロンプトが表示されます。

Raspbian GNU/Linux 8 raspberrypi ttyAMA0

raspberrypi login:

RaspberryerryPiにログインする

ここで下記を入力します

Input field Value
raspberrypi login pi
Password raspberry

するとpiユーザーとしてログイン出来ます。

raspberrypi login: pi
Password: 
Linux raspberrypi 4.4.34-v7+ #930 SMP Wed Nov 23 15:20:41 GMT 2016 armv7l

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
pi@raspberrypi:~$ 

その後rootユーザーに切り替えます

pi@raspberrypi:~$ sudo su -
root@raspberrypi:~# 

swap領域の無効化

Raspbianはswap領域としてSDカードを利用します。これはSDカードの寿命を縮める原因の一つとなるため、無効化します。

まずswapを一時的に無効化し、データをメモリ上に移動します。

root@raspberrypi:~# swapoff --all
root@raspberrypi:~# 

次にdphys-swapfileを停止した後パッケージを削除し、恒久的にswapファイルを作成しないように変更します。

root@raspberrypi:~# systemctl stop dphys-swapfile
root@raspberrypi:~# systemctl disable dphys-swapfile
Synchronizing state for dphys-swapfile.service with sysvinit using update-rc.d...
Executing /usr/sbin/update-rc.d dphys-swapfile defaults
Executing /usr/sbin/update-rc.d dphys-swapfile disable
insserv: warning: current start runlevel(s) (empty) of script `dphys-swapfile' overrides LSB defaults (2 3 4 5).
insserv: warning: current stop runlevel(s) (2 3 4 5) of script `dphys-swapfile' overrides LSB defaults (empty).
root@raspberrypi:~# apt-get remove -y dphys-swapfile
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following package was automatically installed and is no longer required:
  dc
Use 'apt-get autoremove' to remove it.
The following packages will be REMOVED:
  dphys-swapfile
0 upgraded, 0 newly installed, 1 to remove and 0 not upgraded.
After this operation, 85.0 kB disk space will be freed.
Do you want to continue? [Y/n] 
(Reading database ... 31272 files and directories currently installed.)
Removing dphys-swapfile (20100506-1) ...
Processing triggers for man-db (2.7.0.2-5) ...
root@raspberrypi:~# 

時刻同期ツールの設定

RaspberryPiが起動するタイミングでNTPによる時刻同期を行うよう、にする。実際にはNTPデーモンを停止し、起動シーケンスの中でntpdateコマンドにより時刻同期を実現する。

まずNTPデーモンを停止します

root@raspberrypi:~# systemctl stop ntp
root@raspberrypi:~# systemctl disable ntp
Synchronizing state for ntp.service with sysvinit using update-rc.d...
Executing /usr/sbin/update-rc.d ntp defaults
Executing /usr/sbin/update-rc.d ntp disable
insserv: warning: current start runlevel(s) (empty) of script `ntp' overrides LSB defaults (2 3 4 5).
insserv: warning: current stop runlevel(s) (2 3 4 5) of script `ntp' overrides LSB defaults (empty).
root@raspberrypi:~# 

次にntpdateコマンドをインストールします

root@raspberrypi:~# apt-get install -y ntpdate
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following extra packages will be installed:
  liblockfile-bin liblockfile1 lockfile-progs
The following NEW packages will be installed:
  liblockfile-bin liblockfile1 lockfile-progs ntpdate
0 upgraded, 4 newly installed, 0 to remove and 0 not upgraded.
Need to get 112 kB of archives.
After this operation, 250 kB of additional disk space will be used.
Get:1 http://mirrordirector.raspbian.org/raspbian/ jessie/main liblockfile-bin armhf 1.09-6 [18.2 kB]
Get:2 http://mirrordirector.raspbian.org/raspbian/ jessie/main liblockfile1 armhf 1.09-6 [14.7 kB]
Get:3 http://mirrordirector.raspbian.org/raspbian/ jessie/main ntpdate armhf 1:4.2.6.p5+dfsg-7+deb8u2 [69.0 kB]
Get:4 http://mirrordirector.raspbian.org/raspbian/ jessie/main lockfile-progs armhf 0.1.17 [10.6 kB]
Fetched 112 kB in 2s (38.4 kB/s)     
Selecting previously unselected package liblockfile-bin.
(Reading database ... 31253 files and directories currently installed.)
Preparing to unpack .../liblockfile-bin_1.09-6_armhf.deb ...
Unpacking liblockfile-bin (1.09-6) ...
Selecting previously unselected package liblockfile1:armhf.
Preparing to unpack .../liblockfile1_1.09-6_armhf.deb ...
Unpacking liblockfile1:armhf (1.09-6) ...
Selecting previously unselected package ntpdate.
Preparing to unpack .../ntpdate_1%3a4.2.6.p5+dfsg-7+deb8u2_armhf.deb ...
Unpacking ntpdate (1:4.2.6.p5+dfsg-7+deb8u2) ...
Selecting previously unselected package lockfile-progs.
Preparing to unpack .../lockfile-progs_0.1.17_armhf.deb ...
Unpacking lockfile-progs (0.1.17) ...
Processing triggers for man-db (2.7.0.2-5) ...
Setting up liblockfile-bin (1.09-6) ...
Setting up liblockfile1:armhf (1.09-6) ...
Setting up ntpdate (1:4.2.6.p5+dfsg-7+deb8u2) ...
Setting up lockfile-progs (0.1.17) ...
Processing triggers for libc-bin (2.19-18+deb8u6) ...
root@raspberrypi:~# 

起動時シーケンスを設定

/etc/fstab/etc/rc.localを編集して起動シーケンスを設定します。

SDカードの寿命を少しでも延ばすため、ログデータやテンポラリファイルをtmpfs上に作成するよう変更します

root@raspberrypi:~# cat << EOT >> /etc/fstab
tmpfs           /tmp            tmpfs   defaults,size=32m,noatime,mode=1777      0       0
tmpfs           /var/tmp        tmpfs   defaults,size=16m,noatime,mode=1777      0       0
tmpfs           /var/log        tmpfs   defaults,size=32m,noatime,mode=0755      0       0
EOT
root@raspberrypi:~# 

次に起動時に時刻同期と、必要なディレクトリ作成を行います。

root@raspberrypi:~# cat << EOT > /etc/rc.local
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
  ntpdate ntp.nict.jp
fi

mkdir -p /var/log/ConsoleKit
mkdir -p /var/log/samba
mkdir -p /var/log/fsck
mkdir -p /var/log/apt
mkdir -p /var/log/ntpstats
chown root.ntp /var/log/ntpstats
chown root.adm /var/log/samba
touch /var/log/lastlog
touch /var/log/wtmp
touch /var/log/btmp
chown root.utmp /var/log/lastlog
chown root.utmp /var/log/wtmp
chown root.utmp /var/log/btmp

exit 0
EOT
root@raspberrypi:~# 

RaspberryPiのroot領域を拡大する

raspi-configコマンドで領域をいっぱいいっぱいまで拡げます。

root@raspberrypi:~# raspi-config --expand-rootfs
root@raspberrypi:~# 

再起動の実行

ここまできたらとりあえず再起動を行います

root@raspberrypi:~# reboot

再起動後は前回同様ログインし、rootユーザーに切り替えます

Raspbian GNU/Linux 8 raspberrypi ttyAMA0

raspberrypi login: pi
Password: 
Linux raspberrypi 4.4.34-v7+ #930 SMP Wed Nov 23 15:20:41 GMT 2016 armv7l

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
pi@raspberrypi:~$ sudo su -
root@raspberrypi:~# 

WiFiの設定を行う

RaspberryPi3の場合はWiFiが内蔵されてます。それ以外のWiFi非搭載モデルの場合はBuffaloのWLI-UC-GNMとかを使って繋ぎます。(挿して起動するだけでwlan0として認識されます)

有線LANで接続する場合は不要ですが、WiFiを使って接続する場合、下記のようにwpa_supplicantの設定を施します。

下記の例:

設定項目 設定内容 設定値 備考
ssid SSID test-ap 接続先WiFiのSSID
proto プロトコル RSN WPA2の場合RSNでWPAの場合はそのままWPAを設定する
key_mgmt 認証方式 WPA-PSK プリシェアードキーを使って最初の通信を確立する場合
pairwise ユニキャスト通信の暗号化方式 CCMP TKIP AESを利用する場合CCMP、TKIPの場合はTKIPを指定する
group ブロードキャストまたはマルチキャスト通信の暗号化方式 CCMP TKIP AESを利用する場合CCMP、TKIPの場合はTKIPを指定する
psk WPAプリシェアードキー password_for_your_wifi 接続先WiFiのパスフレーズ

設定方法の詳細はman wpa_supplicant.confでも確認できます

root@raspberrypi:~# cat << EOT >> /etc/wpa_supplicant/wpa_supplicant.conf
network={
        ssid="test-ap"
        proto=RSN
        key_mgmt=WPA-PSK
        pairwise=CCMP TKIP
        group=CCMP TKIP
        psk="password_for_your_wifi"
}
EOT
root@raspberrypi:~# 

その後ifdownifupで無線LANのインターフェースを再起動します。

root@raspberrypi:~# ifdown wlan0
root@raspberrypi:~# ifup wlan0
root@raspberrypi:~# ip a s wlan0 | grep 'inet '
    inet 192.168.0.12/24 brd 192.168.0.255 scope global wlan0
root@raspberrypi:~# 

各種アップデートの実行

まず各パッケージをアップデートします。

root@raspberrypi:~# apt-get update -y -qq && apt-get upgrade -y -qq
root@raspberrypi:~# apt-get dist-upgrade -y -qq
root@raspberrypi:~# 

次にRaspberryPiのファームウェアをアップデートしてくれるrpi-updateをインストールします。

root@raspberrypi:~# apt-get install rpi-update
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following package was automatically installed and is no longer required:
  dc
Use 'apt-get autoremove' to remove it.
The following NEW packages will be installed:
  rpi-update
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 4,408 B of archives.
After this operation, 45.1 kB of additional disk space will be used.
Get:1 http://archive.raspberrypi.org/debian/ jessie/main rpi-update all 20140705 [4,408 B]
Fetched 4,408 B in 0s (4,976 B/s)                        
Selecting previously unselected package rpi-update.
(Reading database ... 31264 files and directories currently installed.)
Preparing to unpack .../rpi-update_20140705_all.deb ...
Unpacking rpi-update (20140705) ...
Setting up rpi-update (20140705) ...
root@raspberrypi:~# 

その後ファームウェアアップデートを実行します

root@raspberrypi:~# SKIP_WARNING=1 rpi-update                                                                                                                    
 *** Raspberry Pi firmware updater by Hexxeh, enhanced by AndrewS and Dom
 *** Performing self-update
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 12022  100 12022    0     0  67782      0 --:--:-- --:--:-- --:--:-- 67920
 *** Relaunching after update
 *** Raspberry Pi firmware updater by Hexxeh, enhanced by AndrewS and Dom
 *** We're running for the first time
 *** Backing up files (this will take a few minutes)
 *** Backing up firmware
 *** Backing up modules 4.4.34-v7+
This update bumps to rpi-4.4.y linux tree
Be aware there could be compatibility issues with some drivers
Discussion here:
https://www.raspberrypi.org/forums/viewtopic.php?f=29&t=144087
##############################################################
 *** Downloading specific firmware revision (this will take a few minutes)
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   168    0   168    0     0    198      0 --:--:-- --:--:-- --:--:--   198
100 51.8M  100 51.8M    0     0  2136k      0  0:00:24  0:00:24 --:--:-- 2371k
 *** Updating firmware
 *** Updating kernel modules
 *** depmod 4.4.38-v7+
 *** depmod 4.4.38+
 *** Updating VideoCore libraries
 *** Using HardFP libraries
 *** Updating SDK
 *** Running ldconfig
 *** Storing current firmware revision
 *** Deleting downloaded files
 *** Syncing changes to disk
 *** If no errors appeared, your firmware was successfully updated to 87bf11faa6ccefb7969821bff8cbc44f72be7414
 *** A reboot is needed to activate the new firmware
root@raspberrypi:~# 

最後に不要なパッケージを整理します

root@raspberrypi:~# apt-get autoremove -y -qq
The mail frontend needs a installed 'sendmail', using pager
(Reading database ... 31268 files and directories currently installed.)
Removing dc (1.06.95-9) ...
Processing triggers for install-info (5.2.0.dfsg.1-6) ...
Processing triggers for man-db (2.7.0.2-5) ...
root@raspberrypi:~# apt-get autoclean -y -qq
root@raspberrypi:~# 

不要なサービスの停止

RaspberryPiの起動時間を短縮するため、不要なサービスを停止します

root@raspberrypi:~# systemctl stop avahi-daemon
root@raspberrypi:~# systemctl disable avahi-daemon
Synchronizing state for avahi-daemon.service with sysvinit using update-rc.d...
Executing /usr/sbin/update-rc.d avahi-daemon defaults
Executing /usr/sbin/update-rc.d avahi-daemon disable
insserv: warning: current start runlevel(s) (empty) of script `avahi-daemon' overrides LSB defaults (2 3 4 5).
insserv: warning: current stop runlevel(s) (0 1 2 3 4 5 6) of script `avahi-daemon' overrides LSB defaults (0 1 6).
insserv: warning: current start runlevel(s) (empty) of script `avahi-daemon' overrides LSB defaults (2 3 4 5).
insserv: warning: current stop runlevel(s) (0 1 2 3 4 5 6) of script `avahi-daemon' overrides LSB defaults (0 1 6).
Removed symlink /etc/systemd/system/dbus-org.freedesktop.Avahi.service.
Removed symlink /etc/systemd/system/sockets.target.wants/avahi-daemon.socket.
root@raspberrypi:~# 
root@raspberrypi:~# systemctl stop triggerhappy
root@raspberrypi:~# systemctl disable triggerhappy
Synchronizing state for triggerhappy.service with sysvinit using update-rc.d...
Executing /usr/sbin/update-rc.d triggerhappy defaults
Executing /usr/sbin/update-rc.d triggerhappy disable
insserv: warning: current start runlevel(s) (empty) of script `triggerhappy' overrides LSB defaults (2 3 4 5).
insserv: warning: current stop runlevel(s) (0 1 2 3 4 5 6) of script `triggerhappy' overrides LSB defaults (0 1 6).
root@raspberrypi:~# 

タイムゾーンの変更

RaspbianはデフォルトのタイムゾーンはUTCになっているため、JSTに変更します。

root@raspberrypi:~# timedatectl set-timezone Asia/Tokyo

ログイン後バナーの変更

必要に応じてログイン後に表示されるバナーを修正します。 ここに今回セットアップするRaspbianの役割やREADME的なものを書いておくと後々便利です。

root@raspberrypi:~# echo '' > /etc/motd
root@raspberrypi:~# 

SSHデーモンの有効化

SSHデーモンを起動し、シリアル接続をしなくてもつながるようにします。

root@raspberrypi:~# systemctl enable ssh
Synchronizing state for ssh.service with sysvinit using update-rc.d...
Executing /usr/sbin/update-rc.d ssh defaults
insserv: warning: current start runlevel(s) (empty) of script `ssh' overrides LSB defaults (2 3 4 5).
insserv: warning: current stop runlevel(s) (2 3 4 5) of script `ssh' overrides LSB defaults (empty).
Executing /usr/sbin/update-rc.d ssh enable
Created symlink from /etc/systemd/system/sshd.service to /lib/systemd/system/ssh.service.
root@raspberrypi:~# 
root@raspberrypi:~# systemctl start ssh
root@raspberrypi:~# 
root@raspberrypi:~# systemctl status ssh | grep Active: 
   Active: active (running) since Sat 2016-12-31 15:00:56 JST; 3min 17s ago
root@raspberrypi:~# 

仕上げに再起動する

最後に再起動して接続の確認を始めます。

root@raspberrypi:~# reboot

ネットワーク接続が成功していれば、起動中にMy IP address is 192.168.0.12 のような形でRaspberryPiのIPアドレスが表示されます。

SSHでの接続確認

ログインプロンプトが出ていることを確認したら、ターミナルをもう一つ開いてSSHしてみましょう。

下記は、RaspberryPiに192.168.0.12が割り当てられた場合の例です。

bash-3.2$ ssh 192.168.0.12 -l pi
The authenticity of host '192.168.0.12 (192.168.0.12)' can't be established.
ECDSA key fingerprint is SHA256:+rgt1e7lkFflZ4snx0boVf3uJ0mKUMjfdAKY1VlD3ms.
Are you sure you want to continue connecting (yes/no)? 

yesと応答し、パスワード入力画面でパスワードを入力します。

Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.0.12' (ECDSA) to the list of known hosts.
pi@ 192.168.0.12's password: 

パスワードはログインプロンプト同様にraspberryです。

その後、ログインできればSSHの設定完了です。

Last login: Sat Dec 31 11:33:17 2016

SSH is enabled and the default password for the 'pi' user has not been changed.
This is a security risk - please login as the 'pi' user and type 'passwd' to set a new password.

pi@raspberrypi:~ $ 

この場は一度ログアウトします。

pi@raspberrypi:~ $ logout
Connection to 192.168.0.12 closed.
bash-3.2$ 
bash-3.2$ exit

シリアル接続の切断

SSHで接続できるようになったので、screenを終了し、シリアル接続を切断します。

ログインプロンプトが出ているターミナルで、Control+aを入力した後、kを入力します。

画面の下にReally kill this window [y/n]と出てくるので、yを入力します

Raspbian GNU/Linux 8 raspberrypi ttyAMA0

raspberrypi login:




Really kill this window [y/n] y
[screen is terminating]
bash-3.2$ 

screenが切断され、元のターミナルに戻ってきます。

ここまできたらGPIOからシリアルアダプタを取り外して完了です。

今後

今後はSSHベースでの作業になるので、パスワードの変更等必要に応じて行いましょう。

いろいろな書き方ができるRuby。そのRubyを取り巻くコーディングポリシーの状況やツールについて、この回では書いていきたいと思います。

以前に某AdventCalendarに載せた内容だけどRubyStyleGuideもRubocopもその時に比べて変更されてきているので、改めて内容の反映した上でこっちに掲載。

Rubyの特徴とベストプラクティス

Rubyは1つの目的を達成するために複数のアプローチが可能な言語です。例えば配列の長さを取得したいときにarray.sizearray.lengthのどちらを使っても、配列の長さを取得することができます。これはもともと他の言語を使っていた人がRubyに親しみやすくなる要因の一つとなっているのですが、ある程度大きな開発をするときにはコーディングポリシーをしっかり決めておかないと、プログラムを書いた人によっては癖が強すぎて読みづらい事態が起きたり、最悪の場合追加開発の時に元のコードを読み違えて、バグの誘発につながるリスクもあります。

それらのリスクを低減し、他のRubyプログラマーが保守できるコードを書くためのベストプラクティスとして、The Ruby Style Guideというものが規定されています。

しかし定義されているポリシーは膨大で、気をつけていたつもりでも実は準拠していないコードを書いてしまうケースもあるでしょう。

そんな時に役にたつツールの一つとして、RuboCopがあります。

静的解析ツール:RuboCop

RuboCopは、あらかじめ決められたコーディングポリシーを元に、それに対して違反していないか、違反していた場合どう直すべきかの指標を示してくれるツールの1つです。

インストールも簡単で、下記のコマンドでインストールを行えます。

$ gem install rubocop --no-ri --no-rdoc
Successfully installed rubocop-0.27.1
1 gem installed

実際に下記のようなrubyファイルを作成し、rubocopコマンドで検査してみましょう

rubyファイル(sample.rb)

def aaaa
    bbbb
end

チェックの実行

$ rubocop sample.rb 
Inspecting 1 file
C

Offenses:

sample.rb:2:1: C: Use 2 (not 6) spaces for indentation.
      bbbb
^^^^^^

1 file inspected, 1 offense detected

実行してみるとエラーが出ます。 これはインデントが半角スペース2つ(RubyStyleGuide準拠)ではないために起こっているエラーです。

これを半角スペース2つに直すと、今度は無事に実行完了となるはずです。

$ rubocop sample.rb 
Inspecting 1 file
.

1 file inspected, no offenses detected

RuboCop活用する

さきほどは保存したファイルに対してRuboCopを実行しましたが、保存する前段階にエディタに警告を表示してくれるプラグインもあったりします。

また、Jenkinsを活用している場合、rubocop-checkstyle_formatterJenkinsViolationsPluginを組み合わせることで、Jenkinsのジョブ上に警告を表示させることもできます。

RuboCopとチーム開発

RuboCopは上記ツール類と組み合わせることで、チームとしてのコーディングポリシーをある程度統一することができます。ただ、各々のプロジェクトチームのこれまでの積み重ねであったり、文化的な背景であったりで、「どうしてもこの記述は合わない」といったような例外事項が出てくると思います

そんなときのためにRuboCopには設定ファイルでポリシーを制御する機能があります。プロジェクトのルートフォルダに.rubocop.ymlという名前で設定を記述することで、チームにとって最適なポリシーでコーディングすることができます。設定ファイルの内容はconfig/default.ymlを元に記述します。

ただ、各設定項目がRuby Style Guideのどの部分に該当するのかを日本語でまとめたところがなかったので、デフォルトでの有効無効設定と合わせてまとめてみました。(2016/10/01現在)

RubyStyleGuideでの記述 RubocopのCop デフォルト値が有効
ソースファイルのエンコーディングにはUTF-8を用いましょう。 Style/Encoding:  
インデントにはスペース2つを用いましょう(別名ソフトタブ)。ハードタブを用いてはいけません。 Style/IndentationWidth:
Unix-styleの改行にしましょう。(*BSD/Solaris/Linux/OSXユーザーはデフォルトで設定されています。Windowsユーザーは特に注意が必要です。) Style/EndOfLine:
命令文や式の区切りに;を用いてはいけません。当然、1行につき式1つにしましょう。 Style/Semicolon:
本文のないクラスは1行のフォーマットを用いましょう。 #N/A  
1行のメソッドは避けましょう。この用法は実際にはよく見かけるものではあるのですが、構文に奇妙な点があるため、使用は望ましくないです。仮に使うとしても、1行メソッドに含めるのは多くとも式1つまでにすべきです。本文が空のメソッドはこのルールの例外です。 Style/SingleLineMethods:
演算子の前後、コンマ、コロン、セミコロンの後ろ、{の前後、}の前にはスペースを入れましょう。スペースはRubyのインタープリタには(ほとんどの場合)重要ではありませんが、スペースの適切な使用は、読みやすいコードを書くための鍵です。/演算子についてただひとつの例外は、指数演算子です:/{と}については多少の解説が必要でしょう。ブロック、ハッシュリテラル、そして文字列埋め込み式にそれぞれ使われるからです。ハッシュリテラルでは、2つのスタイルが許容できます。/1つ目の書き方は、わずかながら少し読みやすいです(そして、Rubyコミュニティでより広く使われているのはこちらかもしれません)。2つ目の書き方は、ブロックとハッシュを視覚的に差別化できるという点で有利です。どちらでも片方を採用すれば、常に同じ方式を採用しましょう。 Style/SpaceAfterColon:
(、[の後と、]、)の前にはスペースは入れません。 Style/SpaceInsideBrackets:
!の後にはスペースは入れません。 Style/SpaceAfterNot:
範囲リテラルの内側にスペースは入れません。 Style/SpaceInsideRangeLiteral:
whenはcaseと同じ深さに揃えましょう。このスタイルは”TheRubyProgrammingLanguage”、”ProgrammingRuby”双方で確立されたものです。 Style/CaseIndentation:
条件式を変数に代入するときは、条件分岐のインデントは普段と同じ基準で揃えましょう。 Style/MultilineAssignmentLayout:  
メソッド定義式の間には空行をいれ、メソッド同士は論理的なかたまりに分けましょう。 Style/EmptyLineBetweenDefs:
メソッド呼び出しの最後の引数の後ろのコンマは避けましょう。引数が複数行にわかれていない時は、特に避けましょう。 Style/TrailingCommaInArguments:
メソッドの引数に初期値を割り当てるとき、=演算子の周りにはスペースを入れましょう。/いくつかのRuby本は最初のスタイルを提案していますが、2つ目の方が、実用的により優れています(そして、おそらくこちらのほうが読みやすいでしょう)。 Style/SpaceAroundEqualsInParameterDefault:
\を用いた行の継続は可能であれば避けましょう。可能であればというのは、つまり、文字列連結以外のすべての場合でです。 #N/A  
一貫した複数行のメソッドチェーンのスタイルを採用しましょう。Rubyコミュニティには2つのよく使われるスタイル-先頭に.を付けるもの(OptionA)、末尾に.を付けるもの(OptionB)-があり、どちらも良いと考えられています。 Style/DotPosition:
メソッド呼び出しが複数行に及ぶときは、引数は揃えましょう。1行の長さの制約のために、引数を揃えるのに適していない時は、最初の引数以降をインデント1つ分で揃えるスタイルも許容できます。 Style/AlignParameters:
複数行に及ぶ配列は、要素を揃えましょう。 Style/AlignArray:
可読性のため、大きな数値にはアンダースコアをつけましょう。 Style/NumericLiterals:
APIドキュメントを書くなら、RDocとその規約に従いましょう。コメント行とdefの間に空行を入れてはいけません。 #N/A  
1行は80字までにしましょう。 Metrics/LineLength:
行末のスペースは避けましょう。 Style/TrailingWhitespace:
ファイルの終端には改行を入れましょう。 Style/TrailingBlankLines:
ブロックコメントは使ってはいけません。前にスペースが入ると機能しませんし、通常のコメントと違い、簡単に見分けが付きません。 Style/BlockComments:
::は、定数(クラスやモジュールも含みます)やコンストラクタ(例えばArray()やNokogiri::HTML())を参照するときにのみ使いましょう。通常のメソッド呼び出しでは::の使用は避けましょう。 Style/ColonMethodCall:
引数があるとき、defは括弧と共に使いましょう。引数がない場合は括弧は除きましょう。 Style/DefWithParentheses:
オプショナル引数は引数リストの最後に定義しましょう。引数リストの先頭にオプショナル引数があるメソッドを呼んだ場合、Rubyの挙動は予測不能です。 Style/OptionalArguments:
変数を定義するために多重代入を使うのは避けましょう。多重代入を使っていいのはメソッド戻り値を変数に代入する時、splat演算子とともに使う時、変数の値を相互に入れ替えたい時に限ります。多重代入は代入をそれぞれ別に実施した場合と比べて可読性に劣ります。 Style/ParallelAssignment:
多重代入においては不要なアンダースコア変数を後ろに並べないようにしましょう。アンダースコア変数は左辺にsplat変数を定義するときには必要です。その場合、splat変数はアンダースコアではありえないです。 #N/A  
forは、どうしても使わなければいけない明確な理由が明言できる人以外は、使ってはいけません。多くの場合は代わりにイテレータを使うべきです。forはeachをつかって実装されています(だから、より遠回しです)が、forは(eachと違い)新しいスコープを導入せず、そのブロック内で定義された変数は、ブロックの外からも見えます。 Style/For:
thenは複数行にまたがるif/unlessでは使ってはいけません。 Style/MultilineIfThen:
複数行にまたがるif/unlessでは、条件式は常にif/unlessと同じ行に置きましょう。 Lint/ConditionPosition:
三項演算子(?:)をif/then/else/end構文よりも優先的に使いましょう。そちらの方がより一般的だし、あきらかに簡潔です。 Style/OneLineConditional:
三項演算子の1つの分岐には1つだけ式を入れましょう。つまり、三項演算子はネストしてはいけません。そのようなケースではif/elseの方がよいです。 Style/NestedTernaryOperator:
ifx;…を使ってはいけません。代わりに三項演算子を使いましょう。 Style/IfWithSemicolon:
ifやcaseが式で、値を返すという事実を活用しましょう。 #N/A  
1行のcase文ではwhenxthen…を使いましょう。代わりの表現であるwhenx:…は、Ruby1.9で廃止されました。 Style/WhenThen:
whenx;…を使ってはいけません。 #N/A  
notの代わりに!を使いましょう。 Style/Not:
!!は避けましょう。 Style/DoubleNegation:
andとorの使用は禁止です。使うべき理由がないです。常に、代わりに&&と||を使いましょう。 Style/AndOr:
複数行にまたがる三項演算子?:は避けましょう;代わりにif/unlessを使いましょう。 Style/MultilineTernaryOperator:
本文が1行のときは、if/unless修飾子を優先的に使いましょう。他の良い代替案としては&&/||を使った制御構文があります。 Style/IfUnlessModifier:
複数行に渡るような些細とは言えない規模のブロックにif/unless修飾子を用いるのは避けましょう。 #N/A  
if/unless/while/until修飾子をネストして利用しないようにしましょう。可能であれば&&/||を使いましょう。 Style/NestedModifier:
否定形のときはifよりunlessを優先的に使いましょう。(もしくは||構文を使いましょう)。 Style/NegatedIf:
unlessをelse付きで使ってはいけません。肯定条件を先にして書き換えましょう。 Style/UnlessElse:
if/unless/while/untilの条件式の周囲を括弧で括らないようにしましょう。/留意しなければならないのは、このルールには例外(条件式中の安全な代入)があるということです。 Style/ParenthesesAroundCondition:
複数行のwhile/untilでは、while/untilconditiondoを使ってはいけません。 Style/WhileUntilDo:
本文が1行のときは、while/until修飾子を利用しましょう。 Style/WhileUntilModifier:
否定形のときは、whileよりもuntilを使いましょう。 Style/NegatedWhile:
無限ループが必要な時は、while/untilの代わりにKernel#loopを用いましょう。 Style/InfiniteLoop:
後判定ループの場合、begin/end/untilやbegin/end/whileより、break付きのKernel#loopを使いましょう。 Lint/Loop:
内部DSL(例えばRake、Rails、RSpec)や、Rubyで「キーワード」と認識されているメソッド(例えばattr_readerやputs)や、アトリビュートにアクセスするメソッドでは、引数の周りの括弧を省略しましょう。それ以外のすべてのメソッドでは、メソッド呼び出しの時に括弧を付けましょう。 #N/A  
暗黙のオプションハッシュの外側の括弧は省略しましょう。 #N/A  
内部DSLの一部として使われるメソッドの引数では、外側の括弧類は省略しましょう #N/A  
引数のないメソッド呼び出しの括弧は省略しましょう。 #N/A  
ブロック内で呼び出されるメソッドがただ1つである場合、簡略化されたproc呼び出しを用いましょう。 #N/A  
1行のブロックではdo…endより{…}を使いましょう。複数行のブロックでは{…}は避けましょう(複数行のメソッドチェーンは常に醜いです)。制御構文(的な用法)やメソッド定義(的な用法)では常にdo…endを使いましょう(例えばRakefilesや特定のDSLなど)メソッドチェーンでのdo…endは避けましょう。/{…}を用いた複数行のメソッドチェーンをOKと主張する人もいるかもしれないが、自問してみてほしい-そのコードは本当に読みやすいだろうか?また、そのブロックの中はメソッドに切り出すことはできないのか? Style/BlockDelimiters:
単に他のブロックに引数を渡すだけのブロックリテラルを避けるため、ブロック引数を明示することを検討しましょう。ただしブロックがProcに変換されることでのパフォーマンスに気をつけましょう。 #N/A  
制御構文上不要なreturnは避けましょう。 Style/RedundantReturn:
不要なselfは避けましょう(selfのアクセサへの書き込みでのみ必要です)。 Style/RedundantSelf:
当然の帰結として、ローカル変数でメソッドをシャドウイングするのは、それらが等価なものでない限り避けましょう。 #N/A  
代入部分を括弧で囲まずに、=の返り値を条件式に用いてはいけません。これは、Rubyistの中では条件式内での安全な代入としてとても有名です。 Lint/AssignmentInCondition:
利用できるときには省略された自己代入演算子を用いましょう。 Style/SelfAssignment:
変数がまだ初期化されていないときにだけ初期化したいのであれば、||=を使いましょう。 #N/A  
boolean変数には||=を用いてはいけません(現在の値がfalseであったときに何が起こるか考えてみましょう)。 #N/A  
値が入っているかわからない変数の前処理のは&&=を用いましょう。&&=を使えば変数が存在するときのみ値を変更するので、存在確認に用いている不要なifを除去できます。 #N/A  
case等価演算子===の露骨な使用は避けましょう。その名が示す通り、caseの条件判定で用いられており、その外で用いられると混乱のもとになります。 Style/CaseEquality:
==で用が足りるならeql?は使わないようにしましょう。eq?で実現されている、より厳密な等価性が必要になることは、実際には稀です。 #N/A  
Perlスタイルの($:や$;などのような)特別な変数の使用は避けましょう。それらは極めて暗号的なので、ワンライナー以外での利用は推奨できません。Englishライブラリから提供される人にやさしいエイリアスを用いましょう。 Style/SpecialGlobalVars:
メソッド名と開き括弧の間にスペースを入れてはいけません。 Style/SpaceAfterMethodName:
メソッドの最初の引数が開き括弧で始まるならば、常にメソッド呼び出しに括弧を用いましょう。例えば次のように書きますf((3+2)+1)。 #N/A  
Rubyインタープリタを走らせるときは、常に-wオプションを付けましょう。これまでのルールのどれかを忘れてしまった時に警告を出してくれます! #N/A  
ネストしたメソッド定義は行ってはいけません-代わりにラムダを用いましょう。ネストしたメソッド定義は実際には外側のメソッドと同じスコープ(例えばclass)でメソッドを生成します。そのうえ、そのようにネストしたメソッドは、そのメソッドを含んでいるメソッドがが呼び出されるたびに再定義されるでしょう。 Lint/NestedMethodDefinition:
1行の本文を持つラムダには新しいリテラルを持ちましょう。lambdaは複数行にまたがるときに使いましょう。 Style/Lambda:
stabbylambdaを定義するときは、引数の周りの括弧は省略しないようにしましょう。 Style/StabbyLambdaParentheses:
stabbylambdaに引数がないときは、引数のための括弧は省略しましょう。 #N/A  
Proc.newよりprocを使いましょう。 Style/Proc:
lambdaやprocの呼び出しにはproc[]やproc.()よりproc.call()を使いましょう。 Style/LambdaCall:
使わないブロック引数やローカル変数の先頭には_を付けましょう。単に_を用いるのも許容されます(少し説明不足ではありますが)。この記法を使うことで、RubyインタープリタやRubocopのような対応しているツールでは変数を使っていないという警告を抑制できます。 Lint/UnusedBlockArgument:
STDOUT/STDERR/STDINの代わりに$stdout/$stderr/$stdinを用いましょう。STDOUT/STDERR/STDINは定数であり、Rubyでの定数は、実際は再代入できます(つまりリダイレクトに使えます)が、もし実行するとインタープリタからの警告が出ます。 #N/A  
$stderr.putsの代わりにwarnを用いましょう。簡潔さや明快さもさることながら、warnは必要であれば警告を抑制することができます(警告レベルを-W0を用いて0に設定することによって実現できます)。 #N/A  
あまりに暗号めいているString#%メソッドよりもsprintfやformatを使いましょう。 Style/FormatString:
あまりに暗号めいているArray#*メソッドよりもArray#joinを使いましょう。 Style/ArrayJoin:
配列かどうかわからない変数を配列とみなして処理したいときは、明示的にArrayかどうかチェックするよりも、[*var]やArray()を使いましょう。 #N/A  
ロジックを使って複雑な比較を行うよりも、可能な限りRangeやComparable#between?を用いましょう。 #N/A  
==を明示した比較よりも判定メソッドを用いましょう。数値の比較はOKです。 Style/EvenOdd:
boolean値を扱わない限り、明示的なnilでないかの検査は避けましょう。 Style/NonNilCheck:
BEGINブロックの使用は避けましょう。 Style/BeginBlock:
ENDブロックを使ってはいけません。代わりにKernel#at_exitを使いましょう。 Style/EndBlock:
フリップフロップの使用は避けましょう。 Style/FlipFlop:
制御構文で条件式のネストは避けましょう。/不正なデータを弾きたいときはガード節を使いましょう。ガード節とは、できるだけ素早く関数から抜けられるようにと関数の先頭に置かれている条件文のことです。/ループ内では条件判定ブロックよりもnextを使いましょう。 Style/GuardClause:
collectよりmap、detectよりfind、find_allよりselectinjectよりreduce、lengthよりsizeを使いましょう。これは絶対のルールではないです。別名のほうが可読性に優れているなら、そちらを使っていただいて構いません。韻を踏んでいるほうのメソッド名はSmalltalkから引き継いできたもので、他のプログラミング言語でそこまで一般的ではないです。find_allよりもselectが推奨されるのは、rejectとの相性がよいことと、メソッド名から挙動を推察することも容易だからです。 Style/CollectionMethods:  
sizeの代わりにcountを用いてはいけません。Array以外のEnumerableオブジェクトでは、countを使うと要素数の計算のためにコレクション全体を走査してしまいます。 #N/A  
mapとflattenの組み合わせの代わりに、flat_mapを用いましょう。これは深さが2以上の配列には適用できません。すなわち、users.first.songs==[‘a’,[‘b’,’c’]]のときは、flat_mapよりmap+flattenを用いましょう。flattenは配列を全て平坦にするのに対し、flat_mapは配列を1次元だけ平坦にします。 #N/A  
reverse.eachの代わりにreverse_eachを用いましょう。何故なら、Enumerableをincludeしているクラスの側で、より効率のよい実装が行われていることがあるからです。最悪、クラスが特殊化を行っていない場合でも、Enumerableから継承される一般的な実装は、reverse.eachを用いた場合と同じ効率です。 #N/A  
識別子は英語で名づけましょう。 Style/AsciiIdentifiers:
シンボル、メソッド、変数にはsnake_caseを用いましょう。 Style/MethodName:
クラスやモジュールにはCamelCaseを用いましょう。(HTTP、RFC、XMLのような頭字語は大文字を保ちましょう)。 Style/ClassAndModuleCamelCase:
ファイル名にはsnake_caseを用いましょう。例えばhello_world.rbのように。 Style/FileName:
ディレクトリ名にはsnake_caseを用いましょう。例えばlib/hello_world/hello_world.rbのように。 #N/A  
ソースファイル1つにつきただ1つのクラス/モジュールだけが書かれている状態を目指しましょう。そのファイルの名前はそのクラス/モジュールと同じ名前で、ただしキャメルケースをスネークケースに変換して用いましょう。 #N/A  
定数はSCREAMING_SNAKE_CASEを用いましょう。 Style/ConstantName:
述語メソッド(boolean値が返るメソッド)は疑問符で終わりましょう。(すなわちArray#empty?のように)。boolean値を返さないメソッドは、疑問符で終わるべきではないです。 Style/PredicateName:
危険な可能性のあるメソッド(引数やselfを変更するようなメソッドや、exit!(exitと違ってファイナライザが走らない)のようなもの)は、その安全なバージョンがある場合には、危険であることを明示する意味で感嘆符で終わりましょう。 #N/A  
危険な(感嘆符付き)メソッドがあるときは、対応する安全な(感嘆符なし)メソッドを定義できないか検討しましょう。 #N/A  
短いブロックと共にreduceを使うとき、引数は | a,e | と名づけましょう。(accumulator,element). Style/SingleLineBlockParams:
二項演算子を定義するとき、引数名はotherを用いましょう(«と[]は意味が違ってくるので、このルールの例外です)。 Style/OpMethod:
自己説明的なコードを書いて、このセクションの残りのパートは無視しましょう。本当に! #N/A  
コメントは英語で書きましょう。 Style/AsciiComments:
最初の#とコメントの間にスペースを1つ入れましょう。 Style/LeadingCommentSpace:
1語より長いコメントは頭語を大文字化してピリオドを打ちましょう。文と文の間にはスペースを一つだけ入れましょう。 #N/A  
過剰なコメントは避けましょう。 #N/A  
コメントは最新に保ちましょう。古くなったコメントは、コメントがないより悪いです。 #N/A  
悪いコードを説明するコメントは避けましょう。自己説明的なコードへのリファクタリングを行いましょう(やるかやらないか-“やってみる”はなしだ。–Yoda)。 #N/A  
注釈は、通常関連するコードのすぐ上に書きましょう。 #N/A  
注釈のキーワードの後ろは:を続けましょう。その後ろに問題点を書きましょう。 Style/CommentAnnotation:
もし問題点の記述に複数行かかる場合は、後続の行は#の後ろにスペース3つでインデントしましょう。(通常の1つに加え、インデント目的に2つ) #N/A  
もし問題が明らかで、説明すると冗長になる時は、問題のある行の末尾に、本文なしの注釈だけ付けましょう。この用法は例外であり、ルールではありません。 #N/A  
あとで追加されるべき、今はない特徴や機能の注釈にはTODOを使いましょう。 #N/A  
直す必要がある壊れたコードの注釈にはFIXMEを使いましょう。 #N/A  
パフォーマンスに問題を及ぼすかもしれない遅い、または非効率なコードの注釈にはOPTIMIZEを使いましょう。 #N/A  
疑問の残るコードの書き方でコードの臭いを感じた箇所の注釈にはHACKを使いましょう。 #N/A  
意図したとおりに動くか確認する必要がある箇所の注釈にはREVIEWを使いましょう。例:REVIEW:ArewesurethisishowtheclientdoesXcurrently? #N/A  
適切に感じるのであれば、他の独自のキーワードを用いても構いませんが、それらのキーワードはREADMEやそれに類するものに書いておきましょう。 #N/A  
クラス定義の構造には一貫性をもたせましょう。 #N/A  
クラスの中に複数行あるようなクラスをネストしてはいけません。それぞれのクラスごとにファイルに分けて、外側のクラスの名前のついたフォルダに含めるようにしましょう。 #N/A  
クラスメソッドしかないクラスよりモジュールを使いましょう。クラスはインスタンスを生成することに意味がある時にのみ使われるべきです。 #N/A  
モジュールのインスタンスメソッドをクラスメソッドにしたいときは、extendselfよりもmodule_functionを使いましょう。 Style/ModuleFunction:
クラス階層の設計を行うときは、リスコフの置換原則.に従いましょう。 #N/A  
あなたのクラスを可能な限りSOLIDに保ちましょう。 #N/A  
ドメインオブジェクトのクラスにおいては常に適切なto_sメソッドを提供しましょう。 #N/A  
単純なアクセサやミューテータの定義には、attr群を用いましょう。 Style/TrivialAccessors:
attrの使用は避けましょう。代わりにattr_readerやattr_accessorを使いましょう。 Style/Attr:
Struct.newの使用を考えましょう、それは、単純なアクセサ、コンストラクタや比較演算子を定義してくれます。 #N/A  
Struct.newで初期化されたインスタンスを拡張してはいけません。それは余分なクラスレベルをもたらし、複数回requireされた時に、奇妙なエラーの原因にもなります。 Style/StructInheritance:
あるクラスのインスタンス生成する追加の方法を提供したいときは、ファクトリメソッドの追加を検討しましょう。 #N/A  
継承よりダック・タイピングを使いましょう。 #N/A  
継承での振る舞いが”扱いづらい”ので、クラス変数(@@)の使用は避けましょう。/このように、ひとつのクラス階層に属するすべてのクラスは、1つのクラス変数を共有してしまいます。クラス変数よりもクラスのインスタンス変数のほうを使うべきです。 Style/ClassVars:
意図した使い方に沿って、可視性(private、protected)を設定しましょう。全てをpublic(デフォルトの設定)のままにしないようにしましょう。結局私達は今Rubyを書いているのだから。Pythonではなく。 #N/A  
public、protected、privateは、適用するメソッド定義と同じインデントにしましょう。そして、以降のすべてのメソッド定義に適用されることを強調するために、それらの修飾子の前1行と後1行に空行を入れましょう。 Style/AccessModifierIndentation:
クラスメソッドを定義するときはdefself.methodを用いましょう。クラス名を繰り返さないので、簡単にリファクタリングできるようになります。 Style/ClassMethods:
メソッドの別名をつける時はaliasを使いましょう。クラスのレキシカルスコープの中ではselfと同じように名前解決されます。またユーザーにとっても、実行時やサブクラス側で明示的にエイリアスを変更しなければ、変更されないことが読み取りやすいです。/aliasはdefと同じく予約語なので、シンボルや文字列よりも名前そのものを使いましょう。言い換えると、alias:foo:barではなく、aliasfoobarと書きましょう。/また、Rubyがエイリアスや継承をどのように扱うか注意しましょう。aliasはそれが定義された地点で解決されたメソッドを参照します。動的には解決されません。/この例では、Fugitive#given_nameは、Fugitive#first_nameではなく、オリジナルのWesterner#first_nameを呼び出します。Fugitive#given_nameもオーバーライドしたい時は、継承したクラスでも再定義しなければなりません。 #N/A  
モジュールやクラス、実行時のシングルトンクラス等では、aliasの挙動が予期できないので、エイリアス定義には常にalias_methodを用いましょう。 Style/Alias:
例外はfailよりraiseを使いましょう。 Style/SignalException:
2引数のraiseでは、RuntimeErrorを明示しないようにしましょう。 Style/RedundantException:
raiseの引数としては例外クラスのインスタンスよりも、例外クラスとメッセージをそれぞれの引数で渡す方を使いましょう。 Style/RaiseArgs:
ensureブロックからreturnしてはいけません。もしensureの中から明示的に値を返した場合は、returnはどの例外発生よりも優先されて、例外など発生していなかったかのように値を返してしまいます。事実上、例外は静かに捨てられます。 Lint/EnsureReturn:
可能な場所では、暗黙のbeginブロックを用いましょう。 Style/RedundantBegin:
不確実性のあるメソッド(AvdiGrimmによって作られた言葉です)を用いてbeginの蔓延を和らげましょう。 #N/A  
例外をもみ消してはいけません。 Lint/HandleExceptions:
rescueを修飾子として利用するのは避けましょう。 Style/RescueModifier:
制御フローに例外を使っては行けません。 #N/A  
Exceptionをrescueするのは避けましょう。これはexitのシグナルも捕捉するため、プロセスを殺すのにkill-9が必要になります。 Lint/RescueException:
より詳細な例外をrescueチェーンの上に配置しましょう。そうでなければ、決してrescueされません。 #N/A  
プログラム内で確保した外部リソースは、ensureで開放しましょう #N/A  
自動的にリソースを開放してくれる機能を含むメソッドを利用可能な時は、そちらを使いましょう。 #N/A  
新しい例外クラスを導入するより、基本ライブラリの例外クラスを使いましょう #N/A  
配列やハッシュを生成する時はリテラル記法を使いましょう。(コンストラクタに引数を渡す場合を除けば、ということですが) Style/EmptyLiteral:
単語(空でなく、スペースを含まない文字列)の配列を生成する時は%wリテラルを使いましょう。このルールは配列の要素が2つ以上の場合に限ります。 Style/WordArray:
シンボルの配列が必要な時(かつRuby1.9との互換性を維持しなくていい時)は%iリテラルを使いましょう。このルールは配列の要素が2つ以上の場合に限ります。 Style/SymbolArray:  
ArrayやHashリテラルの最後の要素の後ろの,は避けましょう。複数行にわかれていない時は特に避けましょう。 Style/TrailingCommaInLiteral:
配列に大きな隙間を作るのは避けましょう。 #N/A  
配列の最初や最後にアクセスしたいときは、[0]や[-1]よりfirstやlastを使いましょう。 #N/A  
要素が一意のものを扱うときは、Arrayの代わりにSetを用いましょう。Setは要素に重複と順序がないようなコレクションの実装です。これはArrayの直感的な二項演算子と、Hashの速さが合わさっています。 #N/A  
ハッシュのキーには文字列よりシンボルが好まれます。 #N/A  
変更のできるオブジェクトをハッシュのキーに使うのは避けましょう。 #N/A  
ハッシュのキーがシンボルの時は、Ruby1.9のハッシュリテラル記法を用いましょう。 Style/HashSyntax:
Ruby1.9のハッシュ記法とロケット記法を同じハッシュリテラル内で混在させてはいけません。シンボルでないキーがある場合は、ロケット記法を使いましょう。 #N/A  
Hash#has_key?よりHash#key?を、Hash#has_value?よりHash#value?を用いましょう。ここでMatzが述べているように、長い記法は廃止が検討されています。 Style/PreferredHashMethods:
存在すべきキーを扱う時は、Hash#fetchを用いましょう。 #N/A  
Hash#fetchのデフォルト値を使い、自力でロジックを書かないようにしましょう。 #N/A  
Hash#fetchのデフォルト値は評価するべき式に副作用があったり実行コストが高いときはうまくいかないので、代わりにブロックを使いましょう。 #N/A  
ハッシュから連続して複数の値が必要になる時は、Hash#values_atを用いましょう。 #N/A  
Ruby1.9以降、ハッシュは順序付けられるということを信頼しましょう。 #N/A  
コレクションを走査している時に変更を加えてはいけません。 #N/A  
コレクションにアクセスするとき、[n]の代替のリーダーメソッドが提供されている場合に直接[n]経由でアクセスすることは避けましょう。nilに対して[]を呼ぶことを避けることが出来ます。 #N/A  
コレクションに対するアクセサを提供するとき、コレクション内の要素にアクセスする前に、nilでアクセスするのを防ぐための代替のアクセス方法を提供しましょう。 #N/A  
文字列連結の代わりに文字列挿入や文字列整形を使いましょう。 #N/A  
文字列挿入時には、括弧の内部にスペースを入れるべきではありません。 Style/SpaceInsideStringInterpolation:
文字列リテラルの引用符は一貫したスタイルで使いましょう。Rubyコミュニティでは、デフォルトでシングルクォートを用いるもの(OptionA)、ダブルクォートを用いるもの(OptionB)の二つのよく使われるスタイルがあって、どちらも良いと考えられています。 Style/StringLiterals:
文字リテラル構文?xを用いてはいけません。Ruby1.9以降、この表記法を必要とする場面はないはずです-?xは’x’(1文字の文字列)と解釈されるからです。 Style/CharacterLiteral:
文字列の中の挿入されるインスタンス変数やグローバル変数の周りの{}は省略してはいけません。 Style/VariableInterpolation:
文字列に挿入するときにObject#to_sを使ってはいけません。自動的に呼び出されます。 Lint/StringConversionInInterpolation:
大きなデータの塊を作る必要があるときは、String#+の使用は避けましょう。代わりに、String#«を使いましょう。文字列連結は、文字列インスタンスを直接書き換えるため、たくさんの新しいオブジェクトを作ってしまうString#+よりも常に速いです。 #N/A  
利用するケースにより特化した速い代替手段がある場合、String#gsubは使わないようにしましょう。 #N/A  
複数行のヒアドキュメントを用いるときは、先頭のスペースも保持してしまうということを頭に入れておかなければなりません。過剰なスペースを取り除くためのマージンを採用するのはよい習慣です。 #N/A  
単に文字列中から文字列を探すだけの時は、正規表現を使ってはいけません:string[‘text’]を使いましょう。 #N/A  
文字列の添字に直接正規表現を渡すことで、文字列の構築をシンプルにできます。 #N/A  
キャプチャした結果を使う必要のないときは、キャプチャしないグループを用いましょう。 #N/A  
最後に正規表現にマッチした値を示すPerlレガシーの暗号的な変数を用いてはいけません($1、$2など)。代わりにRegexp.last_match(n)を用いましょう。 Style/PerlBackrefs:
どの値が入っているか追うのが困難になるので、グループ番号を使うのは避けましょう。代わりにグループに名前をつけましょう。 #N/A  
文字クラスの中では、特別な意味を持つ文字が少ないので注意が必要です:^、-、\、]のみが特別な意味を持つので、.や括弧を[]の中でエスケープしてはいけません。 #N/A  
^や$は、文字列の先頭や末尾ではなく、行頭や行末にマッチするので注意が必要です。もし文字列全体の先頭末尾にマッチさせたいときは、\A、\zを使いましょう(\n?\zと等価である\Zと混同しないようにしましょう)。 #N/A  
複雑な正規表現にはx識別子を用いましょう。これを用いることで、より読みやすくなり、便利なコメントを使えるようになります。スペースが無視されることに注意しましょう。 #N/A  
sub/gsubでの複雑な置換は、ブロックやハッシュを用いることで実現できます。 #N/A  
文字列挿入と”文字の双方が入る1行の文字列には、%()(%Q()の短縮形)を使いましょう。複数行の時はヒアドキュメントを使いましょう。 Style/BarePercentLiterals:
文字列に’と”双方が含まれない限り、%qの使用は避けましょう。通常の文字列リテラルのほうがより読みやすいので、エスケープが大量に必要出ない限りは、そちらを使いましょう。 Style/UnneededPercentQ:
/’が1つ以上の正規表現に限り、%rを使いましょう。 Style/RegexpLiteral:
呼び出すコマンドにバッククォートが含まれる(かなり起こりえないが)ことがない限り、%xの使用は避けましょう。 Style/CommandLiteral:
%sの使用は避けましょう。Rubyコミュニティは、スペース含むシンボルをを作る時は:”文字列”がよいと決めたようです。 #N/A  
パーセントリテラルの区切り文字は、%rを除いて()が好まれます。正規表現の中では、括弧は色々なシーンで使われるので、正規表現の内容によっては、より使われる機会の少ない{のほうが良い選択となることがあるかもしれません。 Style/PercentLiteralDelimiters:
不要なメタプログラミングは避けましょう。 #N/A  
ライブラリを作成する時にコアクラスを汚染するのはやめましょう。(モンキーパッチを当ててはいけません)。 #N/A  
ブロック渡しのclass_evalのほうが、文字列挿入型よりも好ましいです。 #N/A  
文字列挿入型のclass_eval(または他のeval)を用いる時は、挿入されたときのコードをコメントに追加しましょう(Railsで使われているプラクティス)。 #N/A  
method_missingを用いたメタプログラミングは避けましょう。何故なら、バックトレースがよくわからなくなるし、#methodsのリストの中に出てこず、ミススペルしたメソッド呼び出しも無言で動いてしまいます、例えばnukes.launch_state=falseのようにです。代わりに、移譲、プロキシ、またはdefine_methodを使いましょう。もしmethod_missingを使わなければならない時は:/respond_to_missing?も実装されているか確かめましょう/既知の接頭辞、find_by_*のようなものだけを捕捉しましょう–可能な限りアサートさせましょう/最後にsuperを呼び出しましょう/アサートする、特別でないメソッドに移譲しましょう: Style/MethodMissing:
private/protected制約を回避しないために、sendよりもpublic_sendを使いましょう。 Style/Send:  
sendは他の既存のメソッドと衝突するかもしれないので、__send__を使いましょう。 #N/A  
ruby-wで実行しても何も警告されないコードを書きましょう。 #N/A  
オプショナルな変数としてのハッシュの使用を避けましょう。そのメソッドはあまりにたくさんのことをやろうとしていませんか?(オブジェクトの初期化はこのルールの例外です) #N/A  
コードのある行が10行を超えるメソッドは避けましょう。理想を言えば、多くのメソッドは5行以内がよいです。空行は行数には含めません。 Metrics/MethodLength:
3つや4つ以上引数を設定するのは避けましょう。 Metrics/ParameterLists:
もし本当にグローバルなメソッドが必要な場合は、Kernelに定義し、privateに設定しましょう。 #N/A  
グローバル変数の代わりに、モジュールのインスタンス変数を使用しましょう Style/GlobalVars:
複雑なコマンドラインオプションをパースするためにOptionParserを使いましょう。また、些細なオプションにはruby-sを使いましょう。 #N/A  
現在のシステム時間を読み出すには、Time.newよりもTime.nowを使いましょう。 #N/A  
破壊的変更をしなくても済むなら、できるだけ関数的プログラミング手法を使いましょう。 #N/A  
それがメソッドの目的でない限り、引数に破壊的変更をするのはやめましょう。 #N/A  
3段階を超えるブロックのネストは避けましょう。 Metrics/BlockNesting:
一貫性を保ちましょう。理想を言えば、このガイドラインに沿いましょう。 #N/A  
常識を用いましょう。 #N/A  

もし、すでにある程度ポリシーが固まっている場合は、下記のコマンドを使って.rubocop_todo.ymlを生成することができます。

$ rubocop --auto-gen-config
Inspecting 1 file
C

Offenses:

sample.rb:2:1: C: Use 2 (not 4) spaces for indentation.
    bbbb
^^^^

1 file inspected, 1 offense detected
Created .rubocop_todo.yml.
Run `rubocop --config .rubocop_todo.yml`, or
add inherit_from: .rubocop_todo.yml in a .rubocop.yml file.

これはそのプロジェクトのコーディングポリシーがconfig/default.ymlからどれだけ違っているかが記述されているファイルです。現在のコードを規範として利用するときにはここで生成した.rubocop_todo.yml.rubocop.ymlとして配布するか、実行時に下記のように指定することで、コーディングポリシーの統一を図ることができます。

$ rubocop --config .rubocop_todo.yml

RuboCopによるコードの自動修正

RuboCopはコーディングポリシーに即していないコードについて、自動修正する機能があります。ただし、修正できる項目は上記で生成した.rubocop_todo.ymlの内容として、# Cop supports --auto-correct.が付与されているものに限ります。

# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: Width.
Style/IndentationWidth:
  Enabled: false

上記の記述が.rubocop_todo.ymlに含まれていた場合は、Ruby Style Guideインデントには スペース 2つ(別名ソフトタブ)。 ハードタブを用いてはいけません。に則していない項目があることを示しています。これに対して下記のコマンドで自動修正を行うことができます。

$ rubocop --auto-correct
Inspecting 1 file
C

Offenses:

sample.rb:2:1: C: [Corrected] Use 2 (not 4) spaces for indentation.
    bbbb
^^^^

1 file inspected, 1 offense detected, 1 offense corrected

まとめ

今回はRubyの記述に関するベストプラクティスであるRuby Style Guideと、それを元にした静的解析ツールのRuboCop、そしてその周辺ツール、コーディングポリシーのチューニング方法、コードの自動修正機能について紹介しました。

自分のコードの客観評価としても利用できるツールだと思いますし、導入も簡単なのでちょっと試してみてはいかがでしょう?