UR::Manual::Overview - UR from Ten Thousand Feet
Standard software languages provide a facility for making objects. Those objects have certain characteristics which are different with UR objects.
A standard object in most languages:
object(s)
into a structure
to support any searching requiredRegular objects like those described above are the building blocks of most software.
In many cases, however, they are often used for a second, higher-level purpose: defining entities in the domain model of the problem area the software addresses. UR objects are tailored to represent domain model entities well. In some sense, UR objects follow many of the design principles present in relational databases, and as such mapping to a database for UR objects is trivial, and can be done in complex ways.
UR objects differ from a standard object in the following key ways:
UR's primary reason for existing is to function as an ORM. That is, managing how to store instances of objects in memory of a running program with more persistant storage in a relational database, and retrieve them later. It handles the common cases where each table is implemented by a class their columns are properties of the classes; retrieving objects by arbitrary properties; creating, updating and deleting objects with enforced database constraints; and named relationships between classes.
It can also handle more complicated things like:
With UR, every object you create is made a part of the current ``Context''. Conceptually, the Context is the lens by which your application views the data that exists in the world. At one level, you can think of the current context as an in-memory transaction. All changes to the object are tracked by the context. The Context knows how to map objects to their storage locations, called Data Sources. Saving your changes is simply a matter of asking the current context to commit.
The Context can also reverse the saving process, and map a request for an object to a query of external storage. Requests for objects go through the Context, are loaded from outside as needed, and are returned to the caller after being made part of the current context's transaction.
Objects never reference each other by actual Perl reference internally, instead they use the referent's ID. Accessors on an object which return another object send the ID through the context to get the object back, allowing the context to load the referenced object only when it is actually needed. This means that your objects can hook together until references span an entire database schema, and pulling one object from the database will not load the entire database into memory.
The context handles caching, and by default will cache everything it touches. This means that you can ask for the same thing multiple times, and only the first request will actually hit the underlying database. It also means that requests for objects which map to the same ID will return the exact same instance of the object.
The net effect is that each process's context is an in-memory database. All object creation, deletion, and change is occurring directly to that database. For objects configured to have external persistence, this database manages itself as a ``diff'' vs. the external database, allowing it to simulate representing all UR data everywhere, while only actually tracking what is needed.
At the top of every module implementing a UR class is a block of code that defines the class to explicitly spell out its inheritance, properties and types, constraints, relationships to other classes and where the persistent storage is located. It's meant to be easy to read and edit, if necessary. If the class is backed by a database table, then it can also maintain itself.
Besides the object instances representing data used by the program, the UR system has other objects representing metadata about the classes (class information, properties, relationships, etc), database entities (databases, tables, columns, constraints, etc), transactions, data sources, etc. All the metadata is accessable through the same API as any of the database-backed data.
For classes backed by the database, after a schema change (like adding tables or columns, altering types or constraints), a command-line tool can automatically detect the change and alter the class definition in the Perl module to keep the metadata in sync with the database.
At the simplest level, most entities have a 'doc' metadata attribute to attach some kind of documentation to. There's also a set of tools that can be run from the command line or a web browser to view the documentation. It can also be used to browse through the class and database metadata, and generate diagrams about the metadata.
If a retrieval from the database is likely to result in the generation of tons of objects, you can choose to get them back in a list and keep them all in memory, or get back a special Iterator object that the program can use to get back objects in batches.
UR has a central command-line tool that cam be used to manipulate the metadata in different ways. Setting up namespaces, creating data sources, syncing classes with schemas, accessing documentation, etc.
There is also a framework for creating classes that represent command line tools, their parameters and results, and makes it easy to create tools through the Command Pattern.
Given these classes:
use strict; use warnings; use PathThing; # The application's UR::Namespace module class PathThing::Path { id_by => 'path_id', has => [ desc => { is => 'String' }, length => { is => 'Integer' }, ], data_source => 'PathThing::DataSource::TheDB', table_name => 'PATHS', };
class PathThing::Node { id_by => 'node_id', has => [ left_path => { is => 'PathThing::Path', id_by => 'left_path_id' }, left_path_desc => { via => 'left_path', to => 'desc' }, left_path_length => { via => 'left_path', to => 'length' }, right_path => { is => 'PathThing::Path', id_by => 'right_path_id' }, right_path_desc => { via => 'right_path', to => 'desc' }, right_path_length => { via => 'right_path', to => 'length' }, map_coord_x => { is => 'Integer' }, map_coord_y => { is => 'String' }, ], data_source => 'PathThing::DataSource::TheDB', table_name => 'NODES', };
For a script like this one:
use PathThing::Node; my @results = PathThing::Node->get( right_path_desc => 'over the river', left_path_desc => 'through the woods', right_path_length => 10, );
It will generate SQL like this:
select NODES.NODE_ID, NODES.LEFT_PATH_ID, NODES.RIGHT_PATH_ID, NODES.MAP_COORD_X, NODES.MAP_COORD_Y, left_path_1.PATH_ID, left_path_1.DESC, left_path_1.LENGTH right_path_1.PATH_ID, right_path_1.DESC, right_path_1.LENGTH from NODES join PATHS left_path_1 on NODES.LEFT_PATH_ID = left_path_1.PATH_ID join PATHS right_path_1 on NODES.RIGHT_PATH_ID = right_path1.PATH_ID where left_path_1.DESC = 'through the woods' and right_path_1.DESC = 'over the river', and right_path_1.LENGTH = 10
And for every row returned by the query, a PathThing::Node and two
PathThing::Path objects will be instantiated and stored in the Context's
cache. @results
will contain a list of matching PathThing::Node objects.
UR is a Class Framework and Object/Relational Mapper (ORM) for Perl.
After installing, run the "ur" command for a list of options.
As a Class Framework, it starts with the familiar Perl meme of the blessed hash reference as the basis for object instances, and builds upon that with a more formal way to describe classes and their properties, object caching, and metadata about the classes and the ways they connect to each other.
As an ORM, it aims to relieve the developer from having to think about the SQL behind any particular request, instead using the class structure and its metadata as a guide for where the data will be found. Behind the scenes, the RDBMS portion can handle JOINs (both INNER and OUTER) representing inheritance and indirect properties, multi-column primary and foreign keys, and iterators. It does its best to only query the database for information you've directly asked for, and to not query the database for something that has been loaded before. Oracle, SQLite, MySQL and PostgreSQL are all supported.
Additionally, UR can use files or collections of files as if they were tables in a database, as well as internally handling the equivalent of an SQL join between two or more databases if that's what the query and class structure indicates.
UR.pm contains more introductory POD documentation. UR::Manual has a short list of documentation you're likely to want to see next.
install.md 100644 000765 000024 335 13332055231 17135 0 ustar 00abrummet staff 000000 000000 UR-0.47/gmt-web/content {% include install/download.html %} It is also availabe from [CPAN](http://search.cpan.org/search?mode=all&query=UR). {% include install/github.html %} {% include install/help.html %} {% include install/manuals.html %} icon_16.png 100644 000765 000024 1274 13332055231 21172 0 ustar 00abrummet staff 000000 000000 UR-0.47/gmt-web/content/res/images ‰PNG IHDR ‘h6 tEXtSoftware Adobe ImageReadyqÉe<