views:

125

answers:

1

I like HAML. So much, in fact, that in my first Rails app, which is the usual blog/CMS thing, I want to render the body of my Page model using HAML (obviously I won't do the same for Comment!). So here is app/views/pages/_body.html.haml:

.entry-content= Haml::Engine.new(body, :format => :html5).render(self)

...and it works (yay, recursion). What I'd like to do is validate the HAML in the body when creating or updating a Page. I can almost do that, but I'm stuck on the scope argument to render. I have this in app/models/page.rb:

validates_each :body do |record, attr, value|
    begin
        Haml::Engine.new(value, :format => :html5).render(record)
    rescue Exception => e
        record.errors.add attr, "line #{(e.respond_to? :line) && e.line || 'unknown'}: #{e.message}"
    end
end

You can see I'm passing record, which is a Page, but even that doesn't have a controller, and in particular doesn't have any helpers like link_to, so as soon as a Page uses any of that it's going to fail to validate even when it would actually render just fine.

So I guess I need a controller view as scope for this, but accessing that from here in the model (where the validator is) is a big MVC no-no, and as such I don't think Rails gives me a way to do it. (I mean, I suppose I could stash a controller in some singleton somewhere or something, but... excuse me while I throw up.)

What's the least ugly way to properly validate HAML in an ActiveRecord validator?

A: 

With a little more searching, I've found a thread on Google Groups with an idea that gets most of the way there for my purposes:

base = ActionView::Base.new('/app/views/pages', {}, PagesController) 
Haml::Engine.new(value, :format => :html5).render(base)

...but as the thread goes on to say, some things, like partials and extra helpers, don't work.

Here's what I ended up using, which seems to cope with everything I've thrown at it:

contr = PagesController.new()
contr.response = ActionController::Response.new()
scope = ActionView::Base.new(["#{RAILS_ROOT}/app/views/pages","#{RAILS_ROOT}/app/views"], {}, contr)
scope.template_format = 'html'
Haml::Engine.new(value, :format => :html5).render(scope)

(context)

Chris Boyle