# $Id$
# $Source$
# $Author$
# $HeadURL$
# $Revision$
# $Date$
package design::keywords;

# -------------------------------------------------------------------------- #
#                   Dot 2.0 Design Specification.
# -------------------------------------------------------------------------- #

1;

__END__

=begin wikidoc

= NAME

design::keywords    Dot 2.0 Keywords Specification

= SYNOPSIS

    use Dot;

= ATTRIBUTES

== The 'has' keyword.
=for implementation_status implemented

An attribute with no type, a get and a set accessor is described like this:

    has foo => ();

or

    has 'foo';

This is an attribute with the default type {Any}.
You can also define it with the "yada yada yada" type:

    has 'foo' => (isa => "...");

which is syntactic sugar for saying the type is not important,
or just not known yet, choose your interpretation :)

You can use an attribute after it is declared like this:

    $self->foo                  # Get foo's value
    $self->set_foo($value)      # Set foo to a new value.

== Changing accessor names.
=for implementation_status implemented

Yes, it is possible to change the names of these accessors, or use mutators
instead. The hard way is to set the class options {-getter_prefix} and {-setter_prefix}
manually, the easy way is to use policies. Policies are small modules that
just change the way Dot works by changing class options or defining new
types.

This is an example using the Affordance policy, which is a policy that gets
you the accessor names reccommended by Perl Best Practices.

    package MyClass2;
    use Dot::Policy::Affordance;
   
    has foo => (isa => '...');

    my $self = MyClass2->new();

    $self->get_foo();
    $self->set_foo("new value");

And here's an example using the Mutator policy, which gives you only one
accessor to do both get and set operations.

    package MyClass3;
    use Dot::Policy::Mutator;

    has foo => (isa => '...');
     
    my $self = MyClass3->new();

    # Get and set with the same method
    my $value = $self->foo();
    $self->foo("new value");

== Attribute Privacy.
=for implementation_status implemented

Attribute privacy is specified with the {is} option:

    has 'foo'   => (is => 'rw') # rw == set and get accessor.
    has 'foo'   => (is => 'ro') # ro == only get accessor.
    has 'foo'   => (is => 'wo') # wo == only set accessor.
    has 'foo'   => (is => 'xx') # xx == private, no autogenerated accessors.

If you don't want to create accessors for the method
you can including setting {is} to {xx} also prefix the attribute name with an exclamation
point ({!}). This is a convenient shortcut and it resembles how it works in Perl 6.

    has '!foo'  => (isa => 'String');

== Attribute types and default values.

You can specify which type the attribute is with the {isa} option,
and you can set default values with the {default} option.

    has 'foo'   => (isa => 'String');
    has 'bar'   => (isa => 'String', default => 'The quick fox');

Different types has different ways of handling the default
values you give.

=== More about default values.
=for implementation_status implemented

All default values are lazy and not actually set before
they are accessed using the get accessor (or the {__getattr__} method
of all Dot classes, but more on that later).

    has 'name'   => (isa => 'String', default => 'Cosmo Kramer');

    my $myobj = MyClass->new();
    $myobj->name(); # <-- default value first initialized here!

If it's set before that, like when it's set by the user via {new()}, it's
never initialized at all:

    my $myobj = MyClass->new({name => 'Jerry Seinfeld'})

    $myobj->name(); # <-- value is now {Jerry Seinfeld}, 
                    # so the default value was never set.
                    

== {-defaults}: Assigning list defaults by using defaults with prefix {-}
=for implementation_status todo default_list_flattening deadline => 2.0

Prefix {-} on the default option is special if {-default} is the last option.

So

    has 'list'  => (isa => 'Array', -default => @array);

is the same as
    
    has 'list'  => (isa => 'Array', default => \@array);

But this won't work:

    has 'list'  => (-default => @array, isa => 'Hash');

You can even make the array inline:
    
    has 'list'  => (isa => 'Array', -default => (qw(foo bar baz)));

It also works for hashes:

    has 'dict'  => (isa => 'Hash',  -default => (
                                                 'next' => 1,
                                                 'prev' => 2,
                                                 'data' => 'jumps over dog',
    ));

= OBJECT CONSTRUCTION

# TODO WRITE SOMETHING ABOUT OBJECT CONSTRUCTION HERE

# BUILD etc.

= OBJECT DESTRUCTION

# TODO WRITE SOMETHING ABOUT OBJECT DESTRUCTION HERE

# DEMOLISH etc.

= INHERITANCE

Inheritance is specified with the extends keyword

    extends 'Foo';              # Single base class.
    extends qw(Foo Bar Xuzzy);  # Multiple inheritance.

=for implementation_status todo extends_append deadline => 2.0

By default {extends} overwrites the list of base classes,
but you can append classes if the first element is a plus sign ({+}):

    extends + => qw(Fubar::Base);

you can also write

    extends qw(+ Fubar::Base);

= COMPOSITION

An attribute type that has double colon ({::}) in it's name means composition.

    has model     => (isa => 'MyApp::Model');

Classes that ordinarily doesn't include double colon
can be described by tacking them at the end of the
class name.

    has master    => (isa => 'Catalyst::');

Can also say {composites} specifically

    composites 'view' => 'MyApp::View';

The class will be automaticly created once you access the accessor.
So you can do this if the {MyApp::Model} class has a {select()} method:

    my $self = MyClass->new();
    $self->model->select('user', {user_id => 10});

Of course, this is just a default value, so if the user gives his/her own
instance it won't' be overwritten.

    my $other_model = SomeOther::Model->new();
    my $myclass     = MyClass->new({ model => $other_model });

    $myclass->select('user', {user-id => 10}); # <--- uses SomeOther::Model

=for implementation_status todo composite_build_option deadline => 2.0
You can do object intialization on the class level by using the build option

    has model           => (isa     => 'MyApp::Model',
                            build   => {
                                database_name => 'mydb',
                            });

If you want to do dynamic object initialization, you have to do that
in your {BUILD} method. Say you want to initialize the {model} object
differently if the user provides his own database name, you can do that like
this:

    has model           => (isa => 'MyApp::Model');
    has database_name   => (isa => 'String');

    sub BUILD {
        my ($self, $opts_ref) = @_;

        # If the user provides a database name, and not a custom model
        # instance
        if (exists $opts_ref->{database_name) && !exists $opts_ref->{model}) {
            # Create the model based on the database name

=for implementation_status todo dot::meta::get_attribute_info deadline => 2.0
            # We get the class name from our metaclasss instead of just 
            # writing MyApp::Model->new, this lets us change the class name
            # only once. Of course this might be too much of an abstraction
            # for you, and you can just write MyApp::Model->new if you want,
            # and it's probably just as good. But for the examples sake... :)
            my $model_class = $self->dot::meta::get_attribute_info('model')->type;

            my $model = $model_class->new({
                database_name => $opts_ref->{database_name});
            });

            $self->set_model($model);
        }
            

= METACLASSES
=for implementation_status design_phase metaclass deadline => 2.0

All Dot classes is associated with a metaclass. You can even set
the metaclass for a class with the q{-metaclass} class option.

You can get accesss to your metaclass instance like this:

    my $metaclass = $self->meta;

The problem with this is that this only works with Dot classes because
Perl 5 does not have a standard metaclass interface (not even {meta} is
standard, but it is in Perl 6), so if you were trying to do something like
this

    sub decorate_class_with_exception_attribute {
        my ($self, $other_class) = @_;

        my $metaclass = $other_class->metaclass;

        $metaclass->define_attribute('exception', (isa => 'Object'));
        
        $other_class->set_exception( Exceptions->new() );

        return;
    }


it would blow up in your face as soon as it hit a class not using Dot.
That's why we have the {dot::meta} pseudoclass. The above can be rewritten
using the {dot::meta::has} method to work perfectly for all (hash based)
classes like this:

    use Carp 'confess';

    sub decorate_class_with_exception_attribute {
        my ($self, $other_class) = @_;

        # test instance for incompatibility.
        if (my $reason = $other_class->dot::meta::incompatible) {
            confess "Cannot decorate: $reason";
        }
 
        $other_class->dot::meta::has('exception' => (isa => 'Object')); 

        $other_class->set_exception( Exception->new() );

        return;
    }




= UTILITY METHODS

== The {Maybe} keyword

If you don't care wether a method exists or not you can use the {Maybe} keyword:

    my $return_value = Maybe { $self->close_map_file() }

{Maybe} will return undef if {close_map_file()} doesn't exist, or it's
return value if it does. It's something like the equivalent to:

    my $return_value;
    if ($self->has('close_map_file')) {
        $return_value = $self->close_map_file()
    }




=end wikidoc

# Local Variables:
#   mode: cperl
#   cperl-indent-level: 4
#   fill-column: 78
# End:
# vim: expandtab tabstop=4 shiftwidth=4 shiftround
