ASP4x-Linker

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

Changelog for ASP4x::Linker:

2012-05-07      v1.002
  - DEPRECATED
  - ASP4 has been marked as deprecated, and so this module follows suit.

2012-03-16      v1.002
  - Disabled some failing tests temporarily until we can fix them.

2012-02-03      v1.001
  - Added chained widget-attribute-setters.
    $widget->set(foo => 'bar', ...)->uri();
    $widget->set(%args)->set(%more_args)->uri();
  - Thanks to eric.hayes++ for the suggestion.

2011-05-01      v1.000
  - $widget->set( non_existent_attribute => "value" ) will result in an exception.
  - Simplified the documentation for ASP4x::Linker - the simplified style of
    $linker->widget(...)->get/set(...) interface is very much preferred.
  - API has otherwise remained unchanged for over a year.  Time to go v1.000!!!

2010-05-13      v0.003
  - Now, $linker->uri({foo => 'bar'}) returns a uri with foo=bar in there someplace.
  - Upgrade recommended.

2010-04-01      v.0.002
  - Added vars() method.
  - Updated docs.
  - Removed AUTOLOAD behavior from Widget and Linker.
  - Added $widget->get( $attr ) and $widget->set( $attr => $value )
  - Added $widget->on_change( $attr => sub { ... } )
  - Added several tests.

2010-04-01      v0.001
  - Initial release.
  - No joke!

inc/Module/Install.pm  view on Meta::CPAN

#line 1
package Module::Install;

# For any maintainers:
# The load order for Module::Install is a bit magic.
# It goes something like this...
#
# IF ( host has Module::Install installed, creating author mode ) {
#     1. Makefile.PL calls "use inc::Module::Install"
#     2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install
#     3. The installed version of inc::Module::Install loads
#     4. inc::Module::Install calls "require Module::Install"
#     5. The ./inc/ version of Module::Install loads
# } ELSE {
#     1. Makefile.PL calls "use inc::Module::Install"
#     2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install
#     3. The ./inc/ version of Module::Install loads
# }

BEGIN {
	require 5.004;
}
use strict 'vars';

use vars qw{$VERSION};
BEGIN {

inc/Module/Install.pm  view on Meta::CPAN

	*inc::Module::Install::VERSION = *VERSION;
	@inc::Module::Install::ISA     = __PACKAGE__;

}





# Whether or not inc::Module::Install is actually loaded, the
# $INC{inc/Module/Install.pm} is what will still get set as long as
# the caller loaded module this in the documented manner.
# If not set, the caller may NOT have loaded the bundled version, and thus
# they may not have a MI version that works with the Makefile.PL. This would
# result in false errors or unexpected behaviour. And we don't want that.
my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm';
unless ( $INC{$file} ) { die <<"END_DIE" }

Please invoke ${\__PACKAGE__} with:

	use inc::${\__PACKAGE__};

not:

inc/Module/Install/Metadata.pm  view on Meta::CPAN

	}

	return 1;
}

sub all_from {
	my ( $self, $file ) = @_;

	unless ( defined($file) ) {
		my $name = $self->name or die(
			"all_from called with no args without setting name() first"
		);
		$file = join('/', 'lib', split(/-/, $name)) . '.pm';
		$file =~ s{.*/}{} unless -e $file;
		unless ( -e $file ) {
			die("all_from cannot find $file from $name");
		}
	}
	unless ( -f $file ) {
		die("The path '$file' does not exist, or is not a file");
	}

inc/Module/Install/Metadata.pm  view on Meta::CPAN

	return $self->{values}{no_index};
}

sub read {
	my $self = shift;
	$self->include_deps( 'YAML::Tiny', 0 );

	require YAML::Tiny;
	my $data = YAML::Tiny::LoadFile('META.yml');

	# Call methods explicitly in case user has already set some values.
	while ( my ( $key, $value ) = each %$data ) {
		next unless $self->can($key);
		if ( ref $value eq 'HASH' ) {
			while ( my ( $module, $version ) = each %$value ) {
				$self->can($key)->($self, $module => $version );
			}
		} else {
			$self->can($key)->($self, $value);
		}
	}

lib/ASP4x/Linker.pm  view on Meta::CPAN

{
  my ($s, $name) = @_;
  
  my ($widget) = grep { $_->name eq $name } $s->widgets
    or return;
  
  return $widget;
}# end widget()


sub reset
{
  map { $_->reset } shift->widgets;
}# end reset()


sub uri
{
  my ($s, $args) = @_;
  
  my $vars = $s->vars( $args );
  
  no warnings 'uninitialized';
  my ($uri) = split /\?/, $s->base_href;

lib/ASP4x/Linker.pm  view on Meta::CPAN

      }# end if()
    }# end foreach()
  }# end foreach()
  
  # Also add any non-ref values that were passed in as $args:
  map { $vars{$_} = $args->{$_} }
    grep { ! ref($args->{$_}) }
      sort keys %$args;
  
  my $res = \%vars;
  $s->reset();
  return $res;
}# end _prepare_vars()


sub DESTROY { my $s = shift; undef(%$s); }

1;

=pod

lib/ASP4x/Linker.pm  view on Meta::CPAN

  
  # Add a widget:
  $linker->add_widget(
    name  => "widgetA",
    attrs => [qw( page_size page_number sort_col sort_dir )]
  );
  
  # If the page size is changed, go back to page 1:
  $linker->widget("widgetA")->on_change( page_size => sub {
    my ($s) = @_;
    $s->set( page_number => 1 );
  });
  
  # Add another widget:
  $linker->add_widget(
    name  => "widgetB",
    attrs => [qw( keywords tag start_date stop_date )]
  );
  
  # Chained accessor goodness:
  # New as of v1.001
  my $fancy_uri = $linker->widget('widgetA')->set( %args )->uri;

=head2 Setting Variables

Since the C<$linker> starts out with only the variables found in C<$ENV{REQUEST_URI}>
or in the C<base_href> argument, calling C<< $linker->uri() >> on a new linker object
will return the same uri that you are currently on (eg: C</some-page.asp>

To generate links that contain specific attributes, set the attributes for those
widgets and then call C<< $linker->uri() >> to get the resultant uri to match.

  $linker->widget("widgetA")->set( page_number => 2 );
  $uri = $linker->uri;
  # $uri is now "/some-page.asp?widgetA.page_number=2"

=head1 DESCRIPTION

C<ASP4x::Linker> aims to solve the age-old problem of:

B<How do I change one widget on the page without losing my settings for all the other widgets on the page?>

OK - say you have one data grid on your web page that allows paging and sorting.  You can move forward and backward between
pages, change the sorting - life's great.  B<THEN> your boss says:

  We need to have two of those on the same page.  One for Albums and one for Genres.

Now you have 2 options.

=over 4

lib/ASP4x/Linker.pm  view on Meta::CPAN

Returns undef if no widget by that name is found.

=head2 uri( [$properties] )

Returns the uri for all widgets based on the intersect of:

=over 4

=item * The incoming form data from the original request

=item * Individually-set values for each widget in the collection.

=item * Any properties provided as an argument to C<uri()>.

=back

=head2 hidden_fields( [$properties] )

Returns a string of XHTML hidden input fields (<input type="hidden" name="$name" value="$value" />).

Useful if your persistence logic involves repeated form submissions rather than hyperlinks.

The C<$properties> argument is the same as in the C<uri()> method.

=head2 vars( [$properties] )

Returns a hashref representing the intersect of all widgets' names and attributes.

Supposing you setup your linker like this:

  my $linker = ASP4x::Linker->new();
  
  $linker->add_widget(
    name  => 'albums',
    attrs => [qw( page sort )]
  );
  
  $linker->add_widget(
    name  => 'artists',

lib/ASP4x/Linker.pm  view on Meta::CPAN


And you would get the same thing:

  $VAR1 = {
    'albums.page'  => 2,
    'albums.sort'  => undef,
    'artists.page' => undef,
    'artists.sort' => 'desc',
  };

=head2 reset( )

Resets all widgets to their original values from the original request (as specified in the C<base_href> value used by C<new()>).

=head1 SEE ALSO

L<ASP4>, L<ASP4x::Router>, L<Router::Generic>

=head1 AUTHOR

John Drago <jdrago_999@yahoo.com>

=head1 LICENSE

lib/ASP4x/Linker/Widget.pm  view on Meta::CPAN

  };
  
  return bless \%args, $class;
}# end new()


sub attrs { sort @{ shift->{attrs} } }

sub name { shift->{name} }

sub set
{
  my ($s, %args) = @_;
  while( my ($attr, $val) = each %args )
  {
    confess "widget '$s->{name}' does not have any attribute named '$attr'"
      unless exists($s->{vars}->{$attr});
    $s->{vars}->{$attr} = $val;
    if( my $triggers = $s->{triggers}->{$attr} )
    {
      map { $_->( $s ) } @$triggers
    }# end if()
  }# end while()
  
#  $val;
  $s;
}# end set()


sub get
{
  my ($s, $key) = @_;
  
  exists( $s->{vars}->{ $key } ) or return;
  $s->{vars}->{$key};
}# end get()


sub vars
{
  my $s = shift;
  
  return $s->{vars};
}# end filters()


sub reset
{
  my $s = shift;
  
  %{ $s->{vars} } = %{ $s->{original_vars} };
}# end reset()


sub linker
{
  my $s = shift;
  @_ ? $s->{linker} = shift : $s->{linker};
}# end linker()


sub uri { shift->linker->uri }

lib/ASP4x/Linker/Widget.pm  view on Meta::CPAN

  # Add a widget:
  $linker->add_widget(
    name  => 'albums',
    attrs => [qw( page_number page_size sort_col sort_dir )]
  );
  
  # Get the widget:
  my $widget = $linker->widget('albums');
  
  # Change some attributes:
  $widget->set( page_size   => 10 );
  $widget->set( page_number => 4 );
  
  # Get the value of some attributes:
  $widget->get( 'page_size' );    # 10
  $widget->get( 'page_number' );  # 4

  # Make page_number reset to 1 if the page_size is changed:
  $widget->on_change( page_size => sub {
    my $s = shift;
    $s->set( page_number => 1 );
  });
  
  $widget->set( page_size => 20 );
  print $widget->get( 'page_number' );  # 1
  
  # Set multiple values at once:
  $widget->set( %args );
  
  # Set multiple values at once and get the uri:
  warn $widget->set( %args )->uri();
  
  # Set multiple values by chaining and get the uri:
  warn $widget->set( foo => 'bar' )->set( baz => 'bux' )->uri();

=head1 DESCRIPTION

C<ASP4x::Linker::Widget> provides a simple, simple interface to a "thing" on your
web page (which we'll call a "widget").

A "widget" can be anything.  My experience generally means that a widget is a data grid
that supports paging and sorting through multiple records.  However a widget could
represent anything you want it to, as long as you can describe it with a B<name> and
and arrayref of B<attrs> (attributes).  The B<attrs> arrayref for a data grid might 

lib/ASP4x/Linker/Widget.pm  view on Meta::CPAN

=head2 vars

Returns a hashref of all name/value pairs of attributes and their current values.

=head2 attrs

Returns an B<array> of the names of the widget's attributes.

=head1 PUBLIC METHODS

=head2 reset( )

Restores the widget's C<vars> to its original state - as it was when the widget
was first instantiated.

=head2 set( $attr => $value )

Changes the value of an attribute to a new value.

B<NOTE:> As of version , attempts to apply a value to a non-existant attribute will result in a runtime exception.

=head2 get( $attr )

Returns the current value of the attribute.

=head2 on_change( $attr => sub { ... } )

Adds a trigger to the widget that will be called when the given attribute's value is changed via C<set()>.

=head2 uri()

Just a wrapper around the widget's parent C<ASP4x::Linker> object.

=head1 SEE ALSO

L<ASP4x::Linker>

=head1 AUTHOR

t/010-basic/030-vars.t  view on Meta::CPAN

    'widgetD.type' => undef,
    'widgetA.page_number' => undef,
    'widgetA.sort_col' => undef,
    'widgetB.sort_col' => undef,
    'widgetC.color' => undef,
    'widgetA.sort_dir' => undef,
    'widgetB.page_size' => undef
  }, "Default";


  $linker->widget('widgetA')->set( page_size => 10 );
  is_deeply
    $linker->vars(undef, 1), {
    'widgetB.page_number' => undef,
    'widgetC.type' => undef,
    'widgetD.size' => undef,
    'widgetA.page_size' => 10,
    'widgetB.sort_dir' => undef,
    'widgetC.size' => undef,
    'widgetD.color' => undef,
    'widgetD.type' => undef,

t/010-basic/030-vars.t  view on Meta::CPAN

    'widgetD.type' => undef,
    'widgetA.page_number' => undef,
    'widgetA.sort_col' => undef,
    'widgetB.sort_col' => undef,
    'widgetC.color' => undef,
    'widgetA.sort_dir' => undef,
    'widgetB.page_size' => undef
  }, "Default";


  $linker->widget('widgetA')->set( page_size => 10 );
  is_deeply
    $linker->vars(undef, 1), {
    'widgetB.page_number' => undef,
    'widgetC.type' => undef,
    'widgetD.size' => undef,
    'widgetA.page_size' => 10,
    'widgetB.sort_dir' => undef,
    'widgetC.size' => undef,
    'widgetD.color' => undef,
    'widgetD.type' => undef,

t/010-basic/040-links.t  view on Meta::CPAN

      },
      widgetC => {
        color => 'red'
      },
      widgetD => {
        color => 'orange'
      }
    }) => '/foo/bar/baz/?widgetA.page_number=24&widgetB.page_size=10&widgetC.color=red&widgetD.color=orange&widgetD.type=hat'
  );
  
  $linker->widget('widgetB')->set( page_size => 20 );
  is(
    $linker->uri() => '/foo/bar/baz/?widgetA.page_number=24&widgetB.page_size=20&widgetC.color=blue&widgetD.type=hat',
    'widgetB.page_size = 20'
  );

  is(
    $linker->uri({
      widgetB => {
        page_size => 10
      },

t/010-basic/040-links.t  view on Meta::CPAN

        color => 'orange'
      }
    }) => '/foo/bar/baz/?widgetA.page_number=24&widgetB.page_size=10&widgetC.color=red&widgetD.color=orange&widgetD.type=hat'
  );

  is(
    $linker->uri() => '/foo/bar/baz/?widgetA.page_number=24&widgetB.page_size=100&widgetC.color=blue&widgetD.type=hat',
    '/foo/bar/baz/?widgetA.page_number=24&widgetB.page_size=100&widgetC.color=blue&widgetD.type=hat'
  );
  
  $linker->reset();
  is(
    $linker->uri() => '/foo/bar/baz/?widgetA.page_number=24&widgetB.page_size=100&widgetC.color=blue&widgetD.type=hat',
    '/foo/bar/baz/?widgetA.page_number=24&widgetB.page_size=100&widgetC.color=blue&widgetD.type=hat'
  );
};



WITH_VARS_AND_ROUTER_POSTED: {
last;

t/010-basic/040-links.t  view on Meta::CPAN

      },
      widgetC => {
        color => 'red'
      },
      widgetD => {
        color => 'orange'
      }
    }) => '/foo/bar/baz/?widgetA.page_number=24&widgetB.page_size=10&widgetC.color=red&widgetD.color=orange&widgetD.type=hat'
  );
  
  $linker->widget('widgetB')->set( page_size => 20 );
  is(
    $linker->uri() => '/foo/bar/baz/?widgetA.page_number=24&widgetB.page_size=20&widgetC.color=blue&widgetD.type=hat',
    '/foo/bar/baz/?widgetA.page_number=24&widgetB.page_size=20&widgetC.color=blue&widgetD.type=hat'
  );

  is(
    $linker->uri({
      widgetB => {
        page_size => 10
      },

t/010-basic/040-links.t  view on Meta::CPAN

        color => 'orange'
      }
    }) => '/foo/bar/baz/?widgetA.page_number=24&widgetB.page_size=10&widgetC.color=red&widgetD.color=orange&widgetD.type=hat'
  );

  is(
    $linker->uri() => '/foo/bar/baz/?widgetA.page_number=24&widgetB.page_size=100&widgetC.color=blue&widgetD.type=hat',
    '/foo/bar/baz/?widgetA.page_number=24&widgetB.page_size=100&widgetC.color=blue&widgetD.type=hat'
  );
  
  $linker->reset();
  is(
    $linker->uri() => '/foo/bar/baz/?widgetA.page_number=24&widgetB.page_size=100&widgetC.color=blue&widgetD.type=hat',
    '/foo/bar/baz/?widgetA.page_number=24&widgetB.page_size=100&widgetC.color=blue&widgetD.type=hat'
  );

  is(
    $linker->uri({
      widgetB => {
        page_size => 10
      },

t/010-basic/050-hidden-fields.t  view on Meta::CPAN

);

is
  $linker->hidden_fields() =>
  q(<input type="hidden" name="albums.page_size" value="" />
<input type="hidden" name="artists.page_number" value="" />
<input type="hidden" name="albums.page_number" value="" />
<input type="hidden" name="artists.page_size" value="" />),
  "Hidden Fields look right";

$linker->widget('albums')->set( page_number => 1 );
is
  $linker->hidden_fields() =>
  q(<input type="hidden" name="albums.page_size" value="" />
<input type="hidden" name="artists.page_number" value="" />
<input type="hidden" name="albums.page_number" value="1" />
<input type="hidden" name="artists.page_size" value="" />),
  "Hidden Fields look right";

is
  $linker->hidden_fields() =>
  q(<input type="hidden" name="albums.page_size" value="" />
<input type="hidden" name="artists.page_number" value="" />
<input type="hidden" name="albums.page_number" value="" />
<input type="hidden" name="artists.page_size" value="" />),
  "Auto-reset works";

my $args = {
  albums => { page_size => 20, page_number => 4 },
  artists => { page_size => 5, page_number => 10 }
};
is
  $linker->hidden_fields($args) =>
  q(<input type="hidden" name="albums.page_size" value="20" />
<input type="hidden" name="artists.page_number" value="10" />
<input type="hidden" name="albums.page_number" value="4" />
<input type="hidden" name="artists.page_size" value="5" />),
  "Auto-reset works";



t/010-basic/060-widget-triggers.t  view on Meta::CPAN


my $linker = ASP4x::Linker->new();

my $widget = $linker->add_widget(
  name  => 'artists',
  attrs => [qw( page_number page_size )]
);

ok( $widget, "Got widget" );

# Reset the page number to '1' when the page size is updated:
$widget->on_change( page_size => sub {
  my $s = shift;
  $s->set( page_number => 1 );
});

is( $linker->uri => '/?artists.page_number=4&artists.page_size=10' );
$widget->set( page_size => 20 );
is( $linker->uri => '/?artists.page_number=1&artists.page_size=20' );

t/010-basic/070-chaining.t  view on Meta::CPAN


$api->ua->get('/?artists.page_size=10&artists.page_number=4');

my $linker = ASP4x::Linker->new();

my $widget = $linker->add_widget(
  name  => 'artists',
  attrs => [qw( page_number page_size )]
);

my $uri = $widget->set(page_number => 1, page_size => 2)->uri();
is( $uri => '/?artists.page_number=1&artists.page_size=2' );


t/conf/asp4-config.json  view on Meta::CPAN

  "system": {
    "post_processors": [
    ],
    "libs": [
      "@ServerRoot@/lib"
    ],
    "load_modules": [
    ],
    "env_vars": {
    },
    "settings": {
    }
  },
  "errors": {
    "error_handler":    "ASP4::ErrorHandler",
    "mail_errors_to":   "you@your-server.com",
    "mail_errors_from": "root@localhost",
    "smtp_server":      "localhost"
  },
  "web": {
    "application_name": "ASP4xLinker",



( run in 1.376 second using v1.01-cache-2.11-cpan-49f99fa48dc )