lifecycle hooks

17 views
Skip to first unread message

Thorsten von Eicken

unread,
Sep 7, 2014, 4:36:52 PM9/7/14
to praxis-...@googlegroups.com
Is there a standard place where to put lifecycle hooks that need to apply to all controllers? Like an application controller? Or where do I put them? (The docs don't suggest anything.) I can rig something up, but it might be nice to have a convention.

The scope of where the callbacks are hooked isn't very well explained. For example, the http://praxis-framework.io/reference/request-life-cycle/ page says "Passing no actions option is logically equivalent to passing every possible action", which I suspect really means "... every action of the controller".

Detail: the link to the Request Life Cycle page at the end of http://praxis-framework.io/reference/controllers/ is broken.

I'm unclear about where routing happens. It looks to me like routing happens before anything described in the "Request Life Cycle" page. So one enters the "lifecycle" having already identified a controller and an action? WOuld be great to add this to the page.

The doc also says "There is really no difference between after :validate and before :action since they are subsequent stages" which I hope is incorrect. I expect that all after-validate hooks execute before any before-action hook executes! It would generally be good to specify in which order the hooks get executed for the same stage, at least in Rails, I remember having multiple before-actions callbacks that depended on one another.


Thorsten von Eicken

unread,
Sep 7, 2014, 4:59:25 PM9/7/14
to praxis-...@googlegroups.com
Follow-on question: how does a callback abort request processing and return an error? For example, I am writing an authentication callback that needs to return a 403 if the auth fails or is missing.

I'm actually finding it non-trivial to define global callbacks. I'm trying to do the following:
class Instances
  include
Praxis::Controller
 
implements ApiResources::Instances
  include
GoogleCloudSQLMixin
 
...
end

module GoogleCloudSQLMixin
  include
Praxis::Controller
  before
:action do
   
...
 
end
end

And so far I haven't figured out a combo that works... The above fails because Praxis:Controller:ClassMethods expects to have a definition, which comes from the implements statement...

Thorsten von Eicken

unread,
Sep 7, 2014, 5:16:36 PM9/7/14
to praxis-...@googlegroups.com
A further snag is that I need to access some of the params of the request in my before filter (the auth stuff). I tried request.params.acct to access the account param, but the request object isn't available in the before filter, it seems:
  "message": "undefined local variable or method `request' for Instances:Class",
 What I tried is adding the following to the hello controller of the scaffold:
    before :action do
      puts
"The request is #{request.inspect}"
 
  end

Josep Blanquer

unread,
Sep 15, 2014, 4:37:52 PM9/15/14
to Thorsten von Eicken, praxis-...@googlegroups.com
Ok, so circling back to get these issues properly closed:

Last week we added a bunch of functionality related to that area. It's all in master, but no new gem has been cut yet. 
Here are the parts that pertain to your questions Thorsten:

re: "Is there a standard place where to put lifecycle hooks that need to apply to all controllers? Like an application controller? Or where do I put them? (The docs don't suggest anything.) I can rig something up, but it might be nice to have a convention."

So, there isn't a "single" place for you to add a before/after/around hook across all controllers. The way to do that is to define those hooks in an ActiveResource Concern and add them to any controller you want to share them with. 
See an example in our mini spec app: https://github.com/rightscale/praxis/tree/master/spec/spec_app/app/controllers where both the Instances and Volumes controllers include a base concern that has 2 shareable concerns inside (LogWrapper and Authenticated). 
Note that controller inheritance is not the answer to that as you'd be sharing more than you bargained for. See this test for more details on (how not to do that): https://github.com/rightscale/praxis/blob/master/spec/praxis/callbacks_spec.rb#L73-L106


re: The scope of where the callbacks are hooked isn't very well explained. For example, the http://praxis-framework.io/reference/request-life-cycle/ page says "Passing no actions option is logically equivalent to passing every possible action", which I suspect really means "... every action of the controller".

I've added the clarification to the docs.

re: Detail: the link to the Request Life Cycle page at the end of http://praxis-framework.io/reference/controllers/ is broken.

Fixed!

re: "I'm unclear about where routing happens. It looks to me like routing happens before anything described in the "Request Life Cycle" page. So one enters the "lifecycle" having already identified a controller and an action? WOuld be great to add this to the page."
 
I've added a blurb to the top of the page: "The first of these stages in the pipeline will only be invoked after the routing has 
been processed for the incoming request, and the appropriate controller and action has 
been identified. This means that currently, there is no way to affect the request routing dynamically."

re: "The doc also says "There is really no difference between after :validate and before :action since they are subsequent stages" which I hope is incorrect. I expect that all after-validate hooks execute before any before-action hook executes! It would generally be good to specify in which order the hooks get executed for the same stage, at least in Rails, I remember having multiple before-actions callbacks that depended on one another."

reworked the text:
"Technically speaking there is not much difference between `after :validate` and `before :action`
since they are subsequent stages. Semantically, however, they are different as all the 
`after :validate` callbacks will be executed before any of the `before :action` ones.
So you should really register the callback based on what stage you depend on, and not on neighboring stages. Otherwise, your code might stop functioning when the pipeline order is changed.
There is currently no mechanism to order the callbacks for a given stage. They will be executed
in the order that they were registered."

re: "Follow-on question: how does a callback abort request processing and return an error? For example, I am writing an authentication callback that needs to return a 403 if the auth fails or is missing."

Any callback (before/after/around) can abort the callback chain by returning a "Response-based" value.
That will stop the current chain. If it was a before filter, it will prevent the controller action to be invoked AND any after filters too.

re: "A further snag is that I need to access some of the params of the request in my before filter (the auth stuff). I tried request.params.acct to access the account param, but the request object isn't available in the before filter, it seems"

All callbacks are bound to the original binding you define them at. To get the context of the controller they're executed on, they take parameters.
The first parameter is the controller, so in your case above you'd need to do:

    before :action do |controller|
      puts 
"The request is #{controller.request.inspect}"
 
   end

The around filter, takes two parameters. The first one is still the controller, the second one is the proc to call to continue the chain. I still need to document `around` filters in the docs, but you can see some examples in our spec app: https://github.com/rightscale/praxis/blob/master/spec/spec_app/app/controllers/instances.rb#L18-L37



Allright, I think that is it. Thank you very much for the feedback, keep it coming!

Cheers,

Josep M.


--
You received this message because you are subscribed to the Google Groups "praxis-support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to praxis-suppor...@googlegroups.com.
To post to this group, send email to praxis-...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/praxis-support/8d7cbc52-fb21-41ae-a9c0-472b063d3c24%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages