Apache-Tomcatのプロキシ設定を変更したらsessionがとれなくなった

Javaアプリでsessionスコープに保存したPOJOを取得できずにハマったときのメモ。

構成

  • アプリのフレームワークはSpring3。通常のServletアプリケーション。
  • 前段のWebサーバにApache、JavaアプリのコンテナとしてTomcatを用いて、両者の連携はmod_proxy_ajpを利用。

やりたいこと

ふつうにsessionスコープに保存したobjectを別のパスで取得する。
イメージとしては下の感じ。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

package hoge.foo.contoroller


import hoge.foo.LoginForm

@RequestMapping(value="/user")
public class UserController{

private static final String SESSION_FORM_NAME = "user_input_data"

/**
* ログイン
*/
@RequestMapping(value="/login", method=RequestMethod.POST)
public String login(
@Valid @ModelAttribute LoginForm form,
BindingResult result,
HttpServletRequest request,
Model model) {

if(result.hasError){
return "login"; //入力エラー
}

// ログインに関する処理

request.getSession().setAttribute(SESSION_FORM_NAME, form); //session格納
return "dashboard";
}



/**
* セッションのユーザデータを参照して編集画面を表示する
*/
@RequestMapping(value="/edit", method=RequestMethod.GET)
public String editView(
HttpServletRequest request,
Model model) {

LoginForm form = request.getSession().getAttribute(SESSION_FORM_NAME);

if(form == null) {
return "login"; // ログイン画面に戻る
}
//後続処理

return "edit";
}

}

sessionからデータがとれない!

/user/editを叩いても常に/user/loginに遷移する。つまりform == nullが常にtrueとなってしまっている。
念のためデバッガでステップ実行してみても、やはりformがnullである。

セッションタイムアウトの設定やSpringでのセッションの利用方法など、あらゆるドキュメントを確認するも、
まったく解決の糸口が掴めない。なんだ….なにが悪いっていうんだ….!!

あっ!

ここで、前日の開発で別のコンテキストパスで動作させるために、server.xmlとhttpd.confのProxyPassを変更したことを思い出す。
httpd.confはこんな感じ。

1
ProxyPassMatch  /  ajp://localhost:8009/spring/

そしてJSESSIONIDがクッキーに保存されていて、クッキーにはPathがあって……!?

あれ…?もしかしてセッションIDのクッキーのPath、ずれてるんじゃない…?

とりあえずリバースプロキシの設定を戻し、ApacheとTomcatを再起動。

1
ProxyPassMatch  /  ajp://localhost:8009/

##あー、とれたわー。sessionから普通にobject取得できたわー。

ちなみにリバースプロキシでパス階層がずれる場合は、ProxyPassReverseCookiePathディレクティブを使うらしいです。



Cabin.jsでのブログ運用を保留した話

ブログ生成をNode.jsベースのstatic site generator、Cabinに乗換えようとしたが、すぐに移行したいほどでなかった。

発端

このブログはwintersmithというNode.js製のstatic site generatorを使って、Markdownで書いている。
記事はこちら

不満というほどではないが、懸念はいくつかある。

  • あまり活発に動いていないっぽい
    • 本家の更新がしばらく止まってる
    • プラグインが全然増えてない
  • masterにpushしてからgh-pagesにpushするデプロイステップがない
    • werckerを使って自動化しているが、使っているデプロイステップは自前のものじゃない
    • wercker待ちになるときがある
    • werckerを毎回見に行くのが面倒
  • Node.jsのうまみをもっと引き出した拡張性が欲しい
    • gh-pagesにpushするならGruntがよさそう
    • Gruntは活発に更新されているし、プラグインもたくさんでている
    • デザインもいろいろ選べるといい

Cabin.js

Cabinは、”ブログを書く”ことに注力したNode.jsベースのツールで、Octopressのようにgh-pagesデプロイが標準装備されている。
このデプロイはgrant-gh-pagesをつかって、Gruntファイルつきで用意される(後述)。

CIサービスを使ってデプロイするのも当然便利ではあるのだが、Gruntを使うことでpushとデプロイを分離できるメリットがある。
ブログを途中まで書いてpushしておき、別の場所で続きを書き始めるときなどは、push=デプロイじゃないほうが嬉しい。
また、CIサービスは待たされることがあるのも仕方ない。


Cabinは新規作成時に対話的にブログコンテンツのひな形をつくれるところも非常によい。
付属するGruntfileがこれを実現しているようだ。
npm initのように、何をつくるのか、どういう設定にするかを聞いてくれるので、それに答えるだけで設定が完了する。
ブログのデプロイ先としてgh-pagesを使うようにすると、grunt deployをgh-pages用としてGruntFileを作成してくれる。

また、テンプレートとしてjadeとejsが選べたり、デフォルトでデザインテーマが3つついて来たりする。
CSSもStylusを使ってライブリロードしてくれるので、デザインもストレスなく変更できる。
Disqusも標準で実装されているので、本当にすぐにブログが始められる。

でも移行は保留

非常に便利で素敵で使いやすくて、ブログを最初から作るなら迷わずこれを使っていただろう、とおもう。
だが現状のWintersmithから移行するか、となると話は別であった。

  • RubyとPythonが必要
    • Compassのインストールが必要なので、Rubyを動かす必要がある。(ライブリロードはcompass -wで動いているっぽい。)
    • Syntax highlightのために、Pygmentsを使うので、Pythonが必要。Pygmentsは非常に多くの言語をサポートしているので、好きではあるが。
  • Stylusの学習コストと用意されているコード理解のコストがそこそこかかる
    • そもそも普段はデザインをしないので、いまのところStylusの学習は優先度を上げたくないし、コストもかけたくない。
    • 用意されているStylusの内容を理解するのも結構大変
  • 環境が複数あると面倒
    • すでに複数環境でnpm installするのさえ面倒なくらい(怠惰)
    • ちょっと直したいなと思ってもRuby/gem(Compass)、Python、Node/npmまでやって、やっとのことできたのはHTMLの修正だとちょっと不釣合
    • 今のままでGruntからgh-pagesにpushできるようにした方がスマートな気がする
  • これまでのブログがちゃんと表示されるかチェックしいなといけない
    • Markdown基本の記法は一緒だが、generatorが認識するヘッダ部分とかが違う。デザインが違うので、微妙なずれとかもやっぱり出ちゃう。
    • Markdown意外にも、Google Analyticsとかtweetボタンとかもviewテンプレートに移行する必要がある。

それでもまだ、そのうち移行したいとは思っているが、移行コストほど困っているわけでもないので、とりあえず保留とした。
そのうち時間があれば、そしてGruntやStylusがメインストリームとしてもっと発展したら、移行する。かも。



pplogのbotをつくりました

pplogが楽しすぎて、最近はtwitterより見てる時間が長い。愛が高まりすぎるとなにか作ってみたくなるのがプログラマの性というもの。
というわけで、そんな今もっともホットなインターネット、pplog用botをnode.jsでつくった。

コンセプト的な話

pplogの3大「できない」を守ったbotにしなければならない。
愛着のあるサービスの根幹を技術でつぶすことは許されないからだ。

「つながれない」

統計、その他のポエムへのリンクを入れるなどはよくない。poem analyticsとか考えたけど没にした。

「残せない」

スクレイピングして保存するようなシステムはいけない。どんなポエムが流行っているか、ポエムを形態素解析するってのを考えたけど、没にした。

「しゃべれない」

連投して足あとを稼ぐような結果になるとつまらない。むしろ読んだ人のタメになったり、感心してもらえるようなことがいい。

しばらくの間、色々なポエム生成機、すなわちポエみエンジンを考え続けた結果、辞書的なものをランダムに投稿するものがいいんじゃないか、と思い至った。
淡々と投稿を続けるbotのイメージと、辞書の無機質な文調がマッチする。辞書のジャンルを絞ってあげればbotの特徴も出しやすい。

ポエみのあるジャンル、もう寿司しか思いつかなかった。実際、寿司に関するポエムは非常に多い。寿司が嫌いなひとにも出会ったことがないし、実際、自分が寿司がすきで
寿司がすきでどうしようもない。寿司というものはかなり奥が深く、基本動作が作り終えた後に、機能拡張もしやすい。

ということで、寿司ロボットを作ることにしました。

開発する

ソースにアカウント情報とかいろいろ含まれているので、リポジトリはBitbucketのプライベートGitを使った。寿司ネタ情報を保存するデータストアとしてMongoDBを使い、
取得した寿司ネタをもとにゴニョゴニョしてポエムを生成する。そしてpplogに投稿するためにメール送信をする。

package.jsonでもさらしておこう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"name": "sushi_poem",
"version": "0.0.0",
"description": "sushi poem bot",
"main": "sushi.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": "",
"keywords": [
"pplog"
],
"author": "shoota",
"license": "MIT",
"dependencies": {
"mongoose": "~3.8.7",
"nodemailer": "~0.6.0",
"request": "~2.33.0",
"cheerio": "~0.13.1"
}
}

npm init で無意識にMITライセンスにしてるのはご愛嬌。

bot起動

gists_capture

どう複雑にしようとメール一個送るだけなのでバッチとして作った。今のところ6時間おきにcronが起動してpplogにポエムメールを送る。

### issue

  • ポエムの内容をもうちょっと厚く、パターンを増やす
  • twitterアイコンも同時に変更し、投稿した寿司ネタのイラストが表示されるようにしたい。ただし寿司画像が足りない。
  • どんな寿司ネタが登録されているか一覧するWebページをつくりたい。 ました。 おしながき

というわけで、どうぞご一読ください。
@sushi_pedia



Node.jsでスクレイピング (pplog駆動)

ポエムっぽいのを書くだけのサービス、pplogの絶妙な感じ、文章を練るのがすごく楽しくて見事にはまっています。
いろんな人のポエムを読むのも楽しいし、自分が書いた文章のくせに何度も読み返すのも楽しい。
是非Web API化していただきたい|д゚)チラリ。
でもAPI化するかどうかなんてわからないし、するとしても待てないので、とりあえず自分のポエムをスクレイピングしてみた(pplog-driven development)。Node.jsが楽チン。

### 必要な npm モジュール

リクエスト飛ばすのと、返ってきたHTMLをパースするのができればAPI化できるので、よさげなやつをインストール。HTTPリクエスト用にrequest、jQueryっぽいHTMLparserとしてcheerioを採用。

1
2
3
4
express myPplog-API
cd myPplog-API
npm install
npm install request cheerio --save

スクレイピング実装

index.jsにrequest使ってHTMLを取ってきて、cheerioでパースしてresponceするモジュールを書く。

取得したHTML全体から.content-bodyの中のtextを取ってきて完了。
今考えると、.text()じゃなくて.html()の方がよかった。
一応API化できたけど、勝手にスクレイピングするのグレーな気がするのでデプロイはしてない。

本家サイトのデザインと隠し要素のバランスが楽しいみたいなとこあるのでAPI化しても面白みがない。ということで、みんなもやろうぜー。



GithubのEmojisをFontAwesomeっぽい感じで使う

Emojisってのはcommit commentとか、issueとか、プルリとかでたまに見る、あの絵文字。
Github API v3 をびゃーっっと眺めていたらリストを取得できるのをみつけたので、FontAwesomeっぽい記法でアイコン(というか画像)を埋め込んでくれるjQueryメソッドを書いてみた。

仕様的な何か(つくりかたとか制約とか)

  • GithubのAPIでEmojisの名前とpngのURLをJSON形式で取得できる。認証やAPIキーは不要。
  • FontAwesomeっぽく、アイコンの種類はクラスで指定する。命名規則は、markdownで書くときの名前の頭に”gh-“をつける。
  • アイコンであることを明示するクラスとして’ge’をつける。FontAwesomeでの’fa’に相当するもの。
  • Emojisのpng画像のサイズは親DOMのfont-sizeをpx単位で取得し、それを高さとして設定する。

実装してみる

実装は超簡単で、JSONとってきて’ge’のクラスがついている<i>タグのクラス名から画像URLを引いて、その画像を埋め込むだけ。
<i>タグに親DOMのフォントサイズを高さとして指定して、imgタグの高さはheight: inheritを指定する。

1
2
3
4
5
6
7
8
9
10
11
12
$(function(){
$.getJSON(
"https://api.github.com/emojis",
function(emojis){
$('i.ge').each(function(){
var fsz=$(this).parent().css('font-size');
var cls = $(this).attr('class').replace(/gh-|ge| /g,'');
$(this).append('<img src="'+emojis[cls]+'" style="height:inherit">');
$(this).height(fsz);
});
});
});

表示してみる

gists_capture

そこそこ遜色ない感じで表示できてるっぽい。(ブラウザはChrome v.31.x)

### HTML全体はこんな感じ


サンプルではテキスト高さを揃えるために.ge { vertical-align: middle; }してるけど、好みとか周りの見た目とか、一緒に使うフォントとかで違ってきそう。



オラオラッシュを支える技術

この記事はジョジョの奇妙な冒険 Advent Calendar 2013の12/11の記事です。
お時間がある方はタイピングオラオラッシュタイピング無駄無駄もやってみてください。

好きな主人公

シリーズを通して、部によって主人公が変わっていくのもジョジョの魅力の一つだと思います。
そのなかでも僕は空条徐倫が好きです。それはおそらく、彼女が最も一般人であるからだ思います。
濡れ衣で刑務所に服役している一般人。その彼女が様々な機転と強い意志でプッチ神父と渡り合う様は胸を熱くさせずにはいられません。

好きなスタンド

エアロスミスです。生き物の姿をしていないスタンドが僕は好きなのですが、そのなかでもエアロスミスは抜群にかっこいい。
空を飛べるスタンドというのも魅力的で、それだけで適用性が高いというか、スタンドは精神の力なので空を飛ぶとか普通にできそうな感じがするのに、それをあえて能力の長所として全面にしたスタンドというのは、
なにか男のロマンを感じざるを得ません。

好きなキャラ(主人公以外)

イギーです。「イギー」とスタンド「ザ・フール」、どちらも「愚か」ですが、彼は本当の愚者ではなく、愚者を装った賢者であるところに魅力があります。
つまらない相手には馬鹿にさせておけばいい。自ら馬鹿にされることで状況を有利にする。それは僕自身の考え方からとても共感できるし、哲学でもあります。
子どもを放っておけなかったり、仲間のために命を賭してスタンドを動かすアツさも萌えます。
「ジョジョの奇妙な冒険 オールスターバトル」では、CVに北斗の拳やキョウリュウジャーでおなじみの千葉繁さんが採用されているのも萌えポイントですね。

好きな敵

プッチ神父です。冷静さと人間味を併せ持つなんともいえないバランスを持った敵だと思います。DIOよりも全然いい。
各々の人生が交わり、ぶつかり、全力で戦い抜く姿は敵味方を忘れて心を震わせる。純粋な野心、臆病さ。臆病であるが故の強さ。憧れすら抱いてしまいます。
「『素数』は1と自分の数でしか割ることのできない孤独な数字……わたしに勇気を与えてくれる」はあまりにも有名ですね。

オラオラッシュを支える技術

Advent Calendarに空きがあったので、遊びのつもりでタイピングオラオラッシュを書きました。
まぁ、技術というほど大したものは使っていないので、タイトルは完全に釣りでした。本当にありがとうございました。

個人的には初めてJavascriptでキーバインドを取得するという処理を書いたので、それが一応のところ僕のオラオラッシュを支えていると思っています。

1
2
3
4
5
//jQueryベース
$body.on('keypress', function( event ) {
// keycode map
if( event.which === 111 ){
//"o"を押されたときの処理.....

実装するときに調べて初めて知ったのですが、keypressで取得されるコードってブラウザ依存なんですね。
アルファベットキー(っていうのか知らないけど)は大体同じみたいなので今回はあまり関係なかったのですが。

あと、「The World」がかかるときに、CSSでcursor: waitを指定していて、マウスポインタがくるくる回るUIになるのも気に入っています。
実装するときに書きながら思いつきでやってみたんですが、意外と存在感があった。前から薄々感じてはいたけれど、Look&Feelを変えるのっては結構インパクトあるんですね。

Advent Calendarについて

この業界はなぜかジョジョ好きな人が多いので、今回、カレンダーをつくらせてもらいました。特にAdventarの運営や開発をされているhokacchaさんやjune29さんが参加してくれたのがとてもうれしいです。
12/11現在でまだまだ枠が空いています。もしジョジョへの思い入れが少しでもあったら、twitterでつぶやくだけでも大歓迎なので、ご参加ください。

この記事はジョジョの奇妙な冒険 Advent Calendar 2013の12/11の記事です。



Node.jsでgithub pagesのブログ運用

以前に、Github PagesにMarkdownで書くブログシステムをOctorpressで構築してみたが、諸事情あって結局Wordpressで運用していた。
今回、Node.js製のStatic Site Generator、wintersmithをみつけ、その手軽さと軽快さからとても気に入ったので使うことにした。

このブログはNode.js Adventar Calendar 2013の12/2の記事です。

wintersmithのインストールとひな形作成

Node.jsでGithub PagesにMarkdownで書くブログを構築したい。そんな僕の気持ちに応えてくれるwintersmithに出会ったぁ(ウルルン調)。
readmeに書かれている通り、npmでさくっとインストールします。
その後、wintersmith new <name>でサンプル記事とともに一式が出来上がるのでwintersmith preview でlocalhost:8080でプレビューできます。

1
2
3
4
$ npm install -g wintersmith
$ wintersmith new anaguma-blog
$ cd anaguma-blog
$ wintersmith preview

wintersmith new はオプションをもっていて、-T でテンプレートを変更することもできます。

1
2
3
4
5
6
7
8
9
10
11
$ wintersmith  new --help
usage: wintersmith new [options] <path>

creates a skeleton site in <path>

options:

-f, --force overwrite existing files
-T, --template <name> template to create new site from (defaults to 'blog')

available templates are: basic, blog, webapp

デフォルトは-T blog と同じなので今回はオプションを指定していません。ブラウザで確認するとこんな感じ。

preview

ブログの設定

wintersmithにブログ全体の設定をします。/config.json を開いて、変更していきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"locals": {
"url": "http://localhost:8080",
"name": "Anaguma blog",
"owner": "shoota",
"description": "return new Bug();"
},
"plugins": [
"./plugins/paginator.coffee"
],
"require": {
"moment": "moment",
"_": "underscore",
"typogr": "typogr"
},
"jade": {
"pretty": false
},
"markdown": {
"smartLists": true,
"smartypants": true
},
"paginator": {
"perPage": 3
}
}

ブログコンテンツとして設定するのは基本的にlocalsのname、owner、descriptionです。nameはブログのトップなどに出てくるブログ名、ownerは著者、descriptionはブログ全体の説明です。
pluginsにはwintersmithのプラグインを設定でき、HTML出力の時に利用できます。
sassのプラグインstylusのプラグインなどがあるようです。
paginator.coffeeはデフォルトで付随するプラグインで、ブログのページャを作成してくれます。
jade.prettyはjadeのコンパイルオプションで、trueにするとHTMLが圧縮されずに出力されます。デフォルトではtrueです。

記事を書いてHTMLに出力

/contents/articlesの下に、記事ごとにディレクトリを作成し、index.mdを配置します。
markdown記法がどういう表示になるかは、ひな形を作成したときのサンプル記事を参考にするとよいと思います。気に入らない場合はjadeやCSSを変更します。
書き終えたらwintersmith build でビルドを実行すると、/build の下にHTMLなどが出力されます。wintersmith build --cleanのオプションでcontentsディレクトリ以下にないものが削除されるので、記事を消したり、ディレクトリ名を変更した場合に使えます。

ちなみに、previewは出力したHTMLではなく、サーバがリクエストごとにレスポンスを生成しているので、記事を変更してリロードすれば反映されます。便利ですね。

githubリポジトリに登録

まずはgithubリモートリポジトリを作成し、masterブランチにpushしてあげます。

1
2
3
4
5
$ git init
$ git remote --add <repo_url>
## .gitignoreを設定したりする
$ git add .
$ git commit -m "myblog"

github pagesに公開する

buildの中身をgh-pagesブランチにpushすれば晴れてブログ構築完了なのですが、masterブランチをpushしたら自動でgh-pagesにデプロイされるのがスマートですよね。
そこで僕の場合はwerckerというサービスを利用して、ビルドとデプロイを自動化しました。
werckerはgithubと連携していて、githubアカウントでログインするとリポジトリの参照などがスムーズになります。

githubトークン発行

デプロイにはwerckerからリモートリポジトリへpushを行うために、OAuthが必要なので、github側でトークンの設定を行います。

gittoken

この値は、後でwerckerのデプロイタスクの環境変数として使用します。

werckerをセットアップ

masterブランチのpushをトリガーとしてwerckerのタスクを起動させるには、アプリケーションを登録し、wercker.ymlをリポジトリにpushします。
werckerにログインしてADD APPLICATIONを押すと5ステップで設定が完了します。
細かい説明は不要かと思いますが、ステップ3のコラボレータ設定ではwerckerbotをコラボレートとしてあげる必要があります。

gittoken

アプリケーションのデプロイ設定

次はデプロイの登録を行います。werckerはビルドやデプロイの各ステップをDirectoryという単位で保存することができます。
wintersmithのコンテンツをgh-pagesへpushするステップは、すでに作成している方がいるので、このステップをそのまま使用します。

登録したアプリケーションのsettingsタブを開き、「Add deploy target」から「Custom deploy」を選択し、画像の通り設定します。

customdeploy

このデプロイステップでは、環境変数 GIT_TOKEN に先ほどのトークンを設定することで、gh-pagesへのpush権限を与えています。
デプロイターゲット内の「Add new variable」で環境変数を設定してあげます。このとき、Protectedにチェックを入れておくとログに値が出力されないので安全です。

git_token

wercker.yml

最後に、wercker.ymlをpushして完成です。

1
2
3
4
5
6
7
8
9
10
11
12
13
box: wercker/nodejs
build:
steps:
- npm-install
- script:
name: wintersmith build
code: ./node_modules/.bin/wintersmith build -v -o ./build
deploy:
steps:
- lukevivier/gh-pages:
token: $GIT_TOKEN
domain: blog.anaguma.org
basedir: build

masterへのpushごとにcloneしてビルドします。cloneは自動で行われるので、その後npm-installステップでモジュールをインストールします。npm-installはグローバルインストールではないので、
wintersmithのCLIは階層を指定して実行します。-o オプションで出力ディレクトリ名を指定していて、これはdeployのbasedirと一致していなくてはいけません。

deployステップのdomain は設定しておくと自動的にgh-pagesブランチにCNAMEファイルを作成してくれますので、サブドメインでブログを運用する場合は指定しておきます。

まとめ

以上を盛り込んだこのブログのリポジトリがこちらです。
実際に動かしてみるとわかりますが、Node.jsっぽい軽快さであるのと、導入がすごく簡単なので、ぜひ一度ためして使ってみてほしいと思います。
wintersmithはもともとはblacksmithにインスパイアされて作られたらしく、興味があるかたはこちらもチェックしてみてください。

wintersmithのプラグインのひな形も公開されているので、これをもとに何か作ってみる、というのもいいかもしれません。
Nodeで動くモジュールは当然使えるので、gravatarやtwitterと連携させたブログ生成も可能です。夢が広がりますね。

Node.jsのブログシステムにはghostCalipsoなんかがありますが、導入コストでいうとやはりstatic site generatorに分があると思っています。
僕のように飽きっぽい人はいろいろ使ってみるのが結構楽しかったりするので、本格的にブログシステムを構築する前に遊び気分で触ってみてもらえたらいいな、と思います。

このブログはNode.js Adventar Calendar 2013の12/2の記事です。



Webプログラミング初心者への教え方

最近、いろいろな人に技術的な質問を受ける機会が多くなってきて、今年の夏は通常業務の合間に新人研修の技術教育担当もしました。
そもそも人になにか教えられるほどの技術力なのか、と自問せざるを得ないというのが正直な気持ちです。
それでも沢山の人に質問を受けたりしているうちに、なんとなく教えるべきことというのが見えてきた気がするので、まとめてみました。

そのまえに -前提としていること-

あくまでも僕の経験と考えに基づくものなので、「こうでなくてはならない」、ではありません。つまりタイトルはやや釣り気味です。
「初心者」と一言にいっても、プログラマの初心者の定義はブレが大きすぎるので、最初に対象を絞っておかないといけないかな、と思います。
そういう意味では自分も初心者であるし、それでも研修教育をなんとかできたわけです。

Webプログラミング

サーバ上で動作するアプリケーションのプログラミングをすることを想定しています。僕はオブジェクト指向言語しか扱ったことがないので、それも前提としています。

####目指すレベル

初心者の定義はいつも曖昧になりやすいのですが、研修を担当していたときに、目指すレベルごとにステップを設計してあげるといいのではないかと感じました。
CUIを使ったことのない人が、「入力に対する応答のロジックを実装することができる」くらいが最初のステップなんじゃないかと思います。これにはフレームワークだとかデータベース接続だとか、
アプリケーションの基盤に対する理解は含まれていません。他の開発者とともに、ひとつの画面や動作などの機能要求を満たすことができる能力レベルというのがいちばん近いと思います。

本題 -教えるべきこと(知っているか確認すること)-

  1. 環境変数
  2. 標準入出力
  3. エラーの読み方

この3つだけです。

とはいっても、実際には言語仕様、設計方法、開発環境のつくりかた、ミドルウェアの役割、バージョン管理システムの使い方、
チケットの書き方、コーディング規約、テストの仕方などなど、たくさんのことを教えると思います。
でも初心者にとって、これらの大部分は手を動かしながら身に着けるもので、(文字通り)手を休めて考えるものではないように感じられました。
またそういうものは、ある種の文化に大いに依存したものであるというか、結局は Case By Caseにしかなりえなくて、「教えた」というより「慣れてもらった」という表現のほうがしっくりします。

理由 -なぜ教えるのか、はずせないのか-

####環境変数

これはWebプログラミングに限った話ではありませんが、アプリケーションがOS上で動く以上、プログラマはOSの基礎知識をある程度持っているものと思います。
でもそれは初心者には負荷が高すぎます。GUIでしかコンピュータに触れたことがないのに、その裏でOSがどのような演算をているかなど、考える必要性がないからです。

それならばOSと言語のエンジンを取り持っている環境変数を目で見て触って、そこからOSとの関係を後々導いていけばいいんじゃないかと思いました。
SDKにPATHを通すときに、それがどういう意味なのかを教えてあげると、プログラム処理がCPUの演算まで届く道筋がなんとなーくイメージできているかと。

####標準入出力

Webアプリケーションの入出力のほとんどはHTMLとHTTPを介して行われる一方で、アプリケーションは標準入力で起動し、標準出力やエラー出力にさまざまな情報を出力します。
Webサイトの利用者としては入出力はブラウザ上で完結しているので、標準入出力というものがなんなのかぼんやりとなってしまう。ログとか、コンソールとかは目で見えているのになにか遠い存在のように
思えるみたいです。「標準」という言葉もなんか曖昧でよくない気がします。こわくないよ、プログラムがユーザには聞こえない場所でお話してくれてるんだよ、と教えました。

####エラーの読み方

僕が研修期間中に新人に口が酸っぱくて無くなるくらい言ったことは、「エラーから学んでください」でした。エラーは自分が書いたコードとの唯一の会話手段だと思います。
僕の何がいけなかったのか、プログラムはなぜ困っているのかを明確にズバリと言ってくれるのですが、初めて見ると字面がヤバい感じでパニクってしまうようです。

なので僕の研修では最初に全員にエラーを出させて、コードの変更と内容を対比することから始めました。エラーが読めるか読めないかで大きく成長度が違うはずだからです。
「エラーを読み、コードを直す」のがプログラマの作業の大半だと思いますし、それを突き詰めたものがTDDでしょう。そういった意味でも、エラーの読み方は「慣れるもの」ではなく「教わるべきもの」だと思います。
もしひとつもエラーがでなかったときに、「大丈夫だよ」とエラー出力の心の声が聞こえたら、それが初心者卒業証書です。

一般論とか、僕の考え方

世間にはお金を払って教えてくれる研修サービスがたくさんありますし、僕もそのいくつかに参加したことがあります(会社のお金で)。でもそのとき感じたのは、これらは結局、体系的な知識の詰め合わせコース以上になりえていないことでした。
プログラミングを始めたばかりのひとに、DRY原則やデザインパターンを語るのは悪いことではないと思いますが、さてコードを書くぞ、というときにそれを実体化できる人はいないと思います。
それよりも自分が書いたコードが動かないときの楽しさ、動いたときのうれしさを教えてあげたい。また、上の3点は新しい言語を学ぶときの自分のとっかかりであったりもします。

最近の僕は会社でコードを書く機会がめっきり減ってきていて、それでも困ったときに聞いてくれたりはするんですが、「昼は会社員、夜はプログラマ」みたいな生活になりつつあります。
もっとコーディングがしたい!という思いと、自分よりもすごい人材を育てたいという思いがあって、今回の記事を書きました。

そして、このブログを書き終えたいま、またプログラミングを楽しみたいと思います。



Node-express / EJS ViewでJSON出力

JSONを返すAPIをサーバからたたいて結果をまるっとEJSに埋め込みたかった。地味にはまったのでメモ。

router

1
2
3
4
5
app.get('/hoge', function(req, res){
//データとってくるとかAPI叩くとかしてJSONができる
var json= foo.getJSON();
res.render('hoge.ejs', {data:json});
});

ejs

1
2
3
4
5
<!-- HTML escapeされてしまってだめだった -->
<%= JSON.stringify(data) %>

<!-- %=じゃなくて%-を使うといい -->
<%- JSON.stringify(data) %>

EJSに渡しているjsonはJSONオブジェクトなので、EJS側で stringify して文字列にする。(別にRouter側でやってもいいが)
<%= str %> だと中の文字列に対してオートでHTMLエスケープされちゃうので、&quoat;がいっぱいでてくる。 <%- str %> で書いてあげればOK。



JSON文字列とオブジェクトの相互変換メモ

Node.jsの標準メソッドでJSON文字列とオブジェクトの相互変換するときの注意メモ

Node.jsを使ったアプリを作成するときに、APIの返却値としてデータオブジェクトを文字列に変換してレスポンスしたい場合や、外部から取得した文字列をパースしてオブジェクトとしてデータストアにそのまま保存したい場合など、文字列とオブジェクトの相互変換はいろいろな場面で役に立つ。特に、非同期なI/Oを利用した効率的なStream処理は、Node.jsの最大の売りといってもよく、Stream内外で文字列とオブジェクトの相互変換は頻繁に行われる。
そういった処理は、暗黙的に呼び出すことのできるJSON(フォーマットのことではなく、標準実装のオブジェクト名)が持っている変換メソッド、 JSON.stringifyJSON.parse を使うのが一般的かと思うが、その挙動についてちょっと勘違いをしていたのでメモしておく。

テスト用コードを書いてみる

結果をみて考えると当たり前なのだが、関数オブジェクトは文字列に変換されない。
そもそも、JSONはデータコンテナフォーマットであり、ピュアなオブジェクト(とそのネスト)で構成されているので、関数オブジェクトなどのデータ以外のものは格納できない(する意味が無い)。
一方でMongoDBはデータストアでありながら内部的なインタプリタで関数オブジェクトを実行したり、コレクション内に関数オブジェクトを持つ状態で保存したりすることできるので、この辺が覚え違いの原因だった。
「BSONがJSONの拡張であり、JSONに似て非なるデータ形式」ということを肝に銘じておかなくてはならない。

また、 JSON.parseの引数文字列は正しいフォーマットのみを受け付けるようだ。
ブレース括弧を省略する、Keyをダブルクウォートで括らない、などは許されない。こちらは当たり前といえば当たり前なのだが、文字列リテラルを書いた文字列となることに注意する。

40行目以降は相互変換の可逆性をテストしている。
console出力したときに関数オブジェクトが文字列に変換されていないだけで実態はまだ参照されているのか、本当に関数オブジェクトの参照がなくなっているかを試すためだ。
結果、もともとあった関数オブジェクトが無いというエラーが発生したので、文字列化したときに関数オブジェクトは完全に紛失するということがわかる。「データコンテナとしてのJSON」を貫いているということだろう。

まとめ

JSON.strigify()とJSON.parse()で相互変換、可逆変換は可能。
ただし、それぞれの引数と戻り値はJSONの定義に則った形式で記述しなくてはならない。