Topic: Controller#action outside of a web request?

I will preface this by saying "I may be approaching the problem entirely wrong", so I will explain what I am attempting to do; followed by the way I'm doing it now and why it seems like I'm not doing it correctly:

I need to regularly upload XML files to a 3rd party (user data, once a day, to an e-mail service).

How I have set this up currently is:
I have a user controller with an emaildata action that returns the proper XML document. This controller/action requires authentication to access (because it's not intended for public consumption). I have a rake script that calls this action via http get request, downloads the xml file, and then uploads it to the 3rd party. I did it this way so the proper MVC stack is utilized - controller does its job, model has the data, view is where the view belongs etc. The cons to doing this are: this controller action requires a public route, I needed to set up special authentication for this route, and I tie up the webserver while this slow/large request happens.

Other options I looked at are having the script do something like:
1) app.get '/users/emaildata'
Avoids the http request, but this still requires a route and authentication.
2) have the controller action itself do the uploading
Still requires some sort of http request, and also puts more logic into the controller which isn't good, and also is a weird use case of the xml view, not actually rendering it
3) create a model class method to do this
requires view stuff in the model, which is frowned upon. Does resolve all the other problems though. i can just call Usermodel::emaildata in the rake script, let the model build up the appropriate xml and send it where it needs to go.

What I would really like to do is something like this in the rake script:

upload = Usercontroller::emaildata
upload_my_stuff_to_place(upload)

Because this avoids the http call, I don't need this controller action to be publicly routable (and thus don't need to add authentication to it), and all the view, model, and controller stuff stays where it should be; but I can't see to find a good way to call a controller action from a script without making an http request.

So, what do you all think, any ideas or suggestions? Am I doing this "the rails way" currently, or is there a better way? Or am I crazy and this doesn't make sense?

Thanks in advance.

Last edited by jcantara (2012-02-21 14:37:56)

Re: Controller#action outside of a web request?

You could/should use a background task using a tool like resque or delayed_job to hand off the processing from the main application thread.
Then have the queue call a method on your model.
It is quite acceptable for a model to be able to supply the data you are requesting in whatever format you need, that is why the to_json and to_xml methods exist so drop the rake task, go with a model method and pass it off to a resque queue is my best advice

There is a great rails cast on this here http://railscasts.com/episodes/271-resque which I am currently using and it works superbly well

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: Controller#action outside of a web request?

Hi jamesw, thanks for the reply.

It's not so much the "how to schedule background tasks" I'm interested in, so much as "how to have a background (or rake, or whatever) task access the result of the full rails stack"

The reason why is that the Users#emaildata action does a lot of things, more than I would want to include in just a model's to_xml. The controller action gathers data from a few models @users, @accounts, @etc, and that gets built up in an xml view, such as:

<third_party_format_xml>
<% @users.each do |u| %>
<user>
<name><%= u.name %></name>
</user>
<% end %>
<% @accounts.each do |a| %>
<account>
<credentials><%= a.creds %></credentials>
</account>
<% end %>
</third_party_format_xml>

So on, and so forth; I don't want to lump all of that functionality into one model's to_xml method... that just seems contrary to the whole premise of MVC.

I sometimes have a hard time explaining myself, hopefully this is more clear with examples now.

Thanks for already spending some brain time on my question smile

Last edited by jcantara (2012-03-06 23:33:27)

Re: Controller#action outside of a web request?

In which case I would just use cron to issue a curl or wget command to the controllers action and you can provide the authentication as part of the curkl or wget command

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: Controller#action outside of a web request?

That's exactly what I'm doing currently smile

It _is_ functional as-is; it just ties up one of the app request handlers for a while, which is why I wanted to know if the controller action can be called outside of a "normal" web request; so I can effectively get the same result as a curl/wget, just without tying up the app server.

Last edited by jcantara (2012-03-07 09:55:00)

Re: Controller#action outside of a web request?

You NEED a single public method in a class that produces the output for you.
That method could make a call like this

ActionView::Base.new(Rails::Configuration.new.view_path).render(:partial => "pages/show", :locals => {:page => self})

To achieve that you may need to include the helper classes that the template uses which might include the following

include ActionView::Helpers::UrlHelper

You should then use delayed_job or resque or some other background tool of your choice to do the work in a separate worker that you can priorities as you see fit
The trigger to produce this output then just needs to add something to a queue  so that the back ground tool you choose knows there is work to do.
It is likely processing and memory resources that being eaten up here and if that is the case you should probably also consider load balancing your server or adding more resources or even passing the job off to a different server entirely but ONLY AFTER you have fully streamlined your code to work as efficiently as possible

include ActionController::UrlWriter

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: Controller#action outside of a web request?

Jamesw, thanks for the suggestions, you set me down the right path, and I discovered these links; hopefully this helps anybody else in this situation:
http://amberbit.com/blog/render-views-p … rs-rails-3
http://stackoverflow.com/questions/6318 … in-a-model
that both describe how to create and call an AbstractController::Base derived class from a model method - exactly what I was after.

That way I get the best of both worlds - the full MVC stack with everything in its place (so I don't have to include controller and view type stuff in the model method), and also this AbstractController doesn't need to be publicly routable at all. And this model method can be easily called from wherever; rake, resque, delayed_job, etc.

Re: Controller#action outside of a web request?

The trigger to produce this output then just needs to add something to a queue  so that the back ground tool you choose knows there is work to do.