いづいづブログ

アジャイルコーチになりたい札幌在住SEです。アジャイル札幌スタッフ&ScrumFestSapporo実行委員。Like:パクチー/激辛/牡蠣/猫/初期仏教

wcコマンドのソースコードを読んでみた

f:id:izumii-19:20190603235249p:plain:w400


Rubyの学習の一貫でwcコマンドと同等のものをRubyで書くことにチャレンジしているんですが、なぜだか-wオプションを指定した時の挙動(単語数のカウント)が一致しないのです。

じゃあwcコマンドのCのソースコード読めばいいじゃんってなるんですけどね、Cに苦手意識があって読むのがおっくうでした。

ただこのままじゃ完成させられない気がしてきたので意を決して内容を理解することにしました。自分用に内容をまとめておきます。

そもそもwcコマンドってなに?という人はググったらたくさん良記事が出てくるのでそちらを参考にしてください。

前提

一口にwcコマンドといっても、シェルのコマンドにはGNU版とBSDFree版の2種類があります。 macOSBSD UNIXベースなので、macで動作するwcコマンドはBSDFree版になります。

ちなみにGNU版とBSDFree版は互換性があるわけではないので同じコマンドでも少し振る舞いが違ったりすることがあります。

今回読んだBSDFree版wcコマンドのソースコードはこちら。⇒ wc.c

以降の内容は、ソースコードを一部抜粋して載せているだけで全体は載っていないので、必要であればこのコードを並べながらブログを読み進めるとわかりやすいかもです。

関数の構成

関数はシンプルに3つ。

  • main() … メイン関数。
  • cnt() … 指定したオプションに応じて件数を算出し画面に表示する。
  • usage() … 使用方法を画面に出力する。*1

ソースコードを解析する

ここからは実際にソースコードを解析(というほどでもないけど)していきます。

オプションとフラグの関係

wcコマンドで受け取るオプションによってフラグ(doXXX)を立て、以降はそのフラグで処理を分岐しているところが多いです。

なのでオプションとフラグの関係を最初にまとめておきます。

フラグ/
オプション
dochar
バイト数
domulti
文字数
doline
改行数
doword
単語数
なし 1 0 1 1
-c 1 0 0 0
-m 0 1 0 0
-l 0 0 1 0
-w 0 0 0 1

main()

main()の中身を読んでいきます。

   while ((ch = getopt(argc, argv, "clmw")) != -1)
        switch((char)ch) {
        case 'l':
            doline = 1;
            break;
        case 'w':
            doword = 1;
            break;
        case 'c':
            dochar = 1;
            domulti = 0;
            break;
        case 'm':
            domulti = 1;
            dochar = 0;
            break;
        case '?':
        default:
            usage();
        }
    argv += optind;
    argc -= optind;

まずはgetopt()を使ってコマンドライン引数のオプション(ハイフン'-'で始まる文字)を解析し、オプションごとにフラグを立てます。

getopt()では指定値(上のコードでは"clmw")以外の値が入ってきた場合「?」を返すので、「?」の場合はusage()をコールし使用方法を表示しておしまい。



   errors = 0;
    total = 0;
    if (!*argv) {
        if (cnt((char *)NULL) != 0)
            ++errors;
        else
            (void)printf("\n");
    }
    else do {
        if (cnt(*argv) != 0)
            ++errors;
        else
            (void)printf(" %s\n", *argv);
        ++total;
    } while(*++argv);

while()を抜けたあとのargv[]にはファイル名が残っているはずなので、そのファイル名を引数にしてcnt()を実行。

ちなみにファイル名が存在しない場合(if (!*argv) { ~側の処理)でもエラーと判断せずに、引数にNullを指定してcnt()を実行しています。

これは「ファイル名が存在しない場合は、標準入力からの入力データをインプットとして処理を実行する」ためです。(ちなみに、引数にファイル名が渡ってこない、かつ標準入力からのインプットもない場合はcnt()内でエラー処理をしています)。

cnt()は、引数に指定されているファイル数分実行され、そのたびにtotalがインクリメントされていきます。



   if (total > 1) {
        if (doline)
            (void)printf(" %7ju", tlinect);
        if (doword)
            (void)printf(" %7ju", twordct);
        if (dochar || domulti)
            (void)printf(" %7ju", tcharct);
        (void)printf(" total\n");
    }

total > 1であれば最後にtotal行を表示します。

つまりmain()の処理はなんてことはなくて、(1)オプションを解析し、(2)ファイルの数分だけcnt()をコールしているだけ。

実際に件数を算出する処理はcnt()でやっているようなのでこのあとはcnt()の中身を理解していきます。

cnt()

cnt()はファイル名を引数として該当するオプションに応じた件数を算出し、正常終了(=0)かエラー終了(=1)かを返却する関数です。

   if (file == NULL) {
        file = "stdin";
        fd = STDIN_FILENO;
    } else {
        if ((fd = open(file, O_RDONLY, 0)) < 0) {
            warn("%s: open", file);
            return (1);
        }
    }

引数にファイル名が渡ってこない場合は標準入力をインプットとします。

引数にファイル名が渡ってきた場合は、そのファイルを読み取り専用で開きます。

ファイルオープンでエラーだったら、エラー(=1)を返却して処理を終了します。



   if (doword || (domulti && MB_CUR_MAX != 1))
        goto word;
  • doword が立っている => オプションなしか、-wオプション指定
  • domulti && MB_CUR_MAX != 1 => -mオプション指定、かつ MB_CUR_MAX != 1

MB_CUR_MAXは 現在のロケールでのマルチバイト文字の最大長のことです。

つまり、オプションなしか、-wオプション指定か、「-mオプション指定、かつ マルチバイト文字の最大長が1以外」だったらword:ラベルまで処理をスキップします。

2バイト以上のマルチバイト文字が含まれる場合は、文字数をカウントするときに単純に1バイト=1文字としてカウントできないので、それ専用の処理をword:ラベル以降でやっているということですね。

マルチバイト、ちょっとめんどくさい。

とりあえずword:ラベル以降の処理は後半で記載するとして、次。



   if (doline) {
        while ((len = read(fd, buf, buf_size))) {
            if (len == -1) {
                warn("%s: read", file);
                (void)close(fd);
                return (1);
            }
            charct += len;
            for (p = buf; len--; ++p)
                if (*p == '\n')
                    ++linect;
        }
        tlinect += linect;
        (void)printf(" %7ju", linect);
        if (dochar) {
            tcharct += charct;
            (void)printf(" %7ju", charct);
        }
        (void)close(fd);
        return (0);
    }

ここはdolineフラグが立っている(オプションなしか-lオプション指定)の場合の処理です。

ファイルの中身をbuf_sizeずつ読み込み(while ((len = read(fd, buf, buf_size))) { ~ 側の処理)、\nをみつけたらlinectをインクリメントします。このとき同時にtotal用の変数tlinectもインクリメントしていきます。

つまり、-lオプションの「行数を表示する」というのは、正確にいうと行数を数えているわけではなく改行の数を数えているということです*2

同時にdocharフラグも立っている場合、バイト数も同時にカウントします。ここでもtotal用の変数tcharctを同時にインクリメントしています。

読み込みに失敗した場合はワーニングを表示してエラー(=1)で終了。



   if (dochar || domulti) {
        if (fstat(fd, &sb)) {
            warn("%s: fstat", file);
            (void)close(fd);
            return (1);
        }
        if (S_ISREG(sb.st_mode)) {
            (void)printf(" %7lld", (long long)sb.st_size);
            tcharct += sb.st_size;
            (void)close(fd);
            return (0);
        }
    }

ここはdocharかdomultiフラグが立っている場合(オプションなしか、-cオプション指定か、-mオプション指定」)の場合の処理で、バイト数をカウントします。

domultiフラグに関してはあらかじめここで処理を分岐しているため、1文字=1バイトの場が対象になります。

1文字=1バイトなのでバイト数と文字数は同じカウントの方法で良いというわけですね。

cnt() - word:ラベル以降の処理

       while (len > 0) {
            if (!domulti || MB_CUR_MAX == 1) {  …(1)
                clen = 1;
                wch = (unsigned char)*p;
            } else if ((clen = mbrtowc(&wch, p, len, &mbs)) ==  
                (size_t)-1) {              …(2)
                if (!warned) {
                    errno = EILSEQ;
                    warn("%s", file);
                    warned = 1;
                }
                memset(&mbs, 0, sizeof(mbs));
                clen = 1;
                wch = (unsigned char)*p;
            } else if (clen == (size_t)-2)     …(3)
                break;
            else if (clen == 0)
                clen = 1;
            charct++;
            len -= clen;
            p += clen;
            if (wch == L'\n')       …(4)
                ++linect;
            if (iswspace(wch))     …(5)
                gotsp = 1;
            else if (gotsp) {       …(6)
                gotsp = 0;
                ++wordct;
            }
        }

word:ラベル以降の処理でキモになるのはこの2つ目のwhile文の中。1つずつ順番に確認していきます。

(1)~(3)までの処理では条件に応じて、mbrtowc()でマルチバイト文字が何バイトかを検査しclenに値を格納しています。同時にマルチバイト文字(のポインタ)をwch格納しています。

順番にifによるclenの値を見ていきます。

  • !domulti || MB_CUR_MAX == 1`

  dowordフラグが立っているか、
  「domultiフラグが立っているかつ MB_CUR_MAX == 1」だったら、clen =1

  • (clen = mbrtowc(&wch, p, len, &mbs)) == (size_t)-1

  mbrtowc()の結果、不正なマルチバイト列に遭遇した場合、clen =1

  • clen == (size_t)-2

  mbrtowc()の結果、完全なマルチバイト文字を解析できなかった場合、clen = (size_t)-2*3

  • clen == 0

  L'\0' ワイド文字を認識した場合、clen =1

  • 上記以外

  clen = mbrtowc()が返却したバイト数

clenにバイト数を取得したらcharctをインクリメントして文字数をカウントしていきます。 そしてclenのバイト数分だけポインタを進めてまた次の文字を解析していき、lenが0になるまでループを継続します。

(4)は他のところでもでてきたように\nをみつけたら行数カウントlinectをインクリメントする処理。

(5)と(6)は単語数のカウントをしています。

(5)はiswspace()を使ってwchがホワイトスペース文字かどうかをチェックしています。Trueが返ってきたらスペース取得フラグ(gotsp) を立てていて、

(6) では、iswspace()を使ってwchがホワイトスペース文字かどうかを検査し、ホワイトスペース文字ではない場合かつスペース取得フラグがOn(gotsp=1)の場合、単語数カウントwordctをインクリメントします。その後スペース取得フラグをリセット(gotsp=0)。

ちなみにiswspace() がTrueを返すのは

  • 空白 (0x20, ' ')
  • 改頁 (0x0c, '\f')
  • 改行 (0x0a, '\n')
  • 復帰 (0x0d, '\r')
  • 水平タブ (0x09, '\t')
  • 垂直タブ (0x0b, '\v')

だとここ書いてあったのですが、自分のmacでwcコマンドを実行するかぎりでは

  • ノーブレークスペース (0xa0)*4

もTrueと判断されているようなので、ロケールによって多少違いがある模様。

この後linect、wordct、charctをそれぞれ標準出力に出力する処理がありますが、ここは割愛します。

以上でwc.cの中身はひととおり解析できました。

所感

自分の書いたRubyのコードと実際のwcコマンドで単語数のカウントが一致しなかったのは、このノーブレークスペース (0xa0)をホワイトスペース文字としてカウントしているかどうかの違いだったっぽいです。

ノーブレークスペースを考慮しなくてよければ、単語数のカウントは単純にsplit()するだけでうまくいったんだけどなぁ。

とりあえず、なぜwcコマンドと自作wcコマンドの単語数カウントが一致しないかはcのソースコードを読むことで仕組みがわかり、最終的に同じように実装することができました。

何度も何度もこのコードを読みましたが(多分20回以上読んでいる)、読めば読むほど理解できるようになったので大変だったけど解析できてよかったです。

*1:usage()の中身については見ればわかるような内容なのでブログでの解説はしません。

*2:けっこういろんなところの説明に「行数」を表示すると書いてある

*3:完全なマルチバイト文字を解析できなかった場合というのは、マルチバイト文字の途中という意味かな?

*4:"だ"という文字を16進数に変換すると"0xe3 0x81 0xa0"となり、この"0xa0"がノーブレークスペース。

Bundlerとは

f:id:izumii-19:20190509125243p:plain:w500

gemをインストールするときにはBundlerを使うらしいのでBundlerについてまとめました。

毎日勉強していると知らないことが多すぎて悲しくなります。

前書き

"gem"というと

  1. Ruby言語用のライブラリ
  2. RubyGemsというパッケージ管理システムをインストールすると使えるようになるコマンド

の大きく2種類の意味で使われることが多いので、このエントリーでは以下のように使い分ける。

  1. Ruby言語用のライブラリ ⇒ gem
  2. RubyGemsというパッケージ管理システムをインストールすると使えるようになるコマンド*1 ⇒ gemコマンド

gemコマンド

gem installコマンドを使うとgemをインストールすることができる。(アンインストールはgem uninstall { gem名 }

$ gem install "sinatra"

これ以外にもgemコマンドを使用するとgemに対する様々な操作ができるがここでは割愛する。

gemの依存関係

Bundlerの話の前にgemの依存関係について。

gemの中には依存関係があるものがある。例えば、

  • Aというgemを使うにはBというgemのVer1.0を使う必要がある
  • BというgemのVer1.0はCというgemのVer3.0を使う必要がある
  • BというgemにはVer1.0とVer2.0があるが、BをVer2.0にしてしまうとAは動かない

とこんな感じに。

チームで開発している場合、誰か一人だけバージョンをあげたりすると開発しているプロダクトがうまくうごかなくなる可能性もあるのでチーム内で合わせておく必要がある。

ところが、gem installを使用してgemをインストールする場合、こういった依存関係を把握した上で必要なgemを入れていく必要がある。

もう、間違いが起きる予感しかない。

Bundler

ここでBundlerの登場。

gem間の依存関係を解決しながら必要なgemを一括でインストールできる仕組みのこと。

簡単に説明すると「"Gemfile"というファイルを作成してそこに指定されているgemをインストールする」といったことができるのだが、後述するGemfile.lockのおかげでけっこう柔軟にバージョン管理ができて便利。

ちなみにBundlerもgemの1つ。

インストール

BundlerはBundlerでインストールできないので、ここはgemコマンドを使ってインストールする。

$ gem install bundler

インストールしたら下記のコマンドで最新バージョンが入っていることを確認すること。

$ bundler -v

Gemfileを作成する

Gemfileはインストールしたいgemを書いておくファイル。

以下のコマンドを実行するとGemfileの雛形が作成されるので出力された雛形にgemの情報を記述していく(編集はvimなどのエディタでやる)。

$ bundle init

書く内容はざっくりとこんな感じ。

source "https://rubygems.org" # gemインストール元

ruby "1.9.3" # Rubyのバージョン

gem "nokogiri"    # インストールするgemを指定
gem "RedCloth", ">= 4.1.0", "< 4.2.0"    # バージョンを指定することもできる
gem "rspec", :group => :test    # こんな指定もできるっぽい
…

記述方法はBundler本家のサイトに書いてるので割愛。

bundle install

作成されたGemfileをもとにgemのインストールを実行する。

$ bundle install --path {ディレクトリ名}

--pathでインストール先を指定することが可能。

一度path指定でインストールすると、次回以降はpath指定無しでも同じpathが選択される。この時Gemfileがある場所と同じところに"Gemfile.lock"というファイルが作成される(初回のみ)。

Gemfile.lockについて

Gemfile.lockにはインストールしたgemの名前とバージョンが記載されている。

実はbundle instarllを実行するときには、このGemfile.lockファイルがある場合とない場合で挙動が少し違う。

Gemfile.lockがない場合(初回実行時)

インストールしたgemの名前とバージョンをGemfile.lockに出力する。

Gemfile.lockがある場合(2回目以降) Gemfile.lockにかかれているバージョンのgemがインストールされる。Gemfile.lockは更新されない。

この仕組をうまく利用し、Gemfile.lockをチーム内に配布してbundle installするようにすれば、他の端末でも同様の環境が用意できる。便利!

Gemfileではバージョン指定はゆるめにしておき、gemのバージョン管理はGemfile.lockに任せるというやりかたがよさそう(その場合はGemfile.lock をGitに登録しておくといいかも)。

bundle update

bundle updateは、rubygems.orgから最新版を取得しGemfile.lockを更新する。gemのバージョンを更新したい時に使用する。

まとめ

gemのインストールはgemコマンドではなくBundlerでやろう!

参考

Bundler: The best way to manage a Ruby application's gems

bundle install と bundle updateの違いについて - Qiita

橋本商会 » Ruby書くならBundler使え

*1:Ruby のバージョン 1.9 以降 RubyGems は標準添付となっているので最近だとわざわざインストールとかしない。

「プロを目指す人のためのRuby入門」を読みながらコードを書く日々

f:id:izumii-19:20190507230504p:plain:w400


通称チェリー本です。

私はRails未経験の人間なのですが、Railsをやる前に生のRubyを学んでからのほうが良いとアドバイスをもらったので、まずはRubyを習得しようと思いました。

その時に、プログラミング経験がある人でRubyを始めるならこれが良いよと勧められたのがこのチェリー本です。

GW中にAmazonから届いて、まだ5章くらいまでしか進んでないのですがすごくわかりやすいのでおすすめポイントを書いておきます。

コード例が豊富

ほぼ全てのコンテンツに、説明だけではなくコードの書き方の例が載っています。勉強し始めの頃は文章だけを読んでもピンとこないことが多いものですが、「こう書くんだよ」という例を見るとイメージが湧いたり、同じコードを実際に書いてみると「あーそういうことか!」と腑に落ちることがけっこうありますから、とにかくわからなくても手を動かしてみると良いです。

Rubyではこうも書くこともできるけど見にくいのでやめた方がいい」という書き方についても例が書かれているので「なるほど、確かに見にくいな」と納得できます。

プログラミングがしながら覚えられる

この本では各章に例題が用意されています。

ただ例題が用意されているだけではなく、これを完成させるためにはどんなメソッドを使えばよいか、どういう風にコードを組み立てたらいいかなどのフォローも丁寧に書かれています。

説明通りにプログラミングしていけば一通り完成させることはできますが、プログラミング経験があるのであれば章の内容を一通り読んだうえでまず自分なりにプログラミングしてみるのがよいと思います。

Ruby素人の私が書くとだいたい長くて煩雑なコードになってしまうので、最後に本を見ながら他の書き方を学んだりリファクタリングしたりしています。

現場で使う書き方がわかる

Rubyは同じ目的を達成するための書き方が複数存在します。なのでこの本にも「こうも書けるしああも書けるよ」というパターンがたくさん載ってますが、その上で「現場ではこういう書き方は読みにくいので歓迎されない」というような、現場レベルのアドバイスも書かれているのが親切だと思いました。

「プロを目指す」のであれば書き方を知っているだけではなく、どういうプログラミングをするとそのコードが現場で生きてくるのかを知っている必要があります。

プログラミング以外についても書かれている

Rubyのプログラミングだけではなく、テストの自動化やオブジェクト指向デバッグの観点についてもしっかりと書かれています。

テストについては3章というわりと早い段階で登場しますが、これは「コードを書く前にテストを書く。最初は全て失敗するところから始め、それがグリーン(=テストが成功)になるようにプログラミングしていく」というテストファーストの考え方に基づいているためです。

またRubyオブジェクト指向言語なのでそういったことについてもしっかり説明されています。

読む人のことを考えて書かれた本

全体的に、読む人が何を知りたいと思ってこの本を手にとっているのかがよく考えられた本だなぁと思いました。

いくつかの書き方を示した上で、現場で使われる書き方とそうじゃない書き方、そして理由が書かれています。

なので、この本を読んだ私達は「こういう書き方はしない」と覚えるのではなく「なぜこういう書き方は好ましくないのかの理由を理解することができます。

理由がわかるということは、同じ理由が当てはまる違うシュチュエーションに出会ったときに、自分で判断できるようになるということです。

また、Ruby初心者だらこそ疑問を感じそうな点についても「これはRubyを始めて書く人には馴染みのない書き方かもしれませんが」と補足されているので、知らないということを不安に感じずに進めることができる点も良かったです。

まとめ

「プログラミング経験は少しあるけどRubyは初めて」というレベルの人であればかなりわかりやすいと思います。

技術書にありがちな「読みにくさ」がとにかくない本なので、自身をもって人に勧めたいと思いました。

Gitのリモートリポジトリから特定のファイルを削除する

f:id:izumii-19:20190505171017p:plain

しょっちゅう同じ間違いを繰り返すので備忘録。

GitHubにPushするときに.gitignoreを用意していない状態で、以下のように全ての変更をindexにあげてcommit&pushしてしまう。

$ git add . 
$ git commit -m "hogehoge"
$ git push orogin master 

変更のあるファイルだけを指定してindexにあげればよい(add xxx.rb)のだけどめんどくさくてついついadd .してしまう。

すると.DS_Storeなどの不必要なファイルまでがGitHubのリモートリポジトリに登録されてしまう。

いつも後になってからこのファイルをリモートリポジトリから消したくなるので、そのときの手順。

手順

  1. git rmする。
  2. 変更をcommitする。
  3. リモートにpushする。

削除

ローカルのファイルも一緒に削除したい場合

$ git rm {削除したいファイル}

ローカルのディレクトリごと一緒に削除したい場合

$ git rm -r {削除したいディレクトリ}

ローカルのファイルは残したい場合

$ git rm --cached {削除したいファイル}

変更をcommit

$ git commit -m "hogehoge"

push

$ git push origin master

まとめ

.gitignoreを面倒がらずに用意しよう!

RuboCopを使う

f:id:izumii-19:20190503080015p:plain

RuboCopなるものを始めて使ってみました。

このエントリーではRubocopをインストールしてから使うまでの手順と、RubocopをVSCodeで使うための手順について説明します。

RuboCop

Rubyで書かれたコードが、コーディング規約にそって書かれているかをチェックする解析ツール。

こういったツールを使うことでコードの可読性を高めたり、チーム内のコーディングルールを簡単に適用することができる。

私は使っていないけど、auto-correct という機能を使えば、チェックだけではなくRuboCopが認識してくれる範囲で自動で修正してもらうこともできてさらに便利。

github.com

インストール

以下のコマンドを実行するだけ。

$ gem install rubocop

実行

さっそくRuboCopを実行してみる。 チェックしたい.rbファイルがあるプロジェクトに移動して、以下のコマンドを実行。

$ rubocop

f:id:izumii-19:20190503081556p:plain
RuboCopの実行結果。たくさんエラーが検出されている。

設定ファイル(.rubocop.yml)について

コーディング規約は「.rubocop.yml」というファイルに書かれており、インストール時に一緒に配置される*1

Rubocopにデフォルトで設定されているコーディング規約はけっこう厳しいらしく、そのまま使う人は少ないということ。

コーディング規約を自分で変更したい場合は以下のいずれかの方法で変更できる。

  1. 既にある.rubocop.ymlを開いて設定を変更する。
  2. .rubocop.ymlを新しく作成しプロジェクトルートに配置する。*2

設定ファイルを変更する

設定ファイルの変更を実際に行う。

いくつかのブログを見てみたところ、「.rubocop.ymlを新しく作ってプロジェクトルートに配置する」というやり方が多いようなのでこの方法で変更してみる。*3

  1. 適用したい.rubocop.ymlをwgetコマンドなどを使用して持ってくる。今回はRailsGitHubから持ってきた。
$ wget https://raw.githubusercontent.com/rails/rails/master/.rubocop.yml

wgetの引数にはGitHubの画面の「Raw」ボタンを押して表示されるURLを使う。

f:id:izumii-19:20190503085055p:plain
「Raw」ボタンをクリックするとURLが取得できる

  1. 取得した.rubocop.ymlをプロジェクトルートに配置。今回の例では~/RubyTutorial/に配置。

f:id:izumii-19:20190503085637p:plain
プロジェクトルートに配置

これでOK。のはずなんだけどプロジェクトルートに配置したら、こっちの.rubocop.ymlのチェックが適用されているということはどうやって確認するのだろう。

今回は実行してみてチェックの方法が変わっているかどうかで確認したが、もっと簡単にわかるのだろうか?

RuboCopをVSCodeで使う

私はRubyのコードをVSCodeで書いているので、VSCodeでも使えるようにする。

インストール

拡張機能からruby-rubocop 0.8.0をインストールして有効にする。

f:id:izumii-19:20190503090632p:plain
拡張機能からインストール

設定

[基本設定] - [設定] - [拡張機能]を開いて、以下の画面のように設定。

f:id:izumii-19:20190503091858p:plain
VSCodeでRuboCopを使うための設定

④の「Execute Path」は以下のコマンドで確認できるので、出力結果のパスをそのまま(…shims/まで)貼ればよい。

$ which rubocop
/Users/izumi/.rbenv/shims/rubocop

実行

インストールと設定が完了すると、保存時にチェックが実行されるようになる。

f:id:izumii-19:20190503092706p:plain
保存時にRuboCopのチェックが走る

おまけ

RuboCopを導入したら、Missing frozen string literal comment.の警告が必ず出てしまうようになってしまった…。

結果として# frozen_string_literal: trueというマジックコメントを先頭に入れることで解決できた。

これは Ruby 3.0 では文字列リテラルがデフォルトで freezeになる予定であり、これに先駆けて今から文字列リテラルをfreezeにしておく(=このマジックコメントを入れる)ようにしようということで、RuboCopの設定でチェックが有効になっていることが多いのだとか。

参考

  • インストール

  Rubocop使ってみる - 電波ビーチ

  • .rubocop.ymlの内容

  RuboCopの設定アレコレ - Qiita

  [Mac]VScode で ruby-rubocop が動かなくなったら executePath を設定してみよう - Qiita

  • Missing frozen string literal comment.

  【Ruby】RuboCopのFrozenStringLiteralCommentについて - TASK NOTES

*1:ただし、このファイルがどこに保存されているのかは探してみたけどわからなかった。

*2:実際にはゼロから作ることはなさそうで、GitHubから持ってきたり既存の.ymlファイルに変更を加えたものをプロジェクトルートに置くことが多い。

*3:あと、既存の.rubocop.ymlがどこにあるのかを私が見つけられなかったというのも理由のひとつ。

Vue.jsでHello,World

f:id:izumii-19:20190414231959p:plain:w400


GWの目標だった「JavaScriptもVue.jsもなにも知らないweb初心者が、Vue.jsでHello,Worldを表示させる」が完成したので記録を載せておきたいのと、メモを残しておきます。

ディレクトリ構成

ホームディレクトリの下に「VueTutorial」という練習用フォルダを作り、そこにwebpackを使用して「helloworld」というプロジェクトを作成。

vue init webpack helloworld

すると以下のディレクトリとファイルが作成される。

ディレクトリとファイル 内容
build/ webpackの設定やビルド用の設定用
config/ 各設定ファイルを格納。ポートの指定とかもここ
node_modules/ npmで管理されるモジュールが格納されている
src/ 開発用のディレクト
package-lock.json npmでパッケージを管理するために必要な設定ファイル
package.json npmでパッケージを管理する時に必要ファイル
static/ コンポーネントに依存しない静的ファイルを格納する
test/ テスト用設定ディレクト

ちなみにnpm run buildでビルドすると以下のディレクトリも作成される。

ディレクト 内容
dist/ ビルドが完了するとこのディレクトリにバンドルファイルが配置される。ここのファイル一式をサーバにデプロイする。

webpackを使って自動的に作成されたディレクトリに、自分で変更を加え、最終的に以下のような構成になった。*1

~/VueTutorial/helloworld/

 ├ build/

 ├ config/

 ├ dist/

 ├ node_modules/

 ├ src/

 │ └ assets/

 │  │ ├ avatar.png ← 追加

 │  │ └ logo.png

 │ ├ components/

 │  │ ├ pages/← 追加

 │  │ │ └ topPage.vue← 追加

 │  │ └ HelloWorld.vue ← 最初に表示されるページ

 │ ├ router/

 │  │ └ index.js ← vue-routerの本体。

 │ ├ App.vue ← 全ページ共通で使う部品などはここに書く。

 │ └ main.js ←メイン。「App.vue」と「router」をインポートしてる

 ├ index.html ← 実質何もしない。

 ├ package-lock.json

 ├ package.json

 ├ static/

 └ test/

実行

以下のコマンドで実行。

$ npm run dev 

もし、本番用にビルドする場合はこっち。

$ npm run build 

http://localhost:8080/ にアクセスするとこれまで通りvue.jsデフォルトのトップページが表示される。

トップページには"Hello Worldページへ"というRouterLinkをあらかじめ用意しておいたのでそこをクリックすると、今回あらたに追加した"topPage.vue"の内容が表示される。

このプロジェクトにはindex.htmlは1つしかないのに、このように複数のページをroterで切り替えることができる。

これがSPA(Single Page Application)というやつらしい。

【実行結果】

【コード】

GitHub - izumii19/VueTutorial

参考サイト

coinbaby8.com

*1:フォルダ構成とかファイル名とかアレな感じなのはそのうち直すので、今は大目に見ることにする。

Rubyのインストール

f:id:izumii-19:20190501101628p:plain:w400

今日からサーバーサイド言語であるRubyを勉強します。 まずはインストールしてRubyを使える環境を作成しますが、色々長いので目次を作りました。
(いつも作ればいいのかもしれない)

環境

macOS Mojave 10.14.4

インストール

Xcode

もともと入っていたのですがバージョンが古かったので最新にしました。ただXcodeを最新にするにあたってmacOSのアップデートも必要でした。

  • macOSのアップデート(macOS Mojave 10.14.4)
  • Xcodeのアップデート(10.2.1)

Xcode インストールはApp Storeなどから行います。

Command Line Tools for Xcode

Xcodeをインストールしたら、Xcodeのメニューから Xcode → Open Developer Tool → More Developer Toolsの順番で操作。 Downloads for Apple Developerが開くので、そこのリストから”Command Line Tools(OS X 10.14) for Xcode 10.2.1”をダウンロードしてインストール

Homebrew

RubyRailsに関連するソフトウェアはMacのパッケージ管理システムのHomebrewを使用する。これも入っていたが最新にしておく。この時にupgradeも行って既存のパッケージも更新しておく。*1

$ brew update
$ brew upgrade

updateとupgradeが終わったらdoctorで状態を確認。Your system is ready to brew.が表示されればOK。

$ brew doctor
Your system is ready to brew.

Git

最初から入っているはずだけど一応最新に更新。インストールする場合は以下のコマンドで実行できる。

$ brew install git

irb

irbは"Interactive Ruby"。これを入れると対話形式でRubyのコードの実行結果が確認できる。

$ brew install readline

openssl

httpsを使用するためのパッケージ。すでに入っているので省略するがインストールする場合は以下のコマンドで実行できる。

$ brew install openssl

ruby-build

rbenvでrubyをビルド・管理するためのパッケージ。

$ brew install ruby-build

rbenv

Rubyを使用する上で必須となっているバージョン切り替えツール。インストールもrbenvで行うのでインストールする。*2

$ brew install rbenv

インストール後はpathの設定を行うが基本的に以下の通りやればOK。

$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile

Ruby

ここでやっとrbenvを使ってRubyをインストールします。

まずは最新バージョンを確認。以下のコマンドでバージョン一覧を表示して確認するか公式サイトでも確認可能(2019/4/30時点での最新は2.6.3)。

$ rbenv install -l

次に最新バージョンをインストールする。

$ rbenv install 2.6.3

rbenvで切り替えとか確認とかしてみる

インストールが終わったら、インストール済みのバージョンをversionsコマンドで確認する。*がついているバージョンが現在選択中のバージョン。

$ rbenv versions
  system
  2.6.0
* 2.6.3 (set by /Users/izumi/.rbenv/version)

標準で使うrubyのバージョンを切り替えるときはglobalコマンドで切り替えてversionsで確認する。

$ rbenv global 2.6.3

これでRubyを切替可能な状態で使える環境が作成できた。

難しくはないけど準備が多いので、順番にやっていくのがコツだろうなぁ。

参考

【macOS Sierra】Mac OSX 10.12 macOS Sierra にRuby + Rails4 開発環境の構築 【初心者必見】 - Qiita

*1:何かの環境構築をするために新しいパッケージをインストールする必要がある場合は、Homebrewのupdateとupgradeを必ず行ってからがよい。これをやらずに進むとバージョンの互換性なのかなんなのか環境構築で思いもよらずエラーがでることがあるため。

*2:「rbenv」は"あーるべんぶ"とか"あーるびーえんぶ"と発音するらしい。