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

NAME

DBIx::Class::PseudoEnum - Schema-based enumerations independent of database

VERSION

version 1.0003

SYNOPSIS

   # In your Schema::Result class:
   __PACKAGE__->load_components('PseudoEnum');

   # Load your enumerations into source_info directly
   __PACKAGE__->table('contraption');
   __PACKAGE__->source_info(
   {
      enumerations => { 'status' => [qw/Sold Packaged Shipped/] }
   }

   # Or use the handy-dandy methods
   # (leave off the __PACKAGE__ if you're using DBIx::Class::Candy)
   __PACKAGE__->table('doodad');
   __PACKAGE__->enumerate( 'status', [qw/Ordered In-Stock Out-Of-Stock/] );
   __PACKAGE__->enumerate( 'color',  [qw/Black Blue Green Red/] );

   # Properly handle value collisions in the same table
   # (both fields here could create an 'is_blue' method!)
   __PACKAGE__->table('doohickey');
   __PACKAGE__->enumerations_use_column_names();
   __PACKAGE__->enumerate( 'field1', [qw/One Two Three Four Blue/] );
   __PACKAGE__->enumerate( 'field2', [qw/BLUE RED GREEN/] );

   # Later, in your application:
   # On Results:
   $doodad->is_ordered;                      # Boolean, true if doodad.status == 'Ordered'
   $doodad->is_blue;                         # Boolean, true if doodad.color == 'Blue'
   $doodad->update({ status => 'Dunno'});    # croaks!
   $doodad->update({ status => 'ordered' }); # croaks!
   $doodad->update({ color => 'Black' });    # okay!
   # The module will try to pass this on to the rest of the update() method; if the 
   # field is nullable, it'll work.
   $doodad->update({ color => undef });   

   # On ResultSets:
   $doodad_rs->create({ status => 'Dunno' });    # croaks!
   $doodad_rs->create({ status => 'ordered' });  # croaks!
   $doodad_rs->create({ color  => 'Black' });    # okay!
   $doodad_rs->is_blue                           # Returns a ResultSet where doodad.color == 'Blue'

   # With enumerations_use_column_names:
   $doohickey->is_blue                    # "no such method"
   $doohickey->field1_is_blue             # Now it does what you want!

DESCRIPTION

Enumerations can be a bit of a pain. Not all databases support them equally (or at all), which reduces the portability of your application. Additionally, there are some philosophical and practical problems with them. Lookup tables are an alternative, but maybe you don't want to clutter up your DB with single-column lookup tables.

But searching around the interwebs, no one seems to mind enumerating valid values for a data entity within the application layer. So that's what this module provides: a way to put the enumeration in the DBIx::Class schema, and have it enforced within the application, invisibly to the DB.

SUBROUTINES/METHODS

enumerate( $field, [$value1, $value2,...])

This is the brains of the outfit, right here. The field must be a column in your table, and the values must be sent in as a hashref. Easy and obvious.

This method spins off methods in your Result and ResultSet classes for each value in your list, of the form is_value, which return a boolean (zero or one) if the current value of the enumerated field is the specified value. If the field is nullable, you do not get an is_undef method. Yet. See LIMITATIONS below.

enumerations_use_column_names()

Calling this function will require the schema to create methods with the column name included. E.g. instead of is_value, you get fieldname_is_value methods. It only operates on the Result class where you call it.

DEPENDENCIES

Carp
DBIx::Class
Modern::Perl
Sub::Quote

BUGS AND LIMITATIONS

Bugs? What bugs? (No, really. If you find one, open an issue, please.)

The following limitations are (currently) present:

Text columns only!:

At present, you may only use this with text-based columns.

Collisions:

If you have two enumerated fields in a table, and their lower-cased, underscore-punctuated values collide, the code will choose the last one that you defined with an enumerate statement. In this instance, you should probably use enumerations_use_column_names to force column names to be listed.

If you have multiple enumerated values in a single field that collide on their lower-cased, underscore-punctuated values, then any of them will respond to test methods: e.g. if you have BLUE and blue values in an enumeration, then is_blue will be true for either one. (...but why would you do that?)

undef

If a field is nullable in the DB and the schema, you do not get an is_undef method. Yet.

Case-insensitive

To make the method name, this module replaces all non-alphanumeric characters with underscores, and smashes case on all upper-case letters. This may contribute to collisions (see above).

Adding to existing code

If you have an application where you add this module's functionality after there is data in the table, it will not complain about already-existing invalid values in enumerated fields. You will not, of course, be able to test for those values, nor set any other record to that value, unless you enumerate it.

Error handling

If you've got a Row result, and try to update an enumerated field with an invalid value, it'll croak. That's probably what you want, but if you have that in, for instance, a Try::Tiny block, you then have a "dirty" column for your enumerated column, and the next update may mess with you by going ahead and *doing the update to the invalid value*. You can do $result->discard_changes, and not have to reload your object. This isn't a bug, precisely, but it is a known quirk, one that I'd like to eradicate.

ROADMAP

I have these features in mind, going forward.

  • Handle non-text columns

  • Automatically detect and force collision behavior

  • Add an is_undef method for nullable fields

  • Option flag to make it work with case-sensitive enumerations

  • Method to hunt for 'invalid' values in the database and report

  • is_not_value methods

ACKNOWLEDGEMENTS

My boss at Clearbuilt really, really dislikes enumerations. Hopefully, this module will make them a bit easier for him to use.

Jason Crome encourages this sort of craziness fairly often.

AUTHOR

D Ruth Holloway <ruth@hiruthie.me>

COPYRIGHT AND LICENSE

This software is copyright (c) 2023 by D Ruth Holloway.

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