EntityModel

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

0.102     2013-09-08 18:13:54 Europe/London
	No changes to features.

	Dependency fixes:
	* List::MoreUtils and DateTime were not included in cpanfile

0.101     2013-08-30 21:24:22 Europe/London
	No functional changes.

	Test fixes:
	* Use Future rather than CPS::Future in the resolver.t test

0.100     2013-08-28 23:01:14 Europe/London
	EntityModel::Entity
	* The ->new method has been expanded to accept a full entity definition
	* New ->keyfield for key-value storage types
	* Stringifies to entity:$name

	EntityModel::Field
	* Stringifies to field:$name

	EntityModel::Resolver
	* New class for providing helper functions for gathering values, might be renamed
	in future so don't get too comfortable

	Test::EntityModel
	* Helper module for setting up models and other common functions

	General:
	* ->find can now be run without parameters to return all objects. Not sure how advisable this
	is but presumably the caller knows what they're doing.
	* Fix loading JSON from file.
	* Automatically register plugins on load.
	* "Default" model support for classes such as EntityModel::Resolver
	* Drop DateTime::Format::Duration, seems to use $& in a croak statement and since we only use
	this as a helper module there's no great benefit in keeping it around.

	Bug fixes:
	* Fixes for hash ordering and smartmatch removal for building on perl-5.18, should close RT82151
	(thanks to Andreas J. König for reporting).

	Documentation:
	* Yet more ORMs for the list
	* Sort out field names in example, thanks to Terrence Brannon for reporting this (a long time ago!)

0.017     2012-06-10 16:27:04 Europe/London
	Minor release to update EntityModel::Class dependency.
	Also added more ORMs to the "see also" section.

0.016     2012-03-05 01:25:35 Europe/London
	Another minor update with some code cleanup and preparation for some changes and minor
	restructuring planned for the next version:
	* Clean up several minor dependency issues (the deprecated EntityModel::DB class was trying to
	pull in DBI, and the SQL handling has a dependency on Parser::MGC).
	* Enable some additional dzil tests (mainly author tests for compiling modules, testing
	synopsis code, and the like)

0.015     2012-02-27 02:54:02 Europe/London
	This is a minor update only:
	* Fixes to tests and handling for EntityModel::Collection
	* Missing parameter in storage_ok() was causing failures on newer Test::Builder versions
	 (e.g. http://www.cpantesters.org/cpan/report/fa389848-5f22-11e1-ac30-241d23a3b3ef )

0.014     2011-10-02 03:43:40 Europe/London
	Add the EntityModel::Collection and EntityModel::EntityCollection classes plus some basic tests.
	Remove stray debug line in storage code.
	Rename the rest of the EntityModel::Query methods to use_underscores instead of camelCase.
	Add an example script for generating queries.
	Initial SQL parser for converting raw SQL into an EntityModel::Query object.
	Implemented the commandline client (basic functionality only so far).

0.013     2011-08-02 02:33:36 Europe/London
	Bug fix release: dropped Cache::Memcached::Fast dependency in last release but still had EntityModel::Cache::Memcached in place,
	 and test files still referenced it. Also add a note to the async example file indicating that it's currently unfinished.

0.012     2011-07-26 23:56:35 Europe/London
	Provide EntityModel::Test::Storage and EntityModel::Test::Cache so that external modules
	 can use a common testing interface to verify basic functionality.
	Clean up module loading slightly.
	Disable log output in tests.
	Query interface uses PostgreSQL-style placeholders by default
	 (hardcoded, will eventually come from the engine instead)
	Add check for unique module names to 00-pod.t

0.011     2011-06-21 21:16:13 Europe/London
	Better performance for larger models.
	Includes more backported async support, including EntityModel::Gather.
	Cleaned up some of the tests.
	Added more comments and alternatives to the SEE ALSO sections.

0.010     2011-03-06 02:46:17 Europe/London
	Move ->add_plugin method to EntityModel top level class.
	Allow EntityModel plugins to be provided using the Name => { ... } format
	 or as an instance, for compatibility with other methods such as
	 add_storage

0.009     2011-03-04 17:23:14 Europe/London
	Fix EntityModel::Template handling, needed for EntityModel::Web

0.008     2011-02-20 23:25:09 Europe/London
	Backport some basic plugin support.

0.007     2011-02-20 20:03:23 Europe/London
	Tidy up some POD and package for the first non-trial release.

0.006     2011-02-13 23:38:14 Europe/London
	Attempt to fix test failure seen on Solaris where ref $v ~~ [...] was giving 'Argument "HASH" isn't numeric in
	 smart match', and associated v-string error (http://www.cpantesters.org/cpan/report/f86d3c52-3792-11e0-bb29-ad544afd17af)

0.005     2011-02-12 16:01:19 Europe/London
	Fix dependencies and minor test cleanups.

0.004     2011-02-06 06:02:05 Europe/London
	Add remaining dependencies, including EntityModel::Class 0.002 which provides a UNITCHECK fix for
	non-multiplicity-enabled perl versions.

0.003     2011-02-06 02:46:11 Europe/London
	Add missing XML::XPath dependency

0.002     2011-02-06 00:31:18 Europe/London
	Improve tests and handling for N:M join tables.
	Enable handling for ->find in the Perl 'storage' module.
	Copy across the missing update support for EntityModel::Query.
	Allow fields that don't have an EntityModel::Field definition (and corresponding type).
	Initial support for callback events on entities at EntityModel::Support::Perl level.
	Fix some pod doc headers.
	Do EntityModel load in general.t inside a BEGIN block in an attempt to trace why usemultiplicity
	seems to be required for UNITCHECK blocks to work as required.

0.001     2011-02-01 04:38:41 Europe/London
	Preliminary release to CPAN.

INSTALL  view on Meta::CPAN


This is the Perl distribution EntityModel.

Installing EntityModel is straightforward.

## Installation with cpanm

If you have cpanm, you only need one line:

    % cpanm EntityModel

If you are installing into a system-wide directory, you may need to pass the
"-S" flag to cpanm, which uses sudo to install the module:

    % cpanm -S EntityModel

## Installing with the CPAN shell

Alternatively, if your CPAN shell is set up, you should just be able to do:

    % cpan EntityModel

## Manual installation

As a last resort, you can manually install it. Download the tarball, untar it,
then build it:

    % perl Makefile.PL
    % make && make test

Then install it:

    % make install

If you are installing into a system-wide directory, you may need to run:

    % sudo make install

## Documentation

EntityModel documentation is available as POD.
You can run perldoc from a shell to read the documentation:

    % perldoc EntityModel

LICENSE  view on Meta::CPAN

This software is copyright (c) 2012 by Tom Molesworth.

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

Terms of the Perl programming language system itself

a) the GNU General Public License as published by the Free
   Software Foundation; either version 1, or (at your option) any
   later version, or
b) the "Artistic License"

--- The GNU General Public License, Version 1, February 1989 ---

This software is Copyright (c) 2012 by Tom Molesworth.

This is free software, licensed under:

  The GNU General Public License, Version 1, February 1989

                    GNU GENERAL PUBLIC LICENSE
                     Version 1, February 1989

 Copyright (C) 1989 Free Software Foundation, Inc.
 51 Franklin St, Suite 500, Boston, MA  02110-1335  USA

 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The license agreements of most software companies try to keep users
at the mercy of those companies.  By contrast, our General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  The
General Public License applies to the Free Software Foundation's
software and to any other program whose authors commit to using it.
You can use it for your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Specifically, the General Public License is designed to make
sure that you have the freedom to give away or sell copies of free
software, that you receive source code or can get it if you want it,
that you can change the software or use pieces of it in new free
programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of a such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must tell them their rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  The precise terms and conditions for copying, distribution and
modification follow.

                    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any program or other work which
contains a notice placed by the copyright holder saying it may be
distributed under the terms of this General Public License.  The
"Program", below, refers to any such program or work, and a "work based
on the Program" means either the Program or any work containing the
Program or a portion of it, either verbatim or with modifications.  Each
licensee is addressed as "you".

  1. You may copy and distribute verbatim copies of the Program's source
code as you receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice and
disclaimer of warranty; keep intact all the notices that refer to this
General Public License and to the absence of any warranty; and give any
other recipients of the Program a copy of this General Public License
along with the Program.  You may charge a fee for the physical act of
transferring a copy.

  2. You may modify your copy or copies of the Program or any portion of
it, and copy and distribute such modifications under the terms of Paragraph
1 above, provided that you also do the following:

    a) cause the modified files to carry prominent notices stating that
    you changed the files and the date of any change; and

    b) cause the whole of any work that you distribute or publish, that
    in whole or in part contains the Program or any part thereof, either
    with or without modifications, to be licensed at no charge to all
    third parties under the terms of this General Public License (except
    that you may choose to grant warranty protection to some or all
    third parties, at your option).

    c) If the modified program normally reads commands interactively when
    run, you must cause it, when started running for such interactive use
    in the simplest and most usual way, to print or display an
    announcement including an appropriate copyright notice and a notice
    that there is no warranty (or else, saying that you provide a
    warranty) and that users may redistribute the program under these
    conditions, and telling the user how to view a copy of this General
    Public License.

    d) You may charge a fee for the physical act of transferring a
    copy, and you may at your option offer warranty protection in
    exchange for a fee.

Mere aggregation of another independent work with the Program (or its
derivative) on a volume of a storage or distribution medium does not bring
the other work under the scope of these terms.

  3. You may copy and distribute the Program (or a portion or derivative of
it, under Paragraph 2) in object code or executable form under the terms of
Paragraphs 1 and 2 above provided that you also do one of the following:

    a) accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of
    Paragraphs 1 and 2 above; or,

    b) accompany it with a written offer, valid for at least three
    years, to give any third party free (except for a nominal charge
    for the cost of distribution) a complete machine-readable copy of the
    corresponding source code, to be distributed under the terms of
    Paragraphs 1 and 2 above; or,

    c) accompany it with the information you received as to where the
    corresponding source code may be obtained.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form alone.)

Source code for a work means the preferred form of the work for making
modifications to it.  For an executable file, complete source code means
all the source code for all modules it contains; but, as a special
exception, it need not include source code for modules which are standard
libraries that accompany the operating system on which the executable
file runs, or for standard header files or definitions files that
accompany that operating system.

  4. You may not copy, modify, sublicense, distribute or transfer the
Program except as expressly provided under this General Public License.
Any attempt otherwise to copy, modify, sublicense, distribute or transfer
the Program is void, and will automatically terminate your rights to use
the Program under this License.  However, parties who have received
copies, or rights to use copies, from you under this General Public
License will not have their licenses terminated so long as such parties
remain in full compliance.

  5. By copying, distributing or modifying the Program (or any work based
on the Program) you indicate your acceptance of this license to do so,
and all its terms and conditions.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the original
licensor to copy, distribute or modify the Program subject to these
terms and conditions.  You may not impose any further restrictions on the
recipients' exercise of the rights granted herein.

  7. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of the license which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
the license, you may choose any version ever published by the Free Software
Foundation.

  8. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

                            NO WARRANTY

  9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

                     END OF TERMS AND CONDITIONS

        Appendix: How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to humanity, the best way to achieve this is to make it
free software which everyone can redistribute and change under these
terms.

  To do so, attach the following notices to the program.  It is safest to
attach them to the start of each source file to most effectively convey
the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) 19yy  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 1, or (at your option)
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA


Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) 19xx name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the
appropriate parts of the General Public License.  Of course, the
commands you use may be called something other than `show w' and `show
c'; they could even be mouse-clicks or menu items--whatever suits your
program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  program `Gnomovision' (a program to direct compilers to make passes
  at assemblers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

That's all there is to it!


--- The Artistic License 1.0 ---

This software is Copyright (c) 2012 by Tom Molesworth.

This is free software, licensed under:

  The Artistic License 1.0

The Artistic License

Preamble

The intent of this document is to state the conditions under which a Package
may be copied, such that the Copyright Holder maintains some semblance of
artistic control over the development of the package, while giving the users of
the package the right to use and distribute the Package in a more-or-less
customary fashion, plus the right to make reasonable modifications.

Definitions:

  - "Package" refers to the collection of files distributed by the Copyright
    Holder, and derivatives of that collection of files created through
    textual modification. 
  - "Standard Version" refers to such a Package if it has not been modified,
    or has been modified in accordance with the wishes of the Copyright
    Holder. 
  - "Copyright Holder" is whoever is named in the copyright or copyrights for
    the package. 
  - "You" is you, if you're thinking about copying or distributing this Package.
  - "Reasonable copying fee" is whatever you can justify on the basis of media
    cost, duplication charges, time of people involved, and so on. (You will
    not be required to justify it to the Copyright Holder, but only to the
    computing community at large as a market that must bear the fee.) 
  - "Freely Available" means that no fee is charged for the item itself, though
    there may be fees involved in handling the item. It also means that
    recipients of the item may redistribute it under the same conditions they
    received it. 

1. You may make and give away verbatim copies of the source form of the
Standard Version of this Package without restriction, provided that you
duplicate all of the original copyright notices and associated disclaimers.

2. You may apply bug fixes, portability fixes and other modifications derived
from the Public Domain or from the Copyright Holder. A Package modified in such
a way shall still be considered the Standard Version.

3. You may otherwise modify your copy of this Package in any way, provided that
you insert a prominent notice in each changed file stating how and when you
changed that file, and provided that you do at least ONE of the following:

  a) place your modifications in the Public Domain or otherwise make them
     Freely Available, such as by posting said modifications to Usenet or an
     equivalent medium, or placing the modifications on a major archive site
     such as ftp.uu.net, or by allowing the Copyright Holder to include your
     modifications in the Standard Version of the Package.

  b) use the modified Package only within your corporation or organization.

  c) rename any non-standard executables so the names do not conflict with
     standard executables, which must also be provided, and provide a separate
     manual page for each non-standard executable that clearly documents how it
     differs from the Standard Version.

  d) make other distribution arrangements with the Copyright Holder.

4. You may distribute the programs of this Package in object code or executable
form, provided that you do at least ONE of the following:

  a) distribute a Standard Version of the executables and library files,
     together with instructions (in the manual page or equivalent) on where to
     get the Standard Version.

  b) accompany the distribution with the machine-readable source of the Package
     with your modifications.

  c) accompany any non-standard executables with their corresponding Standard
     Version executables, giving the non-standard executables non-standard
     names, and clearly documenting the differences in manual pages (or
     equivalent), together with instructions on where to get the Standard
     Version.

  d) make other distribution arrangements with the Copyright Holder.

5. You may charge a reasonable copying fee for any distribution of this
Package.  You may charge any fee you choose for support of this Package. You
may not charge a fee for this Package itself. However, you may distribute this
Package in aggregate with other (possibly commercial) programs as part of a
larger (possibly commercial) software distribution provided that you do not
advertise this Package as a product of your own.

6. The scripts and library files supplied as input to or produced as output
from the programs of this Package do not automatically fall under the copyright
of this Package, but belong to whomever generated them, and may be sold
commercially, and may be aggregated with this Package.

7. C or perl subroutines supplied by you and linked into this Package shall not
be considered part of this Package.

8. The name of the Copyright Holder may not be used to endorse or promote
products derived from this software without specific prior written permission.

9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.

The End

MANIFEST  view on Meta::CPAN

Changes
INSTALL
LICENSE
MANIFEST
META.json
META.yml
Makefile.PL
README
SIGNATURE
benchmark/model-size-time.pl
bin/entitymodel
cpanfile
dist.ini
examples/apachelog.pl
examples/async.pl
examples/auth.pl
examples/cpan.pl
examples/cpp/Author.h
examples/cpp/Book.h
examples/cpp/Library.h
examples/cpp/LibraryItem.h
examples/cpp/Model.h
examples/cpp/main
examples/cpp/main.cpp
examples/emt.pl
examples/library.js
examples/library.php
examples/query.pl
lib/EntityModel.pm
lib/EntityModel.pod
lib/EntityModel/App.pm
lib/EntityModel/App.pod
lib/EntityModel/Async.pm
lib/EntityModel/Async.pod
lib/EntityModel/Cache.pm
lib/EntityModel/Cache.pod
lib/EntityModel/Cache/Perl.pm
lib/EntityModel/Cache/Perl.pod
lib/EntityModel/Collection.pm
lib/EntityModel/Collection.pod
lib/EntityModel/DB.pm
lib/EntityModel/DB.pod
lib/EntityModel/Deferred.pm
lib/EntityModel/Deferred.pod
lib/EntityModel/Definition.pm
lib/EntityModel/Definition.pod
lib/EntityModel/Definition/JSON.pm
lib/EntityModel/Definition/JSON.pod
lib/EntityModel/Definition/Perl.pm
lib/EntityModel/Definition/Perl.pod
lib/EntityModel/Definition/XML.pm
lib/EntityModel/Definition/XML.pod
lib/EntityModel/Entity.pm
lib/EntityModel/Entity.pod
lib/EntityModel/Entity/Constraint.pm
lib/EntityModel/Entity/Constraint.pod
lib/EntityModel/EntityCollection.pm
lib/EntityModel/EntityCollection.pod
lib/EntityModel/Field.pm
lib/EntityModel/Field.pod
lib/EntityModel/Field/Refer.pm
lib/EntityModel/Field/Refer.pod
lib/EntityModel/Gather.pm
lib/EntityModel/Gather.pod
lib/EntityModel/Model.pm
lib/EntityModel/Model.pod
lib/EntityModel/Plugin.pm
lib/EntityModel/Plugin.pod
lib/EntityModel/Query.pm
lib/EntityModel/Query.pod
lib/EntityModel/Query/Base.pm
lib/EntityModel/Query/Base.pod
lib/EntityModel/Query/Condition.pm
lib/EntityModel/Query/Condition.pod
lib/EntityModel/Query/Delete.pm
lib/EntityModel/Query/Delete.pod
lib/EntityModel/Query/Except.pm
lib/EntityModel/Query/Except.pod
lib/EntityModel/Query/Field.pm
lib/EntityModel/Query/Field.pod
lib/EntityModel/Query/FromTable.pm
lib/EntityModel/Query/FromTable.pod
lib/EntityModel/Query/GroupField.pm
lib/EntityModel/Query/GroupField.pod
lib/EntityModel/Query/Insert.pm
lib/EntityModel/Query/Insert.pod
lib/EntityModel/Query/InsertField.pm
lib/EntityModel/Query/InsertField.pod
lib/EntityModel/Query/Intersect.pm
lib/EntityModel/Query/Intersect.pod
lib/EntityModel/Query/Join.pm
lib/EntityModel/Query/Join.pod
lib/EntityModel/Query/JoinTable.pm
lib/EntityModel/Query/JoinTable.pod
lib/EntityModel/Query/OrderField.pm
lib/EntityModel/Query/OrderField.pod
lib/EntityModel/Query/ParseSQL.pm
lib/EntityModel/Query/ParseSQL.pod
lib/EntityModel/Query/ReturningField.pm
lib/EntityModel/Query/ReturningField.pod
lib/EntityModel/Query/Select.pm
lib/EntityModel/Query/Select.pod
lib/EntityModel/Query/SubQuery.pm
lib/EntityModel/Query/SubQuery.pod
lib/EntityModel/Query/Table.pm
lib/EntityModel/Query/Table.pod
lib/EntityModel/Query/Union.pm
lib/EntityModel/Query/Union.pod
lib/EntityModel/Query/UnionAll.pm
lib/EntityModel/Query/UnionAll.pod
lib/EntityModel/Query/Update.pm
lib/EntityModel/Query/Update.pod
lib/EntityModel/Query/UpdateField.pm
lib/EntityModel/Query/UpdateField.pod
lib/EntityModel/Resolver.pm
lib/EntityModel/Resolver.pod
lib/EntityModel/Storage.pm
lib/EntityModel/Storage.pod
lib/EntityModel/Storage/Perl.pm
lib/EntityModel/Storage/Perl.pod
lib/EntityModel/Storage/PerlAsync.pm
lib/EntityModel/Storage/PerlAsync.pod
lib/EntityModel/StorageClass/KVStore.pm
lib/EntityModel/StorageClass/KVStore/Layer.pm
lib/EntityModel/StorageClass/KVStore/Layer/Fake.pm
lib/EntityModel/StorageClass/KVStore/Layer/Fake.pod
lib/EntityModel/StorageClass/KVStore/Layer/LRU.pm
lib/EntityModel/StorageClass/KVStore/Layer/LRU.pod
lib/EntityModel/StorageClass/KVStore/Layer/Memcached.pm
lib/EntityModel/StorageClass/KVStore/Layer/Memcached.pod
lib/EntityModel/StorageClass/KVStore/Layer/PostgreSQL.pm
lib/EntityModel/StorageClass/KVStore/Layer/PostgreSQL.pod
lib/EntityModel/StorageClass/KVStore/Mixin/Deferred.pm
lib/EntityModel/Support.pm
lib/EntityModel/Support.pod
lib/EntityModel/Support/CPP.pm
lib/EntityModel/Support/CPP.pod
lib/EntityModel/Support/Javascript.pm
lib/EntityModel/Support/Javascript.pod
lib/EntityModel/Support/Perl.pm
lib/EntityModel/Support/Perl.pod
lib/EntityModel/Support/Perl/Base.pm
lib/EntityModel/Support/Perl/Base.pod
lib/EntityModel/Support/Template.pm
lib/EntityModel/Support/Template.pod
lib/EntityModel/Template.pm
lib/EntityModel/Template.pod
lib/EntityModel/Test/Cache.pm
lib/EntityModel/Test/Cache.pod
lib/EntityModel/Test/Storage.pm
lib/EntityModel/Test/Storage.pod
lib/EntityModel/Transaction.pm
lib/EntityModel/Transaction.pod
lib/EntityModel/Tutorial.pod
lib/EntityModel/Util.pm
lib/EntityModel/Util.pod
lib/Test/EntityModel.pm
lib/Test/EntityModel.pod
share/template/support/android.tt2
share/template/support/c.tt2
share/template/support/cpp.tt2
share/template/support/django.tt2
share/template/support/java.tt2
share/template/support/js.tt2
t/00-check-deps.t
t/00-compile.t
t/00-test-entitymodel.t
t/app.t
t/async-storage-perl.t
t/async.t
t/cache.t
t/collection.t
t/deferred.t
t/definition.t
t/entity-collection.t
t/entity.t
t/entitymodel.t
t/field.t
t/general.t
t/kvstore.t
t/large-model.t
t/new-api.t
t/plugin.t
t/query.t
t/resolver.t
t/sharedir.t
t/storage-create-entity.t
t/storage.t
t/support/c.t
t/support/cpp.t
t/support/django.t
t/support/doctrine.t
t/support/java.t
t/support/js.t
t/support/perl.t
t/support/propel.t
t/support/python.t
t/support/redbean.t
t/template.t
t/test-cache.t
t/test-storage.t
t/transaction.t
xt/author/test-eol.t
xt/release/common_spelling.t
xt/release/mojibake.t
xt/release/pod-syntax.t
xt/release/test-version.t

META.json  view on Meta::CPAN

{
   "abstract" : "Cross-language event-driven ORM",
   "author" : [
      "Tom Molesworth <cpan@entitymodel.com>"
   ],
   "dynamic_config" : 0,
   "generated_by" : "Dist::Zilla version 4.300036, CPAN::Meta::Converter version 2.132140",
   "license" : [
      "perl_5"
   ],
   "meta-spec" : {
      "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
      "version" : "2"
   },
   "name" : "EntityModel",
   "prereqs" : {
      "build" : {
         "requires" : {
            "perl" : "5.010001"
         }
      },
      "configure" : {
         "requires" : {
            "ExtUtils::MakeMaker" : "6.48",
            "File::ShareDir::Install" : "0.03"
         }
      },
      "develop" : {
         "requires" : {
            "Test::Pod" : "1.41"
         }
      },
      "runtime" : {
         "requires" : {
            "CPS" : "0",
            "DateTime" : "0",
            "EntityModel::Class" : "0.012",
            "File::ShareDir" : "1.00",
            "Future" : "0.12",
            "IO::Async" : "0.50",
            "IO::Socket::IP" : "0",
            "JSON::XS" : "2.00",
            "List::MoreUtils" : "0",
            "Mixin::Event::Dispatch" : "1.000",
            "Module::Load" : "0",
            "POSIX::strptime" : "0",
            "Parser::MGC" : "0.10",
            "Template" : "2.24",
            "Tie::Cache::LRU" : "0",
            "Tie::Hash::LRU" : "0",
            "Try::Tiny" : "0",
            "XML::XPath" : "1.00",
            "curry" : "1.000"
         }
      },
      "test" : {
         "requires" : {
            "Capture::Tiny" : "0",
            "Test::CheckDeps" : "0.006",
            "Test::Class" : "0",
            "Test::Fatal" : "0.010",
            "Test::More" : "0.98",
            "Test::Refcount" : "0.07",
            "Test::Script" : "1.05",
            "blib" : "0"
         }
      }
   },
   "release_status" : "stable",
   "resources" : {
      "bugtracker" : {
         "mailto" : "bug-entitymodel at rt.cpan.org",
         "web" : "http://rt.cpan.org/Public/Dist/Display.html?Name=EntityModel"
      },
      "homepage" : "http://search.cpan.org/dist/EntityModel/"
   },
   "version" : "0.102"
}

META.yml  view on Meta::CPAN

---
abstract: 'Cross-language event-driven ORM'
author:
  - 'Tom Molesworth <cpan@entitymodel.com>'
build_requires:
  Capture::Tiny: 0
  Test::CheckDeps: 0.006
  Test::Class: 0
  Test::Fatal: 0.010
  Test::More: 0.98
  Test::Refcount: 0.07
  Test::Script: 1.05
  blib: 0
  perl: 5.010001
configure_requires:
  ExtUtils::MakeMaker: 6.48
  File::ShareDir::Install: 0.03
dynamic_config: 0
generated_by: 'Dist::Zilla version 4.300036, CPAN::Meta::Converter version 2.132140'
license: perl
meta-spec:
  url: http://module-build.sourceforge.net/META-spec-v1.4.html
  version: 1.4
name: EntityModel
requires:
  CPS: 0
  DateTime: 0
  EntityModel::Class: 0.012
  File::ShareDir: 1.00
  Future: 0.12
  IO::Async: 0.50
  IO::Socket::IP: 0
  JSON::XS: 2.00
  List::MoreUtils: 0
  Mixin::Event::Dispatch: 1.000
  Module::Load: 0
  POSIX::strptime: 0
  Parser::MGC: 0.10
  Template: 2.24
  Tie::Cache::LRU: 0
  Tie::Hash::LRU: 0
  Try::Tiny: 0
  XML::XPath: 1.00
  curry: 1.000
resources:
  bugtracker: http://rt.cpan.org/Public/Dist/Display.html?Name=EntityModel
  homepage: http://search.cpan.org/dist/EntityModel/
version: 0.102

Makefile.PL  view on Meta::CPAN


use strict;
use warnings;

use 5.010001;

use ExtUtils::MakeMaker 6.48;

use File::ShareDir::Install;
install_share dist => "share";


my %WriteMakefileArgs = (
  "ABSTRACT" => "Cross-language event-driven ORM",
  "AUTHOR" => "Tom Molesworth <cpan\@entitymodel.com>",
  "BUILD_REQUIRES" => {},
  "CONFIGURE_REQUIRES" => {
    "ExtUtils::MakeMaker" => "6.48",
    "File::ShareDir::Install" => "0.03"
  },
  "DISTNAME" => "EntityModel",
  "EXE_FILES" => [
    "bin/entitymodel"
  ],
  "LICENSE" => "perl",
  "MIN_PERL_VERSION" => "5.010001",
  "NAME" => "EntityModel",
  "PREREQ_PM" => {
    "CPS" => 0,
    "DateTime" => 0,
    "EntityModel::Class" => "0.012",
    "File::ShareDir" => "1.00",
    "Future" => "0.12",
    "IO::Async" => "0.50",
    "IO::Socket::IP" => 0,
    "JSON::XS" => "2.00",
    "List::MoreUtils" => 0,
    "Mixin::Event::Dispatch" => "1.000",
    "Module::Load" => 0,
    "POSIX::strptime" => 0,
    "Parser::MGC" => "0.10",
    "Template" => "2.24",
    "Tie::Cache::LRU" => 0,
    "Tie::Hash::LRU" => 0,
    "Try::Tiny" => 0,
    "XML::XPath" => "1.00",
    "curry" => "1.000"
  },
  "TEST_REQUIRES" => {
    "Capture::Tiny" => 0,
    "Test::CheckDeps" => "0.006",
    "Test::Class" => 0,
    "Test::Fatal" => "0.010",
    "Test::More" => "0.98",
    "Test::Refcount" => "0.07",
    "Test::Script" => "1.05",
    "blib" => 0
  },
  "VERSION" => "0.102",
  "test" => {
    "TESTS" => "t/*.t t/support/*.t"
  }
);


unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) {
  my $tr = delete $WriteMakefileArgs{TEST_REQUIRES};
  my $br = $WriteMakefileArgs{BUILD_REQUIRES};
  for my $mod ( keys %$tr ) {
    if ( exists $br->{$mod} ) {
      $br->{$mod} = $tr->{$mod} if $tr->{$mod} > $br->{$mod};
    }
    else {
      $br->{$mod} = $tr->{$mod};
    }
  }
}

unless ( eval { ExtUtils::MakeMaker->VERSION(6.56) } ) {
  my $br = delete $WriteMakefileArgs{BUILD_REQUIRES};
  my $pp = $WriteMakefileArgs{PREREQ_PM};
  for my $mod ( keys %$br ) {
    if ( exists $pp->{$mod} ) {
      $pp->{$mod} = $br->{$mod} if $br->{$mod} > $pp->{$mod};
    }
    else {
      $pp->{$mod} = $br->{$mod};
    }
  }
}

delete $WriteMakefileArgs{CONFIGURE_REQUIRES}
  unless eval { ExtUtils::MakeMaker->VERSION(6.52) };

WriteMakefile(%WriteMakefileArgs);

{
package
MY;
use File::ShareDir::Install qw(postamble);
}


README  view on Meta::CPAN



This archive contains the distribution EntityModel,
version 0.102:

  Cross-language event-driven ORM

This software is copyright (c) 2012 by Tom Molesworth.

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


SIGNATURE  view on Meta::CPAN

This file contains message digests of all files listed in MANIFEST,
signed via the Module::Signature module, version 0.73.

To verify the content in this distribution, first make sure you have
Module::Signature installed, then type:

    % cpansign -v

It will check each file's integrity, as well as the signature's
validity.  If "==> Signature verified OK! <==" is not displayed,
the distribution may already have been compromised, and you should
not run its Makefile.PL or Build.PL.

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

SHA1 5b1c10a3fc50d0a39511d8ef816583f6baf9b9c2 Changes
SHA1 a4b89291a95aa55231353ba0873fc00fb5d74f0c INSTALL
SHA1 58403eb3f2c8983c79c530c44104344bd938d051 LICENSE
SHA1 2fb281a309e5e92193a12d7558cb3c0febd70d63 MANIFEST
SHA1 eeb28ac63bdc1889cc9932c840dcfbf72de31937 META.json
SHA1 3b7e821a15a7651810b9875a63da55a28029bea1 META.yml
SHA1 fa4f4b14861f71d410b9493a7859236eb1d5216f Makefile.PL
SHA1 264da5b1798434c52903c55b21d8735e7d0b397b README
SHA1 1eccd47554dfa51980bc31fe4bfc2deb21e8457a benchmark/model-size-time.pl
SHA1 67a1f5c8fc319f5675a01ac67693f7cb28d00157 bin/entitymodel
SHA1 b0d868cc6d868badef30e408bf2eef38f8cafe37 cpanfile
SHA1 615e1afed2736cc949358415beb29359bcb04f23 dist.ini
SHA1 a9f8c57cc88fae2dbf4783d29904e5fac2901c93 examples/apachelog.pl
SHA1 1147f172fee2e392ff4ddba63f4bba6e90541913 examples/async.pl
SHA1 d7ece86869744d6368e715cd0762450234dd78e7 examples/auth.pl
SHA1 9c1f798996d4cb37a9ac6eec47bb75eb7877a0e2 examples/cpan.pl
SHA1 5c2adb518caa4157187e495d9ebcb248b60d14b1 examples/cpp/Author.h
SHA1 3c2ec5c4da14543c7134a96a3934f6fddfc011ab examples/cpp/Book.h
SHA1 49ec1ca1516248c9a00dad1cd52d00e519bffb82 examples/cpp/Library.h
SHA1 a3b2a6966531800a4f3525e4cb8d1b0ccf43fca0 examples/cpp/LibraryItem.h
SHA1 ded735d9cf1a9efeb8cc3b726a5a38e3b58bf805 examples/cpp/Model.h
SHA1 0b6e354bfa28e57f03048b8e982c48eecacd035b examples/cpp/main
SHA1 bf7dc93f82fcae3659ec15b9c12a1f4f7e470315 examples/cpp/main.cpp
SHA1 d8d10c3ce7d8530cad6572b3e33dc2c1d6b7aab4 examples/emt.pl
SHA1 d9f75fae465a643fc7518ee2d42334a2c0072457 examples/library.js
SHA1 6b401e0934e5686d5fc314be3a29ae0a62153e9f examples/library.php
SHA1 9ccd121add886197f7c575752c985ff3d32453bc examples/query.pl
SHA1 2b835710b37bdb26771f203c22beaa3ff23de199 lib/EntityModel.pm
SHA1 b52368632f5e1e4cbdc83fd6557788ea8b91ccce lib/EntityModel.pod
SHA1 00b3bc18eba382dfe42b1b9ef675c76094a6cee6 lib/EntityModel/App.pm
SHA1 1f98593ff1f030649fd87e424ae2151844efbb14 lib/EntityModel/App.pod
SHA1 24a37724cd1d8e43e54ebb34b6fcd50e6e1928ef lib/EntityModel/Async.pm
SHA1 8249b463ce9f5d7c71c9afbbe8c1bbdd4516fa44 lib/EntityModel/Async.pod
SHA1 ab01e64bfa6d9916457d944dc15e7a013ec0a12f lib/EntityModel/Cache.pm
SHA1 7ea36a2fd2fd35558d56af6c9e98e70f17d71346 lib/EntityModel/Cache.pod
SHA1 89b679fbe7d22713de441623821673d002aeff72 lib/EntityModel/Cache/Perl.pm
SHA1 8b94f0579dfb1d3672eb379bec54055c0a5a7988 lib/EntityModel/Cache/Perl.pod
SHA1 54986c8143dcde796097cf5a8cc64fa97c6a9a45 lib/EntityModel/Collection.pm
SHA1 811c5b18ff5e1e2660b2767a214f010f15fd928b lib/EntityModel/Collection.pod
SHA1 2f5f2b194aff89d0af2cf7e1c0fcdb40f317c26e lib/EntityModel/DB.pm
SHA1 eca0bcfe456cf43377683d6c16d738cab9e977ae lib/EntityModel/DB.pod
SHA1 aea737531e658001da45f18f6cc5ce217aaff553 lib/EntityModel/Deferred.pm
SHA1 4e9e0ea3a694142d8adc052906a94564d452ff4c lib/EntityModel/Deferred.pod
SHA1 f6a77415d815e1a1c50326a033477b6b3c8c5033 lib/EntityModel/Definition.pm
SHA1 2341f575805f46165e080bf7c61ac3648f00ba25 lib/EntityModel/Definition.pod
SHA1 10999f4638113210a76439343b84a658e1b61e7d lib/EntityModel/Definition/JSON.pm
SHA1 5a06297437a0ba74221317fcfffc8f74f9595cd4 lib/EntityModel/Definition/JSON.pod
SHA1 cec3b1f79cd9d150119ef4157032204ecc941c61 lib/EntityModel/Definition/Perl.pm
SHA1 d27fed14e22b4aeb65ec915a6b13468edf2979c9 lib/EntityModel/Definition/Perl.pod
SHA1 9cc9a2bc289138c262690fbfb61081ec0045ef65 lib/EntityModel/Definition/XML.pm
SHA1 81bd5d3d515433e8324444161d61891d68a87fcf lib/EntityModel/Definition/XML.pod
SHA1 f1ba530823e0a3acb457f14acaf7f1ad34e10f0a lib/EntityModel/Entity.pm
SHA1 80ff45e9827571009f7f37f35272c6b7c7d2f7fe lib/EntityModel/Entity.pod
SHA1 81434858bbbffcd2b4e83b8fd0db6fa7b01811a8 lib/EntityModel/Entity/Constraint.pm
SHA1 28cd056f33809df3ed711563dc33d36fcfc85649 lib/EntityModel/Entity/Constraint.pod
SHA1 19ef6d6f36c590b779df9b2f5be7e6760dd353fd lib/EntityModel/EntityCollection.pm
SHA1 4a85d702eadeefcab24774d35a1d9b40902e3dbe lib/EntityModel/EntityCollection.pod
SHA1 474a64e75fb93effc7d73c69fdba2bf3993c7ec4 lib/EntityModel/Field.pm
SHA1 29da0c913d5368f9d880837db810a4bc41bc3768 lib/EntityModel/Field.pod
SHA1 daf926854557409581dce867ea2704ce51a03c93 lib/EntityModel/Field/Refer.pm
SHA1 437aa917a67e2a8d7540c17cc5b478a20e4fc935 lib/EntityModel/Field/Refer.pod
SHA1 18e6c7447c11715565ac36a9d8778c5f559b741e lib/EntityModel/Gather.pm
SHA1 9a2138641165338fdc9b0384b03fe717846b5d78 lib/EntityModel/Gather.pod
SHA1 4d642e6535bb9708a7202e4aeb931eadb249698a lib/EntityModel/Model.pm
SHA1 119dd4c9e103d6bba6d3da6639388fae4c9bbf97 lib/EntityModel/Model.pod
SHA1 fcafc37f467516f52385d4ce07876795e643cfad lib/EntityModel/Plugin.pm
SHA1 f6f821f7a839bcf6f8d68ed4f8191b2f45228118 lib/EntityModel/Plugin.pod
SHA1 10e483ebfed0f27b367add44b38440d34ce3a7e3 lib/EntityModel/Query.pm
SHA1 923242727a728eb227e00e4585d0a75381d0791e lib/EntityModel/Query.pod
SHA1 b4bc2ba8a5fe44031c62e702e4219e469674a613 lib/EntityModel/Query/Base.pm
SHA1 0e1820ce6f13ee08192e24893f874cdabfc0a332 lib/EntityModel/Query/Base.pod
SHA1 6b803268ba87c97d0e29b7b2760d3ee63f5d0df4 lib/EntityModel/Query/Condition.pm
SHA1 63bf916f64a65c456e9a3e5cdafa240f7157f254 lib/EntityModel/Query/Condition.pod
SHA1 46d58a7df0e26f2e7cf893c481cbf3d4ad6dc6b3 lib/EntityModel/Query/Delete.pm
SHA1 51a283411202a5207e8617e8100dc8f7c6d0fdea lib/EntityModel/Query/Delete.pod
SHA1 b3f7e20c9eaedc6b8d2b11ec772647676337860c lib/EntityModel/Query/Except.pm
SHA1 ad0239b386b0126c54617e99098a15992a2dffb7 lib/EntityModel/Query/Except.pod
SHA1 36197e3568bd73088ee97dae73ec0ec4dced02e9 lib/EntityModel/Query/Field.pm
SHA1 84299dda31590151403970857ad687b55665f8dc lib/EntityModel/Query/Field.pod
SHA1 b862a3cd8bb23f828e5766a407badf597c654a01 lib/EntityModel/Query/FromTable.pm
SHA1 3db99262c76e023b9c966baf3b735c4843f5d2e8 lib/EntityModel/Query/FromTable.pod
SHA1 ddebe950915cdf1216c743cceda48e09be69345b lib/EntityModel/Query/GroupField.pm
SHA1 f10d19d4db87a936ed39c74f380235c61bbb507d lib/EntityModel/Query/GroupField.pod
SHA1 5a7770533ea1574166a1ff76dc52feaed252edda lib/EntityModel/Query/Insert.pm
SHA1 76b1a380c274e8ed72b56640d07436be32dec16f lib/EntityModel/Query/Insert.pod
SHA1 640bfdbc0a623a0e4b07727b97d6cd8286a9337b lib/EntityModel/Query/InsertField.pm
SHA1 200ef1d62ef53ebf5492c122e7588385ef5c119a lib/EntityModel/Query/InsertField.pod
SHA1 5aa43c39ae41deeeaf1d3efbd14ba4412fada4d9 lib/EntityModel/Query/Intersect.pm
SHA1 587bb5429a081a4c8db805f367d3b1510e74830a lib/EntityModel/Query/Intersect.pod
SHA1 2b0dd8cc1fbafb913cf2f302bea16e14e42367ef lib/EntityModel/Query/Join.pm
SHA1 5face017a6bbc57c76a089b72036251d7019bcc2 lib/EntityModel/Query/Join.pod
SHA1 b4179e9b4e88c5e3437586964e31a9d23b5056b4 lib/EntityModel/Query/JoinTable.pm
SHA1 12fe2e702b4c6402800c826c48e6133227fdd667 lib/EntityModel/Query/JoinTable.pod
SHA1 1e2970a044fb4b30b5cc467c3f5aa049a6a470fe lib/EntityModel/Query/OrderField.pm
SHA1 f27011babd9c6149b994de78e6ea517ed4013e3c lib/EntityModel/Query/OrderField.pod
SHA1 eb2007d453c99eca92bba1aab97886ae3dea0a82 lib/EntityModel/Query/ParseSQL.pm
SHA1 ffe57eecc7cdecc2d3767d406ae7963e9298f847 lib/EntityModel/Query/ParseSQL.pod
SHA1 c95120125c0130b293dab7f4d00cecb2c00130c9 lib/EntityModel/Query/ReturningField.pm
SHA1 cdaca6c175d1256d7c08d06893c83e705823dfde lib/EntityModel/Query/ReturningField.pod
SHA1 815a28cf14323f53a550e2c2810a3bc50b67845e lib/EntityModel/Query/Select.pm
SHA1 127935af6bf20ff858a8576218de3cd85ec5271f lib/EntityModel/Query/Select.pod
SHA1 4c6186c9edbe7c6fdd4c561f0ec8b9b4c43bb534 lib/EntityModel/Query/SubQuery.pm
SHA1 ec8f05612241a1774c82a10037ef22502876bc76 lib/EntityModel/Query/SubQuery.pod
SHA1 77fe028477efcba7d76e3938f5f390f1befb1411 lib/EntityModel/Query/Table.pm
SHA1 c35fd4ccb598bff23f93b7cccd7229fdfedd49b8 lib/EntityModel/Query/Table.pod
SHA1 869b5c173e23f752fef3751b9f49d17c26627677 lib/EntityModel/Query/Union.pm
SHA1 d09b5e7d012aff5160d0af8c20169220a096136b lib/EntityModel/Query/Union.pod
SHA1 07439b770f2127de2937a3faff577a10b2a1888f lib/EntityModel/Query/UnionAll.pm
SHA1 0a3b52894e515433a793aa1017ba3d5721a16563 lib/EntityModel/Query/UnionAll.pod
SHA1 13bb66d00a17680aa5331f966eab07f9d8000f79 lib/EntityModel/Query/Update.pm
SHA1 27cc4394b6db78f9f6bfa1abf061184380308daf lib/EntityModel/Query/Update.pod
SHA1 84964f1701c7cde762ef5c62d8ec024ea4caf8ce lib/EntityModel/Query/UpdateField.pm
SHA1 78efbeb660a1905a08802c8cec2b674035a71e65 lib/EntityModel/Query/UpdateField.pod
SHA1 5789349d4ab1dd29bc5b13f6e6a28dae65bb4899 lib/EntityModel/Resolver.pm
SHA1 b499576ff47551a7a4d9740c63dd241d65164787 lib/EntityModel/Resolver.pod
SHA1 d27bff1ae0ac5b06d7654c2b43005ea097fa4f48 lib/EntityModel/Storage.pm
SHA1 6e259808173f828e550d0c736ef8692bb79e07e8 lib/EntityModel/Storage.pod
SHA1 ba8c846a3c19c8bb7c538772c63e49f5f87712fd lib/EntityModel/Storage/Perl.pm
SHA1 f9a2ed116c36d9aa99a598adefe5a2c83922c97c lib/EntityModel/Storage/Perl.pod
SHA1 5894ce872cf6d0ada8c1df61539fd270e7c98770 lib/EntityModel/Storage/PerlAsync.pm
SHA1 d97cc7f5d5af02b40690d94abd8949a539a0aaec lib/EntityModel/Storage/PerlAsync.pod
SHA1 62f70854bf2b790a27a395e38a0dc6d271ee689a lib/EntityModel/StorageClass/KVStore.pm
SHA1 25a4349a640e5ba5ba1c0cac568e589d746057c2 lib/EntityModel/StorageClass/KVStore/Layer.pm
SHA1 cf25bb83331a819e8268aa5bf94ca10955620d78 lib/EntityModel/StorageClass/KVStore/Layer/Fake.pm
SHA1 9d9ddf700864a8fe5b666530c0953f9a2e066695 lib/EntityModel/StorageClass/KVStore/Layer/Fake.pod
SHA1 23621a7b4c996115ff1efba0b70a46e096ec90e9 lib/EntityModel/StorageClass/KVStore/Layer/LRU.pm
SHA1 40d910b350479ab862a3605399d72682347534f8 lib/EntityModel/StorageClass/KVStore/Layer/LRU.pod
SHA1 6419f4bc361f41975b6f6bb4df42735cd4156b1c lib/EntityModel/StorageClass/KVStore/Layer/Memcached.pm
SHA1 7abc3c6ef1890b4a2681468235b0020bfa45d414 lib/EntityModel/StorageClass/KVStore/Layer/Memcached.pod
SHA1 982635cb036209863ef1caa29934ff38d338673d lib/EntityModel/StorageClass/KVStore/Layer/PostgreSQL.pm
SHA1 836dee5c1290a9a98bf261e37bfdcf9410ea81bb lib/EntityModel/StorageClass/KVStore/Layer/PostgreSQL.pod
SHA1 11c1a40299da15c4206a78ece08505fc35573d14 lib/EntityModel/StorageClass/KVStore/Mixin/Deferred.pm
SHA1 181289513bef83a64a6406fac82330dad4d2e272 lib/EntityModel/Support.pm
SHA1 53f7c698c3a4dc7e9fba6f9e941045eeaaa91127 lib/EntityModel/Support.pod
SHA1 2c95d29835581d9cd7aebe9a2958b31eb9859362 lib/EntityModel/Support/CPP.pm
SHA1 d663cdf13217ed88082412c43bdfa45ab4eb51cc lib/EntityModel/Support/CPP.pod
SHA1 496c49da6954116c21613061862f4f98adb7e526 lib/EntityModel/Support/Javascript.pm
SHA1 77cee5c54bc7071640bb81c012cac3de8e0cf5a8 lib/EntityModel/Support/Javascript.pod
SHA1 199d4fc2740ee1a5bff6da070304cc3ea0445663 lib/EntityModel/Support/Perl.pm
SHA1 c9036cfa3a5592b2bfa0a1257d643ddb2de44a92 lib/EntityModel/Support/Perl.pod
SHA1 7b6fe23d115ec936912896795483426d20d569e4 lib/EntityModel/Support/Perl/Base.pm
SHA1 3dde004f0110b488044fed4d49d9f149e7c85ae2 lib/EntityModel/Support/Perl/Base.pod
SHA1 3e740d1a29fdf22e5bbc2c96d1ce76b37eb5d210 lib/EntityModel/Support/Template.pm
SHA1 01dbfd1d8bcb21722f398e4a86c53c6abfbd6d53 lib/EntityModel/Support/Template.pod
SHA1 98e3ec5dc02a81df637ee0f40093551b2bab7715 lib/EntityModel/Template.pm
SHA1 2cc0ac78e206e93f3d5ab646baa5307da6a3f450 lib/EntityModel/Template.pod
SHA1 59496fe7204049467f5cc137b28081494c30954a lib/EntityModel/Test/Cache.pm
SHA1 c6efaca38991051cb6d2a8079c2219e0161cf02f lib/EntityModel/Test/Cache.pod
SHA1 9057d40eb79abc6d1b49a16e9d4d97b7c006d021 lib/EntityModel/Test/Storage.pm
SHA1 0e439a5c415ea6611d214b73c60ace681690ac5f lib/EntityModel/Test/Storage.pod
SHA1 60cc7ae6ad58d5461267ffc15d47a616ba06aa87 lib/EntityModel/Transaction.pm
SHA1 77f759dad38f1c3026b4a454f8d87da8804006e9 lib/EntityModel/Transaction.pod
SHA1 38a43da29c25b6445c43eb0d2845790e4ef0fc81 lib/EntityModel/Tutorial.pod
SHA1 251fd03ffb0684536c5253e4ff4b72002d7424dd lib/EntityModel/Util.pm
SHA1 328c3df02e43dcb4875f94ca6d280114719d631d lib/EntityModel/Util.pod
SHA1 69686edf5ed10f7649297bdb143838cab3ffd3a9 lib/Test/EntityModel.pm
SHA1 e134b4d595409876369181be04b609988e61500d lib/Test/EntityModel.pod
SHA1 da39a3ee5e6b4b0d3255bfef95601890afd80709 share/template/support/android.tt2
SHA1 503cd96f977c908cc01a679f2431d8ace33bd97b share/template/support/c.tt2
SHA1 2fb56aa6a371f16b2545be180c7cfd0684c43ada share/template/support/cpp.tt2
SHA1 99a29bbb70efc7223bc7355d38239e6090d2d61a share/template/support/django.tt2
SHA1 e76df361cf09a9da458392c5752f6067d87fe97b share/template/support/java.tt2
SHA1 c7cc85926102216e20bea43375b22437525b5952 share/template/support/js.tt2
SHA1 90f75289c4dda63c94bc5e897f4996b15d9e42dd t/00-check-deps.t
SHA1 9edf0267a56a6fd1fc2ca57e332dda5599e2eb4a t/00-compile.t
SHA1 0cb40883fa2a55a63607f222e30a3203fffde522 t/00-test-entitymodel.t
SHA1 3ed2e380c9344c9f17c1f45bff437405c5bce165 t/app.t
SHA1 bed349f22b71404e09b8675c438b21d679297ce1 t/async-storage-perl.t
SHA1 6cff07bdc2fe7e45c3191adb7334f629808a2a33 t/async.t
SHA1 5dcc0b28faa4e36067dd2f96d9cbfd76a6a5a231 t/cache.t
SHA1 42ead6ff3bdde094d79ed5ae3e36067d080bf2d1 t/collection.t
SHA1 2f7000ac41bbbe5d6125bf0f007d28f3f48cb223 t/deferred.t
SHA1 fd3839954e553368fc7e2004f28d37279462c867 t/definition.t
SHA1 85f69c3cfa80a28903481303ec8deda141331f3c t/entity-collection.t
SHA1 fd69ee715730e707b8a3d27ab46f40f4dcc449ad t/entity.t
SHA1 450125b4556df9b86b3d97683968a8586783e277 t/entitymodel.t
SHA1 0c3650a1cac1985df8ffd5c2497be9fc1b77fe13 t/field.t
SHA1 45a12cea100e37f38349189695155a67a8087f51 t/general.t
SHA1 cdc6de59c275de108afcb8cc340bd1526caafb92 t/kvstore.t
SHA1 25a47bec3dcc4f5b14525ec9e4e74902b3cbbf48 t/large-model.t
SHA1 929604162e204a235432857f4b886682c3327641 t/new-api.t
SHA1 f9220d322a5309fc287a5dff630b9f743ab92a8b t/plugin.t
SHA1 854da3402b1b422866fbe78bb0d44dc031be100d t/query.t
SHA1 0d2de6814b8909e2ebb451c9fda52324b192f857 t/resolver.t
SHA1 8ea74090330e1e50e48f1be0fae8a23b546520c7 t/sharedir.t
SHA1 cc8d82af85d903df0a54ef553f1714a8f77e1490 t/storage-create-entity.t
SHA1 dbaed61c06c8523636dbad3717725b45c0f862ea t/storage.t
SHA1 b79be57970af2dfcdd61e8ca10ec09b21a8ad08b t/support/c.t
SHA1 d68e4f2869874f8a0b775100978d4c65e4826751 t/support/cpp.t
SHA1 ea28ccbd91e6150fa7698460a510e32bd1c50f4f t/support/django.t
SHA1 52f57d386b711d4c854658d45a19ff6d7a85d519 t/support/doctrine.t
SHA1 3739004743e23ee9a8a4298323a3400d997baca6 t/support/java.t
SHA1 12dcf49419e1c34981edd99dbcf7653dc1feb83f t/support/js.t
SHA1 cf6d27676ba9960b5f097d952814ece90196f886 t/support/perl.t
SHA1 52f57d386b711d4c854658d45a19ff6d7a85d519 t/support/propel.t
SHA1 ea28ccbd91e6150fa7698460a510e32bd1c50f4f t/support/python.t
SHA1 52f57d386b711d4c854658d45a19ff6d7a85d519 t/support/redbean.t
SHA1 1963aa0f67da2ee938a960643654dfae2dd409b6 t/template.t
SHA1 7e1c264a07411dbbf16885b40c8bf52cbcb25264 t/test-cache.t
SHA1 c30cc97a63fa1055f6753896d393cda12e584b8b t/test-storage.t
SHA1 124f231670119306f120e8fb59704bf08eea25cb t/transaction.t
SHA1 6f846f7ede312098a614207bcea6fdb2207b8030 xt/author/test-eol.t
SHA1 198d1665317c3d91e3bb81f0a3b8dad8d4d66f20 xt/release/common_spelling.t
SHA1 c6c3c9f7fe12058b4771449dff874b1c450c982c xt/release/mojibake.t
SHA1 9934d36e45e1143c1a54658bc48872e0591af41b xt/release/pod-syntax.t
SHA1 bbcd59d081d9940af5d43337ae85458075933829 xt/release/test-version.t
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (GNU/Linux)

iQEcBAEBAgAGBQJSLLBWAAoJEFKElMNpjtyDn20H/A3W6mgMferPRYjtOTqj3vhN
6txC4cY2CKUdWT6VqRcu54NJA0V8grrF7U/ydP0/6kGQSLVraTr7kxzX1F81IUgw
HvPZXFN8e5Lfs/6PknJn0jM36mfRlFr5y54MRLdPyp1W3JLhB9AfCLy2eIxtp1O9
yV+Hk8XNOVzlNaNs5CCKaSNcsqUBAt9s6Oz0vYjA4kASWcfSvveaFKLfMBoV/cf3
xPrVXrzS9VWEqs15hYbENQDkHPTgQxKwYKSSSUEOMoIIGLytNoxI56LNInKSWH71
a5sCLHTDQZfzETlKFO5KGqLolEL0+OdmGmWY6J+Ty0SXAyKgiWUAo4gbJUwXB0s=
=TGpy
-----END PGP SIGNATURE-----

benchmark/model-size-time.pl  view on Meta::CPAN

#!/usr/bin/perl
use strict;
use warnings;
use Time::HiRes ();
use EntityModel;

my $name = 'aaaaaaaaaaa';
for my $entity_count (qw(1 2 5 10 20 50 100 200 500 1000 2000)) {
# for my $entity_count (qw(1 2 5 10 20 50 100 200 500 1000 2000 5000 10000 20000 50000 100000 200000 500000 1000000)) {
	my @entities;
	for my $idx (1..$entity_count) {
		push @entities, {
			"name" => $name,
			"primary" => "id$name",
			"field" => [
				{ "name" => "id$name", "type" => "bigserial" },
				{ "name" => "name", "type" => "varchar" },
				{ "name" => "other", "type" => "varchar" }
			]
		};
		++$name;
	}
	my $start = Time::HiRes::time;
	my $model = EntityModel->new;
	my $now = Time::HiRes::time;
	$model->load_from(
		Perl	=> {
			"name"	=> "mymodel",
			"entity"	=> \@entities
		}
	);
	my $now2 = Time::HiRes::time;
	my $load = 1000.0 * ($now2 - $now);
	$now = $now2;
	$model->add_storage(Perl => {});
	$now2 = Time::HiRes::time;
	my $storage = 1000.0 * ($now2 - $now);
	$now = $now2;
	$model->add_support(Perl => {});
	$now2 = Time::HiRes::time;
	my $perl = 1000.0 * ($now2 - $now);
	$now = $now2;
	printf("%9d %12.3f %12.3f %12.3f %12.3f\n", $entity_count, $load, $storage, $perl, 1000.0 * ($now - $start));
}

bin/entitymodel  view on Meta::CPAN

#!/usr/bin/perl
use EntityModel::App;
EntityModel::App->new->from_argv(@ARGV);

cpanfile  view on Meta::CPAN

requires 'curry', '>= 1.000';
requires 'Future', '>= 0.12';
requires 'EntityModel::Class', '>= 0.012';
requires 'JSON::XS', '>= 2.00';
requires 'XML::XPath', '>= 1.00';
requires 'Mixin::Event::Dispatch', '>= 1.000';
requires 'Template', '>= 2.24';
requires 'Parser::MGC', '>= 0.10';
requires 'File::ShareDir', '>= 1.00';
requires 'Module::Load', 0;
requires 'Tie::Hash::LRU', 0;
requires 'POSIX::strptime', 0;
requires 'Try::Tiny', 0;
requires 'Tie::Cache::LRU', 0;
requires 'DateTime', 0;
requires 'List::MoreUtils', 0;
requires 'CPS', 0;
requires 'IO::Socket::IP', 0;
requires 'IO::Async', '>= 0.50';

on 'test' => sub {
	requires 'Test::More', '>= 0.98';
	requires 'Test::Fatal', '>= 0.010';
	requires 'Test::Refcount', '>= 0.07';
	requires 'Test::Class', 0;
};

dist.ini  view on Meta::CPAN

name    	 = EntityModel
author  	 = Tom Molesworth <cpan@entitymodel.com>
license 	 = Perl_5
copyright_holder = Tom Molesworth
copyright_year   = 2012
main_module	 = lib/EntityModel.pm

[GatherDir]
[PruneCruft]
[ManifestSkip]
[MetaYAML]
[License]
[Readme]
[ExecDir]
dir = bin
[ShareDir]
dir = share
[MakeMaker]
eumm_version = 6.48
prereq_fatal = 1
[Manifest]
[TestRelease]
[ConfirmRelease]
[UploadToCPAN]
[Prereqs::FromCPANfile]
[Prereqs / BuildRequires]
perl = 5.010001
[CheckPrereqsIndexed]
[CheckExtraTests]
[NextRelease]
[VersionFromModule]
[PodVersion]
[PkgVersion]
[Homepage]
[Bugtracker]
[InstallGuide]
[MetaJSON]
[PodSyntaxTests]
[InsertExample]
[Test::CheckDeps]
[PodInherit]
; [PodCoverageTests]
[MojibakeTests]
[Test::Compile]
; [Test::Synopsis]
; [Test::UnusedVars]
[Test::EOL]
[Test::Version]
; Broken - can't find CGI::Application, see https://rt.cpan.org/Ticket/Display.html?id=78729
; [Test::Pod::LinkCheck]
[ArchiveRelease]
directory = /home/tom/dev/CPAN-Archive
; seems to be broken in several ways, even after getting past installation hurdles it complains
; about the repository needing to be upgraded - all I want is the basic svn cp functionality
; so handling this outside dzil for now
;[Subversion::Tag]
;tag_url = https://entitymodel.com/svn/tag/
[Signature]
[SpellingCommonMistakesTests]
[ReversionOnRelease]

examples/apachelog.pl  view on Meta::CPAN

#!/usr/bin/perl
use strict;
use warnings;

my $host;
BEGIN {
	eval { require ApacheLog::Compressor } or die "ApacheLog::Compressor is required";
	$host = eval { require Sys::Hostname; Sys::Hostname::hostname } // 'localhost';
}

use Encode qw(decode_utf8 is_utf8);

my $in = shift @ARGV;
die "No input file provided" unless defined $in && length $in;

binmode STDOUT, ':encoding(utf8)';
binmode STDERR, ':encoding(utf8)';

# Processor for incoming data
my $alc_in = ApacheLog::Compressor->new(
	on_write	=> sub {
		my ($self, $pkt) = @_;
		print { $out_fh } $pkt;
	},
	filter => sub {
		my ($self, $data) = @_;
		# Ignore entries with no URL or timestamp
		return 0 unless defined $data->{url} && length $data->{url};
		return 0 unless $data->{timestamp};

		# Skip irrelevant entries (some loadbalancers use this as a 'ping')
		return 0 if $ApacheLog::Compressor::HTTP_METHOD_LIST[$data->{method}] eq 'OPTIONS' && $data->{url} eq '*';
		return 1;
	}
);

# Input file - normally use whichever one's just been closed + rotated
open my $in_fh, '<', $in or die "Failed to open input file $in - $!";
binmode $in_fh, ':encoding(utf8)';

# Initial packet to identify which server this came from
$alc_in->send_packet('server',
	hostname => hostname(),
);

# Read and compress all the lines in the files
while(my $line = <$in_fh>) {
        $alc->compress($line);
}
close $in_fh or die $!;
close $out_fh or die $!;

# Dump the stats in case anyone finds them useful
$alc->stats;


# Provide a callback to send data through to the file
my $alc = ApacheLog::Compressor->new(
	on_log_line	=> sub {
		my ($self, $data) = @_;
		# Use the helper method to expand back to plain text representation
		print { $out_fh } $self->data_to_text($data) . "\n";
	},
);

# Input file - normally use whichever one's just been closed + rotated
open my $in_fh, '<', $in or die "Failed to open input file $in - $!";
binmode $in_fh;

# Read and expand all the lines in the files
my $buffer = '';
while(read($in_fh, my $data, 1024) >= 0) {
	$buffer .= $data;
        $alc->expand(\$buffer);
}
close $in_fh or die $!;
close $out_fh or die $!;

# Dump the stats in case anyone finds them useful
$alc->stats;

#!/usr/bin/env perl
use strict;
use warnings;

use constant MAPPED_KEYS => qw(vhost ip user url refer useragent);
use constant UNMAPPED_KEYS => qw(result duration size method ver);

sub setup {
	my $self = shift;
}

sub on_log_line {
	my $self = shift;
	my $line_data = shift;
	$repo->gather(
		map $_ . ':' . $line_data->{$_}, MAPPED_KEYS,
	)->apply(sub {
		# This will now have mappings from key to ID
		my %data = @_;
		# and for the remainder we use the original values
		@data{+UNMAPPED_KEYS} = @{ $line_data }{+UNMAPPED_KEYS};
		$self->writelog(\%data);
	});
}

sub writelog {
	my $self = shift;
}

1;

__END__

=pod

package Mixin::Data::Source;



=cut

examples/async.pl  view on Meta::CPAN

#!/usr/bin/perl
use strict;
use warnings;
use 5.010;

# Warning: This is an incomplete example. Please see the documentation or t/*.t files instead.

use EntityModel;

=pod

If the first parameter is a hashref, additional options may be given:

 Entity::Thing->create({
 	},
	done	=> sub { ... },
	fail	=> sub { ... },
 );

=cut


=pod

# Bring the model in at compilation stage, since I have a vague
# idea that perhaps this way performance is better.
BEGIN {
	my $model = EntityModel->new;
}

=cut

package Mixin::Deferrable;
sub fail {
	my ($self, $code) = @_;
	if($self->{failed}) {
		$code->($self);
		return $self;
	}

	$self->{on_fail} = $code;
	return $self;
}

sub done {
	my ($self, $code) = @_;
	if($self->{finished}) {
		$code->($self);
		return $self;
	}

	$self->{on_done} = $code;
	return $self;
}

package Entity::Country;
use parent -norequire => 'Mixin::Deferrable';

sub create {
	my $class = shift;

	my $self = bless {
		id => 14
	}, $class;
	$self->{finished} = 1;
	return $self;
}

sub id { shift->{id} }
sub name { shift->{name} }

package Entity::Address;
use parent -norequire => 'Mixin::Deferrable';

sub create {
	my $class = shift;

	my $self = bless {
		id => 9
	}, $class;
	$self->{finished} = 1;
	return $self;
}

sub id { shift->{id} }
sub name { shift->{name} }

package Entity::Author;
use parent -norequire => 'Mixin::Deferrable';

sub create {
	my $class = shift;

	my $self = bless {
		id => 9
	}, $class;
	$self->{finished} = 1;
	return $self;
}

sub id { shift->{id} }
sub name { shift->{name} }

package Entity::Book;
use parent -norequire => 'Mixin::Deferrable';

sub create {
	my $class = shift;
	my @args = @_;

	my $self = bless {
		id => 3,

	}, $class;
	$self->{finished} = 1;
	return $self;
}

sub id { shift->{id} }
sub title { shift->{title} }

#package Deferrable::Util;
#use parent qw(Exporter);
#our @EXPORT_OK = qw(prepare);

package main;
use Scalar::Util qw(blessed);
#use Deferrable::Util qw(prepare);

=head2 prepare

=cut

sub prepare {
	my $req = shift;
	my %args = @_;

# Any entries that are still pending
	my %pending;
# Completed data items
	my %data;
# Failed entries
	my %failed;

# Hit the appropriate callback on completion
	my $done = 0;
	my $complete = sub {
		return if $done++;

		if(keys %failed) {
			return $args{fail}->(\%data, \%failed) if $args{fail};
			die "Failed keys: " . (join ',', keys %failed) . "\n";
		}

		return $args{done}->(\%data);
	};

# Any entries that inherit from the Deferrable mixin need to be queued,
# we do this first so that we know exactly how many we're waiting for
	%pending = map {
		$_ => $req->{$_}
	} grep {
		blessed($req->{$_}) && $req->{$_}->isa('Mixin::Deferrable')
	} keys %$req;

# Queue events and copy values
	foreach my $k (keys %$req) {
		if(exists $pending{$k}) {
			$req->{$k}->done(sub {
				$data{$k} = shift;
				delete $pending{$k};
				$complete->() unless keys %pending;
			})->fail(sub {
				$failed{$k} = shift;
				delete $pending{$k};
				$complete->() unless keys %pending;
			});
# Everything else can be used as-is
		} else {
			$data{$k} = $req->{$k};
		}
	}

# Hit the completion point if everything is already in place
	$complete->() unless keys %pending;
}

# Use ->lookup to find a single entry, ->create to create a single entry,
# and ->lookup_or_create to use the existing entry if available and create
# a new one if not.

Entity::Country->create(
	name => 'UK'
);

# Default behaviour is to ->lookup_or_create
Entity::Book->create(
	title	=> 'Some book title',
	author	=> {
		name	=> 'Fred',
		address	=> {
			street => 'Some road',
			country => {
				-action	=> 'lookup',
				name	=> 'UK',
			}
		}
	}
)->done(sub {
	my $book = shift;
	printf "We now have a book with ID [%s], title %s, author %s (id %s), address id %s with street %s, country %s (id %s)\n",
		$book->id,
		$book->title,
		$book->author->name,
		$book->author->id,
		$book->author->address->id,
		$book->author->address->street,
		$book->author->address->country->name,
		$book->author->address->country->id;
})->fail(sub {
	warn "Something went wrong\n";
});


# Nested creation will wait until all components are ready before hitting the callback,
# and the resulting structure will be fully populated
prepare {
	book	=> Entity::Book->create(
		title	=> 'Some book title',
		author	=> Entity::Author->lookup_or_create(
			name	=> 'Fred',
			address	=> Entity::Address->lookup_or_create(
				street => 'Some road',
				country => Entity::Country->lookup(
					name => 'UK'
				)
			)
		)
	)
}, done => sub {
	my $data = shift;
	say "Book was created with ID " . $data->{book}->id . " and author " . $data->{book}->author->id;
}, fail => sub {
	say "Failed: @_\n";
};

examples/auth.pl  view on Meta::CPAN

#!/usr/bin/perl
use strict;
use warnings;
use EntityModel;

sub validate {
	my %args = @_;
	my $user;
	Entity::User->find({
		username => $args{username},
		password => $args{password},
	})->first(sub {
		$user = shift;
	})->done(sub { $args{done}->($user) });
}

examples/cpan.pl  view on Meta::CPAN

#!/usr/bin/env perl
use strict;
use warnings;
use feature qw(say);
use EntityModel;
use EntityModel::Log qw(:all);
use IO::Async::Loop;
use CPAN::SQLite::Info;

my $model = EntityModel->new->load_from(
	Perl => {
		name => 'cpan',
		entity => [ {
			name => 'author',
			primary => 'cpan_id',
			field => [
				{ name => 'cpan_id', type => 'text' },
				{ name => 'fullname', type => 'text' },
				{ name => 'email', type => 'text' },
			],
		}, {
			name => 'distribution',
			primary => 'name',
			field => [
				{ name => 'name', type => 'text' },
				{ name => 'version', type => 'text' },
				{ name => 'file', type => 'text' },
				{ name => 'abstract', type => 'text' },
				{ name => 'idauthor', type => 'text', refer => [
					{ table => 'author', field => 'cpan_id' },
				] },
			],
		}, {
			name => 'module',
			primary => 'name',
			field => [
				{ name => 'name', type => 'text' },
				{ name => 'version', type => 'text' },
				{ name => 'dist_name', type => 'text', refer => [
					{ table => 'distribution', field => 'name' },
				] },
				{ name => 'abstract', type => 'text' },
				{ name => 'dslip', type => 'text' },
				{ name => 'idauthor', type => 'text', refer => [
					{ table => 'author', field => 'cpan_id' },
				] },
			],
		} ],
	}
)->add_storage(
	PerlAsync => { loop => IO::Async::Loop->new },
)->add_support(
	Perl => { }
);

my $info = CPAN::SQLite::Info->new(CPAN => $ENV{HOME} . '/.cpan/sources');
$info->fetch_info;

say 'Authors';
# First populate all the author entries
foreach my $author_key (keys %{$info->{auths}}) {
	my $a = Entity::Author->create({
		cpan_id => $author_key,
		fullname => $info->{auths}{$author_key}{fullname},
		email => $info->{auths}{$author_key}{email},
	})->commit;
}

say 'Distributions';
foreach my $dist_key (keys %{$info->{dists}}) {
	my $d = Entity::Distribution->create({
		name => $dist_key,
		version => $info->{dists}{$dist_key}{dist_vers},
		file => $info->{dists}{$dist_key}{dist_file},
		abstract => $info->{dists}{$dist_key}{dist_abs},
		cpan_id => $info->{dists}{$dist_key}{cpanid},
	})->commit;
}

say 'Modules';
foreach my $mod (keys %{$info->{mods}}) {
	my $m = Entity::Module->create({
		name => $mod,
		dist_name => $info->{mods}{$mod}{dist_name},
		version => $info->{mods}{$mod}{mod_vers},
		abstract => $info->{mods}{$mod}{mod_abs},
		dslip => $info->{mods}{$mod}{dslip},
		author => Entity::Distribution->new($info->{mods}{$mod}{dist_name})->author,
	})->commit;
}

say "My modules:";
say $_->name for Entity::Module->find({ author => Entity::Author->new('TEAM') });

examples/cpp/Author.h  view on Meta::CPAN

/**
 * @file author.h
 * @author Tom Molesworth <tom@entitymodel.com>
 * @date 17/11/12 12:10:43
 *
 * $Id$
 */

#include <string>

namespace Model {

class Author {
public:
	Author() {
		name_ = "";
	}
virtual	~Author() { }

	Author &name(std::string name) { this->name_ = name; return *this; }
	std::string name(void) { return this->name_; }

private:
	std::string name_;
};

};

examples/cpp/Book.h  view on Meta::CPAN

/**
 * @file book.h
 * @author Tom Molesworth <tom@entitymodel.com>
 * @date 17/11/12 12:03:58
 *
 * $Id$
 */

#include <string>

namespace Model {

class Author;

class Book {
public:
	Book() {
		name_ = "";
		author_ = nullptr;
	}
virtual	~Book() { }

	Book &name(std::string name) { this->name_ = name; return *this; }
	std::string name(void) { return this->name_; }

	Book &author(Author *author) { this->author_ = author; return *this; }
	Author *author(void) { return this->author_; }

private:
	std::string name_;
	Author *author_;
};

};

examples/cpp/Library.h  view on Meta::CPAN

/**
 * @file library.h
 * @author Tom Molesworth <tom@entitymodel.com>
 * @date 17/11/12 12:17:53
 *
 * $Id$
 */

#include <string>

namespace Model {

class Book;

class Library {
public:
	Library() {
	}

virtual	~Library() { }

private:
};

};


examples/cpp/LibraryItem.h  view on Meta::CPAN

/**
 * @file LibraryItem.h
 * @author Tom Molesworth <tom@entitymodel.com>
 * @date 17/11/12 12:25:18
 *
 * $Id$
 */

#include <string>
#include <list>

namespace Model {

class Book;

class LibraryItem {
public:
	LibraryItem(Library &library):
		library_(library)
	{
	}

virtual	~LibraryItem() { }

private:
	Library &library_;
};

};


examples/cpp/Model.h  view on Meta::CPAN

/**
 * @file model.h
 * @author Tom Molesworth <tom@entitymodel.com>
 * @date 17/11/12 12:03:08
 *
 * $Id$
 */
#ifndef ENTITYMODEL__MODEL_H__
#define ENTITYMODEL__MODEL_H__

#include "Author.h"
#include "Book.h"
#include "Library.h"
#include "LibraryItem.h"

#endif

examples/cpp/main.cpp  view on Meta::CPAN

#include <iostream>
#include "Model.h"

using namespace std;
using namespace Model;

int
main(void)
{
/* Basic Test::More-ish behaviour */
	int test = 1;
	int expected_tests;
	auto note = [] (std::string m) {
		cout << u8"# " << m << endl;
	};
	auto ok = [&test] (bool check, std::string m) {
		cout << std::string(check ? u8"ok" : u8"not ok")
		  << u8" " << test++ << u8" - " << m << endl;
	};
	auto plan = [&expected_tests] (int count) {
		expected_tests = count;
		cout << u8"1.." << expected_tests << endl;
	};

	plan(5);

/*
 * Simple key=>value accessors on an object
 */
	note(u8"Creating author");
	Author author;
	author.name(std::string(u8"First author"));
	ok(
	  author.name() == u8"First author",
	  u8"name was set correctly"
	);
	note(u8"Author's name is: " + author.name());

/*
 * References to other objects
 */
	note(u8"Creating book");
	Book book;
	book.name(std::string(u8"First book")).author(&author);
	note(u8"Book name is " + book.name());
	note(u8"Author's name is " + book.author()->name());
	ok(book.name() == u8"First book", u8"book has correct name");
	ok(book.author() == &author, u8"book has correct author");
	ok(
	  book.author()->name() == u8"First author",
	  u8"linked author for book has correct name"
	);

/*
 * Collections
 */
	note(u8"Creating library");
	Library library;
	note(u8"Adding book to library");

	return 0;
}

examples/emt.pl  view on Meta::CPAN

#!/usr/bin/perl
use strict;
use warnings;
use EntityModel::Log qw(:all);
EntityModel::Log->instance->min_level(0);

package Entity::Article;
use EntityModel::EntityCollection;

sub find {
	my $self = shift;
	# Parameters are passed through to the query engine, which should know better than
	# us how to reorganise and optimise them.
	return EntityModel::EntityCollection->new();
}

package main;
use Try::Tiny;

try {
	# Predefine the grouping field since we'll use this in more than one place.
	my $group_by = {
		# Specify a field and modifier
		op	=> 'date',
		field	=> 'created',
		alias	=> 'date_created'
	};

	my @found;

	# We are going to store the result into a temporary variable, which should prevent the automatic
	# commit behaviour in void context. Once this variable goes out of scope and hits cleanup, the
	# commit method will be called via DESTROY. Start by specifying some criteria to search for:
	Entity::Article->find({
		# This will be converted to ILIKE 'Test%' on most databases
		name	=> qr/^Test/i,
	})->group(
		# Use our previously-defined field specification here
		$group_by
	# Select on two fields: first is the same one we're grouping by, second is the article count
	)->select($group_by, {
		op	=> 'count',
		field	=> 'idarticle',
		alias	=> 'count',
	})->order({
	# Sort by that first field
		alias	=> 'date_created'
	})->each(sub {
	# Each item we happen to get from the query so far will pass through this function
		my ($self, $item) = @_;
		logInfo("Had item %s", $item);
		push @found, $item;
	})->done(sub {
	# On completion, just report what we found
		logInfo("Finished with %d items", scalar(@found));
	})->fail(sub {
	# Failure shouldn't happen - but we override the die-on-error default by specifying this here.
		logInfo("Failed?");
	});
} catch {
	logError("Raised error: %s", $_);
};

try {
	# Predefine the grouping field since we'll use this in more than one place.
	my $group_by = {
		# Specify a field and modifier
		op	=> 'date',
		field	=> 'created',
		alias	=> 'date_created'
	};

	my @found;

	Entity::Article->find({
	})->select(
		field	=> [qw(created title)],
	)->order({
	# Sort by that first field
		field	=> 'created',
		direction => 'desc'
	})->each(sub {
	# Each item we happen to get from the query so far will pass through this function
		my ($self, $item) = @_;
		logInfo("Had item %s", $item);
		push @found, $item;
	})->done(sub {
	# On completion, just report what we found
		logInfo("Finished with %d items", scalar(@found));
	})->fail(sub {
	# Failure shouldn't happen - but we override the die-on-error default by specifying this here.
		logInfo("Failed?");
	});
} catch {
	logError("Raised error: %s", $_);
};

examples/library.js  view on Meta::CPAN



/* Create a new article entry */
var article = Entity.Article.create({
 title   : 'Test article',
 content : 'Article content'
});
/* Locate the item we just inserted */
Entity.Article.find({
 title : 'Test article'
}).item(function(article) {
 alert("Had article ID " . article.id());
 article.title('Updated title').done(function() {
  alert("Have updated title");
 });
});

examples/library.php  view on Meta::CPAN

<?php

/* Create new article entry */
$article = Entity\Article::create(array(
 title => 'Test article',
 content => 'Article content'
));

/* Retrieve the article we just created */
$match = Entity\Article::find(array(
 title => 'Test article'
));
$match->title('Updated title');

?>

examples/query.pl  view on Meta::CPAN

#!/usr/bin/perl
use strict;
use warnings;
use EntityModel;
my $model = EntityModel->new;

#$model->add_loop($loop);

# Simple query:
#  select idarticle
#  from article a
#  where title like 'Test%'
Entity::Article->find({
	title	=> qr/^Test/,
});

# Nested conditions
#  select idarticle
#  from article a
#  inner join author au
#  where au.name = 'Tom Molesworth'
Entity::Article->find({
	author	=> {
		name => 'Tom Molesworth'
	}
});

#  select idarticle
#  from article a
#  inner join author au
#  where au.name = 'Tom Molesworth'
#  or a.title = 'Test'
Entity::Article->find({
	author	=> {
		name => 'Tom Molesworth',
	},
	-or => title => 'Test'
});

#  select idarticle
#  from article a
#  inner join author au
#  where au.name = 'Tom Molesworth'
#  and a.title = 'Test'
Entity::Article->find({
	author	=> {
		name => 'Tom Molesworth',
	},
	-and => title => 'Test'
});

# 2-level nesting with no data from intermediate table required:
# just skip the table and go directly to the joining table.
#  select idarticle
#  from article a
#  inner join author_tag at on at.idauthor = a.idauthor
#  where at.name = 'editor'
Entity::Article->find({
	author	=> {
		tag	=> {
			name => 'editor'
		}
	},
});


#  select	ar.idarticle,
#  		sum(case t.name when 'staff' then 1 else 0 end) as "tagged_staff",
#  		sum(case t.name when 'editor' then 1 else 0 end) as "tagged_editor"
#  from		article ar
#  inner join	author_tag at
#  on		aut.idauthor = ar.idauthor
#  inner join	tag t
#  on		at.idtag = t.idtag
#  where	t.txt in ('staff', 'editor')
#  and		title ilike 'Test%'
#  group by	ar.idarticle
#  having	tagged_staff > 0
#  and		tagged_editor > 0
Entity::Article->find({
	author	=> {
		tag	=> {
			name	=> { -all => [qw(staff editor)] },
		}
	},
	title	=> qr/^Test/i,
})->each(sub {

})->done(sub {

});
# Alternative using subquery:
#  select	ar.idarticle
#  from		article ar
#  inner join	(
#  	select		at.idauthor
#  	from		author_tag at
#  	inner join	tag t on t.idtag = at.idtag
#  	group by	at.idauthor
#  	having		sum(case t.name when 'staff' then 1 else 0 end) > 0
#  	and		sum(case t.name when 'editor' then 1 else 0 end) > 0
#  ) as x
#  where	title ilike 'Test%'

# The find method returns a collection of all matched elements.
Entity::Article->find();

# ->create is used for single entities
Entity::Article->create({
	title	=> 'Test Article index ' . $_,
	content	=> 'Article content would be here'
}) for 0..999;

# Mass creation can be handled with the ->populate method. This will mass load data
# into the underlying tables and may disable indexes or perform other cleanup operations
# to ensure that large quantities of data can be loaded as quickly as possible.
Entity::Article->populate(
	fields	=> [qw(title content)],
	data	=> [
		[ ]
	],
);

# Individual entities can be updated or removed via ->update and ->remove.
# These actions can also be applied through the collection interface.
Entity::Article->update(thing => 17);
Entity::Article->find({ title => 'Test' })->remove;

# The entire table can be cleared using ->truncate
Entity::Article->truncate;

Entity::Article->populate(
);

=pod

Author -> author_tag -> Tag

Many-to-many relationships provide additional opportunities for linking. For a simple author/tag link,
you could list authors which have:
* All [these tags]
* None of [these tags]
* Any of [these tags]
* One of [these tags]
Each of these modes requires grouping on the main table, after which additional operations can be
performed using aggregate functions:
* all => sum(case field when value then 1 else 0) having field > 0 for values
* none => sum(case field when value then 1 when value2 then 1 else 0) for values having sum = 0
* any => sum(case field when value then 1 when value2 then 1 else 0) for values having sum > 0
* one => sum(case field when value then 1 else 0) having (sum field) + (sum field2) = 1 for values



 select		ar.idarticle
 from		article ar
 inner join	article_author aa on aa.idarticle = ar.idarticle
 inner join	(
  	select		at.idauthor
  	from		author_tag at
  	inner join	tag t on t.idtag = at.idtag
  	group by	at.idauthor
  	having		sum(case t.name when 'staff' then 1 else 0 end) > 0
  	and		sum(case t.name when 'editor' then 1 else 0 end) > 0
 ) as x on aa.idauthor = x.idauthor
 where	title like 'Test%';

Start off with all intermediate tables included. A later optimisation pass could perhpas elide any tables
which do not contribute to the final result.

Article -> Author -> Address -> City -> Tag

Project -> Issue -> Email -> Commit -> Tag


create table author (
	idauthor bigserial primary key,
	name text
);
create index a_name on author(name);

create table article (
	idarticle bigserial primary key,
	title text,
	content text
);
create index a_title on article(title);

create table tag (
	idtag bigserial primary key,
	name text
);
create index t_name on tag(name);

create table article_tag (
	idarticle_tag bigserial primary key,
	idarticle bigint references article(idarticle) on delete cascade on update cascade,
	idtag bigint references tag(idtag) on delete cascade on update cascade
);
create index at_article on article_tag(idarticle);
create index at_tag on article_tag(idtag);
create table author_tag (
	idauthor_tag bigserial primary key,
	idauthor bigint references author(idauthor) on delete cascade on update cascade,
	idtag bigint references tag(idtag) on delete cascade on update cascade
);
create index aut_author on author_tag(idauthor);
create index au_tag on author_tag(idtag);

create table article_author (
	idarticle_author bigserial primary key,
	idarticle bigint references article(idarticle) on delete cascade on update cascade,
	idauthor bigint references author(idauthor) on delete cascade on update cascade
);
create index aa_article on article_author(idarticle);
create index aa_author on article_author(idauthor);

=cut

lib/EntityModel.pm  view on Meta::CPAN

package EntityModel;
# ABSTRACT: Cross-language event-driven ORM

use EntityModel::Class {
	_isa => [qw(EntityModel::Model)],
	name => { type => 'string' },
	plugin => { type => 'array', subclass => 'EntityModel::Plugin' },
	support => { type => 'array', subclass => 'EntityModel::Support' },
	storage => { type => 'array', subclass => 'EntityModel::Storage' },
	storage_queued => { type => 'array', subclass => 'EntityModel::Storage' },
	cache => { type => 'array', subclass => 'EntityModel::Cache' },
	cache_queued => { type => 'array', subclass => 'EntityModel::Cache' },
	db => { type => 'EntityModel::DB' },
};
no if $] >= 5.017011, warnings => "experimental::smartmatch";

our $VERSION = '0.102';

=head1 NAME

EntityModel - manage entity model definitions

=head1 VERSION

version 0.102

=head1 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;
 });

=head1 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 L</MOTIVATION> and L</SEE ALSO> sections, please check there
before investing any time into using this module.

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

=head1 METHODS

=cut

use Module::Load ();

use EntityModel::Transaction;
use EntityModel::Entity;
use EntityModel::Field;
use EntityModel::Query;

=head2 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:

=over 4

=item * L</load_from>

=item * L</add_storage>

=item * L</add_plugin>

=back

=cut

sub new {
	my $class = shift;

	my @def;
	if(ref $_[0] ~~ 'HASH') {
		@def = %{$_[0]};
	} elsif(ref $_[0] ~~ 'ARRAY') {
		@def = @{$_[0]};
	} else {
		@def = @_;
	}

	my $self = bless { }, $class;

	# Apply plugins and options
	while(@def) {
		my ($k, $v) = splice @def, 0, 2;
		$self->load_plugin($k => $v);
	}

	return $self;
}

=head2 load_from

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

Parameters:

=over 4

=item * Type - must be a valid L<EntityModel::Definition> subclass, such as 'Perl', 'JSON' or 'XML'.

=item * Definition - dependent on the subclass, typically the filename or raw string data.

=back

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" }
    ]
   ]
  }
 );

=cut

sub load_from {
	my $self = shift;
	my ($type, $value) = @_;

	my $class = "EntityModel::Definition::$type";
	$self->load_component($class);

	$class->new->load(
		model	=> $self,
		source	=> $value
	);
	return $self;
}

=head2 save_to

Saves the current model definition to a definition.

Parameters:

=over 4

=item * Type - must be a valid L<EntityModel::Definition> subclass, such as 'Perl', 'JSON' or 'XML'.

=item * Definition - dependent on the subclass, typically the filename or scalarref to hold raw string data.

=back

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
 );

=cut

sub save_to {
	my $self = shift;
	my ($type, $value) = @_;

	my $class = "EntityModel::Definition::$type";
	$self->load_component($class);

	$class->new->save(
		model	=> $self,
		target	=> $value
	);
	return $self;
}

=head2 load_component

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

Typically used by internal methods only.

=cut

sub load_component {
	my $self = shift;
	my $class = shift;
	unless($class->can('new')) {
		Module::Load::load($class);
		$class->register;
	}
	return $self;
}

=head2 add_support

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

Example:

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

=cut

sub add_support {
	my ($self, $name, $v) = @_;
	logDebug("Load support for [%s]", $name);
	my $class = 'EntityModel::Support::' . $name;
	$self->load_component($class);

	my $obj = $class->new($self, $v);
	$obj->setup($v);
	$obj->apply_model($self);
	$self->support->push($obj);
	return $self;
}

=head2 add_storage

Add backend storage provided by an L<EntityModel::Storage> subclass.

Example:

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

=cut

sub add_storage {
	my ($self, $name, $v) = @_;
	delete $self->{backend_ready};
	logDebug("Load storage for [%s]", $name);
	my $class = $name;
	try { Module::Load::load($class) } unless try { $class->can('new') };
	unless(try { $class->can('new') }) {
		$class = 'EntityModel::Storage::' . $name;
		Module::Load::load($class);
	}

	my $obj = $class->new($self, $v);
	$self->storage_queued->push($obj);
	$self->add_handler_for_event(
		backend_ready => sub {
			my $self = shift;
			$self->{backend_ready} = 1;
			0 # one-shot
		}
	);
	$obj->setup($v);
	$obj->wait_for_backend($self->sap( sub {
		my ($model, $obj) = @_;
		$obj->apply_model($model);
		$model->storage->push($model->storage_queued->shift);
		$model->invoke_event(backend_ready =>) unless $model->storage_queued->count;
		0; # one-shot event
	}));
	return $self;
}

=head2 backend_ready

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

=cut

sub backend_ready { shift->{backend_ready} }

=head2 wait_for_backend

Requests an event to run after all backends signal readiness.

=cut

sub wait_for_backend {
	my $self = shift;
	my $code = shift;
	return $code->($self) if $self->backend_ready;
	$self->add_handler_for_event( backend_ready => sub { $code->(@_); 0 });
	return $self;
}

=head2 add_cache

Add backend cache provided by an L<EntityModel::Storage> subclass.

Example:

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

=cut

sub add_cache {
	my ($self, $name, $v) = @_;
	logDebug("Load cache for [%s]", $name);
	my $class = $name;
	try { Module::Load::load($class) } unless try { $class->can('new') };
	unless(try { $class->can('new') }) {
		$class = 'EntityModel::Cache::' . $name;
		Module::Load::load($class);
	}

	my $obj = $class->new($self, $v);
	$obj->setup($v);
	$obj->apply_model($self);
	$self->cache->push($obj);
	return $self;
}

=head2 add_plugin

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

=cut

sub add_plugin {
	my $self = shift;
	my ($name, $v) = @_;
	my $plugin;
	if(eval { $name->isa('EntityModel::Plugin') }) {
		$plugin = $name;
	} else {
		my $mod = 'EntityModel::' . $name;
		Module::Load::load($mod);
		$plugin = $mod->new;
	}
	$plugin->register($self);
	$self->plugin->push($plugin);
	return $self;
}

=head2 transaction

Run the coderef in a transaction.

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

=cut

sub transaction {
	my $self = shift;
	my $code = shift;
	return EntityModel::Transaction->new(
		code	=> $code,
		model	=> $self,
		param	=> [ @_ ]
	);
}

=head2 load_plugin

Used internally, see L</add_plugin>. Will disappear in the future.

=cut

sub load_plugin {
	my ($self, $name, $v) = @_;
	logDebug("Load plugin [%s]", $name);
	my $class = 'EntityModel::Plugin::' . $name;
	unless(eval { $class->can('new') }) {
		Module::Load::load($class);
	}
	logDebug("Activating plugin [%s]", $name);
	my $obj = $class->new($self, $v);
	$obj->setup($v);
	$self->plugin->push($obj);
	return $self;
}

=head2 handler_for

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

=cut

sub handler_for {
	my $self = shift;
	my $name = shift;
	logDebug("Check for handlers for [%s] node", $name);
	my @handler;
	$self->plugin->each(sub {
		push @handler, $_[0]->handler_for($name);
	});
	return @handler;
}

sub defer {
	my $self = shift;
	my $code = shift;

}

{
my $model;
sub default_model {
	my $class = shift;
	if(@_) {
		my $old_model = $model;
		$model = shift;
		return $old_model;
	}
	$model ||= EntityModel->new;
	return $model
}
}

=head2 DESTROY

Unload all plugins on exit.

=cut

sub DESTROY {
	my $self = shift;
	$self->plugin->each(sub {
		$_[0]->unload;
	});
}

1;

__END__

=head2 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 L<EntityModel::Entity> for more
details:

=over 4

=item * B<name> - the name of the entity, must be unique in the model

=item * B<type> - typically 'table'

=item * B<description> - a more detailed description of the entity and purpose

=item * B<primary> - information about the primary key

=back

Each entity may have zero or more fields:

=over 4

=item * B<name> - the unique name for this field

=item * B<type> - standard SQL type for the field

=item * B<null> - true if this can be null

=item * B<reference> - foreign key information

=back

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.

=over 4

=item * B<name> - unique name for the index

=item * B<type> - index type, typically one of 'gin', 'gist', or 'btree'

=item * B<fields> - list of fields that are indexed

=back

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 L<EntityModel::Plugin>
for more details on this.

=head1 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 L<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');

=head1 IMPLEMENTATION

Nearly all classes use L<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 L<EntityModel::Log>, which imports functions such as logDebug.

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

For error handling, an L<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.

=head1 OVERVIEW

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

=over 4

=item * L<EntityModel::Definition::XML> - XML structured definition holding the entities, fields and any additional
plugin-specific data.  All information is held in the content - no attributes are used, allowing this format to be
interchangeable with JSON and internal Perl datastructures.

=item * L<EntityModel::Definition::JSON> - standard Javascript Object Notation format.

=item * L<EntityModel::Definition::Perl> - nested Perl datastructures, with the top level being a hashref. Note that
this is distinct from the Perl class/entity structure described later.

=back

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:

=over 4

=item * L<EntityModel::Support::SQL> - database schema

=item * L<EntityModel::Support::Perl> - Perl classes

=item * L<EntityModel::Support::CPP> - C++ classes

=item * L<EntityModel::Support::JS> - Javascript code

=back

The SQL handling is provided as a generic DBI-compatible layer with additional support in subclasses
for specific databases. Again, Note that the L<EntityModel::Support::SQL> is intended for applying the
model to the database schema, rather than accessing the backend storage. The L<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.

=head2 BACKEND STORAGE

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

=over 4

=item * L<EntityModel::Storage::PostgreSQL> - PostgreSQL database support

=item * L<EntityModel::Storage::MySQL> - MySQL database support

=item * L<EntityModel::Storage::SQLite> - SQLite3 database support

=back

=head2 CACHING

Cache layers are handled by L<EntityModel::Cache> subclasses.

=over 4

=item * L<EntityModel::Cache::MemcachedFast> - memcached layer using L<Cache::Memcached::Fast>.

=item * L<EntityModel::Cache::Perl> - cache via Perl variables

=back

=head2 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 C<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.

=head2 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';
 });

=head2 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++.

=head3 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:

=over 4

=item * audit_action - one of:

=over 4

=item * B<Insert> - regular insert or mass import (such as PostgreSQL COPY statement)

=item * B<Update> - updates directly through database or through the L<EntityModel> API

=item * B<Delete> - manual or API removal

=back

and indicates the action that generated this audit entry.

=item * B<audit_date> - timestamp of this action

=item * B<audit_context> - description of the action, typically the command that was called

=back

=head2 CLASS STRUCTURE

The primary classes used for interaction with models include:

=over 4

=item * L<EntityModel> - top level class providing helper methods

=item * L<EntityModel::Definition> - classes for dealing with model definitions

=item * L<EntityModel::Support> - language-specific support

=back

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

=over 4

=item * L<EntityModel::DB> - wrapper for DBI providing additional support for transaction handling

=item * L<EntityModel::Query> - database query handling

=item * L<EntityModel::Template> - wrapper around Template Toolkit

=item * L<EntityModel::Cache> - simple cache implementation using L<Cache::Memcached::Fast> by default

=back

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

=head1 MOTIVATION

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

=over 4

=item * B<Event-based operation> - backend storage requests run 'asynchronously' where possible,
allowing the application to continue with other tasks while waiting for database queries to complete.

=item * B<Support for languages other than Perl> - most projects end up using at least one other
language, e.g. web-based projects typically have some Javascript on the frontend talking to the
Perl backend.

=item * B<Configurable with a single file> - I like to be able to export, backup and diff the entity
layout and having this in a single file as JSON or XML is more convenient for me than using multiple
Perl packages. This also allows the configuration to be used by non-Perl code, since it's a rare project
these days that only involves a single language.

=item * B<Backend abstraction> - the conceptual model generally doesn't need to be tied to a particular
backend, for example the Perl side of things could either talk directly to a database or use a webservice.

=item * B<Easy editing and visualisation> - I like to have the option of using other tools such as
diagram editors to add/modify the entity layout, rather than having to create or edit Perl files. This
helps to separate actual code changes from configuration / layout issues; the L<EntityModel> distribution
can be installed once and for many common applications no further custom code should be required.

=item * B<Flexibility> - although too much interdependence is generally a bad thing, being able to include
other concepts in the configuration file has been useful for tasks such as building websites with common
features.

=back

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

=head1 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:

=head2 Asynchronous ORMs

The list here is sadly lacking:

=over 4

=item * L<Async::ORM|https://github.com/vti/async-orm> - asynchronous ORM, see also article in L<http://showmetheco.de/articles/2010/1/mojolicious-async-orm-and-dbslayer.html>

=back

=head2 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:

=over 4

=item * L<DBIx::Class> - appears to be the most highly regarded and actively developed one out there, and
the available features, code quality and general stability are far in advance of this module. Unless you
need the L<EntityModel> multi-language or asynchronous support features I would strongly encourage looking
at L<DBIx::Class> first

=item * L<Rose::DB::Object> - written for speed, appears to cover most of the usual requirements, personally
found the API less intuitive than other options but it appears to be widely deployed

=item * L<Fey::ORM> - newer than the other options, also appears to be reasonably flexible

=item * L<DBIx::DataModel> - UML-based Object-Relational Mapping (ORM) framework

=item * L<Alzabo> - another ORM which includes features such as GUI schema editing and SQL diff

=item * L<Class::DBI> - generally considered to be superceded by L<DBIx::Class>, which provides a compatibility
layer for existing applications

=item * L<Class::DBI::Lite> - like L<Class::DBI> but lighter, presumably

=item * L<ORMesque> - lightweight class-based ORM using L<SQL::Abstract>

=item * L<Oryx> - Object persistence framework, meta-model based with support for both DBM and regular RDBMS
backends, uses tied hashes and arrays

=item * L<Tangram> - An object persistence layer

=item * L<KiokuDB> - described as an "Object Graph storage engine" rather than an ORM

=item * L<DBIx::DataModel> - ORM using UML definitions

=item * L<Jifty::DBI> - another ORM

=item * L<ORLite> - minimal SQLite-based ORM

=item * L<Ormlette> - object persistence, "heavily influenced by Adam Kennedy's L<ORLite>". "light and fluffy", apparently!

=item * L<ObjectDB> - another lightweight ORM, currently has only L<DBI> as a dependency

=item * L<ORM> - looks like it has support for MySQL, PostgreSQL and SQLite

=item * L<fytwORM> - described as a "bare minimum ORM used for prototyping / proof of concepts"

=item * L<DBR> - Database Repository ORM

=item * L<SweetPea::Application::Orm> - specific to the L<SweetPea> web framework

=item * L<Jorge> - ORM Made simple

=item * L<Persistence::ORM> - looks like a combination between persistent Perl objects and standard ORM

=item * L<Teng> - lightweight minimal ORM

=item * L<Class::orMapper> - DBI-based "easy O/R Mapper"

=item * L<UR|https://github.com/genome/UR> - class framework and object/relational mapper (ORM) for Perl

=item * L<DBIx::NinjaORM> - "Flexible Perl ORM for easy transitions from inline SQL to objects"

=item * L<DBIx::Oro> - Simple Relational Database Accessor

=item * L<LittleORM> - Moose-based ORM

=item * L<Storm> - another Moose-based ORM

=item * L<DBIx::Mint> - "A mostly class-based ORM for Perl"

=back

=head2 Database interaction

=over 4

=item * L<DBI::Easy> - seems to be a wrapper around L<DBI>

=item * L<AnyData> - interface between L<DBI> and arbitrary data sources such as XML or HTML

=item * L<DBIx::ThinSQL> - helpers for SQL statements

=item * L<DB::Evented> - event-based wrapper for L<DBI>-like behaviour, uses L<AnyEvent::DBI>

=back

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 L<Moose>, L<Moo> and derivatives.

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

=head1 AUTHOR

Tom Molesworth <cpan@entitymodel.com>

=head1 LICENSE

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

lib/EntityModel.pod  view on Meta::CPAN

=for comment POD_DERIVED_INDEX_GENERATED
The following documentation is automatically generated.  Please do not edit
this file, but rather the original, inline with EntityModel
at lib/EntityModel.pm
(on the system that originally ran this).
If you do edit this file, and don't want your changes to be removed, make
sure you change the first line.

=cut

=head1 NAME

EntityModel - manage entity model definitions

=head1 VERSION

version 0.102

=head1 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;
 });

=head1 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 L</MOTIVATION> and L</SEE ALSO> sections, please check there
before investing any time into using this module.

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

=head1 METHODS

=head2 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:

=over 4

=item * L</load_from>

=item * L</add_storage>

=item * L</add_plugin>

=back

=head2 load_from

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

Parameters:

=over 4

=item * Type - must be a valid L<EntityModel::Definition> subclass, such as 'Perl', 'JSON' or 'XML'.

=item * Definition - dependent on the subclass, typically the filename or raw string data.

=back

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" }
    ]
   ]
  }
 );

=head2 save_to

Saves the current model definition to a definition.

Parameters:

=over 4

=item * Type - must be a valid L<EntityModel::Definition> subclass, such as 'Perl', 'JSON' or 'XML'.

=item * Definition - dependent on the subclass, typically the filename or scalarref to hold raw string data.

=back

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
 );

=head2 load_component

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

Typically used by internal methods only.

=head2 add_support

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

Example:

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

=head2 add_storage

Add backend storage provided by an L<EntityModel::Storage> subclass.

Example:

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

=head2 backend_ready

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

=head2 wait_for_backend

Requests an event to run after all backends signal readiness.

=head2 add_cache

Add backend cache provided by an L<EntityModel::Storage> subclass.

Example:

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

=head2 add_plugin

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

=head2 transaction

Run the coderef in a transaction.

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

=head2 load_plugin

Used internally, see L</add_plugin>. Will disappear in the future.

=head2 handler_for

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

=head2 DESTROY

Unload all plugins on exit.

=head2 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 L<EntityModel::Entity> for more
details:

=over 4

=item * B<name> - the name of the entity, must be unique in the model

=item * B<type> - typically 'table'

=item * B<description> - a more detailed description of the entity and purpose

=item * B<primary> - information about the primary key

=back

Each entity may have zero or more fields:

=over 4

=item * B<name> - the unique name for this field

=item * B<type> - standard SQL type for the field

=item * B<null> - true if this can be null

=item * B<reference> - foreign key information

=back

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.

=over 4

=item * B<name> - unique name for the index

=item * B<type> - index type, typically one of 'gin', 'gist', or 'btree'

=item * B<fields> - list of fields that are indexed

=back

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 L<EntityModel::Plugin>
for more details on this.

=head1 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 L<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');

=head1 IMPLEMENTATION

Nearly all classes use L<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 L<EntityModel::Log>, which imports functions such as logDebug.

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

For error handling, an L<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.

=head1 OVERVIEW

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

=over 4

=item * L<EntityModel::Definition::XML> - XML structured definition holding the entities, fields and any additional
plugin-specific data.  All information is held in the content - no attributes are used, allowing this format to be
interchangeable with JSON and internal Perl datastructures.

=item * L<EntityModel::Definition::JSON> - standard Javascript Object Notation format.

=item * L<EntityModel::Definition::Perl> - nested Perl datastructures, with the top level being a hashref. Note that
this is distinct from the Perl class/entity structure described later.

=back

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:

=over 4

=item * L<EntityModel::Support::SQL> - database schema

=item * L<EntityModel::Support::Perl> - Perl classes

=item * L<EntityModel::Support::CPP> - C++ classes

=item * L<EntityModel::Support::JS> - Javascript code

=back

The SQL handling is provided as a generic DBI-compatible layer with additional support in subclasses
for specific databases. Again, Note that the L<EntityModel::Support::SQL> is intended for applying the
model to the database schema, rather than accessing the backend storage. The L<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.

=head2 BACKEND STORAGE

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

=over 4

=item * L<EntityModel::Storage::PostgreSQL> - PostgreSQL database support

=item * L<EntityModel::Storage::MySQL> - MySQL database support

=item * L<EntityModel::Storage::SQLite> - SQLite3 database support

=back

=head2 CACHING

Cache layers are handled by L<EntityModel::Cache> subclasses.

=over 4

=item * L<EntityModel::Cache::MemcachedFast> - memcached layer using L<Cache::Memcached::Fast>.

=item * L<EntityModel::Cache::Perl> - cache via Perl variables

=back

=head2 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 C<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.

=head2 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';
 });

=head2 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++.

=head3 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:

=over 4

=item * audit_action - one of:

=over 4

=item * B<Insert> - regular insert or mass import (such as PostgreSQL COPY statement)

=item * B<Update> - updates directly through database or through the L<EntityModel> API

=item * B<Delete> - manual or API removal

=back

and indicates the action that generated this audit entry.

=item * B<audit_date> - timestamp of this action

=item * B<audit_context> - description of the action, typically the command that was called

=back

=head2 CLASS STRUCTURE

The primary classes used for interaction with models include:

=over 4

=item * L<EntityModel> - top level class providing helper methods

=item * L<EntityModel::Definition> - classes for dealing with model definitions

=item * L<EntityModel::Support> - language-specific support

=back

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

=over 4

=item * L<EntityModel::DB> - wrapper for DBI providing additional support for transaction handling

=item * L<EntityModel::Query> - database query handling

=item * L<EntityModel::Template> - wrapper around Template Toolkit

=item * L<EntityModel::Cache> - simple cache implementation using L<Cache::Memcached::Fast> by default

=back

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

=head1 MOTIVATION

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

=over 4

=item * B<Event-based operation> - backend storage requests run 'asynchronously' where possible,
allowing the application to continue with other tasks while waiting for database queries to complete.

=item * B<Support for languages other than Perl> - most projects end up using at least one other
language, e.g. web-based projects typically have some Javascript on the frontend talking to the
Perl backend.

=item * B<Configurable with a single file> - I like to be able to export, backup and diff the entity
layout and having this in a single file as JSON or XML is more convenient for me than using multiple
Perl packages. This also allows the configuration to be used by non-Perl code, since it's a rare project
these days that only involves a single language.

=item * B<Backend abstraction> - the conceptual model generally doesn't need to be tied to a particular
backend, for example the Perl side of things could either talk directly to a database or use a webservice.

=item * B<Easy editing and visualisation> - I like to have the option of using other tools such as
diagram editors to add/modify the entity layout, rather than having to create or edit Perl files. This
helps to separate actual code changes from configuration / layout issues; the L<EntityModel> distribution
can be installed once and for many common applications no further custom code should be required.

=item * B<Flexibility> - although too much interdependence is generally a bad thing, being able to include
other concepts in the configuration file has been useful for tasks such as building websites with common
features.

=back

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

=head1 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:

=head2 Asynchronous ORMs

The list here is sadly lacking:

=over 4

=item * L<Async::ORM|https://github.com/vti/async-orm> - asynchronous ORM, see also article in L<http://showmetheco.de/articles/2010/1/mojolicious-async-orm-and-dbslayer.html>

=back

=head2 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:

=over 4

=item * L<DBIx::Class> - appears to be the most highly regarded and actively developed one out there, and
the available features, code quality and general stability are far in advance of this module. Unless you
need the L<EntityModel> multi-language or asynchronous support features I would strongly encourage looking
at L<DBIx::Class> first

=item * L<Rose::DB::Object> - written for speed, appears to cover most of the usual requirements, personally
found the API less intuitive than other options but it appears to be widely deployed

=item * L<Fey::ORM> - newer than the other options, also appears to be reasonably flexible

=item * L<DBIx::DataModel> - UML-based Object-Relational Mapping (ORM) framework

=item * L<Alzabo> - another ORM which includes features such as GUI schema editing and SQL diff

=item * L<Class::DBI> - generally considered to be superceded by L<DBIx::Class>, which provides a compatibility
layer for existing applications

=item * L<Class::DBI::Lite> - like L<Class::DBI> but lighter, presumably

=item * L<ORMesque> - lightweight class-based ORM using L<SQL::Abstract>

=item * L<Oryx> - Object persistence framework, meta-model based with support for both DBM and regular RDBMS
backends, uses tied hashes and arrays

=item * L<Tangram> - An object persistence layer

=item * L<KiokuDB> - described as an "Object Graph storage engine" rather than an ORM

=item * L<DBIx::DataModel> - ORM using UML definitions

=item * L<Jifty::DBI> - another ORM

=item * L<ORLite> - minimal SQLite-based ORM

=item * L<Ormlette> - object persistence, "heavily influenced by Adam Kennedy's L<ORLite>". "light and fluffy", apparently!

=item * L<ObjectDB> - another lightweight ORM, currently has only L<DBI> as a dependency

=item * L<ORM> - looks like it has support for MySQL, PostgreSQL and SQLite

=item * L<fytwORM> - described as a "bare minimum ORM used for prototyping / proof of concepts"

=item * L<DBR> - Database Repository ORM

=item * L<SweetPea::Application::Orm> - specific to the L<SweetPea> web framework

=item * L<Jorge> - ORM Made simple

=item * L<Persistence::ORM> - looks like a combination between persistent Perl objects and standard ORM

=item * L<Teng> - lightweight minimal ORM

=item * L<Class::orMapper> - DBI-based "easy O/R Mapper"

=item * L<UR|https://github.com/genome/UR> - class framework and object/relational mapper (ORM) for Perl

=item * L<DBIx::NinjaORM> - "Flexible Perl ORM for easy transitions from inline SQL to objects"

=item * L<DBIx::Oro> - Simple Relational Database Accessor

=item * L<LittleORM> - Moose-based ORM

=item * L<Storm> - another Moose-based ORM

=item * L<DBIx::Mint> - "A mostly class-based ORM for Perl"

=back

=head2 Database interaction

=over 4

=item * L<DBI::Easy> - seems to be a wrapper around L<DBI>

=item * L<AnyData> - interface between L<DBI> and arbitrary data sources such as XML or HTML

=item * L<DBIx::ThinSQL> - helpers for SQL statements

=item * L<DB::Evented> - event-based wrapper for L<DBI>-like behaviour, uses L<AnyEvent::DBI>

=back

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 L<Moose>, L<Moo> and derivatives.

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

=head1 INHERITED METHODS

=over 4

=item L<EntityModel::Model>

L<add_entity|EntityModel::Model/add_entity>, L<add_field_to_table|EntityModel::Model/add_field_to_table>, L<add_table|EntityModel::Model/add_table>, L<apply|EntityModel::Model/apply>, L<apply_fields|EntityModel::Model/apply_fields>, L<commit|EntityMo...

=item L<Mixin::Event::Dispatch>

L<add_handler_for_event|Mixin::Event::Dispatch/add_handler_for_event>, L<clear_event_handlers|Mixin::Event::Dispatch/clear_event_handlers>, L<event_handlers|Mixin::Event::Dispatch/event_handlers>, L<invoke_event|Mixin::Event::Dispatch/invoke_event>, ...

=item L<EntityModel::BaseClass>

L<clone|EntityModel::BaseClass/clone>, L<sap|EntityModel::BaseClass/sap>

=back

=head1 AUTHOR

Tom Molesworth <cpan@entitymodel.com>

=head1 LICENSE

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

 view all matches for this distribution
 view release on metacpan -  search on metacpan

( run in 2.176 seconds using v1.00-cache-2.02-grep-82fe00e-cpan-2cc899e4a130 )