Context-Singleton

 view release on metacpan or  search on metacpan

lib/Context/Singleton.pm  view on Meta::CPAN

use strict;
use warnings;
use feature 'state';

package Context::Singleton;

our $VERSION = v1.0.5;

use parent 'Exporter::Tiny';

use Sub::Install qw();
use Variable::Magic qw();

use Context::Singleton::Frame;

our @EXPORT = keys %{ _by_frame_class_accessors () };

sub _by_frame_class_accessors {
	my ($frame_class) = @_;
	$frame_class //= 'Context::Singleton::Frame';

	state %cache;

	return $cache{$frame_class} //= do {
		my $current_frame = $frame_class->new;

		my $restore_context_wizard = Variable::Magic::wizard
			free => sub { $current_frame = $current_frame->parent; 1 },
		;

		my $frame = sub (&) {
			Variable::Magic::cast my $guard => $restore_context_wizard;
			$current_frame = $current_frame->new;

			$_[0]->();
		};

		+{
			contrive      => sub { $current_frame->contrive (@_) },
			current_frame => sub { $current_frame },
			deduce        => sub { $current_frame->deduce (@_) },
			frame         => $frame,
			is_deduced    => sub { $current_frame->is_deduced (@_) },
			load_rules    => sub { $current_frame->load_rules (@_) },
			proclaim      => sub { $current_frame->proclaim (@_) },
			trigger       => sub { $current_frame->trigger (@_) },
			try_deduce    => sub { $current_frame->try_deduce (@_) },
		};
	};
}

sub _exporter_expand_sub {
	my ($class, $name, $args, $globals) = @_;

	return $name => _by_frame_class_accessors ($globals->{frame_class})->{$name};
}

sub import {
	my ($class, @params) = @_;

	my $globals = Ref::Util::is_hashref ($params[0])
		? shift @params
		: {}
		;

	$globals->{into} //= scalar caller;

	$class->SUPER::import ($globals, @params);

	_by_frame_class_accessors ($globals->{frame_class})->{load_rules}->(@{ $globals->{load_path} })
		if $globals->{load_path};

}

1;

__END__

=head1 NAME

Context::Singleton - handles context specific singletons

=head1 DESCRIPTION

=head2 What is a context specific singleton?

As your workflow handles its tasks, granularity become finer and certain
entities behaves like singletons.

Nice example is user id/object after successful authentication.
Its value is constant for every function/method called after it is known
but is unknown and can represents millions of users.

=head2 How does it differ from the multiton pattern?

Multiton is a set of singletons (global variables) whereas Context::Singleton
provides context scope.

=head2 Doesn't C<local> already provide similar behaviour?

Context::Singleton doesn't provide only localized scope.

It provides immutability on scope and can build values based on dependencies.
With dependency tracking it can rebuild them in inner scope in case their
dependencies were modified.

=head1 EXPORTED FUNCTIONS

=head2 Terms

=head3 resource

Singleton idenfication, string, global.

=head3 recipe

Rule specifies how to build value

	contrive 'resource' => ( ... );

There can be multiple recipes for building a C<singleton value>.



( run in 2.674 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )