ESM アジャイル事業部 開発者ブログ

永和システムマネジメント アジャイル事業部の開発者ブログです。

CurrentAttributesを使ってリクエストごとのdefault_scopeを設定する

こんにちは、永和システムマネジメントに新卒で入社して4年目になりました swamp09 です。

この記事では、以前関わったRailsプロジェクトの default_scopeユースケースと実装例の話をします。

ユーザーが記事を投稿したり、記事にコメントしたりするWebサービスがあり、Twitterのようにユーザーが他のユーザーをブロックする機能がありました。 ブロックすると、ブロックしたユーザーからはブロックされたユーザーの投稿した記事やコメントが一切非表示になります。このブロックのロジックを default_scope で書けるとコーディングが楽になりそうだなぁとプロジェクトメンバーと話していました。 ただ、リクエストごとに異なるログインユーザーのブロックされたユーザーを非表示にするのは default_scope でうまく書けない、というところで悩んでいました。

そんな時に見つけたのが ActiveSupport::CurrentAttributes でした。

ActiveSupprt::CurrentAttributes ではリクエストごとの属性をシステム全体で簡単に利用できるよう保持できます。これを使って上記のような default_scope を書けるのではと思いました。

具体例をRailsチュートリアル第6版のサンプルアプリを元にコードで示します。sample_appにはUserとMicropostというモデルがあります。

class User < ApplicationRecord
  has_many :microposts, dependent: :destroy
end

class Micropost < ApplicationRecord
  belongs_to :user
end

sample_appに追加でユーザーが他のユーザーを非表示にするためのブロック機能を追加していきます。

# Blockモデルを追加する
class Block < ApplicationRecord
  belongs_to :blocker, class_name: "User"
  belongs_to :blocked, class_name: "User"
  validates :blocker_id, presence: true
  validates :blocked_id, presence: true
end

# UserモデルにBlockモデルとの関連を追加する
class User < ApplicationRecord
  has_many :microposts, dependent: :destroy

  has_many :active_blocks, class_name:  "Block",
                                  foreign_key: "blocker_id",
                                  dependent:   :destroy
  has_many :blocking, through: :active_blocks, source: :blocked
  has_many :passive_blocks, class_name:  "Block",
                                   foreign_key: "blocked_id",
                                   dependent:   :destroy
  has_many :blockers, through: :passive_blocks, source: :blocker
end

Blockモデルはsample_appのRelationshipモデルとほぼ同じです。

これに ActiveSupport::CurrentAttributes を用いてリクエストごとの default_scope を設定していきます。

# app/models/current.rb を追加する
class Current < ActiveSupport::CurrentAttributes
  attribute :blocking_ids
end

# app/controllers/concerns/hide_blocking_user.rb を追加する
module HideBlockingUser
  extend ActiveSupport::Concern

  included do
    before_action :set_current
  end

  private

  def set_current
    Current.blocking_ids = current_user&.blocking&.ids
  end
end

# ブロック機能が関わるControllerでconcernをincludeする
class UsersController < ApplicationController
  before_action :logged_in_user, only: [:index, :edit, :update, :destroy,
                                        :following, :followers]
  before_action :correct_user,   only: [:edit, :update]
  before_action :admin_user,     only: :destroy

  # concernをincludeする
  include HideBlockingUser

  def index
    @users = User.paginate(page: params[:page])
  end
end

# MicropostモデルとUserモデルのdefault_scopeを書き換える
class Micropost < ApplicationRecord
  belongs_to :user
  has_one_attached :image
  
  default_scope do
    if Current.blocking_ids.present?
      where.not(user_id: Current.blocking_ids).order(created_at: :desc)
    else
      order(created_at: :desc)
    end
  end
end

class User < ApplicationRecord
  has_many :microposts, dependent: :destroy

  default_scope do
    where.not(id: Currnet.blocking_ids) if Current.blocking_ids.present?
  end
end

以上のようにして、HideBlockedUserinclude したControllerであれば default_scope を使ったブロック機能が使えるようになりました。

まとめ

リクエストごとに default_scope を指定したい、というユースケースはあまり出会うことはないかもしれませんが、ActiveSupprt::CurrentAttributes を使えば実装できることがわかりました。実際に使うかどうかは別として… CurrentAttributesのドキュメントにも注意点が書かれていますが、CurrentAttributes を使う際はグローバルな値をいろんなところからアクセスできるようになるので、慎重に扱う必要があるかと思います。

ブロック機能を追加したコードは GitHub に置きました。なにかの参考になれば幸いです。

プロジェクト参画直後にやってよかった3つのこと

エンジニア2年目に突入しましたyuki0920です。

私は、入社から1年ちょっとの間に、4つのRailsを使ったプロジェクトに携わりました。 おおよそ約3ヶ月に1プロジェクトを経験していることになりますね。

本記事では、この4つのプロジェクトにエンジニアとして携わった経験から得られた、「プロジェクト参画直後にやってよかったこと」を3点紹介します。

1.ペアプログラミングをする

まずは、主要な機能を理解することが重要かと思います。

私は、プロジェクト参画後すぐに、参画済みメンバーと小さなタスクを対象にペアプログラミングすることがあったのですが、いまふりかえると、この経験はよかったなと思います。

早期に主要な機能のソースコードや業務プロセスについての理解を深められたため、その後の改修を効率的に進められるきっかけとなりました。

このペアプログラミングは、キーボードを操作してコードを書く「ドライバ」と、ドライバの補佐をする「ナビゲータ」の役割を、時間を決めて(20分くらい)交代しながら進めました。VSCodeLive Share機能を使うと、1つのエディタを互いに共有できるので、とても捗ります。

2.プロジェクト外のアプリケーションを作って、ライブラリの動作を把握する

アプリケーションで使うライブラリは多岐にわたります。

Railsアプリケーションならば、Gemfileをみると、導入されているGemの一覧がわかります。

就職前に私が利用していたRailsの学習教材では、Gemはあまり取り扱われていなかったので、プロジェクト参画当時は知らないGemが多々ありました。

しかし、Gemと密に関わる改修を行う場合、当然ながらGemの理解が必要となります。

そこで私のオススメは、プロジェクトで扱うアプリケーションとは別のところで、Gemを使い倒してみることです。

rails new してサンプルのアプリケーションを作成し、気になるGemを導入します。 GemのREADMEを見ながらパラメータを変えてみたりと手を動かしながら試すことで、理解が深まるはずです。

下記は私が使ってみたGemの一例です。

管理画面や認証、認可のGemはいくつもの種類がありますが、どれか1種類の使用経験があれば、応用は効きやすいかと思いますので、これらのGemをとっかかりとして、使ってみるのもよいかもしれません。

3. ER図を作成する

アプリケーションの規模が大きくなると、モデルが増え関連が複雑になります。 このモデルの理解には、ER図が役立ちます。

Railsアプリケーションならば、Rails ERDというGemが便利です。 Rails ERDを使うと、コマンド1つでER図を出力することが可能です。

下記は、サンプルのアプリケーションにAuthorモデルとBookモデルを1対多の関係で実装し、Rails ERDを導入して出力したER図です。

ER図

モデルの数が少なければ脳に記憶しておける場合もあるかと思いますが、モデルが増えるにつれ記憶しておくことは難しくなるので、すべてのモデルが記載されているER図が手元にあり、いつでも参照できるのは何かと便利です。

最後に

本記事では、「プロジェクト参画直後にやってよかったこと」を3点紹介しました。

「もっとこれやっといたほうが良いよ」などあれば、教えていただけると幸いです。

ユーザーストーリーマッピングのすすめ

アジャイル事業部の山岸です。

私のプロジェクトでは、作るもの(開発するもの)を決めていくときにユーザーストーリーマッピングのワークショップを開発チームでやっています。ユーザーストーリーマッピングはシンプルな手法であるため、導入のハードルが低く、様々なプロジェクトで活用できるところが特に気に入っています。
今回は、ユーザーストーリーマッピングをよりプロジェクトで有効に機能させるために、なぜユーザーストーリーマッピングが必要なのか、何に利用するのかを説明したいと思います。今回の記事では、ユーザーストーリーマッピングそのものの説明や用語については、書籍や記事などで詳しい解説がされていることもあり、省略させていただきました。

なぜユーザーストーリーマッピングが必要なのか

プロジェクトの開始時点では、MVP(Minimum Viable Product)を意識して開発の計画を立てていたが、気付くと開発したい機能がどんどん増えていき、当初予定したリリースに全然収まりきらなくなったといった事態が発生することがあります。
こういった事象は開発当初のゴールを見失い、全体像がみえなくなってしまったときに起きることが多いように感じています。プロジェクトの目標である、作るものを最小限にして最大限の成果を実現するためには、成果が発生するまでの(全体の)流れとそこに辿りつくまでに必要な活動について、プロジェクトに関わる人たちと認識を合わせていくことが重要だと思います。 このような共通の理解を築くためにユーザーストーリーマッピングという手法が有効だと考えています。

ストーリーマップを作る前に

私はユーザストーリーマッピングからストーリーマップを作成する前に、参加者とユーザーストーリーマッピングから得られる結果の期待値を合わせるようにしています。
ストーリーマップを作ったとしても、すぐに開発可能な粒度でのユーザーストーリーが洗い出されるわけではありません。参加者が会話によってプロダクトの理解を深め、全体像が把握でき、ゴールが明確になることが重要であり、そこに重点を置いて取り組むのがよいと考えています。

ユーザーストーリーのはじまり

ユーザーストーリーマッピングは、ストーリーの開始地点を決めて、そこからユーザーストーリーを書き出していきます。まずは利用者の人物像を決めて、プロダクトが実現したい成果に向けてその利用者や関係者のアクティビティを書き出していきます。
例えば開発するプロダクトが新規のサービス(Webサイト)だった場合、ストーリーの開始時点では、利用者がプロダクトを認識していないことを前提とします。そして、利用者がどのようにして(どのような経路で)サイトを訪れるのか、それを実現するには何が必要か、などを話し合いながらストーリーを開始します。

プロジェクトによっては、機能要件が先行して洗い出していたりすることがありますが、その内容に引っ張られて、「ユーザーが〇〇サイトで会員登録する」などのプロダクトの利用シーンからストーリーを開始してしまい、ストーリーの前後関係を理解しないまま、機能の詳細に話が偏ってしまうことは避けるようにしています。

ユーザーストーリーマッピングの次は

ユーザーストーリーマッピングで書き出されたユーザーストーリーは粒が大きく、開発可能な状態とはなっていないため、ストーリーワークショップと呼ばれる、個々のストーリーの細部を詰めていく作業が必要になります。ここでは開発する機能の完成イメージを共有し、受入基準などを会話しながら確認していきます。開発に着手可能な粒度へ分解できると、見積もりや開発計画を立てることができるようになります。

ストーリーマップに立ち戻る

プロジェクトが進んでいくと開発の優先順位が変わりリリース計画を見直す必要がでてくることがよくあると思います。その場合でも一度ストーリーマップに戻って、リリースに必要なユーザーストーリーを並び替えをすることで、依存関係を視覚化し、開発チームで認識を共有することができます。
また、並び替えたユーザーストーリーは、記載された内容が同じでもあっても文脈が変わっていることがあるため、開発を着手する前には完了条件を改めて話し合う必要があります。

最後に

短いサイクルで開発とリリースを繰り返すアジャイル開発においても、開発チームが全体像と現在地を把握しながらゴールを目指す難しさが存在すると思います。ユーザーストーリーマッピングはその問題を解決する手助けをしてくれると私は考えています。
最後にこれからユーザーストーリーマッピングを学びたい方は書籍「ユーザーストーリーマッピング」をおすすめします。私はこの書籍でユーザーストーリーマッピングを学び、開発プロジェクトで取り組むときはいつも参考にさせていただいています。

Rails / OSS パッチ会オンライン 2020年12月のお知らせ

2020年12月の Rails / OSS パッチ会を 12月10日(木)にオンライン開催します。

この会をひとことでいうと、日頃のお仕事で使っている Rails をはじめとする OSS への upstream にパッチを送る会です。

今回は終わった後に、有志でオンライン忘年会を行います。

会には RubyRails のコミッターである顧問の a_matsuda もいますので、例えば Rails に送るパッチのネタがあるけれど、パッチを送るに適しているかの判断やパッチを送る流れが悩ましいときなど a_matsuda に相談して足がかりにするなどできます。

開催時間は 17:00-19:00 となりますがご都合のあう方はぜひご参加下さい。Google Meet あたりのテレビ会議システムを使います。

当日の招待 URL は Idobata の esminc/rails ルームで共有する予定です。

idobata.io

特に募集ページなど設けませんが、上記理由から Idobata のアカウントと (TV 会議システムによっては Google アカウント) が必要になると思います。

いよいよ Ruby 3.0 がリリース直前であり、Rails 6.1 も rc がリリースされているタイミングなので、そういった話題などあるかもしれません。

その他の開催方針については以下の Gist に記していますので、ご参照ください。

Reboot Rails/OSS meetup online · GitHub

ふりかえりには Continuous KPTA がオススメ

こんにちは、コッパイ星を救うため惑星探索している nsgc です。

組織活動やチームでの開発をよりよくするための「ふりかえり」。
アジャイルなソフトウェア開発を実践しているチームでは、プラクティスとして取り入れて実施している所も多いかと思います。

私が参加しているプロジェクトでは2週間イテレーションで開発しており、リリースした日の夕方に「KPTA」(けぷた)を使ってふりかえりを行っております。

KPTA とは、こちらをご覧いただくと詳しく載っていますが、ざっくりと説明しますと「続けること(Keep)」、「問題点(Problem)」、「試したいこと(Try)」の3つの項目に分けて整理する思考フレームワークである「KPT」を拡張したもので、Try から具体的な行動することを Action として抽出することに注力できるのが特徴になります。

当初、私達はホワイトボードに模造紙を貼り、その前でペンと付箋紙を用いて行なっていたのですが、昨年、同僚の @amapyon@kunitoo が開発している Web上で KPTA でのふりかえりが出来るサービス 「Continuous KPTA 」 がリリースされたので、プロジェクトでのふりかえりをそこで行なうようになりました。

それから1年ほど使ってみた上で、ぜひ皆さんにオススメしたいと思い、今回ブログに書かせてもらいました。

付箋の関連付けが簡単にできる

Continuous KPTA の素晴しい点として、画面いっぱいに広がったKPTAボード上の好きな所に付箋の配置ができ、付箋同士に線を引くことで簡単に関連を結べることです。
これにより、「ある Problem を解消するために、この Try を考えた」ということを分かりやすく表すことが出来ます。

relation
Problem から Try、Try から Action と繋がっている

もちろん、ホワイトボードや模造紙でも線は引けますが、1度線を書くと付箋の位置が動かしずらくなったり、毎回ホワイトボードの線を消したり模造紙を貼り変えるのが手間である、という理由から中々線を引きずらいと思ったことはありませんか?
そのため簡単に関連の線で引けることはとても魅力的なことと私は思います。

ふりかえり自体を見直せる

Continuous というその名前が表すように、実施したふりかえりを次に繋げていくというコンセプトがあり、過去に実施したふりかえり結果がすぐに閲覧できたり、出した意見が長く残り続けているということが可視化されています。

そのため、「最近は Problem が増加傾向になっている」とか 「長く放置されている Action がある」といった、ふりかえり自体の状態の把握が容易にできます。
このことは「解決できない Problem が多い」、「実践しにくい Action になっている」など付箋に書き出した内容を考察するきっかけになり、ふりかえりによる改善活動自体の見直しにも繋がります。

problem
付箋の上に過去の世代が。黄色の付箋は5回前から Problem として残り続けている

便利な機能がそろっている

ふりかえりの基本的なやり方が載っていたり、タイムキーパーをするためのタイマー機能、そして、どの意見にフォーカスして議論するかを参加者が選ぶ投票機能、と実際に行う時にあると便利な機能が揃っているのも嬉しい点です。

vote
赤の数字バッジが投票結果。たくさん出た Try からどれを試すかを決めるのにも使える

最後に

昨今はコロナ渦による外出自粛のため、リモートでチーム開発をしているという開発者の方も多くいらっしゃるんじゃないでしょうか。
もし、一箇所に集まれないためふりかえりがあまり出来ていない、良い方法やツールがなくて困っているというチームがありましたら、1度試してみてはいかがでしょうか?

Continuous KPTA

AWS ECS + Active Storage(AWS S3) で秘匿情報を秘匿して利用する方法

안녕하세요.이토 쿠니히코입니다.*1
Nizi Project から韓国ドラマやK-POP を聞くようになった kunitoo です。

Webアプリケーションを書いていると、アイコン画像やPDFなどファイルをアップロードして保存したいというケースに遭遇すると思います。私が関わってきたプロジェクトではファイル添付する要件が十中八九入っていました。
ファイル添付を実現する Ruby のライブラリは様々ありますが、Rails 5.2 から標準でファイルをアップロードして Active Record モデルにファイルを添付する機能の Active Storage が利用できるようになっています。

今回は RailsAWS ECS で動作させる場合に Active Storage + S3 を Rails サーバーを介して利用する際に秘匿情報を秘匿して利用する設定方法について書いていきます。

簡単な設定方法

Rails ガイド Active Storage の概要 2 セットアップAmazon S3サービスを利用する設定方法について以下のように記載されています。

# config/storage.yml
amazon:
  service: S3
  access_key_id: ""
  secret_access_key: ""
  region: ""
  bucket: ""

一番簡単な設定方法としては、IAM で S3 にフルアクセスできるユーザーを作成し、アクセスキーを発行して、その access_key_id, secret_access_keyYAML に記載するという方法があります。

しかし、この方法はセキュリティとしてあまりよろしくありません。 よろしくない点としては以下があります。

  1. IAM ユーザーのAWS S3 アクセスできる権限が強すぎるため、Active Storage に利用するバケット以外にもアクセスできてしまう
  2. IAM のアクセスキーは流用することができるため、今回利用したい Rails サーバー(ECS) 以外からもアクセスすることができ、キーが漏れると悪用される可能性がある

秘匿情報を秘匿した設定方法

今回はアクセスキーを作成せずに、ECS に必要な必要最低限の権限を付加することで、安全に Active Storage を利用する方法を紹介します。 設定のポイントは以下です。

  • S3 Bucket を private で作成する
  • S3 Bucket にアクセス可能な必要最低限の IAM ポリシーを作成する
  • ECS Task ロールに IAM ポリシーをアタッチする

以下に Terraform を使って実行可能な具体的な設定を記載します。

S3 Bucket を private で作成する

Active Storage で利用する S3 を private で作成します。 これにより、外部からのアクセスを防ぎます。 この状態ではどこからもアクセスができません。

resource "aws_s3_bucket" "images" {
  bucket = "${local.server_subdomain}images.example.com"
  acl    = "private"
}

S3 Bucket にアクセス可能な必要最低限の IAM ポリシーを作成する

ここのポイントは Rails ガイドにも記載されている、s3:ListBucket、s3:PutObject、s3:GetObject、s3:DeleteObject の4つのパーミッションを付与することと、resources に bucket/ とbucket を指定する点です。
はじめはすべてのオブジェクト (bucket/
) の指定だけで動作するかと思ったのですが、 バケット全体 (bucket) の指定がないと Active Storage で purge をする際に失敗します。

data "aws_iam_policy_document" "allow_s3_policy" {
  statement {
    actions = ["s3:ListBucket", "s3:PutObject", "s3:GetObject", "s3:DeleteObject"]
    resources = [
      "${aws_s3_bucket.images.arn}/*",
      "${aws_s3_bucket.images.arn}"
    ]
  }
}

resource "aws_iam_policy" "allow_s3_policy" {
  name        = "${var.environment}_allow_s3"
  description = "allow s3 bucket"

  policy = "${data.aws_iam_policy_document.allow_s3_policy.json}"
}

ECS Task ロールに IAM ポリシーをアタッチする

ここのポイントはタスク実行ロールではなくタスクロールにIAMポリシーを設定する点です。
参考のため、ECS task definition を定義する部分までを載せていますが、aws_iam_role_policy_attachment が重要な点です。

resource "aws_iam_role_policy_attachment" "ecs_task_role_allow_s3" {
  role       = aws_iam_role.ecs_task_role.name
  policy_arn = aws_iam_policy.allow_s3_policy.arn
}

resource "aws_iam_role" "ecs_task_role" {
  name = "${var.environment}EcsTaskRole"
  path = "/"

  assume_role_policy = "${data.aws_iam_policy_document.ecs_assume_role_policy.json}"
}

data "aws_iam_policy_document" "ecs_assume_role_policy" {
  statement {
    principals {
      type        = "Service"
      identifiers = ["ecs-tasks.amazonaws.com"]
    }
    actions = ["sts:AssumeRole"]
  }
}

resource "aws_ecs_task_definition" "server" {
  family                = "${var.environment}-server"
  container_definitions = data.template_file.server_container_definitions.rendered
  execution_role_arn    = aws_iam_role.ecs_task_execution.arn
  task_role_arn         = aws_iam_role.ecs_task_role.arn

  network_mode = "awsvpc"

  cpu                      = 256
  memory                   = 512
  requires_compatibilities = ["FARGATE"]
}

Active Storage の設定

ポリシーを設定することで Rails の設定から access_key_id, secret_access_key は不要になり以下を書いておくだけでよくなります。

# config/storage.yml
amazon:
  service: S3
  region: ap-northeast-1
  bucket: <%= ENV.fetch("AWS_S3_BUCKET_NAME") %>

まとめ

AWS ECS で Rails サーバーを動かす際の Active Storage の設定を IAM ポリシーを適切に設定することで、アクセスキー管理せず、より安全に S3 を利用できるようになりました。
また、具体的な設定方法として Terraform で示しました。
(いくつか記載していないリソースがあるのでそのままでは動作しませんが、実際に構築し動作させたものをベースに記載しています)

AWS で Active Storage を使う際の参考になればと思います。

*1:こんにちは。伊藤邦彦です。

私が思う読みやすいコード

どうも。今は保守をメインの業務にしている muryoimpl です。

今回は、私がこうなっていると読みやすくて嬉しいなぁというコードについて、頑張って言語化していきたいと思います。

明確な指針を出しにくいものなので、リーダブルコードやリファクタリング Ruby エディションには載っていないかもしれないですが、今から書いていくことが意識されているコードを私は読みやすいと思うので、こういう観点もあるのね、くらいの気持ちでお付き合いください。

メソッドを見れば流れがわかる、短期記憶にやさしいコード

私は局所を見るよりも、まず全体の流れがわかったうえで詳細を読むほうが理解が進むタイプの人です。 メソッドの全体の流れがわかると、流れからどの部分で何が行われるかだいたい想像することができるので、詳細を見に行ったときに予めイメージを持っておけるし、イメージがわけば考える範囲を限定できるため、100%手探りよりも効率がよいと感じるからです。

流れがわかると、「考える範囲を限定できる」というのが重要です。そのメソッドのもつ役割と振る舞いにだけ集中してそのメソッドを読めばよいからです。読み進めていくうちに記憶しておくべきことを少なくできると、頭の中がクリアな状態を保てるため、理解も進みます。

ただし、読むべきメソッドが限定できたとしても、そのメソッドが極端に長かったり、複数ファイルにまたがって深いところまで追っていかないと最終的な結果が何をしているかわからなくなっていたりする場合、脳の短期記憶がオーバーフローしてしまい、よくわからなくなってしまいます。こういう場合、戻ることも難しくなって読みなおし、ってなることがあるんですよね。

例えば、他のファイルにあるメソッドへの参照がない10行のメソッドと5,000行のメソッドでは読み進める中で記憶しておくことは 5,000行のメソッドのほうがきっと多くなります。同じ 10行のメソッドでも、同一ファイル内で完結しているメソッドと複数のファイルにまたがって定義されているメソッドをあれこれ組み合わせた10行のメソッドでは後者のほうが記憶しておくことは多いでしょう。

短期記憶を酷使しない脳が疲れないコードが読みやすいやさしいコードだと思うのです。

流れがわかるコード

原則として、コードは上から下に順に処理されるようになっています。上から下に読み進めていくというのは、時系列に処理を追うことができるので読みやすい、というのは同意いただけるのではないかと思います。まず、上から下に読んでいけるようなコードの構成にしましょう。

メソッドや変数が流れを理解できる程度にわかる名前がついていると、更に読みやすくなります。変数やメソッド名は処理の目次に等しいと私は思っているので、これは大事です。概要が名前からわかると、その部分については読む/読まないの判断をつけやすいため無駄に調べる必要がなくなり、読む人にとっては非常やさしくなります。名前についてはリーダブルコードの「第Ⅰ部 表面上の改善」でいろいろと触れられているのでそちらを参照してください。

以下のコードは bin/rails g scaffold User した controller の create メソッドのコードを一部加工、抽出したものです。user_params メソッドを読むのにちょっと下のほうにジャンプして戻ってこなければいけませんが、基本的にファイルの上から下に読み進めていけます。

class UsersController < ApplicationControllerdef create
    @user = User.new(user_params)

    respond_to do |format|
      if @user.save
        format.html { redirect_to @user, notice: 'User was successfully created.' }
        format.json { render :show, status: :created, location: @user }
      else
        format.html { render :new }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  private
    def user_params
      params.fetch(:user, {:first_name, :last_name})
    end
end

私は複数のオブジェクトが相互作用するようなメソッドを作成するときは、いつも controller のメソッドを思い浮かべます。このように流れがわかるようになっているか?それぞれの変数、メソッドは目次的な役割を果たしているか?と自問しながら実装しています。

主となるオブジェクトに対する操作がわかるように書かれている

定義しようとしているメソッドの中で、主となるオブジェクトに対する操作が見えるように記述レベルを揃えて書くことを意識すると、処理の流れが見えるコードになります。

先ほどの UsersController#create の例では、User モデルのインスタンスが主とするオブジェクトと設定されています。このメソッドの中で扱おうとしている User モデルのインスタンスに対する操作の様子が見えるように処理が書かれているため、流れがわかりやすくなっています。

「記述レベルを合わせる」とは、オブジェクトの生成から操作終了までを一貫してここのメソッド内に書くということです。open や close のような対になっている処理については、必ず同じメソッド内に記述します。open はあるのにclose がないと、本来セットで現れるべきものが現れないため不安を煽りますし、close を探す必要が出てきてしまいます。

場合によっては別のオブジェクト(例: Service クラスや Form Object など) に渡して処理をお願いすることもあるでしょうが、その場合は主とするオブジェクトに対して何をしているのかが明確にわかる名前にする必要があります。先に書いたように、変数やメソッドは目次に相当するので名前は重要です。必ず、主とするオブジェクトに対する操作がわかるような名前をつけましょう。

これらを3分クッキングに例えるならば、料理をする際はまな板のある作業台(今定義しようとしているメソッド) の上で材料を扱い、手順をみせます。手順のうち時間のかかるものについては「こちらに予め準備しておいたものがあります」とアシスタント(Serviceクラスや Form Object) に依頼して作ったとしても、必ずできあがったそれを作業台の上で扱うことで全体の流れと操作がわかりやすくなるようになっています。これと同じイメージです。 大きな処理を作る場合は、アシスタントの処理もアシスタント側で作業台の上で操作するようにすると、更にわかりやすくなると思います。

まとめ

今回は、私の読みやすいと思うコードということで、全体の流れがわかるコード、短期記憶を酷使しないコードが私にとっては読みやすいコードです、ということを表明し、どうすればそうなるのかについて少しだけ触れてみました。

構成だけでなく読みやすさ/読みにくさを考えてコードを書くのは大変な行為なのですが、ファイルやコードの構成上優れていたとしても読みにくいコードは理解しにくいものです。理解がしにくいと手を入れるモチベーションが低下してしまうので、大変ですが将来修正する自分のためだと思ってやっていきましょう。