2014年12月20日星期六

[PerlChina] PerlChina Advent Day 20: cpanfile

# cpanfile

传统的 Perl 风格,是将依赖模块写在 Makefile.PL 里。无奈这个单词实在给人太大精神压力,而且在一些项目应用中,我们也不需要除了依赖关系以外其他的 Makefile 内容。所以 modern perl 从 Ruby 学来了 Gemfile 的方式,叫做 [cpanfile](https://metacpan.org/pod/cpanfile)。

cpanfile 写法示例如下:

    requires 'perl', '5.20.0';
    requires 'Plack', '1.0'; # 1.0 or newer
    requires 'JSON', '>= 2.00, < 2.80';

    recommends 'JSON::XS', '2.0';
    conflicts 'JSON', '< 1.0';

    on 'test' => sub {
      requires 'Test::More', '>= 0.96, < 2.0';
      recommends 'Test::TCP', '1.12';
    };

支持 cpanfile 格式的安装工具,目前有两个:[cpanm](https://metacpan.org/pod/cpanm) 和 [carton](https://metacpan.org/pod/carton)。

cpanm 在之前的 advent 已经介绍过,可以说是目前最推荐的 cpan 客户端命令了。在有 cpanfile 存在的目录下,执行如下命令即可安装 cpanfile 里列出的全部依赖模块:

    $ cpanm --installdeps .

而 carton 则是学习 Ruby 的 bundle 命令。了解 bundle 的,基本可以直接照着习惯用 carton 就好了。如果不了解的,继续往下看。

## carton 用法

### 安装依赖

同样是在有 cpanfile 存在的目录下,执行如下命令安装依赖模块:

    $ carton install

和采用 cpanm 方式不同的是,这个命令会在目录下新生成一个文件叫 `cpanfile.snapshot`。这个文件里会记录你这次安装的时候的模块的确切版本,这样你通过 github 等方式共享给其他人、发布到服务器等的时候,再用下面的命令安装就可以同样做到安装完全一致的版本的模块了:

    $ carton install --deployment

### 执行

carton 和 cpanm 的另一个不同, cpanm 默认是把模块安装到系统目录里,用 -L 参数指定才会到指定目录。而 carton 默认是安装到当前目录的 **local/** 子目录下。所以,就需要利用 carton 命令来启动应用才能找到正确的路径:

    $ carton exec hypnotoad ./script/myapp

### 打包

carton 有专门的一个 bundle 命令。把之前 install 在 **local/** 里的模块,复制到 **vendor/cache** 目录下。然后通过下面命令来安装:

    $ carton install --cached

看起来似乎有点多此一举,分发的时候发哪个目录不都一样么?要点在于:carton 复制到 **vendor/cache** 目录下的,不是安装好的模块,而是各模块的 .tar.gz 包。这样,对于跨不同平台和系统环境的部署,这个会重新走一遍编译的过程,有些 XS 就不会有问题了。

好了,大家快给自己的项目加上 cpanfile 吧~

--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

2014年12月19日星期五

[PerlChina] PerlChina Advent Day 19: 客户端来源 IP 信息处理

=encoding utf8

=for advent_year 2014

=for advent_title 客户端来源 IP 信息处理

=for advent_author fukai

=head2 前言

我们进行日志处理, 常有的一个需求就是需要日志中来源的 IP 变成指定的信息来进行分析, 也有可能有一种需求, 就是需要给所有用户的请求转换成 "ISP, 省市, 城市", 然后在排序分析用户来源相关的信息, 如省市排名, 运营商排名, 更加复杂的还有可能, 根据这些信息来做基础信息来判断用户的体验, 比如那个地区, 那个 ISP 的用户访问速度最快等等.
另外, 对于象视频, 大文件下载, 这种应用, 目前很多是基于 HTTP 之类的更加高级的调度, 这时我们就需要更加给请求过来的用户 IP , 也转换成相应的地区, 然后查询自己的全网调度系统中, 是否存在相应最优节点.

基于以上二点, 我们需要非常非常快速的 IP 转换成城市信息的模块. 在 08 年的时候, 我就想过各种方式, 使用过 Geo::IP 之类的库, 当年的 QQway.bat 的结构,还有比如给 IP 段折开, 存到数据之类的地方, 然后有了 IP , 根据这个信息来查询段是什么 ISP 和地区, 试过各种方式, 但速度和效率都不太理想.

比如日志 cat 出来后进行二次分析和清洗的时候, 这个查询和转换是最占用时间的, 真到我找到了 Net::IP::Match::Trie 这个模块, 这个速度快到可以不影响你 cat 的速度, 直接线性的处理 IP 到信息的转换.

当年这个模块还是刚出来的时候我关注到的, 非常好用, 非常想推荐给其它人. 但一直没有发布正式版本, 就这样用了几年, 因为不是正式版本, 所以 cpanm 一直找不到这个模块, 要指定 url 才能安装. 直到去年末, 我发了个邮件给作者, 提醒他, 这个模块几年还没 release , 作者接到邮件后立即就更新, 所以现在正式接这个机会推荐给有相应需求的朋友 ( 注: cpan 作者都非常热情, 有问题直接联系作者哦 ). 注: 我自己的调度系统中一直使用这个模块, 很多年了.

=head2 原始 IP 段和地址信息的加入

进行 IP 信息处理, 我们需要先收集 IP 段是哪个运营商. 目前网络上这种数据很多, 可以下载任何一个数据来加入到这个模块当中.

  use Net::IP::Match::Trie;
  my $matcher = Net::IP::Match::Trie->new;
  $matcher->add(CNC-ZJ-ZJ => [qw(66.249.64.0/19 74.125.0.0/16)]);
  $matcher->add(CNC-ZJ-ZJ  => [qw(69.147.64.0/18 209.191.64.0/18 209.131.32.0/19)]);


我是直接给对应的信息写成了一个如下的文件结构, 然后使用上面的方式导入.


上面 add 的 key 就是后面的 CNC,ZJ-ZJ. 然后在加上网段.


=head2 来源 IP 的查询

查询非常简单, 只需要给 IP 放到 match_ip 的方法中就搞定了.

  say $matcher->match_ip("66.249.64.1"); # => "CNC-ZJ-ZJ"


=head2 样例

下面是一个简单的管道传送过来的日志进行 IP 地址翻译的小程序.

  use strict;
  use 5.010;

  use Net::IP::Match::Trie;
  my $matcher = Net::IP::Match::Trie->new;

  open my $fh, "<", 'isp_ip.csv' or die "print $!";
  while (<$fh>) {
      my ($CIDRS, $isp, $region) = split(/,/, $_);
      chomp $region;
      my $name  = "$isp-$region";
      $matcher->add( $name , [$CIDRS] );
  }

  while(<>){
      chomp;
      my $ISP = $matcher->match_ip($_);
      print "$_, $ISP\n";
  }

=head2 使用

象上面, 可以用于日常的日志处理, 让信息读进来一次, 给 IP 以流的方式 cat 进去就好了, 象日志处理什么之类就非常好用了.

  # echo 202.106.0.20 |perl check.pl
  202.106.0.20, CNC-BJ-BJ

=head2 IP 段的整理

象上面的 IP 段, 因为运营商信息不断的变化, 所以你的 IP 数据库也会跟着不断的变化, 当运营商给你一堆 IP 时, 你对很多重复的段要合并, 有些小的段要折分, 这样手工操作很累, 同样, 也推荐一个模块, 叫 Net::IP::RangeCompare , 小小包装和处理一下, 配合起来使用, 非常合适.

=head2 作者

fukai, L<mail://iakuf@163.com>

=cut


--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

2014年12月18日星期四

Re: [PerlChina] PerlChina Advent Day 18: Time::Piece 模块

cool~ ;-)

于 14/12/18 下午9:58, chenlin rao 写道:
巧合的是今天日本Perl协会的advent发的是 Time::Piece::Iterator 模块,http://t.cn/Rz3HWiy 大家可以一块看看~

在 2014年12月18日 下午9:56,chenlin rao <rao.chenlin@gmail.com>写 道:
# Time::Piece

时间处理是一个有时候蛮麻烦的事情。很多 Perl 程序员可能还习惯于使用下面这样的写法:

    my ($sec, $min, $hour, $mday, $mon, $year_off, $wday, $yday, $isdat) = localtime;

或者稍微简单一点的地方,可以:

    use POSIX qw/strftime/;
    print strftime('%F %T', localtime);

但是涉及到时间运算的时候,就麻烦多了。常见的是换算到秒做加减乘除,或者安装沉重的 DateTime 模块。

## Time::Piece 示例

其实,从 Perl v5.9.5 版本开始,就随内核分发一个时间模块,叫 [Time::Piece](https://metacpan.org/pod/Time::Piece)。在 通常情况下,都足够好用了。

    use 5.010;
    use Time::Piece;
    my $t = localtime() - localtime()->tzoffset;
    say $t->datetime;                              # 2014-12-14T16:06:06
    say localtime()->datetime;                     # 2014-12-15T00:06:06
    say $t->date;                                  # 2014-12-14
    say $t->mdy('/');                              # 12/14/2014
    say $t->time;                                  # 00:06:06
    say $t->hms('.');                              # 00.06.06
    say $t->epoch;                                 # 1418573166
    say $t->month;                                 # Dec
    say $t->add_months(1)->year;                   # 2015

Time::Piece 会覆盖 CORE 里提供的 **localtime** 和 **gmtime** 函数,自动返回 Time::Piece 对象。这点类似的还有 [File::stat](https://metacpan.org/pod/File::stat) 模块,注意是小写的,这个也是 Perl5 现在的内核分发模块。在 `use File::stat` 以后,**stat** 函数也会自动返回 File::stat 对象而不是那个复杂的数组!

## Time::Seconds 示例

上面示例代码里有个隐藏的地方,`localtime->tzoffset` 事实上是返回了一个 [Time::Seconds](https://metacpan.org/pod/Time::Seconds) 对象。这个类是 Time::Piece 模块里一起提供的,不用单独安装。

一个 Time::Piece 对象加减一个 Time::Seconds 对象,得到的还是 Time::Piece 对象。而两个 Time::Piece 对象相加减,得到的则是 Time::Seconds 对象了:

    my $s = $t - localtime();
    say $s->days;                                  # 31

Time::Seconds 如果你单独导入的话,他会导出一系列常量,比如我们再重复一次前面那个加一个月的操作,就可以写成:

    use Time::Seconds;
    $t += ONE_MONTH;                               # 继续用前面的 $t 变量
    say $t->month;                                 # Feb

## 缺陷

Time::Piece 虽然也实现了 `strptime` 方法,但是实现的不是很全面(不支持时区)。如果有很强的从字符串转换成时间对象的需求,可以参考 [Time::Moment](https://metacpan.org/pod/distribution/Time-Moment/lib/Time/Moment.pod) 模块的 `->from_string` 方法。Time::Moment 模块也提供了类似的一些属性方法。


--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

Re: [PerlChina] PerlChina Advent Day 18: Time::Piece 模块

巧合的是今天日本Perl协会的advent发的是 Time::Piece::Iterator 模块,http://t.cn/Rz3HWiy 大家可以一块看看~

在 2014年12月18日 下午9:56,chenlin rao <rao.chenlin@gmail.com>写道:
# Time::Piece

时间处理是一个有时候蛮麻烦的事情。很多 Perl 程序员可能还习惯于使用下面这样的写法:

    my ($sec, $min, $hour, $mday, $mon, $year_off, $wday, $yday, $isdat) = localtime;

或者稍微简单一点的地方,可以:

    use POSIX qw/strftime/;
    print strftime('%F %T', localtime);

但是涉及到时间运算的时候,就麻烦多了。常见的是换算到秒做加减乘除,或者安装沉重的 DateTime 模块。

## Time::Piece 示例

其实,从 Perl v5.9.5 版本开始,就随内核分发一个时间模块,叫 [Time::Piece](https://metacpan.org/pod/Time::Piece)。在通常情况下,都足够好用了。

    use 5.010;
    use Time::Piece;
    my $t = localtime() - localtime()->tzoffset;
    say $t->datetime;                              # 2014-12-14T16:06:06
    say localtime()->datetime;                     # 2014-12-15T00:06:06
    say $t->date;                                  # 2014-12-14
    say $t->mdy('/');                              # 12/14/2014
    say $t->time;                                  # 00:06:06
    say $t->hms('.');                              # 00.06.06
    say $t->epoch;                                 # 1418573166
    say $t->month;                                 # Dec
    say $t->add_months(1)->year;                   # 2015

Time::Piece 会覆盖 CORE 里提供的 **localtime** 和 **gmtime** 函数,自动返回 Time::Piece 对象。这点类似的还有 [File::stat](https://metacpan.org/pod/File::stat) 模块,注意是小写的,这个也是 Perl5 现在的内核分发模块。在 `use File::stat` 以后,**stat** 函数也会自动返回 File::stat 对象而不是那个复杂的数组!

## Time::Seconds 示例

上面示例代码里有个隐藏的地方,`localtime->tzoffset` 事实上是返回了一个 [Time::Seconds](https://metacpan.org/pod/Time::Seconds) 对象。这个类是 Time::Piece 模块里一起提供的,不用单独安装。

一个 Time::Piece 对象加减一个 Time::Seconds 对象,得到的还是 Time::Piece 对象。而两个 Time::Piece 对象相加减,得到的则是 Time::Seconds 对象了:

    my $s = $t - localtime();
    say $s->days;                                  # 31

Time::Seconds 如果你单独导入的话,他会导出一系列常量,比如我们再重复一次前面那个加一个月的操作,就可以写成:

    use Time::Seconds;
    $t += ONE_MONTH;                               # 继续用前面的 $t 变量
    say $t->month;                                 # Feb

## 缺陷

Time::Piece 虽然也实现了 `strptime` 方法,但是实现的不是很全面(不支持时区)。如果有很强的从字符串转换成时间对象的需求,可以参考 [Time::Moment](https://metacpan.org/pod/distribution/Time-Moment/lib/Time/Moment.pod) 模块的 `->from_string` 方法。Time::Moment 模块也提供了类似的一些属性方法。


--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

[PerlChina] PerlChina Advent Day 18: Time::Piece 模块

# Time::Piece

时间处理是一个有时候蛮麻烦的事情。很多 Perl 程序员可能还习惯于使用下面这样的写法:

    my ($sec, $min, $hour, $mday, $mon, $year_off, $wday, $yday, $isdat) = localtime;

或者稍微简单一点的地方,可以:

    use POSIX qw/strftime/;
    print strftime('%F %T', localtime);

但是涉及到时间运算的时候,就麻烦多了。常见的是换算到秒做加减乘除,或者安装沉重的 DateTime 模块。

## Time::Piece 示例

其实,从 Perl v5.9.5 版本开始,就随内核分发一个时间模块,叫 [Time::Piece](https://metacpan.org/pod/Time::Piece)。在通常情况下,都足够好用了。

    use 5.010;
    use Time::Piece;
    my $t = localtime() - localtime()->tzoffset;
    say $t->datetime;                              # 2014-12-14T16:06:06
    say localtime()->datetime;                     # 2014-12-15T00:06:06
    say $t->date;                                  # 2014-12-14
    say $t->mdy('/');                              # 12/14/2014
    say $t->time;                                  # 00:06:06
    say $t->hms('.');                              # 00.06.06
    say $t->epoch;                                 # 1418573166
    say $t->month;                                 # Dec
    say $t->add_months(1)->year;                   # 2015

Time::Piece 会覆盖 CORE 里提供的 **localtime** 和 **gmtime** 函数,自动返回 Time::Piece 对象。这点类似的还有 [File::stat](https://metacpan.org/pod/File::stat) 模块,注意是小写的,这个也是 Perl5 现在的内核分发模块。在 `use File::stat` 以后,**stat** 函数也会自动返回 File::stat 对象而不是那个复杂的数组!

## Time::Seconds 示例

上面示例代码里有个隐藏的地方,`localtime->tzoffset` 事实上是返回了一个 [Time::Seconds](https://metacpan.org/pod/Time::Seconds) 对象。这个类是 Time::Piece 模块里一起提供的,不用单独安装。

一个 Time::Piece 对象加减一个 Time::Seconds 对象,得到的还是 Time::Piece 对象。而两个 Time::Piece 对象相加减,得到的则是 Time::Seconds 对象了:

    my $s = $t - localtime();
    say $s->days;                                  # 31

Time::Seconds 如果你单独导入的话,他会导出一系列常量,比如我们再重复一次前面那个加一个月的操作,就可以写成:

    use Time::Seconds;
    $t += ONE_MONTH;                               # 继续用前面的 $t 变量
    say $t->month;                                 # Feb

## 缺陷

Time::Piece 虽然也实现了 `strptime` 方法,但是实现的不是很全面(不支持时区)。如果有很强的从字符串转换成时间对象的需求,可以参考 [Time::Moment](https://metacpan.org/pod/distribution/Time-Moment/lib/Time/Moment.pod) 模块的 `->from_string` 方法。Time::Moment 模块也提供了类似的一些属性方法。


--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

[PerlChina] Re: 发起两个学习小组:大数据和TDD

发现一个很棒的程序,采用了PDL::stats ,Web 采用的是 Mojolicious构架。

效果演示 :http://gitinsight.mudler.pm/

代码:https://github.com/mudler/GitInsight

Web界面 :https://github.com/mudler/WebApp-GitInsight

对于学习PDL和Mojolicious 都有很好的学习价值。

On Tuesday, December 16, 2014 11:14:28 AM UTC+8, BlackJack wrote:
在上周日上海Perl聚会后,考虑发起两个学习小组:大数据和TDD来活跃和丰富Perl的应用领域。
大数据小组主要分支一是学习PDL ,专注于 计算可视化 ,即数据挖掘,2D/3D演示。主要应用场景是 生物医药统计和计算,金融证券数据分析,电商CRM客户行为建模和挖掘。是不是非常高大上。
大数据小组的另一分支是海量日志收集和分析系统,包括分布式计算和存储。


TDD(即Test-Driven Development )小组主要专注这样开发模式的应用和普及,比如基于Test:Mojo开发网站压力和安全测试,以及普及Perl测试技术和流程。

欢迎有兴趣的Monks来邮件交流和参加对应的小组共同学习和实践。

Xu.hejun@163.com

--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

2014年12月17日星期三

[PerlChina] PerlChina Advent Day 17: File::ChangeNotify

# File::ChangeNotify

[File::ChangeNotify](https://metacpan.org/pod/File::ChangeNotify)通过系统事件通知来获悉文件的变化,所以其性能较好,CPU占用率也低。

我们可以使用[File::ChangeNotify](https://metacpan.org/pod/File::ChangeNotify)监测文件/目录的变化,来达到某些自动化操作。

例如:

1. nginx -s reload

当然[File::ChangeNotify](https://metacpan.org/pod/File::ChangeNotify)也有表现不正常的时候,正如我最近用Vagrant+VirtualBox搭建的Debian 7开发环境,经Host修改的文件,在Guest中用File::ChangeNotify就因为整个环境的问题,修改过的文件,Guest系统并没有发出notify事件,所以[File::ChangeNotify](https://metacpan.org/pod/File::ChangeNotify)无法被通知到。当然这不能算是[File::ChangeNotify](https://metacpan.org/pod/File::ChangeNotify)的问题了

下面是我工作场景的一个示例,监测一些目录的文件变化,并执行drush来清除Drupal Cache操作:

#!perl

# 监控drupal sites下的module文件,有改变则用drush cc清除cache
# 此脚本需要在drupal_site_path对应的目录下执行
# usage:
# 1. cd the_drupal_site_path
# 2. perl $0 path1 path2 path..n

use strict;

use File::ChangeNotify;

sub usage
{
die "perl $0 path1 path2 path..n\n";
}

my @site_path = @ARGV;
if (!@site_path)
{
&usage();
}

my $watcher = File::ChangeNotify->instantiate_watcher(
directories => [@site_path],
filter => qr/\.(?:module|inc|info|php)$/,
sleep_interval => 1,
);

print "monitor @site_path, wait for events\n\n";

while (my @events = $watcher->wait_for_events())
{
print "\nclear cache at: " . scalar(localtime()) . "\n";
system('drush cc all');
print "\n";
}

## 作者
[Beckheng Lam](http://blog.yixinit.com/)

--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

2014年12月16日星期二

Re: [PerlChina] 发起两个学习小组:大数据和TDD

希望更多的高手加入指导学习小组。

On Tuesday, December 16, 2014 11:16:00 AM UTC+8, Fayland Lam wrote:
考虑下将代码放到 github https://github.com/PerlChina? 需要 owner 权限可以找我。

Thanks
On Tue Dec 16 2014 at 11:14:26 AM blackjack xu <blackj...@gmail.com> wrote:
在上周日上海Perl聚会后,考虑发起两个学习小组:大数据和TDD来活跃和丰富Perl的应用领域。
大数据小组主要分支一是学习PDL ,专注于 计算可视化 ,即数据挖掘,2D/3D演示。主要应用场景是 生物医药统计和计算,金融证券数据分析,电商CRM客户行为建模和挖掘。是不是非常高大上。
大数据小组的另一分支是海量日志收集和分析系统,包括分布式计算和存储。


TDD(即Test-Driven Development )小组主要专注这样开发模式的应用和普及,比如基于Test:Mojo开发网站压力和安全测试,以及普及Perl测试技术和流程。

欢迎有兴趣的Monks来邮件交流和参加对应的小组共同学习和实践。

Xu.h...@163.com

--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perl...@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

Re: [PerlChina] 发起两个学习小组:大数据和TDD

最近公司在上cdh5,积极学习中。

发自我的 iPhone

在 2014年12月16日,上午11:14,blackjack xu <blackjack.xu@gmail.com> 写道:

在上周日上海Perl聚会后,考虑发起两个学习小组:大数据和TDD来活跃和丰富Perl的应用领域。
大数据小组主要分支一是学习PDL ,专注于 计算可视化 ,即数据挖掘,2D/3D演示。主要应用场景是 生物医药统计和计算,金融证券数据分析,电商CRM客户行为建模和挖掘。是不是非常高大上。
大数据小组的另一分支是海量日志收集和分析系统,包括分布式计算和存储。


TDD(即Test-Driven Development )小组主要专注这样开发模式的应用和普及,比如基于Test:Mojo开发网站压力和安全测试,以及普及Perl测试技术和流程。

欢迎有兴趣的Monks来邮件交流和参加对应的小组共同学习和实践。

Xu.hejun@163.com

--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

Re: [PerlChina] 发起两个学习小组:大数据和TDD

支持,刚好最近开始参加Hadoop的培训。


于 14/12/16 下午6:48, 松涛 写道:
有兴趣的人如何参加?

Sent from my iPhone

On 2014年12月16日, at 上午11:14, blackjack xu <blackjack.xu@gmail.com> wrote:

在上周日上海Perl聚会后,考虑发起两个学习小组:大数据和TDD来活跃和丰富Perl的应用领域。
大数据小组主要分支一是学习PDL ,专注于 计算可视化 ,即数据挖掘,2D/3D演示。主要应用场景是 生物医药统计和计算,金融证券数据分析,电商CRM客户行为建模和挖掘。是不是非常高大上。
大数据小组的另一分支是海量日志收集和分析系统,包括分布式计算和存储。


TDD(即Test-Driven Development )小组主要专注这样开发模式的应用和普及,比如基于Test:Mojo开发网站压力和安全测试,以及普及Perl测试技术和流程。

欢迎有兴趣的Monks来邮件交流和参加对应的小组共同学习和实践。

Xu.hejun@163.com
--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout
--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

[PerlChina] PerlChina Advent 16: Regexp::Grammar

# Regexp::Grammars

如果让我来列举最有用的Perl module,第一可能是Moo(se), 然后就是Regexp::Grammars 了。
Regexp::Grammars 相当于Perl 6 中的 [Grammars](http://doc.perl6.org/language/grammars)

我早有介绍一下这个module 的想法,但是没有找到好的切入点。今天看到Perl6 advent谈到用grammar来解析FASTA文件。
[Day 15 – Bioinformatics and the joy of Perl 6](http://perl6advent.wordpress.com/2014/12/15/day-15-bioinformatics-and-the-joy-of-perl6/)。正好以此为例来聊一聊。


## FASTA 格式

    cat test.fasta
    >hello
    GCTATATAAGC
    >world prot
    TATAKEKEKELKL

搞生物信息的肯定很熟悉,简单的说就是每个'>'开始一个条目。

每个条目可以概括为:
```
">"<id><comment>?"\n"<sequence>
```
看了以上,可能就理解了。事实上,这行代码直接从Perl 6 FASTA grammar 抄过来了。

完整的Perl 6 grammar

    grammar FASTA::Grammar {
        token TOP { <record>+ }

        token record { ">"<id><comment>?"\n"<sequence> }

        token id { <-[\ \n]>+ }

        token comment { " "<-[\n]>+ }

        proto rule sequence {*}
              rule sequence:sym<dna> { <[ACGTRYKMSWBDHVNX\-\n]>+ }
              rule sequence:sym<rna> { <[ACGURYKMSWBDHVNX\-\n]>+ }
              rule sequence:sym<aa> { <[A..Z\*\-\n]>+ }
    }

Perl 6 对regex 的书写规则做了些改动,而 Regexp::Grammars 是对Perl 5 regex 的扩展,
所以,需要做一些改动。为了简便,我写的Perl 5 代码,并不与Perl 6 Advent 中Perl 6代码完全等价(只要稍加改动就可以了)。

Perl 5 中大致如此,

    my $parser = do {
        use Regexp::Grammars;
        qr/
        <TOP>
        <nocontext:>
        <token: TOP>  <[record]>+
        <token: record> <.start=(\>)><id><comment>?\n<sequence>
        <token: id>  [^\-\s\n]+
        <token: comment> \s[^\n]+
        <token: sequence> <dna>|<rna>|<aa>
        <token: dna> [ACGTRYKMSWBDHVNX\-\n]+
        <token: rna> [ACGURYKMSWBDHVNX\-\n]+
        <token: aa> [A-Z\*\-\n]+
        /;
    };

Perl 6 的写法如果有兴趣,可以研究一下,我很久没有摸过了。这里主要聊Perl 5。

`use Regexp::Grammars` 的作用是 overload qr, 这也是为什么,不把 这一行移到文件开始部分的原因,否则,会overload 整个文件的qr。

qr 中 <TOP> 那一行, TOP 表示真个 grammar 的 pattern,放在 <> 中,表示调用这个pattern,所以,在后面一定要有
TOP 的定义。

定义一个pattern 用 token 或者rule,两者的主要区别在于对于whilespace 的处理。这跟Perl 6 中rule 和 token 有很大的不同。


一个包含fasta 条目的文件,有很多 条目,TOP 匹配所以的条目,你可能猜出来了 <[record]>+ 表示 匹配多个 record,
然后,只需要定义 record 就好了。 record 包括 id, comment 和 sequence。 sequence 要么是 DNA, RNA 或者 蛋白质。


不知道我有没有表达清楚,看看代码吧。

    use v5.20;
    use DDP;

    my $fasta = <<'END';
    >hello
    GCTATATAAGC
    >world prot
    TATAKEKEKELKL
    END

    my $parser = do {
        use Regexp::Grammars;
        qr/
        <TOP>
        <nocontext:>
        <token: TOP>  <[record]>+
        <token: record> <.start=(\>)><id><comment>?\n<sequence>
        <token: id>  [^\-\s\n]+
        <token: comment> \s[^\n]+
        <token: sequence> <dna>|<rna>|<aa>
        <token: dna> [ACGTRYKMSWBDHVNX\-\n]+
        <token: rna> [ACGURYKMSWBDHVNX\-\n]+
        <token: aa> [A-Z\*\-\n]+
        /;
    };

    if ( $fasta =~ $parser ) {
        p %/;
    }

    __END__
    {
      TOP   {
        record   [
         [0] {
      id         "hello",
      sequence   {
                         dna   "GCTATATAAGC
    "
      }
     },
         [1] {
      comment    " prot",
      id         "world",
      sequence   {
        dna   "TATAK"
      }
     }
        ]
      }
    }


文件的末尾是结果。

### 解析结果处理

解析得到的是Perl的数据结果,这样就基本达到了目的,但是仍有改进的空间。

因为每个条目是代表一个sequence, 包括 id, comment 和 sequence, 很自然的, 可以定义如下Seq class。


    package Seq {
    use Moo;
    has ['id', 'comment', 'sequence' ] => ( is => 'rw');
    1;
    }

如Perl 6 中的 actions, Regexp::Grammars 也可以有。

    use v5.20;
    use DDP;

    package Seq {
        use Moo;
        has [ 'id', 'comment', 'sequence' ] => ( is => 'rw' );
        1;
    }

    package FASTA {
        use Moo;

        my $parser = do {
            use Regexp::Grammars;
            qr/
        <TOP>
        <nocontext:>
        <token: TOP>  <[record]>+
        <token: record> <.start=(\>)><id><comment>?\n<sequence>
        <token: id>  [^\-\s\n]+
        <token: comment> \s[^\n]+
        <token: sequence> <dna>|<rna>|<aa>
        <token: dna> [ACGTRYKMSWBDHVNX\-\n]+
        <token: rna> [ACGURYKMSWBDHVNX\-\n]+
        <token: aa> [A-Z\*\-\n]+
        /;
        };

        has 'parser' => ( is => 'ro', default => sub { $parser } );

        sub record {
            my ( $self, $result ) = @_;
            return Seq->new(%$result);
        }

    }

    my $content = <<'END';
    >hello
    GCTATATAAGC
    >world prot
    TATAKEKEKELKL
    END
    my $fasta = FASTA->new();
    if ( $content =~ $fasta->parser->with_actions($fasta) ) {
        p %/;
    }

    __END__
    {
    TOP   {
      record   [
       [0] Seq  {
         Parents       Moo::Object
    public methods (4) : comment, id, new, sequence
    private methods (0)
         internals: {
     id         "hello",
       sequence   {
                               dna   "GCTATATAAGC
    "
     }
     }
       },
       [1] Seq  {
         Parents       Moo::Object
    public methods (4) : comment, id, new, sequence
    private methods (0)
         internals: {
     comment    " prot",
       id         "world",
       sequence   {
         dna   "TATAK"
       }
     }
       }
      ]
     }
    }


解析的结果是 Seq object,大致的过程是,当record 匹配以后,如果有actions 并且,actions 有record method,
那么,这个record method就会被调用。第二个参数是匹配的结果 result hash ref。请参考 record method。
这里为了方便,把record method 和 parser 放到了一个 namespace,这个并不是必须。

### 更OOP 的方式

以上差不多和 Perl6 的写法等价。 Regexp::Grammar 有更 OOP的简便写法。

    use v5.20;
    use DDP;

    package Seq {
        use Moo;
        has [ 'id', 'comment', 'sequence' ] => ( is => 'rw' );
        1;
    }

    my $parser = do {
        use Regexp::Grammars;
        qr/
        <TOP>
        <nocontext:>
        <token: TOP>  <[record]>+
        <objtoken: Seq=record> <.start=(\>)><id><comment>?\n<sequence>
        <token: id>  [^\-\s\n]+
        <token: comment> \s[^\n]+
        <token: sequence> <dna>|<rna>|<aa>
        <token: dna> [ACGTRYKMSWBDHVNX\-\n]+
        <token: rna> [ACGURYKMSWBDHVNX\-\n]+
        <token: aa> [A-Z\*\-\n]+
        /;
    };

    my $content = <<'END';
    >hello
    GCTATATAAGC
    >world prot
    TATAKEKEKELKL
    END

    if ( $content =~ $parser ) {
        p %/;
    }


这个结果与上一段代码是等价的。
注意 objtoken 那一行,

这个是文档中对objtoken的定义。

    <objtoken: CLASS= NAME>  Define token that blesses return-hash into class


其实 FASTA::record 的作用就是bless result hash 为 Seq object。 objtoken 相当于提供了一个简便写法。

## 后记

本文中的三段代码都是可以直接运行的。虽然并不一个完整的FASTA 文件 Parser。需要做些细节的改动,如果你手头有FATSTA 格式的文件,不妨再文中代码基础上,做改进。主要是要对 dan,rna 和 aa 加 action。

另外,Regexp::Grammars 有50多页的文档,本文最主要的没有提到的可能是Grammar 也可以继承的。

### 作者


--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

Re: [PerlChina] 发起两个学习小组:大数据和TDD

有兴趣的人如何参加?

Sent from my iPhone

On 2014年12月16日, at 上午11:14, blackjack xu <blackjack.xu@gmail.com> wrote:

在上周日上海Perl聚会后,考虑发起两个学习小组:大数据和TDD来活跃和丰富Perl的应用领域。
大数据小组主要分支一是学习PDL ,专注于 计算可视化 ,即数据挖掘,2D/3D演示。主要应用场景是 生物医药统计和计算,金融证券数据分析,电商CRM客户行为建模和挖掘。是不是非常高大上。
大数据小组的另一分支是海量日志收集和分析系统,包括分布式计算和存储。


TDD(即Test-Driven Development )小组主要专注这样开发模式的应用和普及,比如基于Test:Mojo开发网站压力和安全测试,以及普及Perl测试技术和流程。

欢迎有兴趣的Monks来邮件交流和参加对应的小组共同学习和实践。

Xu.hejun@163.com

--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

2014年12月15日星期一

Re: [PerlChina] 发起两个学习小组:大数据和TDD

考虑下将代码放到 github https://github.com/PerlChina? 需要 owner 权限可以找我。

Thanks
On Tue Dec 16 2014 at 11:14:26 AM blackjack xu <blackjack.xu@gmail.com> wrote:
在上周日上海Perl聚会后,考虑发起两个学习小组:大数据和TDD来活跃和丰富Perl的应用领域。
大数据小组主要分支一是学习PDL ,专注于 计算可视化 ,即数据挖掘,2D/3D演示。主要应用场景是 生物医药统计和计算,金融证券数据分析,电商CRM客户行为建模和挖掘。是不是非常高大上。
大数据小组的另一分支是海量日志收集和分析系统,包括分布式计算和存储。


TDD(即Test-Driven Development )小组主要专注这样开发模式的应用和普及,比如基于Test:Mojo开发网站压力和安全测试,以及普及Perl测试技术和流程。

欢迎有兴趣的Monks来邮件交流和参加对应的小组共同学习和实践。

Xu.hejun@163.com

--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

[PerlChina] 发起两个学习小组:大数据和TDD

在上周日上海Perl聚会后,考虑发起两个学习小组:大数据和TDD来活跃和丰富Perl的应用领域。
大数据小组主要分支一是学习PDL ,专注于 计算可视化 ,即数据挖掘,2D/3D演示。主要应用场景是 生物医药统计和计算,金融证券数据分析,电商CRM客户行为建模和挖掘。是不是非常高大上。
大数据小组的另一分支是海量日志收集和分析系统,包括分布式计算和存储。


TDD(即Test-Driven Development )小组主要专注这样开发模式的应用和普及,比如基于Test:Mojo开发网站压力和安全测试,以及普及Perl测试技术和流程。

欢迎有兴趣的Monks来邮件交流和参加对应的小组共同学习和实践。

Xu.hejun@163.com

--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

Re: [PerlChina] PerlChina Advent 15: Devel::DidYouMean

cool

On 2014/12/15 16:12, chenlin rao wrote:
# Devel::DidYouMean

我们总是期望程序越智能越好。在 Perl6 里,有一系列的"友好"的提示信息,帮助从 Perl5 程序员转移自己的语法习惯。其中一个功能,叫 **Did You Mean?**。如下:

    $ perl6 -e 'sya "ok"'
    ===SORRY!=== Error while compiling -e
    Undeclared routine:
        sya used at line 1. Did you mean 'say'?

现在,我们在 Perl5 里也可以做到同样的事情。这就是 [Devel::DidYouMean](https://metacpan.org/pod/Devel::DidYouMean) 模块。示例如下:

    $ perl -MDevel::DidYouMean -e 'printX("test")'
    Undefined subroutine &main::printX called at -e line 1.
    Did you mean print, printf?

当然,除了系统内置指令,对程序自己定义的函数,也可以做到相应的处理。比如下面这样:

    use 5.010;
    use Devel::DidYouMean;
    use Try::Tiny;

    sub testsay {
        say @_;
    }
    try {
        testsayx("catch me"); # boom
    } catch {
        my $error_msg = $_;
        $Devel::DidYouMean::DYM_MATCHING_SUBS->[0]->("I'm here");
    }

没错,最终我们成功得到了 "I'm here" 的输出。

--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

[PerlChina] PerlChina Advent 15: Devel::DidYouMean

# Devel::DidYouMean

我们总是期望程序越智能越好。在 Perl6 里,有一系列的"友好"的提示信息,帮助从 Perl5 程序员转移自己的语法习惯。其中一个功能,叫 **Did You Mean?**。如下:

    $ perl6 -e 'sya "ok"'
    ===SORRY!=== Error while compiling -e
    Undeclared routine:
        sya used at line 1. Did you mean 'say'?

现在,我们在 Perl5 里也可以做到同样的事情。这就是 [Devel::DidYouMean](https://metacpan.org/pod/Devel::DidYouMean) 模块。示例如下:

    $ perl -MDevel::DidYouMean -e 'printX("test")'
    Undefined subroutine &main::printX called at -e line 1.
    Did you mean print, printf?

当然,除了系统内置指令,对程序自己定义的函数,也可以做到相应的处理。比如下面这样:

    use 5.010;
    use Devel::DidYouMean;
    use Try::Tiny;

    sub testsay {
        say @_;
    }
    try {
        testsayx("catch me"); # boom
    } catch {
        my $error_msg = $_;
        $Devel::DidYouMean::DYM_MATCHING_SUBS->[0]->("I'm here");
    }

没错,最终我们成功得到了 "I'm here" 的输出。

--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

2014年12月14日星期日

Re: [PerlChina] PerlChina Advent 14: Mojolicious+socket.io+Angular.JS


在 2014年12月14日 下午12:25,Fayland Lam <fayland@gmail.com>写道:
贴个源码 git 吧 好clone直接测试

On 2014年12月14日周日 上午10:53 chenlin rao <rao.chenlin@gmail.com> wrote:
# Mojolicious + SocketIO + AngularJS

今天本来是准备随便写点跟 Web 开发相关的话题,毕竟今年 YAPC 上最大的声音就是 SaywerX 的["CGI.pm must DIE!"](www.youtube.com/watch?v=tu6_3fZbWYw)。正在犹豫是介绍 [Mojolicious](https://metacpan.org/pod/Mojolicious) 还是 [PocketIO](https://metacpan.org/pod/PocketIO) 模块的时候,偶然在 gist 上看到一个单文件程序,结合了 Mojolicious、socket.io 和 angular.js 三大框架,简直就是任性。那么好,今天就拿这个做例子说一说好了:

## Mojolicious::Lite 的 DSL

因为是单文件程序,所以用的是 Mojolicious::Lite,这个提供了一些很方便的类 sinatra 的 DSL。常见的情况是 `get '/' => sub($c) { $c->render('index') }` 这样。如果一个 url 路径同时可能有 GET 或者 POST 请求,那么就用 `any` 指令。这里就是:

    use Mojolicious::Lite;
    use Protocol::SocketIO::Message;
    use Protocol::SocketIO::Handshake;
    use Class::Date;

    any '/' => 'index';
    any '/view1' => 'index';
    any '/view2' => 'index';

    any '/partials/:name' => sub {
        shift->render( $self->param('name') );
    };

在 url 路径这里可以做捕获,方便在控制器函数里处理。这里用到了 `/socket.io/` 这个路径。这是 socket.io 协议规定的固定路径。

    any '/socket.io/:id/' => sub {

渲染方法支持很多种方式。默认写法的话,会自动去找同名的 `.html.ep` 模板来渲染 —— 这种情况更简写的方式就是前面已经看到的 `any '/' => 'index'`,其实就是去找 `index.html.ep` 文件。

如果写 API ,很多时候返回的并不是 HTML 内容,Mojolicious 也支持渲染其他格式的响应。最常见的是 `->render(json => $ref)` 这样。不过这里 socket.io 因为有单独的协议,所以是用 Protocol::SocketIO(这个包就是出自原先我打算讲的 PocketIO 模块) 来单独生成响应。

        shift->render( text=> Protocol::SocketIO::Handshake->new(
            session_id        => 1234567890,
            heartbeat_timeout => 10,
            close_timeout     => 15,
            transports        => [qw/websocket xhr-polling/]
        ) );
    };

普通的 web 方法在 Mojolicious 里大致就是这样。下面进入更高级的异步交互环节了!

我们这里示例的是一个每秒自动更新时间的页面。所以 Mojolicious 要定时执行。`Mojo::IOLoop->delay` 是个完全值得单独讲一次的好东西:

    my $clients = {};

    my $delay = Mojo::IOLoop->delay;
    Mojo::IOLoop->recurring( 1 => sub {
        for my $id( keys %$clients ) {
            # send name
            $clients->{$id}->send( Protocol::SocketIO::Message->new( type => 'event', data=>{ name=>'send:name', args=>[{ name=>'Jamie '.$id }] }) );
            # send time
            $clients->{$id}->send( Protocol::SocketIO::Message->new( type => 'event', data=>{ name=>'send:time', args=>[{ time=>''.Class::Date->now }] }) );
        }
    });

Mojolicious 本身直接指示 websocket 协议。所以用起来 DSL 跟做 GET/POST 是一样的。而控制器函数里用法也跟 socket.io 的写法有些类似。采用 `$self->on(message => sub {})`。

在控制器里,Mojolicious 允许直接操作整个请求响应的事务主体,也就是代码中的 `$self->tx` 。在异步处理的时候,肯定会需要直接操作 tx,这里作为一个纯粹的定时器,就只需要 send 了:

    websocket '/socket.io/:id/websocket/:oid' => sub {
        my $self = shift;

        app->log->debug(sprintf 'Client connected: %s', $self->tx);
        my $id = sprintf "%s", $self->tx;
        $clients->{$id} = $self->tx;

        $self->tx->send( Protocol::SocketIO::Message->new( type => 'connect') );
        $self->tx->send( Protocol::SocketIO::Message->new( type => 'event', data=>{ name=>'send:name', args=>[{ name=>'Jamie starting...' }] }) );
        $self->on(message => sub {
            my ($self, $msg) = @_;
            # no messages are being sent for now
        });
        $self->on(finish => sub {
            app->log->debug('Client disconnected');
            delete $clients->{$id};
        });
    };

最后,调用 start 方法开始运行服务器:

    app->start;

## AngularJS 示例

Mojolicious::Lite 支持在 Perl 的 `__DATA__` 里直接写页面内容,每个页面以 @@开头命名即可:

    __DATA__

    @@index.html.ep

### angular 的模板和变量绑定

angular 深度的改造了 HTML 的样式和书写方式,在前端的层面上提供 MVC 功能。在使用 `ng-app` 标记整个页面归属的具体 angular 应用后,可以利用 `ng-controller` 指令将一个 div 关联到一个 angular 的控制器上。在这个 div 内,可以通过 `{{ }}` 语法加载控制器函数里的变量,可以渲染带有 `ng-view` 指令的 div 作为 HTML 内容展示。

    <!DOCTYPE html>
    <html ng-app="myApp">
    <head>
    <meta charset="utf8">
    <base href="/">
    <title>Angular Socket.io Seed App</title>
    <link rel="stylesheet" href="app.css">
    </head>
    <body>
        <div ng-controller="AppCtrl">
            <h2>Helloo {{name}}</h2>
            <ul class="menu">
                <li><a href="view1">view1</a></li>
                <li><a href="view2">view2</a></li>
            </ul>
            <div ng-view></div>
        <div>
            Angular Socket.io seed app: v<span app-version></span></div>
        </div>

        <script src="socket.js"> </script>
        <script src="app.js"> </script>
    </body>
    </html>

    @@partial1.html.ep
    <p>This is the partial for view 1.</p><p>The current time is {{time}}</p>

    @@partial2.html.ep
    <p>This is the partial for view 2.</p><p>Showing of 'interpolate' filter:
    {{ 'Current version is v%VERSION%.' | interpolate }}</p>

### angular 的应用模块

上面页面部分就完成了。下面就开始在 js 中完成这个 angular 应用。我们前面已经看到,这个页面归属的应用名字叫 **myApp**。下面是应用的代码:

    @@app.js
    'use strict';

    // Declare app level module which depends on filters, and services
    var app = angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives']).
      config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
        $routeProvider.when('/view1', {templateUrl: 'partials/partial1', controller: MyCtrl1});
        $routeProvider.when('/view2', {templateUrl: 'partials/partial2', controller: MyCtrl2});
        $routeProvider.otherwise({redirectTo: '/view1'});
        $locationProvider.html5Mode(true);
      }]);

非常清晰,实现这个 myApp,加载了对应的 filters、services、directives 模块,然后定义了两个路由规则,分别指向两个不同的模板和控制器。

### angular 的控制器

angular 的控制器,最主要的作用就是处理应用作用域 `$scope` 与其他各种数据的关联。在这个示例里,就是把前面模板里的变量,跟 socket.io 从服务器拿到的数据关联在一起:

    function AppCtrl($scope, socket) {
      socket.on('send:name', function (data) {
        $scope.name = data.name;
      });
    }

    function MyCtrl1($scope, socket) {
      socket.on('send:time', function (data) {
        $scope.time = data.time;
      });
    }
    MyCtrl1.$inject = ['$scope', 'socket'];

    function MyCtrl2() {
    }
    MyCtrl2.$inject = [];

### angular 的指令

angular 的指令的作用,就是实际操作、修改变更应用中的数据,包括可能页面元素的变化等等。一般的 Web 开发中,这个事情是交给 jQuery 来操作 DOM 的。而在 angular 里。编写成指令,可以直接写成 HTML 元素的属性,看起来非常清爽。比如这个示例中就是生成了一个 `appVersion` 指令,在前面的 HTML 里,用在了一个 span 元素上。

    angular.module('myApp.directives', []).
      directive('appVersion', ['version', function(version) {
        return function(scope, elm, attrs) {
          elm.text(version);
        };
      }]);

### angular 的过滤器

angular 的过滤器,可以利用管道的方式帮助模板中的变量达到更好的渲染效果,默认提供有 date、json、limitTo、orderBy、number、lowercase、uppercase 等几个过滤器。也可以自己写新的过滤器。过滤器函数很简单,传一个参数返回一个结果即可:

    angular.module('myApp.filters', []).
      filter('interpolate', ['version', function(version) {
        return function(text) {
          return String(text).replace(/\%VERSION\%/mg, version);
        }
      }]);

### angular 的服务和工厂

augular 利用服务(service)或者工厂(factory)的方式来提供整个应用里,不同路由或者说控制器之间共用的单例变量。这二者的不同在于:service 其实就是一种不导入其他变量的简单 factory 的简写。比如下面这段代码是原程序中用 factory 写的,虽然名叫 myApp.services:

    // Demonstrate how to register services
    // In this case it is a simple value service.
    angular.module('myApp.services', []).
      value('version', '0.1').
      factory('socket', function ($rootScope) {
        var socket = io.connect();
        return {
          on: function (eventName, callback) {
            socket.on(eventName, function () {
              var args = arguments;
              $rootScope.$apply(function () {
                callback.apply(socket, args);
              });
            });
          },
          emit: function (eventName, data, callback) {
            socket.emit(eventName, data, function () {
              var args = arguments;
              $rootScope.$apply(function () {
                if (callback) {
                  callback.apply(socket, args);
                }
              });
            })
          }
        };
      });

可以看到就没有导入其他东西,所以可以写成服务,代码应该是下面这样:

    angular.module('myApp.services', []).
      value('version', '0.1').
      service('socket', function ($rootScope) {
        var socket = io.connect();
        this.on = function (eventName, callback) {
          ...
        };
        this.emit = function (eventName, data, callback) {
          ...
        };
      });

## socket.io 接口

上面 angular 服务里,就演示了 socket.io 的客户端用法。可以看到,其实客户端的接口跟服务器端是一一对应的。

示例程序再往下就是纯粹的js和css文件,这就不再继续了。完整代码源地址见:<https://gist.github.com/rodrigolive/5546320>

## 警告

socket.io 的客户端 js 在本例中还是用的去年的 0.90 的版本。最近半年 socket.io 项目发生重大改变,从 1.0 开始,有了自己专门的协议分析库 engine.io,整个 url 路径和 handshake 编解码方式都不太一样了。在 Perl 方面,PocketIO 作者没时间跟进这个变化,所以 Protocol::SocketIO 还是只能支持 0.90 的版本。

--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout

--
您收到此邮件是因为您订阅了Google网上论坛上的"PerlChina Mongers 讨论组"群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到perlchina+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至perlchina@googlegroups.com
访问此群组:http://groups.google.com/group/perlchina
要查看更多选项,请访问https://groups.google.com/d/optout