Class::Meta::Express - Concise, expressive creation of Class::Meta classes package My::Contact; use Class::Meta::Express; class { meta contact => ( default_type => 'string' ); has 'name'; has contact => ( required => 1 ); } This module provides an interface to concisely yet expressively create classes with Class::Meta. Although I am of course fond of Class::Meta, I've never been overly thrilled with its interface for creating classes: package My::Thingy; use Class::Meta; BEGIN { # Create a Class::Meta object for this class. my $cm = Class::Meta->new( key => 'thingy' ); # Add a constructor. $cm->add_constructor( name => 'new' ); # Add a couple of attributes with generated accessors. $cm->add_attribute( name => 'id', is => 'integer', required => 1, ); $cm->add_attribute( name => 'name', is => 'string', required => 1, ); $cm->add_attribute( name => 'age', is => 'integer', ); # Add a custom method. $cm->add_method( name => 'chk_pass', code => sub { return 'code' }, ); $cm->build; } This example is relatively simple; it can get a lot more verbose. But even still, all of the method calls were annoying. I mean, whoever thought of using an object oriented interface for declaring a class? (Oh yeah: I did.) I wasn't alone in wanting a more declarative interface; Curtis Poe, with my blessing, created Class::Meta::Declare, which would use this syntax to create the same class: package My::Thingy; use Class::Meta::Declare ':all'; Class::Meta::Declare->new( # Create a Class::Meta object for this class. meta => [ key => 'thingy', ], # Add a constructor. constructors => [ new => { } ], # Add a couple of attributes with generated accessors. attributes => [ id => { type => $TYPE_INTEGER, required => 1, }, name => { required => 1, type => $TYPE_STRING, }, age => { type => $TYPE_INTEGER, }, ], # Add a custom method. methods => [ chk_pass => { code => sub { return 'code' }, } ] ); This approach has the advantage of being a bit more concise, and it is declarative, but I find all of the indentation levels annoying; it's hard for me to figure out where I am, especially if I have to define a lot of attributes. And finally, everything is a string with this syntax, except for those ugly read-only scalars such as $TYPE_INTEGER. So I can't easily tell where one attribute ends and the next one starts. Bleh. What I wanted was an interface with the visual distinctiveness of the original Class::Meta syntax but with the declarative approach and intelligent defaults of Class::Meta::Declare, while adding expressiveness to the mix. The solution I've come up with is the use of temporary functions imported into a class only until the end of the class declaration: package My::Thingy; use Class::Meta::Express; class { # Create a Class::Meta object for this class. meta 'thingy'; # Add a constructor. ctor new => ( ); # Add a couple of attributes with generated accessors. has id => ( is => 'integer', required => 1 ); has name => ( is => 'string', required => 1 ); has age => ( is => 'integer' ); # Add a custom method. method chk_pass => sub { return 'code' }; } That's much better, isn't it? In fact, we can simplify it even more by setting a default data type and eliminating the empty lists: package My::Thingy; use Class::Meta::Express; class { # Create a Class::Meta object for this class. meta thingy => ( default_type => 'integer' ); # Add a constructor. ctor 'new'; # Add a couple of attributes with generated accessors. has id => ( required => 1 ); has name => ( is => 'string', required => 1 ); has 'age'; # Add a custom method. method chk_pass => sub { return 'code' }; } Not bad, eh? I have to be honest: I borrowed the syntax from Moose. Thanks for the idea, Stevan! Class::Meta::Express exports the following functions into any package that "use"s it. But beware! The functions are temporary! Once the class is declared, the functions are all removed from the calling package, thereby avoiding name space pollution and allowing you to create your own functions or methods with the same names, if you like, after declaring the class.

Functions
meta meta 'thingy'; This function creates and returns the "Class::Meta|Class::Meta" object that creates the class. The first argument must be the key to use for the class, which will be passed as the "key" parameter to "Class::Meta->new". Otherwise, it takes the same parameters as "Class::Meta->new", as well as the following additions: If you've subclassed Class::Meta and want to use your subclass to define your classes instead of Class::Meta itself, specify the subclass with this parameter. The name of a data type that you'd like to be the default for all attributes created with "has" that don't specify their own data types. Installs an "import()" method into the calling name space that exports the express functions. The trick is that, if you've specified values for the "meta_class" and/or "default_type" parameters, they will be used in the "meta" function exported by your class! For example: package My::Base; use Class::Meta::Express; meta base => ( meta_class => 'My::Meta', default_type => 'string', reexport => 1, ); build; And now other classes can use My::Base instead of Class::Meta::Express and get the same defaults. Say that you want My::Contact to inherit from My::Base and use its defaults. Just do this: package My::Contact; use My::Base; # Forces import() to be called. use base 'My::Base'; meta 'contact'; # Uses My::Meta has 'name' # Will be a string. build; If you need your own "import()" method to export stuff, just pass it to the reexport parameter: meta base => ( meta_class => 'My::Meta', default_type => 'string', reexport => sub { ... }, ); Class::Meta::Express will do the right thing by shifting execution to your import method after it finishes its dirty work. The parameters may be passed as either a list, as above, or as a hash reference: meta base => { meta_class => 'My::Meta', default_type => 'string', reexport => 1, }; ctor ctor 'new'; Calls "add_constructor()" on the Class::Meta object created by "meta", passing the first argument as the "name" parameter. All other arguments can be any of the parameters supported by "add_constructor()": ctor new => ( label => 'Foo' ); Or, a if you have Class::Meta 0.53 or later, the second argument can be a code reference that will be passed as the "code" parameter to "add_constructor()": ctor new => sub { bless {} => shift }; If you want to specify other parameters and the code parameter, do so explicitly: ctor new => ( label => 'Foo', code => sub { bless {} => shift }, ); The parameters may be passed as either a list, as above, or as a hash reference: ctor new => { label => 'Foo', code => sub { bless {} => shift }, }; has has name => ( is => 'string' ); Calls "add_attribute()" on the Class::Meta object created by "meta", passing the first argument as the "name" parameter. All other arguments can be any of the parameters supported by "add_constructor()", as in the example above. If the "default_type" parameter was specified in the call to "meta", then the type (or "is" if you have Class::Meta 0.53 or later and prefer it) can be omitted unless you need a different type: meta thingy => ( default_type => 'string' ); has 'name'; # Will be a string. has id => ( is => 'integer' ); # ... The parameters may be passed as either a list, as above, or as a hash reference: has id => { is => 'integer' }; method method 'say'; Calls "add_method()" on the Class::Meta object created by "meta", passing the first argument as the "name" parameter. An optional second argument can be used to define the method itself (if you have Class::Meta 0.51 or later): method say => sub { shift; print @_, $/; } Otherwise, you'll have to define the method in the class itself (as was required in Class::Meta 0.50 and earlier). If you want to specify other parameters to "add_method()", just pass them after the method name and explicitly mix in the "code" parameter if you need it: method say => ( view => Class::Meta::Protected, code => sub { shift; print @_, $/; }, ); All other arguments can be any of the parameters supported by "add_meta()" The parameters may be passed as either a list, as above, or as a hash reference: method say => { view => Class::Meta::Protected, code => sub { shift; print @_, $/; }, }; class class { # Declare function. } Yes, the "class" keyword is secretly a function. It takes a single argument, a code reference, for which may omit the "sub" keyword. Cute, eh?. It simply executes the code refernce passed as its sole argument, removes the "class", "meta", "ctor", "has", "method", and "build" functions from the calling name space, and then calls "build()" on the Class::Meta object created by "meta". build build; This function is a deprecated holdover from before version 0.04. It used to be that there was no "class" keyword and you had to just call the rest of the above functions and then call "build" when you're done. But who liked that? It was actually a bitter pill among all this sweet, sweet sugar. But no more; "build" will likely be removed in a future version. It is possible to override the functions exported by this module by subclassing it (after a fashion). Say that you wanted to change the "meta()" function so that it forces all attributes to default to a the type "string". Just override the function like so: package My::Express; use base 'Class::Meta::Express'; sub meta { splice @_, 1, 0, default_type => 'string'; goto &Class::Meta::Express::meta; } The trick here is to set @_ and then "goto &Class::Meta::Express::meta". This is so that the package that calls this function will be seen as the caller and therefore the Class::Meta object will be properly created for that package. Why would you want to do all this? Well, perhaps you're building a lot of classes and don't want to have to repeat yourself so much. So now all you have to do is use your My::Express module instead of Class::Meta::Express: package My::Person; use My::Express; class { meta person => (); has name => (); } And now you've created a new class with the string type attribute "name". This is the module that's actually doing all the work. Class::Meta::Express just offers a sweeter interface for creating new classes with Class::Meta. You'll still want to know all about Class::Meta's introspection capabilities, type constraints, and more. Check it out! Curtis Poe's declarative inteface to Class::Meta. Deprecated in favor of this module. Please send bug reports to <bug-class-meta-express@rt.cpan.org>. David Wheeler <david@kineticode.com> Copyright (c) 2006 Kineticode, Inc. All Rights Reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.