/usr/share/perl5/UR/Manual/Overview.pod is in libur-perl 0.430-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | =pod
=head1 NAME
UR::Manual::Overview - UR from Ten Thousand Feet
=head1 Perspective on Objects
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:
=over 4
=item *
exists only as long as the program which created it has a reference to it
=item *
requires that the developer manage organizing the object(s) into a structure
to support any searching required
=item *
handles persistence between processes explicitly, by saving or loading the
object to external storage
=item *
references other objects only if explicitly linked to those objects
=item *
acts as a functional software device, but any meaning associated with the
object is implied by how it is used
=back
Regular 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:
=over 4
=item *
the object exists after creation until explicitly deleted, or the transaction
it is in rolled-back
=item *
managing loaded objects is done automatically by a Context object, which
handles queries, saving, lazy-loading and caching
=item *
it is possible to query for an object by specifying the class and the
matching characteristics
=item *
the object can reference other objects which are not loaded in the current
process, and be referenced by objects not in the current process
=item *
the object is a particular truth-assertion in the context in which it exists
=back
=head1 Object-Relational Mapping
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
persistent 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:
=over 4
=item *
classes for things which are not database entities at all
=item *
derived classes where the data spans multiple tables between the parent
and child classes
=item *
loading an object through a parent class and having it automatically
reblessed into the appropriate subclass
=item *
properties with no DB column behind them
=item *
calculated properties with a formula behind them
=item *
inheritance hierarchies that may have tables missing at some or all stages
=item *
meta-data about Properties, Classes and the relationships between them
=back
=head1 Object Context
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.
=head2 Benefits
=over 4
=item *
database queries don't repeat themselves again and again
=item *
you never write insert/update/delete statements, or work out constraint
order yourself
=item *
allows you to write methods which address an object individually, with
ways to avoid many individual database queries
=item *
explicitly clearing the cache is less complex than explicitly managing the
caching of data
=back
=head2 Issues
=over 4
=item *
the cache grows until you explicitly clear it, or allow the Context to prune
the cache by setting object count limits explicitly
=item *
there is CPU overhead checking the cache if you really are always going
directly to the database
=back
=head1 Class Definitions
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.
=head1 Metadata
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 accessible 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.
=head1 Documentation System
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.
=head1 Iterators
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.
=head1 Command Line Tools
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.
=head1 Example
Given these classes:
=over 4
=item PathThing/Path.pm
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',
};
=item PathThing/Node.pm
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',
};
=back
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. C<@results> will contain a list of matching PathThing::Node objects.
|