sub
remove {
my
$self
=
shift
;
return
if
ref
(
$_
[0] ) && !
$_
->[0];
$self
->SUPER::remove(
@_
);
}
yamlHost yamlRoot scratchRoot sqliteRoot
dbHost dbPass dbPort dbUser
memcachedHosts
rcsBindir rcsDir
|
;
Rcs->arcext(
',v'
);
Rcs->bindir(rcsBindir);
Rcs->quiet(true);
our
(
$dbi
,
$memd
,
$transactionLevel
,
$errstr
);
if
( memcachedHosts
&&
ref
(memcachedHosts)
&&
ref
(memcachedHosts) eq
'ARRAY'
)
{
$memd
= Cache::Memcached::Fast->new( {
servers
=> memcachedHosts } );
}
sub
load {
my
$class
=
shift
;
my
$id
=
shift
;
return
$class
->__localLoad(
$class
->__primaryKeyClass->new(
$id
) );
}
sub
query {
my
$class
=
shift
;
my
$query
=
shift
;
return
$class
->__wrapWithReconnect(
sub
{
return
$class
->__query(
$query
) } );
}
sub
write
{
my
$class
=
shift
;
my
$query
=
shift
;
return
$class
->__wrapWithReconnect(
sub
{
return
$class
->__write(
$query
) } );
}
sub
search {
my
$class
=
shift
;
my
$query
=
shift
||
return
;
my
$index
=
$class
->__textIndex;
return
if
!
$index
;
if
( !
ref
(
$query
) ) {
my
$text
=
$query
;
$query
= {};
$class
->__indexedFields->
each
(
sub
{
my
$field
=
shift
;
$query
->{
lc
(
$field
) } =
$text
;
}
);
}
else
{
my
$lcQuery
= {};
for
my
$field
(
keys
%{
$query
} ) {
$lcQuery
->{
lc
(
$field
) } =
$query
->{field};
}
$query
=
$lcQuery
;
}
return
Devel::Ladybug::Hash->new(
$index
->search(
$query
) );
}
sub
selectScalar {
my
$class
=
shift
;
my
$query
=
shift
;
return
Devel::Ladybug::Scalar->new(
$class
->selectSingle(
$query
)->
shift
);
}
sub
selectBool {
my
$class
=
shift
;
my
$query
=
shift
;
return
$class
->selectScalar(
$query
) ? true : false;
}
sub
__selectBool {
warn
"Depracated usage; please use selectBool"
;
return
selectBool(
@_
);
}
sub
selectSingle {
my
$class
=
shift
;
my
$query
=
shift
;
my
$sth
=
$class
->query(
$query
);
my
$out
= Devel::Ladybug::Array->new();
while
(
my
@row
=
$sth
->fetchrow_array() ) {
$out
->
push
(
@row
);
}
$sth
->finish();
return
$out
;
}
sub
__selectSingle {
warn
"Depracated usage; please use selectSingle"
;
return
selectSingle(
@_
);
}
sub
selectMulti {
my
$class
=
shift
;
my
$query
=
shift
;
my
$sth
=
$class
->query(
$query
);
my
$results
= Devel::Ladybug::Array->new();
while
(
my
@row
=
$sth
->fetchrow_array() ) {
$results
->
push
(
@row
> 1 ? Devel::Ladybug::Array->new(
@row
) :
$row
[0] );
}
$sth
->finish();
return
$results
;
}
sub
__selectMulti {
warn
"Depracated usage; please use selectMulti"
;
return
selectMulti(
@_
);
}
sub
allIds {
my
$class
=
shift
;
if
(
$class
->__useFlatfile() && !
$class
->__useDbi() ) {
return
$class
->__fsIds();
}
my
$sth
=
$class
->__allIdsSth();
my
$ids
= Devel::Ladybug::Array->new();
while
(
my
(
$id
) =
$sth
->fetchrow_array() ) {
$ids
->
push
(
$id
);
}
$sth
->finish();
return
$ids
;
}
sub
count {
my
$class
=
shift
;
if
(
$class
->__useFlatfile && !
$class
->__useDbi ) {
return
$class
->allIds->count;
}
return
$class
->selectScalar(
$class
->__countStatement );
}
sub
stream {
my
$class
=
shift
;
my
$sub
=
shift
;
my
$stream
= Devel::Ladybug::Stream->new(
$class
);
return
$stream
;
}
sub
each
{
my
$class
=
shift
;
my
$sub
=
shift
;
return
Devel::Ladybug::Hash::
each
(
$class
,
$sub
)
if
$class
->class;
if
(
$class
->__useFlatfile && !
$class
->__useDbi ) {
return
$class
->allIds->
each
(
$sub
);
}
my
$stream
=
$class
->stream;
$stream
->setQuery(
$class
->__allIdsStatement );
return
$stream
->eachTuple(
$sub
);
}
sub
tuples {
my
$class
=
shift
;
if
(
$class
->__useFlatfile && !
$class
->__useDbi ) {
Devel::Ladybug::MethodIsAbstract->throw(
"tuples method not yet implemented for YAML backing stores"
);
}
return
$class
->selectMulti(
$class
->__tupleStatement );
}
sub
memberClass {
my
$class
=
shift
;
my
$key
=
shift
;
throw Devel::Ladybug::AssertFailed(
"$key is not a member of $class"
)
if
!
$class
->isAttributeAllowed(
$key
);
my
$type
=
$class
->asserts->{
$key
}->externalClass;
}
sub
doesIdExist {
my
$class
=
shift
;
my
$id
=
shift
;
if
(
$class
->__useDbi ) {
return
$class
->selectBool(
$class
->__doesIdExistStatement(
$class
->__primaryKeyClass->new(
$id
) ) );
}
else
{
my
$path
=
join
(
"/"
,
$class
->__basePath,
Devel::Ladybug::ID->new(
$id
)->as_string() );
return
-e
$path
;
}
}
sub
doesNameExist {
my
$class
=
shift
;
my
$name
=
shift
;
return
$class
->selectBool(
$class
->__doesNameExistStatement(
$name
) );
}
sub
pretty {
my
$class
=
shift
;
my
$key
=
shift
;
my
$pretty
=
$key
;
$pretty
=~ s/(.)([A-Z])/$1 $2/gxsm;
$pretty
=~ s/(\s|^)Id(\s|$)/${1}ID${2}/gxsmi;
$pretty
=~ s/(\s|^)Ids(\s|$)/${1}IDs${2}/gxsmi;
$pretty
=~ s/^Ctime$/Creation Time/gxsmi;
$pretty
=~ s/^Mtime$/Modified Time/gxsmi;
return
ucfirst
$pretty
;
}
sub
loadByName {
my
$class
=
shift
;
my
$name
=
shift
;
if
( !
$name
) {
my
$caller
=
caller
();
throw Devel::Ladybug::InvalidArgument(
"BUG (Check $caller): empty name sent to loadByName(\$name)"
);
}
my
$id
=
$class
->idForName(
$name
);
if
(
defined
$id
) {
return
$class
->load(
$id
);
}
else
{
my
$table
=
$class
->tableName();
my
$db
=
$class
->databaseName();
throw Devel::Ladybug::ObjectNotFound(
"Object name \"$name\" does not exist in table $db.$table"
);
}
}
sub
spawn {
my
$class
=
shift
;
my
$name
=
shift
;
my
$id
=
$class
->idForName(
$name
);
if
(
defined
$id
) {
return
$class
->load(
$id
);
}
else
{
my
$self
=
$class
->proto;
$self
->setName(
$name
);
my
$key
=
$class
->__primaryKey();
return
$self
;
}
}
sub
idForName {
my
$class
=
shift
;
my
$name
=
shift
;
return
$class
->selectSingle(
$class
->__idForNameStatement(
$name
) )->
shift
;
}
sub
nameForId {
my
$class
=
shift
;
my
$id
=
shift
;
return
$class
->selectSingle(
$class
->__nameForIdStatement(
$class
->__primaryKeyClass->new(
$id
) ) )
->
shift
;
}
sub
allNames {
my
$class
=
shift
;
if
( !
$class
->__useDbi() ) {
throw Devel::Ladybug::MethodIsAbstract(
"Sorry, allNames() requires a DBI backing store"
);
}
my
$sth
=
$class
->__allNamesSth();
my
$names
= Devel::Ladybug::Array->new();
while
(
my
(
$name
) =
$sth
->fetchrow_array() ) {
$names
->
push
(
$name
);
}
$sth
->finish();
return
$names
;
}
sub
quote {
my
$class
=
shift
;
my
$value
=
shift
;
return
$class
->__dbh()->quote(
$value
);
}
sub
databaseName {
my
$class
=
shift
;
my
$dbName
=
$class
->get(
"__databaseName"
);
if
( !
$dbName
) {
if
(
$class
=~ /Devel::Ladybug::/ ) {
$dbName
=
'ladybug'
;
}
else
{
$dbName
=
lc
(
$class
);
$dbName
=~ s/:.*//;
}
$class
->set(
"__databaseName"
,
$dbName
);
}
return
$dbName
;
}
sub
tableName {
my
$class
=
shift
;
my
$tableName
=
$class
->get(
"__tableName"
);
if
( !
$tableName
) {
$tableName
=
$class
;
if
(
$class
=~ /Devel::Ladybug::/ ) {
$tableName
=~ s/Devel::Ladybug:://;
}
else
{
$tableName
=~ s/.*?:://;
}
$tableName
=~ s/::/_/g;
$tableName
=
lc
(
$tableName
);
$class
->set(
"__tableName"
,
$tableName
);
}
return
$tableName
;
}
sub
loadYaml {
my
$class
=
shift
;
my
$yaml
=
shift
;
throw Devel::Ladybug::InvalidArgument(
"Empty YAML stream received"
)
if
!
$yaml
;
my
$hash
;
eval
{
$hash
= YAML::Syck::Load(
$yaml
) ||
die
$@; };
throw Devel::Ladybug::DataConversionFailed($@)
if
$@;
return
$class
->new(
$hash
);
}
sub
loadJson {
my
$class
=
shift
;
my
$json
=
shift
;
throw Devel::Ladybug::InvalidArgument(
"Empty YAML stream received"
)
if
!
$json
;
my
$hash
= JSON::Syck::Load(
$json
);
throw Devel::Ladybug::DataConversionFailed($@)
if
$@;
return
$class
->new(
$hash
);
}
sub
restore {
my
$class
=
shift
;
my
$id
=
shift
;
my
$version
=
shift
;
if
( !
$id
) {
Devel::Ladybug::InvalidArgument->throw(
"No ID received"
);
}
if
( !
$class
->__useRcs() ) {
Devel::Ladybug::RuntimeError->throw(
"$class instances do not have RCS history"
);
}
my
$self
=
$class
->proto;
$self
->setId(
$id
);
return
$self
->revert(
$version
);
}
sub
__useYaml {
my
$class
=
shift
;
warn
"__useYaml is depracated, please use __useFlatfile"
;
return
$class
->__useFlatfile;
}
sub
__useFlatfile {
my
$class
=
shift
;
my
$useFlatfile
=
$class
->get(
"__useFlatfile"
);
if
( !
defined
$useFlatfile
) {
my
%args
=
$class
->__autoArgs();
$useFlatfile
=
$args
{
"__useFlatfile"
};
if
(
$useFlatfile
&&
$useFlatfile
== Devel::Ladybug::StorageType::JSON ) {
$class
->__supportsJSON;
}
$class
->set(
"__useFlatfile"
,
$useFlatfile
);
}
return
$useFlatfile
;
}
sub
__useRcs {
my
$class
=
shift
;
if
( !
defined
$class
->get(
"__useRcs"
) ) {
$class
->set(
"__useRcs"
, false );
}
my
$use
=
$class
->get(
"__useRcs"
);
my
$backend
=
$class
->__useFlatfile;
if
(
$use
&& ( !
$backend
||
$backend
!= Devel::Ladybug::StorageType::YAML ) )
{
Devel::Ladybug::RuntimeError->throw(
"RCS requires a flatfile type of YAML"
);
}
return
$use
;
}
sub
__yamlHost {
my
$class
=
shift
;
my
$host
=
$class
->get(
"__yamlHost"
);
if
( !
$host
&& yamlHost ) {
$host
= yamlHost;
$class
->set(
"__yamlHost"
,
$host
);
}
return
$host
;
}
sub
__useMemcached {
my
$class
=
shift
;
if
( !
defined
$class
->get(
"__useMemcached"
) ) {
$class
->set(
"__useMemcached"
, 300 );
}
return
$class
->get(
"__useMemcached"
);
}
sub
__dbiType {
my
$class
=
shift
;
warn
"__dbiType is depracated, use __useDbi instead"
;
return
$class
->useDbi;
}
sub
__useDbi {
my
$class
=
shift
;
my
$type
=
$class
->get(
"__useDbi"
);
if
( !
defined
(
$type
) ) {
my
%args
=
$class
->__autoArgs();
$type
=
$args
{__useDbi};
$class
->set(
"__useDbi"
,
$type
);
}
return
$type
;
}
my
%createArgs
;
sub
__autoArgs {
my
$class
=
shift
;
return
%createArgs
if
%createArgs
;
$createArgs
{__useDbi} = false;
$createArgs
{__useFlatfile} = true;
if
(
$class
->__supportsSQLite() ) {
$createArgs
{__useFlatfile} = false;
$createArgs
{__useDbi} = Devel::Ladybug::StorageType::SQLite;
}
if
(
$class
->__supportsPostgreSQL() ) {
$createArgs
{__useFlatfile} = false;
$createArgs
{__useDbi} = Devel::Ladybug::StorageType::PostgreSQL;
}
if
(
$class
->__supportsMySQL() ) {
$createArgs
{__useFlatfile} = false;
$createArgs
{__useDbi} = Devel::Ladybug::StorageType::MySQL;
}
return
%createArgs
;
}
my
$alreadyWarnedForLongDoubles
;
sub
__supportsJSON {
my
$class
=
shift
;
my
$worked
;
eval
{
$worked
++;
};
return
$worked
;
}
sub
__supportsSQLite {
my
$class
=
shift
;
my
$worked
;
eval
{
$worked
++;
};
return
$worked
;
}
sub
__supportsMySQL {
my
$class
=
shift
;
my
$worked
;
eval
{
my
$dbname
=
$class
->databaseName;
my
$dsn
=
sprintf
(
'DBI:mysql:database=%s;host=%s;port=%s'
,
$dbname
,
$class
->__dbHost,
$class
->__dbPort || 3306 );
my
$dbh
=
DBI->
connect
(
$dsn
,
$class
->__dbUser,
$class
->__dbPass,
{
RaiseError
=> 1 } )
||
die
DBI->errstr;
my
$sth
=
$dbh
->prepare(
"show tables"
) ||
die
$dbh
->errstr;
$sth
->execute ||
die
$sth
->errstr;
$sth
->fetchall_arrayref() ||
die
$sth
->errstr;
$worked
++;
};
return
$worked
;
}
sub
__supportsPostgreSQL {
my
$class
=
shift
;
my
$worked
;
eval
{
my
$dbname
=
$class
->databaseName;
my
$dsn
=
sprintf
(
'DBI:Pg:database=%s;host=%s;port=%s'
,
$dbname
,
$class
->__dbHost,
$class
->__dbPort || 5432 );
my
$dbh
=
DBI->
connect
(
$dsn
,
$class
->__dbUser,
$class
->__dbPass,
{
RaiseError
=> 1 } )
||
die
DBI->errstr;
my
$sth
=
$dbh
->prepare(
"select count(*) from information_schema.tables"
)
||
die
$dbh
->errstr;
$sth
->execute ||
die
$sth
->errstr;
$sth
->fetchall_arrayref() ||
die
$sth
->errstr;
$worked
++;
};
return
$worked
;
}
sub
__basePath {
my
$class
=
shift
;
$class
=~ s/::/\//g;
return
join
(
'/'
, yamlRoot,
$class
);
}
sub
__baseRcsPath {
my
$class
=
shift
;
return
join
(
'/'
,
$class
->__basePath(), rcsDir );
}
sub
__primaryKey {
my
$class
=
shift
;
my
$key
=
$class
->get(
"__primaryKey"
);
if
( !
defined
$key
) {
$key
= Devel::Ladybug::Persistence::DefaultPrimaryKey;
$class
->set(
"__primaryKey"
,
$key
);
}
return
$key
;
}
sub
__primaryKeyClass {
my
$class
=
shift
;
return
$class
->asserts->{
$class
->__primaryKey }->objectClass();
}
sub
__localLoad {
my
$class
=
shift
;
my
$id
=
shift
;
if
(
$class
->__useDbi() ) {
return
$class
->__loadFromDatabase(
$id
);
}
elsif
(
$class
->__useFlatfile() ) {
return
$class
->__loadYamlFromId(
$id
);
}
else
{
throw Devel::Ladybug::MethodIsAbstract(
"Backing store not implemented for $class"
);
}
}
sub
__loadFromMemcached {
my
$class
=
shift
;
my
$id
=
shift
;
my
$cacheTTL
=
$class
->__useMemcached();
if
(
$memd
&&
$cacheTTL
) {
my
$cachedObj
=
$memd
->get(
$class
->__cacheKey(
$id
) );
if
(
$cachedObj
) {
return
$class
->new(
$cachedObj
);
}
}
return
;
}
sub
__loadFromDatabase {
my
$class
=
shift
;
my
$id
=
shift
;
my
$cachedObj
=
$class
->__loadFromMemcached(
$id
);
return
$cachedObj
if
$cachedObj
;
my
$query
=
$class
->__selectRowStatement(
$id
);
my
$self
=
$class
->__loadFromQuery(
$query
);
if
( !
$self
|| !
$self
->
exists
() ) {
my
$table
=
$class
->tableName();
my
$db
=
$class
->databaseName();
throw Devel::Ladybug::ObjectNotFound(
"Object id \"$id\" does not exist in table $db.$table"
);
}
return
$self
;
}
sub
__marshal {
my
$class
=
shift
;
my
$self
=
shift
;
bless
$self
,
$class
;
if
( !
$self
) {
my
$caller
=
caller
();
throw Devel::Ladybug::InvalidArgument(
"BUG: (Check $caller): "
.
"$class->__marshal() received an undefined or false arg"
);
}
my
$refType
=
ref
(
$self
);
if
( !
$refType
) {
my
$caller
=
caller
();
throw Devel::Ladybug::InvalidArgument(
"BUG: (Check $caller): "
.
"$class->__marshal() received a non-reference arg ($self)"
);
}
if
( !UNIVERSAL::isa(
$self
,
'HASH'
) ) {
my
$caller
=
caller
();
throw Devel::Ladybug::InvalidArgument(
"BUG: (Check $caller): "
.
"$class->__marshal() received a non-HASH arg ($refType)"
);
}
my
$asserts
=
$class
->asserts();
$asserts
->
each
(
sub
{
my
$key
=
$_
;
my
$type
=
$asserts
->{
$key
};
if
(
$type
) {
if
(
$type
->objectClass()->isa(
'Devel::Ladybug::Array'
) ) {
my
$elementClass
=
$class
->__elementClass(
$key
);
my
$array
=
$type
->objectClass()->new();
$array
->clear();
my
$sth
=
$elementClass
->query(
sprintf
q|
SELECT %s FROM %s
WHERE %s = %s
ORDER BY %s + 0
|
,
$elementClass
->__selectColumnNames()->
join
(
", "
),
$elementClass
->tableName(),
$class
->__elementParentKey(),
$elementClass
->quote(
$self
->{
$class
->__primaryKey() } ),
$class
->__elementIndexKey(),
);
while
(
my
$element
=
$sth
->fetchrow_hashref() ) {
if
(
$element
->{elementValue}
&&
$element
->{elementValue} =~ /^---\s/ )
{
$element
->{elementValue} =
YAML::Syck::Load(
$element
->{elementValue} );
}
$element
=
$elementClass
->__marshal(
$element
);
$array
->
push
(
$element
->elementValue() );
}
$sth
->finish();
$self
->{
$key
} =
$array
;
}
elsif
(
$type
->objectClass()->isa(
'Devel::Ladybug::Hash'
) ) {
my
$elementClass
=
$class
->__elementClass(
$key
);
my
$hash
=
$type
->objectClass()->new();
my
$sth
=
$elementClass
->query(
sprintf
q|
SELECT %s FROM %s WHERE %s = %s
|
,
$elementClass
->__selectColumnNames()->
join
(
", "
),
$elementClass
->tableName(),
$class
->__elementParentKey(),
$elementClass
->quote(
$self
->{
$class
->__primaryKey() } )
);
while
(
my
$element
=
$sth
->fetchrow_hashref() ) {
if
(
$element
->{elementValue}
&&
$element
->{elementValue} =~ /^---\s/ )
{
$element
->{elementValue} =
YAML::Syck::Load(
$element
->{elementValue} );
}
$element
=
$elementClass
->__marshal(
$element
);
$hash
->{
$element
->name() } =
$element
->elementValue();
}
$sth
->finish();
$self
->{
$key
} =
$hash
;
}
}
}
);
return
$class
->new(
$self
);
}
sub
__allIdsSth {
my
$class
=
shift
;
throw Devel::Ladybug::MethodIsAbstract(
"$class->__allIdsSth() requires DBI in class"
)
if
!
$class
->__useDbi();
return
$class
->query(
$class
->__allIdsStatement() );
}
sub
__allNamesSth {
my
$class
=
shift
;
throw Devel::Ladybug::MethodIsAbstract(
"$class->__allNamesSth() requires DBI in class"
)
if
!
$class
->__useDbi();
return
$class
->query(
$class
->__allNamesStatement() );
}
sub
__cacheKey {
my
$class
=
shift
;
my
$id
=
shift
;
if
( !
defined
(
$id
) ) {
my
$caller
=
caller
();
throw Devel::Ladybug::InvalidArgument(
"BUG (Check $caller): $class->__cacheKey(\$id) received undef for \$id"
);
}
elsif
(
$class
->asserts->{
$class
->__primaryKey }->isa(
"Devel::Ladybug::ID"
) )
{
return
$id
;
}
else
{
my
$key
=
join
(
':'
,
$class
,
$id
);
return
$key
;
}
}
sub
__write {
my
$class
=
shift
;
my
$query
=
shift
;
my
$rows
;
eval
{
$rows
=
$class
->__dbh()->
do
(
$query
) };
if
($@) {
my
$err
=
$class
->__dbh()->errstr() || $@;
Devel::Ladybug::DBQueryFailed->throw(
join
(
': '
,
$class
,
$err
,
$query
) );
}
return
$rows
;
}
sub
__query {
my
$class
=
shift
;
my
$query
=
shift
;
my
$dbh
=
$class
->__dbh()
|| throw Devel::Ladybug::DBConnectFailed
"Unable to connect to database"
;
my
$sth
;
eval
{
$sth
=
$dbh
->prepare(
$query
) ||
die
$@; };
if
($@) {
throw Devel::Ladybug::DBQueryFailed(
$dbh
->errstr || $@ );
}
eval
{
$sth
->execute() ||
die
$@; };
if
($@) {
throw Devel::Ladybug::DBQueryFailed(
$sth
->errstr || $@ );
}
return
$sth
;
}
sub
__loadFromQuery {
my
$class
=
shift
;
my
$query
=
shift
;
my
$sth
=
$class
->query(
$query
)
|| throw Devel::Ladybug::DBQueryFailed($@);
my
$self
;
while
(
my
$row
=
$sth
->fetchrow_hashref() ) {
$self
=
$row
;
last
;
}
$sth
->finish();
return
(
$self
&&
ref
(
$self
) )
?
$class
->__marshal(
$self
)
:
undef
;
}
sub
__dbhKey {
my
$class
=
shift
;
return
join
(
"_"
,
$class
->databaseName,
$class
->__useDbi );
}
sub
__dbh {
my
$class
=
shift
;
my
$useDbi
=
$class
->__useDbi;
if
( !
$useDbi
) {
Devel::Ladybug::RuntimeError->throw(
"BUG: $class was asked for its DBH, but it does not use DBI."
);
}
my
$dbName
=
$class
->databaseName();
my
$dbKey
=
$class
->__dbhKey();
$dbi
||= Devel::Ladybug::Hash->new();
$dbi
->{
$dbKey
} ||= Devel::Ladybug::Hash->new();
if
( !
$dbi
->{
$dbKey
}->{$$} ) {
if
(
$useDbi
== Devel::Ladybug::StorageType::MySQL ) {
my
%creds
= (
database
=>
$dbName
,
host
=> dbHost,
pass
=> dbPass,
port
=> dbPort || 3306,
user
=> dbUser
);
$dbi
->{
$dbKey
}->{$$} =
Devel::Ladybug::Persistence::MySQL::
connect
(
%creds
);
}
elsif
(
$useDbi
== Devel::Ladybug::StorageType::SQLite ) {
my
%creds
= (
database
=>
join
(
'/'
, sqliteRoot,
$dbName
) );
$dbi
->{
$dbKey
}->{$$} =
Devel::Ladybug::Persistence::SQLite::
connect
(
%creds
);
}
elsif
(
$useDbi
== Devel::Ladybug::StorageType::PostgreSQL ) {
my
%creds
= (
database
=>
$dbName
,
host
=> dbHost,
pass
=> dbPass,
port
=> dbPort || 5432,
user
=> dbUser
);
$dbi
->{
$dbKey
}->{$$} =
Devel::Ladybug::Persistence::PostgreSQL::
connect
(
%creds
);
}
else
{
throw Devel::Ladybug::InvalidArgument(
sprintf
(
'Unknown DBI Type %s returned by class %s'
,
$useDbi
,
$class
)
);
}
}
my
$err
=
$dbi
->{
$dbKey
}->{$$}->{_lastErrorStr};
if
(
$err
) {
throw Devel::Ladybug::DBConnectFailed(
$err
);
}
return
$dbi
->{
$dbKey
}->{$$};
}
sub
__loadYamlFromId {
my
$class
=
shift
;
my
$id
=
shift
;
if
( UNIVERSAL::can(
$id
,
"as_string"
) ) {
$id
=
$id
->as_string();
}
my
$joinStr
= (
$class
->__basePath() =~ /\/$/ ) ?
''
:
'/'
;
my
$self
=
$class
->__loadYamlFromPath(
join
(
$joinStr
,
$class
->__basePath(),
$id
) );
return
$self
;
}
sub
__loadYamlFromPath {
my
$class
=
shift
;
my
$path
=
shift
;
if
( -e
$path
) {
my
$yaml
=
$class
->__getSourceByPath(
$path
);
my
$backend
=
$class
->__useFlatfile;
if
(
$backend
== Devel::Ladybug::StorageType::JSON ) {
return
$class
->loadJson(
$yaml
);
}
else
{
return
$class
->loadYaml(
$yaml
);
}
}
else
{
throw Devel::Ladybug::FileAccessError(
"Path $path does not exist on disk"
);
}
}
sub
__getSourceByPath {
my
$class
=
shift
;
my
$path
=
shift
;
return
undef
unless
-e
$path
;
my
$lines
= Devel::Ladybug::Array->new();
my
$file
= IO::File->new(
$path
,
'r'
);
while
(<
$file
>) {
$lines
->
push
(
$_
) }
return
$lines
->
join
(
""
);
}
sub
__fsIds {
my
$class
=
shift
;
my
$basePath
=
$class
->__basePath();
my
$ids
= Devel::Ladybug::Array->new();
return
$ids
unless
-d
$basePath
;
find(
sub
{
my
$id
=
$File::Find::name
;
my
$shortId
=
$id
;
$shortId
=~ s/\Q
$basePath
\E\///;
if
( -f
$id
&& !(
$id
=~ /,v$/ )
&& !(
$id
=~ /\~$/ ) )
{
$ids
->
push
(
$shortId
);
}
},
$basePath
);
return
$ids
;
}
sub
__checkYamlHost {
my
$class
=
shift
;
if
(
$class
->__useFlatfile() ) {
my
$yamlHost
=
$class
->__yamlHost();
if
(
$yamlHost
) {
my
$thisHost
= hostname();
if
(
$thisHost
ne
$yamlHost
) {
Devel::Ladybug::WrongHost->throw(
"YAML archives must be saved on host $yamlHost, not $thisHost"
);
}
}
}
}
my
$alreadyWarnedForMemcached
;
my
$alreadyWarnedForRcs
;
sub
__init {
my
$class
=
shift
;
my
$lds
=
$Config
{uselongdouble};
if
(
$lds
&&
$lds
eq
'define'
) {
if
( !
$alreadyWarnedForLongDoubles
) {
warn
"Perl's default float size is 'long double'; Ladybug's dependencies may not do the right thing-- proceed with caution!"
;
$alreadyWarnedForLongDoubles
++;
}
}
if
(
$class
->__useRcs ) {
if
( $^O eq
'openbsd'
) {
$class
->set(
"__useRcs"
, false );
if
( !
$alreadyWarnedForRcs
) {
warn
"Disabling RCS support (OpenRCS not currently supported)\n"
;
$alreadyWarnedForRcs
++;
}
}
else
{
my
$ci
=
join
(
'/'
, rcsBindir,
'ci'
);
my
$co
=
join
(
'/'
, rcsBindir,
'co'
);
if
( !-e
$ci
|| !-e
$co
) {
$class
->set(
"__useRcs"
, false );
if
( !
$alreadyWarnedForRcs
) {
warn
"Disabling RCS support (\"ci\"/\"co\" not found)\n"
;
$alreadyWarnedForRcs
++;
}
}
}
}
if
(
memcachedHosts
&& !
$alreadyWarnedForMemcached
&&
$class
->__useMemcached
&& ( !
$memd
|| !%{
$memd
->server_versions } ) )
{
warn
"Disabling memcached support (no servers found)"
;
$alreadyWarnedForMemcached
++;
}
if
( !
$class
->__useDbi ) {
return
;
}
$class
->__INIT();
my
$asserts
=
$class
->asserts;
my
$indexed
= Devel::Ladybug::Array->new;
$asserts
->
each
(
sub
{
my
$key
=
shift
;
my
$assert
=
$asserts
->{
$key
};
if
(
$assert
->indexed ) {
$indexed
->
push
(
$key
);
}
if
(
$assert
->isa(
"Devel::Ladybug::Type::Array"
)
||
$assert
->isa(
"Devel::Ladybug::Type::Hash"
) )
{
$class
->__elementClass(
$key
);
}
}
);
if
(
$indexed
->count > 0 ) {
my
$index
= Devel::Ladybug::TextIndex->new(
{
index_dbh
=>
$class
->__dbh,
collection
=>
join
(
"_"
,
$class
->tableName,
"idx"
),
doc_fields
=>
$indexed
->
each
(
sub
{
my
$field
=
shift
;
Devel::Ladybug::Array::yield(
lc
(
$field
) );
}
),
}
);
$class
->set(
"__textIndex"
,
$index
);
$class
->set(
"__indexedFields"
,
$indexed
);
}
return
true;
}
sub
__textIndex {
my
$class
=
shift
;
return
$class
->get(
"__textIndex"
);
}
sub
__dbUser {
my
$class
=
shift
;
if
(
scalar
(
@_
) ) {
my
$newValue
=
shift
;
$class
->set(
"__dbUser"
,
$newValue
);
}
return
$class
->get(
"__dbUser"
) || dbUser;
}
sub
__dbPass {
my
$class
=
shift
;
if
(
scalar
(
@_
) ) {
my
$newValue
=
shift
;
$class
->set(
"__dbPass"
,
$newValue
);
}
return
$class
->get(
"__dbPass"
) || dbPass;
}
sub
__dbHost {
my
$class
=
shift
;
if
(
scalar
(
@_
) ) {
my
$newValue
=
shift
;
$class
->set(
"__dbHost"
,
$newValue
);
}
return
$class
->get(
"__dbHost"
) || dbHost;
}
sub
__dbPort {
my
$class
=
shift
;
if
(
scalar
(
@_
) ) {
my
$newValue
=
shift
;
$class
->set(
"__dbPort"
,
$newValue
);
}
return
$class
->get(
"__dbPort"
) || dbPort;
}
sub
__elementClass {
my
$class
=
shift
;
my
$key
=
shift
;
return
if
!
$class
->__useDbi;
my
$elementClasses
=
$class
->get(
"__elementClasses"
);
if
( !
$elementClasses
) {
$elementClasses
= Devel::Ladybug::Hash->new();
$class
->set(
"__elementClasses"
,
$elementClasses
);
}
if
(
$elementClasses
->{
$key
} ) {
return
$elementClasses
->{
$key
};
}
my
$asserts
=
$class
->asserts();
my
$type
=
$asserts
->{
$key
};
my
$elementClass
;
if
(
$type
) {
if
(
$type
->objectClass()->isa(
'Devel::Ladybug::Array'
) ) {
$elementClass
=
join
(
"::"
,
$class
,
$key
);
create
$elementClass
=> {
__useDbi
=>
$class
->__useDbi,
name
=> Devel::Ladybug::Int->assert(
Devel::Ladybug::Type::subtype(
unique
=>
"parentId"
)
),
parentId
=>
$class
->assert,
elementValue
=>
$type
->memberType,
};
}
elsif
(
$type
->objectClass()->isa(
'Devel::Ladybug::Hash'
) ) {
$elementClass
=
join
(
"::"
,
$class
,
$key
);
my
$memberClass
=
$class
->memberClass(
$key
);
create
$elementClass
=> {
__useDbi
=>
$class
->__useDbi,
name
=> Devel::Ladybug::Str->assert(
Devel::Ladybug::Type::subtype(
unique
=>
"parentId"
)
),
parentId
=>
$class
->assert,
elementValue
=> Devel::Ladybug::Str->assert,
};
}
}
$elementClasses
->{
$key
} =
$elementClass
;
return
$elementClass
;
}
sub
save {
my
$self
=
shift
;
my
$comment
=
shift
;
my
$class
=
$self
->class();
$class
->new(
$self
);
$self
->presave();
return
$self
->_localSave(
$comment
);
}
sub
presave {
my
$self
=
shift
;
return
true;
}
sub
remove {
my
$self
=
shift
;
my
$reason
=
shift
;
my
$class
=
$self
->class();
$class
->__checkYamlHost();
my
$asserts
=
$class
->asserts;
if
(
$class
->__useDbi() ) {
my
$began
=
$class
->__beginTransaction();
if
( !
$began
) {
throw Devel::Ladybug::TransactionFailed($@);
}
for
(
keys
%{
$asserts
} ) {
my
$key
=
$_
;
my
$type
=
$asserts
->{
$_
};
next
if
!
$type
->objectClass->isa(
"Devel::Ladybug::Array"
)
&& !
$type
->objectClass->isa(
"Devel::Ladybug::Hash"
);
my
$elementClass
=
$class
->__elementClass(
$key
);
next
if
!
$elementClass
;
$elementClass
->
write
(
sprintf
'DELETE FROM %s WHERE %s = %s'
,
$elementClass
->tableName,
$class
->__elementParentKey,
$class
->quote(
$self
->key )
);
}
$class
->
write
(
$self
->_deleteRowStatement() );
my
$committed
=
$class
->__commitTransaction();
if
( !
$committed
) {
throw Devel::Ladybug::TransactionFailed(
"COMMIT failed on remove"
);
}
}
$self
->_removeFromMemcached;
my
$index
=
$class
->__textIndex;
if
(
$index
) {
$self
->_removeFromTextIndex(
$index
);
}
if
(
$class
->__useFlatfile() ) {
$self
->_fsDelete();
}
return
true;
}
sub
revert {
my
$self
=
shift
;
my
$version
=
shift
;
my
$class
=
$self
->class();
if
( !
$class
->__useRcs() ) {
throw Devel::Ladybug::RuntimeError(
"Can't revert $class instances"
);
}
my
$rcs
=
$self
->_rcs();
if
(
$version
) {
$rcs
->co(
"-r$version"
,
$self
->_path() );
}
else
{
$rcs
->co(
$self
->_path() );
}
%{
$self
} = %{
$class
->__loadYamlFromId(
$self
->id() ) };
return
$self
;
}
sub
exists
{
my
$self
=
shift
;
return
false
if
!
defined
(
$self
->{id} );
my
$class
=
$self
->class;
return
$class
->__useDbi
?
$self
->class->doesIdExist(
$self
->id )
: -e
$self
->_path;
}
sub
key {
my
$self
=
shift
;
my
$class
=
$self
->class();
return
$self
->get(
$class
->__primaryKey() );
}
sub
setIdsFromNames {
my
$self
=
shift
;
my
$attr
=
shift
;
my
@names
=
@_
;
my
$class
=
$self
->class;
my
$asserts
=
$class
->asserts;
my
$type
=
$asserts
->{
$attr
}
|| Devel::Ladybug::InvalidArgument->throw(
"$attr is not an attribute of $class"
);
if
(
$type
->isa(
"Devel::Ladybug::Type::ExtID"
) ) {
Devel::Ladybug::InvalidArgument->throw(
"Too many names received"
)
if
@names
> 1;
}
elsif
(
$type
->isa(
"Devel::Ladybug::Type::Array"
)
&&
$type
->memberType->isa(
"Devel::Ladybug::Type::ExtID"
) )
{
}
else
{
Devel::Ladybug::InvalidArgument->throw(
"$attr does not represent an ExtID"
);
}
my
$names
= Devel::Ladybug::Array->new(
@names
);
my
$extClass
=
$type
->externalClass;
my
$newIds
=
$names
->
each
(
sub
{
my
$obj
=
$extClass
->spawn(
$_
);
if
( !
$obj
->
exists
) {
$obj
->save;
}
Devel::Ladybug::Array::yield(
$obj
->id );
}
);
my
$currIds
=
$self
->get(
$attr
);
if
( !
$currIds
) {
$currIds
= Devel::Ladybug::Array->new;
}
elsif
( !
$currIds
->isa(
"Devel::Ladybug::Array"
) ) {
$currIds
= Devel::Ladybug::Array->new(
$currIds
);
}
if
(
$type
->isa(
"Devel::Ladybug::Type::ExtID"
) ) {
$self
->set(
$attr
,
$newIds
->
shift
);
}
else
{
$self
->set(
$attr
,
$newIds
);
}
return
true;
}
sub
revisions {
my
$self
=
shift
;
return
$self
->_rcs()->revisions();
}
sub
revisionInfo {
my
$self
=
shift
;
my
$rcs
=
$self
->_rcs();
my
$loghead
;
my
$revisionInfo
= Devel::Ladybug::Hash->new();
for
my
$line
(
$rcs
->rlog() ) {
next
if
$line
=~ /----------/;
last
if
$line
=~ /==========/;
if
(
$line
=~ /revision (\d+\.\d+)/ ) {
$loghead
= $1;
next
;
}
next
unless
$loghead
;
$revisionInfo
->{
$loghead
} =
''
unless
$revisionInfo
->{
$loghead
};
$revisionInfo
->{
$loghead
} .=
$line
;
}
return
$revisionInfo
;
}
sub
head {
my
$self
=
shift
;
return
$self
->_rcs()->head();
}
sub
_newId {
my
$self
=
shift
;
my
$class
=
$self
->class;
my
$assert
=
$class
->asserts->{
$class
->__primaryKey };
return
$assert
->objectClass->new();
}
sub
_localSave {
my
$self
=
shift
;
my
$comment
=
shift
;
my
$alreadyInTransaction
=
shift
;
my
$class
=
$self
->class();
my
$now
=
time
();
my
$orig_id
=
$self
->key();
my
$orig_ctime
=
$self
->{ctime};
my
$orig_mtime
=
$self
->{mtime};
my
$idKey
=
$class
->__primaryKey();
if
( !
defined
(
$self
->{
$idKey
} ) ) {
$self
->{
$idKey
} =
$self
->_newId();
}
if
( !
defined
(
$self
->{ctime} ) ||
$self
->{ctime} == 0 ) {
$self
->setCtime(
$now
);
}
$self
->setMtime(
$now
);
my
$useDbi
=
$class
->__useDbi();
if
(
$useDbi
&& !
$alreadyInTransaction
) {
my
$began
=
$class
->__beginTransaction();
if
( !
$began
) {
throw Devel::Ladybug::TransactionFailed($@);
}
}
my
$saved
;
try
{
$saved
=
$self
->_localSaveInsideTransaction(
$comment
);
$self
->_saveToMemcached;
}
catch
Error
with
{
$Devel::Ladybug::Persistence::errstr
=
shift
||
"No message"
;
undef
$saved
;
};
if
(
$saved
) {
if
(
$useDbi
&& !
$alreadyInTransaction
) {
my
$committed
=
$class
->__commitTransaction();
if
( !
$committed
) {
throw Devel::Ladybug::TransactionFailed(
"Could not COMMIT! Check DB and compare history for "
.
"$idKey $self->{$idKey} in class $class"
);
}
}
}
else
{
$self
->{
$idKey
} =
$orig_id
;
$self
->{ctime} =
$orig_ctime
;
$self
->{mtime} =
$orig_mtime
;
my
$details
=
sprintf
'[class: %s] [id: %s] [name: %s]'
,
$self
->class,
$orig_id
||
"No ID"
,
$self
->name ||
"No Name"
;
if
(
$useDbi
&& !
$alreadyInTransaction
) {
my
$rolled
=
$class
->__rollbackTransaction();
if
( !
$rolled
) {
my
$quotedID
=
$class
->quote(
$self
->{
$idKey
} );
throw Devel::Ladybug::TransactionFailed(
"ROLLBACK FAILED - Check DB and compare history:\n "
.
"$details\n "
.
$Devel::Ladybug::Persistence::errstr
);
}
else
{
throw Devel::Ladybug::TransactionFailed(
"Transaction failed - $details\n "
.
$Devel::Ladybug::Persistence::errstr
);
}
}
else
{
throw Devel::Ladybug::TransactionFailed(
"Save failed - $details\n "
.
$Devel::Ladybug::Persistence::errstr
);
}
}
my
$index
=
$class
->__textIndex;
if
(
$index
) {
$self
->_saveToTextIndex(
$index
);
}
return
$saved
;
}
sub
_localSaveInsideTransaction {
my
$self
=
shift
;
my
$comment
=
shift
;
my
@caller
=
caller
();
my
$class
=
$self
->class();
$class
->__checkYamlHost();
my
$idKey
=
$class
->__primaryKey();
my
$useDbi
=
$class
->__useDbi();
my
$saved
;
if
(
$useDbi
) {
$saved
=
$self
->_updateRecord();
if
(
$saved
) {
my
$asserts
=
$class
->asserts();
for
(
keys
%{
$asserts
} ) {
my
$key
=
$_
;
my
$type
=
$asserts
->{
$_
};
next
if
!
$type
->objectClass->isa(
"Devel::Ladybug::Array"
)
&& !
$type
->objectClass->isa(
"Devel::Ladybug::Hash"
);
my
$elementClass
=
$class
->__elementClass(
$key
);
$elementClass
->
write
(
sprintf
'DELETE FROM %s WHERE %s = %s'
,
$elementClass
->tableName(),
$class
->__elementParentKey(),
$class
->quote(
$self
->key() )
);
next
if
!
defined
$self
->{
$key
};
if
(
$type
->objectClass->isa(
'Devel::Ladybug::Array'
) ) {
my
$i
= 0;
for
my
$value
( @{
$self
->{
$key
} } ) {
my
$element
=
$elementClass
->new(
name
=>
$i
,
parentId
=>
$self
->key(),
elementValue
=>
$value
,
);
$saved
=
$element
->_localSave(
$comment
, 1 );
return
if
!
$saved
;
$i
++;
}
}
elsif
(
$type
->objectClass->isa(
'Devel::Ladybug::Hash'
) ) {
for
my
$elementKey
(
keys
%{
$self
->{
$key
} } ) {
my
$value
=
$self
->{
$key
}->{
$elementKey
};
my
$element
=
$elementClass
->new(
name
=>
$elementKey
,
parentId
=>
$self
->key(),
elementValue
=>
$value
,
);
$saved
=
$element
->_localSave(
$comment
, 1 );
return
if
!
$saved
;
}
}
}
}
return
if
!
$saved
;
$self
->{
$idKey
} ||=
$saved
;
}
if
(
$class
->__useFlatfile() ) {
my
$path
=
$self
->_path();
my
$useRcs
=
$class
->__useRcs();
my
$rcs
;
if
(
$useRcs
) {
my
$rcsBase
=
$class
->__baseRcsPath();
if
( !-d
$rcsBase
) {
eval
{ mkpath(
$rcsBase
) };
if
($@) {
Devel::Ladybug::FileAccessError->($@);
}
}
$rcs
=
$self
->_rcs();
$self
->_checkout(
$rcs
,
$path
);
}
$self
->_fsSave();
if
(
$useRcs
) {
$self
->_checkin(
$rcs
,
$path
,
$comment
);
}
}
return
$self
->{
$idKey
};
}
sub
_saveToMemcached {
my
$self
=
shift
;
my
$class
=
$self
->class;
my
$cacheTTL
=
$class
->__useMemcached();
if
(
$memd
&&
$cacheTTL
) {
$self
->_removeFromMemcached;
my
$key
=
$class
->__cacheKey(
$self
->key() );
return
$memd
->set(
$key
,
$self
,
$cacheTTL
);
}
return
;
}
sub
_saveToTextIndex {
my
$self
=
shift
;
my
$index
=
shift
;
return
if
!
$self
->
exists
;
return
if
!
$index
;
my
$save
= {};
$self
->class->__indexedFields->
each
(
sub
{
my
$field
=
shift
;
$save
->{
lc
(
$field
) } =
$self
->{
$field
};
}
);
my
$key
=
$self
->key;
$self
->_removeFromTextIndex(
$index
);
$index
->add(
$key
=>
$save
);
}
sub
_removeFromTextIndex {
my
$self
=
shift
;
my
$index
=
shift
;
return
if
!
$self
->
exists
;
return
if
!
$index
;
return
$index
->remove(
$self
->key );
}
sub
_removeFromMemcached {
my
$self
=
shift
;
my
$class
=
$self
->class;
if
(
$memd
&&
$class
->__useMemcached() ) {
my
$key
=
$class
->__cacheKey(
$self
->key() );
$memd
->
delete
(
$key
);
}
}
sub
_updateRecord {
my
$self
=
shift
;
my
$class
=
$self
->class();
my
$return
=
$class
->
write
(
$self
->_updateRowStatement() );
if
(
$return
== 0 ) {
$return
=
$class
->
write
(
$self
->_insertRowStatement() );
my
$priKey
=
$class
->__primaryKey;
if
(
$class
->asserts->{
$priKey
}->isa(
"Devel::Ladybug::Type::Serial"
) ) {
my
$lastId
=
$class
->__dbh->last_insert_id(
undef
,
undef
,
$class
->tableName,
$priKey
);
$self
->set(
$priKey
,
$lastId
);
}
}
return
$return
;
}
our
$ForceInsertSQL
;
our
$ForceUpdateSQL
;
sub
_fsDelete {
my
$self
=
shift
;
unlink
$self
->_path();
return
true;
}
sub
_fsSave {
my
$self
=
shift
;
$self
->{
$self
->class()->__primaryKey() } ||=
$self
->_newId();
return
$self
->_saveToPath(
$self
->_path() );
}
sub
_path {
my
$self
=
shift
;
my
$key
=
$self
->key();
my
@caller
=
caller
();
Devel::Ladybug::PrimaryKeyMissing->throw(
"Self has no primary key set"
)
if
!
defined
$key
;
if
( UNIVERSAL::can(
$key
,
"as_string"
) ) {
$key
=
$key
->as_string();
}
return
join
(
'/'
,
$self
->class()->__basePath(),
$key
);
}
sub
_saveToPath {
my
$self
=
shift
;
my
$path
=
shift
;
my
$class
=
$self
->class();
my
$base
=
$path
;
$base
=~ s/[^\/]+$//;
if
( !-d
$base
) {
eval
{ mkpath(
$base
) };
if
($@) {
throw Devel::Ladybug::FileAccessError($@);
}
}
my
$id
=
$self
->key();
if
( UNIVERSAL::can(
$id
,
"as_string"
) ) {
$id
=
$id
->as_string();
}
my
$tempPath
=
sprintf
'%s/%s-%s'
, scratchRoot,
$id
,
Devel::Ladybug::Utility::randstr();
my
$tempBase
=
$tempPath
;
$tempBase
=~ s/[^\/]+$//;
if
( !-d
$tempBase
) {
eval
{ mkpath(
$tempBase
) };
if
($@) {
throw Devel::Ladybug::FileAccessError($@);
}
}
my
$backend
=
$class
->__useFlatfile;
my
$yaml
;
if
(
$backend
== Devel::Ladybug::StorageType::JSON ) {
$yaml
=
$self
->toJson();
}
else
{
$yaml
=
$self
->toYaml();
}
open
( TEMP,
"> $tempPath"
);
print
TEMP
$yaml
;
close
(TEMP);
chmod
'0644'
,
$path
;
move(
$tempPath
,
$path
);
return
true;
}
sub
_rcsPath {
my
$self
=
shift
;
my
$key
=
$self
->key();
return
false
unless
(
$key
);
my
$class
=
ref
(
$self
);
my
$joinStr
= (
$class
->__baseRcsPath() =~ /\/$/ ) ?
''
:
'/'
;
return
sprintf
(
'%s%s'
,
join
(
$joinStr
,
$class
->__baseRcsPath(),
$key
),
',v'
);
}
sub
_checkout {
my
$self
=
shift
;
my
$rcs
=
shift
;
my
$path
=
shift
;
return
if
!-e
$path
;
eval
{
$rcs
->co(
'-l'
);
};
if
($@) {
my
$error
=
"RCS Checkout failed with status $@; Check STDERR"
;
Devel::Ladybug::RCSError->throw(
$error
);
}
}
sub
_checkin {
my
$self
=
shift
;
my
$rcs
=
shift
;
my
$path
=
shift
;
my
$comment
=
shift
;
my
$user
=
$ENV
{REMOTE_USER} ||
$ENV
{USER} ||
'nobody'
;
$comment
||=
"No checkin comment provided by $user"
;
eval
{
$rcs
->ci(
'-t-Programmatic checkin from '
.
$self
->class(),
'-u'
,
'-wLadybug'
,
"-mEdited by user $user with comment: $comment"
);
};
if
($@) {
my
$error
=
"RCS Checkin failed with status $@; Check STDERR"
;
Devel::Ladybug::RCSError->throw(
$error
);
}
}
sub
_rcs {
my
$self
=
shift
;
my
$rcs
= Rcs->new();
$self
->_path() =~ /(.*)\/(.*)/;
my
$directory
= $1;
my
$filename
= $2;
$rcs
->file(
$filename
);
$rcs
->rcsdir(
join
(
"/"
,
$directory
, rcsDir ) );
$rcs
->workdir(
$directory
);
return
$rcs
;
}
true;