Abilities

 view release on metacpan or  search on metacpan

lib/Abilities/Features.pm  view on Meta::CPAN

for subscription-based web services. This includes paid services, where
customers subscribe to a plan from a list of available plans, each plan
with a different set of features. Examples of such a service are GitHub
(a Git revision control hosting service, where customers purchase a plan
that provides them with different amounts of storage, SSH support, etc.)
and MailChimp (email marketing service where customers purchase plans
that provide them with different amounts of monthly emails to send and
other features).

The L<Abilities> role defined three entities: users, roles and actions.
This role defines three more entities: customers, plans and features.
Customers are organizations, companies or individuals that subscribe to
your web service. They can subscribe to any number of plans, and thus be
provided with the features of these plans. The users from the Abilities
module will now be children of the customers. They still go on being members
of roles and performing actions they are granted with, but now possibly
only within the scope of their parent customer, and to the limits defined
in the customer's plan. Plans can inherit features from other plans, allowing
for defining plans faster and easier.

Customer and plan objects are meant to consume the Abilities::Features
role. L<Entities> is a reference implementation of both the L<Abilities> and
L<Abilities::Features> roles. It is meant to be used as-is by web applications,
or just as an example of how a user management and authorization system
that consumes these roles might look like. L<Entities::Customer> and
L<Entities::Plan> are customer and plan classes that consume this role.

Just like in L<Abilities>, features can be constrained. For more info,
see L<Abilities/"CONSTRAINTS">.

More information about how these roles work can be found in the L<Entities>
documentation.

=head1 REQUIRED METHODS

Customer and plan classes that consume this role are required to provide
the following methods:

=head2 plans()

This method returns a list of all plan names that a customer has subscribed to,
or that a plan inherits from.

Example return structure:

	( 'starter', 'diamond' )

NOTE: In previous versions, this method was required to return
an array of plan objects, not a list of plan names. This has been changed
in version 0.3.

=cut

requires 'plans';

=head2 features()

This method returns a list of all feature names that a customer has explicitely
been given, or that a plan has. If a certain feature is constrained, then
it should be added to the list as an array reference with two items, the first being
the name of the feature, the second being the name of the constraint.

Example return structure:

	( 'ssh_access', [ 'multiple_users', 5 ] )

NOTE: In previous versions, this method was required to return
an array of feature objects, not a list of feature names. This has been changed
in version 0.3.

=cut

requires 'features';

=head2 get_plan( $name )

Returns the object of the plan named C<$plan>.

=cut

requires 'get_plan';

=head1 METHODS

Classes that consume this role will have the following methods provided
to them:

=head2 has_feature( $feature_name, [ $constraint ] )

Receives the name of a feature, and possibly a constraint, and returns a
true value if the customer/plan has that feature, false value otherwise.

=cut

sub has_feature {
	my ($self, $feature, $constraint) = @_;

	# return false if customer/plan does not have that feature
	return unless $self->available_features->{$feature};

	# customer/plan has feature, but is there a constraint?
	if ($constraint) {
		# return true if customer/plan's feature is not constrained
		return 1 if !ref $self->available_features->{$feature};
		
		# it is constrained (or at least it should be, let's make
		# sure we have an array-ref of constraints)
		if (ref $self->available_features->{$feature} eq 'ARRAY') {
			foreach (@{$self->available_features->{$feature}}) {
				return 1 if $_ eq $constraint;
			}
			return; # constraint not met
		} else {
			carp "Expected an array-ref of constraints for feature $feature, received ".ref($self->available_features->{$feature}).", returning false.";
			return;
		}
	} else {
		# no constraint, make sure customer/plan's feature is indeed
		# not constrained
		return if ref $self->available_features->{$feature}; # implied: ref == 'ARRAY', thus constrained
		return 1; # not constrained



( run in 0.889 second using v1.01-cache-2.11-cpan-75ffa21a3d4 )