DBIx-Class-PseudoEnum
view release on metacpan or search on metacpan
README.mkdn view on Meta::CPAN
__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](https://chateau-logic.com/content/why-we-should-not-use-enums-databases)
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](https://metacpan.org/pod/Carp)
- [DBIx::Class](https://metacpan.org/pod/DBIx%3A%3AClass)
- [Modern::Perl](https://metacpan.org/pod/Modern%3A%3APerl)
- [Sub::Quote](https://metacpan.org/pod/Sub%3A%3AQuote)
# 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](https://metacpan.org/pod/Try%3A%3ATiny) 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
( run in 1.693 second using v1.01-cache-2.11-cpan-524268b4103 )