我有一个读取环境变量的类Configuration:
class Configuration { has $.config_string_a; has $.config_string_b; has Bool $.config_flag_c; method new() { sub assertHasEnv(Str $envVar) { die "environment variable $envVar must exist" unless %*ENV{$envVar}:exists; } assertHasEnv('CONFIG_STRING_A'); assertHasEnv('CONFIG_STRING_B'); assertHasEnv('CONFIG_FLAG_C'); return self.bless( config_string_a => %*ENV{'CONFIG_STRING_A'},config_string_b => %*ENV{'CONFIG_STRING_B'},config_flag_c => Bool(%*ENV{'CONFIG_FLAG_C'}),); } } my $config = Configuration.new; say $config.config_string_a; say $config.config_string_b; say $config.config_flag_c;
是否有更简洁的方式来表达这一点?例如,我在检查中重复环境变量名称和构造函数的返回值.
我可以很容易地看到编写另一个更通用的类,它封装了config参数的必要信息:
class ConfigurationParameter { has $.name; has $.envVarName; has Bool $.required; method new (:$name,:$envVarName,:$required = True) { return self.bless(:$name,:$required); } }
然后将它们滚动到Configuration类中的List中.但是,我不知道如何重构Configuration中的构造函数以适应这种情况.
解决方法
想到的最直接的变化是将新变为:
method new() { sub env(Str $envVar) { %*ENV{$envVar} // die "environment variable $envVar must exist" } return self.bless( config_string_a => env('CONFIG_STRING_A'),config_string_b => env('CONFIG_STRING_B'),config_flag_c => Bool(env('CONFIG_FLAG_C')),); }
虽然//是定义检查而不是存在检查,但环境变量未定义的唯一方法是它是否未设置.这可以归结为%* ENV和每个环境变量的提及.
如果只有少数几个,那么我可能会停在那里,但下一个重复的事情让我感到震惊的是,属性的名称只是环境变量名称的小写,所以我们也可以消除这种重复.更复杂的成本:
method new() { multi env(Str $envVar) { $envVar.lc => %*ENV{$envVar} // die "environment variable $envVar must exist" } multi env(Str $envVar,$type) { .key => $type(.value) given env($envVar) } return self.bless( |env('CONFIG_STRING_A'),|env('CONFIG_STRING_B'),|env('CONFIG_FLAG_C',Bool),); }
现在env返回一对,和|将其展平到参数列表中,就像它是一个命名参数一样.
最后,“电动工具”的方法是在课外写一个这样的特征:
multi trait_mod:<is>(Attribute $attr,:$from-env!) { my $env-name = $attr.name.substr(2).uc; $attr.set_build(-> | { with %*ENV{$env-name} -> $value { Any ~~ $attr.type ?? $value !! $attr.type()($value) } else { die "environment variable $env-name must exist" } }); }
然后把课程写成:
class Configuration { has $.config_string_a is from-env; has $.config_string_b is from-env; has Bool $.config_flag_c is from-env; }
特征在编译时运行,并且可以以各种方式操纵声明.此特征根据属性名称计算环境变量的名称(属性名称始终类似于$!config_string_a,因此是substr). set_build设置在创建类时将运行以初始化属性的代码.在我们的情况下,这会传递各种不重要的东西,所以我们忽略了|的参数. with就像定义一样,所以这与//之前的方法相同.最后,Any ~~ $attr.type检查是否以某种方式约束参数,如果是,则执行强制(通过使用值调用类型来完成).