The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Handel::Storage - Abstract storage layer for cart/order/item reads/writes

SYNOPSIS

    package MyStorage;
    use strict;
    use warnings;
    use base qw/Handel::Storage/;

    sub create {
        my ($self, $data) = @_;

        return $self->result_class->create_instance(
            $ldap->magic($data), $self
        );
    };
    
    package MyCart;
    use strict;
    use warnings;
    use base qw/Handel::Base/;
    
    __PACKAGE__->storage_class('MyStorage');
    __PACKAGE__->storage({
        columns         => [qw/id foo bar baz/],
        primary_columns => [qw/id/]
    });
    
    1;

DESCRIPTION

Handel::Storage is a base class used to create custom storage classes used by cart/order/item classes. It provides some generic functionality as well as methods that must be implemented by custom storage subclasses like Handel::Storage::DBIC.

CONSTRUCTOR

new

Arguments: \%options

Creates a new instance of Handel::Storage, and passes the options to "setup" on the new instance. The three examples below are the same:

    my $storage = Handel::Storage-new({
        item_class => 'Handel::Item'
    });
    
    my $storage = Handel::Storage-new;
    $storage->setup({
        item_class => 'Handel::Item'
    });
    
    my $storage = Handel::Storage->new;
    $storage->item_class('Handel::Item')

The following options are available to new/setup, and take the same data as their method counterparts:

    add_columns
    autoupdate
    constraints
    currency_class
    currency_columns
    currency_code
    currency_code_column
    currency_format
    default_values
    item_class
    iterator_class
    primary_columns
    remove_columns
    result_class
    validation_module
    validation_profile

METHODS

add_columns

Arguments: @columns

Adds a list of columns to the current storage object.

    $storage->add_columns('quix');

add_constraint

Arguments: $column, $name, \&sub

Adds a named constraint for the given column to the current storage object. You can have any number of constraints for each column as long as they all have different names. The constraints may or may not be called in the order in which they are added.

    $storage->add_constraint('id', 'Check Id Format' => \&constraint_uuid);

It is up to each custom storage class to decide if and how to implement column constraints.

add_item

Arguments: $result, \%data

Adds a new item to the specified result, returning a storage result object.

    my $storage = Handel::Storage::DBIC::Cart->new;
    my $result = $storage->create({
        shopper => '11111111-1111-1111-1111-111111111111'
    });
    
    my $item = $storage->add_item($result, {
        sku => 'ABC123'
    });
    
    print $item->sku;

This method must be implemented in custom subclasses.

autoupdate

Arguments: 0|1

Gets/sets the autoupdate flag for the current storage object. When set to 1, an update request will be made to storage for every column change. When set to 0, no updated data will be sent to storage until update is called.

    $storage->autoupdate(1);

The default is 1.

It is up to each custom storage class to decide if and how to implement autoupdates.

check_constraints

Arguments: \%data

Runs the configured constraints against data and returns true if the data passes all current constraints. Otherwise, a Handel::Exception::Constraint exception is thrown.

    $storage->constraints({
        id   => {'Check Id Format' => \&constraint_uuid},
        name => {'Check Name/Type' => \%constraint_cart_type}
    });
    
    my $data = {id => 'abc'};
    
    $storage->check_constraints($data);

A Handel::Exception::Storage exception is thrown if the first parameter is not a HASHREF.

clone

Returns a clone of the current storage instance.

    $storage->item_class('Item');
    $storage->cart_class('Cart');
    
    my $clone = $storage->clone;
    $clone->item_class('Bar');
    
    print $storage->item_class; # Item
    print $clone->item_class;   # Item
    print $clone->cart_class;   $ Cart

This is used mostly between sub/super classes to inherit a copy of the storage settings without having to specify options from scratch.

column_accessors

Returns a hashref containing all of the columns and their accessor names for the current storage object.

    $storage->add_columns(qw/foo bar/);
    print %{$self->column_accessors});
    # foo foo bar bar

The column accessors are used by cart/order/item classes to map public accessors to their columns.

columns

Returns a list of columns from the current storage object;

    $storage->add_columns(qw/foo bar baz/);
    print $storage->columns;  # foo bar baz

constraints

Arguments: \%constraints

Gets/sets the constraints configuration for the current storage instance.

    $storage->constraints({
        id   => {'Check Id Format' => \&constraint_uuid},
        name => {'Check Name/Type' => \%constraint_cart_type}
    });

The constraints are stored in a hash where each key is the name of the column and each value is another hash reference containing the constraint name and the constraint subroutine reference.

It is up to each custom storage class to decide if and how to implement column constraints.

copyable_item_columns

Returns a list of columns in the current item class that can be copied freely. This list is usually all columns in the item class except for the primary key columns and the foreign key columns that participate in the specified item relationship.

count_items

Arguments: $result

Returns the number of items associated with the specified result.

    my $storage = Handel::Storage::DBIC::Cart->new;
    my $result = $storage->create({
        shopper => '11111111-1111-1111-1111-111111111111'
    });
    
    $result->add_item({
        sku => 'ABC123'
    });
    
    print $storage->count_items($result);

This method must be implemented in custom subclasses.

create

Arguments: \%data

Creates a new result in the current storage medium.

    my $result = $storage->create({
        col1 => 'foo',
        col2 => 'bar'
    });

This method must be implemented in custom subclasses.

currency_class

Arguments: $currency_class

Gets/sets the currency class to be used when inflating currency columns. The default currency class is Handel::Currency. The currency class used should be subclass of Handel::Currency.

    $storage->currency_class('CustomCurrency');

A Handel::Exception::Storage exception will be thrown if the specified class can not be loaded.

It is up to each custom storage class to decide if and how to implement currency columns.

currency_columns

Arguments: @columns

Gets/sets the columns that should be inflated into currency objects.

    $storage->currency_columns(qw/total price tax/);

It is up to each custom storage class to decide if and how to implement currency columns.

currency_code

$code

Gets/sets the currency code used by default when formatting currency objects.

See Locale::Currency::Format and Locale::Currency for the list of available currency codes.

currency_code_column

Arguments: $column

Gets/sets the name of the column that contains the currency code to be used for the current row. If no column is specified or it is empty, currency_code will be used instead.

It is up to each custom storage class to decide if and how to implement currency columns.

currency_format

$format_options

Gets/sets the currency formatting options used by default when formatting currency objects.

See Locale::Currency::Format and Locale::Currency for the list of available currency codes.

default_values

Arguments: \%values

Gets/sets the hash containing the default values to be applied to empty columns during create/update actions.

    $storage->default_values({
        id   => \&newid,
        name => 'My New Cart'
    });

The default values are stored in a hash where the key is the name of the column and the value is either a reference to a subroutine to get the value from, or an actual default value itself.

It is up to each custom storage class to decide if and how to implement default values.

delete

Arguments: \%filter

Deletes results matching the filter in the current storage medium.

    $storage->delete({
        id => '11111111-1111-1111-1111-111111111111'
    });

This method must be implemented in custom subclasses.

delete_items

Arguments: $result, \%filter

Deletes items matching the filter from the specified result.

    my $storage = Handel::Storage::DBIC::Cart->new;
    my $result = $storage->create({
        shopper => '11111111-1111-1111-1111-111111111111'
    });
    
    $result->add_item({
        sku => 'ABC123'
    });
    
    $storage->delete_items($result, {
        sku => 'ABC%'
    });

This method must be implemented in custom subclasses.

has_column

Arguments: $column

Returns true if the column exists in the current storage object.

item_storage_class

Arguments: $item_storage_class

Gets/sets the item storage class used to hold item storage configuration and/or create cart/order items.

    my $storage = My::Storage::Cart->new;
    $storage->item_storage_class('My::Storage::Cart::Item');

A Handel::Exception::Storage exception will be thrown if the specified class can not be loaded.

item_storage

Arguments: $storage

Gets/sets the storage objects used to hold item storage configuration options and/or create item storage results. If no storage object is assigned, one will be created using the specified item_storage_class.

    $storage->item_storage_class('My::Storage::Cart::Item');
    my $item_storage = $storage->item_storage;
    
    print ref $item_storage;  # My::Storage::Cart:Item

    my $storage = My::Storage::Order->new;
    my $item_storage = My::Storage::Order::Item->new;
    
    $storage->item_storage($item_storage);

iterator_class

$iterator_class

Gets/sets the class used for iterative result operations. The default iterator is Handel::Iterator::List.

    $storage->iterator_class('MyIterator');
    my $results = $storage->search;
    
    print ref $results # Handel::Iterator::List

A Handel::Exception::Storage exception will be thrown if the specified class can not be loaded.

new_uuid

Returns a new uuid/guid string in the form of

    xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

See DBIx::Class::UUIDColumns for more information on how uuids are generated.

primary_columns

Returns a list of primary columns from the current storage object;

    $storage->add_columns(qw/foo bar baz/);
    $storage->primary_columns('foo');
    print $storage->primary_columns;  # foo

process_error

Arguments: $message

This method accepts errors and converts them into Handel::Exception objects before throwing the error.

If message already a blessed object, it is just re thrown.

remove_columns

Arguments: @columns

Removes a list of columns from the current storage object.

    $storage->remove_columns(qw/description/);

remove_constraint

Arguments: $column, $name

Removes a named constraint for the given column from the current storage object.

    $storage->remove_constraint('id', 'Check Id Format' => \&constraint_uuid);

remove_constraints

Arguments: $column

Removes all constraints for the given column from the current storage object.

    $storage->remove_constraints('id');

result_class

Arguments: $result_class

Gets/sets the result class to be used when returning results from create/search storage operations. The default result class is Handel::Storage::Result.

    $storage->result_class('CustomStorageResult');

A Handel::Exception::Storage exception will be thrown if the specified class can not be loaded.

Arguments: \%filter

Returns results in list context, or an iterator in scalar context from the current source in the current schema matching the search filter.

    my $iterator = $storage->search({
        col1 => 'foo'
    });

    my @results = $storage->search({
        col1 => 'foo'
    });

This method must be implemented in custom subclasses.

search_items

Arguments: $result, \%filter

Returns items matching the filter associated with the specified result.

    my $storage = Handel::Storage::DBIC::Cart->new;
    my $result = $storage->search({
        id => '11111111-1111-1111-1111-111111111111'
    });
    
    my $iterator = $storage->search_items($result);

Returns results in list context, or an iterator in scalar context from the current source in the current schema matching the search filter.

This method must be implemented in custom subclasses.

set_default_values

Arguments: \%data

Sets the default values on any column that is not already defined using the values defined in default_values.

    $self->default_values({
        col1 => 'foo',
        col2 => sub {'stuff'},
        col3 => 2
    });
    
    my $data = {col3 => 4};
    $self->set_default_values($data);

    print %{$data};
    
    # {
    #     col1 => 'foo',
    #     col2 => 'stuff',
    #     col3 => 2
    # }

setup

Arguments: \%options

Configures a storage instance with the options specified. Setup accepts the exact same options that "new" does.

    package MyStorageClass;
    use strict;
    use warnings;
    use base qw/Handel::Storage/;
    
    __PACKAGE__->setup({
        item_class => 'Foo'
    });
    
    # or
    
    my $storage = Handel::Storage-new;
    $storage->setup({
        item_class => 'Items',
        cart_class => 'CustomerCart'
    });

This is the same as doing:

    my $storage = Handel::Storage-new({
        item_class => 'Items',
        cart_class => 'CustomerCart'
    });

If you call setup on a storage instance or class that has already been configured, its configuration will be updated with the new options. No attempt will be made to clear or reset the unspecified settings back to their defaults.

txn_begin

Starts a transaction on the current storage object.

This method must be implemented in custom subclasses.

txn_commit

Commits the current transaction on the current storage object.

This method must be implemented in custom subclasses.

txn_rollback

Rolls back the current transaction on the current storage object.

This method must be implemented in custom subclasses.

validate_data

Arguments: \%data

Validates the specified data against the current <validation_profile> and returns the validation result using the specified validation_module.

    $self->validation_profile([
        col1 => [ ['NOT_BLANK'] ]
    ]);
    
    my $data = {col1 => ''};
    
    my $results = $self->validate_data($data);
    if ($results->success) {
        ...
    };

A Handel::Exception::Storage exception is thrown if validation module is set to FormValidator::Simple and the validation profile isn't a ARRAYREF, or the validation module is set to Data::FormValidator and the validation profile isn't a HASHREF.

A Handel::Exception::Storage exception is thrown if the first parameter is not a HASHREF.

validation_module

Arguments: $validation_module

Gets/sets the module validation class should use to do its column data validation. The default module is FormValidator::Simple.

And validation module may be used that supports the check method.

It is up to each custom storage class to decide if and how to implement data validation.

validation_profile

Arguments: \@profile*

Gets/sets the validation profile to be used when validating column values.

    $storage->validation_profile([
        param1 => ['NOT_BLANK', 'ASCII', ['LENGTH', 2, 5]],
        param2 => ['NOT_BLANK', 'INT'  ],
        mail1  => ['NOT_BLANK', 'EMAIL_LOOSE']
    ]);

* The default validation module is FormValidator::Simple, which expects a profile in an array reference. If you use Data::FormValidator, make sure you pass in the profile as a hash reference instead:

    $storage->validation_profile({
        optional => [qw( company
                         fax 
                         country )],
        required => [qw( fullname 
                         phone 
                         email 
                         address )]
    });

It is up to each custom storage class to decide if and how to implement data validation.

get_component_data

Arguments: $data

Gets the current data for the specified component name.

    my $profile = $self->get_component_data('validation_profile');

There is no good reason to use this. Use the specific class accessors instead.

set_component_data

Arguments: $name, $data

Sets the current class for the specified component name.

    $self->set_component_data('validation_profile', [name => ['NOT_BLANK']]);

There is no good reason to use this. Use the specific class accessors instead.

get_component_class

Arguments: $name

Gets the current class for the specified component name.

    my $class = $self->get_component_class('item_class');

There is no good reason to use this. Use the specific class accessors instead.

set_component_class

Arguments: $name, $value

Sets the current class for the specified component name.

    $self->set_component_class('item_class', 'MyItemClass');

A Handel::Exception::Storage exception will be thrown if the specified class can not be loaded.

There is no good reason to use this. Use the specific class accessors instead.

SEE ALSO

Handel::Storage::DBIC, Handel::Storage::Result, Handel::Manual::Storage, Handel::Storage::DBIC::Cart, Handel::Storage::DBIC::Order

AUTHOR

    Christopher H. Laco
    CPAN ID: CLACO
    claco@chrislaco.com
    http://today.icantfocus.com/blog/