T_Y_CODE

プログラミング学習記録

学習記録 5日目

5日目の学習記録をまとめていきます。
今日からGitの勉強をします。

学習計画

  • プロを目指す人のためのRuby入門 11, 12章, 付録
  • リーダブルコード 10章
  • Udemy講座 もう怖くないGit!チーム開発で必要なGitを完全マスター CHAPTER 1-3

学習内容

プロを目指す人のためのRuby入門 11章

  • 以下はエラー発生時に出力されるバックトレースの例である。
    • NameErrorは例外クラス名です。
    • undefined local variable or method 'distanse' for #がエラーメッセージです。日本語にすると「未定義のローカル変数またはメソッド'distanse'がGateクラスに対して呼ばれた」となっている。
    • #はdistanseが呼び出されたオブジェクトの情報。Gateがクラス名, 0x007fa4ce148320がobject_id, @name=:mikuniはこのオブジェクトが保持しているインスタンス変数。
    • Did you mean? distanceはもしかしてdistanceを使おうとしました?という意味
    • 3行目以降のことをバックトレースという。/(PATH)/lib/gate.rb:36:in 'calc_fare'は/(PATH)/lib/gate.rbというファイルの36行目の'calc_fare'メソッドの中でエラーが起きてますよという意味。
    • 4行目以降は上の行より前に呼び出された呼び出しが記録されています。
NameError: undefined local variable or method 'distanse' for #<Gate:0x007fa4ce148320 @name=:mikuni>
Did you mean? distance
  /(PATH)/lib/gate.rb:36:in 'calc_fare'
  /(PATH)/lib/gate.rb:26:in 'exit'
  test/gate_test.rb:33:in 'test_juso_to_mikuni'
def calc_fare(ticket)
  from = STATIONS.index(ticket.stamped_at)
  to = STATIONS.index(@name)
  distance = to - from
  FARES[distanse - 1] #ここでエラー
end
  • NameErrorは未定義のローカル変数, 定数, privateメソッドを呼び出したときに発生する。単純にタイプミスしている場合もある。requireを忘れている場合もある。
// タイプミスでエラー
NameError: undefined local variable or method 'distanse' for #<Gate:0x007fa4ce148320 @name=:mikuni>
// fateライブラリをrequireせずにDateクラスを使用
NameError: uninitialized constant Date
  • NoMethodErrorは存在しないメソッドを呼び出そうとした場合に発生する。
    • メソッド名の打ち間違え。
    • レシーバの型(クラス)が想定していた型と異なる
    • レシーバが想定に反してnilだった。
NoMethodError: undefined method 'stamped_on' for #<Ticket:0x007fab78013990 @fare=150, @stamped_at=:umeda>
NoMethodError: undefined method 'chars' for :japan:Symbol
NoMethodError: undefined method 'fare' for nil:NilClass
  • TypeErrorは期待しない型(クラス)がメソッドの引数に渡された場合に発生する。
TypeError: String can't be coerced into Integer
  • ArgumentErrorは引数の数が違ったり、期待する値ではなかった場合に発生する。
ArgumentError: wrong number of arguments (given 0, expected 1)
ArgumentError: negative argument
  • ZeroDivisionErrorは整数を0で除算しようとした場合に発生する。
ZeroDivisionError: divided by 0
  • SystemStackErrorはシステムスタックが溢れたときに発生する。よくある例としてメソッドを再帰呼び出しした場合に発生する。
class User
  # 省略
  # def name=(name)のように書くつもりだった
  # nameメソッド内でnameメソッドを代入しているため無限ループ
  def name
    @name = name
  end
end
user = User.new
user.name # => SystemStackError: stack level too deep
  • LoadErrorはrequireやloadの実行に失敗した場合に発生する。
    • requireしたいファイルのパスやライブラリ名が間違っている。
    • requireしたgemが実行環境にインストールされていない。
  • SyntaxErrorはendやカンマの過不足がある場合に発生する。よくこのエラー起きます笑
  • 外部ライブラリ(gem)やフレームワークで定義された例外クラスが表示される場合もある。Railsでのエラー例を以下に示す。
    • ActiveRecord::RecordNotFoundという例外クラスが使用されている。
ActiveRecord::RecordNotFound: Couldn't find User with 'id'=99
  • printデバッグはprint文をプログラムに埋め込んでプログラムの実行途中で変数やメソッドの出力結果をターミナルに出力する。この方法はC#でもDebug.Log();でいつも使ってました。
  • tapメソッドを使用するとメソッドチェーンされているコードの途中経過を確認することが出来る。
'#043c78'.scan(/\w\w/).tap { |rgb| p rgb }.map(&:hex)
# => ['04', '3c', '78']
  • RailsではLogger.debugメソッドが用意されており、ログにデバッグ情報が出力される。
  • 実際の開発現場ではByebugというgemが使用されている。
  • 汎用的なトラブルシューティング
    • irb上で再現してみる。
    • Rails等のフレームワークならログを調べる。Railsチュートリアルをやっていた時はProduction環境でSendGridが動かずログにはお世話になりますた。(結局SendGridアカウントが凍結されていることが原因だった)
    • RubyAPIドキュメントを読む。外部ライブラリのREADMEを読む。
    • issueを検索する。rubyrailsのissueトラッキングサイトがあるので活用するとよい。

bugs.ruby-lang.org
github.com

    • ライブラリのコードを読む。
    • テストコードを書く。
    • ネットを検索することは手軽だが危険でもある。公式の情報でないので絶対にその情報が正しいとは限らない。古い情報の可能性もあるためよく調べること。
    • パソコンの前から離れる。← わかる
    • その日はもう寝る ← それな

プロを目指す人のためのRuby入門 12章

  • Rubyの標準ライブラリには日付や時刻を扱うクラスが3つある。
    • Timeクラス
    • Dateクラス
    • DateTimeクラス
  • Timeクラスは組み込みライブラリ。それ以外は標準ライブラリのためrequireが必要。
  • TimeクラスとDateTimeクラスは処理が似ている。
    • Timeクラスは処理速度が速い。UNIX時間に依存しているため2038年問題を抱えている。(Ruby1.9.2以降で2038年問題は解決した)
    • DateTimeクラスはTimeクラスより処理速度が遅い。UNIX時間に依存していないため柔軟に日時を表現出来る。(Ruby1.9.3以降はCの実装になり高速化された)
    • Timeクラスはサマータイムうるう秒を扱える。
  • Rubyの標準ライブラリにはファイルやディレクトリを扱うクラスがいくつかある。
    • Fileクラス
    • Dirクラス
    • FileUtilsクラス
    • Pathnameクラス
  • FileクラスDirクラスは組み込みライブラリなのでrequireなしで使用出来る。
File.open('./lib/fizz_buzz.rb', 'r') do |f|
  puts f.readlines.count
end
File.write('./lib/hello_world.rb', 'r') do |f|
  f.puts 'Hello world!'
end
  • FileUtilsモジュールはファイル操作を集めたモジュール。ファイルのコピーや削除などが出来る。
requrie 'fileutils'

FileUtils.mv('./lib/hello_world.txt', './lib/hello_world.rb')
requrie 'pathname'

lib = Pathname.new('./lib')
lib.file? # => false
lib.directory? # => true
lib.join('sample.txt') # => "./lib/sample.txt"
  • CSVJSON, YAMLといったファイル形式のデータの読み書きをするライブラリもある。
  • Ruby環境変数に保存された値やrubyコマンドの起動時引数を取得することが可能。
  • evalメソッドは受け取った文字列をrubyのコードとして実行する。
code = '[1, 2, 3].map { |n| n * 10 }'
eval(code) # => [10, 20, 30]
  • バッククオートリテラルはバッククオートで囲まれた文字列をOSコマンドとして実行する。
puts `cat lib/fizz_buzz.rb`
# => def fizz_buzz(n)
#      if n % 15 == 0
#        'Fizz Buzz'
#    #省略
  • RakeはRakefileという名前のファイルにタスクを定義する。
task :hello_world do
  puts 'Hello world'
end
$ rake hello_word
Hello, world!
  • Rakeを使用するとテストの一括実行などが出来る。(P.423参照)

リーダブルコード 10章

  • メソッド内の下位問題は別メソッドにして書く。本書では「与えられた緯度経度の最も近いarrayの要素を返すメソッド」内で「球面三角法の第二余弦定理」に関するコードを別メソッドに分けている。
  • 下位問題を別メソッドに分けることはメリットがある。例えば上記の例なら「球面三角法の第二余弦定理」を他のメソッドでも使用することが出来るようになる。また、もしこのメソッドに改善点があった時もメンテナンスがしやすい。
  • 本書ではライブラリはどんどん作るべきとされている。Rubyの場合gemが多くあるのでまずはgemを探してなければ自分で汎用コードを書いていく感じになるのかな?
  • 下位問題は別メソッドに分けるべきだがやりすぎると逆に読みづらいコードになる。(P.140)

Udemy講座 もう怖くないGit!チーム開発で必要なGitを完全マスター CHAPTER 1-2

RailsチュートリアルGitHubを使用しましたが以下のコマンドなど暗記して使っていました。

git add -A
git commit -m 'comment'
git push

このコマンドがどういう意味なのか(感覚でなんとなくは分かりますが...)基礎からしっかり理解したいと思いUdemyでも評判の良いこちらのコースを受講しようと思いました。
www.udemy.com

  • Gitはファイルのバージョンを管理するためにある。
    • ファイルのバージョン管理をしないと最新のファイルがどれなのかわからなくなる。一人ならまだ分かる可能性があるが複数人で開発するとどれが最新なのかわからなくなる。
    • ファイルのバージョン管理をするといつ誰がどこを編集したかが分かるようになる。
  • GitはLinux開発用のバージョン管理システムとして開発が開始された。
  • GitHubとはGitリポジトリホスティングサービス。
  • GitHubの特徴
    • プルリクエストで複数人開発が出来る。他の人が修正したコードをプルできる。
    • GitHub上で誰でも開発が出来る。
  • GitリポジトリホスティングサービスとしてGitHubの他にBitbucketがある。
  • Mac OSはGitがデフォルトでインストールされている。最新バージョンのアップデートは公式サイトからアプリをDLする。
  • Gitの初期設定は以下の通りにTerminalで入力する。
$ git config --global user.name "Gitで登録したuserID"
$ git config --global user.email Gitで登録したemail
$ git config --global core.editor "エディタ名"
  • 設定内容は以下のコマンドで確認出来る。catはファイルの中身を見るコマンド。
$ cat ~/.gitconfig

Udemy講座 もう怖くないGit!チーム開発で必要なGitを完全マスター CHAPTER 3

  • Gitのバージョン管理は差分ではスナップショットで保存している。変更されたファイルのみをまるごとを保存している。
    • スナップショットで保存すると複数人で並行してブランチを作成する際Git側の処理が早く済む。差分だとすべてのバージョンを遡っていかないといけない。
  • Gitはコミットでバージョンを管理している。コミットを辿ることで以前のバージョンの状態に戻すことが出来る。
  • Gitの使用の流れは以下の通りである。
    • ローカルで編集を行いローカルでスナップショットを作成する。スナップショットはローカルリポジトリへ保存する。
    • ローカルリポジトリ(履歴のデータの保管場所)をGitHub(リモートリポジトリ)へアップロードする。
  • 他人の変更を取得する流れは以下の通りである
  • ローカルは3つのエリアに分かれている。
    • ワークツリー: ファイルを変更する作業場
    • ステージ: スナップショットをするための準備をする場所 (git add)
    • リポジトリ: スナップショットを記録(git commit)
  • 例えばindex.htmlをgit addすると
    • index.htmlを圧縮したファイルAをリポジトリに追加。
    • ステージのインデックスにindex.htmlとファイルAをマッピングした内容を記録する。
  • git commitをすると
    • インデックスを元にリポジトリにツリーが作成される。
    • ツリーが作成されると対応したコミットが作成される。コミットにはツリー名, 作成者名, 作成者メールアドレス, 日付, コミットメッセージが記録される。
    • 変更元のコミットがある場合はコミットに親コミット情報が記載される。(変更履歴を辿れるようにするため)
  • 例えばindex.htmlを変更してgit addした場合は圧縮ファイルは前バージョンとは別ファイルになる。ステージのインデックスはindex.htmlとファイルAという内容が変更されindex.htmlとファイルCとなる。
  • 上記の「圧縮ファイル」「ツリーファイル」「コミットファイル」のことをGitではGitオブジェクトと呼んでいる。Gitオブジェクトは「.git/objects」ディレクトリ下に保存されている。
  • 圧縮ファイルはblobオブジェクトという。ファイル名はファイルの中身に一意なハッシュIDになる。40文字の英数字で先頭2文字がディレクトリ, 38文字がファイル名になる。
  • リーファイルはtreeオブジェクトという。treeオブジェクトにはファイル名とblobオブジェクトのファイル名, そしてディレクトリに応じたツリー名が記載される。treeオブジェクトはディレクトリに1つ作成され、サブディレクトリにツリーがある場合は親にサブのツリー情報が記録される。
  • コミットファイルはcommitオブジェクトという。commitオブジェクトには親commitオブジェクトの情報, 編集者の名前, メールアドレス, ツリーの情報が記載されている。
  • Gitオブジェクトの中身は以下のコマンドで確認出来る。
$ git cat-file -p ハッシュID
  • Gitを使用するには
$ git init
Initialized empty Git repository in <ディレクトリ>/.git/
$ ls -a
.	..	.git
  • GitHub上のクローンを作成するには以下のコマンドを入力する。以下のコマンドを打つとGitHub(リモートリポジトリ)からローカルにワークツリーとリポジトリが複製される。
$ git clone <リポジトリ名>
  • 例えばatomをクローンしてみる
$ git clone https://github.com/atom/atom.git
Cloning into 'atom'...
remote: Enumerating objects: 19, done.
remote: Counting objects: 100% (19/19), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 203854 (delta 11), reused 10 (delta 10), pack-reused 203835
Receiving objects: 100% (203854/203854), 313.15 MiB | 9.15 MiB/s, done.
Resolving deltas: 100% (144835/144835), done.
$ cd atom/
$ ls -a
<省略>
.git  //ローカルリポジトリもクローンされている
<省略>
  • 変更をステージに追加するには以下の方法がある。
$ git add <ファイル名>  //ファイルを追加
$ git add <ディレクトリ名> //ディレクトリごと追加
$ git add .  //ワークツリーのすべてを追加
  • ここで疑問になったのがgit add .とgit add -Aの違いです。以下のサイトを参考に違いを学びました。
    • git add -Aは変更があったすべてのファイルがaddされる
    • git add .はカレントディレクトリ以下の変更があったすべてのファイルがaddされる
    • git add -uというのもあるらしく-Aとの違いは新しく作られたファイルがaddされないところ。

note.nkmk.me

  • addしただけでは変更は記録されない。記録するにはコミットを行う。コミットを行うとtreeオブジェクトとcommitオブジェクトが作成される。
$ git commit //コミットする
$ git commit -m "<メッセージ>" //メッセージを付けてコミット
$ git commit -v //コミット前に変更内容の確認が出来る
  • コミットメッセージは分かりやすく書くこと。
  • 現在の変更状況を確認するには以下のコマンドを使用する。表示されるファイルは以下の2つ
    • リポジトリとステージ間で変更のあるファイル。
    • ステージとワークツリー間で変更のあるファイル。
$ git status
  • 変更差分の確認をするには以下のコマンドを使用する。
// ワークツリーとステージ間の変更内容
$ git diff
$ git diff <ファイル名>
// ステージとコミット間の変更内容
$ git diff --staged
  • 変更履歴の確認をするには以下のコマンドを使用する。
$ git log
// 一行で表示する
$ git log --oneline
// ファイルの変更差分を表示
$ git log -p <ファイル名>
// 表示するコミット数を制限
$ git log -n <コミット数>
  • ファイルの削除を記録するには以下のコマンドを使用する。
// ワークツリー・リポジトリ両方から削除
$ git rm <ファイル名>
$ git rm <ディレクトリ名>
// ワークツリーのファイルを残す場合
$ git rm --cached <ファイル名>
  • ファイルの移動を記録するには以下のコマンドを使用する。
$ git mv <旧ファイル> <新ファイル>
// 以下と同意
$ mv <旧ファイル> <新ファイル>
$ git rm <旧ファイル>
$ git add <新ファイル>
  • リモートリポジトリの新規登録をするには以下のコマンドを使用する。
$ git remote origin <リポジトリurl>
  • ローカルリポジトリをリモートリポジトリにプッシュするには以下のコマンドを使用する。
    • -uオプションを使用することでリモート名, ブランチ名を入力するのは初回のみになり、次回以降はgit pushのみでプッシュ出来るようになる。
$ git push -u <リモート名> <ブランチ名>
  • git commitなど単語が長く一々入力するのが大変なものにはエイリアスを付けておくとよい。以下例
$ git config --global alias.ci commit
// これでcommitはciで実行できる
// 設定ファイルは~/.gitconfigまたは~/.config/git/configに保存される
  • Gitでバージョン管理したくないファイル・ディレクトリは.gitignoreファイルに入力をします。
    • 管理したくないファイルとはパスワード等の機密情報が記載されたファイルがいい例です。

本日の総括

  • チェリー本を読み終えました。Rubyについて知識が深くなったと思います。一方覚えきれていないメソッドが多数ありますので時間がある時に読み返したり、コードを書く際に辞書的に使いたいと思います。
  • 今まで暗記して覚えていたGitのコマンドについて学習しました。暗記していたコマンドの意味が理解出来てスッキリしました。動画教材ですがハンズオン形式だったので覚えやすかったです。
  • 明日からLinux標準教科書を読んでいきます。Terminalともっと仲良くなりたいと思います。