——————————————package
Test::BDD::Cucumber::StepContext;
{
$Test::BDD::Cucumber::StepContext::VERSION
=
'0.17'
;
}
use
Moose;
=head1 NAME
Test::BDD::Cucumber::StepContext - Data made available to step definitions
=head1 VERSION
version 0.17
=head1 DESCRIPTION
The coderefs in Step Definitions have a single argument passed to them, a
C<Test::BDD::Cucumber::StepContext> object. This is an attribute-only class,
populated by L<Test::BDD::Cucumber::Executor>.
=head1 ATTRIBUTES
=head2 columns
If the step-specific data supplied is a table, the this attribute will contain
the column names in the order they appeared.
=cut
has
'columns'
=> (
is
=>
'ro'
,
isa
=>
'ArrayRef'
);
=head2 _data
Step-specific data. Will either be a text string in the case of a """ string, or
an arrayref of hashrefs if the step had an associated table.
See the C<data> method below.
=cut
has
'_data'
=> (
is
=>
'ro'
,
isa
=>
'Str|ArrayRef'
,
init_arg
=>
'data'
,
default
=>
''
);
=head2 stash
A hash of hashes, containing three keys, C<feature>, C<scenario> and C<step>.
The stash allows you to persist data across features, scenarios, or steps
(although the latter is there for completeness, rather than having any useful
function).
=cut
has
'stash'
=> (
is
=>
'ro'
,
required
=> 1,
isa
=>
'HashRef'
);
=head2 feature
=head2 scenario
=head2 step
Links to the L<Test::BDD::Cucumber::Model::Feature>,
L<Test::BDD::Cucumber::Model::Scenario>, and L<Test::BDD::Cucumber::Model::Step>
objects respectively.
=cut
has
'feature'
=> (
is
=>
'ro'
,
required
=> 1,
isa
=>
'Test::BDD::Cucumber::Model::Feature'
);
has
'scenario'
=> (
is
=>
'ro'
,
required
=> 1,
isa
=>
'Test::BDD::Cucumber::Model::Scenario'
);
has
'step'
=> (
is
=>
'ro'
,
required
=> 0,
isa
=>
'Test::BDD::Cucumber::Model::Step'
);
=head2 verb
The lower-cased verb a Step Definition was called with.
=cut
has
'verb'
=> (
is
=>
'ro'
,
required
=> 1,
isa
=>
'Str'
);
=head2 text
The text of the step, minus the verb. Placeholders will have already been
multiplied out at this point.
=cut
has
'text'
=> (
is
=>
'ro'
,
required
=> 1,
isa
=>
'Str'
,
default
=>
''
);
=head2 harness
The L<Test::BDD::Cucumber::Harness> harness being used by the executor.
=cut
has
'harness'
=> (
is
=>
'ro'
,
required
=> 1,
isa
=>
'Test::BDD::Cucumber::Harness'
);
=head2 matches
Any matches caught by the Step Definition's regex. These are also available as
C<$1>, C<$2> etc as appropriate.
=cut
has
'_matches'
=> (
is
=>
'rw'
,
isa
=>
'ArrayRef'
,
init_arg
=>
'matches'
,
default
=>
sub
{ [] } );
has
'transformers'
=> (
is
=>
'ro'
,
isa
=>
'ArrayRef'
,
predicate
=>
'has_transformers'
, );
has
'_transformed_matches'
=> (
is
=>
'ro'
,
isa
=>
'ArrayRef'
,
lazy
=> 1,
builder
=>
'_build_transformed_matches'
,
clearer
=>
'_clear_transformed_matches'
, );
has
'_transformed_data'
=> (
is
=>
'ro'
,
isa
=>
'Str|ArrayRef'
,
lazy
=> 1,
builder
=>
'_build_transformed_data'
,
clearer
=>
'_clear_transformed_data'
, );
=head2 is_hook
The harness processing the output can decide whether to shop information for
this step which is actually an internal hook, i.e. a Before or After step
=cut
has
'is_hook'
=> (
is
=>
'ro'
,
isa
=>
'Bool'
,
lazy
=> 1,
builder
=>
'_build_is_hook'
);
=head1 METHODS
=head2 background
Boolean for "is this step being run as part of the background section?".
Currently implemented by asking the linked Scenario object...
=cut
sub
background {
my
$self
=
shift
;
return
$self
->scenario->background }
=head2 data
See the C<_data> attribute above.
Calling this method will return either the """ string, or a possibly Transform-ed
set of table data.
=cut
sub
data
{
my
$self
=
shift
;
if
(
@_
)
{
$self
->_data(
@_
);
$self
->_clear_transformed_data;
return
;
}
return
$self
->_transformed_data;
}
=head2 matches
See the C<_matches> attribute above.
Call this method will return the possibly Transform-ed matches .
=cut
sub
matches
{
my
$self
=
shift
;
if
(
@_
)
{
$self
->_matches(
@_
);
$self
->_clear_transformed_matches;
return
;
}
return
$self
->_transformed_matches;
}
=head2 transform
Used internally to transform data and placeholders, but it can also be called
from within your Given/When/Then code.
=cut
sub
transform
{
my
$self
=
shift
;
my
$value
=
shift
;
defined
$value
or
return
$value
;
TRANSFORM:
for
my
$transformer
( @{
$self
->transformers } )
{
# uses the same magic as other steps
# and puts any matches into $1, $2, etc.
# and calls the Transform step
if
(
$value
=~ s/
$transformer
->[0]/
$transformer
->[1]->(
$self
)/e )
{
# if we matched then stop processing this match
return
$value
;
}
}
# if we're here, the value will be returned unchanged
return
$value
;
}
# the builder for the is_hook attribute
sub
_build_is_hook
{
my
$self
=
shift
;
return
(
$self
->verb eq
'before'
or
$self
->verb eq
'after'
) ? 1 : 0;
}
# the builder for the _transformed_matches attribute
sub
_build_transformed_matches
{
my
$self
=
shift
;
my
@transformed_matches
= @{
$self
->_matches };
# this stops it recursing forever...
# and only Transform if there are any to process
if
(
$self
->verb ne
'transform'
and
$self
->has_transformers )
{
@transformed_matches
=
map
{
$_
=
$self
->transform(
$_
) }
@transformed_matches
;
}
return
\
@transformed_matches
;
}
# the builder for the _transformed_data attribute
sub
_build_transformed_data
{
my
$self
=
shift
;
my
$transformed_data
=
$self
->_data;
# again stop recursing
# only transform table data
# and only Transform if there are any to process
if
(
$self
->verb ne
'transform'
and
ref
$transformed_data
and
$self
->has_transformers )
{
# build the string that a Transform is looking for
# table:column1,column2,column3
my
$table_text
=
'table:'
.
join
(
','
, @{
$self
->columns } );
if
(
my
$transformer
= first {
$table_text
=~
$_
->[0] } @{
$self
->transformers } )
{
# call the Transform step
$transformer
->[1]->(
$self
,
$transformed_data
);
}
}
return
$transformed_data
;
}
=head1 AUTHOR
Peter Sergeant C<pete@clueball.com>
=head1 LICENSE
Copyright 2011, Peter Sergeant; Licensed under the same terms as Perl
=cut
1;