module Cms module Behaviors # The Rendering Behavior allows a model to be rendered within a view. # The key methods are the instance methods perform_render, render and # inline_options. From within a view or a helper, you can render a # renderable object by calling the perform_render and passing the controller # object to it. # # When perform_render is called, it will first call the render instance method # of the renderable object. This is very similar to a controller action. # The purpose of this method is to setup instance varaibles to be used by the # renderable's view. # # After the render method is called, it checks to see if there is a inline_options # instance method on the renderable object. If so, it calls this and it expects # this to return a Hash that will be passed to render. This expects there to be # an inline option, so this is the way to do inline rendering. # # Assuming there is no inline_options method, it will look for a template in the # view path at cms/pluralized_class_name/render. So if the Renderable class is # Article, the template should be at cms/articles/render. It uses the same # format and template engine options as regular views, to the file name should # be render.html.erb. # module Rendering def self.included(model) model.extend(MacroMethods) end module MacroMethods def renderable? false end def is_renderable(options={}) @instance_variable_name_for_view = options[:instance_variable_name_for_view] extend ClassMethods include InstanceMethods # I'm not pleased with the need to include all of the these rails helpers onto every 'renderable' content item # It's likely to lead to unfortunate side effects. # Need to determine how this can be simplified. # Required to make the calls to add Rails Core controllers work include ActiveSupport::Configurable # Include all the core rails helpers include ActionController::Helpers include ActionController::RequestForgeryProtection helper ApplicationHelper attr_accessor :controller delegate :params, :session, :request, :flash, :to => :controller end end end module ClassMethods def renderable? true end # This will be the used as the name of instance variable # that will be available in the view. The default value is "@renderable" def instance_variable_name_for_view @instance_variable_name_for_view ||= "@renderable" end def helper_path "app/helpers/#{name.underscore}_helper.rb" end def helper_class "Cms::#{name}Helper".constantize end # This is where the path to the template. The default is based on the class # of the renderable, so if you have an Article that is renderable, # the template will be "articles/render" def template_path "#{name.underscore.pluralize}/render" end # Instance variables that will not be copied from the renderable to the view def ivars_to_ignore ['@controller', '@_already_rendered'] end end module InstanceMethods # Returns the Mercury editor type for a given attribute # @param [Symbol] method (i.e. :name, :content, etc) # @return [Hash] def editor_info(method) column = self.class.columns_hash[method.to_s] if column.type == :text {:element => 'div', :region => 'full'} else {:element => 'span', :region => 'simple'} end end def prepare_to_render(controller) # Give this renderable a reference to the controller @controller = controller copy_instance_variables_from_controller! # This gives the view a reference to this object instance_variable_set(self.class.instance_variable_name_for_view, self) # This is like a controller action # We will call it if you have defined a render method # but if you haven't we won't render if should_render_self? end def perform_render(controller) return "Exception: #{@render_exception}" if @render_exception unless @controller # We haven't prepared to render. This should only happen when logged in, as we don't want # errors to bubble up and prevent the page being edited in that case. prepare_to_render(controller) end if self.respond_to?(:deleted) && self.deleted logger.error "Attempting to render deleted object: #{self.inspect}" msg = (edit_mode? ? %Q[