Gruntの環境を作ってみる
ちょっとだけJavaScriptを書く機会ができたから、せっかくなのでCoffeeScriptで書こうと思ってGrunt環境の作り方を調べた。 クソ適当なメモ。
- ndenvを使いつつやる
- CoffeeScriptをJavaScriptにコンパイルするのだけやりたい
coffee/piyo.coffee
をhtdocs/js/piyo.js
へ
mkdir hoge cd hoge ndenv install v0.11.14 ndenv rehash ndenv local v0.11.14 npm install -g grunt-cli npm init npm install grunt --save-dev npm install grunt-contrib-watch --save-dev npm install grunt-contrib-coffee --save-devo vim Gruntfile.coffee
Gruntfile.coffee
の中身
module.exports = (grunt) -> grunt.initConfig pkg: grunt.file.readJSON 'package.json' watch: coffee: files: ['coffee/**/*.coffee'] tasks: 'coffee::app' coffee: app: files: [ expand: true cwd: 'coffee/' src: ['**/*.coffee'] dest: 'htdocs/js/' ext: '.js' ] grunt.loadNpmTasks 'grunt-contrib-coffee' grunt.loadNpmTasks 'grunt-contrib-watch' grunt.registerTask 'default', ['watch'] return
これだけ設定すれば、grunt
でwatchに入れる。
あとは好きなだけCoffeeScriptを書くよろし。
tmux-mem-cpu-loadを使ってみた
tmuxのステータスバーにCPUとかメモリ使用率を出したいなぁ、、、と思い、思うだけでしばらく過ごしていた。 今日、なんとなく気分転換で入れてみたのでメモ。
今までやっていなかった理由はググってもすぐに出てこなかったからだったけども、今日は偶然辿り着いた。 tmux-mem-cpu-loadという便利な物を公開している人がいた。
tmux-mem-cpu-loadをインストール
インストールのやりかたはREADME
に書いてある通り。
自分の環境にはcmake
が入っていなかったから、まずはcmake
から。
brew install cmake # Mac sudo apt-get install cmake # Ubuntu
あとは何も考えずに
cmake . make
適当にビルドしたやつを入れる用に、Mac環境では~/bin
に、Ubuntu環境には~/local/bin
にパスを通してあるので、、
cp tmux-mem-cpu-load ~/bin # Mac cp tmux-mem-cpu-load ~/local/bin # Ubuntu
普通に実行してみる。
$ tmux-mem-cpu-load 10471/16384MB [| ] 10.7% 1 1.22 1.4
tmuxの設定を変える
普段、右側に時間とかを表示しているので、そっちに寄せて表示するようにした。
status-right-length
よりもstatus-right
の文字数が多くなると、表示がアレなことになるので多めに設定している。
set -g status-right-length 80 set -g status-right "#(tmux-mem-cpu-load 1) | #[fg=white]%Y-%m-%d %H:%M:%S "
実際には右側がこんな感じ。
10869/16384MB [| ] 14.3% 2.62 1.65 1.5 | 2014-11-18 19:59:07
これで晴れて良い感じに表示されるようになりましたとさ。 めでたしめでたし。
(ただの作業メモだった)
Objective-Cにて動的にメソッドを書き換える君を作ってみた(車輪の再発明)
OCMockではクラスメソッドは書き換えられるが、全インスタンスに影響するインスタンスメソッドを書き換えられなさそうなので書いてみた。 OCMockでクラスにかかるインスタンスメソッドを変更する方法を調べるのに半日ほど無駄にしてしまったので、何も形を残さないと本当に無駄になってしまいそうだったから作ってみた。
機能
注意
これ
#import <objc/runtime.h> @interface NSObject (TestMethodReplacer) + (void)replaceClassMethodWithSelector:(SEL)selector block:(id)block; + (void)replaceInstanceMethodWithSelector:(SEL)selector block:(id)block; // convenience + (void)replaceClassMethodWithSelector:(SEL)selector returnObject:(id)returnObject; + (void)replaceInstanceMethodWithSelector:(SEL)selector returnObject:(id)returnObject; @end @implementation NSObject (TestMethodReplacer) + (void)replaceClassMethodWithSelector:(SEL)selector block:(id)block { if (!class_respondsToSelector(self, selector)) return; [self _replaceMethod:selector block:block method:class_getClassMethod(self, selector)]; } + (void)replaceInstanceMethodWithSelector:(SEL)selector block:(id)block { if (![self instancesRespondToSelector:selector]) return; [self _replaceMethod:selector block:block method:class_getInstanceMethod(self, selector)]; } + (void)_replaceMethod:(SEL)selector block:(id)block method:(Method)method { struct objc_method_description *desc = method_getDescription(method); IMP imp = imp_implementationWithBlock(block); class_replaceMethod(self, selector, imp, desc->types); } + (void)replaceClassMethodWithSelector:(SEL)selector returnObject:(id)returnObject { [self replaceClassMethodWithSelector:selector block:^{ return returnObject; }]; } + (void)replaceInstanceMethodWithSelector:(SEL)selector returnObject:(id)returnObject { [self replaceInstanceMethodWithSelector:selector block:^{ return returnObject; }]; } @end
こんな感じで使う。
@interface Hoge : NSObject + (NSString *)hoge:(NSString *)str; - (NSString *)fuga:(NSString *)str; @end @implementation Hoge + (NSString *)hoge:(NSString *)str { return [NSString stringWithFormat:@"hoge, %@", str]; } - (NSString *)fuga:(NSString *)str { return [NSString stringWithFormat:@"fuga, %@", str]; } @end ////////// [Hoge hoge:@"Yo"]; //=> hoge, Yo [[Hoge new] fuga:@"Yo"]; //=> fuga, Yo // hoge: を書き換える [Hoge replaceClassMethodWithSelector:@selector(hoge:) returnObject:@"dummy"]; [Hoge hoge:@"Yo"]; //=> dummy [[Hoge new] fuga:@"Yo"]; //=> fuga, Yo // fuga: を書き換える [Hoge replaceInstanceMethodWithSelector:@selector(fuga:) returnObject:@"homu!"]; [Hoge hoge:@"Yo"]; //=> dummy [[Hoge new] fuga:@"Yo"]; //=> homu! // hoge: を書き換える [Hoge replaceClassMethodWithSelector:@selector(hoge:) block: ^(id class, NSString *str){ return [NSString stringWithFormat:@"dummy, %@", str]; }]; [Hoge hoge:@"Yo"]; //=> dummy, Yo [[Hoge new] fuga:@"Yo"]; //=> homu! // fuga: を書き換える [Hoge replaceInstanceMethodWithSelector:@selector(fuga:) block: ^(id obj, NSString *str){ return [NSString stringWithFormat:@"homu! %@", str]; }]; [Hoge hoge:@"Yo"]; //=> dummy, Yo [[Hoge new] fuga:@"Yo"]; //=> homu! Yo
(面倒だったから例は実行確認していない)
感想とか
黒魔術楽しい。
今後は、restoreする仕組みを用意したい。 guardっぽいのは難しそう(というか、呼び出し側でautoreleasepoolで括らないと難しそう)。
Test::Mock::Guardを使ってみた
テストの時にこんな感じのことをして内部で使用しているメソッドの挙動を無理矢理変えていた。
{ # てきとうなクラス package Hoge; sub hoge { 'hoge' } # こいつを上書きしたい } say Hoge::hoge; #=> hoge sub run_with_mock { my %args = @_; no warnings 'redefine'; *Hoge::hoge = $args{mock}; $args{run}->(); } run_with_mock( mock => sub { return 'homu'; }, run => sub { say Hoge::hoge; #=> homu }, );
そしたら、レビューの時に先輩氏から「似たようなことがTest::Mock::Guardでできるから試してみると良いよ」的なことを言われたので試してみた。
say Hoge::hoge; #=> hoge { my $guard = mock_guard 'Hoge' => { hoge => sub { 'homu' }, # hoge => 'homu', 値を返すだけならコードリファレンスにしなくても良い }; say Hoge::hoge; #=> homu } say Hoge::hoge; #=> hoge
$guard
が有効なスコープに限り、挙動を書き換えられる。
普段通りにテストを書くときは、わざわざ新たなスコープを作らなくてもsubtestを使うので、とくに深く考えなくても使えるから便利。
本筋の処理のネストが深くならずに済むのが地味にメリットだと思う。 ネストが深いと、それだけで読むのが辛くなってしまうからorz
以上、めもっす。
Amon2を読むぞい その1
現時点で最新の6.10を読んでみる。 まずは全体の構成を眺める。
lib ├── [ 374] Amon2 │ ├── [ 102] Config │ │ └── [1.4K] Simple.pm │ ├── [ 436] ContextGuard.pm │ ├── [ 424] Declare.pm │ ├── [ 102] Plugin │ │ └── [ 272] Web │ │ ├── [1.5K] FillInFormLite.pm │ │ ├── [4.9K] JSON.pm │ │ ├── [ 783] NoCache.pm │ │ ├── [ 491] PlackSession.pm │ │ ├── [2.6K] Streaming.pm │ │ └── [3.0K] WebSocket.pm │ ├── [ 204] Setup │ │ ├── [ 408] Asset │ │ │ ├── [ 15K] Blueprint.pm │ │ │ ├── [900K] Bootstrap.pm │ │ │ ├── [ 14K] ES5Shim.pm │ │ │ ├── [1.7K] MicroDispatcherJS.pm │ │ │ ├── [2.8K] MicroLocationJS.pm │ │ │ ├── [1.8K] MicroTemplateJS.pm │ │ │ ├── [6.9K] SprintfJS.pm │ │ │ ├── [5.9K] StrftimeJS.pm │ │ │ ├── [1.2K] XSRFTokenJS.pm │ │ │ └── [ 83K] jQuery.pm │ │ ├── [ 170] Flavor │ │ │ ├── [4.9K] Basic.pm │ │ │ ├── [7.6K] Large.pm │ │ │ └── [2.2K] Minimum.pm │ │ ├── [6.0K] Flavor.pm │ │ └── [ 102] VC │ │ └── [1.1K] Git.pm │ ├── [2.2K] Trigger.pm │ ├── [1.9K] Util.pm │ ├── [ 238] Web │ │ ├── [ 170] Dispatcher │ │ │ ├── [1.2K] Lite.pm │ │ │ ├── [5.6K] RouterBoom.pm │ │ │ └── [1.8K] RouterSimple.pm │ │ ├── [5.2K] Request.pm │ │ ├── [ 102] Response │ │ │ └── [1.6K] Callback.pm │ │ ├── [ 363] Response.pm │ │ └── [1.3K] WebSocket.pm │ └── [8.0K] Web.pm └── [7.2K] Amon2.pm
Setup系が多い印象。
メインはAmon2::Web
あたりなのかな。
ひとまず上の階層から中身を覗いてみる。
Amon2.pm
- 設定を持ったりプラグインを読んだりする場所
- context
これは、インスタンス/クラスどちらから呼んでも$proto
にクラス名の文字列が入るようにするイディオムっぽい。
sub base_dir { my $proto = ref $_[0] || $_[0]; # ... }
ここだけ読んでもいまいちわからんから、後でまた読む。
Amon2/Config/Simple.pm
$c
はAmon2
のインスタンス- 所定の場所に置かれた、ハッシュリファレンスを返す設定ファイルを読む
- 異常が無ければそのハッシュリファレンスを返す
Amon2::Util.pm
フレームワーク外から使う想定のものではないようだ。
add_method
クラスにメソッドを生やす君
base_dir
ファッ?!
sub base_dir($)
とはいったい...。
「perldoc.jp sub 検索」
...どうやら、引数がただ1つだけであることを明示しているようだ。
random_string
/dev/urandom
から乱数を得ている/dev/urandom
が無い時はDigest::SHA
で生成
Amon2/Trigger.pm
- 任意のクラス、オブジェクトに、文字列でメソッドを関連づける君
- フックポイントに使っている
Class::Trigger
と同じようなアレっぽい
そろそろWeb内を漁りたい
疲れたから次の記事にて...