影響範囲は何のことなのか?ミスのない仕事をするための重要なこと

こんにちは! TECH DRIVEのTedです。

今回は影響範囲という言葉についてご紹介したいと思います。

未経験で現場に入られた方や現場に入って日が浅い方に多いと思いますが、業界で使われる語彙の意味をとらえきれずに苦しい思いをされている方も少なくはないと思います。

ちなみに筆者(Ted)は現場に入って7〜8ヶ月ほどになりますが、現場に入った瞬間から様々な語彙に囲まれて四苦八苦しました(今もですがw)

日常的に使う語彙の意味を知っておくことはチームでお仕事する時に役立ちますので、ぜひ抑えていただければと思います!

*チームごとに使用される語彙の差はあると思いますが、弊社では当記事で紹介する内容を「影響範囲」と呼んでおりますので下記も同様に進めさせていただくことをご了承ください。

[解説編] 影響範囲って何ですか?

ここでいう「影響範囲」とは、何かしらのコードを書いたとき、そのコードが影響を及ぼす範囲のことを差します。

言葉だけでは説明がつきにくいので、例をあげてみましょう。

ここではRuby on Railsを題材に取り上げます。

例えば、Userモデルが姓: last_name名: first_nameという情報(カラム)を持っていたとします。

そしてユーザーのフルネームを表示するために下記のようなメソッドが定義されているとします。

class User < ApplicationRecord

  def name
    last_name + ' ' + first_name
  end

end

何度も使われそうな処理はメソッドにしてしまうと便利なのです。

そしてユーザーのフルネームが呼びたくなっと時はこんな風に使えそうですね。

user.name

このような便利なメソッドがあるといろんな場所で使われそうです。

しかし、複数の場所で使われるということは、このnameに変更が発生した場合、このメソッドを呼び出している箇所全てが「変更の影響」をうけるようになります。

[まだまだ解説] 影響範囲って何が重要なの?

さらに重要な話になってきます。

影響範囲を意識せずにコードに変更を加えると、予期せぬ場所でバグを埋め込んでしまったりサービスの動きが変わってしまったり最悪の場合、本番環境でサービスが望む動きをしなくなる可能性も十分にありえます。

先ほど挙げたUserモデルとnameというメソッドで例を挙げていきましょう。

nameというメソッド名を変更しなければいけないことになったとしましょう。

下記のように修正するはずです

class User < ApplicationRecord

  def name # name → full_nameへ
    last_name + ' ' + first_name
  end

end

nameメソッドがfull_nameに変更されたということは、プロジェクトでnameメソッドを使っている箇所を全てfull_nameへ修正しなければエラーが発生してしまいます。

なので、nameを変更することで影響を受ける範囲を洗い出し、同様に修正をかける必要があります。

もしも修正漏れがあった場合、今回の例だとエラーによってユーザーが画面を開くことができなくなってしまいます。

そしてこの変更が誰のレビューの目にも止まらずに本番にデプロイされてしまったときは致命的なミスになります。

というように今回は表示するメソッドを例えに挙げましたが、大きな例でいうとお金の支払いに関する処理で同様のことが起きると本当に大変であることがわかります。

なので影響範囲を確認することは重要で、確実な仕事をするためには必要不可欠な要素です。

次からは、じゃあどうやって影響範囲を調べるの?に迫ります!

[実践編] 影響範囲の見極め方

では、実際に影響範囲をどうやって調べて見極めるのかが大事になってくると思います。

実際に私が現場に入りたてのころ使っていたやり方をご紹介しようと思います。

ご紹介する方法は現在では古典的なやり方なので、お使いの開発環境によってはより良い方法があると思いますが、知っておいて損は無いので眺めてみてください。

今回の場合ならば、nameというメソッドがプロジェクト内のどこで使われているかを知る必要があります。

nameはUserモデルのメソッドでuserオブジェクト.nameのように使われるはずです。

ここでLinuxコマンドのgrepを使います。

grepはファイル内の文字列に対して指定した文字列(正規表現)に該当する文字列を検索するコマンドです。

grepには-rというオプションがあるのでファイル単体だけでなくディレクトリごと指定することができます。

なので

grep -r name app/*

みたいに検索するとappディレクトリ以下でnameの文字列が書かれている場所を一気に検索してくれます。

これでプロジェクトでnameが使われている場所の全てがわかります!

あとはヒットした箇所が自分の変更によって変わるべきなのかを吟味すると良いでしょう。

ちなみに私の場合は検索結果をリスト化して残し、動作チェックする時に使用したりもしますし、チームのメンバーに相談する時の良い材料にもなります。

Rubyを用いた開発をしていてRubyMineをお使いの方の場合は、RubyMineの便利機能がありますのでそちらを使うとなお強力な検索ができます。

下記の記事の検索系やコードジャンプを使うとgrep相当のことが手軽にできます。

RubyMine以外の多くのエディタの場合、一括検索もしくは全文検索という名前でgrep相当の機能がありますのでそちらを活用するのも良いでしょう。

dev.techdrive.top

コラム

今回検索した文字列はnameでしたが、表記方法には数種類あります。

  • スネークケース&キャメルケース →クラス名はキャメルケース、テーブル名はスネークケースだったりします。
  • シンボル表記

これらを全て網羅した検索ができると、影響範囲の見積もり漏れをよりしっかり防ぐことができるでしょう。

終わりに

影響範囲は開発作業をする上で非常に重要な要素であり、必要不可欠と言えるでしょう。

逆に検索する能力をしっかり高めて、これを抑えた仕事をきっちりできる人は信頼されるはずです。

一度癖つけると自然と体がついてくるので是非実行してみましょう!

PR

TECH DRIVE協賛企業のサークルアラウンド株式会社では、プログラマーの成長を加速させるためのトレーニングを行なっています。フロントエンド/バックエンド問わず各種バリエーションがございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

Ruby Climbing

週1からはじめられる「Ruby」でWEB開発の基礎が習得できる塾です。現役のプログラミング講師&Rubyエンジニアがプログラミング入門からフレームワーク(Sinatra/Ruby on Rails)を使用した本格的なWEB開発の学習までをしっかりとサポートします。

ruby climbing

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

RubyMine便利機能のご紹介

こんにちは、TECH DRIVEのTedです。

今回はRubyMineが持っている便利な機能についてご紹介したいと思います。

弊社ではメンバー全員がRubyMineを使って開発を行なっています。

先日、RubyMineの機能をどこまで引き出せているかを会話したのですが、意外とメンバー間で持ち合わせている知識にバラツキがあることに気がつきました。

会話の中ではお互い「なるほど」「それクールだw」等、良い知識の交換会になりました。

RubyMineは有料のエディタなので、お使いになられていない方は「ほうほう、こんな感じなのか」と若干でもRubyMineの感じを知って頂ければと思います。

*弊社ではソースコードの管理にGitを使用しており、その前提でのご紹介となりますのでご了承ください。

ご紹介機能一覧

どれも強力ですが、★は取り分け強力で超便利機能

  • 機能検索機能(Find Action)★
  • コミット(Commit)
  • プッシュ(Push)
  • コードジャンプ1(Forward, Back)
  • コードジャンプ2(Declaration) ★
  • 全体検索(Find in Path) ★
  • ファイル内検索(Find)
  • 全体ファイル検索 ★
  • ファイル内置換(Replace)
  • 全体置換(Replace in Path)
  • Railways ★
  • Database ★

アクション検索機能

Command + Shift + A

RubyMineで使用できる機能を検索することができます。この機能でご紹介機能一覧の()の文字で検索すると各機能にヒットするはずです。

キーバインドを設定したけど、「何を押せば・・・」みたいにバインドを忘れた時なんかや、直感的に検索すると使いたい機能がヒットすることもあります。

例えば、RubyMineからGit操作を行う場合、branch newとかで検索するとブランチを作ってくれる機能がヒットしたりします。

これがあればどんな機能でも探して実行できるので超便利です。

コミット&プッシュ

コミット: Command + K

Gitのコミットメッセージや差分を1つのウインドウで操作することができます。コマンドラインでやらなくて良いので結構楽になります。

右下の Commit を押せばコミット完了!

f:id:travy:20190412111530p:plain

プッシュ: Command + Shift + K

コミット一覧をコミットメッセージと一緒に確認することができます。

右下の Push でプッシュできます。Push ボタンの右のプルダウンで Force Pushも選べます。

f:id:travy:20190412111855p:plain

コードジャンプ(Forward, Back)

ツールバー NavigateのForward,Back, Declarationを呼び出す方法です。

使うと自分が作業していた前後のファイルへジャンプできます。

Forward: Option + Command + →

Back : Option + Command + ←

もっと使えるコードジャンプ(Declaration)

Command + B

こいつは使えます!これを使いこなせると開発やデバックやコードリーディングのスピードが段違いにUpすること間違いなしです。

  • 自分で定義した変数やメソッドにジャンプ
  • ViewからControllerやModelにジャンプ
  • Gemが提供しているメソッドにジャンプ
  • レンダリングでしてしているファイルへジャンプ
  • メソッドを使用している箇所へジャンプ

とにかくファイルを超えて定義元や対象のファイルへジャンプすることができます。

困ったときには Command + B でジャンプしまくると良いでしょう。

最後の、メソッドを使用している箇所へジャンプを利用した応用テクニックがあります。

メソッドの定義元で Command + B をすると使用している箇所の一覧が表示されます。

こうすることで、メソッド自体を修正するときに影響範囲を明確にすることができます。

デバッグ作業をしている時なんかに、メソッドのロジック自体に問題があるので迂闊に修正をかけると思わぬところでバグが出たり挙動が変わってしまった、みたいなことが発生することがあります。

しかし、このコードジャンプをマスターしていれば修正する前に影響範囲を探ることができます。

修正したいメソッドの影響範囲が大きすぎる場合、対象のメソッドを修正した後の大量の動作確認がもれなくついて来て時間が無駄に過ぎてしまいます。

なので別のメソッドを作ってしまう等、別の方法で開発を進めることができるかもしれません。

コードジャンプはファイル移動を楽にするだけでなくて上等な検索手段であり、プロジェクト内の情報収拾の強力な道具になります!

検索機能

ファイル検索: Command + Shift + O

f:id:travy:20190412121553p:plain

プロジェクト以下のファイル検索をやってくれます。

検索ヒットしたファイルにジャンプすることができます。

「あのファイル開きたいからあのディレクトリ開いて、開いて、開いて、、、」

面倒だと思います。

ファイル名を記憶しているのであれば検索して一気にジャンプすると楽ですね。

僕はschema.rbをよく開くのですがプロジェクトのルート→db→schema.rbと移動がとにかく面倒なので1発検索で開いてますw

ファイル内検索: Command + F

ファイル内の指定した文字列を検索することができます。

全体検索

Command + Shift + F

f:id:travy:20190412123242p:plain

こいつも強力です。もっと使えるコードジャンプでも紹介しましたが、影響範囲の情報を知る上で欠かせないツールです。

やってる内容は指定した文字列がプロジェクト内で使われていたら一覧に表示するだけなのですが検索の仕方によっては強力な武器になります。

例えば、定義したインスタンス変数やメソッドを全体検索すると自分が修正する箇所がどこに影響するのかを知ることができます。先ほどと重複するので割愛しますが、影響範囲を特定することはデバッグに置いて重要なことなのでコードジャンプと合わせて使うとより正確なデバッグができます。

さらに全体検索には他と違う機能があります。

検索結果(画像)右下のOpen in Find Windowを開くことで検索結果をファイルツリーにして表示してくれます。

f:id:travy:20190412124321p:plain

さらに!画像左下の赤枠のボタンを押すと検索結果一覧をテキストにして表示してくれます。

f:id:travy:20190412124450p:plain

こいつを丸ごとコピーしてエクセルやGoogleスプレッドシートに貼り付けると影響範囲兼、動作確認チェックシートができたりするので確認の抜け漏れを防いだりするのにすごく便利です。

僕はよくこの要領でチェックシートを作ってたりするのでおすすめです。

ファイル内置換&全体置換

ファイル内置換: Command + R

全体置換: Command + Shift + R

検索方法はCommand + FCommand + Shift + Fと同じです。

あとはReplaceを押すだけで置換してくれます。

検索結果を全部一気に置換したいときはReplace Allを使ってみるのもありです。(ただし影響範囲にご注意ください)

こいつは正規表現で検索することができるので一意の文字列ではなくともまとめて検索をヒットさせることができるのである特定の文字列を一気に置換したいときに正規表現で検索をかけると便利だったりします。

僕がseedファイルを修正する時なんかは正規表現でまとめてヒットさせて一気に置換したりしてますw

Railways

これは超便利なプラグインです。RubyMineをご使用されている場合はぜひインストールをお勧めします。

Railwaysは何ができるようになるかですが、bin/rails routesで表示されるルーティング一覧をさらに見やすく表示してくれるプラグインです。

インストールすると右上にRoutesというタブが出現してプロジェクトが提供するURLとそれにひもづくControllerのアクションと名前付きパスが表示されます。

さらに上の検索窓でパスを検索することができるので必要なルーティングだけを表示することができます。

f:id:travy:20190412143651p:plain

プラグインのインストールはPreferences(Command + ,)Pluginsでできます。

Database

プロジェクトで使用するデータベースにアクセスすることができます。

アクセスすることでRubyMine上で主に以下の操作をすることができます。

  • レコードの作成、更新、削除
  • SQL文の実行

何が嬉しいかというと、開発中にあるレコードだけを更新させたいけどブラウザ上でできない or 面倒であるときなんかにコマンドラインで

User.find(1).update(name: 'hoge')

のようにレコードを更新する処理を叩くと思いますがRubyMineでデータベースに接続しておくとエディタ上で全て完結させることができます!

こんな感じ

f:id:travy:20190412145235p:plain

接続方法が少し難しいので簡単にご紹介します。

エディタの右上にDatabaseというタブが出ていると思うのでクリックして'+ → Data Source → + 今使っているDBの種類を選択します。

f:id:travy:20190412145651p:plain

すると次の画像のようなウインドウが表示されますが、初めて設定する場合は訳がわからないかも知れないです。

ですが、複雑な方法でDBにアクセスしているプロジェクトでない限り必要なことは実はそこまで多くありません。

Ruby on Railsで開発されている方の場合はconfig/database.ymlにそこに入力する内容は揃っているはずです。

ではconfig/database.ymlの情報を使ってDBに接続してみましょう。

以下のウインドウで入力します。

f:id:travy:20190419124002p:plain

ウインドウのDatabaseの入力蘭に使用しているデータベース名、Userにはusernameの内容、Passwordにはpasswordの内容を入力して

Test ConnectionをクリックしてDBにテストアクセスをします。上手くいけば緑色でSuccessfulと出るはずで、あとは右下のApplyをクリックして接続完了です!

再度右上のDatabaseタブを開くと接続完了したデータベースが表示されるので▶︎をクリックして開いていくと自分が作った見覚えのあるテーブル名が並んでいるはずです。

それをクリックすることでデータベースを開くことができます。

f:id:travy:20190412151430p:plain

レコードの更新はカラムに変更内容を入力してDBと書いてあるボタンをクリックするだけです

あとは好きな時にデータを作ったり更新したり削除したりできるのでコマンドラインで一回一回メソッドを叩くよりも素早くレコードの変更ができてしまいます。

f:id:travy:20190412151845p:plain

まとめ

今回ご紹介したものはよく使う機能のみなので是非試してみてください。

RubyMineならではの機能もあるので、せっかくのエディタを最大限活かせるようになると開発スピードも上がるしもっと楽に開発できるようになるはずです。

筆者である僕自身もまだまだ知らない機能もあるのでいろいろ試したいと思います。面白い機能を見つけた時はまた記事にてお会いしましょうw

PR

TECH DRIVE協賛企業のサークルアラウンド株式会社では、プログラマーの成長を加速させるためのトレーニングを行なっています。フロントエンド/バックエンド問わず各種バリエーションがございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

Ruby Climbing

週1からはじめられる「Ruby」でWEB開発の基礎が習得できる塾です。現役のプログラミング講師&Rubyエンジニアがプログラミング入門からフレームワーク(Sinatra/Ruby on Rails)を使用した本格的なWEB開発の学習までをしっかりとサポートします。

ruby climbing

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

新しく加わったRuby on Railsプロジェクトを素早く把握するコツと便利なツールのご紹介

こんにちは、TECH DRIVEのTedです。

今回は新しく加わったRuby on Railsプロジェクトを素早く把握するための便利なツールをご紹介します。 本記事はこれから開発現場に入っていく方や、新しく別の現場に入られる方のお役に立てる記事になっていると思います。

本題

新しく入ったプロジェクトを手元のマシンにとってきた後に環境構築を始めると思います。
そして、例えばgithubを使っている現場であれば「さて、振られたissueを対応していきますか!」と開発を始めることになると思いますが、自分が加わったプロジェクトについての情報がないとスムーズに開発の流れに乗れないと思います。 逆に、情報が多いと効率よく開発作業を進めることができるはずです。

ここで情報が手元にない時に情報を集める方法がいくつかあるかと思いますが、 私の場合は下記の情報があると開発を始めやすいのではないかと思います。

  1. データベースの詳細、テーブル数やその相関関係
  2. ルーティングの規模感
  3. ディレクトリ構造

2~3についてはプロジェクトのコードを読むことで解決できます。例えば

2. ルーティングの規模感が調べたければroutes.rbを読んでみたり、bin/rails routesを実行してみると良いでしょう。

3. ディレクトリ構造rails newで自動生成されるディレクトリ以外についてで、よく使われているディレクトリではapp/decoratorsapp/services(このあたりはプロジェクトによって多少の違いはあります)のようなディレクトリです。見慣れないディレクトリ名を見つけた時は気をつけましょう。

今回は下記に焦点をあてたいと思います。

データベースの詳細を知りたい

先ほど、挙げた3項目の中で最も大事だと思います。

この1. データベースの詳細、テーブル数やその相関関係についてはER図があると把握しやすくなります。

ER図とは下記のようなテーブル同士の関係を示す図のこと

f:id:travy:20190327183149p:plain

schema.rbを参照することで同様にテーブル数や相関を読むこともできますが、ER図ではこのあたりを図で見ることができるので理解しやすく、schema.rbを見るよりもよりテーブル構造を把握することができるでしょう。

ER図をプロジェクトのチームが持っているならば共有してもらえば良いのですが、持っていない場合もあります。

そんな時に自動でER図をPDF形式で生成してくれるrails-erdというgemが存在します。

rails-erdの導入

Gemfileに以下のような記述をします。本番環境で使うことはないのでdevelopment環境で使えるようになっていればOKです。

*Gitを使用して開発をしている場合、既存のコードを汚さないために新しく作成したブランチで作業することをお勧めします。

group :development do
  gem 'rails-erd'
end

続いてgemをインストールします

bundle install

これで準備完了です!あとはER図を出力するだけです

bundle exec erd

下記のような図が出力されます。

f:id:travy:20190327154800p:plain

ER図をコマンド1発で手に入れることができるのは非常に強力だと思います。

ER図があると何が良いのか

ここで、ER図があると何が良いのかおさらいしておきましょう。

ER図があると親子関係や中間テーブルのようなテーブル同士の相関を一目で見ることができ、各テーブルがどのようなカラム、つまりどのような情報を持っているのかも把握することができますし、開発する時にもテーブル同士の関係性を確かめながら進めることができます。

さらに、図で見ることでサービスにおいて重要なテーブルや機能を推察するためのヒントが得られます。

ER図から得た情報は、プロジェクトの中で目を通しておくべきコードの把握や、その意味を理解するための助けになります。

終わりに

ER図が手元にある状態でコードリーディングや開発作業をすると凄く心強い気持ちになれます。

余計な脳のリソースを使うことなくテーブル構造をインプットできるER図はとても強力です。

私もよく自分で作ったサービスだったり関係するプロジェクトのER図を今回ご紹介したrails-erdで描画しようとします。開発中も常にER図の画面を開きながら開発しています。

本記事を通して少しでも新規プロジェクトへの加入が楽になるのであれば幸いです。

PR

TECH DRIVE協賛企業のサークルアラウンド株式会社では、プログラマーの成長を加速させるためのトレーニングを行なっています。フロントエンド/バックエンド問わず各種バリエーションがございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

Ruby Climbing

週1からはじめられる「Ruby」でWEB開発の基礎が習得できる塾です。現役のプログラミング講師&Rubyエンジニアがプログラミング入門からフレームワーク(Sinatra/Ruby on Rails)を使用した本格的なWEB開発の学習までをしっかりとサポートします。

ruby climbing

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

脱・Rails初心者!子モデルのインスタンス生成と同時に親モデルに紐付ける方法

はじめに

こんにちは、TECH DRIVEのTedです。

今回はRuby、Ruby on Railsを学習されている初学者へ向けた、初学者から1歩レベルアップするためのコードの書き方をご紹介します。開発現場でよく使われる書き方の1つでもあるのでぜひ習得してみてください!

本題

親子関係のテーブルで親テーブルに紐づいた子テーブルのインスタンスを作るときに親のテーブルのidを簡単に持たせることができます。結果的にコントローラーやモデルに書く処理を少なくすることができます。

例えばユーザーの情報を持つusersテーブルとユーザーの投稿の内容を持つpostsテーブルがあったとします。下記のような感じ

f:id:travy:20190320151608p:plain

class User < ApplicationRecord
  has_many :posts
end

class Post < ApplicationRecord
  belongs_to :user
end

userspostsの親になります。そして、サービスの中でユーザーが入力した内容をpostsテーブルのデータとして保存する処理があるとします。

ツイッターをイメージすればわかりやすいと思います。ユーザー(users)とツイート(posts)のような関係です。

ここで子テーブルと親テーブルを紐づけるときにいくつか方法がありますが普通に書くと下記のようになると思います。

class PostsController < ApplicationController
  before_action :set_user
  # 他のアクション
  def new
    @post = Post.new
  end
  
  def create
    @post = Post.new(post_params)
    @post.user_id = @user.id
    if @post.save
    # 保存が成功した時の処理
    else
    # 失敗したときの処理
    end
  end
  
  private
  def set_user
    @user = User.find(params[:user_id])
  end
  
  def post_params
    params.require(:post).permit(:title, :body)
  end
end

上記は特に問題はないのですが、さらに下記のように書くこともできます。

class PostsController < ApplicationController
  before_action :set_user
  # 他のアクション
  def new
    # ここがポイント!↓
    @post = @user.posts.new
  end
  
  def create
    # ここがポイント!↓
    @post = @user.posts.new(post_params)
    if @post.save
    # 保存が成功した時の処理
    else
    # 失敗したときの処理
    end
  end
  
  private
  def set_user
    @user = User.find(params[:user_id])
  end
  
  def post_params
    params.require(:post).permit(:title, :body)
  end
end

2つのコードの違いはnewcreateアクション の中にあります。
Postモデルのインスタンスをnewするときに親であるUserモデルに紐づけてnewしているかどうかです。
こうすることで新しく作られたPostモデルのインスタンスは親であるUserモデルのid、つまりuser_idを最初から持っている状態になります。
逆にPost.newで作るとuser_idはあとでセットしなくてはなりません。

# これが
@post = Post.new(post_params)
@post.user_id = @user.id
# こうなる
@post = @user.posts.new(post_params)

これで少ないコードで実装できて投稿される内容はユーザーに紐づいていることがすぐにわかりそうです。

おしまいに

今回はコントローラーに処理を書いてご紹介しましたが、親子関係の子にあたるモデルのインスタンスを作るときは他の場所でも同じことができます。(例:モデルのメソッド内とか)

こういったコードが書けるだけで他の初学者と1歩差をつけることができますし、現場のエンジニアが見ても「こいつ、わかってるな」とアピールすることができます。

私もよく自作のサービスで前者のようなコードをよく書いていました。 しかし、同じ動作が期待できるコードでも書き方によってあとで見る人やあとで自分で見たときに「これは何か意味があるのだろうか?」と迷ってしまうこともあります。

ご自分でサービスやコードを書かれている方は、本記事の内容を踏まえてもう1度自分のコードを眺めて見るのも良いかもしれません。
このような細かい配慮の積み重ねが読みやすいコードに繋がります!

PR

TECH DRIVE協賛企業のサークルアラウンド株式会社では、プログラマーの成長を加速させるためのトレーニングを行なっています。フロントエンド/バックエンド問わず各種バリエーションがございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

Ruby Climbing

週1からはじめられる「Ruby」でWEB開発の基礎が習得できる塾です。現役のプログラミング講師&Rubyエンジニアがプログラミング入門からフレームワーク(Sinatra/Ruby on Rails)を使用した本格的なWEB開発の学習までをしっかりとサポートします。

ruby climbing

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

【古典】 jQueryでSPAする時のポイント。もしくはオブザーバーパターンのサンプル。

TECHDRIVE の佐藤です。今回は書いてみたコードについて解説しつつ進めたいと思っています。

はじめに

歴史は繰り返しているので「またそういう話か」ということはよくあるのですが、いかに流行っている(流行っていそうな)ライブラリやフレームワークでも、多くの開発現場にいきなり導入できる事は少なく、古い技術を使ってやりくりしている事は多くあります。

特にJavaScriptでSPAなどのところは「どうしても必須でこれが無いと成り立たない」というレベルにできる業務領域がまだそれ程大きく無い為か、私や私の知り合いが対応している開発の現場では未だにjQueryでDOM操作がかなり多いと思われます。

そういう中でも「この部分だけ、ここだけで良いからSPAぽくしたい」というような要望はたまにあります。そういう時にいきなりVue.jsReactが導入できるような現場も稀有なのでは無いでしょうか(その背景についてはここでは論じません)。

というわけで今回はjQueryでSPAしたサンプルの解説記事です。jQuery使ってもごちゃごちゃにならない考え方もそこそこ入っていると思います。

作ったもの

https://github.com/CircleAround/message_server_js ここにあります。動作サンプルは以下です。 https://message-server-js-app.herokuapp.com/

内容としては https://message-server-app.herokuapp.com/ に置いてあるごくごく簡単なAPIサーバー(ソースはこちら)の、JavaScriptでのサンプルです。

今回の実装では「jQuery&レガシーバニラJS」縛りでお送りしています(forEachとかfind使ったのは許して下さい)。

ポイント

オブザーバーパターンを利用してViewとロジックを分ける

古式ゆかしいクライアント実装の設計ではMVCがよく用いられていると思いますが、大事なのはMとVの間がオブザーバーパターンで書かれていることだと思います。

「ViewはModelの詳細について知っているが、ModelはViewについて無知であ る」

ということをキッチリすると、どれが大事なロジックであり、どれが描画の為のコードなのかが明瞭になります。

オブザーバーパターンは 「データの状態の変化を自ら通知せずに、勝手に取得させる」 という考え方なので、それに忠実に書きます。 クラス名のプレフィクスはModelにする方が良いかもしれませんが、趣味でMessageServiceとしています*1

https://github.com/CircleAround/message_server_js/blob/master/main.js#L89

MessageService はクライアントで保持しなければいけない以下のような情報を持っています。

  • ログインしたユーザー
  • 表示しなければならない全てのメッセージ

また、情報の変化を受け取るリスナを仕掛けられます。リスナの実装は複数考え方があるとは思いますが、今回はシンプルに「変数に関数を設定できるだけ」にしています。以下のあたりです。

https://github.com/CircleAround/message_server_js/blob/master/main.js#L344

    this.service.onChangeMessages = function() {
      _this.updateMessages(_this.service.messages); // メッセージ一覧に変更がなされたメッセージの見た目を更新する
    };

    this.service.onChangeTargetMessage = function() {
      _this.updateForm(); // 編集のためのターゲットメッセージが変更されたら、フォームを更新する
    };

保有している状態が通信の結果などで変化した際に、このリスナを実行するようにしています。例えば signup関数が呼ばれると、通信が成功すれば onSignUp が呼ばれるようにしています。

  MessageService.prototype.signup = function (email) {
    var _this = this;
    return this.handleError(this.connector.signup(email).then(function(data){
      _this.user = data;
      _this.onSignUp();
    }));
  }

長い文字列はHTMLから取得して編集しやすくする

https://github.com/CircleAround/message_server_js/blob/master/index.html#L48

このあたりです。<script></script> に typeをうまく指定すると中身が文字列であるような扱いをしてくれるので、これを使ってHTML内に文字列を埋め込みます。今回はメッセージ表示のDOM表示部です。

    <script id="message_template" type="text/template">
      <div class="message">
        <div class="user">user:<span class="id"></span></div>      
        <div class="contents"></div>
        <div class="actions">
          <a class="edit" href="javascript:void(0)">編集</a>
          <a class="delete" href="javascript:void(0)">削除</a>
        </div> 
      </div>
    </script>

https://github.com/CircleAround/message_server_js/blob/master/main.js#L194

使う時にはIDでセレクタを辿ってから jQuery.text() を使っています。

文字列連結を避けてXSSを回避する

最近社内でも話題になった事ですが 「HTMLを文字列連結で作成してjQuery.html()でDOM化すると、XSSの危険性が増す」 ということがあります。文字列連結を行うと埋め込まれた変数が本当に安全かを由来を辿って確認する必要が出ててきてしまい、抜け漏れが発生しやすくなるということですね。

そこで私がよく用いる方法は 「jQuery.appendTo()を使ってDOM化して、その戻り値オブジェクトを介してjQuery.text()を利用する」 です。

https://github.com/CircleAround/message_server_js/blob/master/main.js#L220

      var $message = $(_this.messageTemplate).appendTo($messages);
      $message.find('.contents').text(message.contents);
      $message.find('.user>.id').text(message.user_id);

こんな形で、jQuery.appendTo()の戻り値が「今作成されたDOM構造」へのオブジェクトを戻してくれるので、そこからfind()で辿ってtext()で値を入れています。text()なら確実にエスケープされるのでこの効果を利用しています。

クラス的なオブジェクトを作成する時は無名関数で囲むとプライベートな関数が置けて便利

https://github.com/CircleAround/message_server_js/blob/master/main.js#L5

JavaScriptは都合よくスコープを作れると適切に書けそうな気がしています。クラス的なオブジェクトを作る場合にも、下記のようにスコープの中身が外に漏れ出ないことを利用しています。

var Connector = function() {
  
  // プライベートな関数群。これは公開されない。
  function request(params) {
    params['dataType'] = 'json';
    return $.ajax(params);
  }

  function url(path) {
    return endpoint + path;
  }

  function Connector(){} // ここからクラスの内容

  ...

  return Connector; // 最後にコンストラクタを返せばクラスだけ公開できる
}();

おしまいに

まぁ、滅びゆく技術ですよね。ただ、そういうものが必要な開発現場もあるので適切に利用できれば良いかと。

最近だと「フレームワークを使わないとうまく作れない」というような風潮を感じますが、便利なものが利用できない時でも上手に設計することでうまくやれるのではないかなと思いますし、そういう姿を目指していたいです。ReactをViewの部分に入れる変更をしてみるのもいいかなと思いました。差し替え可能感が感じられそうなのと、状態管理フレームワーク無しのReactはこういうところに価値がありそうに思うのです。

このコードはあえて縛っていますが、業務でやるなら外部ライブラリを少し入れます。これだと1ファイルが長過ぎますしね。そういうのも今後書くかもしれません。下記のようなことと組み合わせていく感じです。

dev.techdrive.top

PR

TECH DRIVE協賛企業のサークルアラウンド株式会社では、プログラマーの成長を加速させるためのトレーニングを行なっています。フロントエンド/バックエンド問わず各種バリエーションがございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

JavaScript Climbing

JavaScriptに特化したトレーニング「JavaScript Climbing」を行なっています。
this、クロージャ、Class等、本格的なフロントエンドの開発に臨むにあたり、必須となるスキル習得を現役のWEBエンジニアが徹底してサポートします。
無料体験期間もございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

circlearound.co.jp

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

*1:イメージ的にApplicationServiceに近い部分を担うため

JavaScript中級者へのステップアップ!callメソッドの使い所がわからないという方へ

こんにちは。TECH DRIVEの小笠原です。
今回は、JavaScriptのcallメソッドについてご紹介をしたいと思います。
callメソッドの使い所がいまいちわからないという方を対象としております。
また、本記事はJavaScriptにおける以下の知識があることが前提となりますので、予めご了承くださいませ。

  • thisの性質を理解している
  • プリミティブ値以外がオブジェクトであることを理解している
  • prototypeを理解している

callメソッドを知ろう

callメソッドは「関数を実行するための関数」です ※ 。
※ 同様のことを実現するメソッドとしてapplyが存在しますが、引数の渡し方等が異なるものの、実現できることはほぼ同じのため、applyの解説は割愛します。

「関数を実行するための関数」と聞いて「?」が浮かんだ方も多いのではないでしょうか?
順を追ってご説明していきます。

まずは、callメソッドの使い方をみてみましょう。

関数.call(thisに指定する値, 呼び出した関数に渡す引数情報)

callメソッドは、レシーバ(呼び出し元となる関数)を実行するメソッドなのですが、その際、第一引数に「実行する関数内でのthis」を指定することができます。
また第二引数は、callメソッドで呼び出す関数に渡す引数となりますので、関数が引数を必要としない場合は省略可能です。

まずは以下のコードを見てください。

var person = {
  name: 'Ken',
  greet: function () {
    console.log('My name is ' + this.name) 
  }
}

var dog = {
  name: 'Pochi'
}

person.greet() // 結果: My name is Ken
dog.greet() // 結果: Uncaught TypeError: dog.greet is not a function

person/dogという変数にオブジェクトを代入し、それぞれgreetメソッドを実行しています。 しかし、変数dogに代入されたオブジェクトはgreetというメソッドを持たないため、エラーとなっていますね。 これはみなさんも期待どおりの結果なのではないでしょうか?
次にcallメソッドでgreetメソッドのthisをdogに指定し、実行してみたいと思います。

person.greet.call(dog) // 結果: My name is Pochi

なんと、Pochiがしゃべりました。
personが持つgreetメソッドのthisをdogに指定したことにより、まるでdogがgreetメソッドを持っているかのような振る舞いになりました。
誤解を恐れずに言うなら、callメソッドを使用することにより、あるオブジェクトのメソッドを拝借し実行することができるのです。

しかし、これだけではまだメリットが見えてきませんね。 次に以下のコードをみてください。

HTML

<li class="item">hoge</li>
<li class="item">fuga</li>
<li class="item">piyo</li>

JavaScript

var $els = document.getElementsByClassName('item') // 結果: HTMLCollection(3) [li.item, li.item, li.item]
for(var i = 0; i < $els.length; i++) {
  console.log($els[i].innerText)
} // 結果: hoge fuge piyo

これは、itemという名前のclassをもったli要素のテキスト情報を出力するための処理です。
ここで注目していただきたいのは、getElementsByClassNameメソッドの返り値である「HTMLCollection」です。
このHTMLCollectionは、インデックス番号からデータの参照が行えることからも、配列に非常によく似たオブジェクトと言えます。
そのため、上のコードを以下のようにforEachを使用し、よりスマートに処理を書けるように変更してみたいと思います。

var $els = document.getElementsByClassName('item')
$els.forEach(function(val){
  console.log(val.innerText)
})  // 結果: Uncaught TypeError: $els.forEach is not a function

しかし、上記のコードを実行するとエラーが発生してしまいます。
なぜならforEachは配列(Arrayオブジェクト)のメソッドであり、HTMLCollectionはそのようなメソッドを持っていないためです。
このことから、HTMLCollectionは、フォーマットこそ配列と似ていますが、似ているだけで全く異なるオブジェクトであることがわかります。

しかし、この問題は、以下のようにcallメソッドを使用することで解決します。

var $els = document.getElementsByClassName('item')
Array.prototype.forEach.call($els, function($el){
  console.log($el.innerText)
})  // 結果: hoge fuge piyo

ここでcallメソッドの使い方をもう一度みてみましょう。

関数.call(thisに指定する値, 引数情報)

今回の場合、callメソッドのレシーバとなる関数は Array.prototype.forEach となります。
そして、そして第1引数の$els(HTMLCollection)がforEach実行時のthisとなります。
第2引数は実行する関数(forEach)へ渡す引数です。

この結果、forEachはまるでHTMLCollectionがもつメソッドかのように振る舞います。 今回の様に「あるオブジェクトのメソッドを拝借したい」「関数実行時にthisの値を強制したい」と思った時、callメソッドという選択肢を覚えておくと役立つシーンもあるはずです。

まとめ

いかがでしょうか?
callメソッドは、「thisの性質」や「オブジェクトへのより深い理解」が求められることからも、JavaScript中級者向けのメソッドであることは間違いないでしょう。
またメリットが掴みづらいという点も、理解のハードルを上げてしまう要因かと思います。
本記事を通して、少しでもcallメソッドの理解を深めていただけたのなら幸いです。

PR

TECH DRIVE協賛企業のサークルアラウンド株式会社では、プログラマーの成長を加速させるためのトレーニングを行なっています。フロントエンド/バックエンド問わず各種バリエーションがございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

JavaScript Climbing

JavaScriptに特化したトレーニング「JavaScript Climbing」を行なっています。
this、クロージャ、Class等、本格的なフロントエンドの開発に臨むにあたり、必須となるスキル習得を現役のWEBエンジニアが徹底してサポートします。
無料体験期間もございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

circlearound.co.jp

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

JavaScriptのthisの性質を知ろう! ~ 脱初心者をしたい方向け ~

こんにちは。TECH DRIVEの小笠原です。 JavaScriptを書いていると度々目にする「this」ですが、その性質に癖があるため、JS初学者の方にとってハードルになりがちです。
本記事では、JS中級者へのステップアップを目指される方に向けて、thisの性質を知っていただくための記事を書かせていただきました。

thisの性質を知ろう

誤解を恐れず言うなら、thisとは「JavaScriptが予め用意している変数である」と言えます。
基本的にthisは、グローバルオブジェクトを指します。
例えば、以下のようにグローバルスコープでthisを参照するとWindowオブジェクトを返します。(※ クライアントサイドで動作するJavaScriptの場合)

console.log(this) // Window {省略}

また、以下のようにグローバルスコープ直下に定義した関数内でthisを参照した場合も、同じくWindowオブジェクトがthisの値となります。

function hoge () {
  console.log(this)
}
hoge() // Window {省略}

このようにthisは、基本的にグローバルオブジェクトを指します。
※ ただし、「strictモード」が有効となっている場合、関数内のthisは基本的にundefinedとなりますので、注意が必要です。
ここで「基本的に」とつけたのは、thisの値が上記の限りではないためです。
本章の冒頭で述べたようにthisは変数であるため、その値が一定のものを指すとは限らないのです。

thisが変化する4種類のパターンを知ろう

関数内のthisは、関数の呼び出し方によって値が変化することがあります。
抑えておくべきは、以下4パターンで関数が実行された場合、いずれもthisの値が異なるという点です。

  • 1.関数として呼ばれた場合
  • 2.メソッドとして呼ばれた場合
  • 3.コンストラクタ関数として呼ばれた場合
  • 4.call, applyから呼ばれた場合

それでは順番に見ていきましょう。

1. 関数として呼ばれた場合

これは、冒頭で例としてあげたグローバルスコープに定義された関数内での話になります。
先ほどお伝えした通り、この場合thisはグローバルオブジェクトを返します。 ※ 繰り返しとなりますが、「strictモード」が有効となっている場合、thisはundefinedとなります。

2. メソッドから呼ばれた場合

次は、あるオブジェクトに属するメソッド内で参照されるthisについてです。
例えば以下のようなオブジェクトがあるとします。

var person = {
  name: 'Ken',
  greet: function() {
    console.log('My name is ' + this.name)
  }
}

さて、上記のコードの次に person.greet() を実行した場合、結果はどうなるでしょうか?
「1. 関数の中で呼ばれた場合」同様に、thisがグローバルオブジェクトを指すのであれば、Windowオブジェクトのnameプロパティが参照され「My name is Window」となってしまいそうですね。

しかし、greetメソッドの実行結果は「My name is Ken」となります。
これは、あるオブジェクトのメソッド内で呼ばれたthisは、メソッドが所属するオブジェクトを指すようになるためです。
そのためgreet内でのthisは、person(厳密には変数person代入したオブジェクト)となるため、上記のような結果となります。

3. コンストラクタ関数から呼ばれた場合

次にコンストラクタ関数内でのthisについてです。
本章では、thisと合わせコンストラクタ関数に関する最低限の情報も合わせてお伝えしたいと思います。
コンストラクタ関数とは、乱暴な言い方をしてしまうとnewというワードを使用し実行する関数になります。
まずは、以下のコードを見てください。

function Person (gender) {
  this.gender = gender
  this.isMale = function() {
    return this.gender === 'male'
  }
}

// 通常の関数として実行
Person('male') // 結果: undefined

// コンストラクタ関数として実行
var person = new Person('male') // 結果: Person {gender: "male", isMale: ƒ}
// コンストラクタ関数の返り値(インスタンス)に含まれるメソッドを実行
person.isMale() // 結果: true

上のコードでは、Personという関数を定義し、まずは通常の関数として実行しています。
この結果は「undefined」※ となります 。
※ JavaScriptでは、関数内で明示的に返り値を指定(return)していない場合、関数はundefined(未定義値)を返す仕様になっているのです。

しかし、コンストラクタ関数として実行した場合、関数の返り値がオブジェクトになっていることが確認できます。
これは、コンストラクタとして実行した関数は、内部で動的に生成したオブジェクト(これをインスタンスと言います)を返すようになるためです。
そしてこのインスタンスの中には、関数内で「this.プロパティ名=値」とした結果が、プロパティ/メソッドとして入っていることが確認できます。
上のコードではnew Person('male’)の結果(インスタンス)を変数personに代入していることになります。
故に最終行では person.isMale() が正常に動作していることが確認できます。
ここでは、コンストラクタ関数内でのthisは、自身が返すインスタンスを指すことを抑えておきましょう。

4. call, applyから呼ばれた場合

さて、ここまでthisの性質と合わせ様々な関数の呼び出し方をみてきました。
しかし、関数の呼び出し方は、これだけではありません。
本記事において詳細は割愛しますが、JavaScriptでは、callやapplyといった「関数を実行するための関数」が存在します。
callやapplyは以下のように使用され、関数を実行する際にその関数内におけるthisを第一引数で渡した任意の値にすることができます。

関数.call(thisに指定する値, 呼び出した関数に渡す引数情報)

call/applyの理解については、本記事の内容はもちろん、オブジェクトに関するより深い理解も求められます。
そのため本記事で詳細に触れることはしませんが、call/applyが気になるという方は、以下の記事をご一読ください。

dev.techdrive.top

ここでは、call/applyを使用し関数を実行した場合、関数内でのthisの値は、プログラマが指定した任意の値になるということを覚えておきましょう。

おまけ Class内でのthisについて

JavaScriptのClassを使用したコードを見たことがある方は、その内部でもthisが使われているのを目にしたことがあるのではないでしょうか?

Class内でのthisは、先程ご紹介した「コンスラクタ関数内でのthis」とほぼ同じと言えます。
Class内のメソッドから参照するthisは 「new Class名」 実行時に生成されるインスタンスを指すことを覚えておきましょう。

まとめ

いかがでしょうか? JavaScriptの学習を進めていると必ず出てくるthisですが、本記事でもご紹介した通り、関数内のthisは「関数の呼出し方」によって値が変わります。
このthisの柔軟性が学習時のハードルになりがちです。
本記事を通して、少しでもthisへの理解を深めていただけたのなら幸いです。

PR

TECH DRIVE協賛企業のサークルアラウンド株式会社では、プログラマーの成長を加速させるためのトレーニングを行なっています。フロントエンド/バックエンド問わず各種バリエーションがございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

JavaScript Climbing

JavaScriptに特化したトレーニング「JavaScript Climbing」を行なっています。
this、クロージャ、Class等、本格的なフロントエンドの開発に臨むにあたり、必須となるスキル習得を現役のWEBエンジニアが徹底してサポートします。
無料体験期間もございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

circlearound.co.jp

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

TECH DRIVEについて

TECH DRIVEは「技術者の成長を加速させる」をキーワードに都内で活動をしているコミュニティです。
TwitterやFacebookにて技術ネタやイベント情報の発信を行っていますので、ご興味があれば、いいねやフォローをお願いいたします。