Skip to content

Instantly share code, notes, and snippets.

@csirac2
Created August 20, 2011 07:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save csirac2/1158799 to your computer and use it in GitHub Desktop.
Save csirac2/1158799 to your computer and use it in GitHub Desktop.
Foswiki::Addressable ?
# See bottom of attachment for default license and copyright information
package Foswiki::Contrib::MigrationScriptsContrib::Roles::FoswikiAddressable;
use Moose::Role;
use Assert;
use Foswiki::Func();
use Foswiki::Meta();
use Foswiki::Address();
has 'address' => (
is => 'ro',
isa => 'Foswiki::Address',
builder => '_build_address',
lazy => 1
);
sub _build_address {
my ($self) = @_;
return Foswiki::Address->new(
string => $self->location(),
existHints => 0
);
}
=begin TML
---++ ClassMethod get_data([$metaObj]) -> data
Get data from resource specified by the Foswiki::Address object.
* =$metaObj= - optional (may be undef), an Foswiki::Meta instance of the
resource (it *must* contain the resource identified by =$self->address()=).
* =data= - the data returned depends on the resource type:
* =attachment=: =$fh= (attachmenthandle to the attachment stream)
* =metatype=: =@members= (an array of hashrefs, as returned by
Foswiki::Meta::find())
* =metamember=: =$member= (a hashref, as returned by
Foswiki::Meta::get() )
* =metakey=: =$value= (a scalar value)
* =text=: =$text=
=cut
sub get_data {
my ( $self, $metaObj ) = @_;
ASSERT( !defined $metaObj || $metaObj->isa('Foswiki::Meta') ) if DEBUG;
return $self->_addressDo( 'GET', $metaObj );
}
=begin TML
---++ ClassMethod put_data($metaObj, data) -> $metaObj
Put data to the resource specified by the Foswiki::Address object.
If =$self->address()= is of type attachment, the file attachment is updated.
For all other types, this method does *not* update the resource. You must call
=->save()= on the returned Foswiki::Meta object to do that.
* =$metaObj= - may be undef, an Foswiki::Meta instance of the
resource (it *must* contain the resource identified by =$self->address()=). Useful
when you want to do multiple =put_data()= operations on the same $metaObj,
and would rather do only one =$metaObj->save()= call at the end.
* =data= - the input data depends on the resource type:
* =attachment=: =$input_fh=, =%opts= (filehandle to the attachment stream to
be saved, and =%opts= which are understood by Foswiki::Meta::attach() ).
* =metatype=: =@input_members= (an array of hashrefs, as used in
Foswiki::Meta::putAll())
* =metamember=: =$input_member= (a hashref, as used in
Foswiki::Meta::put())
* =metakey=: =$input_value= (a scalar value)
* =text=: =$input_text=
=cut
sub put_data {
my ( $self, $metaObj, @data ) = @_;
ASSERT(
(
!defined $metaObj || $metaObj->isa('Foswiki::Meta'),
( $metaObj || 'undef' ) . ' is a ' . ref($metaObj)
)
) if DEBUG;
return $self->_addressDo( 'PUT', $metaObj, @data );
}
sub _equivMeta {
my ( $self, $metaObj ) = @_;
ASSERT( $metaObj->isa('Foswiki::Meta') ) if DEBUG;
ASSERT( $self->address()->isa('Foswiki::Address') ) if DEBUG;
ASSERT( $self->address()->web() ) if DEBUG;
my $addrCTopic = $self->address()->topic();
my $addrCRev = $self->address()->rev();
my $addrCObj =
Foswiki::Address->new( webpath => $self->address()->webpath() );
ASSERT($metaObj) if DEBUG;
ASSERT( $metaObj->isa('Foswiki::Meta') ) if DEBUG;
ASSERT( $metaObj->web() ) if DEBUG;
my %metaAddr = ( web => $metaObj->web() );
my $metaTopic = $metaObj->topic();
my $metaRev = $metaObj->latestIsLoaded() ? $metaObj->getLoadedRev() : undef;
if ($metaTopic) {
$metaAddr{topic} = $metaTopic;
}
if ( defined $metaRev ) {
$metaAddr{rev} = $metaRev;
}
if ($addrCTopic) {
$addrCObj->topic($addrCTopic);
}
if ( defined $addrCRev ) {
$addrCObj->rev($addrCRev);
}
return $addrCObj->equiv( Foswiki::Address->new(%metaAddr) );
}
sub _addressDo {
my ( $self, $action, $metaObj, @data ) = @_;
ASSERT( $action eq 'PUT' || $action eq 'GET' ) if DEBUG;
ASSERT( !defined $metaObj || $metaObj->isa('Foswiki::Meta') ) if DEBUG;
ASSERT( $self->address()->web() ) if DEBUG;
my $method = '_handleAddr_' . $self->address()->type();
ASSERT( defined &{$method}, "sub '$method' exists" ) if DEBUG;
return $self->$method( $action, $metaObj, @data );
}
sub _getMetaObj {
my ( $self, $check, $metaObj ) = @_;
ASSERT( $check eq 'VIEW' || $check eq 'CHANGE' ) if DEBUG;
ASSERT( !defined $metaObj || $metaObj->isa('Foswiki::Meta') ) if DEBUG;
my $web = $self->address()->web();
my $topic = $self->address()->topic();
ASSERT( defined $web and length($web) ) if DEBUG;
my $rev = $self->address()->rev();
ASSERT( $check eq 'VIEW' or $check eq 'CHANGE' ) if DEBUG;
if ( $self->address()->isA('attachment') ) {
$rev = undef;
}
if ($metaObj) {
ASSERT( $metaObj->isa('Foswiki::Meta') ) if DEBUG;
ASSERT( $self->_equivMeta($metaObj) ) if DEBUG;
}
else {
($metaObj) = Foswiki::Func::readTopic( $web, $topic, $rev );
}
Foswiki::Func::checkAccessPermission( $check,
Foswiki::Func::getCanonicalUserID(),
undef, $topic, $web, $metaObj );
return $metaObj;
}
sub _handleAddr_attachment {
my ( $self, $action, $metaObj, $input_fh, %opts ) = @_;
ASSERT( $action eq 'PUT' || $action eq 'GET' ) if DEBUG;
ASSERT( !defined $metaObj || $metaObj->isa('Foswiki::Meta') ) if DEBUG;
my $fh;
my $retMetaObj;
ASSERT( $self->address()->isA('attachment') ) if DEBUG;
my $name = $self->address()->attachment();
ASSERT( $name and length($name) ) if DEBUG;
if ( $action eq 'GET' ) {
$metaObj = $self->_getMetaObj( 'VIEW', $metaObj );
$fh = $metaObj->openAttachment( $name, '<', %opts );
}
elsif ( $action eq 'PUT' ) {
$metaObj = $self->_getMetaObj( 'CHANGE', $metaObj );
$metaObj->attach( name => $name, stream => $input_fh, %opts );
$retMetaObj = 1;
}
else {
ASSERT( 0, "Action '$action' is valid" );
}
return $retMetaObj ? $metaObj : $fh;
}
sub _handleAddr_metatype {
my ( $self, $action, $metaObj, @input_metamembers ) = @_;
ASSERT( $action eq 'GET' || $action eq 'PUT' ) if DEBUG;
my @metamembers;
my $retMetaObj;
ASSERT( $self->address()->type() eq 'metatype' ) if DEBUG;
my @tompath = @{ $self->address()->tompath() };
ASSERT( scalar(@tompath) == 2 ) if DEBUG;
ASSERT( $tompath[0] eq 'META' ) if DEBUG;
my $typename = $tompath[1];
ASSERT( $typename and length($typename) ) if DEBUG;
if ( $action eq 'GET' ) {
$metaObj = $self->_getMetaObj( 'VIEW', $metaObj );
@metamembers = $metaObj->find($typename);
}
elsif ( $action eq 'PUT' ) {
$metaObj = $self->_getMetaObj( 'CHANGE', $metaObj );
$metaObj->putAll( $typename, @input_metamembers );
$retMetaObj = 1;
}
else {
ASSERT( 0, "Action '$action' is valid" );
}
return $retMetaObj ? $metaObj : @metamembers;
}
sub _metamemberGetNameKey {
my ( $self, $topicObj ) = @_;
ASSERT( $topicObj->isa('Foswiki::Meta') ) if DEBUG;
my @tompath = @{ $self->address()->tompath() };
ASSERT( scalar(@tompath) >= 3, 'tompath is length: ' . scalar(@tompath) )
if DEBUG;
ASSERT( $tompath[0] eq 'META' ) if DEBUG;
ASSERT( $tompath[1] ) if DEBUG;
my $typename = $tompath[1];
my $selector = $tompath[2];
ASSERT( defined $selector ) if DEBUG;
my $namekey;
if ( $selector =~ /^[0-9]+$/ ) {
my @members = $topicObj->find($typename);
ASSERT( exists $members[$selector]->{name} ) if DEBUG;
$namekey = $members[$selector]->{name};
}
else {
ASSERT( ref($selector) eq 'HASH' ) if DEBUG;
ASSERT( $selector->{name}, 'selector is numeric or has a name key' )
if DEBUG;
ASSERT(
scalar( keys( %{$selector} ) ) == 1,
'is not a compound selector'
) if DEBUG;
$namekey = $selector->{name};
}
return $namekey;
}
sub _handleAddr_metamember {
my ( $self, $action, $metaObj, $input_metamember ) = @_;
ASSERT( $action eq 'GET' || $action eq 'PUT' ) if DEBUG;
ASSERT( !defined $input_metamember || ref($input_metamember) eq 'HASH' )
if DEBUG;
my $metamember;
my $retMetaObj;
ASSERT( $self->address()->type() eq 'metamember' ) if DEBUG;
my @tompath = @{ $self->address()->tompath() };
ASSERT( scalar(@tompath) == 3 ) if DEBUG;
ASSERT( $tompath[0] eq 'META' ) if DEBUG;
my $typename = $tompath[1];
ASSERT( $typename and length($typename) ) if DEBUG;
my $selector = $tompath[2];
if ( $action eq 'GET' ) {
my $namekey;
$metaObj = $self->_getMetaObj( 'VIEW', $metaObj );
ASSERT( $metaObj->topic() ) if DEBUG;
$namekey = $self->_metamemberGetNameKey($metaObj);
$metamember = $metaObj->get( $typename, $namekey );
}
elsif ( $action eq 'PUT' ) {
my $namekey;
$metaObj = $self->_getMetaObj( 'CHANGE', $metaObj );
ASSERT( $metaObj->topic() ) if DEBUG;
$namekey = $self->_metamemberGetNameKey($metaObj);
if ( defined $namekey ) {
$metaObj->putKeyed( $typename, $input_metamember );
}
else {
$metaObj->put( $typename, $input_metamember );
}
$retMetaObj = 1;
}
else {
ASSERT( 0, "Action '$action' is valid" );
}
return $retMetaObj ? $metaObj : $metamember;
}
sub _handleAddr_metakey {
my ( $self, $action, $metaObj, $input_value ) = @_;
my $metakey;
my $retMetaObj;
ASSERT( $action eq 'GET' || $action eq 'PUT' ) if DEBUG;
ASSERT( !defined $metaObj || $metaObj->isa('Foswiki::Meta') ) if DEBUG;
ASSERT( !defined $input_value || !ref($input_value) ) if DEBUG;
ASSERT( $self->address()->type() eq 'metakey' ) if DEBUG;
ASSERT( scalar( @{ $self->address()->tompath() } ) == 4 ) if DEBUG;
my $typename = $self->address()->tompath()->[1];
ASSERT( $typename and length($typename) ) if DEBUG;
if ( $action eq 'GET' ) {
my $namekey;
my $member;
$metaObj = $self->_getMetaObj( 'VIEW', $metaObj );
ASSERT( $metaObj->topic() ) if DEBUG;
$namekey = $self->_metamemberGetNameKey($metaObj);
$member = $metaObj->get( $typename, $namekey );
if ( defined $member ) {
my $memberkey = $self->address()->tompath()->[3];
ASSERT( defined $memberkey && length($memberkey) ) if DEBUG;
ASSERT( ref($member) eq 'HASH' ) if DEBUG;
$metakey = $member->{$memberkey};
}
}
elsif ( $action eq 'PUT' ) {
my $namekey;
my $memberkey = $self->address()->tompath()->[3];
ASSERT( defined $memberkey && length($memberkey) ) if DEBUG;
$metaObj = $self->_getMetaObj( 'CHANGE', $metaObj );
ASSERT( $metaObj->topic() ) if DEBUG;
$namekey = $self->_metamemberGetNameKey($metaObj);
if ( defined $namekey ) {
my $member = $metaObj->get( $typename, $namekey );
if ( defined $member ) {
ASSERT( ref($member) eq 'HASH' ) if DEBUG;
$member->{$memberkey} = $input_value;
}
else {
$member = {
name => $namekey,
$memberkey => $input_value
};
}
$metaObj->putKeyed( $typename, $member );
}
else {
$metaObj->put( $typename, { $memberkey => $input_value } );
}
$retMetaObj = 1;
}
else {
ASSERT( 0, "Action '$action' is valid" );
}
return $retMetaObj ? $metaObj : $metakey;
}
sub _handleAddr_text {
my ( $self, $action, $metaObj, $input_text ) = @_;
ASSERT( $action eq 'GET' || $action eq 'PUT' ) if DEBUG;
ASSERT( !defined $metaObj || $metaObj->isa('Foswiki::Meta') ) if DEBUG;
my $text;
my $retMetaObj;
ASSERT( $self->address()->type() eq 'text' ) if DEBUG;
my @tompath = @{ $self->address()->tompath() };
ASSERT( scalar(@tompath) == 1 ) if DEBUG;
ASSERT( $tompath[0] eq 'text' ) if DEBUG;
if ( $action eq 'GET' ) {
$metaObj = $self->_getMetaObj( 'VIEW', $metaObj );
ASSERT( $metaObj->topic() ) if DEBUG;
$text = $metaObj->text();
}
elsif ( $action eq 'PUT' ) {
$metaObj = $self->_getMetaObj( 'CHANGE', $metaObj );
ASSERT( $metaObj->topic() ) if DEBUG;
$metaObj->text($input_text);
$retMetaObj = 1;
}
else {
ASSERT( 0, "Action '$action' is valid" );
}
return $retMetaObj ? $metaObj : $text;
}
1;
package Test::Consumes::FoswikiAddressable;
use Moose;
with 'Foswiki::Contrib::MigrationScriptsContrib::Roles::FoswikiAddressable';
1;
package Test::Foswiki::Contrib::MigrationScriptsContrib::Roles::FoswikiAddressable;
use strict;
use warnings;
use Test::More;
use Test::Class();
use FoswikiFnTestCase();
use base qw(Test::Class);
my $fwtest = FoswikiFnTestCase->new('FoswikiAddressable');
sub init {
my $data = 'some data';
$fwtest->createNewFoswikiSession('admin');
Foswiki::Func::createWeb( $fwtest->{test_web} );
$fwtest->{test_topicObject}->text('some text');
$fwtest->{test_topicObject}->putAll(
'FIELD',
{ name => 'Field1', value => 'Value1' },
{ name => 'Field2', value => 'Value2' }
);
$fwtest->{test_topicObject}->putAll(
'FOO',
{ name => 'Foo1', ness => 'Fooness1' },
{ name => 'Foo2', ness => 'Fooness2' }
);
$fwtest->{test_topicObject}->save();
open( my $fh, '<', \$data ) or die $!;
Foswiki::Func::saveAttachment(
$fwtest->{test_web}, $fwtest->{test_topic},
'Foo.bar', { stream => $fh }
);
close $fh or die $!;
return;
}
sub attachment : Tests(4) {
my $addressable = Test::Consumes::FoswikiAddressable->new(
address => Foswiki::Address->new(
string => "$fwtest->{test_web}.$fwtest->{test_topic}/Foo.bar"
)
);
cmp_ok(
"$fwtest->{test_web}.$fwtest->{test_topic}/Foo.bar",
'eq',
$addressable->address()->stringify(),
'address accessor'
);
my $dname =
"$Foswiki::cfg{PubDir}/$fwtest->{test_web}/$fwtest->{test_topic}";
ok( -d $dname, 'pub directory exists' );
my $attach_fh = $addressable->get_data();
cmp_ok( 'some data', 'eq', <$attach_fh>, 'attachment read ok' );
my $new_data = 'some new data';
open( my $fh, '<', \$new_data ) or die $!;
$addressable->put_data( undef, $fh );
close($fh);
$attach_fh = $addressable->get_data();
cmp_ok( <$attach_fh>, 'eq', 'some new data', 'attachment write ok' );
return;
}
sub topic_text : Tests(2) {
my $addressable = Test::Consumes::FoswikiAddressable->new(
address => Foswiki::Address->new(
string => "'$fwtest->{test_web}.$fwtest->{test_topic}'/text"
)
);
cmp_ok( $addressable->get_data(), 'eq', 'some text', 'topic text read ok' );
my ($topicObj) =
Foswiki::Func::readTopic( $fwtest->{test_web}, $fwtest->{test_topic} );
$addressable->put_data( $topicObj, 'some new topic text' );
$topicObj->save();
cmp_ok(
$addressable->get_data(),
'eq',
'some new topic text',
'topic text write ok'
);
$topicObj->finish();
return;
}
sub topic_meta : Tests(15) {
my $addressable = Test::Consumes::FoswikiAddressable->new(
address => Foswiki::Address->new(
string => "'$fwtest->{test_web}.$fwtest->{test_topic}'/Field1"
)
);
cmp_ok( $addressable->get_data(),
'eq', 'Value1', 'topic META:FIELD[name=...].value read ok' );
my ($topicObj) =
Foswiki::Func::readTopic( $fwtest->{test_web}, $fwtest->{test_topic} );
$addressable->put_data( $topicObj, 'New Value1' );
$topicObj->save();
cmp_ok( $addressable->get_data(),
'eq', 'New Value1', 'topic META:FIELD[name=...].value write ok' );
is_deeply(
$topicObj->get( 'FIELD', 'Field1' ),
{
name => 'Field1',
value => 'New Value1'
},
'topic META:FIELD[name=...].value write ok via Foswiki::Meta'
);
$addressable = Test::Consumes::FoswikiAddressable->new(
address => Foswiki::Address->new(
string =>
"'$fwtest->{test_web}.$fwtest->{test_topic}'/META:FOO[name='Foo2']"
)
);
my $expected_foo_member = {
name => 'Foo2',
ness => 'Fooness2'
};
is_deeply( $topicObj->get( 'FOO', 'Foo2' ),
$expected_foo_member,
'topic META:FOO[name=...] read ok via Foswiki::Meta' );
is_deeply( $addressable->get_data($topicObj),
$expected_foo_member, 'topic META:FOO[name=...] read ok' );
my $new_expected_foo_member = {
name => 'Foo2',
ness => 'New Fooness2'
};
$addressable->put_data( $topicObj, $new_expected_foo_member );
$topicObj->save();
is_deeply( $addressable->get_data(),
$new_expected_foo_member, 'topic META:FOO[name=...] write ok' );
is_deeply( $topicObj->get( 'FOO', 'Foo2' ),
$new_expected_foo_member,
'topic META:FOO[name=...] write ok validated with Foswiki::Meta' );
$addressable = Test::Consumes::FoswikiAddressable->new(
address => Foswiki::Address->new(
string => "'$fwtest->{test_web}.$fwtest->{test_topic}'/META:FOO"
)
);
my $expected_foo = [
{
name => 'Foo1',
ness => 'Fooness1'
},
{
name => 'Foo2',
ness => 'New Fooness2'
}
];
is_deeply( [ $topicObj->find('FOO') ],
$expected_foo, 'topic META:FOO read ok via Foswiki::Meta' );
is_deeply( [ $addressable->get_data($topicObj) ],
$expected_foo, 'topic META:FOO read ok' );
my $new_expected_foo = [
{
name => 'Foo3',
ness => 'The only foo3'
}
];
$addressable->put_data( $topicObj, @{$new_expected_foo} );
$topicObj->save();
is_deeply(
[ $addressable->get_data() ],
$new_expected_foo,
'topic META:FOO write ok'
);
is_deeply( [ $topicObj->find('FOO') ],
$new_expected_foo,
'topic META:FOO write ok validated with Foswiki::Meta' );
$topicObj->finish();
return;
}
init();
Test::Class->runtests();
1;
__END__
Foswiki - The Free and Open Source Wiki, http://foswiki.org/
Copyright (C) 2010-2012 Paul.W.Harvey@csiro.au, TRIN http://trin.org.au/ &
Centre for Australian National Biodiversity Research http://anbg.gov.au/cpbr
Copyright (C) 2008-2011 Foswiki Contributors. Foswiki Contributors
are listed in the AUTHORS file in the root of this distribution.
NOTE: Please extend that file, not this notice.
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 2
of the License, or (at your option) any later version. For
more details read LICENSE in the root of this distribution.
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.
As per the GPL, removal of this notice is prohibited.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment