FAQ
After much discussion, I've unleased Catalyst::Action::REST upon an
unsuspecting CPAN. It's an attempt at making RESTful web applications
in Catalyst easier (they were already pretty easy.) The basics:

1) Uses an Action class to extend Catalyst's dispatch mechanism to allow
for different methods based on the HTTP Method. For example:

sub foo :Local :ActionClass('REST') {}

sub foo_GET {}

sub foo_POST {}

sub foo_DELETE {}

2) Handles serializing and deserializing data based on the content type
of the request. Currently only YAML and Data::Dumper are implemented,
but it's easy to add more. (XML, anyone?)

3) Wraps those parts together in a Catalyst::Controller::REST, so you
don't have to. :) The Controller base class also includes helper
methods for returning the various HTTP Result Codes (and their proper
attending headers and bodies.)

The code is still pretty early in it's development. It's working great
for me, but there are certainly still areas for improvement. At the
very least, I think it's a good starting place for those of us doing
REST app's in Catalyst to collaborate.

On the CPAN:

http://search.cpan.org/~holoway/Catalyst-Action-REST-0.1/

If you have any questions, comments, patches, flames, or beer, you can
usually find me as holoway on #catalyst.

The all important shout-out goes to mst and jrockway, who helped more
than considerably with the design. (But all the bugs, I assure you, are
mine.)

Adam

Search Discussions

  • Boris Ćeranić at Nov 20, 2006 at 10:55 am
    This sounds like a good idea & module to me. Most of the time, I find
    myself doing

    if ( $c->req->method eq 'POST' ) { } else { }

    ..which doesn't improve code readability (at last in my case).

    Thanks for this addition ;)
    On 20/11/06, Adam Jacob wrote:
    After much discussion, I've unleased Catalyst::Action::REST upon an
    unsuspecting CPAN. It's an attempt at making RESTful web applications
    in Catalyst easier (they were already pretty easy.) The basics:

    1) Uses an Action class to extend Catalyst's dispatch mechanism to allow
    for different methods based on the HTTP Method. For example:

    sub foo :Local :ActionClass('REST') {}

    sub foo_GET {}

    sub foo_POST {}

    sub foo_DELETE {}

    2) Handles serializing and deserializing data based on the content type
    of the request. Currently only YAML and Data::Dumper are implemented,
    but it's easy to add more. (XML, anyone?)

    3) Wraps those parts together in a Catalyst::Controller::REST, so you
    don't have to. :) The Controller base class also includes helper
    methods for returning the various HTTP Result Codes (and their proper
    attending headers and bodies.)

    The code is still pretty early in it's development. It's working great
    for me, but there are certainly still areas for improvement. At the
    very least, I think it's a good starting place for those of us doing
    REST app's in Catalyst to collaborate.

    On the CPAN:

    http://search.cpan.org/~holoway/Catalyst-Action-REST-0.1/

    If you have any questions, comments, patches, flames, or beer, you can
    usually find me as holoway on #catalyst.

    The all important shout-out goes to mst and jrockway, who helped more
    than considerably with the design. (But all the bugs, I assure you, are
    mine.)

    Adam

    _______________________________________________
    List: [email protected]
    Listinfo: http://lists.rawmode.org/mailman/listinfo/catalyst
    Searchable archive: http://www.mail-archive.com/[email protected]/
    Dev site: http://dev.catalyst.perl.org/
  • Christopher H. Laco at Nov 20, 2006 at 1:47 pm

    Adam Jacob wrote:
    After much discussion, I've unleased Catalyst::Action::REST upon an
    unsuspecting CPAN. It's an attempt at making RESTful web applications
    in Catalyst easier (they were already pretty easy.) The basics:

    1) Uses an Action class to extend Catalyst's dispatch mechanism to allow
    for different methods based on the HTTP Method. For example:

    sub foo :Local :ActionClass('REST') {}

    sub foo_GET {}

    sub foo_POST {}

    sub foo_DELETE {}
    Eek. Is this related to this in any way?
    http://marcusramberg.livejournal.com/37104.html


    Hopefully this module will change when that becomes reality.

    -=Chris

    -------------- next part --------------
    A non-text attachment was scrubbed...
    Name: signature.asc
    Type: application/pgp-signature
    Size: 189 bytes
    Desc: OpenPGP digital signature
    Url : http://lists.scsys.co.uk/pipermail/catalyst/attachments/20061120/8ed7b2e3/signature.pgp
  • Aristotle Pagaltzis at Nov 20, 2006 at 2:10 pm

    * Christopher H. Laco [2006-11-20 15:00]:
    Adam Jacob wrote:
    For example:

    sub foo :Local :ActionClass('REST') {}

    sub foo_GET {}

    sub foo_POST {}

    sub foo_DELETE {}
    Eek. Is this related to this in any way?
    http://marcusramberg.livejournal.com/37104.html
    I have to say I like the syntax Marcus proposes much better than
    the above.

    Regards,
    --
    Aristotle Pagaltzis // <http://plasmasturm.org/>
  • Bogdan Lucaciu at Nov 20, 2006 at 2:17 pm

    On Monday 20 November 2006 16:10, A. Pagaltzis wrote:
    I have to say I like the syntax Marcus proposes much better than
    the above.
    I think it's more than just a proposal

    http://dev.catalyst.perl.org/changeset/5551

    --
    Bogdan Lucaciu
    http://www.wiz.ro
  • Matt S Trout at Nov 20, 2006 at 2:19 pm

    On 20 Nov 2006, at 14:10, A. Pagaltzis wrote:

    * Christopher H. Laco [2006-11-20 15:00]:
    Adam Jacob wrote:
    For example:

    sub foo :Local :ActionClass('REST') {}

    sub foo_GET {}

    sub foo_POST {}

    sub foo_DELETE {}
    Eek. Is this related to this in any way?
    http://marcusramberg.livejournal.com/37104.html
    I have to say I like the syntax Marcus proposes much better than
    the above.
    Feel free to propose a variant on the syntax marcus proposed that
    will allow the implementation of equivalent functionality without the
    addition of multimethods to the perl core.

    Until then, I'm voting for the way it's currently done :)

    --
    Matt S Trout, Technical Director, Shadowcat Systems Ltd.
    Offering custom development, consultancy and support contracts for
    Catalyst,
    DBIx::Class and BAST. Contact mst (at) shadowcatsystems.co.uk for
    details.
    + Help us build a better perl ORM: http://dbix-
    class.shadowcatsystems.co.uk/ +
  • Aristotle Pagaltzis at Nov 20, 2006 at 3:02 pm

    * Matt S Trout [2006-11-20 15:25]:
    Feel free to propose a variant on the syntax marcus proposed
    that will allow the implementation of equivalent functionality
    without the addition of multimethods to the perl core.
    Oh! Now I feel silly.

    Eh.

    OK, to lay out my own thinking a little, when I was doing my
    silly PATHINFO-based hack-up of CGI::Prototype::Hidden, the plan
    eventually became to have one class per handler, with methods
    named after, err, methods, eg.:

    sub GET { ... }
    sub POST { ... }
    sub HEAD { ... }

    I still think that?s cleanest approach.

    Catalyst as it stands somewhat encourages a confusion between
    nouns (URIs) and verbs (methods), with URIs like
    `/entry/1234/comments/add`, where the `/entry/1234/comments` part
    identifies a resource, but the `/add` bit at the end is really an
    verb. That should simply be a POST to `/entry/1234/comments`. And
    most of the time, if the design is RESTful from the start, you
    can implement a web app as pure CRUD using the HTTP methods; eg.
    the methods in a controller should simply correspond 1:1 to HTTP
    verbs. That was as far as I had gotten my own thoughts.

    Of course, there are going to be some cases where that isn?t
    quite enough; for these, you could have the option of adding
    methods that work as if they were custom invented HTTP methods.
    The framework could permit tunneling unsupported methods inside
    POST requests, which it would automatically unravel before
    dispatching. The Rails guys were doing something along these
    lines; I have to take another look sometime.

    What I don?t like about the current proposals in Catalyst-land
    is that they make the RESTful approach a wordy non-default
    option. It takes quite a bit of extra annotation with any of
    them, even if each annotation is concise. It ends easier to just
    put verbs in your URIs, where it should be just as easy to do it
    properly.

    I guess this would be a special kind of controller base class?
    I should really take the time out to do a deep dive on Catalyst
    so I can sort out its taxonomy in my head and hack out some
    actual code?

    Regards,
    --
    Aristotle Pagaltzis // <http://plasmasturm.org/>
  • John Napiorkowski at Nov 20, 2006 at 3:24 pm

    --- "A. Pagaltzis" wrote:

    * Matt S Trout [2006-11-20
    15:25]:
    Feel free to propose a variant on the syntax
    marcus proposed
    that will allow the implementation of equivalent
    functionality
    without the addition of multimethods to the perl
    core.

    Oh! Now I feel silly.

    Eh.

    OK, to lay out my own thinking a little, when I was
    doing my
    silly PATHINFO-based hack-up of
    CGI::Prototype::Hidden, the plan
    eventually became to have one class per handler,
    with methods
    named after, err, methods, eg.:

    sub GET { ... }
    sub POST { ... }
    sub HEAD { ... }

    I still think that???s cleanest approach.

    Catalyst as it stands somewhat encourages a
    confusion between
    nouns (URIs) and verbs (methods), with URIs like
    `/entry/1234/comments/add`, where the
    `/entry/1234/comments` part
    identifies a resource, but the `/add` bit at the end
    is really an
    verb. That should simply be a POST to
    `/entry/1234/comments`. And
    most of the time, if the design is RESTful from the
    start, you
    can implement a web app as pure CRUD using the HTTP
    methods; eg.
    the methods in a controller should simply correspond
    1:1 to HTTP
    verbs. That was as far as I had gotten my own
    thoughts.
    One this that I think works really well in catalyst is
    how easy it it so divide your private names from the
    public urls. So you can do:

    Package myapp::Controller::foo

    __PACKAGE__->_action_class('Custom::Rest');

    sub foos :Chained('/') PathPart('') CaptureArgs(0) {}

    sub create :Chained('foos') PathPart('') Args(0)
    Method(POST) Method(PUT) Method(POST) {}

    sub foo :Chained('foos) PathPart('') CaptureArgs(1) {}

    sub update :Chained('foo') PathPart('') Args(0)
    Method(POST) {}

    sub delete :Chained('foo') PathPart('delete') Args(0)
    Method(POST) METHOD(DELETE) Method(GET) {}

    In the example above I loaded a custom action class to
    handle the Method Attributes. I end up having to
    support a verby delete URL because most user clients
    can't send an HTTP DELETE from a form in HTML, but
    that's just coping with reality.

    Maybe it get's a little verbose, but then I can have
    sane names for my templates, like 'foos/create,
    foos/delete, foos/update, etc'. Also it creates sane
    URL with
    $c->uri_for($c->controller('foos')->action_for('create'));

    Doing it this way gives you the noun oriented URI you
    spoke of, but keeps the actions as verbs inside your
    code.

    However I do find that with heavy use of action
    classes my controllers start to look rather empty :)

    Cheers, John
    Of course, there are going to be some cases where
    that isn???t
    quite enough; for these, you could have the option
    of adding
    methods that work as if they were custom invented
    HTTP methods.
    The framework could permit tunneling unsupported
    methods inside
    POST requests, which it would automatically unravel
    before
    dispatching. The Rails guys were doing something
    along these
    lines; I have to take another look sometime.

    What I don???t like about the current proposals in
    Catalyst-land
    is that they make the RESTful approach a wordy
    non-default
    option. It takes quite a bit of extra annotation
    with any of
    them, even if each annotation is concise. It ends
    easier to just
    put verbs in your URIs, where it should be just as
    easy to do it
    properly.

    I guess this would be a special kind of controller
    base class?
    I should really take the time out to do a deep dive
    on Catalyst
    so I can sort out its taxonomy in my head and hack
    out some
    actual code???

    Regards,
    --
    Aristotle Pagaltzis // <http://plasmasturm.org/>

    _______________________________________________
    List: [email protected]
    Listinfo:
    http://lists.rawmode.org/mailman/listinfo/catalyst
    Searchable archive:
    http://www.mail-archive.com/[email protected]/
    Dev site: http://dev.catalyst.perl.org/



    ____________________________________________________________________________________
    Sponsored Link

    $200,000 mortgage for $660/ mo -
    30/15 yr fixed, reduce debt -
    http://yahoo.ratemarketplace.com
  • Robert 'phaylon' Sedlacek at Nov 20, 2006 at 3:25 pm
    A. Pagaltzis said:
    Catalyst as it stands somewhat encourages a confusion between
    nouns (URIs) and verbs (methods), with URIs like
    `/entry/1234/comments/add`, where the `/entry/1234/comments` part
    identifies a resource, but the `/add` bit at the end is really an
    verb. That should simply be a POST to `/entry/1234/comments`. And
    most of the time, if the design is RESTful from the start, you
    can implement a web app as pure CRUD using the HTTP methods; eg.
    the methods in a controller should simply correspond 1:1 to HTTP
    verbs. That was as far as I had gotten my own thoughts.
    That works with the comment example, yes. But mostly I have stuff like

    /user - list of users (does GET and maybe HEAD)
    /user/add - interface to add users (does GET and POST)
    /user/123 - show user
    /user/123/edit - interface to edit user (does GET and POST)
    What I don't like about the current proposals in Catalyst-land
    is that they make the RESTful approach a wordy non-default
    option. It takes quite a bit of extra annotation with any of
    them, even if each annotation is concise. It ends easier to just
    put verbs in your URIs, where it should be just as easy to do it
    properly.
    That's why the docs on :Method aren't even speaking about REST:

    "C<Method> is another action modifier. It restricts this action
    to only match for requests with the specified method."
    -- <http://dev.catalyst.perl.org/changeset/5551>

    Although it might be a good idea to add an explicit note about this and
    point the user to CPAN or a InventedWheels page on the wiki.

    gr.,
    Robert

    --
    # Robert 'phaylon' Sedlacek
    # Perl 5/Catalyst Developer in Hamburg, Germany
    { EMail => ' [email protected] ', Web => ' http://474.at ' }
  • Adam Jacob at Nov 20, 2006 at 5:16 pm

    On Mon, Nov 20, 2006 at 04:02:29PM +0100, A. Pagaltzis wrote:
    OK, to lay out my own thinking a little, when I was doing my
    silly PATHINFO-based hack-up of CGI::Prototype::Hidden, the plan
    eventually became to have one class per handler, with methods
    named after, err, methods, eg.:

    sub GET { ... }
    sub POST { ... }
    sub HEAD { ... }

    I still think that?s cleanest approach.
    It's pretty close to the same thing. If you wanted to do this, you
    could still get the benefits of the auto-serialization parts of
    Catalyst::Action::Rest by having:

    sub begin :ActionClass('Deserialize') {}

    and

    sub end :ActionClass('Serialize') {}
    Catalyst as it stands somewhat encourages a confusion between
    nouns (URIs) and verbs (methods), with URIs like
    `/entry/1234/comments/add`, where the `/entry/1234/comments` part
    identifies a resource, but the `/add` bit at the end is really an
    verb. That should simply be a POST to `/entry/1234/comments`. And
    most of the time, if the design is RESTful from the start, you
    can implement a web app as pure CRUD using the HTTP methods; eg.
    the methods in a controller should simply correspond 1:1 to HTTP
    verbs. That was as far as I had gotten my own thoughts.
    I can see why that would be appealing. It would probably not be hard to
    add an option to Catalyst::Action::REST to do things that way. If you
    really wanted to do things like tunneling unsupported methods, that may
    be difficult. (From within Catalyst::Action::REST)

    Adam
  • Garrett Goebel at Nov 21, 2006 at 7:21 am

    On Nov 20, 2006, at 9:02 AM, A. Pagaltzis wrote:

    * Matt S Trout [2006-11-20 15:25]:
    Feel free to propose a variant on the syntax marcus proposed
    that will allow the implementation of equivalent functionality
    without the addition of multimethods to the perl core.
    Oh! Now I feel silly.

    Eh.

    OK, to lay out my own thinking a little, when I was doing my
    silly PATHINFO-based hack-up of CGI::Prototype::Hidden, the plan
    eventually became to have one class per handler, with methods
    named after, err, methods, eg.:

    sub GET { ... }
    sub POST { ... }
    sub HEAD { ... }

    I still think that?s cleanest approach.
    That's pretty much what I wound up doing. Except I was more focused
    on a RESTful rewrite of InstantCRUD to generate one controller class
    per table. It is very clean and simple. All my per-table controllers
    inherited from a single base controller which implemented actions for
    create, read, update, delete, edit, show, index.

    Catalyst as it stands somewhat encourages a confusion between
    nouns (URIs) and verbs (methods), with URIs like
    `/entry/1234/comments/add`, where the `/entry/1234/comments` part
    identifies a resource, but the `/add` bit at the end is really an
    verb. That should simply be a POST to `/entry/1234/comments`.
    I ended up disagreeing with the RoR folks and Audrey's REST work in
    Jifty. I think leaving the key unspecified is a bad idea. Sometimes a
    table has more than one primary key candidate. Or you may want to
    limit the result set based on field that isn't a primary key. What I
    implemented back around the same time as John Napiorkowski was
    working on the same thing was:

    /entry/id/1234/comments

    vs.

    /entry/surname,given_name/Pagaltzis,Aristotle/comments

    vs.

    /entry/status/open

    And
    most of the time, if the design is RESTful from the start, you
    can implement a web app as pure CRUD using the HTTP methods; eg.
    the methods in a controller should simply correspond 1:1 to HTTP
    verbs. That was as far as I had gotten my own thoughts.

    Of course, there are going to be some cases where that isn?t
    quite enough; for these, you could have the option of adding
    methods that work as if they were custom invented HTTP methods.
    The framework could permit tunneling unsupported methods inside
    POST requests, which it would automatically unravel before
    dispatching. The Rails guys were doing something along these
    lines; I have to take another look sometime.
    I tunneled PUT and DELETE inside a POST via a "_method" body
    parameter, which was automatically unravelled before dispatch within
    an overridden Catalyst::prepare_body_parameters method.

    I never did get around to supporting all the HTTP methods plus custom
    extensions. I do agree that the :Method(...) style syntax is the way
    to go.

    What I don?t like about the current proposals in Catalyst-land
    is that they make the RESTful approach a wordy non-default
    option. It takes quite a bit of extra annotation with any of
    them, even if each annotation is concise. It ends easier to just
    put verbs in your URIs, where it should be just as easy to do it
    properly.
    I'm not sure I follow you. RESTful uri's end up being shorter. I
    suppose you're talking about the pain associated with implementing a
    RESTful interface within a catalyst app.

    The annoying problem that I ran up against was:

    1) Catalyst is designed with the assumption of dispatch based on
    first match instead of best match. I.e. Catalyst::Dispatcher short
    circuits on the first dispatch type attribute which matches. So side
    effects in the dispatch type's ->match method aren't guaranteed if
    multiple dispatch type parameters were specified.

    2) All dispatch types are based on the request's path. If you want to
    dispatch based on additional criterion, you're supposed to bury the
    dispatch match logic in action classes.

    Fast is good and well, but I'd rather be correct than fast.

    I ended up subclassing the Catalyst::Dispatcher to make
    prepare_action match all the dispatch types attributes specified for
    an action. This allowed me to move my dispatch logic out of action
    classes and into dispatch types where IMHO they belong.

    I guess this would be a special kind of controller base class?
    yep.

    I should really take the time out to do a deep dive on Catalyst
    so I can sort out its taxonomy in my head and hack out some
    actual code?
    yep.

    Almost all my free time comes in 5-15 minute spans of time, or at the
    expense of sleep. This is something I'd like to work on further, but
    I just don't see the time being available any time soon. I'm envious
    of you folks with enough time on your hands to dig in deep and
    actually do something ;)

    cheers,

    Garrett
  • Matt S Trout at Nov 21, 2006 at 3:16 pm

    Garrett Goebel wrote:
    On Nov 20, 2006, at 9:02 AM, A. Pagaltzis wrote:

    * Matt S Trout [2006-11-20 15:25]:
    Feel free to propose a variant on the syntax marcus proposed
    that will allow the implementation of equivalent functionality
    without the addition of multimethods to the perl core.
    Oh! Now I feel silly.

    Eh.

    OK, to lay out my own thinking a little, when I was doing my
    silly PATHINFO-based hack-up of CGI::Prototype::Hidden, the plan
    eventually became to have one class per handler, with methods
    named after, err, methods, eg.:

    sub GET { ... }
    sub POST { ... }
    sub HEAD { ... }

    I still think that?s cleanest approach.
    That's pretty much what I wound up doing. Except I was more focused on a
    RESTful rewrite of InstantCRUD to generate one controller class per
    table. It is very clean and simple. All my per-table controllers
    inherited from a single base controller which implemented actions for
    create, read, update, delete, edit, show, index.
    This is basically how Reaction handles CRUD
    Catalyst as it stands somewhat encourages a confusion between
    nouns (URIs) and verbs (methods), with URIs like
    `/entry/1234/comments/add`, where the `/entry/1234/comments` part
    identifies a resource, but the `/add` bit at the end is really an
    verb. That should simply be a POST to `/entry/1234/comments`.
    I ended up disagreeing with the RoR folks and Audrey's REST work in
    Jifty. I think leaving the key unspecified is a bad idea. Sometimes a
    table has more than one primary key candidate. Or you may want to limit
    the result set based on field that isn't a primary key. What I
    implemented back around the same time as John Napiorkowski was working
    on the same thing was:

    /entry/id/1234/comments

    vs.

    /entry/surname,given_name/Pagaltzis,Aristotle/comments

    vs.

    /entry/status/open
    Again, pretty much exactly how Reaction handles it, except that I've shied
    away from dealing with multi-col PKs -just- yet :)
    And
    most of the time, if the design is RESTful from the start, you
    can implement a web app as pure CRUD using the HTTP methods; eg.
    the methods in a controller should simply correspond 1:1 to HTTP
    verbs. That was as far as I had gotten my own thoughts.

    Of course, there are going to be some cases where that isn?t
    quite enough; for these, you could have the option of adding
    methods that work as if they were custom invented HTTP methods.
    The framework could permit tunneling unsupported methods inside
    POST requests, which it would automatically unravel before
    dispatching. The Rails guys were doing something along these
    lines; I have to take another look sometime.
    I tunneled PUT and DELETE inside a POST via a "_method" body parameter,
    which was automatically unravelled before dispatch within an overridden
    Catalyst::prepare_body_parameters method.
    Clever. Would you be willing to separate that out as a plugin with a
    configurable parameter name? I think that might be well-received (and actually
    something that *should* be a plugin for once :)
    What I don?t like about the current proposals in Catalyst-land
    is that they make the RESTful approach a wordy non-default
    option. It takes quite a bit of extra annotation with any of
    them, even if each annotation is concise. It ends easier to just
    put verbs in your URIs, where it should be just as easy to do it
    properly.
    I'm not sure I follow you. RESTful uri's end up being shorter. I suppose
    you're talking about the pain associated with implementing a RESTful
    interface within a catalyst app.

    The annoying problem that I ran up against was:

    1) Catalyst is designed with the assumption of dispatch based on first
    match instead of best match. I.e. Catalyst::Dispatcher short circuits on
    the first dispatch type attribute which matches. So side effects in the
    dispatch type's ->match method aren't guaranteed if multiple dispatch
    type parameters were specified.
    It's not -exactly- first match, more an implicit assumption that the match of
    the most path parts is the best match - I effectively consider the current
    dispatcher to be half refactored from the 5.0 implementation, but haven't yet
    found a design for the second half I consider satisfactory.
    2) All dispatch types are based on the request's path. If you want to
    dispatch based on additional criterion, you're supposed to bury the
    dispatch match logic in action classes.
    You seem to consider dispatching on the path above all to be a bad thing; I
    don't quite understand this since the nature of HTTP/REST is that the URI
    identifies the entity and -then- the query parameters are applied, if present,
    representing a search on that entity, and only then is the method (and body if
    any) applied to the entity resulting.
    Fast is good and well, but I'd rather be correct than fast.
    That's nice, but mapping path -> entity first *is* correct, at least for
    implementing REST :)
    I ended up subclassing the Catalyst::Dispatcher to make prepare_action
    match all the dispatch types attributes specified for an action. This
    allowed me to move my dispatch logic out of action classes and into
    dispatch types where IMHO they belong.

    I think what we really need to do is re-think the dispatch logic somewhat -
    entity, then search, then apply method+body is definitely correct, but
    matching the action first (and having the actions be singleton-per-app) is
    almost certainly wrong.

    What I think we really want to be doing is something like Chained, but where
    the controllers rather than being a singleton-per-app are instantiated
    per-request, so

    POST /foo/id/1/bar/id/3?baz=quux would do something like

    my $foo_c = Controller::Foo->new(id => 1, parent => $app);

    my $bar_c = Controller::Bar->new(id => 3, parent => $foo_c);

    $bar_c->apply_search(baz => 'quux');

    $bar_c->POST($body);

    or similar. I don't think the above is -quite- right but I think it -is-
    closer than how things currently work.
  • Garrett Goebel at Nov 22, 2006 at 3:44 am

    On Nov 21, 2006, at 9:16 AM, Matt S Trout wrote:

    Garrett Goebel wrote:
    On Nov 20, 2006, at 9:02 AM, A. Pagaltzis wrote:

    Of course, there are going to be some cases where that isn?t
    quite enough; for these, you could have the option of adding
    methods that work as if they were custom invented HTTP methods.
    The framework could permit tunneling unsupported methods inside
    POST requests, which it would automatically unravel before
    dispatching. The Rails guys were doing something along these
    lines; I have to take another look sometime.
    I tunneled PUT and DELETE inside a POST via a "_method" body
    parameter, which was automatically unravelled before dispatch
    within an overridden Catalyst::prepare_body_parameters method.
    Clever. Would you be willing to separate that out as a plugin with
    a configurable parameter name? I think that might be well-received
    (and actually something that *should* be a plugin for once :)
    I'll see what I can do. I'll be off the grid over the Thanksgiving
    holidays. But I'll take my laptop with me. There's a good chance I'll
    have the time, but with 4 kids under the age of 10, I can't make any
    promises ;)

    cheers,

    Garrett
  • Jonathan Rockway at Nov 22, 2006 at 4:46 pm

    Garrett Goebel wrote:
    I'll be off the grid over the Thanksgiving
    holidays. But I'll take my laptop with me.
    svk++

    --
    package JAPH;use Catalyst qw/-Debug/;($;=JAPH)->config(name => do {
    $,.=reverse qw[Jonathan tsu rehton lre rekca Rockway][$_].[split //,
    ";$;"]->[$_].q; ;for 1..4;$,=~s;^.;;;$,});$;->setup;
  • Garrett Goebel at Nov 27, 2006 at 12:25 pm

    On Nov 21, 2006, at 9:44 PM, Garrett Goebel wrote:
    On Nov 21, 2006, at 9:16 AM, Matt S Trout wrote:
    Garrett Goebel wrote:
    I tunneled PUT and DELETE inside a POST via a "_method" body
    parameter, which was automatically unravelled before dispatch
    within an overridden Catalyst::prepare_body_parameters method.
    Clever. Would you be willing to separate that out as a plugin with
    a configurable parameter name? I think that might be well-received
    (and actually something that *should* be a plugin for once :)
    I'll see what I can do. I'll be off the grid over the Thanksgiving
    holidays. But I'll take my laptop with me. There's a good chance
    I'll have the time, but with 4 kids under the age of 10, I can't
    make any promises ;)
    On the ride home from the holidays, I managed to throw together my
    first catalyst plugin module. That was, the easy part. Now I need to
    write the tests and package it up for CPAN consumption.


    I'd like to invite criticism and comments now, before changes get
    more painful.

    For starters:

    Is there a better package name?

    Should I be inheriting from Catalyst::Base or Catalyst::Component?
    The documentation says to use the former, but from scanning the code,
    it looks like I only need the latter. On this one, I've stuck with
    the documentation.

    Should I be using Class::C3 for ->next::method instead of -
    NEXT::method? There are quite a few caveats in the Class::C3
    documentation about not playing well with SUPER, but no mention of
    how well it plays with NEXT.

    Should I be using $c->config->{'Catalyst::Plugin::Request::Method'}?
    The documentation seems to indicate this, but the existing plugin
    modules seem to prefer shorter keys. Here I went with the preference
    for shorter keys that seemed evident in the plugin modules I scanned.
    Easy enough to reverse.


    package Catalyst::Plugin::Request::Method;
    use 5.008;
    use strict;
    use warnings;
    use version; our $VERSION = qv('0.0.1');
    use base qw(Catalyst::Base);
    use NEXT;

    sub setup {
    my $c = shift @_;

    $c->NEXT::setup(@_);

    my $cfg = ( $c->config->{'request_method'} ||= {} );
    $cfg->{tunnel_param} ||= '_method';

    return $cfg;
    }

    sub prepare_body_parameters {
    my $c = shift @_;
    my $req = $c->request;

    $c->NEXT::prepare_body_parameters(@_);

    my $tunnel_param = $c->config->{'request_method'}->
    {'tunnel_param'};

    # Allow POST to masquerade as other request method via
    body_parameter
    if ($req->method eq 'POST' && exists $req->parameters->
    {$tunnel_param}) {
    my $request_method = $req->parameters->{$tunnel_param};
    $req->method($request_method);
    delete $req->parameters->{$tunnel_param};
    }

    1;
    }

    1;

    __END__
  • Matt S Trout at Nov 27, 2006 at 4:01 pm

    Garrett Goebel wrote:
    On Nov 21, 2006, at 9:44 PM, Garrett Goebel wrote:
    On Nov 21, 2006, at 9:16 AM, Matt S Trout wrote:
    Garrett Goebel wrote:
    I tunneled PUT and DELETE inside a POST via a "_method" body
    parameter, which was automatically unravelled before dispatch within
    an overridden Catalyst::prepare_body_parameters method.
    Clever. Would you be willing to separate that out as a plugin with a
    configurable parameter name? I think that might be well-received (and
    actually something that *should* be a plugin for once :)
    I'll see what I can do. I'll be off the grid over the Thanksgiving
    holidays. But I'll take my laptop with me. There's a good chance I'll
    have the time, but with 4 kids under the age of 10, I can't make any
    promises ;)
    On the ride home from the holidays, I managed to throw together my first
    catalyst plugin module. That was, the easy part. Now I need to write the
    tests and package it up for CPAN consumption.


    I'd like to invite criticism and comments now, before changes get more
    painful.

    For starters:

    Is there a better package name?

    Should I be inheriting from Catalyst::Base or Catalyst::Component? The
    documentation says to use the former, but from scanning the code, it
    looks like I only need the latter. On this one, I've stuck with the
    documentation.
    For a plugin, neither most likely.
    Should I be using Class::C3 for ->next::method instead of
    ->NEXT::method? There are quite a few caveats in the Class::C3
    documentation about not playing well with SUPER, but no mention of how
    well it plays with NEXT.
    Much though I despise it, probably NEXT
    Should I be using $c->config->{'Catalyst::Plugin::Request::Method'}? The
    documentation seems to indicate this, but the existing plugin modules
    seem to prefer shorter keys. Here I went with the preference for shorter
    keys that seemed evident in the plugin modules I scanned. Easy enough to
    reverse.
    Acceptable.
  • Matt S Trout at Nov 20, 2006 at 2:17 pm

    On 20 Nov 2006, at 13:47, Christopher H. Laco wrote:

    Adam Jacob wrote:
    After much discussion, I've unleased Catalyst::Action::REST upon an
    unsuspecting CPAN. It's an attempt at making RESTful web
    applications
    in Catalyst easier (they were already pretty easy.) The basics:

    1) Uses an Action class to extend Catalyst's dispatch mechanism to
    allow
    for different methods based on the HTTP Method. For example:

    sub foo :Local :ActionClass('REST') {}

    sub foo_GET {}

    sub foo_POST {}

    sub foo_DELETE {}
    Eek. Is this related to this in any way?
    http://marcusramberg.livejournal.com/37104.html


    Hopefully this module will change when that becomes reality.
    No, I don't believe it will - or at least I hope it won't.

    The syntax marcus proposes is likely to be in core as a convenience
    for lightweight HTTP method restricting as of 5.80.

    Catalyst::Action::REST is designed to be a full REST handler, and as
    such needs the slightly more wordy syntax in order to fulfill its
    rather more extensive function.

    --
    Matt S Trout, Technical Director, Shadowcat Systems Ltd.
    Offering custom development, consultancy and support contracts for
    Catalyst,
    DBIx::Class and BAST. Contact mst (at) shadowcatsystems.co.uk for
    details.
    + Help us build a better perl ORM: http://dbix-
    class.shadowcatsystems.co.uk/ +
  • Christopher H. Laco at Nov 20, 2006 at 2:21 pm

    Matt S Trout wrote:
    On 20 Nov 2006, at 13:47, Christopher H. Laco wrote:

    Adam Jacob wrote:
    After much discussion, I've unleased Catalyst::Action::REST upon an
    unsuspecting CPAN. It's an attempt at making RESTful web applications
    in Catalyst easier (they were already pretty easy.) The basics:

    1) Uses an Action class to extend Catalyst's dispatch mechanism to allow
    for different methods based on the HTTP Method. For example:

    sub foo :Local :ActionClass('REST') {}

    sub foo_GET {}

    sub foo_POST {}

    sub foo_DELETE {}
    Eek. Is this related to this in any way?
    http://marcusramberg.livejournal.com/37104.html


    Hopefully this module will change when that becomes reality.
    No, I don't believe it will - or at least I hope it won't.

    The syntax marcus proposes is likely to be in core as a convenience for
    lightweight HTTP method restricting as of 5.80.

    Catalyst::Action::REST is designed to be a full REST handler, and as
    such needs the slightly more wordy syntax in order to fulfill its rather
    more extensive function.

    --Matt S Trout, Technical Director, Shadowcat Systems Ltd.
    Offering custom development, consultancy and support contracts for
    Catalyst,
    DBIx::Class and BAST. Contact mst (at) shadowcatsystems.co.uk for details.
    + Help us build a better perl ORM:
    http://dbix-class.shadowcatsystems.co.uk/ +

    Can't the two get along? Having to do _GET seems silly if/when the core
    actually has a Method('POST') attribute...

    -=Chris

    -------------- next part --------------
    A non-text attachment was scrubbed...
    Name: signature.asc
    Type: application/pgp-signature
    Size: 189 bytes
    Desc: OpenPGP digital signature
    Url : http://lists.scsys.co.uk/pipermail/catalyst/attachments/20061120/fc98b2f5/signature.pgp
  • Christopher H. Laco at Nov 20, 2006 at 2:24 pm

    Christopher H. Laco wrote:
    Matt S Trout wrote:
    On 20 Nov 2006, at 13:47, Christopher H. Laco wrote:

    Adam Jacob wrote:
    After much discussion, I've unleased Catalyst::Action::REST upon an
    unsuspecting CPAN. It's an attempt at making RESTful web applications
    in Catalyst easier (they were already pretty easy.) The basics:

    1) Uses an Action class to extend Catalyst's dispatch mechanism to allow
    for different methods based on the HTTP Method. For example:

    sub foo :Local :ActionClass('REST') {}

    sub foo_GET {}

    sub foo_POST {}

    sub foo_DELETE {}
    Eek. Is this related to this in any way?
    http://marcusramberg.livejournal.com/37104.html


    Hopefully this module will change when that becomes reality.
    No, I don't believe it will - or at least I hope it won't.

    The syntax marcus proposes is likely to be in core as a convenience for
    lightweight HTTP method restricting as of 5.80.

    Catalyst::Action::REST is designed to be a full REST handler, and as
    such needs the slightly more wordy syntax in order to fulfill its rather
    more extensive function.

    --Matt S Trout, Technical Director, Shadowcat Systems Ltd.
    Offering custom development, consultancy and support contracts for
    Catalyst,
    DBIx::Class and BAST. Contact mst (at) shadowcatsystems.co.uk for details.
    + Help us build a better perl ORM:
    http://dbix-class.shadowcatsystems.co.uk/ +

    Can't the two get along? Having to do _GET seems silly if/when the core
    actually has a Method('POST') attribute...

    -=Chris
    Sigh. ENOCOFFEEYET

    _GET ... Method('GET')

    -------------- next part --------------
    A non-text attachment was scrubbed...
    Name: signature.asc
    Type: application/pgp-signature
    Size: 189 bytes
    Desc: OpenPGP digital signature
    Url : http://lists.scsys.co.uk/pipermail/catalyst/attachments/20061120/38a622dd/signature.pgp
  • John Napiorkowski at Nov 20, 2006 at 2:58 pm

    --- "Christopher H. Laco" wrote:

    Adam Jacob wrote:
    After much discussion, I've unleased
    Catalyst::Action::REST upon an
    unsuspecting CPAN. It's an attempt at making
    RESTful web applications
    in Catalyst easier (they were already pretty
    easy.) The basics:
    1) Uses an Action class to extend Catalyst's
    dispatch mechanism to allow
    for different methods based on the HTTP Method.
    For example:
    sub foo :Local :ActionClass('REST') {}

    sub foo_GET {}

    sub foo_POST {}

    sub foo_DELETE {}
    Eek. Is this related to this in any way?
    http://marcusramberg.livejournal.com/37104.html


    Hopefully this module will change when that becomes
    reality.
    I actually wrote something that supported this syntax
    a few months ago pushed it to the mailing list for
    comments but it didn't seem that anyone was
    interested. Maybe we'll prefer this syntax instead?
    Anyway, attached is what I wrote, it let's you match
    on multiple method types like:

    sub catchall : Local
    ActionClass('Rest')
    Method('PUT')
    Method('POST')
    {}

    I had intended to do more with this, like to enable
    some sort of automatic in/deflation of xml objects
    based on the incoming message body, but got
    sidetracked on some paying projects.

    Maybe we could combine efforts?

    --john
    -=Chris
    _______________________________________________
    List: [email protected]
    Listinfo:
    http://lists.rawmode.org/mailman/listinfo/catalyst
    Searchable archive:
    http://www.mail-archive.com/[email protected]/
    Dev site: http://dev.catalyst.perl.org/


    ____________________________________________________________________________________
    The all-new Yahoo! Mail beta
    Fire up a more powerful email and get things done faster.
    http://new.mail.yahoo.com
    -------------- next part --------------
    A non-text attachment was scrubbed...
    Name: Rest.pm
    Type: application/octet-stream
    Size: 2043 bytes
    Desc: 3911937034-Rest.pm
    Url : http://lists.scsys.co.uk/pipermail/catalyst/attachments/20061120/74e6930d/Rest-0001.obj
  • Adam Jacob at Nov 20, 2006 at 5:20 pm

    On Mon, Nov 20, 2006 at 06:58:30AM -0800, John Napiorkowski wrote:
    sub catchall : Local
    ActionClass('Rest')
    Method('PUT')
    Method('POST')
    {}

    I had intended to do more with this, like to enable
    some sort of automatic in/deflation of xml objects
    based on the incoming message body, but got
    sidetracked on some paying projects.

    Maybe we could combine efforts?
    There is no reason you can't already. The automatic inflation/deflation
    is already there. If you prefer this mechanism, you can just not use
    ActionClass('REST') and go about your way.

    I would love XML serialization support. Patches, commit bits, and CPAN
    privileges are but awaiting your submission. :)

    Adam
  • Adam Jacob at Nov 20, 2006 at 5:05 pm

    On Mon, Nov 20, 2006 at 08:47:17AM -0500, Christopher H. Laco wrote:
    Eek. Is this related to this in any way?
    http://marcusramberg.livejournal.com/37104.html

    Hopefully this module will change when that becomes reality.
    You can do both. The Serialization parts have nothing to do
    with the Dispatching parts, so if you wanted to dispatch with appending
    Method(POST) you can.

    use Catalyst::Controller::REST;

    sub foo: Chained CaptureArgs(1) {
    setup_widget();
    }

    sub show: Chained('foo') PathPart('') Method(GET) {
    display_form();
    }

    sub proc: Chained('foo') PathPart('') Method(POST) {
    process_form();
    }

    Would be basically equivilant to:

    use Catalyst::Controller::REST;

    sub foo: Chained CaptureArgs(1) {
    setup_widget();
    }

    sub handle_foo: Chained('foo') PathPart('') ActionClass('REST') {}

    sub handle_foo_GET {
    display_form();
    }

    sub handle_foo_POST {
    process_form();
    }

    We talked about implementing things that way, with Method(POST) and
    friends. The reason we decided not to was that we preferred keeping the
    same base sub name, and a single place to declare the action for
    dispatching.

    At least for doing REST, it felt pretty intuitive to have a single place
    where you tell the dispatcher you care about some entity, and then what
    HTTP methods that entity will accept.

    Additionally, using the Method(POST) mechanism would make it very
    difficult to have things like the automatic 405 Not Implemented
    handlers.

    But it's perl, so TMTOWTDI.

    Adam
  • Bill Moseley at Nov 20, 2006 at 6:48 pm

    On Mon, Nov 20, 2006 at 09:05:14AM -0800, Adam Jacob wrote:
    use Catalyst::Controller::REST;

    sub foo: Chained CaptureArgs(1) {
    setup_widget();
    }

    sub handle_foo: Chained('foo') PathPart('') ActionClass('REST') {}

    sub handle_foo_GET {
    display_form();
    }

    sub handle_foo_POST {
    process_form();
    }
    So what does that look like in real life? What if process_form() fails
    validation?

    My urls look like this:

    /user - list users
    /user/edit/1234 - edit/update that specific user.
    /user/edit - create user
    /user/delete/1233

    My create, edit, and update all use the same interface (form) so seems
    to make sense that all those actions should use the same controller.
    It also means my template can be set automatically based on the
    controller's action name.



    --
    Bill Moseley
    [email protected]
  • Adam Jacob at Nov 20, 2006 at 7:48 pm

    On Mon, Nov 20, 2006 at 10:48:54AM -0800, Bill Moseley wrote:
    So what does that look like in real life? What if process_form() fails
    validation?

    My urls look like this:

    /user - list users
    /user/edit/1234 - edit/update that specific user.
    /user/edit - create user
    /user/delete/1233

    My create, edit, and update all use the same interface (form) so seems
    to make sense that all those actions should use the same controller.
    It also means my template can be set automatically based on the
    controller's action name.
    For the sake of argument, lets say you want to expose this as a REST
    service. We have a few things to define:

    1) A "user" is a resource, and they are accessable through an URL.
    2) Actions on a resource are determined by the HTTP Method I ask for.
    3) Most operations can be seen as a transform of the data from another.
    For example, updating a User can usually be done by just changing the
    data structure returned from "GET"-ing the user.

    A Sample controller would look like this (this is off the top of my
    head, so it may or may not work):

    ## Code begins ##
    package Foo::Controller::User;
    use base 'Catalyst::Controller::REST';

    sub user_list :Index :ActionClass('REST') { }

    sub user_list_GET {
    my ($self, $c) = @_;

    my @users = $c->model('User')->list; # Made this up

    # Returns a 200 OK with the Serialized Array
    $self->status_ok($c, entity => \@users);
    }

    sub single_user :PathPart('user') :Chained :Args(1) :ActionClass('REST')
    {}

    sub single_user_GET {
    my ($self, $c, $user_id) = @_;

    my $user = $c->model('User')->find($user_id);
    if (defined($user)) {
    $self->status_ok($c, entity => $user);
    } else {
    $self->status_not_found($c, message => "I could not find User $user_id");
    }
    }

    sub single_user_PUT {
    my ($self, $c, $user_id) = @_;

    # The deserialized request is in $c->req->data
    my $user = $c->model('User')->create($user_id, $c->req->data);
    if (defined($user)) {
    $self->status_ok($c, entity => $user);
    } else {
    $self->status_bad_request($c, message => "Could not create $user_id");
    }
    }

    sub single_user_POST {
    my ($self, $c, $user_id) = @_;

    # The deserialized request is in $c->req->data
    my $user = $c->model('User')->update($user_id, $c->req->data);
    if (defined($user)) {
    $self->status_ok($c, entity => $user);
    } else {
    $self->status_bad_request($c, message => "Could not update $user_id");
    }
    }

    sub single_user_DELETE {
    my ($self, $c, $user_id) = @_;

    # The deserialized request is in $c->req->data
    my $user = $c->model('User')->delete($user_id);
    if (defined($user)) {
    $self->status_ok($c, entity => $user);
    } else {
    $self->status_bad_request($c, message => "Could not delete $user_id");
    }
    }
    ## Code ends ##

    That's the gist. It's not the sort of thing you would use to power your
    user-facing website; it's for building machine-useable web services.

    Adam
  • Robert 'phaylon' Sedlacek at Nov 20, 2006 at 7:50 pm

    Bill Moseley said:

    So what does that look like in real life? What if process_form() fails
    validation?
    Well, this is my plan. Although it's written with :Method syntax, but
    that's only a slight difference I assume.

    # ties this stuff to the rest of the controller. also does stuff
    # that auto did in the past.
    sub base: Chained PathPart('foo') CaptureArgs(0) { }

    # form setup
    sub form: Chained('base') PathPart('') CaptureArgs(0) {
    # setup form (e.g. H:W) here
    }

    # handles /foo/create on GET
    # display creation, set defaults in form
    sub create: Chained('form') Method('GET') { }

    # handles /foo/create on POST
    # try to store. form should contain submitted information. H:W can
    # do this easily for example
    sub do_create: Chained('form') PathPart('create') Method('POST') {
    # if valid, create and redirect
    # otherwise, display form again.
    }

    # loads model into stash. 404 should also be handled here-in, but
    # I'll spare us that.
    sub load: Chained('form') PathPart('') CaptureArgs(1) {
    my ($self, $c, $foo_id) = @_;
    my $foo :Stashed = $c->model('DBIC::Foo')->find($foo_id);
    }

    # handles /foo/123/edit on GET
    sub edit: Chained('load') Method('GET') {
    # fill form with data from my $foo :Stashed
    }

    # handles /foo/123/edit on POST
    sub save: Chained('load') Method('GET') {
    # if valid, store item and redirect
    # otherwise display form with submitted information again
    }

    # handles /foo and returns list of items
    sub list: Chained('base') PathPart('') Method('GET') { }

    # optimize HEAD requests
    sub head: Chained('base') PathPart('') Method('HEAD') { }

    To list the chains:
    /foo -> base, list|head
    /foo/123/edit -> base, form, load, edit|save
    /foo/create -> base, form, create|do_create

    Might look like many actions at first, but they do very little stuff. I'd
    probably also add some controller base magic to do a _NoForm attribute to
    tie to load when I don't need a form. A simple 'view' action for example.
    Well, ideally the widget system would know how to render as form _and_ as
    static display.

    gr.,
    Robert

    --
    # Robert 'phaylon' Sedlacek
    # Perl 5/Catalyst Developer in Hamburg, Germany
    { EMail => ' [email protected] ', Web => ' http://474.at ' }
  • Adam Jacob at Nov 20, 2006 at 8:08 pm

    On Mon, Nov 20, 2006 at 08:50:18PM +0100, Robert 'phaylon' Sedlacek wrote:
    Bill Moseley said:
    So what does that look like in real life? What if process_form() fails
    validation?
    Well, this is my plan. Although it's written with :Method syntax, but
    that's only a slight difference I assume.
    ..snip Method code..
    To list the chains:
    /foo -> base, list|head
    /foo/123/edit -> base, form, load, edit|save
    /foo/create -> base, form, create|do_create

    Might look like many actions at first, but they do very little stuff. I'd
    probably also add some controller base magic to do a _NoForm attribute to
    tie to load when I don't need a form. A simple 'view' action for example.
    Well, ideally the widget system would know how to render as form _and_ as
    static display.
    If you get to the point where you need to provide both a serialized
    interface (XML, YAML, JSON) look at using the
    Catalyst::Action::Serialized and ::Deserialized from the
    Catalyst::Action::REST distribution. Extending it to "do the right
    thing" with a text/html request is definetly a possibility. (Where
    "right thing" is probably just hand things to the default view)

    Adam
  • Robert 'phaylon' Sedlacek at Nov 20, 2006 at 9:03 pm
    Adam Jacob said:
    If you get to the point where you need to provide both a serialized
    interface (XML, YAML, JSON) look at using the
    Catalyst::Action::Serialized and ::Deserialized from the
    Catalyst::Action::REST distribution. Extending it to "do the right
    thing" with a text/html request is definetly a possibility. (Where
    "right thing" is probably just hand things to the default view)
    Yes. It all depends on what you want to do. I'd mostly prefer to have a
    separate backend (REST) and frontend (Method). So the actual "stuff" is
    restfully implemented and the method reactive stuff just "transforms" into
    html, html form, json, xml, etc.

    I'm still waiting for customers who want to pay to "do it right" :)

    gr.,
    Robert

    --
    # Robert 'phaylon' Sedlacek
    # Perl 5/Catalyst Developer in Hamburg, Germany
    { EMail => ' [email protected] ', Web => ' http://474.at ' }
  • Jonathan Rockway at Nov 20, 2006 at 4:43 pm

    Adam Jacob wrote:
    After much discussion, I've unleased Catalyst::Action::REST upon an
    unsuspecting CPAN. It's an attempt at making RESTful web applications
    in Catalyst easier (they were already pretty easy.) The basics:

    1) Uses an Action class to extend Catalyst's dispatch mechanism to allow
    for different methods based on the HTTP Method. For example:

    sub foo :Local :ActionClass('REST') {}

    sub foo_GET {}

    sub foo_POST {}

    sub foo_DELETE {}
    It just occurred to me (after reading Aristotle's post below), that it
    might be a good idea to provide verbs for dumb HTTP clients (web
    browsers). Example:

    sub foo :Local :ActionClass('REST') {}
    sub foo_DELETE {}

    This would create URIs:

    foo
    foo/delete

    so that a GET (or POST, which might be better in this case) request to
    foo/delete would do the same thing as a real DELETE request. This means
    that your AJAX and smart HTTP clients could do a DELETE /foo/bar, but
    you could still have a link in the browser to /foo/delete/bar.

    (Maybe foo/arg0/.../argn/delete would look nicer than
    foo/delete/arg0/.../argn ?)

    Also, is it currently possible to do something like this?

    sub foo :Local :ActionClass('REST'){
    my ($self, $c) = @_
    do_something_before_method_specific_action();
    $self->next::method(@_);
    do_something_after_method_specific_action();
    }

    As always, thanks for writing this. I look forward to using it for
    something ;)

    --
    package JAPH;use Catalyst qw/-Debug/;($;=JAPH)->config(name => do {
    $,.=reverse qw[Jonathan tsu rehton lre rekca Rockway][$_].[split //,
    ";$;"]->[$_].q; ;for 1..4;$,=~s;^.;;;$,});$;->setup;
  • John Napiorkowski at Nov 20, 2006 at 5:26 pm

    --- Jonathan Rockway wrote:

    Adam Jacob wrote:
    After much discussion, I've unleased
    Catalyst::Action::REST upon an
    unsuspecting CPAN. It's an attempt at making
    RESTful web applications
    in Catalyst easier (they were already pretty
    easy.) The basics:
    1) Uses an Action class to extend Catalyst's
    dispatch mechanism to allow
    for different methods based on the HTTP Method.
    For example:
    sub foo :Local :ActionClass('REST') {}

    sub foo_GET {}

    sub foo_POST {}

    sub foo_DELETE {}
    It just occurred to me (after reading Aristotle's
    post below), that it
    might be a good idea to provide verbs for dumb HTTP
    clients (web
    browsers). Example:

    sub foo :Local :ActionClass('REST') {}
    sub foo_DELETE {}

    This would create URIs:

    foo
    foo/delete

    so that a GET (or POST, which might be better in
    this case) request to
    foo/delete would do the same thing as a real DELETE
    request. This means
    that your AJAX and smart HTTP clients could do a
    DELETE /foo/bar, but
    you could still have a link in the browser to
    /foo/delete/bar.
    Some REST API providers use an HTTP line:

    X-HTTP-Method-Override: ....

    to deal with this issue. This is very ontentiousin
    the community however :)

    --john

    (Maybe foo/arg0/.../argn/delete would look nicer
    than
    foo/delete/arg0/.../argn ?)

    Also, is it currently possible to do something like
    this?

    sub foo :Local :ActionClass('REST'){
    my ($self, $c) = @_
    do_something_before_method_specific_action();
    $self->next::method(@_);
    do_something_after_method_specific_action();
    }

    As always, thanks for writing this. I look forward
    to using it for
    something ;)

    --
    package JAPH;use Catalyst
    qw/-Debug/;($;=JAPH)->config(name => do {
    $,.=reverse qw[Jonathan tsu rehton lre rekca
    Rockway][$_].[split //,
    ";$;"]->[$_].q; ;for 1..4;$,=~s;^.;;;$,});$;->setup;

    _______________________________________________
    List: [email protected]
    Listinfo:
    http://lists.rawmode.org/mailman/listinfo/catalyst
    Searchable archive:
    http://www.mail-archive.com/[email protected]/
    Dev site: http://dev.catalyst.perl.org/



    ____________________________________________________________________________________
    Sponsored Link

    Compare mortgage rates for today.
    Get up to 5 free quotes.
    Www2.nextag.com
  • Adam Jacob at Nov 20, 2006 at 5:27 pm

    On Mon, Nov 20, 2006 at 10:43:39AM -0600, Jonathan Rockway wrote:
    It just occurred to me (after reading Aristotle's post below), that it
    might be a good idea to provide verbs for dumb HTTP clients (web
    browsers). Example:

    sub foo :Local :ActionClass('REST') {}
    sub foo_DELETE {}

    This would create URIs:

    foo
    foo/delete

    so that a GET (or POST, which might be better in this case) request to
    foo/delete would do the same thing as a real DELETE request. This means
    that your AJAX and smart HTTP clients could do a DELETE /foo/bar, but
    you could still have a link in the browser to /foo/delete/bar.
    Wel, since most of the content you are sending is serialized, browsing
    it with Firefox just isn't particularly useful out of the box. If it
    was me, I would probably just roll an interface that called my service
    and did the right thing behind the scenes.

    Patches to turn that on with a config option would be neat, though.
    Doesn't feel very RESTful. :)
    Also, is it currently possible to do something like this?

    sub foo :Local :ActionClass('REST'){
    my ($self, $c) = @_
    do_something_before_method_specific_action();
    $self->next::method(@_);
    do_something_after_method_specific_action();
    }

    As always, thanks for writing this. I look forward to using it for
    something ;)
    No. The "real" sub foo only gets called if we don't have a more
    specific sub that matches, and it pre-populates the 405 response.

    Adam
  • Aristotle Pagaltzis at Nov 20, 2006 at 5:51 pm

    * Jonathan Rockway [2006-11-20 17:50]:
    It just occurred to me (after reading Aristotle's post below),
    that it might be a good idea to provide verbs for dumb HTTP
    clients (web browsers). Example:

    sub foo :Local :ActionClass('REST') {}
    sub foo_DELETE {}

    This would create URIs:

    foo
    foo/delete

    so that a GET (or POST, which might be better in this case)
    request to foo/delete would do the same thing as a real DELETE
    request.
    Yes, POST would be better. Accepting GET on such URIs will only
    lead to tears.
    This means that your AJAX and smart HTTP clients could do
    a DELETE /foo/bar, but you could still have a link in the
    browser to /foo/delete/bar.
    Exactly, except for the form of URI.
    (Maybe foo/arg0/.../argn/delete would look nicer than
    foo/delete/arg0/.../argn ?)
    I would prefer the first. The URI should be as nearly the same as
    possible. In fact I?d prefer to put the tunneled method in
    a query parameter: `POST /foo/bar?_http_methÞLETE`.

    Regards,
    --
    Aristotle Pagaltzis // <http://plasmasturm.org/>

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupcatalyst @
categoriescatalyst, perl
postedNov 20, '06 at 8:00a
activeNov 27, '06 at 4:01p
posts31
users11
websitecatalystframework.org
irc#catalyst

People

Translate

site design / logo © 2023 Grokbase