Re: [Catalyst] RESTful client

2009-12-04 Thread David Wright
Tomas Doran wrote:

 On 4 Dec 2009, at 00:25, Sungsam Gong wrote:

 OK, let me just clarify my question to narrow down the problem.

 How does your catalyst application consume remote web services,
 especially for RESTful?

 As noted, if at all possible, I don't consume them direct from my
 application (due to latency and availability issues)
If one can't avoid consuming them, then using Time::Hires's ualarm
allows sub-second bailing if the remote call takes too long.

I too, at $work, want to stop runtime REST calls, but we're paid up with
$remote_provider for a few more months.

Cheers,
David


___
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/


Re: [Catalyst] testing catalyst app - need context

2009-03-02 Thread David Wright

Ian Docherty wrote:

Kate Yoak wrote:

Hi there,


Here is a newbie question:

I like to test my functionality in bits and pieces as I write it.  How
do I go about getting myself the context object in a test script?

For example, one of the tests catalyst installs is t/model_App.t where
it loads the model.  I'd love to then be able to use the model the same
way a controller would:

my $acc = $c-model('Account')-find(1);

Instead, I am doing
my $model = MyApp::Model::App-new();
my $acc = $model-recordset('Account')-find(1);

In addition to being less than ideal because I am doing something
different that I expect real application code to do, it presents
configuration problems.  Like, turns out, in order for config to take
effect, I have to run __PACKAGE__-setup; after configuring it - which
won't be necessary, I think, in a real app.

Is the practice of unit tests that break up the layers of catalyst
frowned upon? Or what should I do to make it work right?
  
Best practice is to keep your model separate from Catalyst so that you 
can (for example) create batch scripts or cron

jobs that work on the model without having to load the whole of Catalyst

Your model then would be in something like MyApp::Storage and accessed 
by your tests as so...


my $schema = MyApp::Storage-connect(
   'DBI:mysql:host=localhost;database=my_database',
   'username',
   'password',
   { 'mysql_enable_utf8' = 1 },
   {on_connect_do =[ 'set names utf8' ] }
);
my $acc = $schema-resultset('Account')-find(1);

You would be testing your database layer separately from Catalyst.

Models aren't necessarily database layers.

I'd suggest that with a sufficiently rich Catalyst application, the 
'model' as known by Catalyst could easily become an effective 
'controller' of a further model. That secondary controller is going to 
want to load model classes, and Catalyst provides a nice way of 
controlling instantiation.


e.g.

sub handle_payment : Path {
  my ($self, $c, @args) = @_;

  my $proc = $c-model('PaymentProcessor');
  my $result = $proc-pay( { currency = $args[0], amount = $args[1], 
id = $args[2] } );


  $c-stash-{result}-{message} = $result-message;
  $c-stash-{result}-{code} = $result-code;
}

# then, within PaymentProcessor
sub pay {
   my ($self, $args) = @_;

   MyApp-model( 'Ledger' )-insert( { # blah } );
   MyApp-model('Order')-update( { # blah } ) ;
}

This definitely does tightly couple the PaymentProcessor to the 
application. However, it also allows for better whitebox testing. If 
your logic is embedded within a Catalyst controller, the only sensible 
way to test it is to make an HTTP request. If it's in the model, the 
individual steps can be tested.


FWIW, the principle here is Catalyst controllers should do nothing 
except validate and translate HTTP parameters to 'model' parameters. 
They shouldn't manipulate model objects, or build complicated logic by 
combinations of model calls.


Regards,
David


___
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/


Re: [Catalyst] testing catalyst app - need context

2009-03-02 Thread David Wright

J. Shirley wrote:
On Mon, Mar 2, 2009 at 5:00 AM, David Wright dave-catal...@dexy.org 
mailto:dave-catal...@dexy.org wrote:


Ian Docherty wrote:

Kate Yoak wrote:

Hi there,


Here is a newbie question:

I like to test my functionality in bits and pieces as I
write it.  How
do I go about getting myself the context object in a test
script?

For example, one of the tests catalyst installs is
t/model_App.t where
it loads the model.  I'd love to then be able to use the
model the same
way a controller would:

my $acc = $c-model('Account')-find(1);

Instead, I am doing
my $model = MyApp::Model::App-new();
my $acc = $model-recordset('Account')-find(1);

In addition to being less than ideal because I am doing
something
different that I expect real application code to do, it
presents
configuration problems.  Like, turns out, in order for
config to take
effect, I have to run __PACKAGE__-setup; after
configuring it - which
won't be necessary, I think, in a real app.

Is the practice of unit tests that break up the layers of
catalyst
frowned upon? Or what should I do to make it work right?
 


Best practice is to keep your model separate from Catalyst so
that you can (for example) create batch scripts or cron
jobs that work on the model without having to load the whole
of Catalyst

Your model then would be in something like MyApp::Storage and
accessed by your tests as so...

my $schema = MyApp::Storage-connect(
  'DBI:mysql:host=localhost;database=my_database',
  'username',
  'password',
  { 'mysql_enable_utf8' = 1 },
  {on_connect_do =[ 'set names utf8' ] }
);
my $acc = $schema-resultset('Account')-find(1);

You would be testing your database layer separately from Catalyst.

Models aren't necessarily database layers.

I'd suggest that with a sufficiently rich Catalyst application,
the 'model' as known by Catalyst could easily become an effective
'controller' of a further model. That secondary controller is
going to want to load model classes, and Catalyst provides a nice
way of controlling instantiation.

e.g.

sub handle_payment : Path {
 my ($self, $c, @args) = @_;

 my $proc = $c-model('PaymentProcessor');
 my $result = $proc-pay( { currency = $args[0], amount =
$args[1], id = $args[2] } );

 $c-stash-{result}-{message} = $result-message;
 $c-stash-{result}-{code} = $result-code;
}

# then, within PaymentProcessor
sub pay {
  my ($self, $args) = @_;

  MyApp-model( 'Ledger' )-insert( { # blah } );
  MyApp-model('Order')-update( { # blah } ) ;
}

This definitely does tightly couple the PaymentProcessor to the
application. However, it also allows for better whitebox testing.
If your logic is embedded within a Catalyst controller, the only
sensible way to test it is to make an HTTP request. If it's in the
model, the individual steps can be tested.

FWIW, the principle here is Catalyst controllers should do
nothing except validate and translate HTTP parameters to 'model'
parameters. They shouldn't manipulate model objects, or build
complicated logic by combinations of model calls.

Regards,
David




It's good advice to have things standalone and available outside of 
Catalyst, but your assessment of model dependencies stops short.
What you want is an independent model that accepts a schema object for 
recording, and then a very thin adapter class inside of Catalyst.
And the config object, cache backends, the logger? The code above could 
be made more complex:


sub pay {
 my ($self, $args) = @_;

 my $order = MyApp-cache('ordercache')-get( $args{id} ) ||
   do {
  my $o = MyApp-model('Order')-find( $args{id} ) ;
  MyApp-cache('ordercache')-set( $o-id, $o, 
MyApp-config-{cache_expiry});

  $o;
  };
  $o-update( { #blah  } );
 MyApp-log-debug (Got order);
 MyApp-model( 'Ledger' )-insert( { # blah } );
}

As I said, this 'model' class is very controller-like. Something has to 
deal with the interactions between those components, and I contend that 
it shouldn't be a standard Catalyst controller.


Using something like this is the suggested better practice:

package MyApp::Model::PaymentProcessor;
use base 'Catalyst::Model::Adaptor';

__PACKAGE__-config( class = 'MyApp::PaymentProcessor' );

sub prepare_arguments {
 my ($self, $c) = @_;
 return { log = $c-log, expiry = $c-config-{cache_expiry},  cache 
= $c-cache, schema = $c-model-schema };

}

But this doesn't help much: now, when testing

Re: [Catalyst] Maybe there is a need for some speedups of 'config' method ?

2009-02-23 Thread David Wright

Jonathan Rockway wrote:

* On Mon, Feb 23 2009, Jason Gottshall wrote:
  

Oleg Pronin wrote:


  I use many actions that take params from config in runtime, for example
  sub pay_for_vip : Private {
   ...
   my $price = $c-cfg-{vip}{price};
  }
  

As I understand it, this is NOT the way config is intended to
work. All the config for your component (controller in this case) is
passed to the constructor at setup time; all you need to do is make
accessors for whatever you want access to:

  __PACKAGE__-mk_accessors(qw/vip/);

  sub pay_for_vip : Private {
  ...
  my $price = $self-vip-{price};
  }



Yes, exactly.

This is another case of the all-too-frequent change Catalyst so that I
won't have to change any code in my poorly-implemented app.
  

Jeez. That's a little harsh, though I sympathise with the general point.

We use the Catalyst config file to initialize Catalyst components, to 
initialise non-Catalyst configuration objects, and also to provide via 
config() a simple runtime interface to 'get this key/value pair'. We 
store other (runtime) conf values in the db, wrapped with memcached, to 
avoid having to deploy a config file if we need timely changes. Many of 
our conf values are shared between components, not directly relevant to 
components, or are most readably kept together as a discrete 
configuration set, rather than added per component. Finally, given the 
size of our conf file, I don't think we'd want to clutter either our 
packages with accessors, or our project with classes that merely wrap 
configuration sets.

I don't think the patch will be harmful, though, so if there is one we
might as well apply it.  But it's a lot easier to not misuse config than
it is to patch Catalyst to make the misuse faster.
  
I can't see anywhere in the docs where this 'intended use' is stated. 
That's not to say that it isn't there.


Having said all that, our app must call config 10s of million times a 
day, and we've never perceived it to be a bottleneck.


Regards,
David


___
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/


Re: [Catalyst] RFC: The paradox of choice in web development

2009-02-15 Thread David Wright




I can't say much because of confidentiality, but from the Catalyst 
survey late last year, I can say that there are some pretty high 
profile places using Catalyst around about. It's public knowledge that 
two of the biggest streaming media websites in the world use Catalyst. 
Aye, that it is: 
http://www.bbc.co.uk/blogs/bbcinternet/2008/12/iplayer_day_performance_tricks.html


David




___
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/


Re: [Catalyst] Testing RESTful web services

2008-10-06 Thread David Wright
Ian Docherty wrote:
 Moritz Onken wrote:

 Am 05.10.2008 um 10:47 schrieb Ian Docherty:

 Hi
 I am writing a simple test to test a POST method in a web service
 but my controller does not see any content in the POSTed request.

 In the controller both the $c-request-body and
 $c-request-content_length are undefined.

 Any ideas?

 -- test.t ---
 use strict;
 use warnings;

 use Catalyst::Test 'MyApp';
 use HTTP::Request;

 my $req = HTTP::Request-new(
   'POST',
   '/foo',
   [
   Content_Type = 'text/plain'
   ],
   hello world,
   );

 It's Content-type = ...
 Yup, typo on my part, but it does not change the problem. There is
 still no content in the request when it gets to the controller.
It's not a typo, the constructor accepts underscores as well as dashes
so that you can build requests without having to quote the hash keys.


 Just a slight modification to my original statement. In the controller
 the request body is undef, but the request length is 0 (not undef)

IIRC you have to manually add content-length yourself, HTTP::Request
doesn't build it for you.

Regards,
David


___
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/


Re: [Catalyst] Catalyst::Log::Log4perl autoflush

2008-04-20 Thread David Wright

J. Shirley wrote:

On Sat, Apr 19, 2008 at 8:01 AM, David Wright [EMAIL PROTECTED] wrote:

Hi,

 It seems that 'autoflush' doesn't mean what it usually does in
Catalyst::Log::Log4perl. It usually means that a write should happen
immediately, and not be buffered.

 That isn't the behaviour in Catalyst::Log::Log4perl, either when set using
the log4perl conf, or when using the option to new(). The option to new()
disables 'abort', which I think is a misunderstanding. The logging functions
always simply push to the log4perlstack, which is only flushed after the
request has been mostly handled.

 In order to get autoflush working as expected, I have had to create a
 derived class and override _log():

 sub _log {
 my $self = shift;
 $self-SUPER::_log(@_);
 $self-_flush if scalar(caller(1)) =~ /^MyApp(::|$)/;
 }

 perl -v: v5.8.8 built for i486-linux-gnu-thread-multi
 uname -a: Linux PC-5023452 2.6.22-14-generic #1 SMP Fri Feb 1 04:59:50
 UTC 2008 i686 GNU/Linux
 Catalyst::Log::Log4perl version: 1.0

 I've raised a bug in RT for this:
http://rt.cpan.org/Public/Bug/Display.html?id=35221

 Thanks,
 David Wright




David,

Could you throw a test case in with this (either on the RT bug or in
this mail) and I'll get a patch put in place?


Diff to 10-basic.t attached.

Cheers,
David




6c6
 use Test::More tests = 14;
---
 use Test::More tests = 15;
91a92,106
 
 
 # check that if autoflush is enabled, the log is written to immediately
 $app-log(
 Catalyst::Log::Log4perl-new( \CONF, override_cspecs = 1, autoflush = 1 ) );
 log4perl.rootLogger=WARN, LOG
 log4perl.appender.LOG=Log::Log4perl::Appender::String
 log4perl.appender.LOG.layout=PatternLayout
 log4perl.appender.LOG.layout.ConversionPattern=[%c] %m
 CONF
 
 $appender-layout( Log::Log4perl::Layout::PatternLayout-new('%m') );
 $c = $app-log-warn('delay');
 log_like( qr|delay|, autoflush );
 
___
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/


[Catalyst] Catalyst::Log::Log4perl autoflush

2008-04-19 Thread David Wright

Hi,

It seems that 'autoflush' doesn't mean what it usually does in 
Catalyst::Log::Log4perl. It usually means that a write should happen 
immediately, and not be buffered.


That isn't the behaviour in Catalyst::Log::Log4perl, either when set 
using the log4perl conf, or when using the option to new(). The option 
to new() disables 'abort', which I think is a misunderstanding. The 
logging functions always simply push to the log4perlstack, which is only 
flushed after the request has been mostly handled.


In order to get autoflush working as expected, I have had to create a
derived class and override _log():

sub _log {
my $self = shift;
$self-SUPER::_log(@_);
$self-_flush if scalar(caller(1)) =~ /^MyApp(::|$)/;
}

perl -v: v5.8.8 built for i486-linux-gnu-thread-multi
uname -a: Linux PC-5023452 2.6.22-14-generic #1 SMP Fri Feb 1 04:59:50
UTC 2008 i686 GNU/Linux
Catalyst::Log::Log4perl version: 1.0

I've raised a bug in RT for this: 
http://rt.cpan.org/Public/Bug/Display.html?id=35221


Thanks,
David Wright

___
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/


Re: [Catalyst] Catalyst::Log::Log4perl autoflush

2008-04-19 Thread David Wright

 It seems that 'autoflush' doesn't mean what it usually does in
Catalyst::Log::Log4perl. It usually means that a write should happen
immediately, and not be buffered.

 That isn't the behaviour in Catalyst::Log::Log4perl, either when set using
the log4perl conf, or when using the option to new(). The option to new()
disables 'abort', which I think is a misunderstanding. The logging functions
always simply push to the log4perlstack, which is only flushed after the
request has been mostly handled.

 In order to get autoflush working as expected, I have had to create a
 derived class and override _log():

 sub _log {
 my $self = shift;
 $self-SUPER::_log(@_);
 $self-_flush if scalar(caller(1)) =~ /^MyApp(::|$)/;
 }

 perl -v: v5.8.8 built for i486-linux-gnu-thread-multi
 uname -a: Linux PC-5023452 2.6.22-14-generic #1 SMP Fri Feb 1 04:59:50
 UTC 2008 i686 GNU/Linux
 Catalyst::Log::Log4perl version: 1.0

 I've raised a bug in RT for this:
http://rt.cpan.org/Public/Bug/Display.html?id=35221


David,

Could you throw a test case in with this (either on the RT bug or in
this mail) and I'll get a patch put in place?


Hello Jay,

Which of the autoflushes should I test against: the parameter to 
new(), or the option in a log4perl configuration file?


Do you (or others) agree that the autoflush option to new() is a misnomer? 
If not, wouldn't my test simply prove that the code doesn't do what it 
doesn't say it does?


David

___
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/


Re: [Catalyst] fix config right before database connect?

2008-03-24 Thread David Wright

Hi,


Hello!

I'm running several instances of Catalyst app (for every developer and 
production) on the same box; I'm thinking of automatically patch 
database name loaded from config file to allow separate copies of 
databases to coexist transparently.


We have the same multi-instance set up, and instead of patching have two 
config files per instance. The first contains entries common to all, and 
the second adds in instance specific database connect info.


Catalyst will load any config named $myapp_foo. In this case, I doubt 
the order matters.


e.g.
cat conf/myapp_common.xml

config
  nameMyapp/name
  logconf/logging.conf/log

  model name=Core
schema_classMyapp::Schema::DB/schema_class
  /model
/config

cat conf/myapp_perinstance.xml

config
  nameMyapp/name
  model name=Core   
connect_infodsn:blah/connect_info
connect_infomonkey/connect_info
connect_infotennis/connect_info
  /model
/config

HTH,
David


___
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/