Gruntの環境を作ってみる

ちょっとだけJavaScriptを書く機会ができたから、せっかくなのでCoffeeScriptで書こうと思ってGrunt環境の作り方を調べた。 クソ適当なメモ。

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でクラスにかかるインスタンスメソッドを変更する方法を調べるのに半日ほど無駄にしてしまったので、何も形を残さないと本当に無駄になってしまいそうだったから作ってみた。

機能

注意

  • エラー処理あんまり考えていないw
  • 元に戻す処理は行っていない
  • 同じメソッドに対して複数回呼び出すと、最後のやつが勝つ

これ

#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

これは、インスタンス/クラスどちらから呼んでも$protoにクラス名の文字列が入るようにするイディオムっぽい。

sub base_dir {
  my $proto = ref $_[0] || $_[0];
  # ...
}

ここだけ読んでもいまいちわからんから、後でまた読む。

Amon2/Config/Simple.pm

  • $cAmon2インスタンス
  • 所定の場所に置かれた、ハッシュリファレンスを返す設定ファイルを読む
  • 異常が無ければそのハッシュリファレンスを返す

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内を漁りたい

疲れたから次の記事にて...