• このエントリーをはてなブックマークに追加

メモ

Data::Dump::dump() 後の文字列を eval して得られるものが想定してたものと違っていることを知らなくてハマったけど,ちょっとした修正で解決できたのでそのあたりのメモ.

確認した環境は次のとおりです:

  • perl-5.12.4
  • Data-Dump-1.22
  • ソースコード読んでません><
use 5.12.0;
use strict;
use warnings;
use Data::Dumper;
use Data::Dump qw/dump/;
 
$Data::Dumper::Terse = 1;
 
my %data = (
    'foo' => [ 'bar', 'baz' ],
    '-hoge' => { 'fuga' => 'piyo' },
    -abc => { -x => 0, -y => 10 },
);
 
 
my $d_Dump   = dump \%data;
my $d_Dumper = Dumper \%data;
 
say << "...";
### HashRef -> Data::Dump::dump()
$d_Dump
 
### HashRef -> Data::Dumper::Dumper()
$d_Dumper
 
### eval Data::Dump::dump()-ed (with Data::Dump::dump())
@{[ dump eval $d_Dump ]}
 
### eval Data::Dump::dump()-ed (with Data::Dumper::Dumper())
@{[ Dumper eval $d_Dump ]}
 
### eval Data::Dump::Dumper()-ed (with Data::Dumper::Dumper())
@{[ Dumper eval $d_Dumper ]}
 
### eval Data::Dump::Dumper()-ed (with Data::Dump::dump())
@{[ dump eval $d_Dumper ]}
...
### HashRef -> Data::Dump::dump()
{
  -abc  => { -x => 0, -y => 10 },
  -hoge => { fuga => "piyo" },
  foo   => ["bar", "baz"],
}
 
### HashRef -> Data::Dumper::Dumper()
{
  '-abc' => {
              '-y' => 10,
              '-x' => 0
            },
  'foo' => [
             'bar',
             'baz'
           ],
  '-hoge' => {
               'fuga' => 'piyo'
             }
}
 
 
### eval Data::Dump::dump()-ed (with Data::Dump::dump())
(
  "-abc",
  { -x => 0, -y => 10 },
  "-hoge",
  { fuga => "piyo" },
  "foo",
  ["bar", "baz"],
)
 
### eval Data::Dump::dump()-ed (with Data::Dumper::Dumper())
'-abc'
 {
  '-y' => 10,
  '-x' => 0
}
 '-hoge'
 {
  'fuga' => 'piyo'
}
 'foo'
 [
  'bar',
  'baz'
]
 
 
### eval Data::Dump::Dumper()-ed (with Data::Dumper::Dumper())
{
  '-abc' => {
              '-y' => 10,
              '-x' => 0
            },
  'foo' => [
             'bar',
             'baz'
           ],
  '-hoge' => {
               'fuga' => 'piyo'
             }
}
 
 
### eval Data::Dump::Dumper()-ed (with Data::Dump::dump())
{
  -abc  => { -x => 0, -y => 10 },
  -hoge => { fuga => "piyo" },
  foo   => ["bar", "baz"],
}

ちなみに,本来の挙動はこんな感じです:

my %data = (
    'foo' => [ 'bar', 'baz' ],
    'hoge' => { 'fuga' => 'piyo' },
    abc => { -x => 0, -y => 0 },
);
 
say << "...";
### dumped
@{[ dump \%data ]}
 
### eval dumped
@{[ Dumper eval dump \%data ]}
...
### dumped
{
  abc  => { -x => 0, -y => 0 },
  foo  => ["bar", "baz"],
  hoge => { fuga => "piyo" },
}
 
### eval dumped
{
  'abc' => {
             '-y' => 0,
             '-x' => 0
           },
  'foo' => [
             'bar',
             'baz'
           ],
  'hoge' => {
              'fuga' => 'piyo'
            }
}

- で始まるキーを含む HashRef で起こるようです:

とりあえず想定どおりになるようにするには, "+{dumped な文字列}" を eval すればよいっぽい:

$data = eval( "+$dumped" );
say << "...";
### eval Data::Dump::dump()-ed (expected)
@{[ Dumper eval "+$d_Dump" ]}
...
### eval Data::Dump::dump()-ed (expected)
{
  '-abc' => {
              '-y' => 10,
              '-x' => 0
            },
  'foo' => [
             'bar',
             'baz'
           ],
  '-hoge' => {
               'fuga' => 'piyo'
             }
}

これで大丈夫かな.

...4年くらい前に書いたアプリケーションが,これが原因で最近になって不具合起こしたので,アドホックな修正で対処.ほぼ 1日かかってしまいました ><

  • このエントリーをはてなブックマークに追加

メモ

こちらのエントリのその後,最近になって次のような変更を加えました:

  • $c->validator() で呼べるようにした

    • $c->new_validator() とかするのがちょっとアレになってきたので
  • $v->validate() に,リクエストオブジェクト(is-a Plack::Request)を直接渡せるようにした

    • HashRef を渡すために毎度 $req->parameters->mixed するのがメンドイので
  • 内部的なバリデータクラスの名前を Data::Validator::Amon2 に変更した

    • Data::Validator::Filterable という名前がちょっとアレになってきたので
    • Plack::Request オブジェクトを直接受けることから,Data::Validator::Amon2::Web みたいの方がよいのかもしれない

サンプルコード

package MyApp::C::Root;
use strict;
use warnings;
use MouseX::Types::Mouse qw/Bool Str/;
 
sub index {
    my ($class, $c) = @_;
    my ($req, $res) = ( $c->req );
 
    ### $c->validator() で呼べるように
    my $v = $c->validator(
        foo => { isa => Str },
        bar => { isa => Bool },
    )->with('NoThrow');
 
    ### $req を直接渡せるように
    my $q = $v->valide( $req );
 
    if ( $v->has_errors ) {
        ...
    }
 
    ...
}
 
...

GitHub

まぁ修正自体は 1週間ほど前ですが.

  • このエントリーをはてなブックマークに追加

2013-05-28T19:50:00 追記

デフォルトで define {単語} として同様のことができるようですね.無知でした><

ss-1369736045

はじめに

単語の意味を調べたり確認したりするたびに「辞書.app」をアクティヴにして検索窓をフォーカスして...とかメンドイので,Alfred の窓で(キーワードと)単語を入れるだけで「辞書.app」を引けるようにしてみました.

根本

「dict://」が利用できることを応用して、
open dict://文字列
とコマンドを実行すると辞書を引くことができます。
辞書(Dictionary).appを使い倒そう : 紹介マニア

こちらの単純な応用です.

設定

こんな感じ:

ss-1369736202

シェルスクリプト部分をテキストにて:

#!/bin/bash
open dict://{query}

これで dic {単語} して Enter するだけで,辞書.app が引けるようになりました!

おわりに

そういえば,Alfred がバージョン 2 になって,アップグレード用ライセンスは既に購入してたけど, UI がガラっと変わるっぽいのでそのまま放置状態だったなw そろそろシフトしないとな.

  • このエントリーをはてなブックマークに追加

はじめに

<head>
  <script type-"text/javascript" src="jquery-1.9.1.min.js"></script>
  <script type="text/javascript" src="router.js"></script>
  <script type="text/javascript" src="myapp.js"></script>
</head>
<body>
  <div id="my-simple-app">
    <ul>
      <li><a href="#/hello">hello</a></li>
      <li><a href="#/show/123">hello guest</a></li>
      <li><a href="#/show/456">hello guest</a></li>
    </ul>
  </div>
</body>

こんな HTML において,<a> 要素をクリックしたときに,その属性 href="#..." にしたがって挙動を定義したい.そんな要件.

大規模なアプリケーションに縁のない私は,数ある JavaScript フレームワークを利用する機会もない(勉強してないだけ)ので,オレオレな「ルータ」クラスを定義して,「クリックされた要素の URL がこれの場合はこの関数を呼ぶ」といった定義を集約するようなことをすることで,小規模ながら少しはお仕事を省力できているかもしれない今日このごろです.

ちなみに,Perl の CPAN モジュール Router::Simple のインタフェイスに似せているかもしれません.

SYNOPSIS

# ルーティングを定義する
router = new Router()
router.connect( '/hello', -> alert('hello') )
router.connect( '/show/([0-9]+)', _handle_show )
 
# ハンドラを定義する
_handle_show = ( [id] ) ->
    $a = @
    console.log "href: #{ $a.attr('href') }"
    console.log "shows id:#{id}"
 
# イベントリスナを張る
$(...).on( 'click', 'a[href^="#"]', (ev) ->
    $a = $(ev.target)
    router.match( $a.attr('href') )($a)
    return false
)
 
# 直接呼んだり
setTimeout(
    ->
        router.match( '/hello' )()
    10 * 1000
)

実装例

冒頭の HTML における myapp.js に対応する coffeescript,例えばこんな感じで書けます:

# myapp.coffee
class MyApp
    @init: ->
        @$ = $('#my-simple-app')
        @setup_router()
        @listen()
 
    @setup_router: ->
        self = @
        @_router = new Router
            connect:
                '/hello': -> self._handle_hello()
                '/show/([0-9]+)': (m, ev) -> self._handle_show(m, ev)
 
    @listen: ->
        @$.on( 'click', 'a[herf^="#"]', (ev) =>
            $a = $(ev.target)
            href = $a.attr('href')
            @_router.match( href, ev )($a)
            return false
        )
 
    @_handle_hello: ->
        alert 'hello'
 
    @_handle_show: ([id], ev): ->
        alert "shows id:#{id}"
 
$ ->
    app = MyApp.init()

こんな感じに書いておけば,HTML 上に href="#..." な <a> 要素が増えた場合,

  1. ルータにルールを追加する
  2. 対応するハンドラを追加する

といったステップを踏むだけで拡張できたりします.

ルータクラスのコード

router.js に対応する coffeescript. Router クラスの定義のみ:

# router.coffee
class @Router
    constructor: (params = {}) ->
        @_rules = []
        for k, v of params.connect ? {}
            @connect(k, v)
 
    connect: (hash, action) ->
        @_rules.push( hash: hash, action: (action ? ->) )
        return @
 
    match: (url_hash, ev) ->
        ret = null
        url_hash = url_hash.replace( /^.*\#!?/, '' )  # '#' or '#!' を含む以前を削除
 
        for r in @_rules
            {hash: h, action: a} = r
            m = url_hash.match( @_build_regexp(h) )
 
            if m?
                ret = (o) ->
                    m.shift()
                    return a.apply(o, [m, ev])
                break
 
        if !ret?
            ret = (o) =>
                return @_cannot_route.apply(o, [url_hash, ev])
 
        return ret
 
    _build_regexp: (h) ->
        return new RegExp( ("^#{h}$").replace(/^\^+/, '^').replace(/\$+$/, '$') )
 
    _cannot_route: (h, ev) ->
        throw new Error('cannot route: ' + h)

おわりに

以上,大規模なお仕事に触れることのない私が,オレオレな「ルータ」クラスを定義してイベントハンドリングまわりのルールを集約することで,小規模なお仕事の開発やメンテナンスの手間を少しだけ省力しているかもしれないというお話でした.

  • このエントリーをはてなブックマークに追加

ss-1366457267

はじめに

最近ようやく Mouse な実装を始めたところで(Moose もほとんどやったことない),これがなかなか楽しいので,ちょうどお仕事で必要だった「パジネーション」なクラスを書いてみたのでそのへんを晒してみます.

個人的に使いたいようにしか実装していませんw

こんな感じで使いたい

「モデル」という定義にそぐわないような気もしますが,まぁあまり気にしないでください><

コントローラ内

コード中に出てくる $c->model(...) については,次をご参照ください:

sub foobar {
    my ($class, $c) = @_;
    my $req = $c->req;
 
    my $page  = $req->param('page') // 1;
    my $num   = $req->param('num')  // 50;
    my $count = ...;
 
    my @nanika_no_list = ...;
 
    my %pg_params = (
        page    => $page,
        entries => $num,
        count   => $count,
    );
    my $pg = $c->model('Pagination' => \%pg_params);  # ココ!
 
    return $c->render('foobar.tx', {
        nanika_no_list => \@nanika_no_list,
        pg             => $pg,
    })
}

ビュー foobar.tx

...
 
<div id="nanika_no_list">
:include inc::pg
 
  <ol>
:for $nanika_no_list -> $i {
    <li><: $i.foobar :></:></li>
:}
  </ol>
 
:include inc::pg
</div><!-- /#nanika_no_list -->
 
...

パジネーション部分のテンプレート inc/pg.tx

:if defined $pg {
<div class="row-fluid">
  <ul class="pager span1">
:  if $pg.has_prev {
    <li class="previous">
      <a href="?page=<: $pg.prev :>">&laquo;</a>
    </li>
:  } else {
    <li class="previous disabled">
      <a>&lt;&lt;</a>
    </li>
:  }
  </ul>
 
  <div class="pagination pagination-centered span10">
    <ul>
:  for $pg.list_page( range => 3, first => 1, last => 1 ) -> $p {
:    if defined $p {
      <li <:if $p == $pg.page {:>class="active"<: }:>>
        <a href="?page=<: $p :>"><: $p :></:></a>
      </:></li>
:    } else {
      <li class="disabled"><a>...</a></li>
:    }
:  }
    </ul>
  </div>
 
  <ul class="pager span1">
:  if $pg.has_next {
    <li class="next">
      <a href="?page=<: $pg.next :>">&raquo;</a>
    </li>
:  } else {
    <li class="next disabled">
      <a>&gt;&gt;</a>
    </li>
:  }
  </ul>
</div><!-- /.row-fluid -->
:}

パジネーションなクラス

上記の要件の下,以下のように書いてみました.
続きを読む »

10 of 1221...89101112...203040...122