MiG
2006-Mar-12 13:40 UTC
[Rails] ERB and Builder template engines shouldn''t be so heavily wired in ActionView::Base
Problem: 1. ERB and Builder template engines are heavily wired in ActionView::Base 2. Should be optional. If we use different engine - why load all their stuff if not needed? 3. 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: 1. The ActionView::Base should work just like a register / dispatcher for template engines. 2. Every template engine (including ERB and Builder) should be placed inside the vendor directory. 3. 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={} ) 4. 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" ) 5. 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 6. 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: 1. Modularize ERB and Builder. 2. 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. 3. 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 5. layout.rb would pass the ''content_for_layout'' variable to local_assigns of the render method (no special injecting into template). 6. 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. 7. 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