dhtakeuti’s diary

開発について考えたこと、感じたことのメモ

塾に通わずに大学入学試験に合格する方法

f:id:dhtakeuti:20190114082234p:plain
勉強中

今回は、学習方法について考えてみた。 長くなったため、最初にまとめを述べる。

まとめ

  • 国語はとても大切。
  • 授業中は無為に過ごさない。
  • 予習と復習は大切。
  • 教師と友人は大いに利用する。
  • ノートってすごい。

そろそろ、大学の入学試験が本格的に始まる時期になった。私は、友人や兄弟の子供も大学受験を迎えたり、既に終わったという世代である。

そういえば、自分の大学受験はどうであったかを思い出すと、当時は周りには浪人生を除いて、塾に通っている友人は少なかった。それでも、卒業時には7~8割の生徒がいずれかの大学の入学試験に合格していたと思う。当時の普通科高校の進学クラスに在籍していたので、就職クラスの状況は分からない。

当時と今とでは、入学試験の内容や実施方法が異なるだろうが、少なくとも、試験範囲は小学校、中学校、高等学校で学習の範囲を超える事は無いはずである。難易度は違えど、応用問題として回答できるはずだ。その範囲を超える場合については、経験が無いので触れないでおく。

さて、表題の「塾に通わずに入学試験に合格する方法」だが、予習と復習と授業ですべてをまかなうということである。以下にその方法について紹介する。ちなみに、この方法は父が学生時代に実践していたものを自分なりにアレンジしてみて実践したものなので、本質的には現世代でも通用すると思う。地方の国立大学に合格した実績を持つ手法であるであることを付記しておく。

受験対象は国公立大学を想定し、受験対象の教科は全教科とする。共通一次試験の世代であるため、国語、数学、英語、そして、理科と社会は選択であるため、得意な科目を選ぶことになる。私の場合は、どちらかと言えば文系が好きだったが、受験対象の大学の選択肢を狭めたくなかったため、高校時代は理系クラスを選択した。苦手な数学、物理の授業は嫌でも受けることで、共通一次試験の合計点数を上げることを目指した。ちなみに、理科は化学と生物を、社会は世界史と政治・経済を選択した。

最も大切な教科

最初に、国語について述べる。

大学受験だけでなく、就職して実社会に出てからも含めて、日本人の最も大切な教科は現代国語であると考える。国内で大学を受験する場合、外国語学部など一部を除いて、試験問題は日本語で書かれている。問題文を理解できなければ、回答を書くこともできない。マークシート方式であれば適当に選択もできるが、それは残り時間が足りなくなったときの応急措置と考えよう。

学習全般についても、参考書は通常は日本語で記述されているし、授業の説明も日本語で行われる。質問する場合も、その回答も日本語だ。脳の中で自身の考えをまとめるにも通常は日本語を使用する。そのため、空気を吸うが如く日本語が使えることが、高等学校卒業レベルでは求められていると考えてよいし、目指すレベルだ。

国語力を身につけるには、社会人なら新聞の社説を読むのが良いだろう。高校生では社説は内容が身近に感じられない。とにかく本を読むのが良いだろう。題材は短めのエッセイ集とか参考書で良いので、起承転結がしっかりした文章が良い。現代国語の問題集の例文を読むのも良いだろう。共通一次試験の過去問も入手しやすかった。小論文の解答例もよい題材となる。とにかく、程良い長さの、構成がしっかりした文章に常に接すること、結論までの流れを把握することを続ければ、基礎力は付く。世の中に広く行われている Blog を書くことも、文章の構成や語彙の習得、問題の整理に役に立つ。復習の方法としても、役に立つのではないか。小論文の練習にもなる。フィードバックがあれば、改善することもできるし、モティベーションにもなる。

社会人になってからは、国語力がないと仕事にならない。仕事によって差はあるが、SE として働くには、仕様書を読む書く、設計書を読む書く、設計書を読む書く、プログラムのソースコードを書く(日本語はコメントのみだが)、メールを読む書く、説明資料を読む書く、などのように日本語を使っての情報の伝達に非常に多くの時間を割くことになる。昨今では、この中で英語の比重が増えて来るが、標準語が日本語である企業で働く限り、日本語でのコミュニケーションによって様々な活動が行われる。業務指示も日本語であり、伝達ミスは障害発生要因となり、場合によっては訴訟に及ぶトラブルにもなりかねない。訴訟は極端な例であるが、会議室の予約時間の伝達ミスで、会議開始の時間に使える会議室が一つもなくて会議ができない、といった事は新人の頃に経験はあるだろう。如何に正確に伝えられるかは非常に重要な技術となる。

プログラミングの世界では、1990 年代にオブジェクト指向が出てきた。オブジェクト指向での重要な考え方として、「継承」がある。この継承の概念を説明する時に、オブジェクトの例として「犬」で説明しようとするような例が多かった。「犬」オブジェクトには「鳴く」メソッドがあり、出力は「ワンワン」である。この「犬」オブジェクトのスーパークラスは「動物」であり、「鳴く」メソッドの他に「眠る」メソッドがあり、出力は「スヤスヤ」である。この「動物」クラスを「犬」クラスは継承することで、「眠る」メソッドはスーパークラスの「動物」クラスに実装された「眠る」メソッドをそのまま使用する。一方、犬特有の「鳴く」メソッドは独自に実装する。ここで、新たに「猫」クラスを考えよう。「猫」クラスは「動物」クラスを継承して、「犬」クラスと同様に「鳴く」クラスを独自に実装し、出力は「にゃあにゃあ」としよう。このように、犬と猫の共通した特性はスーパークラスで実装し、犬と猫の独自の特性は「動物」クラスを継承したサブクラスとして実装する。犬と猫のそれぞれの独自の特性である「鳴く」メソッドを実装するのが、オブジェクト指向の継承を使用した差分コーディングである、云々。この説明では本質的には間違っていない。しかし、プログラマーにはピンと来ない。抽象的な継承という概念を、たとえ話をプログラミングでは扱わない例で説明しているからだ。このケースでは、UNIX の「ファイル」と「ディレクトリー」を例にした方が、より具体的なイメージができる。「ファイル」がスーパークラスで、「ディレクトリー」は「ファイル」のサブクラスである。「ファイル」クラスには「名前」の取得メソッド、変更メソッドがある。また、作成メソッドと削除メソッドがある。「ディレクトリー」クラスは、それに加えて、自身に含まれる複数の「ファイル」または「ディレクトリー」オブジェクトを操作するメソッドがある。また、削除メソッドは自身を削除する前に、含まれている全ての「ファイル」「ディレクトリー」オブジェクトの削除メソッドを呼ぶようにメソッドの振る舞いが変わる。「名前」の取得メソッド、変更メソッドはスーパークラスのメソッドをそのまま使用する。このような例で説明した方が、実際にプログラムを設計する際の参考となりやすい。単純に左から右に伝えても良い場合もあるが、相手が理解しやすい様に表現する必要もある。そういう国語力が社会人になってからは求められる。

授業の受け方

授業は集中して受けること。

社会人であれば、年間の授業料を1時限当たり幾らになるかを意識することで、授業を無為に受けることは無いだろう。授業をないがしろに受けて塾で補完することは、学費が嵩むばかりでなく、塾への通学時間などで学習時間を減らすことになる。それほど裕福な家庭でもなかったので、塾に通うお金と時間は、参考書の購入と予習復習の時間や趣味の時間に充てる方が良いと考えていた。

授業は予習で理解できなかった箇所の確認の場とする。

授業での説明だけで理解できれば復習は不要になる。また、予習では自分なりの解釈をするが、授業では教師による説明がより洗練されているはずだ(もちろん、そうでない教師もいる訳だが...)。さらに、耳から説明を聞くのも、自習では得られない学習方法であり、記憶の残り方が違う。

授業だけで分からない箇所は教師に質問する。

質問はその場でもいいし、授業終了後でもいいし、放課後でもいい。教師は教えるプロであるため、疑問点に対して親身になって教えてくれる。担当教師がそれができなければ、別の教師に質問する手もある。授業料を払っているのだから、効率よく授業を受けて、教師の能力を活用しよう。学生の頃、塾の講師のアルバイトをしていて、教える立場で接してみると、質問の回数と内容が生徒の学力と関係があるように思えた。また、質問を受けると、相手が理解し納得するまであの手この手を使って説明するようになり、相手が理解できたことが自身の快楽中枢への刺激となっていく。分からないことを質問して理解することは、生徒側だけでなく、教師側にもプラスの方向に働くことは知っておくと良いだろう。

時にはハズレの教師もいる。授業ではなにを言っているのかよく分からない、教科書に書いてあること以上の説明がない、つまらなさそうに生徒に接する、そういった教師の授業は真面目に受ける必要は無い。損切りと考え、その授業は自習の時間と割り切る。テストに関連する説明だけに注意を向けて、後は問題集を解きまくって補完し、分からない箇所は別の教師に質問する。

予習の仕方

予習は授業を効率よく受けるための準備である。

私自身は、予習にそれほど力を入れた記憶はないが、授業を受ける前に教科書に目を通し、分からない箇所、補足が欲しい箇所にアタリを付ける程度は行っていた。これは、余裕があれば前日、余裕がなければ授業前の休み時間を利用して行っていた。

また、年度初めに教科書が配布され受け取った直後に、現代国語と英語の教科書は全て読んでいた。そのため、授業を受けるときには、その文面にデジャヴ感を覚えていた。他の教科については、ザッと目を通す程度だった。世界史は多少読み込んで、脳内の世界地図と年表に照らし合わせるようにした。

予習にかける時間があれば、授業を受ける前に、その範囲の練習問題を解くところまでできているとよい。そこまでできなくとも、事前に授業で注目する箇所、質問が必要な箇所を洗い出しておく。そうすることで授業を効率良く利用することができる。

復習の仕方

復習は授業内容の整理と理解の定着を行う儀式である。

高校生時代から、教科毎のノートにはルーズリーフを使用していた。各教科はインデックスシートで分割管理し、2~3日分のブランク用紙を持ち歩くことで、全教科でノートは薄いバインダー1冊持ち歩けば良かった。

帰宅後、本日分のノートを自宅の保管用バインダーに移動する。その際、ノートを読み返して、理解できていない箇所について、教科書や参考書を読んで補完する。授業ノートで足りない部分については、ルーズリーフの新しいシートにポイントをまとめて、追加しておく。これが主な復習となっていた。

ルーズリーフを薄く軽くすることがモティベーションとなり、授業ノートの整理を毎日行った。その際に、当日分のノートを見直すことで授業内容を思い出し、反復学習とする。また、参考書などの問題集を解くことで、理解を定着する。その繰り返しだ。理解ができない箇所については、参考書等で補足し、それでも理解できなければ、翌日以降の質問内容としてメモしておき、学校で教師に質問する。そうすることで、理解できない箇所を潰していく。

ルーズリーフを使う方法がベストとは言わないが、ノートの整理が復習のトリガーとなり、自分の中では上手く機能した。恐らく、教科毎にノートを使うのが一般的だと思う。その場合は、毎日、当日分のノートの記載内容に目を通す習慣をどのように身につけるかを考えると良いだろう。

その他

その他の勉強方法について述べる。

友人と相互に教え合う

高校時代はクラスメートは、国公立大学受験を目指すという共通の目標があり、それぞれの学力レベルも知っている間柄だった。そのため、得意科目について教え合うという行為も自然に行われていた。教えるのも勉強と言われるが、実際に他人に自分が理解している内容を、相手が理解できるように説明する作業は、自分が理解することに比べて数倍の力を要する。説明対象について正確に理解していることは当然で、その内容の伝達の為の例えとか説明を考え、相手の理解レベルに合わせて説明内容も変化する必要がある。時には、自分で説明をしながら、ストンと腹に落ちるように理解することもあったり、逆に自分がとんでもない誤解をしていることが見つかることもある。複数人で話していれば、より効率の良い考え方や教え方に出会うこともある。教えて貰う立場では、教師とは異なる背景が自分に近い同世代の人間から教わることで、より理解しやすかったり、楽しかったりする。こうして教え合う関係が構築されると、次第にこの分野は誰に聞け、といった得意分野が出てくる。そうなると、自分の得意分野については一層理解しようと努力するようになるし、説明を重ねることでさらに理解が深まるという正のスパイラルが発生する。このように、教師とは異なる視点と立場を持った友人と教え合うのは、メリットが多い。

苦手分野の巻き取り

授業と予習、復習だけではなんともし難い部分も出てくる。基本的に、大学受験で必要とされる学力とは、小学校、中学校、高等学校で学ぶ範囲の集大成である。どちらかと言えば、知識に重点が置かれて記憶力が高い方が有利である。しかし、前年度に習得した知識に基づいて内容が構成される、積み重ね型の教科もある。この場合は、特定の箇所のみ重点的に学習しても効果は出ない。自分にとっては、数学がそれに該当し、中学校の正比例、反比例の当たりがモヤモヤしていた。そこで、本格的に受験勉強を始める高校3年生の時に、中学生用の参考書を購入して、6年分を勉強し直した。結果として、平均点以上を取れるようになった。

ノートについて

以下は、ノートについての蛇足である。

高校生時代に採用したルーズリーフによる授業ノート管理は、大学時代も続けた。大学でも機能したが、ノートを貸した場合に紛失するという事故も発生した。それ以降は、コピーを貸すことで防止した。

社会人になって 1990 年代は、小型化してシステム手帳でスケジュール管理と備忘録として使用する様になり、ルーズリーフは使用しなくなった。仕事の文書類は、ほぼA4サイズ (一部、A3 サイズ) に統一され、プリントアウトしたものを持ち歩く様になった。

2000 年代からは、文書の電子化が進むと同時に、可搬性の高いノートPCを携帯する様になった。その結果、プリントアウトした文書の量は減少していった。一方で、仕事を表すものとして、案件毎に紙の文書を扱うようになった。ある案件に必要な資料をクリアホルダーでまとめて管理していた。仕事に行く場合、必要なクリアホルダーと、ノートPCのみ持って行く。ノートは持たず、A4のノートパッド、もしくは、A4のブランク用紙を無印良品のアクリル クリップボードに挟み、書き込んだ紙は案件毎のクリアホルダーに挟んでいく。クリップボードのお陰で、立った状態でもメモが取れ、用紙には不要なプリントアウトの裏面を使ったりしていた。案件毎のクリアホルダーの最上面には、手書きで案件名とチェックリストを書いて、進捗も分かるようにしていた。作業が完了したら、赤字で済みと記述し、保管が不要なら破棄すれば仕事が1件片付くということが分かりやすかった。クリアホルダーが数冊になる場合は持ち歩く時用に薄いドキュメントケースにクリップボードと共に入れて鞄に入れていた。これで、クリップボードの金具部分でノートPCへの傷防止も果たすことができる。

2010 年代は更に文書の電子化が進み、共有機能が増したことと、ビューアーとしてスマートフォンタブレットなども使用できる用になった。その分、手書きの紙媒体が使いにくくなり、スキャナーでPDF化することで補完する様になった。普段のメモも、テキストファイルで記録する様になった。最近は、より表現力が高く入力負荷の低い Markdown 形式のテキストファイルでメモを作成するようになった。Markdown 文書の利点として、Word や Excel 文書と異なり diff による差分確認ができることと、Git でのバージョン管理との相性の良さがある。また、OS 標準の文字列検索が利用できる点も利点となる。コマンドラインgrep や findstr でも検索は可能だ。出力結果に意味不明な文字列が含まれることもない。更に、インターネット環境下では UTF-8が標準の文字コードとして使用されるようになり、エディターなどのツール類でUTF-8対応している物が増えた結果、自分に合った使い方ができるツールの選択肢が増え、より使い易い編集環境で作業ができるようになった。文書管理は、PC 上のファイルシステムのフォルダーで行い、OneDrive や Dropboxクラウド化している。短い使い捨てのメモや、アイデア出しには Evernote を利用している。単純な表計算や図の作成には Google Drive を利用している。プロジェクトで使用する文書は主に ExcelPowerPoint が現役だった。ただ、一段落した所で PDF でも出力していた。そうすれば、Mac の QuickLook でスペースキー押下で内容の確認ができる。Windows ならば、Microsoft Store アプリの WinQuickLook で同様の使い方ができる。

実現する方法は異なるが、高校時代に目指していた、ノートの薄型化と軽量化の追求は継続している。電子化が進むことで、ネットワークに繋がれば、現物のファイルを持ち歩かずに、どこでもノートにアクセスできるようになった。しかも、シチュエーションに合わせた機器でアクセスできる。混んだ通勤電車内ではスマートフォンの方が良いし、場所と電源の確保ができる場所ならノートPC、ベッドやソファーで寛いだ姿勢ではタブレットで、と選びたい放題だ。

また、ルーズリーフで始めたドキュメントの薄型化と軽量化の追求は、そのまま、ノートPCの進化とも連動している。自分達の世代は、紙文書の文化からデジタル文書の文化への移行の流れに巻き込まれて生きてきた。親の世代は、紙文書の文化が中心だろう。現在の子供の世代は、デジタル文書の文化の世代だ。次の世代は音声入力だろうか、喧しくなりそうだ。いずれにせよ、ペンと紙による文書記述の手法が、文字変換ソフトに置き換わる事で、漢字の読み書きや適切な漢字、用語の選択などの国語力が落ちないようにする必要があろう。

近い将来、小学校からのプログラミング教育が必修化される。プログラミングに必要な論理的な思考を、幼い頃から育もうとしているようだが、どうだかなあ、というのが率直な意見である。小学生までは国語を中心とした教育を行って、中学生で英語を導入するのと同時期にプログラミング教育を始めた方が効果的ではないだろうか。SVOC といった英文法を修得した後にプログラム言語を学んだ方が理解し易いだろう。一般的にプログラム言語は暗黙的に英語をベースとして作り出されている。日本語ではなく英語で考えた方がプログラミングには向いているし、各種ライブラリーやメソッド名も英語が使用されている。ドキュメントは通常は英語のみである場合も少なくない。日本語がされている場合は、英語版と日本語版の両方みることで、英語の学習にもなるだろう。

また、大学の共通試験も、導入時に普及していたマークシート方式が採用されて30年近く経過した。昨今の多くのIT関連の技術資格試験では、PC操作の選択式回答方式が採用されている。技術的にはWebサーバーで試験問題を出題し、スマートフォンで回答することが可能となっている。大学の共通試験を記述式も含めてPCで行うようにすれば、必然的にPC操作やキーボード入力が受験の前提スキルとなり、教えなくとも勝手に学ぶことになるだろう。

随分前にネットブックが話題になったが、その根底には発展途上国での子供のコンピューター教育用として、安くて丈夫で必要十分な能力を持った可搬性の高いコンピューターを提供する目標が有った。それにより、ノートPC型で5万円以下というジャンルが生まれた。大学入学試験をPCで行うのと同時に、3万円レベルのサブノートPCのジャンルの確立を国策として進めてみてはどうだろうか。ミドルレンジのスマートフォンレベルのPCである。海外では、そのセグメントは Chromebook が強いと聞く。Google へのロックインを避けるなら、OS は例えば Ubuntu 日本語 remix を使えば良い。最悪、学校には据え置きのデスクトップPCを用意して、生徒はポータブルSSD のみを持ち歩く。学校で、ポータブル SSD から PC を起動すれば、生徒の個人環境が立ち上がる。授業で使用する教科書もノートも辞書も全て ポータブル SSD に収まるため、「なまら重」い教材を持ち歩く事も無くなる。ノート PC の場合はそれよりもずっと重くなるが、それでも800gに抑えられれば、ランドセルよりは軽い。価格も3万円程度であれば、ランドセルと同程度と思えよう。前述のネットブックが出て来たのが約10年前だが、Windows XP を動かして 1kg 程度の重量に押さえていた。OS についても、新たに開発せずに Ubuntu など既存の Linux ディストリビューションを国で定め、国費からそのディストリビューションの改良、開発を行うことで、使い易くするだけでなく、国内の開発技術の底上げを行うことも望めよう。OS の新規開発よりも、教材開発や通信費補助に予算を割り当てれば、費用対効果が高いのではないだろうか。また、OS に一般的な Linuxディストリビューションを採用する事は、即ち各生徒の学習環境が開発環境ともなるメリットがある。bash だけでなく、PerlRubyPython といったスクリプト言語は使える状態になっているだろうし、GCC は通常は直ぐに導入できるはずだ。本格的なプログラミング環境を購入する必要がない。興味を持った生徒はその日からプログラミングを始められる。また、互換性はまだ十分ではないが、オフィスソフトも揃っている。物足りなければ、国の負担で改良すればよい。CPU も、x86 に限定せずに ARM や RISC-V 準拠のものも対象にして省電力に注視すれば、バッテリー容量を減らすことで、より軽量化が望めるのではないだろうか。そうして環境面の均一化ができて、コンピューターリテラシーが底上げされるようになれば、新入社員がキーボードで入力できないなどと嘆くことも無くなるだろう。

さて、絶賛脱線中だが、もう少し続く。

ノートは自分の脳の補助記憶装置である。PC で言えば、外付け HDD だったり、NAS だったり、クラウドストレージだったりに該当する。以前、同じ様な文書を作ったなあ、というときに紐解くような使い方が主である。しかし、忘れたときに思い出すためだけではなく、ノートに纏めるという作業自体に、物事について自分の捉え方と考え方を整理するという作業が含まれている。ノートは紙媒体でもテキストファイルでもWebサービスでも何でも良い。将来は言葉で入力したり、脳波によって思考を入力する様になるかもしれない。何れも、自分の考えを整理するツールとして使い、迷ったときの道標として使えればよい。自分にとって入力しやすく、参照しやすく、無くならないものであれば、時代の環境に合わせて保存方法は選択すればよい。ノートに纏めるというだけでも立派な学習手法であるし、後で見返して自信を取り戻すこともできる。ノートを取るというシンプルな行為が持つ効能は、もっと評価して活用したいものだ。


参考 - 「日本語の主語とは何か」 - 「ニトリ ランドセル スピリッツ

Windows Update 適用後に ThinkPad トラックポイント・キーボードのミドルクリックが効かなくなる

f:id:dhtakeuti:20181224190910j:plain
ThinkPad トラックポイント・キーボード

備忘録である。

私は、LenovoThinkPad トラックポイント・キーボードを愛用している。長年、ThinkPad を使ってきたから慣れているというのもあるし、マウス操作をするのにキーボードから手を離さずに済むのが良い。また、センターボタンがあり、スクロールではなく、ミドルクリックとして使っている。ChromeFireFox でタブのクローズやリンクを新規タブで開く操作が、ミドルクリック一発でできるのが便利だ。

久しぶりに Windows 10 のデスクトップ PC を使うことになり、ひたすら待たされる Windows Update を適用した後で再起動したら、ミドルクリックが使えなくなってしまった。どうやら、マウスのデバイスドライバーが更新された様子である。

Lenovo のサイトから最新版のドライバーをダウンロードして適用してもダメ、マウスのプロパティ設定ではスクロールの設定のみで、センターボタンにする設定は無い。

Google で調べてみたところ、やっと原因がわかった。マウスのプロパティの詳細設定タブで「トラックポイントの優先スクロール」がチェックされた状態になっていた。このチェックを外したところ、ミドルクリックが復活し、元の操作ができるようになった。分かってしまえばしょうもない設定漏れとなるが、この設定画面は何度も見たはずなのに、「トラックポイントの優先スクロール」が何を意味するのかが理解できていなかった。

f:id:dhtakeuti:20181224191024j:plain
マウスのプロパティで「トラックポイントの優先スクロール」のチェックを外す。

まとめ

  • 大規模な Windows Update を適用した後は、設定が初期化されることがあることを前提に確認しよう。
  • 設定項目が何を示しているのか冷静に見よう。

Ruby on Rails チュートリアル第2章 MVC を理解する (2周目)

f:id:dhtakeuti:20181109110131p:plain
Ruby on Rails Tutorial

Ruby on Rails チュートリアル の2周目、第2章の User モデル追加を行った。演習でなんとなく MVC の図で分かったつもりだったが不十分だったので、もう少し突っ込んでみた。

シーケンス図にしてみる

第2章の「2.2.2 MVCの挙動」のような図はよく見かけるもので、なんとなくは理解していたが、実際のソースコードとのマッピングが自分の中で十分できていなかった。そこで、シーケンス図を描いてみてより理解を深めることにした。下図はチュートリアル内の MVC を説明する図である。

図 2.11: RailsにおけるMVC

実際に Rails を動かし、ユーザーの一覧表示、新規登録、ユーザー情報編集、削除のオペレーションを行って、新たに追加されたソースコードがどのように使用されているかを確認した。結果は次のようになった。

ユーザーの一覧表示

[sequence]index

ユーザーの新規登録

[sequence]edit

ユーザー情報編集

[sequence]create

ユーザーの削除

[sequence]destroy

解析の方法

最初は、byebug を使って少しづつ動作してみたが、効率が悪い。それぞれのソースコードに記述されたクラスのメソッドで、呼び出しと return の箇所に puts でメッセージを標準出力させるようにした。いわゆる print debugging である。これでブラウザーで操作をしては、Rails のメッセージ出力を読むことで呼び出し順を確認した。

メッセージを出力するのに苦労したのは、モデルの user.rb である。scaffold で生成されたソースコードは下記の通り。

class User < ApplicationRecord
end

使用されているメソッドとメッセージ出力を加えたソースコードは下記の通り。

class User < ApplicationRecord

  def User.hello
    puts "-------------------------------- user.rb#hello ------"
  end

  def User.all
    puts "-------------------------------- user.rb#all Enter --"
    @users = super
    puts "-------------------------------- user.rb#all Exit ---"
    return @users
  end

  def initialize(*)
    puts "-------------------------------- user.rb#new Enter --"
    @user = super
    puts "-------------------------------- user.rb#new Exit ---"
  end

  def User.find(args)
    puts "-------------------------------- user.rb#find Enter --"
    @user = super(args)
    puts "-------------------------------- user.rb#find Exit ---"
    return @user
  end
end

また、ビューの index.html.erb は試行錯誤した。修正後のソースコードは下記の通り。他のビューのソースコードも同様に修正した。

<p id="notice"><%= notice %></p>

<% puts "-------------------------------- index.html.erb Enter --" %>
<h1>Users</h1>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Email</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @users.each do |user| %>
      <tr>
        <td><%= user.name %></td>
        <td><%= user.email %></td>
        <td><%= link_to 'Show', user %></td>
        <td><%= link_to 'Edit', edit_user_path(user) %></td>
        <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New User', new_user_path %>
<% puts "-------------------------------- index.html.erb Exit ---" %>

コントローラーは、各メソッドの最初と最後にメッセージ出力を追加した。修正後のソースコードは下記の通り。

class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]

  # GET /users
  # GET /users.json
  def index
    puts "------------------------- UserController#index Enter --"
    #User.hello # => method not found error
    @users = User.all
    puts "------------------------- UserController#index Exit ---"
  end

  # GET /users/1
  # GET /users/1.json
  def show
  end

  # GET /users/new
  def new
    puts "------------------------- UserController#new Enter --"
    #@user = User.new
    @user = User.new()
    puts "------------------------- UserController#new Exit ---"
  end

  # GET /users/1/edit
  def edit
  end

  # POST /users
  # POST /users.json
  def create
    puts "------------------------- UserController#create Enter --"
    @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
    puts "------------------------- UserController#create Exit ---"
  end

  # PATCH/PUT /users/1
  # PATCH/PUT /users/1.json
  def update
    puts "------------------------- UserController#update Enter --"
    respond_to do |format|
      if @user.update(user_params)
        format.html { redirect_to @user, notice: 'User was successfully updated.' }
        format.json { render :show, status: :ok, location: @user }
      else
        format.html { render :edit }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
    puts "------------------------- UserController#update Exit ---"
  end

  # DELETE /users/1
  # DELETE /users/1.json
  def destroy
    puts "------------------------- UserController#destroy Enter --"
    @user.destroy
    respond_to do |format|
      format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
      format.json { head :no_content }
    end
    puts "------------------------- UserController#destroy Exit ---"
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_user
      puts "------------------------- UserController#set_user Enter --"
      @user = User.find(params[:id])
      puts "------------------------- UserController#set_user Exit ---"
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def user_params
      params.require(:user).permit(:name, :email)
    end
end

シーケンス図は PlantText を使用して作成した。ユーザーの追加の操作の PlantUML の記述は下記のようにした。他の図も同様に作成した。

@startuml

title
  Ruby on Rails チュートリアル 2.2.2 MVCの挙動
  (POST /users createアクション)
end title
hide footbox

actor ブラウザー as user
box "Control"
participant users_contoller.rb as control
end box

box "View"
participant new.html.erb as vnew
participant show.html.erb as vshow
end box

box "Model"
participant user.rb as model
participant ActiveRecord as ar
end box

database SQLite3 as db

user -> control : GET /users/new
activate control

control --> model : new
activate model
model --> ar : new
model -> control
deactivate model

control -> vnew
activate vnew
deactivate control
vnew -> user
note left
  ユーザー登録
  画面の表示
end note
deactivate vnew

====

user -> control : POST /users
activate control
note left
  新規ユーザー
  の登録
end note

control -> model : create(1)
activate model

model -> ar : create(1)
ar -> db : SQL
note right
  INSERT INTO users
  (name, email, created_at, updated_at)
  VALUES (?, ?, ?, ?)
end note
model -> control : 成功
deactivate model

deactivate control
control -> control
activate control
note right : Redirect GET /users/1

control -> model : find(1)
activate model

model -> ar : find(1)
ar -> db : SQL
note right
  SELECT users.*
  FROM users
  WHERE users.id=1
  LIMIT 1
end note
db -> ar : user (1)
ar -> model : user (1)
model -> control : user (1)
deactivate model

control -> vshow : user (1)
activate vshow
deactivate control
vshow -> user : user (1)
note left
  ユーザー情報
  画面の表示
end note
deactivate vshow

@enduml

まとめ

  • シーケンス図を書いてみたら理解が深まった。
  • モデル クラスでは、ActiveRecord が大活躍。
  • Ruby のメソッドのオーバーライドの書き方がわかった。
  • byebug の使い方がわかった。
  • PlantUML でのシーケンス図の書き方がわかった。

以上。

Ruby on Rails チュートリアル第1章で Heroku へのデプロイに失敗 (2周目)

f:id:dhtakeuti:20181109110131p:plain Ruby on Rails チュートリアル の2周目、最後の Heroku へのデプロイがうまくできなかったため、対処方法をまとめておく。

ビルドエラー

git push heroku master で Heroku に push するとビルドが開始するが、途中でエラーとなってしまう。発生したエラーの内容は、下記のようになっている。

  :
remote: Building source:
remote:
remote:  !     Warning: Multiple default buildpacks reported the ability to handle this app. The first buildpack in the list below will be used.
remote:                         Detected buildpacks: Ruby,Node.js
remote:                         See https://devcenter.heroku.com/articles/buildpacks#buildpack-detect-order
remote: -----> Ruby app detected
remote: -----> Compiling Ruby/Rails
remote: -----> Using Ruby version: ruby-2.4.5
remote: -----> Installing dependencies using bundler 1.15.2
remote:        Running: bundle install --without development:test --path vendor/bundle --binstubs vendor/bundle/bin -j4 --deployment
remote:        Warning: the running version of Bundler (1.15.2) is older than the version that created the lockfile (1.17.1). We suggest you upgrade to the latest version of Bundler by running `gem install bundler`.
remote:        Fetching gem metadata from https://rubygems.org/.........
  :
remote:        Running: rake assets:precompile
remote:        Yarn executable was not detected in the system.
remote:        Download Yarn at https://yarnpkg.com/en/docs/install
remote:        rake aborted!
  :

上記で、1箇所ワーニングメッセージが出ている。Heroku 上では Bundler のバージョンが 1.15.2 を使用していることを示している。PC 上では 1.17.1 を使用していたため、Gemfile.lock に 1.17.1 が記録されている。これについては、PC 上の Bundler を一旦削除して 1.15.2 を導入することで対応した。

remote:        Warning: the running version of Bundler (1.15.2) is older than the version that created the lockfile (1.17.1). We suggest you upgrade to the latest version of Bundler by running `gem install bundler`.

エラーメッセージが出ている。これは node.js でビルドできなければならないことを示しているらしい。

remote:        Yarn executable was not detected in the system.

このエラーに対しては、「herokuにアップできません」を参考にして、下記コマンドで対応した。

$ heroku buildpacks:add --index 1 heroku/nodejs

アプリケーションエラー

ビルドのエラーがなくなっても、ブラウザー画面にはアプリケーションエラーが表示される。ログを参照すると、npm start を実行しようとしている。

$ heroku logs --tail
  :
2018-12-06T17:01:39.670033+00:00 heroku[web.1]: Starting process with command `npm start`
2018-12-06T17:01:37.789569+00:00 app[api]: Scaled to web@1:Free by user xxxxxxxx@xxxx.com
2018-12-06T17:01:38.000000+00:00 app[api]: Build succeeded
2018-12-06T17:01:37.767042+00:00 app[api]: Release v3 created by user xxxxxxxx@xxxx.com
2018-12-06T17:01:41.658747+00:00 app[web.1]: npm ERR! missing script: start
2018-12-06T17:01:41.665561+00:00 app[web.1]:
2018-12-06T17:01:41.665874+00:00 app[web.1]: npm ERR! A complete log of this run can be found in:
2018-12-06T17:01:41.666017+00:00 app[web.1]: npm ERR!     /app/.npm/_logs/2018-12-06T17_01_41_660Z-debug.log
2018-12-06T17:01:41.736904+00:00 heroku[web.1]: State changed from starting to crashed
2018-12-06T17:01:41.738983+00:00 heroku[web.1]: State changed from crashed to starting
2018-12-06T17:01:41.719407+00:00 heroku[web.1]: Process exited with status 1
  :

herokuにアップできません」を参考にして、「Heroku - The Procfile」をProcfile を作成してみたがアプリケーションエラー発生。Profile の内容は下記の通り。

web: bundle exec rails server -p $PORT

ログの内容は下記の通り。

  :
2018-12-06T17:17:14.000000+00:00 app[api]: Build succeeded
2018-12-06T17:17:20.202948+00:00 heroku[web.1]: Process exited with status 1
2018-12-06T17:17:20.227931+00:00 heroku[web.1]: State changed from starting to crashed
2018-12-06T17:17:20.230674+00:00 heroku[web.1]: State changed from crashed to starting
2018-12-06T17:17:20.134913+00:00 app[web.1]: /usr/lib/ruby/2.5.0/rubygems.rb:289:in `find_spec_for_exe': Could not find 'bundler' (1.17.1) required by your /app/Gemfile.lock. (Gem::GemNotFoundException)
  :

一旦、別の Rails アプリを作成して Heroku に新しいリポジトリーを追加し、同様にデプロイしたところサーバーの起動まではうまくいった。

この時点で Heroku のダッシュボードを見ると、エラーが発生しているリポジトリーの Settings -> Framework が Node.js になっている。もう一つのリポジトリーを見ると、Settings -> Framework は Ruby,Node.js になっている。

恐らく heroku buildpacks:add --index 1 heroku/nodejs が悪さをしていると思われたため、heroku/ruby も追加したところうまくサーバーの起動まで行った。

アプリケーションエラー

しかし、ブラウザーからアクセスすると、アプリケーションエラーが発生している。ログを参照したところ、下記のエラーが発生している。

  :
2018-12-08T15:52:05.876298+00:00 heroku[web.1]: State changed from starting to crashed
2018-12-08T15:52:05.754921+00:00 app[web.1]: /app/vendor/bundle/ruby/2.4.0/gems/activerecord-5.1.6.1/lib/active_record/connection_adapters/connection_specification.rb:188:in `rescue in spec': Specified 'sqlite3' for database adapter, but the gem is not loaded. Add `gem 'sqlite3'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord). (Gem::LoadError)
  :

Rails アプリを初めて Heroku にデプロイして動かなかったときの対処法 」を参考に config/database.yml を書き換えてデプロイしたところ、エラーは解消して、ブラウザーから期待通りの表示がされることが確認できた。

まとめ

Heroku でのビルドの失敗への対応

  • Bundler のバージョンを heroku に合わせる。(不要かもしれない)
$ bundle clean --force
$ gem uninstall bundler
$ gem install bundler:1.5.2
$ rm Gemfile.lock
$ bundle install --without production
  • config/database.yml の puroduction のデータベースの設定を SQLite3 から PostgresQL に変更する。
$ vi config/database.yml
$ git diff config/database.yml
diff --git a/config/database.yml b/config/database.yml
index 0d02f24..9ea67d8 100644
--- a/config/database.yml
+++ b/config/database.yml
@@ -22,4 +22,6 @@ test:

 production:
   <<: *default
-  database: db/production.sqlite3
+  adapter: postgresql
+  encoding: unicode
+  pool: 5
$
  • Heroku のアプリケーションを作り直して、ビルドパックに heroku/nodejs と heroku/ruby を設定する。
$ heroku apps:destroy
$ heroku create
$ heroku buildpacks:add heroku/nodejs
$ heroku buildpacks:add heroku/ruby
  • ビルドを実行する。
$ git commit -a -m "retry building on heroku"
$ git push heroku master
$ heroku logs --tail

以上。

何故、私は Ruby on Rails チュートリアルを読むことにしたのか?(1周目)

f:id:dhtakeuti:20181109110131p:plainまだ1周目であるが、Ruby on Rails チュートリアル を読み始めて、第1章から第14章まで読了したので、振り返ってみる。

眠れる10年

私は50代で SE だった。仕事では主に Java SE/EE でアプリケーション・サーバーの部分の設計、開発を担当していたが、社内の既存フレームワーク上での開発を中心だったため、ほとんど POJO での開発となっていた。そのため、この10年くらいの間のWebサービスの動向や、実際の実装などとは縁がない状況だった。その後、参画しているプロジェクトから抜け、"API" を扱うプロジェクトに携わることになった。

ところが、である。まずもって、"API" が何なのか分からない。Webサービスを構築するんだと言われ、それと "API" が結びつかない。自分にとって "API" とは、Windows の Win32 API とか、UNIXシステムコールとかであるという認識であった。

いろいろ調べてみると、"API" とは RESTful API を指していることが分かった。そして、RESTful APISOAP の簡略版のようなもので、JSON フォーマット(及び、XML) でやり取りを行うものと理解した。リソースに対して HTTP の4つのメソッド "GET"、"POST"、"PUT"、"DELETE" を CRUD に当てはめて操作をする点がシンプルでわかりやすいことと、JSON フォーマットを採用することで JavaScript と親和性が高いことから、WebAPI の主流になり、API と言えば RESTful API を指していることがようやく分かった。

さて、10年以上昔から、Web を中心としたビジネスが企業にとって大きな影響を持ち、楽天Amazon といったネットショッピングを始め、我々はブラウザーを通じてのサービスの提供を受けている。この Web サービスの画面を表示する技術として、当時から MVC モデルが使用されている。今でも Java による Web アプリケーション入門として Apache Tomcat を使用して、MVC モデルでの構築を紹介されている。では MVC モデルで Web アプリケーションを作ったことがあるかと言えば、web.xmlJSPServletを書いてフォーム入力画面を入力して、"こんにちは、〇〇さん!" と表示される程度で分かったつもりになっていた1。ログイン処理の実装とか、 HTTP セッションの使用方法、Cookie の使い方も知らず、Struts での実装の仕組みを何となく掴んでいただけである。

これはまずい。

Web サービスが渦巻く現代において、その技術を自分の手を動かして身につけなければ今後 IT 関連で喰って行くことができなくなる、そう思った。このままでは会話でさえついて行けない。

その後、いろいろあって退職し、現在は再就職に向けての準備期間と決めた。そして、一応 経験がある IT 関連への再就職を目指して、眠っていた10年を取り返そうと考えた。

いきなり最新技術に行くのはハードルが高い。人工知能ブロックチェーンといったもの2に行く前に、Web サービスを構築できるようになることを目指し、RESTful API とか DevOps、テスト駆動開発など、自分のこれまでの経験の延長線上にある技術や手法を身に付けたいと考えた。

そこで、まずは Web サービス関連のここ10年間の流れを追いかけつつ、少し前の主流となる技術を実際に実装、運用してみることにする。世のオジさんに多い Java SE 1.8 の壁3を最初に取り除く必要もあり、最初は慣れ親しんでいる Java から始めて行こうと考えていた。

まずは MVC モデルを理解しよう。

最近の Web アプリケーションの構築は、Java では従来の Struts を始め、Strus2、Spring-MVC などのフレームワークが代表的なものらしい。PHP での実装では、CakePHP、Laravel などがある。Ruby では Ruby on Rails が代表的だ。Node.js では Express が多く使われている。また、Microsoft では主に C# で実装する ASP.NET がある。

今だとこの中で Struts2 を使用して、JSPServlet、JavaBeas、アノテーションを使用した MVC モデルでの実装の紹介が多く見られたが、あまり具体的な例は用いられていなかった。他の言語でのサンプルを探す中で、 Ruby on Rails チュートリアルで構築する Web アプリケーションが、非常に具体的で実践的な例だった。目次のレベルから見てもログイン処理でセッション管理やパスワード認証を含んでおり、テスト駆動での開発を推奨している。そこで、実際の内容はどうかと第1章と第2章を読んでみた時に、びっくりした。

第2章を読んで衝撃を受けた。

第1章では、Cloud 9 を IDE として使用する方法、Rails の導入とデフォルトの Rails アプリケーションの起動、開発言語ではお馴染みの "Hello World!" の出力の仕方、Git の操作方法、Bitbucket の操作方法、Heroku へのデプロイの仕方までが紹介されている。ここまでは、MVC モデルのさわりだけの紹介に留まっており、モデルは使用していないが、Rails で Web 画面上に文字列を出力する方法が分かる。ここまでは、なるほど Cloud で開発するのはこう進めるのか、といった感じでさらりと読めた。

第2章に進むと、簡単な Web アプリケーションを作成する方法が紹介されている。この中で、User モデルを作成する方法が紹介されているが、name と email という要素を持つことを指定して、以下のスクリプトを実行すると、User モデルのクラス定義だけでなく、それに関係する画面のテンプレートまで作ってくれる。ここまでは、今時のフレームワークとしては割と一般的になっているじゃないかと感じた。

$ rails generate scaffold User name:string email:string

ところが、次の操作として、下記を実行するように解説している。

$ rails db:migrate

このスクリプトで、現在参照している DB に対して、裏でテーブル作成が行われ、その内容に対して CRUD オペレーションに対応する RESTful API の画面から操作ができるようになってしまう。

これまで Java で DB を使う場合は、DDL を書いて DB にテーブルを作成し、Java のクラスに jdbc で接続して SQL 書いて行を取り出して、といろいろ記述してやっとデータが取り出せるようになっていた。その後 JavaBeans 経由で JSP に反映してやっと画面に表示されるようになるはずだ。それが、お仕着せではあるが、スクリプトを1行実行するだけで DB のテーブル定義から RESTful に則った操作画面までできてしまうことに心底驚くと共に、フレームワークブラックボックス性に一種の恐ろしさを感じた。非常に生産性が高い反面として、ある程度内部の動作を知って使わないと、危険な目に会うのではないかという不安だ。

これで、俄然、興味が湧き、実際に Ruby on Rails チュートリアルをやってみようと決めた次第である。

第3章で静的ページの表示方法、テストの作成と実行が説明され、第4章は Ruby の基本的な文法を説明した後、第5章以降は twitter に似た Web アプリケーションの作成の説明となっている。

Web サービスでは必ず必要と思われるユーザー管理やセッション管理、セキュリティー対策、メール連携、クラウドストレージ(Amazon S3)など、実用的な実装のサンプルとして非常に興味深いと感じた。2周目は読むだけでなく、実際にコードを書いて実行しながら読み進め、更に3周目もコーディングしながらフレームワークの理解を深めて行く予定だ。

参照先


  1. 2周目にしてMVCのモヤモヤが晴れた!(twitter) で紹介されている図が分かりやすい。この程度は理解していたつもりだった。

  2. ガートナーが「日本におけるテクノロジのハイプサイクル」2018年版発表 によると、『人工知能』『ブロックチェーン』は既に『幻滅期』に入っているそうだ。

  3. JDK 8の新機能ラムダ式とかストリーム機能とか、読むことすらできない。