Validate HAML from ActiveRecord: scope/controller/helpers for link_to etc?
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 =&g开发者_C百科t; :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?
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)
精彩评论