可插拔/动态数据处理/修改/转换perl模块?

时间:2010-11-17 17:25:21

标签: perl process transform pluggable data-munging

来自perlmonks的交叉发布:

我必须在$ work处清理一些粗略的古代代码, 在我尝试制作新模块之前,如果有人知道某些合适的话,我会喜欢使用现有模块。

在运行时,我正在解析一个文件,以确定我需要对一组数据进行哪些处理。

如果我要编写一个模块,我会尝试更一般地(非DBI特定的),但我的确切用例是这样的:

我读取了一个SQL文件来确定要对数据库运行的查询。 我在顶部解析评论并确定

     
  • 列A需要应用s ///,
  •  
  • 列B需要转换为看似给定格式的日期,
  •  
  • C列得到一种tr ///.
  •  
  • 此外,可以链接一些内容,以便列D可能为///,然后说如果它不是1或2,则将其设置为3.

因此,当从db中获取时,程序会在返回数据之前应用各种(可能是堆叠的)转换。

目前,该代码是一个令人作呕的大而难的if子句系列 处理难以阅读或维护指令数组。

所以我想象的可能是一个解析这些线条的对象 (并另外暴露功能界面), 堆叠要应用的处理器列表, 然后能够在传递的数据上执行它。

可选地,可以有名称/类别选项, 这样一个对象就可以动态使用,只为给定的名称/类别/列堆栈处理器。

一个传统的人为例子:

$obj = $module->new();  
$obj->parse("-- greeting:gsub: /hi/hello"); # don't say "hi"  
$obj->parse("-- numbers:gsub: /\D//"); # digits only  
$obj->parse("-- numbers:exchange: 1,2,3 one,two,three"); # then spell out the numbers  
$obj->parse("-- when:date: %Y-%m-%d 08:00:00"); # format like a date, force to 8am  
$obj->stack(action => 'gsub', name => 'when', format => '/1995/1996/'); # my company does not recognize the year 1995.  

$cleaned = $obj->apply({greeting => "good morning", numbers => "t2", when => "2010116"});  

每个处理器(gsub,日期,交换)将是一个单独的子例程。 可以定义插件以按名称添加更多插件。

$obj->define("chew", \&CookieMonster::chew);  
$obj->parse("column:chew: 3x"); # chew the column 3 times  

所以显而易见的第一个问题是,是否有人知道我可以使用的模块? 到目前为止我唯一能找到的是[mod:// Hash :: Transform], 但是因为我将确定在运行时动态执行哪些处理 我总是最终使用“complex”选项,我仍然需要构建解析器/堆栈器。

是否有人知道任何类似的模块,甚至是我可能想要利用/包装的温和相关的模块?

如果没有任何通用的公共消费(当然我的不是黑暗中唯一的那个), 是否有人建议记住事项或接口建议甚至其他可能的用途 除了从DBI,Text :: CSV等返回数据?

如果我最终编写一个新模块,是否有人有命名空间建议? 我觉得Data ::下的东西可能是合适的...... “可插拔”一词不断浮现在脑海中,因为我的用例让我想起了PAM, 但我真的没有任何好主意......

     
  • Data :: Processor :: Pluggable?
  •  
  • Data :: Munging :: Configurable?
  •  
  • I :: Chew :: Data?

3 个答案:

答案 0 :(得分:0)

首先,如果可能的话,我会尝试在SQL查询中尽可能多地放置格式。 像日期格式等的东西肯定应该在SQL中处理。

我知道的一个模块可以用于你的目的是Data::FormValidator。尽管is主要用于验证CGI参数,但它具有您需要的功能:您可以定义过滤器和约束,并以各种方式链接它们。并不意味着没有其他模块可供您使用,我只是不知道。

或者你可以做一些你已经暗示的事情。您可以定义某种命令类并将它们链接到各种数据输入上。我会沿着这些方向做点什么:

package MyDataProcessor;

use Moose;
has 'Transformations' => (
    traits => ['Array'],
    is => 'rw',
    isa => 'ArrayRef[MyTransformer]',
    handles => {
        add_transformer => 'push',
    }
);

has 'input' => (is => 'rw', isa => 'Str');

sub apply_transforms {  }


package MyRegexTransformer;

use Moose;

extends 'MyTransformer';

has 'Regex' => (is => 'rw', isa => 'Str');
has 'Replacement' => (is => 'rw', isa => 'Str');

sub transform {  }

# some other transformers
#

# somewhere else
#
#

my $processor = MyDataProcessor->new(input => 'Hello transform me');

my $tr = MyRegexTransformer->new(Regex => 'Hello', Replacement => 'Hi');

$processor->add_transformer($tr);

#...

$processor->apply_transforms;

答案 1 :(得分:0)

我不知道任何数据转换CPAN模块,所以我不得不自己动手工作。它比这复杂得多,但是按照类似的原则运作;它本质上是一个穷人的Informatica风格的ETL实现没有花哨的GUI ...配置是Perl哈希(Perl而不是XML,因为它允许我实现某些复杂的规则作为子程序引用)。

就命名空间而言,我会选择Data::Transform::*

答案 2 :(得分:0)

感谢大家的想法。

简短版本: 在尝试调整一些现有模块后,我最终抽象出了我自己的:Sub :: Chain。 它需要一些工作,但到目前为止我正在做我需要的工作。

长版: (摘自POD)

= head1 RATIONALE

这个模块最初是Data :: Transform :: Named, 一个命名的包装器(如Sub :: Chain :: Named) Data :: Transform(特别是Data :: Transform :: Map)。

当模块接近完成时,我意识到我使用的很少 Data :: Transform(及其文档提示) 我可能不想使用我使用的唯一部分)。 我还发现输出并不总是我所期望的。 根据可能的目的,我认为这似乎是合理的 Data :: Transform,这个模块只需要不同。

所以我试图更抽象地思考 并意识到该模块的本质并不依赖于 数据转换,但仅仅是简单子程序调用的继承。

然后我发现并考虑了Sub :: Pipeline 但需要能够使用相同的 在单个链中使用不同参数的命名子例程, 所以我似乎更容易坚持我写的代码 然后重命名它并进一步抽象它。

我还研究了开始开发的Rule :: Engine 在我搜索的时候。 但是,像Data :: Transform一样,它似乎比我需要的更复杂。 当我看到Rule :: Engine正在使用[非常优秀的] Moose时 我决定通过,因为我在一些非常旧的机器上工作 旧发行版和旧的perls以及有限的资源。 再一次,它似乎比我想要的要多得多。

=切

至于我原来的想法/例子中的“解析”方法, 我没有发现这是必要的,目前我正在使用像

这样的语法

$chain->append($sub, \@arguments, \%options)