[Webinar] Streamline your web hosting managementRegister Today

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1542
  • Last Modified:

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
0
svennersteel
Asked:
svennersteel
  • 3
  • 3
  • 2
2 Solutions
 
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
Never miss a deadline with monday.com

The revolutionary project management tool is here!   Plan visually with a single glance and make sure your projects get done.

 
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
 
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

Featured Post

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 3
  • 3
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now