.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "UR::DataSource::Filesystem 3pm" .TH UR::DataSource::Filesystem 3pm "2019-01-02" "perl v5.28.1" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" UR::DataSource::Filesystem \- Get and save objects to delimited text files .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 8 \& # Create an object for the data file \& my $people_data = UR::DataSource::Filesystem\->create( \& columns => [\*(Aqperson_id\*(Aq,\*(Aqname\*(Aq,\*(Aqage\*(Aq,\*(Aqstreet_address\*(Aq], \& sorted_columns => [\*(Aqage\*(Aq,\*(Aqperson_id\*(Aq], \& path => \*(Aq/var/lib/people/$state/$city/people.txt\*(Aq, \& delimiter => "\et", # between columns in the file \& record_separator => "\en", # between lines in the file \& ); \& \& # Define an entity class for the people in the file \& class MyProgram::Person { \& id_by => \*(Aqperson_id\*(Aq, \& has => [ \& name => { is => \*(AqString\*(Aq }, \& age => { is => \*(AqNumber\*(Aq }, \& street_address => { is => \*(AqString\*(Aq }, \& city => { is => \*(AqString\*(Aq }, \& state => { is => \*(AqString\*(Aq }, \& ], \& data_source_id => $people_data\->id, \& }; \& \& # Get all people that live in any city named Springfield older than 40 \& my @springfielders = MyProgram::Person\->get(city => \*(AqSpringfield\*(Aq, \*(Aqage >\*(Aq => 40); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" A Filesystem data source object represents one or more files on the filesystem. In the simplest case, the object's 'path' property names a file that stores the data. .SS "Properties" .IX Subsection "Properties" These properties determine the configuration for the data source. .IP "path " 4 .IX Item "path " path is a string representing the path to the files. Besides just being a simple pathname to one file, the string can also be a specification of many similar files, or a directory containing multiple files. See below for more information about 'path' .IP "record_separator " 4 .IX Item "record_separator " The separator between lines in the file. This gets stored in $/ before calling \&\fBgetline()\fR to read data. The default record_separator is \*(L"\en\*(R". .IP "delimiter " 4 .IX Item "delimiter " The separator between columns in the file. It is used to construct a regex with \fBqr()\fR to \fBsplit()\fR a line into a list of values. The default delimiter is '\es*,\es*', meaning that the file is separated by commas. Another common value would be \*(L"\et\*(R" for tabs. .IP "columns <\s-1ARRAY\s0>" 4 .IX Item "columns " A listref of column names in the file. Just as \s-1SQL\s0 tables have columns, Filesystem files also have named columns so the system knows how to read the file data into object properties. A Filesystem data source does not need to specify named columns if the 'columns_from_header' property is true. .Sp Classes that use the Filesystem data source attach their properties to the data source's columns via the 'column_name' metadata. Besides the columns directly named in the 'columns' list, two additional column-like tokens may be used as a column_name: '_\|_FILE_\|_' and '$.'. _\|_FILE_\|_ means the object's property will hold the name of the file the data was read from. $. means the value will be the input line number from the file. These are useful when iterating over the contents of a file. Since these two fake columns are always considered \*(L"sorted\*(R", it makes reading from the file faster in some cases. See the 'sorted_columns' discussion below for more information. .IP "sorted_columns <\s-1ARRAY\s0>" 4 .IX Item "sorted_columns " A listref of column names that the file is sorted by, in the order of the sorting. If a column is sorted in descending order, put a minus (\-) in front of the name. If the file is sorted by multiple columns, say first by last_name and then by first_name, then include them both: .Sp .Vb 1 \& sorted_columns => [\*(Aqlast_name\*(Aq,\*(Aqfirst_name\*(Aq] .Ve .Sp The system uses this information to know when to stop reading if a query is done on a sorted column. It's also used to determine whether a query done on the data source matches the sort order of the file. If not, then the data must be gathered in two passes. The first pass finds records in the file that match the filter. After that, the matching records are sorted in the same way the query is requesting before returning the data to the Context. .Sp The Context expects incoming data to always be sorted by at least the class' \s-1ID\s0 properties. If the file is unsorted and the caller wants to be able to iterate over the data, then it is common to have the class' \s-1ID\s0 properties specified like this: .Sp .Vb 4 \& id_by => [ \& file => { is => \*(AqString\*(Aq, column_name => \*(Aq_\|_FILE_\|_\*(Aq }, \& line => { is => \*(AqInteger\*(Aq, column_name => \*(Aq$.\*(Aq }, \& ] .Ve .Sp Otherwise, it will need to read in the whole file and sort the contents before returning the first row of data from its iterator. .IP "columns_from_header " 4 .IX Item "columns_from_header " If true, the system will read the first line of the file to determine what the column names are. .IP "header_lines " 4 .IX Item "header_lines " The number of lines at the top of the file that do not contain entity data. When the file is opened, this number of lines are skipped before reading data. If the columns_from_header flag is true, the header_lines value should be at least 1. .IP "handle_class " 4 .IX Item "handle_class " Which class to use for reading and writing to the file. The default is IO::File. Any other value must refer to a class that has the same interface as IO::File, in particular: new, input_line_number, getline, tell, seek and print. .SS "Path specification" .IX Subsection "Path specification" Besides referring to just one file on the filesystem, the path spec is a recipe for finding files in a directory tree. If a class using a Filesystem data source does not have 'table_name' metadata, then the path specification must resolve to file names. Alternatively, classes may specify their \&'table_name' which is interpreted as a file within the directory indicated by the path specification. .PP Three kinds of special tokens can also appear in a file spec: .ie n .IP "$property" 4 .el .IP "\f(CW$property\fR" 4 .IX Item "$property" When querying, the system will extract the value (or values, for an in-clause) of \f(CW$property\fR from the BoolExpr when constructing the pathname. If the BoolExpr does not have a value for that property, then the system will do a shell glob to find the possible values. For example, given this path spec and query: .Sp .Vb 2 \& path => \*(Aq/var/people/$state/$city/people.txt\*(Aq \& my @people = MyProgram::People\->get(city => \*(AqSpringfield\*(Aq, \*(Aqage >\*(Aq => 40); .Ve .Sp it would find the data files using the glob expression .Sp .Vb 1 \& /var/people/*/Springfield/people.txt .Ve .Sp It also knows that any objects coming from the file .Sp .Vb 1 \& /var/people/CA/Springfield/people.txt .Ve .Sp must have the value '\s-1CA\s0' for their 'state' property, even though that information is not in the contents of the file. .Sp When committing changes back to the file, the object property values are used to determine which file it should be saved to. .Sp The property name can also be wrapped in braces: .Sp .Vb 1 \& /var/people/${state}_US/city_${city}/people.txt .Ve .IP "&method" 4 .IX Item "&method" The replacement value is resolved by calling the named method on the subject class of the query. The method is called like this: .Sp .Vb 1 \& $replacement = $subject_class\->$method( $boolexpr_or_object); .Ve .Sp During a query, the method is passed a BoolExpr; during a commit, the method is passed an object. It must return a string. .Sp The method name can also be wrapped in braces: .Sp .Vb 1 \& /&{resolve_prefix}.dir/people.txt .Ve .IP "*, ?" 4 Literal shell glob wildcards are honored when finding files, but their values are not used to supply values to objects. .SS "Environment Variables" .IX Subsection "Environment Variables" If the environment variable \f(CW$UR_DBI_MONITOR_SQL\fR is true, then the Filesystem data source will print information about the queries it runs. .SH "INHERITANCE" .IX Header "INHERITANCE" .Vb 1 \& UR::DataSource .Ve .SH "SEE ALSO" .IX Header "SEE ALSO" \&\s-1UR,\s0 UR::DataSource