Problem:
- ERB and Builder template engines are heavily wired in
ActionView::Base - Should be optional. If we use different engine - why load all their
stuff if not needed? - There are template engines which expect files in their own locations
and cache them in their own way. For instance a template engine may
store templates in a database. These engines don’t need any central
source loader of files which they don’t need, moreover of files which
don’t exist. Files, which expects a file per an action.
Why is it problem for me?
I wrote a ViewController templating engine. ViewController is similar to
Controller - is just one file, a class, which controls views of all
actions.
It uses yaml data files from which loads data. It is real push-style
template engine.
app/views/foo/foo_view_controller.vcrb
class FooViewController < ActiveView::ViewController::Base
def index
return get( :body ) do |body|
body.title = ‘Homepage’
body.time = Time.now
body.table do |table|
@people.each do |person|
table << get( :row, :name=>person.name,
:surname=>person.sn )
end
end
end
end
def list
return get( :body, :title=>‘under construction’, :time=>Time.now )
end
end
Request:
More modularization. Template engines should be independent and
optional,
even ERB and Builder. Template engines should do source loading and
caching itself.
Solution:
-
The ActionView::Base should work just like a register / dispatcher
for
template engines. -
Every template engine (including ERB and Builder) should be placed
inside the vendor directory. -
Every template engine should have a base class
ActionView::Engines::EngineName with these methods:
self::template_exists?( render_options={} )
self::type # return :rhtml, :rxml, :vcrb, etc.
initialize( action_view )
render( render_options={}, local_assigns={} )
- Only used template engines will be then registered in environment.rb:
ActionView::Base.register_template_handler( ‘erb’ )
does: require( “vendor/erb.rb” )
ActionView::Base.register_template_handler( ‘view_controller’ )
does: require( “vendor/view_controller.rb” )
- Each required file from vendor registers itself into
@@template_handlers.
@@template_handlers[ActionView::Handlers::Erb.type] =
ActionView::Handlers::Erb
@@template_handlers[ActionView::Handlers::ViewController.type] =
ActionView::Handlers::ViewController
- If a request comes, the ActionView::Base will ask all registered
template handlers (in @@template_handlers), if the template specified by
render_options exists. If a handler responds true, the Base would create
an instance of that handler and call it’s render method. That’s all.
Every other stuff should be done by the handler.
How to do it:
-
Modularize ERB and Builder.
-
Don’t pick the handler by the file extension, instead pass the
render_options to all registered handler’s template_exists? and
check if some handler responds true. -
File rendering would be processed this way (from ActionView::Base):
def render_file( render_options={}, local_assigns={} )
@@template_handlers.each_value do |handler_class|
if handler_class::template_exists?( render_options )
return handler_class.new( render_options, local_assigns )
end
end
raise ‘no handler is able to render the file!’
end
Inline rendering would be processed this way (from ActionView::Base):
def render_inline( render_options={} )
handler = @@template_handlers[ render_options[:type] ]
return handler.render( render_options )
end
-
layout.rb would pass the ‘content_for_layout’ variable to
local_assigns of the render method (no special injecting into template). -
rescue.rb shouldn’t call render_file with a special file path.
Instead it should call render() with an option :error=>anError. This
would allow a handler to display the error. Then a handler which
responds to true will be used to display the error. Not only ERB can do
it. -
For backward compatibility reasons and reasons when no other template
handler is registered, ERB will be loaded defaultly.
I will public my ViewController after testing it a bit, I realized it is
really good
Now I can run it only by hacking the ActionView::Base class but I don’t
want to do it, I want ActionView to be more modularized to allow this
normal way.
What do you think about it? If you want, I can get more into the
ActionPack’s code and work out the separation of ERB (with backward
compatibility, of course)
jan molic