Problems when rendering ruby on rails template from a cron job

Hi,

I am trying to use a cron job to render and mail pdf files on a daily
basis. Problem is that this code doesn't compile and render the
template; it just passes the embedded ruby code on as text.

app = ActionView::Base.new
app.controller = ActionController::Base.new
data = app.render_template('rhtml', "path_to_template/template.rhtml")
pdf << data

Reason that I use render_template is that render_to_string, which I
originally used, is a protected method... and if I try to bypass that
with send or instance_eval, I get a null pointer exception.

Cheers
/Markus
svennersteelAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

dberner9Commented:
Well, for starters, PDF is not HTML, so appending HTML code to a string will in no way give you a valid PDF file.

You will have to generate the PDF separately from the HTML and send it as an attachment.

Have you looked at any ruby PDF generators? http://wiki.rubyonrails.org/rails/pages/HowtoGeneratePDFs
0
svennersteelAuthor Commented:
Hi,

Sorry if I was unclear. That was just a snippet from the application in question; after that I do generate a from the code. The PDF generation works fine; the whole application works fine when I use it normally through its web interface. The problems arise when I try to do the same from a cron job.

Here's how it works through the web interface:
1) A rhtml file (a view) is rendered as a string (str)
2) str is passed to the pdf generator, which creates a pdf file
3) The pdf file is mailed to the recipients

When I try to do the same through a cron job, however, complications arise as I have to render the rhtml from outside the controller. The problem is that the file isn't properly rendered - all embedded ruby code is just treated as plain text.

I'm happy for any suggestions to solve this.

BR
/Markus

0
dberner9Commented:
I got it -- it renders the HTML without parsing out the embedded ruby.

My guess is you're getting the NPE with render_to_string because you aren't getting the proper instance variables set up in the controller, since no action is being called. Have you tried ERb instead of ActionView?
0
Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

wesgarrisonCommented:
If you're not averse to a different way to do it, you could write a rake task to do it and call that from your cron job.

Then, you'd have all the rails environment niceties and could just call the appropriate controller action just like the web interface.  

Or, you could just request the webpage with curl (if the application is running all the time) and send ignore the result (because the email is a by-product of the request, correct?)

I use these techniques to kick off nightly reports and other scheduled tasks.

If those won't work for you, some basic questions:
* rails frozen into vendor/rails?
* rails version?

Like dberner9 said, you need the instance variables that are in that action.  So, set those up in your cron job before rendering or load that controller and call the action before you render the template.

I still think that rake or curl would be an easier solution to automate running an action, though.
0
svennersteelAuthor Commented:
Hi,

Thanks for the suggestions. I went with the 'rake task' approach; it also feels tidier than doing it through script/runner. However, I still run into some problems similar to those I had before.

Here's what I do:

buzz.rake:
namespace :buzzmail do
        desc "Sends a Buzz report to all subscribed users"
        task(:send_buzz_report => :environment) do
                require 'application'
                require 'action_controller/base'
                require 'action_controller'
                require 'localization'
                require 'user_system'
                require 'tour4_report'
                controller = Tour4ReportController.new
                controller.auto_generate_report
        end
end

def auto_generate_report (in tour4_report_controller.rb)
...
            data = render_to_string :partial => "report_pdf_template", :layout => false, :locals =>  
{:@datestr => @datestr, :@report_day => report_day}

Above line gives this error message:

> rake RAILS_ENV=development buzzmail:send_buzz_report --trace
.../config/boot.rb:28:Warning: require_gem is obsolete.  Use gem instead.
rake aborted!
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occured while evaluating nil.[]=
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/base.rb:986:in `add_instance_variables_to_assigns'
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/base.rb:984:in `add_instance_variables_to_assigns'
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/base.rb:968:in `add_variables_to_assigns'
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/base.rb:755:in `render_partial'
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/base.rb:680:in `render_with_no_layout'
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/layout.rb:253:in `render_without_benchmark'
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/benchmarking.rb:53:in `render'
/usr/lib/ruby/1.8/benchmark.rb:293:in `measure'
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/benchmarking.rb:53:in `render'
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/base.rb:704:in `render_to_string'
/home/websites/mywebsite/config/../app/controllers/tour4_report_controller.rb:127:in `auto_generate_report'
./lib/tasks/buzzmail.rake:11
/usr/lib/ruby/gems/1.8/gems/rake-0.7.3/lib/rake.rb:392:in `execute'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.3/lib/rake.rb:392:in `execute'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.3/lib/rake.rb:362:in `invoke'
/usr/lib/ruby/1.8/thread.rb:135:in `synchronize'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.3/lib/rake.rb:355:in `invoke'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.3/lib/rake.rb:1739:in `top_level'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.3/lib/rake.rb:1739:in `top_level'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.3/lib/rake.rb:1761:in `standard_exception_handling'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.3/lib/rake.rb:1733:in `top_level'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.3/lib/rake.rb:1711:in `run'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.3/lib/rake.rb:1761:in `standard_exception_handling'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.3/lib/rake.rb:1708:in `run'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.3/bin/rake:7
/usr/bin/rake:16


Line 986 in action_controller/base.rb:

 982:     def add_instance_variables_to_assigns
 983:       @@protected_variables_cache ||= protected_instance_variables.inject({}) { |h, k| h[k] = true; h }
 984:       instance_variables.each do |var|
 985:        next if @@protected_variables_cache.include?(var)
 986:        @assigns[var[1..-1]] = instance_variable_get(var)
 987:      end
 988:     end


So it's something with the instance variables still. But I have no idea how to load them manually.

Br
/Markus
0
dberner9Commented:
:locals =>  
{:@datestr => @datestr, :@report_day => report_day}

I could be wrong but AFAIK, this should not work. ":locals =>" only sets local variables, not instance variables. Instead, you should simply assign "@report_day = report_day" within the controller before making the render call and remove the :locals option from your render. ActionView will pick up the instance variables appropriately.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
wesgarrisonCommented:
Is this the action that works when you call it directly from a webpage?

I've never tried to set instance variables that way, either!  I don't know how ruby handles the symbol notation followed by an instance variable.  That's why I was curious if it worked via the web-page call.

Like dberner9 said, you should be able to set instance variables in the action and the partial will be able to see them, so no need to pass them in a hash.

Looks like you've already set @datestr, so just rename report_day to @report_day and then take out the locals hash from your render_to_string line.
0
svennersteelAuthor Commented:
Hi,

I found the solution. It was to change the rake task to the following:
                include ActionController::Integration
                session = Session.new()
                session.post ("tour4_report/auto_generate_report")

and thus call the function through a session rather than directly from the controller. Thanks alot for your help, I'll split the points equally betwen you. :)
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Programming Languages-Other

From novice to tech pro — start learning today.