Aug 10, 2009

Render without calling the controller's action in Merb

I was working on a Merb project in the company, I found a common request is generate the report to view in the browser and also send the report as an attachment of an email in a cron job. Usually I have to do two parts of job, one is for the regular MVC parts to get the report in a browser. Then I have to generate the report and write it a file(html, text or pdf). Then send it out later. As you might see, there is a duplication here, I have to design the report twice. This really bothered me, why can't I just re-use the view templates which is used in the MVC. After poking around the merb-core source files, it turns out pretty easy to do that:

def generate_daily_pos_report
# create a fake request
fake_request = Merb::Request.new({})
# init the controller
controller = Purchasing::Reports.new(fake_request)
# gets all the pos created on today
pos = PurchaseOrder.all(:deleted => false, :po_date => date)
results = "No purchase orders created on #{date}"
if pos.size > 0
## set the instance varialbe for the html.erb, :@pos see report/deaily_pos.html.erb
controller.instance_variable_set(:@pos, pos)
# gets the html string
results = controller.render :_daily_pos_results, :layout => :email
end
# if you want to re-use the logic in controll, you have to use _dispatch
# but this is not a good solution, you have embed in some extra logic to
# handle the render method in controller, the best way to re-use the logic
# in controller is to move the logic out of controller, put it some services
# layer, then you can re-use it.
# result = controller._dispatch(:daily_pos)
file = purchasing_dir + "/#{date}.html"
File.open(file, 'w') {|f| f.puts results}
file
end


This is another reason I love Merb.

No comments: