Tom Molesworth > EntityModel-0.102 > EntityModel

Download:
EntityModel-0.102.tar.gz

Dependencies

Annotate this POD

Website

CPAN RT

Open  0
View/Report Bugs
Module Version: 0.102   Source  

NAME ^

EntityModel - manage entity model definitions

VERSION ^

version 0.102

SYNOPSIS ^

 use EntityModel;
 # Define model
 my $model = EntityModel->new->load_from(
        JSON => { entity : [
                { name : 'article', field : [
                        { name : 'idarticle', type : 'bigserial' },
                        { name : 'title', type : 'varchar' },
                        { name : 'content', type : 'text' }
                ], primary => { field : [ 'idarticle' ], separator : ':' }
         ] }
 );
 # Apply PostgreSQL schema (optional, only needed if the model changes)
 $model->apply('PostgreSQL' => { schema => 'datamodel', host => 'localhost', user => 'testuser' });
 # Create Perl classes
 $model->apply('Perl' => { namespace => 'Entity', baseclass => 'EntityModel::EntityBase' });

 my $article = Entity::Article->create(
        title => 'Test article',
        content => 'Article content'
 )->done(sub {
        my $article = shift;
        say "ID was " . $article->id;
 })->fail(sub {
        die 'Failed to create new article';
 });
 Entity::Article->find(
        title => 'Test article'
 )->first(sub {
        my $match = shift;
        $match->title('Revised title');
        die "Instances of the same object should always be linked, consistent and up-to-date"
                unless $article->title eq $match->title;
 });

DESCRIPTION ^

This module provides a data storage abstraction system (in the form of an Object Relational Model) for accessing backend storage from Perl and other languages. The intent is to take a model definition and generate or update database tables, caching layer and the corresponding code (Perl/C++/JS) for accessing data.

A brief comparison and list of alternatives is in the "MOTIVATION" and "SEE ALSO" sections, please check there before investing any time into using this module.

Eventually a full set of documentation will be added to http://entitymodel.com/documentation.html but for now see the examples further down in this document.

METHODS ^

new

Constructor. Given a set of options, will load any plugins specified (and/or the defaults), applying other config options via the appropriate plugins.

Typically run without options:

 my $model = EntityModel->new;

The exciting things happen elsewhere. See:

load_from

Read in a model definition from the given EntityModel::Definition-based source.

Parameters:

Common usage includes reading from inline Perl:

 $model->load_from(
  Perl => {
   name => 'kvstore',
   entity => [
    name => 'object',
    primary => 'iditem',
    field => [
     { name => 'iditem', type => 'bigserial' },
     { name => 'key', type => 'varchar' },
     { name => 'value', type => 'varchar' },
    ],
   ],
  }
 );

or the equivalent from JSON:

 $model->load_from(
  JSON => \q{
   "name" : "kvstore",
   "entity" : [
    "name" : "object",
    "primary" : "iditem",
    "field" : [
     { "name" : "iditem", "type" : "bigserial" },
     { "name" : "key", "type" : "varchar" },
     { "name" : "value", "type" : "varchar" }
    ]
   ]
  }
 );

save_to

Saves the current model definition to a definition.

Parameters:

You might use something like this to store the current model to a file in JSON format:

 $model->save_to(
  JSON => 'model.json'
 );

or this to copy everything from a source model to a target model (wiping everything in the target in the process):

 my $target = EntityModel->new;
 $source->save_to(
  model => $target
 );

load_component

Brings in the given component if it hasn't already been loaded.

Typically used by internal methods only.

add_support

Bring in a new EntityModel::Support class for this EntityModel::Model.

Example:

 $model->add_support(Perl => { namespace => 'Entity' });

add_storage

Add backend storage provided by an EntityModel::Storage subclass.

Example:

 $model->add_storage(PostgreSQL => { service => ... });

backend_ready

Returns true if all storage and cache backends are ready, false otherwise.

wait_for_backend

Requests an event to run after all backends signal readiness.

add_cache

Add backend cache provided by an EntityModel::Storage subclass.

Example:

 $model->add_cache(PostgreSQL => { service => ... });

add_plugin

Adds a plugin. Currently the definition of a 'plugin' is somewhat nebulous, but EntityModel::Web is one example.

transaction

Run the coderef in a transaction.

Notifies all the attached EntityModel::Storage instances that we want a transaction, runs the code, then signals end-of-transaction.

load_plugin

Used internally, see "add_plugin". Will disappear in the future.

handler_for

Returns the handler for a given entry in the EntityModel::Definition.

DESTROY

Unload all plugins on exit.

ENTITY MODELS

A model contains metadata and zero or more entities.

Each entity typically represents something that is able to be instantiated as an object, such as a row in a table. Since this module is heavily biased towards SQL-style applications, most of the entity definition is similar to SQL-92, with some additional features suitable for ORM-style access via other languages (Perl, JS, C++).

An entity definition primarily contains the following information - see EntityModel::Entity for more details:

Each entity may have zero or more fields:

Additional metadata can be defined for entities and fields. Indexes apply at an entity level. They are used in table construction and updates to ensure that common queries can be optimised, and are also checked in query validation to highlight potential performance issues.

The fields in an index can be defined as functions.

Constraints include attributes such as unique column values.

Models can also contain additional information as defined by plugins - see EntityModel::Plugin for more details on this.

USAGE ^

An entity model can be loaded from several sources. If you have a database definition:

 create table test ( id int, name varchar(255), url text );

then loading the SQL plugin with the database name will create a single entity holding two fields.

If you also load EntityModel::Plugin::Apply::Perl, you can access this table as follows:

 my $tbl = Entity::Test->create({ name => 'Test', url => '/there' })->commit;
 my ($entity) = Entity::Test->find({ name => 'Test' });
 is($orig->id, $entity->id, 'found same id');

IMPLEMENTATION ^

Nearly all classes use EntityModel::Class to provide basic structure including accessors and helper functions and methods. This also enables strict, warnings and Perl 5.10 features.

Logging is handled through EntityModel::Log, which imports functions such as logDebug.

Arrays and hashes are typically wrapped using EntityModel::Array and EntityModel::Hash respectively, similar in concept to autobox.

For error handling, an EntityModel::Error object is returned - this allows chained method calling without having to wrap in eval or check the result of each step when you don't care about failure. The last method in the chain will return false in boolean context.

OVERVIEW ^

The current EntityModel::Model can be read from a number of sources:

Aside from entities, models can also contain plugin-specific information such as site definition or database schema. It is also possible - but not recommended - to store credentials such as database user and password.

Once a model definition has been loaded, it can be applied to one or more of the following:

The SQL handling is provided as a generic DBI-compatible layer with additional support in subclasses for specific databases. Again, Note that the EntityModel::Support::SQL is intended for applying the model to the database schema, rather than accessing the backend storage. The EntityModel::Support classes apply the model to the API, so in the case of the database this involves creating and updating tables. For Perl, this dynamically creates a class structure in memory, and for C++ or JS this will export the required support code for inclusion in other projects.

In terms of accessing backend storage, each of the language-specific support options provides an API which can communicate with one or more backend storage implementations, rather than being tightly coupled to a data storage method. Typically the Perl backend would interact directly with the database, and C++/JS would use a REST API against a Perl server.

BACKEND STORAGE

Backend storage services are provided by subclasses of EntityModel::Storage.

CACHING

Cache layers are handled by EntityModel::Cache subclasses.

USAGE EXAMPLE

Given a simple JSON model definition:

 { entity : [
        { name : 'article', field : [
                { name : 'idarticle', type : 'bigserial' },
                { name : 'title', type : 'varchar' },
                { name : 'content', type : 'text' }
        ], primary => { field : [ 'idarticle' ], separator : ':' }
 ] }

this would create or alter the article table to meet this definition:

 create table "article" (
        idarticle bigserial,
        title varchar,
        content text,
        primary key (idarticle)
 )

Enabling the Perl plugin would grant access via Perl code:

 my $article = Entity::Article->create(title => 'Test article', content => 'Article content')
 say "ID was " . $article->id;
 my ($match) = Entity::Article->find(title => 'Test article');
 $match->title('Revised title');
 die "Instances of the same object should always be linked, consistent and up-to-date"
        unless $article->title eq $match->title;

with the equivalent through Javascript being:

 var article = Entity.Article.create({ title : 'Test article', 'content' : 'Article content' });
 alert("ID was " + article.id());
 var match = Entity.Article.find({ title : 'Test article' })[0];
 match.title('Revised title');
 if(article.title() != match.title())
        alert("Instances of the same object should always be linked, consistent and up-to-date");

or in C++:

 Entity::Article article = new Entity::Article().title('Test article').content('Article content');
 std::cout << "ID was a.id() << std::endl;
 Entity::Article *match = Entity::Article::find().title('Test article').begin();
 match->title('Revised title');
 if(article->title() != match->title())
         throw new std::string("Instances of the same object should always be linked, consistent and up-to-date");

The actual backend implementation may vary between these, but the intention is to maintain a recognisable, autogenerated API across all supported languages. The C++ implementation may inherit from a class that writes directly to the database, for example, and the Javascript code could be designed to run in a web browser accessing the resources through HTTP or as a node.js implementation linked directly to the database, but the top-level code should not need to care which underlying storage method is being used.

ASYNCHRONOUS MODEL ACCESS

Since backend storage response times can vary, it may help to use an asynchronous API for accessing entities. Given the 'article' example from earlier, the Perl code is now:

 my $article = Entity::Article->create(
        title => 'Test article',
        content => 'Article content'
 )->done(sub {
        my $a = shift;
        say "ID was " . $a->id;
 })->fail(sub {
        die 'Failed to create the requested article :(';
 });
 Entity::Article->find(title => 'Test article')->each(sub {
        my $match = shift;
        $match->title('Revised title');
        die "Instances of the same object should always be linked, consistent and up-to-date"
                unless $article->title eq $match->title;
 })->none(sub {
        die 'Nothing found';
 });

EXPORTING MODEL DEFINITIONS

Although it is possible to reverse-engineer the model in some cases, such as SQL, normally this is not advised. This may be useful however for a one-off database structure import, by writing the results to a model config file in JSON or XML format:

 my $model = EntityModel::Plugin::Apply::SQL->loadFrom(
        db => $db,
        schema => $schema
 );
 $model->export(xml => 'model.xml');

Once the model has been exported any further updates should be done in the model definition file rather than directly to the database if possible, since this would allow the generation of suitable upgrade/downgrade scripts.

Currently there is support for SQL and Perl model export, but not for Javascript or C++.

AUDITING

Audit tables are generated by default in the _audit schema, following the same naming convention as the audited tables with the addition of the following columns:

CLASS STRUCTURE

The primary classes used for interaction with models include:

The following classes provide features that are used throughout the code:

See http://entitymodel.com/documentation.html for more details and diagrams.

MOTIVATION ^

Some of the primary motivations for this distribution over any of the existing approaches (see next section for some alternatives):

Clearly none of these features are necessarily unique to EntityModel, and many of the alternative systems described in the next section could be adapted to support the above requirements.

SEE ALSO ^

There are plenty of other ORM implementations available on CPAN, one of which may be more suited to your needs than this is. These are the ones I've found so far:

Asynchronous ORMs

The list here is sadly lacking:

Synchronous ORMs

If you're happy for the database to tie up your process for an indefinite amount of time, you're in luck - there's a nice long list of modules to choose from here:

Database interaction

Since this is Perl, there are probably many more, if you have something which isn't in the above list (or a better description of any of the existing entries), please raise via RT or email.

Distributions which provide class structure and wrappers around the Perl OO mechanism are likewise covered by several other CPAN modules, with the clear winner here in the forms of Moose, Moo and derivatives.

Eventually I'll try to put up a better set of comparisons on http://entitymodel.com.

INHERITED METHODS ^

EntityModel::Model

add_entity, add_field_to_table, add_table, apply, apply_fields, commit, commit_pending_add, commit_pending_remove, commit_pending_update, create_entity, create_table, dump, entity_by_name, flush, handle_item, hasPending, load_model, matches, new_entity, pending, pending_entities, provide_handler_for, read_tables, remove_entity, remove_table, resolve_entity_dependencies, rollback, table, update_from, update_table

Mixin::Event::Dispatch

add_handler_for_event, clear_event_handlers, event_handlers, invoke_event, subscribe_to_event, unsubscribe_from_event

EntityModel::BaseClass

clone, sap

AUTHOR ^

Tom Molesworth <cpan@entitymodel.com>

LICENSE ^

Copyright Tom Molesworth 2008-2013. Licensed under the same terms as Perl itself.

syntax highlighting: