NAME

Object::Simple - a simple class builder

VERSION

Version 2.1203

FEATURES

1. You can define accessors in a very simple way.
2. The "new()" method is already defined.
3. You can define various accessor option (default, type, chained, weak).
4. you can use a mixin system like Ruby's.

By using Object::Simple, you will be exempt from the bitter work of repeatedly writing the new() constructor and the accessors.

But I recommend now Object::Simple::Base than Object::Simple.

It is more Object Oriented, more simple and easier, and Mojo::Base compatible.

See Object::Simple::Base

SYNOPSIS

# Class definition( Book.pm )
package Book;
use Object::Simple;

sub title  : Attr {}
sub author : Attr {}
sub price  : Attr {}

Object::Simple->build_class; 
# End of module. Don't forget to call 'build_class' method

# Using class
use Book;
my $book = Book->new(title => 'a', author => 'b', price => 1000);

# Default
sub author  : Attr { default => 'Good new' }
sub persons : Attr { default => sub {['Ken', 'Taro']} }

# Build
sub title   : Attr { build => 'Good news' }
sub authors : Attr { build => sub{['Ken', 'Taro']} }

# Read only accessor
sub year   : Attr { read_only => 1 }

# Weak reference
sub parent : Attr { weak => 1 }

# Method chaine (default is true)
sub title  : Attr { chained => 1 }

# Variable type
sub authors : Attr { type => 'array' }
sub country : Attr { type => 'hash' }

# Convert to object
sub url : Attr { convert => 'URI' }
sub url : Attr { convert => sub{ ref $_[0] ? $_[0] : URI->new($_[0]) } }

# Dereference return value
sub authors    : Attr { type => 'array', deref => 1 }
sub country_id : Attr { type => 'hash',  deref => 1 }

# Trigger when value is set
sub error : Attr { trigger => sub{
    my $self = shift;
    $self->state('error');
}}
sub state : Attr {}

# Define accessor for class attriute
sub options : ClassAttr { type => 'array',  build => sub {[]} }

# Define accessor for both object attribute and class attribute
sub options : HybridAttr { type => 'array', build => sub {[]} }

# Inheritance
package Magazine;
use Object::Simple(base => 'Book');

# Mixin
package Book;
use Object::Simple( 
    mixins => [ 
        'Object::Simple::Mixin::AttrNames',
        'Object::Simple::Mixin::AttrOptions'
    ]
);

Methods

new

Object::Simple defines a 'new' method for the subclass, so you do not need to define 'new'. 'new' accepts a hash or a hash reference.

$book = Book->new(title => 'Good life', author => 'Ken', price => 200);
$book = Book->new({title => 'Good life', author => 'Ken', price => 200});

You can also override 'new' for finer-grained initialization or arranging the arguments.

The following is an initialization sample.

sub new {
    my $self = shift->SUPER::new(@_);
    
    $self->initialize;
    
    return $self;
}

sub initialize {
    my $self = shift;
    
    # write what you want
}

The following sample arranges the arguments differently:

   sub new {
       my ($class, $title, $author) = @_;
       
       # Arrange arguments
       my $self = $class->SUPER::new(title => $title, author => $author);
       
       return $self;
   }

build_class

You must call build_class at the end of the package. The class will be completely constructed by this invocation.

Object::Simple->build_class;

The following processes is excuted.

1. Inherit base class
2. Include mixin classes
3. Create accessors
4. Create constructor

You can specify class name if you need.

Object::Simple->build_class($class);

class_attrs

Get class attributes

$class_attrs = $class->class_attrs;

If you want to delete class attribute or check existents of class attribute You can use this method

delete $class->class_attrs->{title};
exists $class->class_attrs->{title};

call_super

Call method of super class.

$self->call_super('initialize');

You can call method of super class. but the method is not one of real super class. Method is searched by using the follwoing order.

  +-------------+
3 | BaseClass   | # real super class
  +-------------+
        |
  +-------------+
2 | MixinClass1 |
  +-------------+
        |
  +-------------+
1 | MixinClass2 |
  +-------------+
        |
  +-------------+
  | ThisClass   |
  +-------------+

If 'Mixin class2' has 'initialize' method, the method is called.

call_mixin

Call a method of mixined class

$self->call_mixin('MixinClass1', 'initialize');

You can call any method of mixin class, even if method is not imported to your class

mixin_methods

Get all same name methods of mixin classes

my $methods = $self->mixin_methods('initialize');

You can call all methods of mixined classes as the following way.

foreach my $method (@$methods) {
    $self->$method();
}

resist_accessor_info

You can resist accessor informations.

Object::Simple->resist_accessor_info($class, $accessor_name, 
                                     $accessor_options, $accessor_type);

The following is arguments sample

Object::Simple->resist_accessor_info('Book', 'title', {default => 1}, 'Attr');

This is equal to

package Book;
sub title : Attr {default => 1}

This method only resist accessor infomation. If you want to build class, you must call 'build_class'

Object::Simple->build_class('Book');

Accessor options

default

You can define attribute default value.

   sub title    : Attr {default => 'Good news'}

If you define default values using reference or Object, you need wrapping it by sub{}.

sub authors : Attr { default => sub{['Ken', 'Taro']} }

build

Create attribute when accessed first. Usage is almost same as 'default'.

sub title   : Attr { build => 'Good news' }
sub authors : Attr { build => sub{['Ken', 'Taro']} }

'build' subroutine receive $self. using other attribute, you can build this attribute.

sub title : Attr { build => sub {
    my $self = shift;
    return Some::Module->new($self->authors);
}}

read_only

You can create read only accessor

   sub title: Attr { read_only => 1 }

chained

Setter return value is self by default. So you can do method chain.

$book->title('aaa')->author('bbb')->...

If you do not use method chain,You do following.

sub title  : Attr { chained => 0 }
sub author : Attr { chained => 0 }

Setter retrun value is current value;

my $current_value = $book->title('aaa');

weak

attribute value is weak reference.

sub parent : Attr {weak => 1}

type

You can specify variable type( array, hash );

# variable type
sub authors    : Attr { type => 'array' }
sub country_id : Attr { type => 'hash' }

If you specity 'array', arguments is automatically converted to array reference

$book->authors('ken', 'taro'); # ('ken', 'taro') -> ['ken', 'taro']
$book->authors('ken');         # ('ken')         -> ['ken']

If you specity 'hash', arguments is automatically converted to hash reference

$book->country_id(Japan => 1); # (Japan => 1)    -> {Japan => 1}

convert

You can convert a non blessed scalar value to object.

sub url : Attr { convert => 'URI' }

$book->url('http://somehost'); # convert to URI->new('http://somehost')

You can also convert a scalar value using your convert function.

sub url : Attr { convert => sub{ ref $_[0] ? $_[0] : URI->new($_[0]) } }

deref

You can derefference returned value.You must specify it with 'type' option.

sub authors : Attr { type => 'array', deref => 1 }
sub country_id : Attr { type => 'hash',  deref => 1 }

my @authors = $book->authors;
my %country_id = $book->country_id;

trigger

You can defined trigger function when value is set.

sub error : Attr { trigger => sub{
    my ($self, $old) = @_;
    $self->state('error') if $self->error;
}}
sub state : Attr {}

trigger function recieve two argument.

1. $self
2. $old : old value

extend

You can extend super class accessor options. If you overwrite only default value, do the following

package BaseClass
use Object::Simple;
sub authors { default => sub {['taro', 'ken']}, array => 1, deref => 1 }

Object::Simple->build_class;

package SomeClass
use Object::Simple(base => 'BaseClass');
sub authors { extend => 1, default => sub {['peter', 'miki']} }

Object::Simple->build_class;

clone

This accessor options is only used when accessor type is 'ClassAttr', or 'HybridAttr'.

Clone Class attribute or Object attribute

sub method : HybridAttr {
    clone => $clone_method, build => $default_value }
}

Sample

sub constraints : HibridAttr { clone => 'hash', build => sub {{}} }

If 'clone' option is specified and when access this attribute, super class value is cloned when invocant is class and class attribute is cloned when invacant is object

'clone' option must be specified.The following is clone options

The following is clone options

1. 'scalar'     # Normal copy
2. 'array'      # array ref shallow copy : sub{ [@{shift}] }
3. 'hash'       # hash ref shallow copy  : sub{ {%{shift}} }
4. code ref     # your clone method, for exsample : 
                #   sub { shift->clone }

Samples

clone => 'scalar'
clone => 'array'
clone => 'hash'
clone => sub { shift->clone }

Special accessors

ClassAttr - Accessor for class variable

You can also define accessor for class variable.

sub options : ClassAttr { type => 'array', build => sub {[]} }

options set or get class variable, not some instance.

you can use the same accessor options as normal accessor except 'default' option.

If you define default value to class variable, you must use 'build' option.

If this accessor is used subclass, it access subclass class variable, not the class it is defined.

HybridAttr - Accessor for object or class variable

You can define object or class hibrid accessor.

If you invoke method from object, data is saved into object

$obj->m1('a'); # save into object

If you invoke method from class, data is saved into class

$class->m1('a'); # save into class

This is very useful.

Inheritance

   # Inheritance
   package Magazine;
   use Object::Simple( base => 'Book' );

Object::Simple do not support multiple inheritance because it is so complex.

Mixin

Object::Simple support mixin syntax

   # Mixin
   package Book;
   use Object::Simple( 
       mixins => [ 
           'Object::Simple::Mixin::AttrNames',
           'Object::Simple::Mixin::AttrOptions'
       ]
   );

Object::Simple mixin merge mixin class attribute.

# mixin class
package Some::Mixin;
use Object::Simple;

sub m2 : Attr {}

Object::Simple->build_class;

# using mixin class
package Some::Class;
use Object::Simple( mixins => [ 'Some::Mixin' ] );

sub m1 : Attr {}

Object::Simple->build_class;

Because Some::Mixin is mixined, Some::Class has two attribute m1 and m2.

Method searching order

Method searching order is like Ruby.

If method names is crashed, method search order is the following

1. This class

2. Mixin class2

3. Mixin class1

4. Base class

  +--------------+
4 | Base class   |
  +--------------+
        |
  +--------------+
3 | Mixin class1 |
  +--------------+
        |
  +--------------+
2 | Mixin class2 |
  +--------------+
        |
  +--------------+
1 | This class   |
  +--------------+

 #       1
 package ThisClass;
 #                       4                       3              2
 Object::Simple(base => 'BaseClass', mixins => ['MixinClass1', 'MixinClass2']);

Using your MODIFY_CODE_ATTRIBUTES subroutine

Object::Simple define own MODIFY_CODE_ATTRIBUTES subroutine. If you use your MODIFY_CODE_ATTRIBUTES subroutine, do 'no Object::Simple;'

package T19;
use Object::Simple;

sub m1 : Attr {}

no Object::Simple; # unimport MODIFY_CODE_ATTRIBUTES

# defined MODIFY_CODE_ATTRIBUTES
sub MODIFY_CODE_ATTRIBUTES {
    my ($class, $ref, @attrs) = @_;
    # do what you want
    return;
}

sub m2 : YourAttribute {}

Object::Simple->build_class;

Internal

CLASS_INFOS package variable

$CLASS_INFOS data structure
$class base             $base
       mixins           [$mixin1, $mixin2]
       mixin            $mixin  methods  $method
       methods          $method derive
       class_attrs
       constructor      $constructor
       
       accessors        $accessor   type     $type
                                    options  {default => $default, ..}
       
       marged_accessors $accessor   type     $type
                                    options  {default => $default, ..}

This variable structure will be change. so You shoud not access this variable. Please only use to undarstand Object::Simple well.

Object::Simple sample

The following modules use Object::Simple. it will be Good sample.

You can create custamizable module easy way.

Validator::Custom, DBIx::Custom

Discoraged options and methods

The following options and methods are discuraged now.

Do not use these. This will be removed in future.

Translate accessor option

Translate accessor is discuraged now, because it is a little complex for reader

sub attr : Translate { target => 'aaa' }

Output accessor options

Output accessor is discuraged now, because it is a little complex for reader

sub attr : Output { target => 'aaa' }

exists_class_attr methods

Check existence of class attribute

$class->exists_class_attr($attr);

This is discuraged now. instead, you write this

exists $class->class_attrs->{$attr};

auto_build

'auto_build' options is now discoraged.

Instead of 'auto_build', use 'build'. 'build' is more simple than 'auto_build'.

sub author : Attr { auto_build => sub { shift->author(Some::Module->new) } }

sub author : Attr { build => sub { Some::Module->new } }

The following is original document.

When accessor is called first,a methods is called to build attribute.

   sub author : Attr { auto_build => 1 }
   sub build_author{
       my $self = shift;
       $self->atuhor( Person->new );
   }

Builder method name is build_ATTRIBUTE_NAME by default;

You can specify build method .

sub author : Attr { auto_build => 1 }
sub create_author{
    my $self = shift;
    $self->atuhor( Person->new );
}

initialize

is now discoraged. instead of 'initialize', use 'clone'.

The following is original document.

This accessor options is only used when accessor type is 'ClassAttr', or 'ClassObjectAttr'.

Initialize Class attribute or Object attribute

sub method : ClassObjectAttr {
    initialize => {clone => $clone_method, default => $default_value }
}

Sample

sub constraints : ClassObjectAttr {
    initialize => {clone => 'hash', default => sub { {} } }; 
}

If 'initialize' option is specified and when access this attribute, super class value is cloned when invocant is class and class attribute is cloned when invacant is object

'clone' option must be specified.The following is clone options

The following is clone options

1. 'scalar'     # Normal copy
2. 'array'      # array ref shallow copy : sub{ [@{shift}] }
3. 'hash'       # hash ref shallow copy  : sub{ {%{shift}} }
4. code ref     # your clone method, for exsample : 
                #   sub { shift->clone }

Samples

clone => 'scalar'
clone => 'array'
clone => 'hash'
clone => sub { shift->clone }

'default' must be scalar or code ref

default => 'good life' # scalar 
default => sub {[]}  # array ref
default => sub {{}}  # hash

delete_class_attr methods

Delete class attribute

$class->delete_class_attr($attr);

This is discuraged now. instead, you write this

delete $class->class_attrs->{$attr};

ClassObjectAttr

'ClassObjectAttr' is deprecated. This is renamed to 'HybridAttr'.

Similar modules

The following is various class builders.

Class::Accessor,Class::Accessor::Fast, Moose, Mouse, Mojo::Base

Author

Yuki Kimoto, <kimoto.yuki at gmail.com>

Github http://github.com/yuki-kimoto/

I develop this module at http://github.com/yuki-kimoto/object-simple .

Please tell me bug if you find.

Copyright & license

Copyright 2008 Yuki Kimoto, all rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.