Browse files

+ moved hierarchical code to hierarchical renderer

  • Loading branch information...
1 parent 2c09a09 commit ef3cd0c6708dfedce7eafce6b96540976494772b @floere committed Mar 4, 2010
View
158 lib/view_models/base.rb
@@ -2,11 +2,6 @@
#
module ViewModels
- # Gets raised when render_as, render_the, or render_template cannot
- # find the named template, not even in the hierarchy.
- #
- class MissingTemplateError < StandardError; end
-
# Base class from which all view_models inherit.
#
class Base
@@ -31,17 +26,6 @@ def initialize model, context
class << self
- # Installs a path store, a specific store for
- # template inheritance, to remember specific
- # [path, name, format] tuples, pointing to a template path,
- # so the view models don't have to traverse the inheritance chain always.
- #
- attr_accessor :path_store
- def inherited subclass
- ViewModels::PathStore.install_in subclass
- super
- end
-
# Installs the model_reader Method for filtered
# model method delegation.
#
@@ -72,101 +56,6 @@ def add_template_helper helper_module
include helper_module
old_add_template_helper helper_module
end
-
- # Sets the view format and tries to render the given options.
- #
- # Note: Also caches [path, name, format] => template path.
- #
- def render view, options
- options.format! view
- path_store.cached options do
- options.file = template_path view, options
- view.render_with options
- end
- end
-
- protected
-
- # Returns the next view model class in the render hierarchy.
- #
- # Note: Just returns the superclass.
- #
- # TODO Think about raising the MissingTemplateError here.
- #
- def next_in_render_hierarchy
- superclass
- end
-
- # Just raises a fitting template error.
- #
- def raise_template_error_with message
- raise MissingTemplateError.new "No template #{message} found."
- end
-
- # Check if the view lookup inheritance chain has ended.
- #
- # Raises a MissingTemplateError if yes.
- #
- def inheritance_chain_ends?
- self == ViewModels::Base
- end
-
- # Returns a template path for the view with the given options.
- #
- # If no template is found, traverses up the inheritance chain.
- #
- # Raises a MissingTemplateError if none is found during
- # inheritance chain traversal.
- #
- def template_path view, options
- raise_template_error_with options.error_message if inheritance_chain_ends?
-
- template_path_from(view, options) || self.next_in_render_hierarchy.template_path(view, options)
- end
-
- # Accesses the view to find a suitable template path.
- #
- def template_path_from view, options
- template = view.find_template tentative_template_path(options)
-
- template && template.path
- end
-
- # Return as render path either a stored path or a newly generated one.
- #
- # If nothing or nil is passed, the store is ignored.
- #
- def tentative_template_path options
- path_store[options.path_key] || generate_template_path_from(options)
- end
-
- # Returns the root of this view_models views with the template name appended.
- # e.g. 'view_models/some/specific/path/to/template'
- #
- def generate_template_path_from options
- File.join generate_path_from(options), options.name
- end
-
- # If the path is explicitly defined, return it, otherwise
- # generate a view model path from the class name.
- #
- def generate_path_from options
- options.path || view_model_path
- end
-
- # Returns the path from the view_model_view_paths to the actual templates.
- # e.g. "view_models/models/book"
- #
- # If the class is named
- # ViewModels::Models::Book
- # this method will yield
- # view_models/models/book
- #
- # Note: Remembers the result since it is dependent on the Class name only.
- #
- def view_model_path
- @view_model_path || @view_model_path = self.name.underscore
- end
end # class << self
@@ -194,7 +83,7 @@ def view_model_path
# * If no format is given, it will render the default format, which is (currently) html.
#
def render_as name, options = {}
- render RenderOptions::Partial.new(name, options)
+ Renderer::Hierarchical.new(self, options).render_as name
end
# render_the is used for small parts.
#
@@ -220,49 +109,22 @@ def render_as name, options = {}
# * If no format is given, it will render the default format, which is (currently) html.
#
def render_template name, options = {}
- render RenderOptions::Template.new(name, options)
+ Renderer::Hierarchical.new(self, options).render_template name
end
+ # Returns a view instance for render_xxx.
+ #
+ def view_instance
+ View.new controller, master_helper_module
+ end
+
+ attr_accessor :template_format
+
protected
# CaptureHelper needs this.
#
attr_accessor :output_buffer
- # Internal render method that uses the options to get a view instance
- # and then referring to its class for rendering.
- #
- def render options
- options.view_model = self
-
- determine_and_set_format options
-
- self.class.render view_instance, options
- end
-
- # Returns a view instance for render_xxx.
- #
- # TODO Try getting a view instance from the controller.
- #
- def view_instance
- # view = if controller.response.template
- # controller.response.template
- # else
- View.new controller, master_helper_module
- # end
-
- # view.extend Extensions::View
- end
-
- # Determines what format to use for rendering.
- #
- # Note: Uses the template format of the view model instance
- # if none is explicitly set in the options.
- # This propagates the format to further render_xxx calls.
- #
- def determine_and_set_format options
- options.format = @template_format = options.format || @template_format
- end
-
end
end
View
4 lib/view_models/path_store.rb
@@ -23,8 +23,8 @@ def self.install_in klass
# Cache the result of the rendering.
#
- def cached options, &block
- prepare options.path_key
+ def cached view_model_class, options, &block
+ prepare options.path_key(view_model_class)
result = block.call
save options and result if result
end
View
4 lib/view_models/render_options.rb
@@ -45,8 +45,8 @@ def format! view
# Used for caching.
#
- def path_key
- [self.path, self.name, self.format]
+ def path_key view_model_class
+ [view_model_class, self.path, self.name, self.format]
end
private
View
163 lib/view_models/renderer/hierarchical.rb
@@ -0,0 +1,163 @@
+module ViewModels
+
+ module Renderer
+
+ # Gets raised when render_as, render_the, or render_template cannot
+ # find the named template, not even in the hierarchy.
+ #
+ class MissingTemplateError < StandardError; end
+
+ # This class handles hierarchical rendering.
+ #
+ # Note: The render_as, render_the, render_template on the view model.
+ #
+ # Traverses the ancestors chain, and accesses the path store of
+ # the view model classes and included modules.
+ #
+ class Hierarchical
+
+ metaclass.send :attr_accessor, :path_store
+ PathStore.install_in self
+
+ attr_reader :view_model, :options, :current_class
+
+ def initialize view_model, options
+ @view_model = view_model
+ @options = options
+ end
+
+ def render_as name
+ @options = RenderOptions::Partial.new name, @options
+ prepare_and_render
+ end
+
+ def render_template name
+ @options = RenderOptions::Template.new name, @options
+ prepare_and_render
+ end
+
+ # Sets the view format and tries to render the given options.
+ #
+ # Note: Also caches [path, name, format] => template path.
+ #
+ def render
+ @options.format! @view_instance
+ path_store.cached @current_class, @options do
+ @options.file = template_path
+ @view_instance.render_with @options
+ end
+ end
+
+ # Returns the next view model class in the render hierarchy.
+ #
+ # Note: Just returns the superclass.
+ #
+ # TODO Think about raising the MissingTemplateError here.
+ #
+ def next_in_render_hierarchy
+ @current_class = @render_hierarchy.shift
+ end
+
+ # Just raises a fitting template error.
+ #
+ def raise_template_error
+ raise MissingTemplateError.new "No template #{@options.error_message} found."
+ end
+
+ # Check if the view lookup inheritance chain has ended.
+ #
+ # Raises a MissingTemplateError if yes.
+ #
+ def inheritance_chain_ends?
+ @current_class == ViewModels::Base
+ end
+
+ # Returns a template path for the view with the given options.
+ #
+ # If no template is found, traverses up the inheritance chain.
+ #
+ # Raises a MissingTemplateError if none is found during
+ # inheritance chain traversal.
+ #
+ def template_path
+ raise_template_error if inheritance_chain_ends?
+
+ template_path_from_view_instance || next_in_render_hierarchy && template_path
+ end
+
+ # Accesses the view to find a suitable template path.
+ #
+ def template_path_from_view_instance
+ template = @view_instance.find_template tentative_template_path
+
+ template && template.path
+ end
+
+ # Return as render path either a stored path or a newly generated one.
+ #
+ # If nothing or nil is passed, the store is ignored.
+ #
+ def tentative_template_path
+ path_store[@options.path_key(@current_class)] || generate_template_path
+ end
+
+ # Returns the root of this view_models views with the template name appended.
+ # e.g. 'view_models/some/specific/path/to/template'
+ #
+ def generate_template_path
+ File.join generate_path, @options.name
+ end
+
+ # If the path is explicitly defined, return it, otherwise
+ # generate a view model path from the class name.
+ #
+ def generate_path
+ @options.path || view_model_path
+ end
+
+ #
+ #
+ def view_model_path
+ @current_class.name.underscore
+ end
+
+ # Internal render method that uses the options to get a view instance
+ # and then referring to its class for rendering.
+ #
+ def prepare_and_render
+ @options.view_model = @view_model
+
+ determine_and_set_format @options
+
+ @view_instance = @view_model.view_instance
+ @render_hierarchy = render_hierarchy
+ @current_class = next_in_render_hierarchy
+
+ render
+ end
+
+ #
+ #
+ def render_hierarchy
+ @view_model.class.ancestors
+ end
+
+ # Determines what format to use for rendering.
+ #
+ # Note: Uses the template format of the view model instance
+ # if none is explicitly set in the options.
+ # This propagates the format to further render_xxx calls.
+ #
+ def determine_and_set_format options
+ options.format = @view_model.template_format = options.format || @view_model.template_format
+ end
+
+ def path_store
+ self.class.path_store
+ end
+
+ end
+
+ end
+
+end
View
1 rails/init.rb
@@ -7,6 +7,7 @@
require 'view_models/render_options'
require 'view_models/controller_extractor'
require 'view_models/path_store'
+require 'view_models/renderer/hierarchical'
require 'view_models/base'
require 'view_models/view'
View
28 spec/integration/integration_spec.rb
@@ -208,13 +208,13 @@ class TestController < ActionController::Base; end
end
describe 'template inheritance' do
it 'should raise ViewModels::MissingTemplateError if template is not found' do
- lambda { @view_model.render_as(:this_template_does_not_exist_at_allllll) }.should raise_error(ViewModels::MissingTemplateError, "No template '_this_template_does_not_exist_at_allllll' with default format found.")
+ lambda { @view_model.render_as(:this_template_does_not_exist_at_allllll) }.should raise_error(ViewModels::Renderer::MissingTemplateError, "No template '_this_template_does_not_exist_at_allllll' with default format found.")
end
it 'should raise ViewModels::MissingTemplateError if template is not found, with specific path' do
- lambda { @view_model.render_as(:partial => 'view_models/sub_subclass/this_template_does_not_exist_at_allllll') }.should raise_error(ViewModels::MissingTemplateError, "No template 'view_models/sub_subclass/_this_template_does_not_exist_at_allllll' with default format found.")
+ lambda { @view_model.render_as(:partial => 'view_models/sub_subclass/this_template_does_not_exist_at_allllll') }.should raise_error(ViewModels::Renderer::MissingTemplateError, "No template 'view_models/sub_subclass/_this_template_does_not_exist_at_allllll' with default format found.")
end
it 'should raise ViewModels::MissingTemplateError if template is not found, with format' do
- lambda { @view_model.render_as(:this_template_does_not_exist_at_allllll, :format => :gaga) }.should raise_error(ViewModels::MissingTemplateError, "No template '_this_template_does_not_exist_at_allllll' with format gaga found.")
+ lambda { @view_model.render_as(:this_template_does_not_exist_at_allllll, :format => :gaga) }.should raise_error(ViewModels::Renderer::MissingTemplateError, "No template '_this_template_does_not_exist_at_allllll' with format gaga found.")
end
it "should use its own template" do
@view_model.render_as(:exists).should == '_exists.html.erb' # The default
@@ -255,14 +255,20 @@ class TestController < ActionController::Base; end
end
end
describe 'memoizing' do
- it 'should memoize and not generate always a new path' do
- @view_model.class.should_receive(:generate_template_path_from).once
-
- @view_model.render_as :not_found_in_sub_subclass
- @view_model.render_as :not_found_in_sub_subclass
- @view_model.render_as :not_found_in_sub_subclass
- @view_model.render_as :not_found_in_sub_subclass
- @view_model.render_as :not_found_in_sub_subclass
+ context 'path generation' do
+ before(:each) do
+ @renderer = ViewModels::Renderer::Hierarchical.new @view_model, {}
+ ViewModels::Renderer::Hierarchical.stub! :new => @renderer
+ end
+ it 'should memoize and not generate always a new path' do
+ @renderer.should_receive(:generate_template_path).once
+
+ @view_model.render_as :not_found_in_sub_subclass
+ @view_model.render_as :not_found_in_sub_subclass
+ @view_model.render_as :not_found_in_sub_subclass
+ @view_model.render_as :not_found_in_sub_subclass
+ @view_model.render_as :not_found_in_sub_subclass
+ end
end
it 'should render the right one' do
@view_model.render_as :exists_in_both

0 comments on commit ef3cd0c

Please sign in to comment.