Latest Version: 1.00010 - Download it now.
Currently Working On: Mango: making it happen......
Handel 1.0 Released
After just over a year of development and testing, I'm pleased to finally announce the 1.0 release of Handel. The 1.0 release has undergone a major rewrite from the 0.3x series of releases and includes many new features and fixes. The new features include:
DBIx::Class Support
Handel now uses DBIx::Class instead of Class::DBI as the default ORM. Many thanks to the DBix::Class folk for all the support. Without the flexibility that DBIx::Class provides, Handel 1.0 would be a much less flexible.
Rose::DB::Object Support
For those who prefer working with Rose instead, Handel::Storage::RDBO 1.0 has also been released.
Schema Alterations
You can now add/remove columns from/to the default classes and schema with only a few lines of code.
Interchangable Schemas
If you already have an existing schema in your application, Handel can now use that instead of it's own.
Abstract Storage Layer
Handel now uses an abstract storage layer for cart/order operations. The default layer is Handel::Storage::DBIC and a Handel::Storage::RDBO layer is also available. New storage layers may be written (XML, LDAP, SOAP, Amazon, Google, etc) and can be used by your Handel code with almost no code changes.
Module::Starter QuickStart
Handel now includes a Module::Starter module and scripts to quick get a custom cart/order system up an running and deploy the schema to an existing database.
Pre 1.0 Compat Layer
To make the conversion to 1.0 a little less painful, 1.0 includes a few Compat modules allowing you used the old API with a new 1.0 install.
Tests, Tests, and More Tests
In addition to all of the new features and refactoring, Handel now has a plethora of tests and a 99% test coverage. That means that the code base is considered to be very stable and all new code has been accounted for.
The new version should be available on a CPAN mirror nearest you shortly. As always, you can find the latest version in the Subversion repository. If you need help, please join the mailing list or stop by #handel@irc.perl.org.
Happy Carting!
Handel 0.99_19 Released
Tags: changes (26) : development release (16)
DBIx::Class 0.08 is out. Code coverage is up to date. All tests pass. With any luck, this will be the last release before I kick 1.0 out into the world.
0.99_19 Thur June 21 21:02:36 2007
- Storage->setup now puts non-accessor keys into %$self
- Removed incomplete L10N Lexicons
- Cleanup coverage before 1.0 release
- DBIx::Class 0.08 Released & Required
- Added AutoCommit to Handel::Test
- Tweaked failing checkout process tests
0.99_18 Wed Apr 18 19:55:09 2007
- Added --version to handel.pl/handel.bat
- Fixed Handel::Compat::Currency pod
- Updated prereq to use DBIx::Class 0.07999_02
- Added skips for DBD::SQLite 1.13 (teh devil) causing failing tests
- Updated Perl::Critic tests
Once 1.0 is kicked out the door, I need to spend some time getting the RDBO storage layer up to date. Then, it's back to Mango.
New Mango Website
Tags: mango (3)
Since things have died down on the Handel front until DBIx::Class 0.08 comes out, and I recently hit a progress slump on Mango, I've put up a new blog for Mango.
With any luck, I'll try and post about progress often.
Breaking API Change in 0.99_17
Tags: api change (1) : development release (16)
First, to those who are using 0.99_16 or so, I'm sorry, but this has to be done. Breathe in. Breathe Out. It's not going to be that bad. :-)
Through all of my tinkering with the Handel::Currency class to do conversions, formatting and integrate inflation into DBIx::Class, I kept feeling the urge to put some parts of it on CPAN because it felt like reusable code. During a few down days, I did just that. Thus, Data::Currency and DBIx::Class::InflateColumn::Currency were born.
During that process, I had a chance to clean up the Handel::Currency API into something more precise for non Handel users. The biggest change was that
So, for those using Handel 0.33, nothing is changing. When you upgrade to Handel 1.0, you can still use the Handel::Compat::Currency module if you like. For those of you using a recent version of Handel 0.99_xx, your code will have to change to use
I only feel slight bad about that. It is a developer release after all. :-)
Handel 0.99_16 Released
Tags: changes (26) : development release (16)
One more for the trolls, with some added order_by goodness for Kaare.
- Added \%options/order_by to search/items in Cart/Order
- Added has_column to DBIC Storage to use ResultSource->has_column
- Added has_column to Result/DBIC Result
- Fixed TT demo code. Maybe that will die some day. Seriously. Two words: Catalyst Helpers. :-)
- Fixed issue where Compat Currency convert failed with converter returns 0
- Changed quickstart setup script to *_handel.pl
- Added 0700 perms to handel.pl app script
- Storage::process_error now only dies Handel::Exception subclasses instead of all blessed objects
- Renamed DBIC schema classes into Schema::DBIC space, like their RDBO cousins
Handel::Storage::RDBO 0.01_02 Released
Tags: rdbo (2) : rose (2) : storage (3)
From the I need a new hobby department, I present to you the second dev release of Handel::Storage::RDBO...
- Reworked init_db/Storage connection with the help of John Siracusa
- Storage connection_info is now just like the DBIC storage connection_info
- Requires latest version of Rose::DB from svn/0.732 when released
- Fixed pod typo in Storage::Order::Item
Handel::Storage::RDBO 0.01_01 Released
Tags: rdbo (2) : rose (2) : storage (3)
Just because I can, and also as an exercise to make sure the storage layer in the upcoming Handel 1.0 is abstracted to my liking, I'm happy to announce the first release of Rose::DB::Object support for Handel.
The dist includes the requisite storage, schema, and result classes needed to use Handel 1.0 with Rose::DB::Object classes instead of the DBIx::Class schema classes.
To use RDBO instead of DBIC, all you have to do is change the storage_class in your storage classes generated by the bootstrap:
package MyApp::Storage::Cart;
use strict;
use warnings;
BEGIN {
use base qw/Handel::Storage::DBIC::Cart/;
};
becomes:
package MyApp::Storage::Cart;
use strict;
use warnings;
BEGIN {
use base qw/Handel::Storage::RDBO::Cart/;
};
You will need to do this in the Cart, Cart::Item, Order, and Order::Item storage classes. At some point I will change the generation scripts to allow one to specify DBIC or RDBO during code generation.
Handel-Storage-RDBO-0.01_01 should be available on CPAN shortly, and requires Handel 1.0, which doesn't yet exist. Please use the latest code from the 1.0 branch for now until I get another dev release out the door:
http://svn.handelframework.com/CPAN/Handel/branches/DBIC-1.0/
-=Chris
Handel 0.99_15 Released
Wow. I think my eyes are bleeding. This release has a lot of work behind it. One of the big new features is Module::Starter::Handel, the handel.pl starter script, and another Catalyst helper that combines the old helper output with the new starter subclasses. And oh yeah, ~99% test coverage doesn't suck either.
I still have to make Perl::Critic happy with the test suite (test labels). The L!0N lexicons need completed, and the demos still need fixed. This might possibly be the last dev release before 1.0.
- L10N files now use constant-like keys rather than english errors
- L10N::es_es.pm reworked by Diego Kuperman
- L10N::fr reworked by Pierrick DINTRAT
- Added l10n_lexicon_synced.t to ensure language lexicons are synced with each other via key checks
- Removed constraint_uuid check from Checkout order/cart since it's not subclass friendly (RT#19813,TKP)
- Checkout cart now throws an exception of now order can be found matching the search criteria
- Converted tests to use Handel::Test
- Cleanup tests against Perl::Critic
- Fixed Checkout prototype mismatch warning under perl > 5.8.8
- Added more config tests for MP1/MP2
- Added more Schema connect tests
- Added more Base tests
- Added Iterator tests
- Fixed bug in Iterator:::DBIC where first increments the resulset
- Fixed issue in Cat Helpers that treated MyApp::Controller:: as a valid name
- Added Catalyst Model setup tests
- Added Catalyst Scaffold Helper tests
- Added checkout_class to Order pod
- Added Module::Starter::Handel to help kickstart Handel projects
- Added currency_code_column to storage so DBIC inflators can set currency code using another column in the row
- Removed formatting options from Currency->convert now that it always returns a currency object instead of formatted strings
- Reworked Handel::Currency to be more object like with its parameters
- Added Handel::Compat::Currency, inserted automatically when loading Handel::Compat
- Added compat_currency.t tests
- Converted to Module::Pluggable::Object
- Cart restore now throws exception on unknown mode like it was supposed to
- Only create currency converter when calling convert for the first time
- Added handel.pl quickstart scripts for Module::Starter::Handel apps
- Added Catalyst::Helper::Handel to create entire cat app w/ starter subclasses
- Huge test coverage. 99% w00t!
- Mostly Perl::Critic happy...except for some test labels
- Probably last release before 1.0!
Finished With Code Coverage
Tags: coverage (2) : testing (2)
It was a long journey to total test coverage, but I estimate I fixed about 15 unknown untested bugs in the process. That's never a bad thing. Now, it's time to fix some pod spelling, write the QuickStart pod, make Perl::Critic happy again, fix the old demo code, and release 1.0. Getting closer every day.
An Introduction to Handel
Tags: introduction (1) : slides (1)
I've put the slides from tonights RubberCity Perl Mongers meeting presentation online. You can find them in the slides directory.
Code Coverage Statistics
Tags: coverage (2) : testing (2)
For those who like to play along in my game of testing OCD, I've put the current code coverage stats online. It's getting greener all the time. :-)
Module::Starter::Handel
Tags: module-starter (1) : quick start (1)
After a few different people have struggled through customizing their use of Handel, I decided that it might be worth it to hack together a quickstart script that took care of all the basic subclassing hoopla. The new module can be found in the 1.0 dev branch and it requires that you have Module::Starter installed.
To create a new project, call the module-starter script passing in the class parameter, the project name and the other parameters required by module-starter:
module-starter --class=Module::Starter::Handel --module=MyProject \
--author="Me" --email="me@example.com" --verbose
and you should end up with some nice new spiffy project modules:
Created MyProject
Created MyProject\lib\MyProject
Created MyProject\lib\MyProject\Cart.pm
Created MyProject\lib\MyProject\Cart
Created MyProject\lib\MyProject\Cart\Item.pm
Created MyProject\lib\MyProject\Storage
Created MyProject\lib\MyProject\Storage\Cart.pm
Created MyProject\lib\MyProject\Storage\Cart
Created MyProject\lib\MyProject\Storage\Cart\Item.pm
Created MyProject\lib\MyProject\Order.pm
Created MyProject\lib\MyProject\Order
Created MyProject\lib\MyProject\Order\Item.pm
Created MyProject\lib\MyProject\Storage\Order.pm
Created MyProject\lib\MyProject\Storage\Order
Created MyProject\lib\MyProject\Storage\Order\Item.pm
Created MyProject\lib\MyProject\Checkout.pm
Created MyProject\t
Created MyProject\t\pod-coverage.t
Created MyProject\t\pod.t
Created MyProject\t\boilerplate.t
Created MyProject\t\00-load.t
Created MyProject\.cvsignore
Created MyProject\Makefile.PL
Created MyProject\MANIFEST
Created starter directories and files
Handel 0.99_14 Released
Tags: changes (26) : development release (16)
Second verse, same as the first.
- Added Order::save method
- MarkOrderSaved plugin now uses Order::save method instead of setting the type field directly
- Removed constraint_uuid check from Order create/reconcile as it's not subclass of interchangable schema friendly (RT#19813,TKP)
- Changed id references in search to use first primary key from Storage (RT#19813,TKP)
- Order creation from cart no does can('shopper') to help w/ subclassing
Handel 0.99_13 Released
Tags: changes (26) : development release (16)
There's nothing to see here. Just some methods to make custom storage classes a tad bit easier. Please move along.
- Added NEXT to prereqs for Handel::Compat
- Moved FormValidator::Simple from recommended to prereqs
- Added set_default_values to Storage
- Added check_constraints to Storage
- Added validate_data to Storage
Handel 0.99_12 Released
Tags: changes (26) : development release (16)
I swear, the closer I get, the farther away I am [from 1.0].
- Moved Locale::Currency/Format and WebServiceX modules to requirements
- Currency new() now takes $code and $format params
- Added name/code to Currency
- Added converter_class to Currency
- Added currency_code and currency_format to Storage
- Storage::DBIC now passes currency_code/format into DBIC inflate subs for currency_columns
- Added Storage::DBIC currency_code/currency_format tests
- Finished Perl::Critic cleanup
Cookbook: Writing a Custom Storage Class
Tags: article (1) : cookbook (2) : customizing (2) : storage (3)
This article will cover the basic steps needed to write a custom storage class for Handel 1.0. For this exercise, we will write an XML storage class that will store a cart and its items in a single xml file with the ability to do wildcard searches just like our DBIC storage class. The new storage class could be used for order and order items as well.
Writing a custom storage class is just a matter of subclassing Handel::Storage and Handel::Storage::Result and implementing the methods necessary to create/delete/search carts and items as well as begin/commit/rollback transactions and update calls.
The example code below demonstrates how to write a custom storage class in Handel. While it covers the basic steps, it is by no means a complete implementation of all possible features, including things like column default values, parameter validation, etc. Those are left as an exercise for the reader to implement based on their particular situation and application needs.
Getting Started
Create A Blank XML File
First, let's create the xml file we'll use to store shopping carts in:
<?xml version="1.0"?>
<carts/>
Save this as carts.xml in a location of your choosing. As we add to this file later, it will take on the following format:
<?xml version="1.0"?>
<carts>
<cart id="1" shopper="1" name="My Cart">
<item id="1" sku="ABC-123" quantity="1" price="1.23"/>
</cart>
</cart>
Create Storage and Storage Result Subclasses
First, lets create the basic modules for the new xml storage:
package Handel::Storage::XML;
use strict;
use warnings;
BEGIN {
use base qw/Handel::Storage/;
};
__PACKAGE__->result_class('Handel::Storage::XML::Result');
1;
and the xml storage results:
package Handel::Storage::XML::Result;
use strict;
use warnings;
BEGIN {
use base qw/Handel::Storage::Result/;
};
1;
Pretty straight forward stuff so far. We've subclassed Handel::Storage and Handel::Storage::Result and told Storage::XML to use the XML::Result class for its results.
Setup Private Storage Mechanism
Next, in Storage::XML, we will load the necessary XML modules and find a place to store a parser object.
BEGIN {
use base qw/Handel::Storage/;
use XML::LibXML;
use XML::LibXML::XPathContext;
# we use Class::Accessor::Grouped in this here town
__PACKAGE__->mk_group_accessors('inherited', qw/parser/);
};
# create a default xml parser
__PACKAGE__->parser(XML::LibXML->new);
We're going to use XML::LibXML as the parser and XML::LibXML::XPathContext for the wildcard XPath searches using a custom XPath function in perl.
Next we need to load our newly created F
BEGIN {
use base qw/Handel::Storage/;
use XML::LibXML;
use XML::LibXML::XPathContext;
use Handel::Exception qw/:try/;
__PACKAGE__->mk_group_accessors('inherited', qw/parser file_name/);
};
__PACKAGE__->parser(XML::LibXML->new);
and setup a method to load and return the XML::Document object:
sub document {
my $self = shift;
# if no document has been loaded, load it from file_name
if (!$self->{'xmldoc'}) {
$self->{'xmldoc'} = $self->parser->parse_file($self->file_name);
};
return $self->{'xmldoc'};
};
Now that we can load the xml file, let's create a method to save it back to the original file.
sub save_file {
my $self = shift;
$self->document->toFile($self->file_name);
};
Next, we'll create a method to get the document root. Just for the sake of configurability, let's make a preference for the parent node and child node names. Those will be set later when we create a new instance of Storage::XML.
BEGIN {
use base qw/Handel::Storage/;
use XML::LibXML;
use XML::LibXML::XPathContext;
__PACKAGE__->mk_group_accessors('inherited', qw/parser file_name parent_node child_node/);
};
__PACKAGE__->parser(XML::LibXML->new);
sub document_root {
my $self = shift;
# if we havn't set root, do so
if (!$self->{'xmldocroot'}) {
my $root = XML::LibXML::XPathContext->new(
$self->document->getDocumentElement
);
# register a new function in XPath
$root->registerFunction('attr_matches', sub {
my ($nodelist, $pattern) = @_;
my $node = $nodelist->get_node(0);
if ($node->getValue =~ /^$pattern$/i) {
return $node;
};
return;
});
$self->{'xmldocroot'} = $root;
};
return $self->{'xmldocroot'};
};
If no document root is set, we create a new context object and register our wildcard matching function called 'attr_matches'. Now, anytime we write an XPath query, we can use that function:
/carts[attr_match(@name, 'foo.*')]
Now that we can get XML results using wildcards, we need a method to convert our standard search filters into XPath attribute queries:
sub filter_to_xpath {
my ($self, $filter) = @_;
my @attrs = map {
my $value = $filter->{$_} || '';
$value =~ s/'/"/g;
# turn % into regex .* and do attr_match
if ($value =~ s/%/.*/g) {
"attr_matches(\@$_, '$value')";
# just a straight compare thanks
} else {
"\@$_='$value'";
};
} keys %{$filter};
return @attrs ? '[' . join(' and ', @attrs) . ']' : undef;
};
The method above turns each hash key/value pair into XPath attribute match statements:
print $self->filter_to_xpath({
shopper => 1,
name => 'My%'
});
# prints [@shopper=1 and attr_match('@name', .*)]
We'll see later how to use that during search operations and in create operations to check if a cart exists before we create another one.
Cart Actions
Now that we have a rough framework for reading an xml document and matching wildcards using XPath, it's time to start adding methods to create/search/delete cart records from the xml file.
Create
To create a new cart record, we need to accept a hash full of data and write a new xml node if no node exists with the same primary key value.
sub create {
my ($self, $data) = @_;
# turn the hash data into an xpath compare for the primary columns
my %filter = map {$_ => $data->{$_}} $self->primary_columns;
my $attributes = $self->filter_to_xpath(\%filter);
# look for an existing cart and die if it exists
if ($self->document_root->find($self->child_node . $attributes)) {
throw Handel::Exception::Storage( -text => 'Cart already exists with that id');
} else {
# create a new cart node
my $cart = $self->document->createElement($self->child_node);
# set all of the attributes using the supplied data
map {$cart->setAttribute($_ => $data->{$_})} keys %{$data};
# using document instead of document_root due to issue w/ XPathContext
# Can't locate auto/XML/LibXML/XPathContext/appendChild.al in @INC
# $self->document_root->appendChild($cart);
# may be my install
#
# append the new cart node to the document and save the changes
$self->document->getDocumentElement->appendChild($cart);
$self->save_file;
# now return a new result object containing the new node
return $self->result_class->create_instance($cart, $self);
};
};
Search
Now that we can create a new xml node, it would be nice to be able to load it and other nodes. We need to search all nodes and create an iterator containing the results.
sub search {
my ($self, $filter) = @_;
# turn the hash filter into an xpath attribute query
my $attributes = $self->filter_to_xpath($filter);
# find all cart nodes matching the query
my @nodes = $self->document_root->findnodes($self->child_node . $attributes);
# create an iterator of results
my $iterator = $self->iterator_class->new({
data => \@nodes,
result_class => $self->result_class,
storage => $self
});
# return the iterator in scalar, or all in list context
return wantarray ? $iterator->all : $iterator;
};
The default result iterator class in storage is Handel::Iterator::List. It simply takes an array reference, and iterates over the list contents.
Delete
Last but not least in this section, we need to be able to remove carts matching the specified filter. We could reuse C
sub delete {
my ($self, $filter) = @_;
# turn the hash filter into an xpath attribute query
my $attributes = $self->filter_to_xpath($filter);
# find all cart nodes matching the query
my @nodes = $self->document_root->findnodes($self->child_node . $attributes);
foreach my $node (@nodes) {
$node->unbindNode;
};
$self->save_file;
};
Item Actions
Now that we can create, search and delete carts, we need to do the same thing for cart items. Now would be a good time to review Handel::Manual::Storage/Items_As_Second_Class_Objects. Since carts and their items reside in the same file, we shall consider items and the Item class used later as second class objects. They will hold configuration information about items, but will do none of the work and the same storage instance will take care of reading/writing cart and cart items.
Adding Items
To create a new item record, we simply need to make sure on item doesn't already exist with the supplied id, then create a new node, populate it and insert it into the cart nodes child node collection.
sub add_item {
my ($self, $result, $data) = @_;
my $item_storage = $self->item_storage;
# get the cart node
my $cart = $result->storage_result;
# convert the data into a primary key filter
my %filter = map {$_ => $data->{$_}} $item_storage->primary_columns;
my $attributes = $self->filter_to_xpath(\%filter);
# see if there is already an item in this cart matching the id
if ($cart->find($item_storage->child_node . $attributes)) {
throw Handel::Exception::Storage( -text => 'Cart item already exists with that id');
# create a new item node and insert it into the cart node
} else {
my $item = $self->document->createElement($item_storage->child_node);
map {$item->setAttribute($_ => $data->{$_})} keys %{$data};
$cart->appendChild($item);
$self->save_file;
return $item_storage->result_class->create_instance($item, $self);
};
};
Searching Items
Now that we can add cart items, we need to be able to fetch them later.
sub search_items {
my ($self, $result, $filter) = @_;
my $item_storage = $self->item_storage;
# get the cart node
my $cart = $result->storage_result;
# convert the filter to a xpath statement
my $attributes = $self->filter_to_xpath($filter);
# get items in this cart matching the xpath
my @nodes = $self->document_root->findnodes($item_storage->child_node . $attributes, $cart);
my $iterator = $self->iterator_class->new({
data => \@nodes,
result_class => $item_storage->result_class,
storage => $self
});
return wantarray ? $iterator->all : $iterator;
};
Just like the cart search, we just need to create an XPath query from the supplied filter, and return any found nodes wrapped in storage results.
Counting Items
To make Cart::count happy, we also need a way to count the number of items in a cart. Again, we could just use search_items and count the number of results, but this is more efficient.
sub count_items {
my ($self, $result) = @_;
my $item_storage = $self->item_storage;
my $cart = $result->storage_result;
# find all ./item nodes in the current cart
return $cart->find($item_storage->child_node)->size;
};
Deleting Items
Last on the list again, we need to be able to delete items. This should look pretty familiar by now.
sub delete_items {
my ($self, $result, $filter) = @_;
my $item_storage = $self->item_storage;
# get the cart node
my $cart = $result->storage_result;
# make an xpath wquery from the filter if we have one
my $attributes = $self->filter_to_xpath($filter);
my @nodes = $self->document_root->findnodes($item_storage->child_node . $attributes, $cart);
foreach my $node (@nodes) {
$node->unbindNode;
};
$self->save_file;
};
Transaction Support
Each custom storage class should strive to support transactions if possible. If it is not possible, you need to override the base classes methods to keep them from throwing 'not implemented' exceptions.
sub txn_begin {};
sub txn_rollback {};
sub txn_commit {};
sub discard_changes {};
Storage Results
Mapping Methods to XML Attributes
Now that we have created the basic XML storage class, we need to create a results class that knows how to update individual xml nodes. The default Storage::Result class simply passes methods to the underlying storage result:
$result->id;
# the same as:
$result->storage_result->id;
Since XML::Node objects only expose attributes using set/getAttribute, we need to write a custom AUTOLOAD method to redirect method calls.
package Handel::Storage::XML::Result;
use strict;
use warnings;
BEGIN {
use base qw/Handel::Storage::Result/;
};
sub AUTOLOAD {
my $self = shift;
# leave DESTROY alone
return if (our $AUTOLOAD) =~ /::DESTROY$/;
$AUTOLOAD =~ s/^.*:://;
return unless $self->storage->has_column($AUTOLOAD);
# if we are setting, call setAttribute
if (scalar @_) {
$self->storage_result->setAttribute($AUTOLOAD, shift);
};
# return from getAttribute
$self->storage_result->getAttribute($AUTOLOAD);
};
1;
This is the easiest way to make results and the underlying storage results talk to each other. Using this method, you can use the same result class for carts, order and cart/order items even if their fields differ.
If have a particular hatred for AUTOLOAD, you could just as well create two result classes: Handel::Storage::XML::Cart::Result and Handel::Storage::XML::Cart::Item::Result and assign them to the corresponding storage classes:
package Handel::Storage::XML::Cart;
use strict;
use warnings;
use base qw/Handel::Storage::XML/;
__PACKAGE__->result_class('Handel::Storage::XML::Cart::Result');
1;
package Handel::Storage::XML::Cart::Item;
use strict;
use warnings;
use base qw/Handel::Storage::XML/;
__PACKAGE__->result_class('Handel::Storage::XML::Cart::Item::Result');
1;
In each result class, you could map actual attributes to real methods instead of using autoload:
package Handel::Storage::XML::Cart::Result;
use strict
use warnings;
use base qw/Handel::Storage::Result/;
sub id {
my ($self, $value) = @_;
if ($value) {
$self->storage_result->setAttribute('id', $value);
};
return $self->storage_result->getAttribute('id');
};
Updating Results
When autoupdates are off, one has to call update manually to save the changes made to cart and item nodes. When autoupdates are enabled, update will be called automatically on each result. To support updates we need to create an update method on the custom result class:
sub update {
my ($self, $data) = @_;
if ($data) {
foreach my $key (keys %{$data}) {
$self->storage_result->setAttribute($key, $data->{$key});
};
};
$self->storage->save_file;
};
When update is called, the xml file is saved. If a hashref of data is supplied, it will set the appropriate attributes and then save the changes to the xml file.
Deleting Results
Each result needs to be able to remove itself from storage. To do so, we simply need to add a delete method to the result that removes itself and updates the file:
sub delete {
my $self = shift;
$self->storage_result->unbindNode;
$self->update;
};
Using Your New Storage Class
Now that we have a shiny new XML storage class, we need to use it. There are two ways we can go about using our new storage class. The easiest way would be to load and configure them directly in the Cart and Item interface classes:
package My::Cart;
use strict;
use warnings;
use base qw/Handel::Cart/;
__PACKAGE__->item_class('My::Cart::Item');
__PACKAGE__->storage_class('Handel::Storage::XML');
__PACKAGE__->storage({
add_columns => [qw/id shopper name description type/],
primary_columns => [qw/id/],
file_name => 'carts.xml',
parent_node => 'carts',
child_node => 'cart',
});
1;
While this way works, it has one problem. If you decide to change storage classes later, it may not take the same options that this storage class does.
Create Cart and Item Storage Classes
In order to make changing storage classes less painful, it is recommended that you make subclasses of your custom storage class to set storage options, and then use those subclasses within your Cart and Item interface classes:
package My::Storage::Cart;
use strict;
use warnings;
use base qw/Handel::Storage::XML/;
__PACKAGE__->item_storage_class('My::Storage::Cart::Item');
__PACKAGE__->setup({
file_name => 'carts.xml',
parent_node => 'carts',
child_node => 'cart',
add_columns => [qw/id shopper name description type/],
primary_columns => [qw/id/],
});
1;
package My::Storage::Cart::Item;
use strict;
use warnings;
use base qw/Handel::Storage::XML/;
__PACKAGE__->setup({
parent_node => 'items',
child_node => 'item',
add_columns => [qw/id sku quantity price description/],
primary_columns => [qw/id/]
});
1;
Create Cart and Item Classes
Now that we have custom storage subclasses for Carts and Items, we just need to tell our cart and item classes to use them:
package My::Cart;
use strict;
use warnings;
use base qw/Handel::Cart/;
__PACKAGE__->item_class('My::Cart::Item');
__PACKAGE__->storage_class('My::Storage::Cart');
1;
package My::Cart::Item;
use strict;
use warnings;
use base qw/Handel::Cart::Item/;
__PACKAGE__->storage_class('My::Storage::Cart::Item');
1;
Now that we have some separation between the Cart/Items classes and the storage classes, changing storage classes in the future just means changing My::Storage::Cart and My::Storage::Cart::Item.
All Hands on Deck
That's all there is to it. Using the same techniques above, you should be able to write a custom storage class for just about anything; LDAP, INI files, SOAP/XMLRPC, etc.
Caveats
The example above is by no means complete. It makes no effort to deal with constraints, default values or data validation. It also does not make any attempt to support transactions in any meaningful way nor does it deal with concurrency issues with xml file reading and writing.
Given more time, I may work on making this example into a supported Storage::XML class.
Handel 0.99_11 Released
Look out Ned, it's coming right for us!
- Fix examples code in AddingColumns.pod
- Fixed pod references after DBIC Storage rearrangement
- Added item_storage_class/item_storage to Storage so item related methods could get item storage config from item storage without being tied to top level item class
- Moved item_class/cart_class back into Handel::Base. They don't belong in storage
- Moved checkout_class back into Handel::Order. That doesn't belong in storage either
- Added WritingCustomStorage to Cookbook
- Added has_column to Storage
Cookbook: Adding Columns in 1.0
Tags: articles (2) : columns (1) : cookbook (2) : customizing (2)
Introduction
I've updated this to reflect changes made in 0.99_11, which should be available on CPAN shortly.
As 1.0 draws near, it was about time I started on the ever popular Cookbook/Tutorial stuffs. I decided to tackle something everyone asks: how to add columns.
The methods below outline the steps necessary to add columns to the default install of Handel 1.x. The steps below generally apply to customizing most storage options, including removing columns, adding constraints, etc.
This article is broken down into three solutions: Good, Better, and Best.
Good
If you need a quick fix in a script or want to alter all instances of Handel objects within the same process, you can simple add a column to the desired classes storage using Handel::Storage::add_columns.
#!/usr/bin/perl -w
use strict;
use warnings;
use Handel::Cart;
Handel::Cart->storage->add_columns('custom');
Handel::Cart->create_accessors;
my $carts = Handel::Cart->search;
print $carts->count;
while (my $cart = $carts->next) {
print $cart->custom, "\n";
};
Better
The example above gets the job done with a minimal amount of code. But if you're on shared server with other people using Handel, you're going to get a nasty phone call when every one discovers their stuff has an extra field in it.
The better, more polite approach is to make your own custom classes and use [ab]use them instead.
package My::Cart;
use strict;
use warnings;
use base qw/Handel::Cart/;
__PACKAGE__->item_class('My::Cart::Item');
__PACKAGE__->storage->add_columns('custom');
__PACKAGE__->create_accessors;
1;
You don't have to subclass both the cart and items classes, but for the sake of good practice, there's no reason not to if you've gone this far.
package My::Cart::Item;
use strict;
use warnings;
use base qw/Handel::Cart::Item/;
1;
Then in your script, use you're cart instead.
#!/usr/bin/perl -w
use strict;
use warnings;
use My::Cart;
my $carts = My::Cart->search;
print $carts->count;
while (my $cart = $carts->next) {
print $cart->custom, "\n";
};
Best
The two approaches above have one potential issue: they assume that the current classes will always use the default storage. If you add columns using only the column name, that won't be an issue. If you add columns using the DBIx::Class C<\%column_info> syntax, you will have a problem if the new storage class doesn't understand the syntax.
package My::Cart;
use strict;
use warnings;
use base qw/Handel::Cart/;
__PACKAGE__->storage_class('Future::Storage::XML');
# this line goes boom!
# don't know how to add column 'HASH(0x2254b8)'
__PACKAGE__->storage->add_columns('foo' => {
data_type => 'varchar',
size => 50,
is_nullable => 1
});
1;
This is a potential problem when setting any storage class option from within the interface classes that uses it. To help abstract the storage specifics away from the interface classes that use them, you should go the extra mile and declare custom storage classes. I did mention that it's a 'framework' right? :-)
Create Cart and Item Storage Classes
First we need to create custom storage classes for the Cart and Item interface classes.
package My::Storage::Cart;
use strict;
use warnings;
use base qw/Handel::Storage::DBIC::Cart/;
__PACKAGE__->item_storage_class('My::Storage::Cart::Item');
__PACKAGE__->add_columns('foo' => {
data_type => 'varchar',
size => 50,
is_nullable => 1
});
1;
package My::Storage::Cart::Item;
use strict;
use warnings;
use base qw/Handel::Storage::DBIC::Cart::Item/;
1;
Create Cart and Item Classes
package My::Cart;
use strict;
use warnings;
use base qw/Handel::Cart/;
__PACKAGE__->item_class('My::Cart::Item');
# No storage changes in here
# The storage class takes care of adding the column
__PACKAGE__->storage_class('My::Storage::Cart');
__PACKAGE__->create_accessors;
1;
package My::Cart::Item;
use strict;
use warnings;
use base qw/Handel::Cart::Item/;
__PACKAGE__->storage_class('My::Storage::Cart::Item');
__PACKAGE__->create_accessors;
1;
Code Away Merrill
#!/usr/bin/perl -w
use strict;
use warnings;
use My::Cart;
my $carts = My::Cart->search;
print $carts->count;
while (my $cart = $carts->next) {
print $cart->custom, "\n";
};
Now that we've pushed the add column magic down into the storage class, My::Cart is blissfully unaware of any changes that storage class needs to make in order to add a column:
package My::Storage::Cart;
use strict;
use warnings;
use base qw/Handel::Storage::DBIC::Cart/;
__PACKAGE__->item_storage_class('My::Storage::Cart::Item');
__PACKAGE__->add_columns([qw/foo varchar 50 nullable/]);
1;
Conclusion
That's all there is to it! Like most of Handel, you can pick your level of magic based on your needs.
Handel 0.99_10 Released
Tags: changes (26) : development release (16)
The coding monkeys are on crack, and they can't be stopped!
- Added start of Cookbook and Cookbook/AddColumns
- Moved DBIC specific Storage classes into DBIC namespace
- Changed process to get items->all instead of (items)
Handel 0.99_09 Released
Tags: changes (26) : development release (16)
Fresh out of the monkey pit.
- Catalyst Helpers now require FormValidator::Simple 0.17 (Woohoo!)
- Validation component now requires DBIx::Class::Validation 0.01001 which uses new FV::S for profile/instance/messages fixes
- Filled in pod generated by Catalyst Helpers in a moment of weakness
Handel 0.99_08 Released
Tags: changes (26) : development release (16)
Another quick release before I lose my energy today and forget what I was thinking about this morning.
- Added txn_begin/commit/rollback to Storage and Storage::DBIC to help abstract Checkout::process from Storage.
- Added Storage::DBIC::Result to get DBIC specific things out of Storage::Result
- Minor pod fixes
Handel 0.99_07 Released
Tags: catalyst (5) : changes (26) : development release (16) : helpers (1) : scaffold (2)
I can see light at the end of the tunnel. It could always be the train though. The Catalyst helpers have been reworked. They still need some tweaking, but what doesn't. Maybe I'll feel generous and fill in the auto generated pod. :-)
WARNING: The auto generated Catalyst helper code requires FormValidator::Simple changes/fixes that have yet to be commited or accepted until the author responds. Contact me if you need a copy of the modified version. Keep your fingers crossed that I don't have to do a hostile takeover of the module just to fix a few bugs.
- Moved perl_critic.t to style_perl_critic.t
- Added style_no_tabs.t
- Style tests now enabled via TEST_PRIVATE
- Reworked Catalyst Helpers for Controllers and their tests.
- Fixed issue where add_handler wasn't assigning unique pref ids when non were specified in the plugins themselves.
- Moved requirements to Requirements.pod
- Cart destroy now works on a blessed object under Catalyst
- AssignOrderNumber checkout plugin no longer sets the updated field. This will be rolled into the Order class in the next release.
Handel 0.99_06 Released
Tags: changes (26) : development release (16)
Close, but no Bob Seger. We're getting close. I'm done futzing with storage I think, and the Catalyst Models/Model Helpers have been reworked. All tests pass (except for perl_critic.t). Tomorrow I start on the rest of the Cat code.
- Simplified Storage->setup and removed clear/reset nonsense
- Added Spanish lexion provided by Diego Kuperman
- Abstracted Iterator and added subclasses for lists, DBIC resultsets and storage results
- Added result_iterator_class to Base using default of Handel::Iterator::Results
- Storage now leaves DBIC result_class alone and returns Handel::Iterator::DBIC iterators for search/search_items
- Cart/Order now use Handel::Iterator::Results iterator
- Split DBIC specific storage into Storage::DBIC w/ massive tests
- Started moving news tests to Handel::Test w/ better db deploy/var directory
- Split Manual Storage into Storage/Storage::DBIC
- Added param checks to many Storage methods
- Replaced old Makefile warnings w/ mention of Test::More and DateTime compares
- Added Perl::Critic tests for my personal gratification. None pass yet. :-)
- currency_columns gets/sets list instead of arrayrefs to better match generic columns/primary_columns
- Bumped DBIC requirement to 0.08 (use -current for now)
- Reworked Catalyst Helpers for Models (Controllers are still broken)
A Personal Note About Handel/Mango
Tags: mango (3)
I may moderate this off topic, but I felt it important that my statements below be made public for the sake of posterity given recent events.
<begin long brain spew>
As anyone who has read the pod or Changes file knows, I started working on Handel at the end of 2004. Handel started as a hobby project, born out of the need to convert an ASP site to LAMP and out of frustration have having to write Yet Another Damn Shopping Cart without many modules to support it [on CPAN]. In the year and a half since then, Handel 0.33 has become pretty stable with the help of a lot of tests, some kind patch submitters and TDD goodness; and we're on the cusp of 1.0, which I hope will prove to be the best release yet.
But I digress. You knew that already. The purpose of this email is to hopefully clarify my intentions with Handel and Mango as well as ward off any future problems that may arise due to my [maybe not so] unique personality traits. :-)
As I suspect most true 'geeks coders' are, I take my projects to a personal level. They're not just bits of code to put me on the fast path to CPANTS fame but instead, are obsessions. If I can't figure something out, something is broken or tests don't pass, I can't sleep; literally. I've had dreams about code I'm working one. (*cough*loser*cough*).
I liken Handel to an earwig of that tune you can get out of your head (Ring,Ring,Ring,Ring,Ring,Ring,Ring: Banana Phone!). The only way to get rid of it is to sing the tune over and over until your brain loses interest. The same is true for this project for me. I'm pushing to get everything in my head out into 1.0 so I can set it free and move on. If that makes me a control freak, then so be it. You caught me.
Since Handel is a 'personal obsession' for me, I've purposefully kept it a one man show where I can make all of the decisions and ultimately, all of the mistakes as well. I do appreciate the interest in Handel so far, as well as the patches submitted by various guilty parties. So, if you feel like I have ignored you, or spurned your offers of patches for rewrites and such, my apologies. It's not personal. It's just men battling my control-freak-obsession demons. But have no fear. Once I finish my 1.0 obsession, I fully intended to start handing out commit bits on the repo and finding a victim or two to start taking on special godlike powers. I do acknowledge that is what is needed for Handel to grow and succeed and live on.
With the recent approval of the Mango grant by The Perl Foundation, I now find myself on the cusp of another 'personal obsession'. I've made my promises for a price bed, and now have to lay in it and frankly, it scares me. Mango is a project born out of my desire to help bring Catalyst/Handel/Perl to the forefront of this Web 2.0 RoR age we live in rather than playing the bystander role. I think Catalyst is better, and I want to prove it.
So, as I work to put one personal obsession to rest, and embark on new one, I will undoubtedly piss someone off, make someone feel like their help, opinions, patches or help aren't wanted or welcome. You've been warned. It isn't personal, usually not intentional, and it's definitely not permanent.
Game on.
-=Chris
TPF Grant Proposal Approved: Mango
Tags: catalyst (5) : mango (3)
Much to my surprise, The Perl Foundation has approved the grant proposal for Mango. For those who hadn't heard about Mango yet, it is going to be an "ecommerce solution" built using Catalyst+Handel. The main goal of Mango is to be one of those 'Web 2.0' buzzword applications [but better] to help promote Catalyst outside of the circle of us who already know its virtues.
Please feel free to add any comments, thoughts, concerns, requirements etc to the Mango wiki space.
Right now, I'm still pushing hard to get Handel 1.0 done so I can dole out maint bits and concentrate on Mango. I'm also a bit in shock about the grant. I'm a little bit hyped, a whole lot scared, and quite a bit rock and roll. Here's hoping that I can live up to the promises I've made and not make us all look bad in the process. :-)
-=Chris
Handel 0.99_05 Released
Tags: changes (26) : development release (16)
It's that time of the month again. Another round of bits and bytes has escaping from the coding monkeys. These new bits and bytes formed:
- Changed create_result in Base to create_instance
- Added create/search/uuid_maker/copyable_item_columns to Storage
- Storage->clone now clones even with an active schema instance
- Schema configuration in Storage now uses a clone of the item_classes storage instead of the original, and redirects result->storage
- Cart/Order/Items now use storage create/search
- Cart/Order add() now use copyable_item_columns when passed objects
- Cart/Order add() now look for source columns and methods to fill destination column values
- Various pod fixes
- Added more tests for create_instance/clone changes
- Cart/Order/Item new/load/destroy now take \%options, specifically $options{storage}
- Order->new $process argument moved into \%options
- Order->copy_cart_items now just uses add
- Added checkout_class to Storage
- Storage now uses exception_action rather than dbh->{HandleError}
- Storage process_error now creates a Storage Exception for unknown errors
- Moved storage settings in Cart/Order Item into Storage::Cart/Order::Item
- Applied Catalyst Helper test patch from Todd W.
- Added Storage::Result, which is now returned by storage actions
- Handel::Currency convert now returns a new currency object
- Cart/Order/Item now use generic result objects
- Cart/Order clear now returns result of action like delete does
- Renamed Cart/Order/Item new() to create()
- Renamed Cart/Order/Item load() to search()
- Added load/new/items to Compat.t w/subclass tests
At this point, I think I'm close to being done rearranging the API and there's even a good bit of compat tests for those -4 people using the old API in subclasses. Now that the big stuff is out of the way, it's time to get busy reworking the Catalyst helpers and I still need to fix the demo code and get some good Cookbook stuff in place.
CatInABox Update
Tags: catalyst (5)
With a little prompting on #catalyst, I've updated CatInABox to use Catalyst 5.7001. I've also updated a few of the plugins including View::TT, Template-Toolkit and Authentication. As always, you can download a copy from the downloads directory.
For the brave Win32 users out there, I've also posted the CatInABox directory I use on my USB thumb drive that includes just about everything plus the kitchen sink compiled for Perl 5.8.x on win32.
Happy coding.
Handel 0.99_04 Released
Tags: changes (26) : development release (16)
Just because I can't seem to stop, here's another development release which includes the following changes:
- Schema configuration now finally uses load_components without the Class::C3 recalc slowdown
- Validation component now uses throw_exception/next::method now that load_components is used
- Major pod cleanup
- Excised all forms of RETURN_AS
- TEST_SPELLING is now TEST_POD, which all pod tests now use
- Order created/updated fields now return DateTime objects using DBIx::Class::InflateColumn::DateTime
- Excised remaining UNIVERSAL::isa mistakes. Blessed is your friend
- Order created/updated fields now default to DateTime->now
Handel 0.99_03 Released
Tags: changes (26) : development release (16)
Once again, the well trained monkey servants have put forth a new development version of Handel. Their master coding includes:
- Fixed AxKit Exception Error (RT#19707,TKP)
- Added start of Handel::Manual
- uuid is now new_uuid, and now in Storage
- Removed setup_columns_accessors in favor or create_accessors in Base
- Cart/Order/Item classes now have a instance of storage instead of subclassing it
- Added Handel::Base as super class for Cart/Order/Item classes
- Added Handel::Storage tests
- Handel::Storage now does all schema configurate during first schema_instance call
- Added Handel::Storage::new/setup
- Cart/Order/Item classes now delegate direct schema access to Storage
- Added validation/constraint/default_value_class to Storage
- Converted Storage from Class::Data::Accessor to Class::Accesssor::Grouped
- Refactored injection of components into schema source classes
- schema_instance now creates a clone using compose_namespace and does its component injection into the clone instead
- Added currency_class/currency_columns to Storage
- Added value() to Handel::Currency
- Added autoupdate to results that inherits from storage->autoupdate
- Added basic compatibility layer for older subclasses
If you are one of the brave souls that is using 0.99_01/02, I apologize now. There has been a major rewrite of the storage layer, and you will have to change code. But hey, that's why they call it a development release right? :-)
Handel 0.99_02 Released
Tags: changes (26) : development release (16)
Fresh from the assembly line, I present Handel 0.99_02:
- Fixed test counts in t/order_new.t (RT#19700,TKP)
- Moved _error_handler in Storage to process_error and set dbh->{HandelError} using $self->can
- Use blessed when setting schema_instance in Storage
- Abstracted schema setup when setting Storage->schema_instance($schema)
Handel 0.99_01 Released
Tags: changes (26) : development release (16)
After what seems like an eternity, the first dev version of the upcoming Handel 1.0 is now available. It should be available on CPAN shortly, or you can get the latest version from SVN here.
Changes in the release include:
- RETURN_AS has been removed from the API
- Data filters now take SQL::Abstract syntax for wildcards
- Converted from Class::DBI to DBIx::Class schemas
- Handel::ConfigReader is now a singleton via instance()
- Handel::DBI is deprecated in favor if Handel::Storage
- Moved column defaults into Handel::Components::DefaultValues
- Added constraint_cart_name
Note: Handel 1.0 is not API compatible with Handel 0.32 and older. One of the major goals of 1.0 was to correct some bad API decisions I shouldn't have made to begin with. I've tried to keep the incompatible changes down to a minimum. The main change is that new/load/item now always returns an interator in scalar context, and a list in list context. New/load/items no longer returns a cart/item object if there is only one result. That was bad magic that I shouldn't have ever done.
Handel IRC Channel
Tags: irc (1)
For those of you who need help, or just want to abuse me [claco] on IRC, feel free to join the #handel channel on irc.perl.org. Beware or purl. She's a saucy little infobot. :-)
Subversion Repository Relocated
Due to some issues that forced me to move the site from mod_perl to mod_fastcgi, the Subversion repository is no longer available at /svn. The repository is now located at http://svn.handelframework.com/CPAN/Handel/.
Nevermind. The subversion repository is right where it's always been. Pay no attention to the man behind the curtain.
Scratch that. Reverse it. I had to remove the repository again. While it appeared to work properly, it didn't really work completely.
Catalyst Powered Site
Tags: catalyst (5)
Rather than updating Handel or working on a Catalyst/Handel powered ecommerce solution, I decided to spend some time and make the site a little easier to work with. Up until this week, it's been a mish mosh of MovableType templates, Apache handlers, MediaWiki, static pages, and the Catalyst powered demo. I've now 'fixed the glitch'.
The site now shares the same templates and rendering tech (Template Toolkit) running on top of Catalyst. Browing the source now uses a Catalyst SVN model. The wiki is now Catalyst powered using Textile, with no logins required.
Handel 0.32 Released
Tags: changes (26) : release (11)
Just because I can...
- Added order_reconcile.t tests
- Order::reconcile now uses copy_cart/copy_cart_items
Handel 0.31 Released
Tags: changes (26) : release (11)
Live from the monkey pit, Handel version 0.31 had been scrape from the
programming mouldes and set free against the world. Changes in 0.31 include:
- Fixed order creation from cart uuid to use cart_class
- Fixed order reconcile to use cart_class
- Added ccissuenumber, ccstartdate and ccenddate temp fieds to orders
- Use version to property compare CDBI versions (3.0.1 doesn't use version)
- Added clear_messages to Handel::Checkout
- Checkout plugin handlers are now run in the order of their declared preference number
- Fixed Carts item_class to return a default
- Fixed Carts items(), delete(), destroy() and restore() to use item_class
- Fixed Orders item_class to return a default
- Fixed Orders items(), delete() to use item_class
- PRAGMA synchronous = OFF and temp_store = MEMORY for SQLite tests to reduce disk trashing during testing
- Handel::Exception::Taglib subclasses Apache::AxKit::Exception if available to play nice with AxKits exception handling
- Added destroy() to Handel::Order
- Added subclassing tests to current cart/order/checkout tests
- Checkout::process now clears the stash before the call to plugin init, so plugins can set stash data
- Added Handel::Checkout::Stash to checkout process
- Added Checkout->stash_class to allow the use of a custom stash class in Checkout subclasses
- Checkout::new how takes the stash option, which should contain a instance of a Handel::Checkout::Stash subclass
- Added t/checkout_stash,t tests
- Class setter methods (order_class, item_class, stash_class, cart_class) now try to require the specified class
Happy coding.
Handel 0.30 Released
Tags: changes (26) : release (11)
Back by popular demand, I present to thee Handel 0.30. It includes the following coding mistakes:
0.30 Fri Dec 23 22:52:23 2005
- Constraint constraint_price now accepts 0 as a valid value
- Added order_class to Handel::Checkout to allow Checkout->order to create subclassed versions of Order objects
- Added cart_class to Handel::Order to allow Order->new to create subclassed versions of Cart objects
- Added copy_cart/copy_cart_items in Handel::Order so subclasses can customize order creation from custom carts
- When copying cart items into a new order, only columns in Handel::Cart::Item instead of all columns found
Handel 0.29 Released
Direct from the monkey pit, Handel 0.29 is out with the following changes:
0.29 Thu Dec 8 20:12:38 2005
- Constraint constraint_checkout_phase now checks CHECKOUT_ALL_PHASES
- Added add_phase to Handel::Checkout to add custom phases
- Removed APR::UUID usage under OpenBSD
- Disabled Apache-Tests tests by default and added TEST_HTTP to enable them
- Added pod to Handel::Order about temporary fields
0.28 Fri Nov 25 15:09:25 2005
- Added pod_spelling.t tests which don't run by default
- Corrected a boatload of spelling errors :-)
- Catalyst Helpers now require Catalyst 5.56 to deal with short vs. long M/V/C modules names.
DBIx::Class Powered Handel
Tags: dbic (1)
After some tinkering and constantly bothering #dbix-class with questions, there is a development version of Handel running on DBIx::Class.
0.28_01 Sun Nov 27 15:13:25 2005
- Converted from Class::DBI to DBIx::Class
- constraint_price now accepts 0 as a valid value
- price fields are now set to 0 by default
- shopper constraint is now working can't be empty during new
Beware! There be dragons!
Even though DBIx::Class 0.04 is the prereq, please use the latest from the DBIC trunk. This fixes problems with inflated columns not being updated during Checkout::process().
There is currently a 'feature' when updating item columns that causes changes to be lost. For now, please always retrieve items using array context and not the iterator in scalar context for now.
You can download the dist here:
http://handelframework.com/downloads/Handel-0.28_01.tar.gz
You can also get the latest from the DBIC branch here:
http://handelframework.com/svn/CPAN/Handel/branches/DBIC/
-=Chris
Handel Article on Perl.com
Tags: articles (2)
There's a new article this week on Perl.com entitled Building E-Commerce Sites with Handel. It's a basic introduction to Handel and how to use Handel and Catalyst to quick start your ecommerce site. Check it out!
Handel 0.27 Released
Tags: changes (26) : release (11)
Fresh from the coding monkey pit, Handel 0.27 is out.
- Tested with Class::DBI 3.0.12
- Require Apache-Test 1.27
- Added IfDefine APACHE1 block to ignore AxKit under Apache2
- Catalyst helper tests now use short => 1 under Catalyst 5.5
- Added Traditional Chinese L10N by Kang-min Liu
Handel 0.26 Released
Tags: changes (26) : release (11)
Yet another one from the monkey factory.
0.26 Wed Oct 5 21:12:56 2005
- Tested with Class::DBI 3.0.9
- Fixed Exception creation after triggers under Class::DBI 3.0.9
- Handel::DBI now uses Class::DBI->insert in Class::DBI 3.0.9+
- Added subclassing tests
- Changed item_class to use class name instead of __PACKAGE__ See README for known issues
If you're making extensive use of subclassing of the cart, specifically Handel::Cart::Item and setting the ->item_class() method, this is a must upgrade. You will need to upgrade your Class::DBI install to 3.0.8 or greater to take advantage of the has_many fix for for subclasses.
Handel 0.25 Released
Tags: changes (26) : release (11)
From the bowls of the SVN server, I'm pleased to announce the release of Handel 0.25. It should be available on CPAN shortly.
0.25 Sun Oct 3 20:15:35 2005
- Removed -T from Catalyst helpers tests to keep older versions of File::Path/IO::File happy
- Fixed Catalyst helper test failures due to setting $FindBin::Bin
- Fixed mod_perl detection and code to deal with MP2 and PerlModule/PerlLoadModule
- Fixed Catalyst checkout/order helpers error when trying to get the value of a cookie that didn't already exist
- Fixed obscure bug with Data::UUID crashing in the first call only under MP2
- Added support to create uuids form APR::UUID if it's available
0.24 Mon Sep 26 20:28:13 2005
- Added Catalyst Helper tests
- Added pod notes about Data::FormValidator/HTML::FillInForm in helpers
Handel 0.23 Released
Tags: changes (26) : release (11)
Rinse. Lather. Repeat.
Because I'm an idiot, here is Handel 0.23. It fixes a problem where Catalyst helpers weren't replacing :: globally when creating uri in templates.
Handel 0.22 Released
Tags: changes (26) : release (11)
Handel 0.22 has been released and it's on its way to a CPAN mirror nearest you. This is a bugfix release fixing some Catalyst helper problems including:
- Removed leftover hardcoded MyApp::M calls in Catalyst controllers
- Catalyst controller helpers now require Data::FormValidator 4.00+
- Tested with Class::DBI 3.0.8
- Catalyst helpers are smarter about doing the right thing with MyApp::M(odel)::Cart vs. Cart in arguments
Handel 0.21 Released
Tags: changes (26) : release (11)
The Catalyst helpers in Handel versions before 0.21 have a problem creating files from within the __DATA__ sections on *nix. This is due to a bug in Catalyst::Helper in addition to the fact that the CPAN dist was made on my machine using XP;causing LF/CRLF issues. :-)
A fix has been commited to the Catalyst trunk. To make things easier for the common man, I've also set the svn:eol-style to LF for all of the Catalyst helpers in the Handel svn repository and uploaded Handel version 0.21 to CPAN. This version contains updated Catalyst helpers with Unix line endings that should work on Catalyst 5.33 and below.
A big thanks to Andy Grundman and Bernard FRIT for the help tracking this problem down.
Handel 0.20 Released
Tags: changes (26) : release (11)
Handel 0.20 has been released and should be available on a CPAN mirror nearest you shorty. Among the change are:
- Catalyst controllers now use Data::FormValidator along with better error handling
- Catalyst Checkout contoller now uses HTML::FillInForm to maintain form state after a POST when there are Data::FormValidator/other errors
- Tested with Class::DBI 3.0.7
- Added add_columns to Handel::DBI to add custom columns to Cart/Items/Order/Items
- Added item_class to Cart/Order classes to specify the item class to be returned from add/items
- Added HandelDBIDSN config variable
Handel 0.19 Released
Tags: changes (26) : release (11)
After a few late weeks of Catalyst hacking and prodding on #catalyst, I'm pleased to announce the release of Handel 0.19 to the world. The most notable changes are:
- Added Catalyst Helpers/Scaffold support
- Added handel.sqlite.sql SQL Create Script
- Updated French lexicon with more human-friendly translations sent from Bernard FRIT
- Various README fixes patched by Uwe Voelker
- Added Cart::destroy method to actually delete cart record
- Order::new now copies shopper id from cart when creating a new order from an existing shopping cart when no order shopper is specified
- Added temp credit card fields to orders: ccn cctype ccm ccy ccvn ccname
- Added CHECKOUT_PHASE_FINALIZE to checkout phases
- Added MarkOrderSaved and AssignOrderNumber checkout plugins
- Added Order::reconcile to keep cart items and order items synced
It should be available on a CPAN mirror nearest you by morning.
Browse Module Documentation Online
With a little bit of tinkering, you can now browse the module documentaton (POD) online. The html version of the module docs are built every hour from the latest code in the HEAD revision of the Handel repository trunk.
Source Browsing Completed. Respository Moved.
Tags: repository (1) : source (1) : subversion (1)
After another fit of tinkering, I have finished the changes to allow styled browsing of the Handel source online. You can browse the Handel source tree at /source/. In the past, this was also the url of the Subversion respository. Access to the Subversion repository has been relocated to http://svn.handelframework.com/CPAN/Handel/.
Happy browsing!
YAPC::EU, Catalyst and Handel
Tags: catalyst (5) : demo (1) : scaffold (2) : yapc (1)
Close, but no Bob Seger.
Last week, Marcus Ramberg, one of the Catalyst core developers, expressed interest in demoing Handel integration into Catalyst at his presentation at YAPC::EU. After a few furious days of hacking, I managed to toss together a scaffold for Catalyst that created all of the necessary Handel->Catalyst naughty bits.
Unfortunately, he ran out of time and didn't get around to Handel, or the demo before it. Such is life. At least we're mentioned in the slides. :-)
Many thanks to the Catalyst folk for all the help so far and answering the silly questions time and time again. I'm flattered that they took the time to even consider Handel, let alone put it into their YAPC presentation.
Wiki Logins Opened
Tags: wiki (2)
When I installed the wiki, I made the decision to turn on white-list edits (edit requires login) and disable account creation by non sysops. This was intentionally tight to keep out spam until I had more time to finish setting up the server and the website.
Today, presumably after posting this sites url on #catalyst, I got this comment about someone wanting to help but didn't want to be bother with waiting on me to create a login. Fair enough.
Not being one to look a gift horse in the mouth, I've changed the wiki configuration. Anyone can now create account and edit wiki content. Anonymous editing is still forbidden. IF things get out of hand, I'll probably reverse this decision.
Handel Wiki Available
Tags: wiki (2)
Sure it's empty. Sure it's read-only unless you have a Sysop approved account. Either way, the Handel Wiki is now online. Hopefully it can become all things how-to over time.
Want to get started adding to the wiki? Just request an account and get started today!
Subversion Repository Now Available!
Why stop with a blog right? I've moved the subversion repository to the new web server. You may now browse the Handel source at your leasure and checkout code until the cows come home.
Rather split out Handel and create a no repo, including trying to keep the property history, I decided it was better just to move my entire repository. Now people can access or stuff I'm tinkering with as well.
Handel Blog
Welcome to the Handel website. Hopefully this will be one of many posts on the Handel blog. I'm still working on getting the wiki and the rest of the site in place as well as getting the Subversion repository online.
Stay tuned.
