そんな今日この頃でして、、、

コード書いたり映画みたり。努力は苦手だから「楽しいこと」を探していきたい。

goemonでMojolicious::Liteでの開発速度を加速してみた

先日↓こんなツールがリリースされてたので試してみたら、なかなか快適だった。

Big Sky :: 開発速度を加速するツール、goemon を書いた。

大雑把に説明すると、ファイルの変更を読み取ってリロードやらminifyやらを自動でこなしてくれる補助ツール

ファイル編集からの定形処理を自動化することで、コードを書くことに専念することができる。


昨今じゃ色々とビルドツール的なものが出てきてて、例えば前に使ってみたYeomanなんかを使えば、テンプレート作成からテストまでカバーできる。

Yeomanのチュートリアルの個人的なまとめ - そんな今日この頃でして、、、

確かに便利は便利なんだが、結構覚えなきゃいけないことは多いし、テンプレートとしてあるものを使う分には良いのだけれど、いざ自分好みの環境に適用しようと思うとちょっと面倒くさい・・・

そこまでフロント開発に比重を置いてるわけではないし物覚えも悪い身からすると正直しんどい。


その点goemonはシンプルで学習コストが低く、それでいて柔軟性があるので、今ある環境に気軽に導入できるのが良い。

今回は個人的に愛用してるPerlフレームワークMojolicious::Liteで使ってみた。

続きを読む

Yeomanのチュートリアルの個人的なまとめ

前年末の予告通りYeomanのチュートリアルをやってみたので、そのまとめ。

Let's Scaffold a Web App with Yeoman | Yeoman

公式ページのチュートリアル手順は結構親切に書いてはあるが、何点か引っかかるところがあったのでその辺も。

ちなみに今回やってみた際の各バージョン↓

version
yo 1.3.3
grunt-cli 0.1.13
bower 1.3.12


Yeomanとは?

大雑把にいえば、今時っぽいWebアプリ開発の基盤をよしなにしてくれるツールである。

The web's scaffolding tool for modern webapps | Yeoman

アプリのひな形を作ってくれるYo、ライブラリ管理をするBower、テストだのビルドだのを自動化してくれるGruntという3つのツールを統合したもの。


Yeoman導入

何はともあれ導入。

チュートリアル的にはStep1(Yeoman - Modern workflows for modern webapps)にあたる。

$ npm install -g yo bower grunt-cli


ジェネレータの導入

作りたいアプリに合うひな形のジェネレータを導入する。

チュートリアル的にはStep2(Yeoman - Modern workflows for modern webapps)の手順。

今回はAngularJSを用いるので↓

$ npm install -g generator-angular@0.9.2


ちなみに他のジェネレータなんかは

$ npm search yeoman-generator

で探せる。


ひな形の作成

Step3(Yeoman - Modern workflows for modern webapps)あたり。

とりあえず作業用のディレクトリを用意し、

$ mkdir mytodo && cd mytodo


yoコマンドでひな形を作成。

$ yo
? 'Allo *****! What would you like to do? Angular

Make sure you are in the directory you want to scaffold into.
This generator can also be run with: yo angular


     _-----_
    |       |    .--------------------------.
    |--(o)--|    |    Welcome to Yeoman,    |
   `---------´   |   ladies and gentlemen!  |
    ( _´U`_ )    '--------------------------'
    /___A___\
     |  ~  |
   __'.___.'__
 ´   `  |° ´ Y `

Out of the box I include Bootstrap and some AngularJS recommended modules.

? Would you like to use Sass (with Compass)? No
? Would you like to include Bootstrap? Yes
? Which modules would you like to include? angular-animate.js, angular-cookies.js, angular-resource.js, angular-route.js, angular-sanitize.js, angular-touch.js

現バージョンでは公式と表示がいくらか異なるが、そこはニュアンスで進めていけば良い。


正常に終了すると、作業ディレクトリに諸々のファイルが作成される。

Step4(Yeoman - Modern workflows for modern webapps)。

この中の主にappディレクトリ以下のファイルを編集して開発を進めていく感じになる。


Gruntを用いてひな形を確認する

Step5(Yeoman - Modern workflows for modern webapps)。

grunt serveコマンドによりnodeベースのhttpサーバが立ち上がるらしい。

ということで意気揚々と打ってみると・・・

$ grunt serve
Running "serve" task

Running "clean:server" (clean) task

Running "wiredep:app" (wiredep) task
Warning: ENOENT, no such file or directory '/Users/*****/mytodo/app/bower.json' Use --force to continue.

Aborted due to warnings.


Execution Time (2015-01-02 08:20:01 UTC)
loading tasks    4ms  ▇▇ 2%
clean:server     5ms  ▇▇ 2%
wiredep:app    201ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 94%
Total 213ms

あれ、bower.jsonのパスが違うとかで怒られる・・・

どうも作業ディレクトリ直下にあるはずのbower.jsonapp以下に見に行こうとしてしまってる模様。

ググった所grunt-wiredepというライブラリのバージョンに依存する問題らしい。

参考↓

YEOMANのチュートリアルでいきなりハマった人向け | mah365

作業フィルダ直下のGruntの動作に関わるGruntfile.jsのwiredep・options項のcmdをコメントアウトすればOK

    // Automatically inject Bower components into the app
    wiredep: {
      options: {
        //cwd: '<%= yeoman.app %>'
      },
      app: {
        src: ['<%= yeoman.app %>/index.html'],
        ignorePath:  /\.\.\//
      }
    },


意気揚々と再実行。

$ grunt serve
Running "serve" task

Running "clean:server" (clean) task

Running "wiredep:app" (wiredep) task
app/index.html modified.

Running "concurrent:server" (concurrent) task

    Running "copy:styles" (copy) task
    Copied 1 files

    Done, without errors.


    Execution Time (2015-01-02 08:25:09 UTC)
    loading tasks   4ms  ▇▇▇▇▇▇▇▇▇▇ 21%
    copy:styles    15ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 79%
    Total 19ms

Running "autoprefixer:dist" (autoprefixer) task
File .tmp/styles/main.css created.

Running "connect:livereload" (connect) task
Started connect web server on http://localhost:9000

Running "watch" task
Waiting...

ちゃんと動いた。

ブラウザからhttp://localhost:9000にアクセスすることでページを確認できる。


ちなみに./app/view/main.htmlが正に表示されてるページの見た目部分となるわけだが、サーバを立ち上げたままこのファイルを編集するとライブリロードされる(保存されたタイミングでブラウザ側も勝手に追従してくれる)。

地味に便利。


コーディング

Step6(Yeoman - Modern workflows for modern webapps)。

./app/views/main.htmlおよび./app/scripts/controllers/main.jsを編集してアプリ作成していく。

この辺はチュートリアルページをまんま写経すればOK。

予めAngularJSの基礎的な使い方を知ってると理解しやすいだろう。

(こういうチュートリアルで出てくるようになったあたりもはや業界標準感あるし、去年のうちにある程度使えるようになっておいてよかった。)

AngularJSリファレンス

AngularJSリファレンス


Bowerを用いてライブラリを追加する

Step7(Yeoman - Modern workflows for modern webapps)。

作業ディレクトリでbower listで現状を確認。

ひな形作成時に選択されたのが入っているはず。

$ bower list
bower check-new     Checking for new versions of the project dependencies..
mytodo#0.0.0 /Users/*****/mytodo
├── angular#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
├─┬ angular-animate#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
│ └── angular#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
├─┬ angular-cookies#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
│ └── angular#1.2.16
├─┬ angular-mocks#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
│ └── angular#1.2.16
├─┬ angular-resource#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
│ └── angular#1.2.16
├─┬ angular-route#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
│ └── angular#1.2.16
├─┬ angular-sanitize#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
│ └── angular#1.2.16
├─┬ angular-scenario#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
│ └── angular#1.2.16
├─┬ angular-touch#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
│ └── angular#1.2.16
├─┬ bootstrap#3.1.1 (latest is 3.3.1)
│ └── jquery#2.1.3
├── es5-shim#3.1.1 (latest is 4.0.5)
└── json3#3.3.2


追加したいライブラリをインストール。

$ bower install --save angular-ui-sortable jquery-ui


再度確認。

$ bower list
bower check-new     Checking for new versions of the project dependencies..
mytodo#0.0.0 /Users/*****/mytodo
├── angular#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
├─┬ angular-animate#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
│ └── angular#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
├─┬ angular-cookies#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
│ └── angular#1.2.16
├─┬ angular-mocks#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
│ └── angular#1.2.16
├─┬ angular-resource#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
│ └── angular#1.2.16
├─┬ angular-route#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
│ └── angular#1.2.16
├─┬ angular-sanitize#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
│ └── angular#1.2.16
├─┬ angular-scenario#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
│ └── angular#1.2.16
├─┬ angular-touch#1.2.16 (latest is 1.3.9-build.3744+sha.66ceecc)
│ └── angular#1.2.16
├─┬ angular-ui-sortable#0.13.1 extraneous
│ ├── angular#1.2.16 (1.3.9-build.3744+sha.66ceecc available)
│ └─┬ jquery-ui#1.11.2
│   └── jquery#2.1.3
├─┬ bootstrap#3.1.1 (latest is 3.3.1)
│ └── jquery#2.1.3
├── es5-shim#3.1.1 (latest is 4.0.5)
└── json3#3.3.2


実際のファイルなんかは./bower_componentsに追加される。

$ ls -1 bower_components/
angular
angular-animate
angular-cookies
angular-mocks
angular-resource
angular-route
angular-sanitize
angular-scenario
angular-touch
angular-ui-sortable
bootstrap
es5-shim
jquery
jquery-ui
json3


また、bower.jsonにも記述される。


一旦立ち上げてたhttpdサーバをCtrl+Cで終了し、grunt sarveで再度起動すると.app/index.htmlにも追記されることが確認できる。


追加されたライブラリをアプリで使用するために、./app/script/app.jsに追加を行う。

※ 最後に述べるが、本来はこの時config項の修正をするのが望ましいのかもしれない。

angular
  .module('mytodoApp', [
    'ngAnimate',
    'ngCookies',
    'ngResource',
    'ngRoute',
    'ngSanitize',
    'ngTouch',
    'ui.sortable'
  ])

またhtmlにも対応する記述を追加する。


テスト

Step8(Yeoman - Modern workflows for modern webapps)。

./test/karma.conf.jsに新たに追加されたライブラリの記述を加える。

また、実際のコードに合わせて./test/spec/controllers/main.jsのテスト要項を修正する。

あとはgrunt testコマンドでテストが走る。


余談だけど、このテストにはPhantomJSが使われている。

全く別件で勉強してた技術が、そうと意識せずに繋がる感じは面白い。

CasperJSで動的ページをスクレイピング、あるいは毎月100円ゲットする方法について - そんな今日この頃でして、、、


本番用のファイルを出力する

Step9(Yeoman - Modern workflows for modern webapps)。

実際のサーバに上げるためのファイルを出力する。

gruntコマンドで最適化その他諸々を行った後、grunt serve:distコマンドで./distディレクトリ内に出力&サーバ起動で動作確認できる。

問題なければ./distディレクトリ以下をもっていけばOK。


LocalStorage対応

Step10(Yeoman - Modern workflows for modern webapps)。

チュートリアルでは更にその後にブラウザに保存するLocalStorageの対応する手順が載っている。

が、ここがちょっと記載足りなかったりで引っかかりどころあった。


基本的には公式ページに従い追記していき、さらにテストに対応するために./test/karma.conf.js'bower_components/angular-local-storage/dist/angular-local-storage.js'を記述を追加する。

これでテストは通るようになる。


ここで、僕の環境だとgrunt serveで立ち上げてた時は問題ないのに、grunt serve:sidtで作成した方だと[$injector:modulerr]なるエラーが発生する。

ドンピシャな記事は見当たらなかったのでちょっと正確なところは分からないけど、雰囲気的にはminifyした後のコードではLocalStorageモジュールが加わったことによりngRouteモジュールが正しく読めてない感じなのだろうか?

参考↓

$routeProviderがない – AngularJS 1.2.0-rc にアップデートする際の注意 | ygoto3's caught up

モジュールの読み込みを行ってる./app/script/app.jsのconfigでrouteProviderを明示的にしてやることでとりあえず解消できた。

  .config(['$routeProvider', function ($routeProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'views/main.html',
        controller: 'MainCtrl'
      })
      .when('/about', {
        templateUrl: 'views/about.html',
        controller: 'AboutCtrl'
      })
      .otherwise({
        redirectTo: '/'
      });
  }]);



何かと面倒なテストのひな形まで作ってくれるのは実にありがたい。

僕はAltJSには手を出せてないが、そのへん使ってる人ならより便利に感じるんだろうなって気がする。

実用的には自分の使ってる環境に合わせてジェネレータを作成すると良いんだろうな。


ただ、僕はフロントは門外漢なせいか、こうやって作成したコードってどうバーション管理&デプロイしてくのがベターなのかイマイチわからなかった。



そういえば、我慢できずにマルチコプター買いました!

1万前後の価格帯では大きめで安定性のある↓を選択。

届いたタイミングが遅かったこともあって今日は外にはもっていけず、かといって室内で飛ばすには大きさ的に厳しくて、今のところ玄関前で浮かせてみる位しか遊べていないけれど、空物ラジコンで動画が撮れるのは超ワクワクする!

非実在小学4年生の謝罪ページのJSを読む

方々で話題の非実在小学4年生が作ったサイト。

どうして解散するんですか?


【顛末追記】小4『どうして解散するの?サイトつくったからおしえて』民主くん『天才少年現る!』→仕込みっぽすぎて炎上 ➡結果NPO運営でした - Togetterまとめ

稚拙な手法であえなく炎上→謝罪と相成ったわけですが、謝罪文がまた上から目線で再炎上してるあたりが様式美ですね。

しかも、それだけじゃまだ燃え足りないのかJavascript本文を消す細工があった模様。


そんなわけで0時を回り、ワクワクしながらサイトを開くと・・・

アレ?ちゃんと表示されてる。

https://twitter.com/kikumaco/status/536172921035583489


噂のJavascriptを読むと・・・

※ちなみにminifyされたJSを読みやすく整形してくれるツールの存在を初めて知った。 何気に便利すな↓

Minifier - compress JavaScript or CSS, deminify JavaScript or CSS, create SQL INSERT commands from CSV

function countdown() {
    var a = new Date(); //現在の(端末の)時刻を取得
    var b = a.getTime(); //比較用に数値に
    var c = new Date(2014, 10, 23, 0, 0, 0); //2014年11月23日 00時00分00秒を取得(月は0始まりのカウントなので10で正しい)
    var d = c.getTime(); //比較用に↑のを数値化
    if (d < b && document.getElementById('report_countdown')) { 
        //23日以後かつカウンタ表示あれば
        var e = document.getElementById('report_countdown'); 
        e.parentNode.removeChild(e);//カウントダウンの要素を削除
        clearInterval(interval)
    } else {
        //23日以前なら…
        var f = d - b;
        var g = 30000000 * f / 10800000;
        generateCounter2(Math.floor(g))//カウンタを表示
    }
}

あれ?別に本文削除する仕組みもないぞ???


ガセだったのかなーと心配しつつ魚拓を確認。

http://why-kaisan.com/ - 2014年11月22日 21:43 - ウェブ魚拓

お。ちゃんと消えてる。

魚拓の方のJSをみると・・・

function countdown() {
    var a = new Date();
    var b = a.getTime();
    var c = new Date(2014, 10, 23, 0, 0, 0);
    var d = c.getTime();
    if (d < b) {
           //23日以後なら…
        document.getElementById("wrapper").innerHTML = "" //謝罪文が含まれる"wrapper"要素を削除
    } else {
        var e = d - b;
        var f = 30000000 * e / 10800000;
        console.log(e);
        generateCounter2(Math.floor(f))
    }
}

あ、ちゃんとある!!


わざわざJavascriptで仕込んだのは、きっと魚拓対策だったんだろうなと推測。

昨今では炎上するとサーバ側で本文消しても魚拓としていつまでも残っちゃう難儀な時代だし、政治家を志向する身として後々に悪材料になりそうなものを残さないための悪あがきだったのだろうけど、日を回る前にバレてしまったせいなのか流石にアカンと思ったのかあわてて小細工を消したのだろう。

まあ、結局証拠残っちゃってるんですけどね。


これに尽きる。



最近『動物農場』読んでたこともあって、ああいう「賢い俺が馬鹿な民衆を先導(扇動)するんだ」的な発想て凄く嫌悪感を催してしまう。

動物農場 (角川文庫)

動物農場 (角川文庫)


ちょっとだけ補足:なんでJavascriptで消すと魚拓対策になるか?

普通の静的なHTMLはもとよりPHPその他サーバサイドの言語で作ったページというのは、あくまでサーバから表示する文言その他を指定したコードを吐き出すだけ。

なので、魚拓のようなあるタイミングで吐き出されたものを保持しておくサービスでは、後でサーバから消そうがそういう仕組みを仕込んでいれてようが意味がない。


一方でJavascriptサーバから受け取ったコードを個々の端末(ブラウザ)が実行するもの。

なので、魚拓とっても消す構造ごと保管されてしまうわけで、個々の端末では保存時のコードに従って動作するので消えて見えるという感じ。

(一応Javascriptを切るとか端末の時間ずらすとかすりゃ見れるけど)

今更ながら補足

さも魚拓対策が真相みたいに拡散しちゃった後でいうのもなんだけど。

”謝罪文”の雰囲気から察するに実際のところは、世間の怒りの本気度を理解できてなくて(あいつらシャレ通じねーな位に思っていたのかも)、「ドッキリ大成功」的なノリで茶目っ気発揮しちゃったのがあのタイマーだったんじゃないかなとも思える。

(元々ここまで炎上しなくても、週明けに「ツイッターで話題」つってある程度メディアに取り上げられた後でネタばらしして名を上げようみたいな公算もあったのかもしれない)

それはそれでイラッとはするけども。


この件に関して感じるところは大体↓の方と同じ。

「どうして解散するんですか?」はどうしてわたしたちを失望させたんですか?(若者22歳)追記の更に追記 - Action Bit

「釣られて怒ってるんじゃねーよ」みたいな擁護意見も見かけるけど、問題はそういうことじゃなくて(というか大概の方が釣られなかったからこうなってるわけで)、やり方があまりに卑劣な点だと思うんだよね。

「実在する小学4年生」を装うってのはなりきりキャラの仮面をつけるのとは訳が違う。

そして「謝罪したから良いじゃん」というのもあるけど、少なくともあの文面から謝罪の意識も誠実さも見えない。

この件に関してはどうしようもなく嫌悪感を抱く。

STAP細胞を作って学ぶAngularJSのデータバインディング

↑に触発されて作ってみた。


f:id:blue1st:20140604010450p:plain

STAP NO.1

中身は単純明快、ひたすら文字列を生成するガチャ。

そんなわけで、ソース見つつ読んでもらえればと。

準備

AngularJS読み込む

当たり前だけど、何は無くともとりあえず読み込んでおく。

<head>
  <meta charset="UTF-8">
  <title>STAP NO.1</title>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
  <script>

要素をAngularJSと関連付ける

タグにng-appと記述することで、AngularJSのアプリであることを示す。 また、ng-controllerで、後述するアプリケーションと表示とを関連付ける。

<body>
  <div ng-app ng-controller="Ctrl">

アプリケーションの記述

とりあえず作る

AngularJSを読み込んだ後にfunctionを作る。 とりあえず今は何も考えずに$scopeを渡しておこう。

  <script>
    function Ctrl($scope){
      …
    }
  </script>

データと表示を紐付ける

本題。

$scopeに入れる

扱いたいデータは$scope内に格納。

$scope.cells = ['STEP', 'STAP', 'STOP'];
テンプレート側

HTML側でデータを表示する部分は{{[変数名]}}の形で記述しよう。

 {{cells.length}}回 実験しました!

↑なんかは$scope.cells.lengthに対応していて、配列が増えると勝手に表示も更新される。

繰り返し要素

同じ形式のタグが繰り返される場合は、ng-repeatが利用できる。

<button ng-repeat="loop in [1, 10, 100, 1000, 10000]">{{loop}}回 実験する</button>

↑のコードだと、1・10・100・1000・10000のそれぞれのボタンが作成できる。

今回は単純にbuttonタグだけを繰り返しているが、内部にもっと複雑な要素が入ったdivタグなんかも繰り返すことができる。

それぞれの回数はloopという変数に格納されてタグ内でアクセスできる。


ちなみに$scopeの中の配列や連想配列についても同様に使うことができる。

 <span ng-repeat="cell in cells track by $index">{{cell}}細胞</span>

しかし、配列の場合は全くおなじ要素が含まれるとngRepeat:dupesというエラーがでてしまうため、ループを内容ではなくインデックスで管理するようにtrack by $indexをつけてやる。

要素によってclassを変える

例えば今回cellがSTAPだった場合だけbingoというクラスにしたいので、ng-class="{[クラス名]:'条件'}"を使って下記の様に記述する。

 <span ng-class="{bingo:cell == 'STAP'}" ng-repeat="cell in cells track by $index">{{cell}}細胞</span>
クリックによるアクションの記述

ng-click要素に記述することで、function内に指定した動作をさせることができる。

 <button ng-repeat="loop in [1, 10, 100, 1000, 10000]" ng-click="generate(loop)">{{loop}}回 実験する</button>
      $scope.generate = function(loop){
        …諸々の動作…
      }

まとめ

この程度だったら別にAngularJS使うまでもないというか、むしろ毎度ng-repeatでcells展開するせいでcellsが増えると動作が厳しくなってしまってる面はあるのだけど、とにかくデータバインディングによって凄く簡潔にアプリケーションを作成できることは分かったと思う。

jQueryが「HTMLに機能を付加してく」イメージなのに対して、AngularJS使ってると「動作に合わせてHTMLを組んでく」ような印象になるのが興味深い。

AngularJSアプリケーション開発ガイド

AngularJSアプリケーション開発ガイド

AngularJSの$http.post()のメモ

今年に入ってから「流行りものに手を出してみよう」ということでAngularJSをよく使うようになったのだが、最近ではもはや理由がない限りは使うのがデフォって位に僕の中では無くてはならない存在になってきた。

昨今のブラウザ側にある程度データを持たせてガリガリいじるような設計の場合、生のJavascriptなりjQueryなりで作ると内部的なデータと実際の画面表示とで状態を同期するのが一丁面倒だったりする。

そのへんデータバインディングの恩恵に与ることで凄く生産性が上がるのである。


さて本題。

AngularJSでは$http.post()によってPOSTでデータを送信することはできるのだが、これをjQueryの$.post()のつもりで使おうとすると、なぜかサーバ側で上手く取得できなくて面食らうことになる。

  #ブラウザ側
  $http.post('/api', {test:1}); #"test"って要素に"1"って入れて送ってみるじゃん
  #サーバ側(今回はMojolicious::Liteで)
  $self->param('test');#これでは"1"が取れない!!

結論から言えば、AngularJSの$http.post()はformのinput要素のような形で送信するものではなくて、application/x-www-form-urlencodedが付かないからJSONのまんま自動では展開されないのが原因。

jQueryのやつっぽく使いたい場合

無理くりjQueryライクにやりたければ、細かい設定を入れて$http()使えばよい。

$http(
  {
    url:"/api",
    method:"POST",
    data:{test:1}
    headers: {'Content-Type': 'application/x-www-form-urlencoded'}
  }
)

https://docs.angularjs.org/api/ng/service/$http

(そもそもAngularJSとjQuery共存できるので、そこだけjQuery使う方がベターな気がするけど・・・)

Mojolicious::Liteで受け取る

とはいえ最新のフレームワークがわざわざこんな形で送ってるのは当然理由がある。

普通にpostした場合は一次の連想配列でカバーできる形式のデータしか送れないわけで、複雑な構造のデータを扱いたい場合は何かと工夫が必要となるのだが、JSON形式でならばそのあたりの面倒がない。


そんなわけで、ちゃんと$http.post()で送った物を取ってみる。

要はリクエストボディを取得してJSONでデコードしてやりゃ良い。

個人的に専ら良く使っているPerlフレームワークMojolicious::Liteには初めっから便利なものが用意されている。

my $data = $self->req->json;
$data->{test}; #これで目的のものが取れる!

AngularJSアプリケーション開発ガイド

AngularJSアプリケーション開発ガイド


Vita用のソフトは何本か買ってみたのだけど、最近は『地球防衛軍3Portable』ついついばっかりやってしまう。

地球防衛軍 3 PORTABLE

地球防衛軍 3 PORTABLE

それほど敵に動作にバリエーションはないけれど、ボリュームはあるしアンロックされる武器もいっぱいあって、TPSとして楽しい要素をしっかり抑えている。

1ステージだけってつもりでやってたら、気づいたら夜になってたりするから恐ろしい。

今一番(?)おいしいJavascriptフレームワーク「Meteor」触ってみた

ちょっと前こんな記事がブクマを集めていた。

JavaScript プログラマの職種は4種類くらいに分けるべき

プログラムを生業としている者としては、確かに自分の領分が拡大解釈されて仕事を押し付けられることのしんどさは凄くよく分かる。

Node.jsの登場によりサーバサイドまでもJavascriptで記述できるようになったとはいえ、実際的には考慮しなければいけない要素も記述の仕方もサーバとクライアントで全然異なるわけで、「一つの言語で全てを記述できる」とは言ってもなかなか簡単ではなかったりする。

(特にクライアントサイドはフレームワークの概念が何だか混沌としてるし)


とは言え、「単一の言語でサーバからクライアントまで弄れる」ということが大きな魅力であるのも確かなわけで、機会があれば勉強してみたいなーと思ってはいた。


そこに面白い設計思想のJavascriptフレームワークMeteorが登場!

Meteor

このフレームワークでは、サーバ側とクライアント側の処理を同じコードに記述できる!!

しかもNode.js/NoSQL(MongoDB)/データバインディング/WebSocketといったナウい要素を一気に味わえるのがおいしい。


バージョンが1に満たないとかMongoが必須とか、まだちょっと実戦で導入は厳しいかなという気はするけど、プライベートで使ってみる分には面白そうなんで触ってみた。

Meteorをインストール

CentOSに以下のコマンドで一発簡単インストール。

$ curl install.meteor.com|/bin/sh

以下のコマンドでバーション確認ができればインストール成功。

$ meteor --version
Release 0.7.2

meteoriteを導入

ついでにMeteorのバージョンとパッケージ管理を行えるmeteoriteを導入しておく。

$ sudo -H npm install -g meteorite

アプリケーションを作成してみる

meteorite経由でアプリを作成。

$ mtr create hello

アプリ名のディレクトリ以下に下記のファイルが生成される。

$ tree hello/
hello
├── hello.css
├── hello.html
├── hello.js
└── smart.json

0 directories, 4 files

実際にファイルを覗いてみる

hello.css

これはまあ普通のCSSファイル

hello.html

AngularJSみたいに{{}}で要素の挿入。

{{> [テンプレート名]}}でテンプレートタグ内を入れ込む。

<head>
  <title>hello</title>
</head>

<body>
  {{> hello}}
</body>

<template name="hello">
  <h1>はろーわーるど</h1>
  {{greeting}}
  <input type="button" value="Click" />
</template>

hello.js

if (Meteor.isClient) {...}内でクライアント側で実行する処理、if (Meteor.isServer) {...}でサーバ側処理を記述する。

Meteor.startup(function () {...});は開始時の処理。

if (Meteor.isClient) {
  Template.hello.greeting = function () {
    return "こんにちは、世界!";
  };

  Template.hello.events({
    'click input': function () {
      // template data, if any, is available in 'this'
      if (typeof console !== 'undefined')
        console.log("You pressed the button");
    }
  });
}

if (Meteor.isServer) {
  Meteor.startup(function () {
    // code to run on server at startup
  });
}

他にもクライアントからサーバ側のメソッドを呼び出すとかクライアントからのDBの値の操作とか色々面白そうな要素はあるっぽいけど、とりあえず今回はこれだけ。

起動してみる

作成されたディレクトリに移動して起動。

$ cd hello
$ meteor --port [ポート番号]

ローカルの該当ポートにブラウザでアクセスできればOK。

Herokuにデプロイしてみる

herokuで動かしてみる。

ひとまずgitに登録。

$ git init
$ git add .
$ git commit -m 'meteor hello'

ビルドパック付きでHerokuに作成。

ここで表示されるURLをメモっておく。

$ heroku create --stack cedar --buildpack https://github.com/oortcloud/heroku-buildpack-meteorite.git

さっきのURLを環境変数に入れておく。

$ heroku config:add ROOT_URL=http://[...].herokuapp.com/

MongoDBのアドオンやらWebSocketの有効化やらをしておく。

$ heroku addons:add mongohq
$ heroku labs:enable websockets

最後にプッシュして完了。

$ git push heroku master

僕はJavascript界隈には疎いのでまだ全然使いこなしたイメージは持ててないけれど、とりあえず「クライアントとサーバの垣根を取り払う」という発想は非常に面白いなと思った。

(職業柄あまり「全部カバーしました」なプロダクトに良いイメージはないし、正直Meteorみたいなのが主流になっていくという風には思えないけれどね)

余談

Herokuでも話題の新言語Hack使えるようになったらしい。

CentOSなんでローカルへのHHVM導入が面倒らしいのでちょっと手を出すの躊躇ってるんだけど、使ってみたい気もする・・・

最近よく使うJavascriptライブラリ

本業はPerlでサーバサイドのプログラムをカリカリいじる仕事なのだけど、管理画面の作成なんかだとJavascriptをいじる機会も少なくはない。

そんなわけで今回は管理画面作成なんかでよく使うライブラリ紹介。

Underscore.js/Lo-Dash.js

配列・オブジェクト操作の簡単化とテンプレート機能を持ったライブラリ(とその互換高速版)。

_.[method]の形で使用する。

jQueryと組み合わせて使うと良い感じ。

配列操作

Perlをメインで使い、たまにJavascriptを使っていると、常々配列まわりで何かしらの操作をするのが非常にかったるく感じることが多い。


例えばオブジェクトの格納された配列を、特定のキーを持ったオブジェクトに変換しようとした場合の話。

Perlなら

my $array = [ { a => 1,  b => 'hoge'}, { a => 2, b => 'moge'} ];
my $hash = { map { $_->{a} => $_ } @$array };

↓とでもすれば一行でサクッと変換できる。

{1 => { a => 1, b => 'hoge'}, 2 => { a => 2, b => 'moge' }};

だが、Javascriptでこれをやろうと思うとArray.forEach文をゴリゴリ書いてやらなければならない。

この程度の例ならまだ良いのだが、ここに更にgrepやらsortやらを絡めるような話になってくると俄然面倒くさくなってくる。


そこでUnderscore.js(Lo-dash.js)が登場。

面倒くささを幾らかは緩和してくれる。

例えば先ほどの配列→オブジェクト変換でいえば

var array = [{ a:1, b:"hoge" }, { a:2, b:"moge" }];
var hash = _.indexBy(array, 'a');

↓という形で目的の達することができる。

{ 1:{ a:1, b:"hoge"}, 2: { a:2, b:"moge" }}

他にも特定の条件のオブジェクトだけ取り出せるfindとか内容に特定の操作をした結果を返すmapとか、色々と気の利いたメソッドが揃っている。



旧来的なWebシステムのセオリーでいえば「コストのかかるデータ操作はサーバ側でこなしてクライアント側は表示に徹するべし」ではあるのだけど、インタラクティブな表現が求められる昨今ではそれにも限界があるわけで、凄く助かるのである。

テンプレート

インタラクティブな表示」といえば、ユーザ操作に従ってHTMLを生成して挿入する機会は多い。

しかしJavascriptで文字列をどんどん結合して長文を生成していくのは非常にコストがかかるし、何より可読性も悪くなる。

そんな時助かるのがtemplateメソッド。

var compiled = _.template(
  '<p><%=title%></p><ul><%_.each(list, function(str){%><li><%=str%><%})%></ul>'
  );
$(''#target").html(
  compiled({title:"タイトル", list:[data:"あああ"], [data:"いいい"]})
  );

みたいな感じの記述で#targetに↓のようなHTMLが挿入できる。

タイトル

  • あああ
  • いいい

公式ドキュメントがめちゃめちゃ読みやすいのも好感度高い。

Underscore.js

Sammy.js

手の込んだページのJavascriptがスパゲティ化する原因としては、場当たり的にイベントハンドラを追記していった結果「ある状態の時どういう挙動をするか」が読み取りにくくなってしまうというのが一因としてある思う。


そこでSammy.js。

URLのコメント項(http://hoge.com/hoge#/[ココ!])で処理を分けることができる。

var app = $.sammy(function(){
  this.get('#/action1', {
    #/action1にアクセスした時の処理を記述
    
  });

  this.get('#/action2', {
    #/action2にアクセスした時の処理を記述
    
  });
  
};

app.run();

例えば$('#btn').click(function(){〜})みたいなイベントをキャッチしてその中で処理を記述する形だったものを、ボタンをページ内リンクに変更してそのURL毎の処理を記述するように変えることによって、状態と処理との対応が読み取りやすい形に書き換えることができる。

また、動作によりURLが書き換わる構造なためブラウザバックが使用できるし、「ある状態」を指定した形でリンクを貼れるので運用的にも便利な側面がある。


こっちは簡素なページだけど、そんなに覚えなきゃいけない要素はないから問題ない。

Sammy.js / A Small Web Framework with Class / RESTFul Evented JavaScript

JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス

JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス