aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller/metal/renderers.rb
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_controller/metal/renderers.rb')
-rw-r--r--actionpack/lib/action_controller/metal/renderers.rb136
1 files changed, 136 insertions, 0 deletions
diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb
new file mode 100644
index 0000000000..02c4e563f5
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/renderers.rb
@@ -0,0 +1,136 @@
+require 'set'
+
+module ActionController
+ # See <tt>Renderers.add</tt>
+ def self.add_renderer(key, &block)
+ Renderers.add(key, &block)
+ end
+
+ # See <tt>Renderers.remove</tt>
+ def self.remove_renderer(key)
+ Renderers.remove(key)
+ end
+
+ class MissingRenderer < LoadError
+ def initialize(format)
+ super "No renderer defined for format: #{format}"
+ end
+ end
+
+ module Renderers
+ extend ActiveSupport::Concern
+
+ included do
+ class_attribute :_renderers
+ self._renderers = Set.new.freeze
+ end
+
+ module ClassMethods
+ def use_renderers(*args)
+ renderers = _renderers + args
+ self._renderers = renderers.freeze
+ end
+ alias use_renderer use_renderers
+ end
+
+ def render_to_body(options)
+ _handle_render_options(options) || super
+ end
+
+ def _handle_render_options(options)
+ _renderers.each do |name|
+ if options.key?(name)
+ _process_options(options)
+ return send("_render_option_#{name}", options.delete(name), options)
+ end
+ end
+ nil
+ end
+
+ # A Set containing renderer names that correspond to available renderer procs.
+ # Default values are <tt>:json</tt>, <tt>:js</tt>, <tt>:xml</tt>.
+ RENDERERS = Set.new
+
+ # Adds a new renderer to call within controller actions.
+ # A renderer is invoked by passing its name as an option to
+ # <tt>AbstractController::Rendering#render</tt>. To create a renderer
+ # pass it a name and a block. The block takes two arguments, the first
+ # is the value paired with its key and the second is the remaining
+ # hash of options passed to +render+.
+ #
+ # Create a csv renderer:
+ #
+ # ActionController::Renderers.add :csv do |obj, options|
+ # filename = options[:filename] || 'data'
+ # str = obj.respond_to?(:to_csv) ? obj.to_csv : obj.to_s
+ # send_data str, type: Mime::CSV,
+ # disposition: "attachment; filename=#{filename}.csv"
+ # end
+ #
+ # Note that we used Mime::CSV for the csv mime type as it comes with Rails.
+ # For a custom renderer, you'll need to register a mime type with
+ # <tt>Mime::Type.register</tt>.
+ #
+ # To use the csv renderer in a controller action:
+ #
+ # def show
+ # @csvable = Csvable.find(params[:id])
+ # respond_to do |format|
+ # format.html
+ # format.csv { render csv: @csvable, filename: @csvable.name }
+ # end
+ # end
+ # To use renderers and their mime types in more concise ways, see
+ # <tt>ActionController::MimeResponds::ClassMethods.respond_to</tt> and
+ # <tt>ActionController::MimeResponds#respond_with</tt>
+ def self.add(key, &block)
+ define_method("_render_option_#{key}", &block)
+ RENDERERS << key.to_sym
+ end
+
+ # This method is the opposite of add method.
+ #
+ # Usage:
+ #
+ # ActionController::Renderers.remove(:csv)
+ def self.remove(key)
+ RENDERERS.delete(key.to_sym)
+ method = "_render_option_#{key}"
+ remove_method(method) if method_defined?(method)
+ end
+
+ module All
+ extend ActiveSupport::Concern
+ include Renderers
+
+ included do
+ self._renderers = RENDERERS
+ end
+ end
+
+ add :json do |json, options|
+ json = json.to_json(options) unless json.kind_of?(String)
+
+ if options[:callback].present?
+ if self.content_type.nil? || self.content_type == Mime::JSON
+ self.content_type = Mime::JS
+ end
+
+ "/**/#{options[:callback]}(#{json})"
+ else
+ self.content_type ||= Mime::JSON
+ json
+ end
+ end
+
+ add :js do |js, options|
+ self.content_type ||= Mime::JS
+ js.respond_to?(:to_js) ? js.to_js(options) : js
+ end
+
+ add :xml do |xml, options|
+ self.content_type ||= Mime::XML
+ xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
+ end
+ end
+end