Add code from gists 12311[56]
[catagits/CatalystX-DynamicComponent.git] / t / lib / TestComplexApp / Controller / PaymentProvider.pm
1 package TestComplexApp::Controller::PaymentProvider;
2 use Moose;
3 use Moose::Meta::Class;
4 use namespace::autoclean;
5
6 BEGIN { extends 'Catalyst::Controller'; };
7
8 =head1 NAME
9
10 PaymentApp::Controller::PaymentProvider - Provider Controller factory
11
12 =head1 DESCRIPTION
13
14 This module creates dynamic Catalyst controllers based on
15 configuration. It's intended for use with Catalyst::Engine::Stomp,
16 where each controller namespace corresponds to a message queue
17 subscription.
18
19 The generated controllers expect Model classes to be defined for the
20 configured payment providers, as configured by
21 PaymentApp::Model::PaymentProvider - and in fact we borrow that
22 module's config.
23
24 We create one controller for each configured payment provider, for
25 each configured environment. The generated namespace is based on both
26 these names, so you end up with queues like "live_datacash". 
27
28 The relevant configuration comes from Catalyst, and should resemble
29 this:
30
31   Model::PaymentProvider:
32     providers:
33       - PaymentProvider::Datacash
34       - PaymentProvider::Cybersource
35       - PaymentProvider::Null
36
37   Controller::PaymentProvider:
38     environments:
39       - qa
40       - test
41       - custqa
42       - uat
43
44 =head1 TODO
45
46 We're currently hardcoding the list of methods which form the
47 interface to a payment processor. This will change - we will require
48 that a payment provider class "does" a Moose role, and that role will
49 indicate the methods we should consider dispatchable. 
50
51 =head1 SEE ALSO
52
53 PaymentApp::Model::PaymentProvider - this module's partner in crime.
54
55 =cut
56
57 __PACKAGE__->config( namespace => '' );
58
59 sub COMPONENT {
60         my $self = shift->next::method(@_);
61         
62         # Sneakily borrow our corresponding model's config for providers
63         my $model_config = $self->_app->config->{'Model::PaymentProvider'};
64         my $provider_classes = $model_config->{providers};
65
66         # Environment list is from our own config. 
67         my $environments = $self->{environments};
68         
69         # For each configured provider/environment combination, create
70         # a Controller class which dispatches a set of methods to the
71         # provider's Model.
72         for my $provider_class (@$provider_classes) {
73                 for my $environment (@$environments) {
74
75                         # Compose the name of the Controller
76                         my $env_class = ucfirst($environment);
77                         my $provider_controller_name = "Controller::${provider_class}::${env_class}";
78
79                         # Compose its namespace - i.e. the STOMP queue name
80                         my $namespace = $environment . '_' . $provider_class->name;
81                         $self->_app->config( $provider_controller_name => { namespace => $namespace } );
82
83                         # Create the class with Moose
84                         my $provider = Moose::Meta::Class->create("PaymentApp::${provider_controller_name}");
85                         $provider->superclasses('PaymentApp::ControllerBase::Message');
86
87                         # XXX from interface role, not static list here!
88                         my @dispatchable_methods = qw/ payment_auth_request
89                                                        payment_settle_request
90                                                        payment_refund_request
91                                                      /;
92                         
93                         # Attach a method for each action, making sure
94                         # to apply the right method attribute to turn
95                         # these into actions
96                         for my $method_name (@dispatchable_methods) {
97                                 my $sub = sub {
98                                         my ($self, $c, $message) = @_;
99                                         my $provider_model = $c->model($provider_class);
100                                         my $response = $provider_model->$method_name($message);
101                                         $c->stash->{response} = $response;
102                                 };
103                                 
104                                 # Why doesn't add_method return the method?
105                                 $provider->add_method($method_name, $sub);
106                                 my $method = $provider->get_method($method_name);
107                                 $provider->register_method_attributes($method->body, ['Local']);
108                         }
109                 }
110         }
111
112         return $self;
113 }
114         
115 __PACKAGE__->meta->make_immutable;
116