# -*- mode: perl; coding: utf-8 -*- package YATT::Class::Configurable; use strict; use warnings FATAL => qw(all); our %FIELDS; use fields; sub MY () {__PACKAGE__} use YATT::Util::Symbol qw(fields_hash globref); use Carp; sub new { my MY $self = fields::new(shift); $self->before_configure; if (@_) { $self->init(@_); } else { $self->after_configure; } $self } sub initargs {return} sub init { my MY $self = shift; if (my @member = $self->initargs) { @{$self}{@member} = splice @_, 0, scalar @member; } if (@_) { $self->configure(@_); } else { $self->after_configure; } $self; } sub refid { $_[0] + 0; } sub stringify { my MY $self = shift; require Data::Dumper; sprintf '%s->new(%s)', ref $self , join ", ", Data::Dumper->new ([map($self->{$_}, $self->initargs) , $self->configure])->Terse(1)->Indent(0)->Dump; } sub clone { my MY $ref = shift; ref($ref)->new(map($ref->{$_}, $ref->initargs) , $ref->configure , @_); } sub cget { (my MY $self, my ($cf)) = @_; $cf =~ s/^-//; # For Tcl/Tk co-operatability. my $fields = fields_hash($self); croak "Can't cget $cf" unless exists $fields->{"cf_$cf"}; $self->{"cf_$cf"}; } sub cgetlist { (my MY $self) = shift; map { if (exists $self->{"cf_$_"}) { ($_ => $self->{"cf_$_"}) } else { () } } @_; } sub before_configure {} sub configkeys { my MY $self = shift; return map { if (m/^cf_(.*)/) { $1 } else { () } } keys %$self; } sub can_configure { (my MY $self, my ($name)) = @_; my $fields = fields_hash($self); exists $fields->{"cf_$name"} || $self->can("configure_$name"); } sub configure { my MY $self = shift; my $fields = fields_hash($self); unless (@_) { # list all configurable options. return map { if (m/^cf_(.*)/) { ($1 => $self->{$_}) } else { () } } keys %$fields; } if (@_ == 1) { croak "No such config item: $_[0]" unless exists $fields->{"cf_$_[0]"}; return $self->{"cf_$_[0]"}; } if (@_ % 2) { croak "Odd number of arguments"; } my @task; while (my ($name, $value) = splice @_, 0, 2) { croak "undefined name for configure" unless defined $name; if (my $sub = $self->can("configure_$name")) { push @task, [$sub, $value]; } else { croak "No such config item: $name" unless exists $fields->{"cf_$name"}; $self->{"cf_$name"} = $value; } } foreach my $task (@task) { $task->[0]->($self, $task->[1]); } $self->after_configure; $self; } sub after_configure { my MY $self = shift; # $self->SUPER::after_configure; foreach my $cf (grep {/^cf_/} keys %{fields_hash($self)}) { next if defined $self->{$cf}; # XXX: should be: # (my $name = $cf) =~ s/^cf_//; # my $sub = $self->can("default_$name") or next; my $sub = $self->can("default_$cf") or next; $self->{$cf} = $sub->(); } } sub define { my ($class, $method, $sub) = @_; # XXX: API 以外の関数は弾くべきかもしれない。 *{globref($class, $method)} = $sub; } 1;