mirror of https://github.com/rails/rails
Merge branch 'master' of github.com:lifo/docrails
This commit is contained in:
commit
6b9b0767bf
12
Gemfile
12
Gemfile
|
@ -61,13 +61,13 @@ platforms :ruby do
|
|||
end
|
||||
|
||||
platforms :jruby do
|
||||
git 'git://github.com/jruby/activerecord-jdbc-adapter.git' do
|
||||
gem 'activerecord-jdbcsqlite3-adapter'
|
||||
group :db do
|
||||
gem 'activerecord-jdbcmysql-adapter'
|
||||
gem 'activerecord-jdbcpostgresql-adapter'
|
||||
end
|
||||
git 'git://github.com/jruby/activerecord-jdbc-adapter.git' do
|
||||
gem 'activerecord-jdbcsqlite3-adapter'
|
||||
group :db do
|
||||
gem 'activerecord-jdbcmysql-adapter'
|
||||
gem 'activerecord-jdbcpostgresql-adapter'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# gems that are necessary for ActiveRecord tests with Oracle database
|
||||
|
|
33
README.md
33
README.md
|
@ -8,11 +8,6 @@ pattern.
|
|||
Understanding the MVC pattern is key to understanding Rails. MVC divides your
|
||||
application into three layers, each with a specific responsibility.
|
||||
|
||||
The _View layer_ is composed of "templates" that are responsible for providing
|
||||
appropriate representations of your application's resources. Templates can
|
||||
come in a variety of formats, but most view templates are HTML with embedded
|
||||
Ruby code (ERB files).
|
||||
|
||||
The _Model layer_ represents your domain model (such as Account, Product,
|
||||
Person, Post, etc.) and encapsulates the business logic that is specific to
|
||||
your application. In Rails, database-backed model classes are derived from
|
||||
|
@ -24,16 +19,26 @@ as provided by the Active Model module. You can read more about Active Record
|
|||
in its [README](activerecord/README.rdoc).
|
||||
|
||||
The _Controller layer_ is responsible for handling incoming HTTP requests and
|
||||
providing a suitable response. Usually this means returning HTML, but Rails
|
||||
controllers can also generate XML, JSON, PDFs, mobile-specific views, and
|
||||
more. Controllers manipulate models and render view templates in order to
|
||||
generate the appropriate HTTP response.
|
||||
providing a suitable response. Usually this means returning HTML, but Rails controllers
|
||||
can also generate XML, JSON, PDFs, mobile-specific views, and more. Controllers load and
|
||||
manipulate models, and render view templates in order to generate the appropriate HTTP response.
|
||||
In Rails, incoming requests are routed by Action Dispatch to an appropriate controller, and
|
||||
controller classes are derived from `ActionController::Base`. Action Dispatch and Action Controller
|
||||
are bundled together in Action Pack. You can read more about Action Pack in its
|
||||
[README](actionpack/README.rdoc).
|
||||
|
||||
In Rails, the Controller and View layers are handled together by Action Pack.
|
||||
These two layers are bundled in a single package due to their heavy interdependence.
|
||||
This is unlike the relationship between Active Record and Action Pack, which are
|
||||
independent. Each of these packages can be used independently outside of Rails. You
|
||||
can read more about Action Pack in its [README](actionpack/README.rdoc).
|
||||
The _View layer_ is composed of "templates" that are responsible for providing
|
||||
appropriate representations of your application's resources. Templates can
|
||||
come in a variety of formats, but most view templates are HTML with embedded
|
||||
Ruby code (ERB files). Views are typically rendered to generate a controller response,
|
||||
or to generate the body of an email. In Rails, View generation is handled by Action View.
|
||||
You can read more about Action View in its [README](actionview/README.rdoc).
|
||||
|
||||
Active Record, Action Pack, and Action View can each be used independently outside Rails.
|
||||
In addition to them, Rails also comes with Action Mailer ([README](actionmailer/README.rdoc)), a library
|
||||
to generate and send emails; and Active Support ([README](activesupport/README.rdoc)), a collection of
|
||||
utility classes and standard library extensions that are useful for Rails, and may also be used
|
||||
independently outside Rails.
|
||||
|
||||
## Getting Started
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ Gem::Specification.new do |s|
|
|||
s.requirements << 'none'
|
||||
|
||||
s.add_dependency 'actionpack', version
|
||||
s.add_dependency 'actionview', version
|
||||
|
||||
s.add_dependency 'mail', '~> 2.5.4'
|
||||
end
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#++
|
||||
|
||||
require 'abstract_controller'
|
||||
require 'action_view'
|
||||
require 'action_mailer/version'
|
||||
|
||||
# Common Active Support usage in Action Mailer
|
||||
|
|
|
@ -3,6 +3,7 @@ require 'action_mailer/collector'
|
|||
require 'active_support/core_ext/string/inflections'
|
||||
require 'active_support/core_ext/hash/except'
|
||||
require 'active_support/core_ext/module/anonymous'
|
||||
|
||||
require 'action_mailer/log_subscriber'
|
||||
|
||||
module ActionMailer
|
||||
|
@ -361,18 +362,17 @@ module ActionMailer
|
|||
# <tt>delivery_method :test</tt>. Most useful for unit and functional testing.
|
||||
class Base < AbstractController::Base
|
||||
include DeliveryMethods
|
||||
|
||||
abstract!
|
||||
|
||||
include AbstractController::Logger
|
||||
include AbstractController::Rendering
|
||||
include AbstractController::Layouts
|
||||
|
||||
include AbstractController::Logger
|
||||
include AbstractController::Helpers
|
||||
include AbstractController::Translation
|
||||
include AbstractController::AssetPaths
|
||||
include AbstractController::Callbacks
|
||||
|
||||
self.protected_instance_variables = [:@_action_has_layout]
|
||||
|
||||
helper ActionMailer::MailHelper
|
||||
|
||||
private_class_method :new #:nodoc:
|
||||
|
@ -385,6 +385,10 @@ module ActionMailer
|
|||
parts_order: [ "text/plain", "text/enriched", "text/html" ]
|
||||
}.freeze
|
||||
|
||||
def self.default_protected_instance_vars
|
||||
super.concat [:@_action_has_layout]
|
||||
end
|
||||
|
||||
class << self
|
||||
# Register one or more Observers which will be notified when mail is delivered.
|
||||
def register_observers(*observers)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require 'active_support/log_subscriber'
|
||||
|
||||
module ActionMailer
|
||||
# Implements the ActiveSupport::LogSubscriber for logging notifications when
|
||||
# email is delivered and received.
|
||||
|
|
|
@ -11,11 +11,11 @@ end
|
|||
require 'active_support/testing/autorun'
|
||||
require 'action_mailer'
|
||||
require 'action_mailer/test_case'
|
||||
require 'mail'
|
||||
|
||||
silence_warnings do
|
||||
# These external dependencies have warnings :/
|
||||
require 'mail'
|
||||
end
|
||||
# Emulate AV railtie
|
||||
require 'action_view'
|
||||
ActionMailer::Base.send(:include, ActionView::Layouts)
|
||||
|
||||
# Show backtraces for deprecated behavior for quicker cleanup.
|
||||
ActiveSupport::Deprecation.debug = true
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require 'abstract_unit'
|
||||
require 'action_view'
|
||||
require 'action_controller'
|
||||
|
||||
class I18nTestMailer < ActionMailer::Base
|
||||
|
@ -14,6 +15,9 @@ class I18nTestMailer < ActionMailer::Base
|
|||
end
|
||||
end
|
||||
|
||||
# Emulate AV railtie
|
||||
ActionController::Base.superclass.send(:include, ActionView::Layouts)
|
||||
|
||||
class TestController < ActionController::Base
|
||||
def send_mail
|
||||
I18nTestMailer.mail_with_i18n_subject("test@localhost").deliver
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
* Introduce `BasicRendering` which is the most basic rendering implementation. It
|
||||
allows to `render :text` and `render :nothing` without depending on Action View.
|
||||
|
||||
*Łukasz Strzałkowski*
|
||||
|
||||
* Separate Action View completely from Action Pack.
|
||||
|
||||
*Łukasz Strzałkowski*
|
||||
|
||||
* Development mode exceptions are rendered in text format in case of XHR request.
|
||||
|
||||
*Kir Shatrov*
|
||||
|
||||
* Fix an issue where :if and :unless controller action procs were being run
|
||||
before checking for the correct action in the :only and :unless options.
|
||||
|
||||
|
|
|
@ -20,11 +20,11 @@ Gem::Specification.new do |s|
|
|||
s.requirements << 'none'
|
||||
|
||||
s.add_dependency 'activesupport', version
|
||||
s.add_dependency 'actionview', version
|
||||
|
||||
s.add_dependency 'rack', '~> 1.5.2'
|
||||
s.add_dependency 'rack-test', '~> 0.6.2'
|
||||
|
||||
s.add_development_dependency 'activemodel', version
|
||||
s.add_development_dependency 'tzinfo', '~> 0.3.37'
|
||||
s.add_development_dependency 'actionview', version
|
||||
s.add_development_dependency 'activemodel', version
|
||||
s.add_development_dependency 'tzinfo', '~> 0.3.37'
|
||||
end
|
||||
|
|
|
@ -10,12 +10,11 @@ module AbstractController
|
|||
autoload :Base
|
||||
autoload :Callbacks
|
||||
autoload :Collector
|
||||
autoload :DoubleRenderError, "abstract_controller/rendering"
|
||||
autoload :Helpers
|
||||
autoload :Layouts
|
||||
autoload :Logger
|
||||
autoload :Rendering
|
||||
autoload :Translation
|
||||
autoload :AssetPaths
|
||||
autoload :ViewPaths
|
||||
autoload :UrlFor
|
||||
end
|
||||
|
|
|
@ -114,6 +114,11 @@ module AbstractController
|
|||
end
|
||||
end
|
||||
|
||||
# Define some internal variables that should not be propagated to the view.
|
||||
def self.default_protected_instance_vars
|
||||
[]
|
||||
end
|
||||
|
||||
abstract!
|
||||
|
||||
# Calls the action going through the entire action dispatch stack.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
require "abstract_controller/base"
|
||||
require "action_view"
|
||||
require 'active_support/concern'
|
||||
require 'active_support/core_ext/class/attribute'
|
||||
|
||||
module AbstractController
|
||||
class DoubleRenderError < Error
|
||||
|
@ -10,91 +10,11 @@ module AbstractController
|
|||
end
|
||||
end
|
||||
|
||||
# This is a class to fix I18n global state. Whenever you provide I18n.locale during a request,
|
||||
# it will trigger the lookup_context and consequently expire the cache.
|
||||
class I18nProxy < ::I18n::Config #:nodoc:
|
||||
attr_reader :original_config, :lookup_context
|
||||
|
||||
def initialize(original_config, lookup_context)
|
||||
original_config = original_config.original_config if original_config.respond_to?(:original_config)
|
||||
@original_config, @lookup_context = original_config, lookup_context
|
||||
end
|
||||
|
||||
def locale
|
||||
@original_config.locale
|
||||
end
|
||||
|
||||
def locale=(value)
|
||||
@lookup_context.locale = value
|
||||
end
|
||||
end
|
||||
|
||||
module Rendering
|
||||
extend ActiveSupport::Concern
|
||||
include AbstractController::ViewPaths
|
||||
|
||||
included do
|
||||
class_attribute :protected_instance_variables
|
||||
self.protected_instance_variables = []
|
||||
end
|
||||
|
||||
# Overwrite process to setup I18n proxy.
|
||||
def process(*) #:nodoc:
|
||||
old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
|
||||
super
|
||||
ensure
|
||||
I18n.config = old_config
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def view_context_class
|
||||
@view_context_class ||= begin
|
||||
routes = respond_to?(:_routes) && _routes
|
||||
helpers = respond_to?(:_helpers) && _helpers
|
||||
|
||||
Class.new(ActionView::Base) do
|
||||
if routes
|
||||
include routes.url_helpers
|
||||
include routes.mounted_helpers
|
||||
end
|
||||
|
||||
if helpers
|
||||
include helpers
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
attr_internal_writer :view_context_class
|
||||
|
||||
def view_context_class
|
||||
@_view_context_class ||= self.class.view_context_class
|
||||
end
|
||||
|
||||
# An instance of a view class. The default view class is ActionView::Base
|
||||
#
|
||||
# The view class must have the following methods:
|
||||
# View.new[lookup_context, assigns, controller]
|
||||
# Create a new ActionView instance for a controller
|
||||
# View#render[options]
|
||||
# Returns String with the rendered template
|
||||
#
|
||||
# Override this method in a module to change the default behavior.
|
||||
def view_context
|
||||
view_context_class.new(view_renderer, view_assigns, self)
|
||||
end
|
||||
|
||||
# Returns an object that is able to render templates.
|
||||
def view_renderer
|
||||
@_view_renderer ||= ActionView::Renderer.new(lookup_context)
|
||||
end
|
||||
|
||||
# Normalize arguments, options and then delegates render_to_body and
|
||||
# sticks the result in self.response_body.
|
||||
def render(*args, &block)
|
||||
options = _normalize_render(*args, &block)
|
||||
self.response_body = render_to_body(options)
|
||||
def self.default_protected_instance_vars
|
||||
super.concat [:@_action_name, :@_response_body, :@_formats, :@_prefixes, :@_config]
|
||||
end
|
||||
|
||||
# Raw rendering of a template to a string.
|
||||
|
@ -109,88 +29,52 @@ module AbstractController
|
|||
# overridden in order to still return a string.
|
||||
# :api: plugin
|
||||
def render_to_string(*args, &block)
|
||||
options = _normalize_render(*args, &block)
|
||||
render_to_body(options)
|
||||
end
|
||||
|
||||
# Raw rendering of a template.
|
||||
# :api: plugin
|
||||
def render_to_body(options = {})
|
||||
_process_options(options)
|
||||
_render_template(options)
|
||||
end
|
||||
|
||||
# Find and renders a template based on the options given.
|
||||
# :api: private
|
||||
def _render_template(options) #:nodoc:
|
||||
lookup_context.rendered_format = nil if options[:formats]
|
||||
view_renderer.render(view_context, options)
|
||||
# Normalize arguments, options and then delegates render_to_body and
|
||||
# sticks the result in self.response_body.
|
||||
# :api: public
|
||||
def render(*args, &block)
|
||||
end
|
||||
|
||||
DEFAULT_PROTECTED_INSTANCE_VARIABLES = [
|
||||
:@_action_name, :@_response_body, :@_formats, :@_prefixes, :@_config,
|
||||
:@_view_context_class, :@_view_renderer, :@_lookup_context
|
||||
]
|
||||
# Return Content-Type of rendered content
|
||||
# :api: public
|
||||
def rendered_format
|
||||
end
|
||||
|
||||
# This method should return a hash with assigns.
|
||||
# You can overwrite this configuration per controller.
|
||||
# :api: public
|
||||
def view_assigns
|
||||
hash = {}
|
||||
variables = instance_variables
|
||||
variables -= protected_instance_variables
|
||||
variables -= DEFAULT_PROTECTED_INSTANCE_VARIABLES
|
||||
variables.each { |name| hash[name[1..-1]] = instance_variable_get(name) }
|
||||
(instance_variables - self.class.default_protected_instance_vars).each do |name|
|
||||
hash[name[1..-1]] = instance_variable_get(name)
|
||||
end
|
||||
hash
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Normalize args and options.
|
||||
# :api: private
|
||||
def _normalize_render(*args, &block)
|
||||
options = _normalize_args(*args, &block)
|
||||
_normalize_options(options)
|
||||
options
|
||||
end
|
||||
|
||||
# Normalize args by converting render "foo" to render :action => "foo" and
|
||||
# render "foo/bar" to render :file => "foo/bar".
|
||||
# :api: plugin
|
||||
def _normalize_args(action=nil, options={})
|
||||
case action
|
||||
when NilClass
|
||||
when Hash
|
||||
options = action
|
||||
when String, Symbol
|
||||
action = action.to_s
|
||||
key = action.include?(?/) ? :file : :action
|
||||
options[key] = action
|
||||
else
|
||||
options[:partial] = action
|
||||
end
|
||||
|
||||
options
|
||||
end
|
||||
|
||||
# Normalize options.
|
||||
# :api: plugin
|
||||
def _normalize_options(options)
|
||||
if options[:partial] == true
|
||||
options[:partial] = action_name
|
||||
end
|
||||
|
||||
if (options.keys & [:partial, :file, :template]).empty?
|
||||
options[:prefixes] ||= _prefixes
|
||||
end
|
||||
|
||||
options[:template] ||= (options[:action] || action_name).to_s
|
||||
options
|
||||
end
|
||||
|
||||
# Process extra options.
|
||||
# :api: plugin
|
||||
def _process_options(options)
|
||||
options
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,6 +13,7 @@ module ActionController
|
|||
autoload :Middleware
|
||||
|
||||
autoload_under "metal" do
|
||||
autoload :BasicRendering, 'action_controller/metal/rendering'
|
||||
autoload :Compatibility
|
||||
autoload :ConditionalGet
|
||||
autoload :Cookies
|
||||
|
@ -46,18 +47,9 @@ module ActionController
|
|||
def self.eager_load!
|
||||
super
|
||||
ActionController::Caching.eager_load!
|
||||
HTML.eager_load!
|
||||
end
|
||||
end
|
||||
|
||||
# All of these simply register additional autoloads
|
||||
require 'action_view'
|
||||
require 'action_view/vendor/html-scanner'
|
||||
|
||||
ActiveSupport.on_load(:action_view) do
|
||||
ActionView::RoutingUrlFor.send(:include, ActionDispatch::Routing::UrlFor)
|
||||
end
|
||||
|
||||
# Common Active Support usage in Action Controller
|
||||
require 'active_support/core_ext/class/attribute_accessors'
|
||||
require 'active_support/core_ext/load_error'
|
||||
|
|
|
@ -2,6 +2,21 @@ require "action_controller/log_subscriber"
|
|||
require "action_controller/metal/params_wrapper"
|
||||
|
||||
module ActionController
|
||||
# The <tt>metal</tt> anonymous class was introduced to solve issue with including modules in <tt>ActionController::Base</tt>.
|
||||
# Modules needes to be included in particluar order. First wee need to have <tt>AbstractController::Rendering</tt> included,
|
||||
# next we should include actuall implementation which would be for example <tt>ActionView::Rendering</tt> and after that
|
||||
# <tt>ActionController::Rendering</tt>. This order must be preserved and as we want to have middle module included dynamicaly
|
||||
# <tt>metal</tt> class was introduced. It has <tt>AbstractController::Rendering</tt> included and is parent class of
|
||||
# <tt>ActionController::Base</tt> which includes <tt>ActionController::Rendering</tt>. If we include <tt>ActionView::Rendering</tt>
|
||||
# beetween them to perserve the required order, we can simply do this by:
|
||||
#
|
||||
# ActionController::Base.superclass.send(:include, ActionView::Rendering)
|
||||
#
|
||||
metal = Class.new(Metal) do
|
||||
include AbstractController::Rendering
|
||||
include ActionController::BasicRendering
|
||||
end
|
||||
|
||||
# Action Controllers are the core of a web request in \Rails. They are made up of one or more actions that are executed
|
||||
# on request and then either it renders a template or redirects to another action. An action is defined as a public method
|
||||
# on the controller, which will automatically be made accessible to the web-server through \Rails Routes.
|
||||
|
@ -160,7 +175,7 @@ module ActionController
|
|||
# render action: "overthere" # won't be called if monkeys is nil
|
||||
# end
|
||||
#
|
||||
class Base < Metal
|
||||
class Base < metal
|
||||
abstract!
|
||||
|
||||
# We document the request and response methods here because albeit they are
|
||||
|
@ -200,7 +215,6 @@ module ActionController
|
|||
end
|
||||
|
||||
MODULES = [
|
||||
AbstractController::Layouts,
|
||||
AbstractController::Translation,
|
||||
AbstractController::AssetPaths,
|
||||
|
||||
|
@ -247,11 +261,12 @@ module ActionController
|
|||
include mod
|
||||
end
|
||||
|
||||
# Define some internal variables that should not be propagated to the view.
|
||||
self.protected_instance_variables = [
|
||||
:@_status, :@_headers, :@_params, :@_env, :@_response, :@_request,
|
||||
:@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout
|
||||
]
|
||||
def self.default_protected_instance_vars
|
||||
super.concat [
|
||||
:@_status, :@_headers, :@_params, :@_env, :@_response, :@_request,
|
||||
:@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout
|
||||
]
|
||||
end
|
||||
|
||||
ActiveSupport.run_load_hooks(:action_controller, self)
|
||||
end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
require 'action_dispatch/http/response'
|
||||
require 'delegate'
|
||||
require 'active_support/json'
|
||||
|
||||
module ActionController
|
||||
# Mix this module in to your controller, and all actions in that controller
|
||||
|
@ -32,6 +33,79 @@ module ActionController
|
|||
# the main thread. Make sure your actions are thread safe, and this shouldn't
|
||||
# be a problem (don't share state across threads, etc).
|
||||
module Live
|
||||
# This class provides the ability to write an SSE (Server Sent Event)
|
||||
# to an IO stream. The class is initialized with a stream and can be used
|
||||
# to either write a JSON string or an object which can be converted to JSON.
|
||||
#
|
||||
# Writing an object will convert it into standard SSE format with whatever
|
||||
# options you have configured. You may choose to set the following options:
|
||||
#
|
||||
# 1) Event. If specified, an event with this name will be dispatched on
|
||||
# the browser.
|
||||
# 2) Retry. The reconnection time in milliseconds used when attempting
|
||||
# to send the event.
|
||||
# 3) Id. If the connection dies while sending an SSE to the browser, then
|
||||
# the server will receive a +Last-Event-ID+ header with value equal to +id+.
|
||||
#
|
||||
# After setting an option in the constructor of the SSE object, all future
|
||||
# SSEs sent accross the stream will use those options unless overridden.
|
||||
#
|
||||
# Example Usage:
|
||||
#
|
||||
# class MyController < ActionController::Base
|
||||
# include ActionController::Live
|
||||
#
|
||||
# def index
|
||||
# response.headers['Content-Type'] = 'text/event-stream'
|
||||
# sse = SSE.new(response.stream, retry: 300, event: "event-name")
|
||||
# sse.write({ name: 'John'})
|
||||
# sse.write({ name: 'John'}, id: 10)
|
||||
# sse.write({ name: 'John'}, id: 10, event: "other-event")
|
||||
# sse.write({ name: 'John'}, id: 10, event: "other-event", retry: 500)
|
||||
# ensure
|
||||
# sse.close
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Note: SSEs are not currently supported by IE. However, they are supported
|
||||
# by Chrome, Firefox, Opera, and Safari.
|
||||
class SSE
|
||||
|
||||
WHITELISTED_OPTIONS = %w( retry event id )
|
||||
|
||||
def initialize(stream, options = {})
|
||||
@stream = stream
|
||||
@options = options
|
||||
end
|
||||
|
||||
def close
|
||||
@stream.close
|
||||
end
|
||||
|
||||
def write(object, options = {})
|
||||
case object
|
||||
when String
|
||||
perform_write(object, options)
|
||||
else
|
||||
perform_write(ActiveSupport::JSON.encode(object), options)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def perform_write(json, options)
|
||||
current_options = @options.merge(options).stringify_keys
|
||||
|
||||
WHITELISTED_OPTIONS.each do |option_name|
|
||||
if (option_value = current_options[option_name])
|
||||
@stream.write "#{option_name}: #{option_value}\n"
|
||||
end
|
||||
end
|
||||
|
||||
@stream.write "data: #{json}\n\n"
|
||||
end
|
||||
end
|
||||
|
||||
class Buffer < ActionDispatch::Response::Buffer #:nodoc:
|
||||
def initialize(response)
|
||||
@error_callback = nil
|
||||
|
|
|
@ -6,6 +6,13 @@ module ActionController
|
|||
Renderers.add(key, &block)
|
||||
end
|
||||
|
||||
class MissingRenderer < LoadError
|
||||
def initialize(format)
|
||||
@format = format
|
||||
super("No renderer defined for format: #{@format}")
|
||||
end
|
||||
end
|
||||
|
||||
module Renderers
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
|
|
|
@ -1,8 +1,38 @@
|
|||
module ActionController
|
||||
module Rendering
|
||||
# Basic rendering implements the most minimal rendering layer.
|
||||
# It only supports rendering :text and :nothing. Passing any other option will
|
||||
# result in `UnsupportedOperationError` exception. For more functionality like
|
||||
# different formats, layouts etc. you should use `ActionView` gem.
|
||||
module BasicRendering
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include AbstractController::Rendering
|
||||
# Render text or nothing (empty string) to response_body
|
||||
# :api: public
|
||||
def render(*args, &block)
|
||||
super(*args, &block)
|
||||
opts = args.first
|
||||
if opts.has_key?(:text) && opts[:text].present?
|
||||
self.response_body = opts[:text]
|
||||
elsif opts.has_key?(:nothing) && opts[:nothing]
|
||||
self.response_body = " "
|
||||
else
|
||||
raise UnsupportedOperationError
|
||||
end
|
||||
end
|
||||
|
||||
def rendered_format
|
||||
Mime::TEXT
|
||||
end
|
||||
|
||||
class UnsupportedOperationError < StandardError
|
||||
def initialize
|
||||
super "Unsupported render operation. BasicRendering supports only :text and :nothing options. For more, you need to include ActionView."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Rendering
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
# Before processing, set the request formats in current controller formats.
|
||||
def process_action(*) #:nodoc:
|
||||
|
@ -12,17 +42,17 @@ module ActionController
|
|||
|
||||
# Check for double render errors and set the content_type after rendering.
|
||||
def render(*args) #:nodoc:
|
||||
raise ::AbstractController::DoubleRenderError if response_body
|
||||
raise ::AbstractController::DoubleRenderError if self.response_body
|
||||
super
|
||||
self.content_type ||= Mime[lookup_context.rendered_format].to_s
|
||||
response_body
|
||||
self.content_type ||= rendered_format.to_s
|
||||
self.response_body
|
||||
end
|
||||
|
||||
# Overwrite render_to_string because body can now be set to a rack body.
|
||||
def render_to_string(*)
|
||||
if self.response_body = super
|
||||
string = ""
|
||||
response_body.each { |r| string << r }
|
||||
self.response_body.each { |r| string << r }
|
||||
string
|
||||
end
|
||||
ensure
|
||||
|
|
|
@ -202,6 +202,7 @@ module ActionController #:nodoc:
|
|||
# This is the common behavior for formats associated with APIs, such as :xml and :json.
|
||||
def api_behavior(error)
|
||||
raise error unless resourceful?
|
||||
raise MissingRenderer.new(format) unless has_renderer?
|
||||
|
||||
if get?
|
||||
display resource
|
||||
|
@ -269,6 +270,11 @@ module ActionController #:nodoc:
|
|||
resource.respond_to?(:errors) && !resource.errors.empty?
|
||||
end
|
||||
|
||||
# Check whether the neceessary Renderer is available
|
||||
def has_renderer?
|
||||
Renderers::RENDERERS.include?(format)
|
||||
end
|
||||
|
||||
# By default, render the <code>:edit</code> action for HTML requests with errors, unless
|
||||
# the verb was POST.
|
||||
#
|
||||
|
|
|
@ -193,31 +193,29 @@ module ActionController #:nodoc:
|
|||
module Streaming
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include AbstractController::Rendering
|
||||
|
||||
protected
|
||||
|
||||
# Set proper cache control and transfer encoding when streaming
|
||||
def _process_options(options) #:nodoc:
|
||||
super
|
||||
if options[:stream]
|
||||
if env["HTTP_VERSION"] == "HTTP/1.0"
|
||||
options.delete(:stream)
|
||||
else
|
||||
headers["Cache-Control"] ||= "no-cache"
|
||||
headers["Transfer-Encoding"] = "chunked"
|
||||
headers.delete("Content-Length")
|
||||
# Set proper cache control and transfer encoding when streaming
|
||||
def _process_options(options) #:nodoc:
|
||||
super
|
||||
if options[:stream]
|
||||
if env["HTTP_VERSION"] == "HTTP/1.0"
|
||||
options.delete(:stream)
|
||||
else
|
||||
headers["Cache-Control"] ||= "no-cache"
|
||||
headers["Transfer-Encoding"] = "chunked"
|
||||
headers.delete("Content-Length")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Call render_body if we are streaming instead of usual +render+.
|
||||
def _render_template(options) #:nodoc:
|
||||
if options.delete(:stream)
|
||||
Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
|
||||
else
|
||||
super
|
||||
# Call render_body if we are streaming instead of usual +render+.
|
||||
def _render_template(options) #:nodoc:
|
||||
if options.delete(:stream)
|
||||
Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -298,7 +298,7 @@ module ActionController
|
|||
# params.slice(:d) # => {}
|
||||
def slice(*keys)
|
||||
self.class.new(super).tap do |new_instance|
|
||||
new_instance.instance_variable_set :@permitted, @permitted
|
||||
new_instance.permitted = @permitted
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -312,10 +312,15 @@ module ActionController
|
|||
# copy_params.permitted? # => true
|
||||
def dup
|
||||
super.tap do |duplicate|
|
||||
duplicate.instance_variable_set :@permitted, @permitted
|
||||
duplicate.permitted = @permitted
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def permitted=(new_permitted)
|
||||
@permitted = new_permitted
|
||||
end
|
||||
|
||||
private
|
||||
def convert_hashes_to_parameters(key, value)
|
||||
if value.is_a?(Parameters) || !value.is_a?(Hash)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
require "rails"
|
||||
require "action_controller"
|
||||
require "action_dispatch/railtie"
|
||||
require "action_view/railtie"
|
||||
require "abstract_controller/railties/routes_helpers"
|
||||
require "action_controller/railties/helpers"
|
||||
|
||||
|
|
|
@ -57,22 +57,25 @@ module ActionDispatch
|
|||
# you'll get a weird error down the road, but our form handling
|
||||
# should really prevent that from happening
|
||||
def normalize_encode_params(params)
|
||||
if params.is_a?(String)
|
||||
return params.force_encoding(Encoding::UTF_8).encode!
|
||||
elsif !params.is_a?(Hash)
|
||||
return params
|
||||
end
|
||||
|
||||
new_hash = {}
|
||||
params.each do |key, val|
|
||||
new_key = key.is_a?(String) ? key.dup.force_encoding(Encoding::UTF_8).encode! : key
|
||||
new_hash[new_key] = if val.is_a?(Array)
|
||||
val.map! { |el| normalize_encode_params(el) }
|
||||
case params
|
||||
when String
|
||||
params.force_encoding(Encoding::UTF_8).encode!
|
||||
when Hash
|
||||
if params.has_key?(:tempfile)
|
||||
UploadedFile.new(params)
|
||||
else
|
||||
normalize_encode_params(val)
|
||||
params.each_with_object({}) do |(key, val), new_hash|
|
||||
new_key = key.is_a?(String) ? key.dup.force_encoding(Encoding::UTF_8).encode! : key
|
||||
new_hash[new_key] = if val.is_a?(Array)
|
||||
val.map! { |el| normalize_encode_params(el) }
|
||||
else
|
||||
normalize_encode_params(val)
|
||||
end
|
||||
end.with_indifferent_access
|
||||
end
|
||||
else
|
||||
params
|
||||
end
|
||||
new_hash.with_indifferent_access
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,7 +18,6 @@ module ActionDispatch
|
|||
include ActionDispatch::Http::MimeNegotiation
|
||||
include ActionDispatch::Http::Parameters
|
||||
include ActionDispatch::Http::FilterParameters
|
||||
include ActionDispatch::Http::Upload
|
||||
include ActionDispatch::Http::URL
|
||||
|
||||
autoload :Session, 'action_dispatch/request/session'
|
||||
|
|
|
@ -73,18 +73,5 @@ module ActionDispatch
|
|||
filename.force_encoding(Encoding::UTF_8).encode! if filename
|
||||
end
|
||||
end
|
||||
|
||||
module Upload # :nodoc:
|
||||
# Replace file upload hash with UploadedFile objects
|
||||
# when normalize and encode parameters.
|
||||
def normalize_encode_params(value)
|
||||
if Hash === value && value.has_key?(:tempfile)
|
||||
UploadedFile.new(value)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
private :normalize_encode_params
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -34,27 +34,35 @@ module ActionDispatch
|
|||
log_error(env, wrapper)
|
||||
|
||||
if env['action_dispatch.show_detailed_exceptions']
|
||||
request = Request.new(env)
|
||||
template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
|
||||
:request => Request.new(env),
|
||||
:exception => wrapper.exception,
|
||||
:application_trace => wrapper.application_trace,
|
||||
:framework_trace => wrapper.framework_trace,
|
||||
:full_trace => wrapper.full_trace,
|
||||
:routes_inspector => routes_inspector(exception),
|
||||
:source_extract => wrapper.source_extract,
|
||||
:line_number => wrapper.line_number,
|
||||
:file => wrapper.file
|
||||
request: request,
|
||||
exception: wrapper.exception,
|
||||
application_trace: wrapper.application_trace,
|
||||
framework_trace: wrapper.framework_trace,
|
||||
full_trace: wrapper.full_trace,
|
||||
routes_inspector: routes_inspector(exception),
|
||||
source_extract: wrapper.source_extract,
|
||||
line_number: wrapper.line_number,
|
||||
file: wrapper.file
|
||||
)
|
||||
file = "rescues/#{wrapper.rescue_template}"
|
||||
body = template.render(:template => file, :layout => 'rescues/layout')
|
||||
render(wrapper.status_code, body)
|
||||
|
||||
if request.xhr?
|
||||
body = template.render(template: file, layout: false, formats: [:text])
|
||||
format = "text/plain"
|
||||
else
|
||||
body = template.render(template: file, layout: 'rescues/layout')
|
||||
format = "text/html"
|
||||
end
|
||||
render(wrapper.status_code, body, format)
|
||||
else
|
||||
raise exception
|
||||
end
|
||||
end
|
||||
|
||||
def render(status, body)
|
||||
[status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
|
||||
def render(status, body, format)
|
||||
[status, {'Content-Type' => "#{format}; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
|
||||
end
|
||||
|
||||
def log_error(env, wrapper)
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<%
|
||||
clean_params = @request.filtered_parameters.clone
|
||||
clean_params.delete("action")
|
||||
clean_params.delete("controller")
|
||||
|
||||
request_dump = clean_params.empty? ? 'None' : clean_params.inspect.gsub(',', ",\n")
|
||||
|
||||
def debug_hash(object)
|
||||
object.to_hash.sort_by { |k, _| k.to_s }.map { |k, v| "#{k}: #{v.inspect rescue $!.message}" }.join("\n")
|
||||
end unless self.class.method_defined?(:debug_hash)
|
||||
%>
|
||||
|
||||
Request parameters
|
||||
<%= request_dump %>
|
||||
|
||||
Session dump
|
||||
<%= debug_hash @request.session %>
|
||||
|
||||
Env dump
|
||||
<%= debug_hash @request.env.slice(*@request.class::ENV_METHODS) %>
|
||||
|
||||
Response headers
|
||||
<%= defined?(@response) ? @response.headers.inspect.gsub(',', ",\n") : 'None' %>
|
|
@ -0,0 +1,15 @@
|
|||
<%
|
||||
traces = { "Application Trace" => @application_trace,
|
||||
"Framework Trace" => @framework_trace,
|
||||
"Full Trace" => @full_trace }
|
||||
%>
|
||||
|
||||
Rails.root: <%= defined?(Rails) && Rails.respond_to?(:root) ? Rails.root : "unset" %>
|
||||
|
||||
<% traces.each do |name, trace| %>
|
||||
<% if trace.any? %>
|
||||
<%= name %>
|
||||
<%= trace.join("\n") %>
|
||||
|
||||
<% end %>
|
||||
<% end %>
|
|
@ -8,7 +8,7 @@
|
|||
</header>
|
||||
|
||||
<div id="container">
|
||||
<h2><%= @exception.message %></h2>
|
||||
<h2><%= h @exception.message %></h2>
|
||||
|
||||
<%= render template: "rescues/_source" %>
|
||||
<%= render template: "rescues/_trace" %>
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
</header>
|
||||
|
||||
<div id="container">
|
||||
<h2><%= @exception.message %></h2>
|
||||
<h2><%= h @exception.message %></h2>
|
||||
</div>
|
|
@ -0,0 +1,3 @@
|
|||
Template is missing
|
||||
|
||||
<%= @exception.message %>
|
|
@ -2,7 +2,7 @@
|
|||
<h1>Routing Error</h1>
|
||||
</header>
|
||||
<div id="container">
|
||||
<h2><%= @exception.message %></h2>
|
||||
<h2><%= h @exception.message %></h2>
|
||||
<% unless @exception.failures.empty? %>
|
||||
<p>
|
||||
<h2>Failure reasons:</h2>
|
|
@ -0,0 +1,11 @@
|
|||
Routing Error
|
||||
|
||||
<%= @exception.message %>
|
||||
<% unless @exception.failures.empty? %>
|
||||
Failure reasons:
|
||||
<% @exception.failures.each do |route, reason| %>
|
||||
- <%= route.inspect.delete('\\') %></code> failed because <%= reason.downcase %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= render template: "rescues/_trace", format: :text %>
|
|
@ -10,7 +10,7 @@
|
|||
<p>
|
||||
Showing <i><%= @exception.file_name %></i> where line <b>#<%= @exception.line_number %></b> raised:
|
||||
</p>
|
||||
<pre><code><%= @exception.message %></code></pre>
|
||||
<pre><code><%= h @exception.message %></code></pre>
|
||||
|
||||
<div class="source">
|
||||
<div class="info">
|
|
@ -0,0 +1,8 @@
|
|||
<% @source_extract = @exception.source_extract(0, :html) %>
|
||||
<%= @exception.original_exception.class.to_s %> in <%= @request.parameters["controller"].camelize if @request.parameters["controller"] %>#<%= @request.parameters["action"] %>
|
||||
|
||||
Showing <%= @exception.file_name %> where line #<%= @exception.line_number %> raised:
|
||||
<%= @exception.message %>
|
||||
<%= @exception.sub_template_message %>
|
||||
<%= render template: "rescues/_trace", format: :text %>
|
||||
<%= render template: "rescues/_request_and_response", format: :text %>
|
|
@ -2,5 +2,5 @@
|
|||
<h1>Unknown action</h1>
|
||||
</header>
|
||||
<div id="container">
|
||||
<h2><%= @exception.message %></h2>
|
||||
<h2><%= h @exception.message %></h2>
|
||||
</div>
|
|
@ -0,0 +1,3 @@
|
|||
Unknown action
|
||||
|
||||
<%= @exception.message %>
|
|
@ -64,28 +64,6 @@ module RackTestUtils
|
|||
extend self
|
||||
end
|
||||
|
||||
module RenderERBUtils
|
||||
def view
|
||||
@view ||= begin
|
||||
path = ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH)
|
||||
view_paths = ActionView::PathSet.new([path])
|
||||
ActionView::Base.new(view_paths)
|
||||
end
|
||||
end
|
||||
|
||||
def render_erb(string)
|
||||
@virtual_path = nil
|
||||
|
||||
template = ActionView::Template.new(
|
||||
string.strip,
|
||||
"test template",
|
||||
ActionView::Template::Handlers::ERB,
|
||||
{})
|
||||
|
||||
template.render(self, {}).strip
|
||||
end
|
||||
end
|
||||
|
||||
SharedTestRoutes = ActionDispatch::Routing::RouteSet.new
|
||||
|
||||
module ActionDispatch
|
||||
|
@ -268,6 +246,8 @@ class Rack::TestCase < ActionDispatch::IntegrationTest
|
|||
end
|
||||
end
|
||||
|
||||
ActionController::Base.superclass.send(:include, ActionView::Layouts)
|
||||
|
||||
module ActionController
|
||||
class Base
|
||||
include ActionController::Testing
|
||||
|
@ -290,15 +270,8 @@ module ActionController
|
|||
end
|
||||
end
|
||||
|
||||
class ::ApplicationController < ActionController::Base
|
||||
end
|
||||
|
||||
module ActionView
|
||||
class TestCase
|
||||
# Must repeat the setup because AV::TestCase is a duplication
|
||||
# of AC::TestCase
|
||||
include ActionDispatch::SharedRoutes
|
||||
end
|
||||
class ::ApplicationController < ActionController::Base
|
||||
end
|
||||
|
||||
class Workshop
|
||||
|
|
|
@ -8,6 +8,9 @@ require 'abstract_unit'
|
|||
require 'controller/fake_controllers'
|
||||
|
||||
require 'action_mailer'
|
||||
require 'action_view'
|
||||
|
||||
ActionMailer::Base.send(:include, ActionView::Layouts)
|
||||
ActionMailer::Base.view_paths = FIXTURE_LOAD_PATH
|
||||
|
||||
class AssertSelectTest < ActionController::TestCase
|
||||
|
|
|
@ -17,7 +17,7 @@ class ActionController::Base
|
|||
def assigns(key = nil)
|
||||
assigns = {}
|
||||
instance_variables.each do |ivar|
|
||||
next if ActionController::Base.protected_instance_variables.include?(ivar)
|
||||
next if ActionController::Base.default_protected_instance_vars.include?(ivar)
|
||||
assigns[ivar[1..-1]] = instance_variable_get(ivar)
|
||||
end
|
||||
|
||||
|
|
|
@ -2,6 +2,94 @@ require 'abstract_unit'
|
|||
require 'active_support/concurrency/latch'
|
||||
|
||||
module ActionController
|
||||
class SSETest < ActionController::TestCase
|
||||
class SSETestController < ActionController::Base
|
||||
include ActionController::Live
|
||||
|
||||
def basic_sse
|
||||
response.headers['Content-Type'] = 'text/event-stream'
|
||||
sse = SSE.new(response.stream)
|
||||
sse.write("{\"name\":\"John\"}")
|
||||
sse.write({ name: "Ryan" })
|
||||
ensure
|
||||
sse.close
|
||||
end
|
||||
|
||||
def sse_with_event
|
||||
sse = SSE.new(response.stream, event: "send-name")
|
||||
sse.write("{\"name\":\"John\"}")
|
||||
sse.write({ name: "Ryan" })
|
||||
ensure
|
||||
sse.close
|
||||
end
|
||||
|
||||
def sse_with_retry
|
||||
sse = SSE.new(response.stream, retry: 1000)
|
||||
sse.write("{\"name\":\"John\"}")
|
||||
sse.write({ name: "Ryan" }, retry: 1500)
|
||||
ensure
|
||||
sse.close
|
||||
end
|
||||
|
||||
def sse_with_id
|
||||
sse = SSE.new(response.stream)
|
||||
sse.write("{\"name\":\"John\"}", id: 1)
|
||||
sse.write({ name: "Ryan" }, id: 2)
|
||||
ensure
|
||||
sse.close
|
||||
end
|
||||
end
|
||||
|
||||
tests SSETestController
|
||||
|
||||
def wait_for_response_stream_close
|
||||
while !response.stream.closed?
|
||||
sleep 0.01
|
||||
end
|
||||
end
|
||||
|
||||
def test_basic_sse
|
||||
get :basic_sse
|
||||
|
||||
wait_for_response_stream_close
|
||||
assert_match(/data: {\"name\":\"John\"}/, response.body)
|
||||
assert_match(/data: {\"name\":\"Ryan\"}/, response.body)
|
||||
end
|
||||
|
||||
def test_sse_with_event_name
|
||||
get :sse_with_event
|
||||
|
||||
wait_for_response_stream_close
|
||||
assert_match(/data: {\"name\":\"John\"}/, response.body)
|
||||
assert_match(/data: {\"name\":\"Ryan\"}/, response.body)
|
||||
assert_match(/event: send-name/, response.body)
|
||||
end
|
||||
|
||||
def test_sse_with_retry
|
||||
get :sse_with_retry
|
||||
|
||||
wait_for_response_stream_close
|
||||
first_response, second_response = response.body.split("\n\n")
|
||||
assert_match(/data: {\"name\":\"John\"}/, first_response)
|
||||
assert_match(/retry: 1000/, first_response)
|
||||
|
||||
assert_match(/data: {\"name\":\"Ryan\"}/, second_response)
|
||||
assert_match(/retry: 1500/, second_response)
|
||||
end
|
||||
|
||||
def test_sse_with_id
|
||||
get :sse_with_id
|
||||
|
||||
wait_for_response_stream_close
|
||||
first_response, second_response = response.body.split("\n\n")
|
||||
assert_match(/data: {\"name\":\"John\"}/, first_response)
|
||||
assert_match(/id: 1/, first_response)
|
||||
|
||||
assert_match(/data: {\"name\":\"Ryan\"}/, second_response)
|
||||
assert_match(/id: 2/, second_response)
|
||||
end
|
||||
end
|
||||
|
||||
class LiveStreamTest < ActionController::TestCase
|
||||
class TestController < ActionController::Base
|
||||
include ActionController::Live
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
require 'abstract_unit'
|
||||
|
||||
class StarStarMimeController < ActionController::Base
|
||||
layout nil
|
||||
|
||||
def index
|
||||
render
|
||||
end
|
||||
end
|
||||
|
||||
class StarStarMimeControllerTest < ActionController::TestCase
|
||||
tests StarStarMimeController
|
||||
|
||||
def test_javascript_with_format
|
||||
@request.accept = "text/javascript"
|
||||
get :index, :format => 'js'
|
||||
assert_match "function addition(a,b){ return a+b; }", @response.body
|
||||
end
|
||||
|
||||
def test_javascript_with_no_format
|
||||
@request.accept = "text/javascript"
|
||||
get :index
|
||||
assert_match "function addition(a,b){ return a+b; }", @response.body
|
||||
end
|
||||
|
||||
def test_javascript_with_no_format_only_star_star
|
||||
@request.accept = "*/*"
|
||||
get :index
|
||||
assert_match "function addition(a,b){ return a+b; }", @response.body
|
||||
end
|
||||
end
|
||||
|
||||
class AbstractPostController < ActionController::Base
|
||||
self.view_paths = File.dirname(__FILE__) + "/../../fixtures/post_test/"
|
||||
end
|
||||
|
||||
# For testing layouts which are set automatically
|
||||
class PostController < AbstractPostController
|
||||
around_action :with_iphone
|
||||
|
||||
def index
|
||||
respond_to(:html, :iphone, :js)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def with_iphone
|
||||
request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone"
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
class SuperPostController < PostController
|
||||
end
|
||||
|
||||
class MimeControllerLayoutsTest < ActionController::TestCase
|
||||
tests PostController
|
||||
|
||||
def setup
|
||||
super
|
||||
@request.host = "www.example.com"
|
||||
Mime::Type.register_alias("text/html", :iphone)
|
||||
end
|
||||
|
||||
def teardown
|
||||
super
|
||||
Mime::Type.unregister(:iphone)
|
||||
end
|
||||
|
||||
def test_missing_layout_renders_properly
|
||||
get :index
|
||||
assert_equal '<html><div id="html">Hello Firefox</div></html>', @response.body
|
||||
|
||||
@request.accept = "text/iphone"
|
||||
get :index
|
||||
assert_equal 'Hello iPhone', @response.body
|
||||
end
|
||||
|
||||
def test_format_with_inherited_layouts
|
||||
@controller = SuperPostController.new
|
||||
|
||||
get :index
|
||||
assert_equal '<html><div id="html">Super Firefox</div></html>', @response.body
|
||||
|
||||
@request.accept = "text/iphone"
|
||||
get :index
|
||||
assert_equal '<html><div id="super_iphone">Super iPhone</div></html>', @response.body
|
||||
end
|
||||
|
||||
def test_non_navigational_format_with_no_template_fallbacks_to_html_template_with_no_layout
|
||||
get :index, :format => :js
|
||||
assert_equal "Hello Firefox", @response.body
|
||||
end
|
||||
end
|
|
@ -0,0 +1,493 @@
|
|||
require 'abstract_unit'
|
||||
|
||||
class RespondToController < ActionController::Base
|
||||
layout :set_layout
|
||||
|
||||
def html_xml_or_rss
|
||||
respond_to do |type|
|
||||
type.html { render :text => "HTML" }
|
||||
type.xml { render :text => "XML" }
|
||||
type.rss { render :text => "RSS" }
|
||||
type.all { render :text => "Nothing" }
|
||||
end
|
||||
end
|
||||
|
||||
def js_or_html
|
||||
respond_to do |type|
|
||||
type.html { render :text => "HTML" }
|
||||
type.js { render :text => "JS" }
|
||||
type.all { render :text => "Nothing" }
|
||||
end
|
||||
end
|
||||
|
||||
def json_or_yaml
|
||||
respond_to do |type|
|
||||
type.json { render :text => "JSON" }
|
||||
type.yaml { render :text => "YAML" }
|
||||
end
|
||||
end
|
||||
|
||||
def html_or_xml
|
||||
respond_to do |type|
|
||||
type.html { render :text => "HTML" }
|
||||
type.xml { render :text => "XML" }
|
||||
type.all { render :text => "Nothing" }
|
||||
end
|
||||
end
|
||||
|
||||
def json_xml_or_html
|
||||
respond_to do |type|
|
||||
type.json { render :text => 'JSON' }
|
||||
type.xml { render :xml => 'XML' }
|
||||
type.html { render :text => 'HTML' }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def forced_xml
|
||||
request.format = :xml
|
||||
|
||||
respond_to do |type|
|
||||
type.html { render :text => "HTML" }
|
||||
type.xml { render :text => "XML" }
|
||||
end
|
||||
end
|
||||
|
||||
def just_xml
|
||||
respond_to do |type|
|
||||
type.xml { render :text => "XML" }
|
||||
end
|
||||
end
|
||||
|
||||
def using_defaults
|
||||
respond_to do |type|
|
||||
type.html
|
||||
type.xml
|
||||
end
|
||||
end
|
||||
|
||||
def using_defaults_with_type_list
|
||||
respond_to(:html, :xml)
|
||||
end
|
||||
|
||||
def using_defaults_with_all
|
||||
respond_to do |type|
|
||||
type.html
|
||||
type.all{ render text: "ALL" }
|
||||
end
|
||||
end
|
||||
|
||||
def made_for_content_type
|
||||
respond_to do |type|
|
||||
type.rss { render :text => "RSS" }
|
||||
type.atom { render :text => "ATOM" }
|
||||
type.all { render :text => "Nothing" }
|
||||
end
|
||||
end
|
||||
|
||||
def custom_type_handling
|
||||
respond_to do |type|
|
||||
type.html { render :text => "HTML" }
|
||||
type.custom("application/crazy-xml") { render :text => "Crazy XML" }
|
||||
type.all { render :text => "Nothing" }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def custom_constant_handling
|
||||
respond_to do |type|
|
||||
type.html { render :text => "HTML" }
|
||||
type.mobile { render :text => "Mobile" }
|
||||
end
|
||||
end
|
||||
|
||||
def custom_constant_handling_without_block
|
||||
respond_to do |type|
|
||||
type.html { render :text => "HTML" }
|
||||
type.mobile
|
||||
end
|
||||
end
|
||||
|
||||
def handle_any
|
||||
respond_to do |type|
|
||||
type.html { render :text => "HTML" }
|
||||
type.any(:js, :xml) { render :text => "Either JS or XML" }
|
||||
end
|
||||
end
|
||||
|
||||
def handle_any_any
|
||||
respond_to do |type|
|
||||
type.html { render :text => 'HTML' }
|
||||
type.any { render :text => 'Whatever you ask for, I got it' }
|
||||
end
|
||||
end
|
||||
|
||||
def all_types_with_layout
|
||||
respond_to do |type|
|
||||
type.html
|
||||
end
|
||||
end
|
||||
|
||||
def iphone_with_html_response_type
|
||||
request.format = :iphone if request.env["HTTP_ACCEPT"] == "text/iphone"
|
||||
|
||||
respond_to do |type|
|
||||
type.html { @type = "Firefox" }
|
||||
type.iphone { @type = "iPhone" }
|
||||
end
|
||||
end
|
||||
|
||||
def iphone_with_html_response_type_without_layout
|
||||
request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone"
|
||||
|
||||
respond_to do |type|
|
||||
type.html { @type = "Firefox"; render :action => "iphone_with_html_response_type" }
|
||||
type.iphone { @type = "iPhone" ; render :action => "iphone_with_html_response_type" }
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def set_layout
|
||||
case action_name
|
||||
when "all_types_with_layout", "iphone_with_html_response_type"
|
||||
"respond_to/layouts/standard"
|
||||
when "iphone_with_html_response_type_without_layout"
|
||||
"respond_to/layouts/missing"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class RespondToControllerTest < ActionController::TestCase
|
||||
tests RespondToController
|
||||
|
||||
def setup
|
||||
super
|
||||
@request.host = "www.example.com"
|
||||
Mime::Type.register_alias("text/html", :iphone)
|
||||
Mime::Type.register("text/x-mobile", :mobile)
|
||||
end
|
||||
|
||||
def teardown
|
||||
super
|
||||
Mime::Type.unregister(:iphone)
|
||||
Mime::Type.unregister(:mobile)
|
||||
end
|
||||
|
||||
def test_html
|
||||
@request.accept = "text/html"
|
||||
get :js_or_html
|
||||
assert_equal 'HTML', @response.body
|
||||
|
||||
get :html_or_xml
|
||||
assert_equal 'HTML', @response.body
|
||||
|
||||
assert_raises(ActionController::UnknownFormat) do
|
||||
get :just_xml
|
||||
end
|
||||
end
|
||||
|
||||
def test_all
|
||||
@request.accept = "*/*"
|
||||
get :js_or_html
|
||||
assert_equal 'HTML', @response.body # js is not part of all
|
||||
|
||||
get :html_or_xml
|
||||
assert_equal 'HTML', @response.body
|
||||
|
||||
get :just_xml
|
||||
assert_equal 'XML', @response.body
|
||||
end
|
||||
|
||||
def test_xml
|
||||
@request.accept = "application/xml"
|
||||
get :html_xml_or_rss
|
||||
assert_equal 'XML', @response.body
|
||||
end
|
||||
|
||||
def test_js_or_html
|
||||
@request.accept = "text/javascript, text/html"
|
||||
xhr :get, :js_or_html
|
||||
assert_equal 'JS', @response.body
|
||||
|
||||
@request.accept = "text/javascript, text/html"
|
||||
xhr :get, :html_or_xml
|
||||
assert_equal 'HTML', @response.body
|
||||
|
||||
@request.accept = "text/javascript, text/html"
|
||||
|
||||
assert_raises(ActionController::UnknownFormat) do
|
||||
xhr :get, :just_xml
|
||||
end
|
||||
end
|
||||
|
||||
def test_json_or_yaml_with_leading_star_star
|
||||
@request.accept = "*/*, application/json"
|
||||
get :json_xml_or_html
|
||||
assert_equal 'HTML', @response.body
|
||||
|
||||
@request.accept = "*/* , application/json"
|
||||
get :json_xml_or_html
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_json_or_yaml
|
||||
xhr :get, :json_or_yaml
|
||||
assert_equal 'JSON', @response.body
|
||||
|
||||
get :json_or_yaml, :format => 'json'
|
||||
assert_equal 'JSON', @response.body
|
||||
|
||||
get :json_or_yaml, :format => 'yaml'
|
||||
assert_equal 'YAML', @response.body
|
||||
|
||||
{ 'YAML' => %w(text/yaml),
|
||||
'JSON' => %w(application/json text/x-json)
|
||||
}.each do |body, content_types|
|
||||
content_types.each do |content_type|
|
||||
@request.accept = content_type
|
||||
get :json_or_yaml
|
||||
assert_equal body, @response.body
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_js_or_anything
|
||||
@request.accept = "text/javascript, */*"
|
||||
xhr :get, :js_or_html
|
||||
assert_equal 'JS', @response.body
|
||||
|
||||
xhr :get, :html_or_xml
|
||||
assert_equal 'HTML', @response.body
|
||||
|
||||
xhr :get, :just_xml
|
||||
assert_equal 'XML', @response.body
|
||||
end
|
||||
|
||||
def test_using_defaults
|
||||
@request.accept = "*/*"
|
||||
get :using_defaults
|
||||
assert_equal "text/html", @response.content_type
|
||||
assert_equal 'Hello world!', @response.body
|
||||
|
||||
@request.accept = "application/xml"
|
||||
get :using_defaults
|
||||
assert_equal "application/xml", @response.content_type
|
||||
assert_equal "<p>Hello world!</p>\n", @response.body
|
||||
end
|
||||
|
||||
def test_using_defaults_with_all
|
||||
@request.accept = "*/*"
|
||||
get :using_defaults_with_all
|
||||
assert_equal "HTML!", @response.body.strip
|
||||
|
||||
@request.accept = "text/html"
|
||||
get :using_defaults_with_all
|
||||
assert_equal "HTML!", @response.body.strip
|
||||
|
||||
@request.accept = "application/json"
|
||||
get :using_defaults_with_all
|
||||
assert_equal "ALL", @response.body
|
||||
end
|
||||
|
||||
def test_using_defaults_with_type_list
|
||||
@request.accept = "*/*"
|
||||
get :using_defaults_with_type_list
|
||||
assert_equal "text/html", @response.content_type
|
||||
assert_equal 'Hello world!', @response.body
|
||||
|
||||
@request.accept = "application/xml"
|
||||
get :using_defaults_with_type_list
|
||||
assert_equal "application/xml", @response.content_type
|
||||
assert_equal "<p>Hello world!</p>\n", @response.body
|
||||
end
|
||||
|
||||
def test_with_atom_content_type
|
||||
@request.accept = ""
|
||||
@request.env["CONTENT_TYPE"] = "application/atom+xml"
|
||||
xhr :get, :made_for_content_type
|
||||
assert_equal "ATOM", @response.body
|
||||
end
|
||||
|
||||
def test_with_rss_content_type
|
||||
@request.accept = ""
|
||||
@request.env["CONTENT_TYPE"] = "application/rss+xml"
|
||||
xhr :get, :made_for_content_type
|
||||
assert_equal "RSS", @response.body
|
||||
end
|
||||
|
||||
def test_synonyms
|
||||
@request.accept = "application/javascript"
|
||||
get :js_or_html
|
||||
assert_equal 'JS', @response.body
|
||||
|
||||
@request.accept = "application/x-xml"
|
||||
get :html_xml_or_rss
|
||||
assert_equal "XML", @response.body
|
||||
end
|
||||
|
||||
def test_custom_types
|
||||
@request.accept = "application/crazy-xml"
|
||||
get :custom_type_handling
|
||||
assert_equal "application/crazy-xml", @response.content_type
|
||||
assert_equal 'Crazy XML', @response.body
|
||||
|
||||
@request.accept = "text/html"
|
||||
get :custom_type_handling
|
||||
assert_equal "text/html", @response.content_type
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_xhtml_alias
|
||||
@request.accept = "application/xhtml+xml,application/xml"
|
||||
get :html_or_xml
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_firefox_simulation
|
||||
@request.accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
|
||||
get :html_or_xml
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_handle_any
|
||||
@request.accept = "*/*"
|
||||
get :handle_any
|
||||
assert_equal 'HTML', @response.body
|
||||
|
||||
@request.accept = "text/javascript"
|
||||
get :handle_any
|
||||
assert_equal 'Either JS or XML', @response.body
|
||||
|
||||
@request.accept = "text/xml"
|
||||
get :handle_any
|
||||
assert_equal 'Either JS or XML', @response.body
|
||||
end
|
||||
|
||||
def test_handle_any_any
|
||||
@request.accept = "*/*"
|
||||
get :handle_any_any
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_handle_any_any_parameter_format
|
||||
get :handle_any_any, {:format=>'html'}
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_handle_any_any_explicit_html
|
||||
@request.accept = "text/html"
|
||||
get :handle_any_any
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_handle_any_any_javascript
|
||||
@request.accept = "text/javascript"
|
||||
get :handle_any_any
|
||||
assert_equal 'Whatever you ask for, I got it', @response.body
|
||||
end
|
||||
|
||||
def test_handle_any_any_xml
|
||||
@request.accept = "text/xml"
|
||||
get :handle_any_any
|
||||
assert_equal 'Whatever you ask for, I got it', @response.body
|
||||
end
|
||||
|
||||
def test_browser_check_with_any_any
|
||||
@request.accept = "application/json, application/xml"
|
||||
get :json_xml_or_html
|
||||
assert_equal 'JSON', @response.body
|
||||
|
||||
@request.accept = "application/json, application/xml, */*"
|
||||
get :json_xml_or_html
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_html_type_with_layout
|
||||
@request.accept = "text/html"
|
||||
get :all_types_with_layout
|
||||
assert_equal '<html><div id="html">HTML for all_types_with_layout</div></html>', @response.body
|
||||
end
|
||||
|
||||
def test_xhr
|
||||
xhr :get, :js_or_html
|
||||
assert_equal 'JS', @response.body
|
||||
end
|
||||
|
||||
def test_custom_constant
|
||||
get :custom_constant_handling, :format => "mobile"
|
||||
assert_equal "text/x-mobile", @response.content_type
|
||||
assert_equal "Mobile", @response.body
|
||||
end
|
||||
|
||||
def test_custom_constant_handling_without_block
|
||||
get :custom_constant_handling_without_block, :format => "mobile"
|
||||
assert_equal "text/x-mobile", @response.content_type
|
||||
assert_equal "Mobile", @response.body
|
||||
end
|
||||
|
||||
def test_forced_format
|
||||
get :html_xml_or_rss
|
||||
assert_equal "HTML", @response.body
|
||||
|
||||
get :html_xml_or_rss, :format => "html"
|
||||
assert_equal "HTML", @response.body
|
||||
|
||||
get :html_xml_or_rss, :format => "xml"
|
||||
assert_equal "XML", @response.body
|
||||
|
||||
get :html_xml_or_rss, :format => "rss"
|
||||
assert_equal "RSS", @response.body
|
||||
end
|
||||
|
||||
def test_internally_forced_format
|
||||
get :forced_xml
|
||||
assert_equal "XML", @response.body
|
||||
|
||||
get :forced_xml, :format => "html"
|
||||
assert_equal "XML", @response.body
|
||||
end
|
||||
|
||||
def test_extension_synonyms
|
||||
get :html_xml_or_rss, :format => "xhtml"
|
||||
assert_equal "HTML", @response.body
|
||||
end
|
||||
|
||||
def test_render_action_for_html
|
||||
@controller.instance_eval do
|
||||
def render(*args)
|
||||
@action = args.first[:action] unless args.empty?
|
||||
@action ||= action_name
|
||||
|
||||
response.body = "#{@action} - #{formats}"
|
||||
end
|
||||
end
|
||||
|
||||
get :using_defaults
|
||||
assert_equal "using_defaults - #{[:html].to_s}", @response.body
|
||||
|
||||
get :using_defaults, :format => "xml"
|
||||
assert_equal "using_defaults - #{[:xml].to_s}", @response.body
|
||||
end
|
||||
|
||||
def test_format_with_custom_response_type
|
||||
get :iphone_with_html_response_type
|
||||
assert_equal '<html><div id="html">Hello future from Firefox!</div></html>', @response.body
|
||||
|
||||
get :iphone_with_html_response_type, :format => "iphone"
|
||||
assert_equal "text/html", @response.content_type
|
||||
assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body
|
||||
end
|
||||
|
||||
def test_format_with_custom_response_type_and_request_headers
|
||||
@request.accept = "text/iphone"
|
||||
get :iphone_with_html_response_type
|
||||
assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body
|
||||
assert_equal "text/html", @response.content_type
|
||||
end
|
||||
|
||||
def test_invalid_format
|
||||
assert_raises(ActionController::UnknownFormat) do
|
||||
get :using_defaults, :format => "invalidformat"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,529 +1,5 @@
|
|||
require 'abstract_unit'
|
||||
require 'controller/fake_models'
|
||||
require 'active_support/core_ext/hash/conversions'
|
||||
|
||||
class StarStarMimeController < ActionController::Base
|
||||
layout nil
|
||||
|
||||
def index
|
||||
render
|
||||
end
|
||||
end
|
||||
|
||||
class RespondToController < ActionController::Base
|
||||
layout :set_layout
|
||||
|
||||
def html_xml_or_rss
|
||||
respond_to do |type|
|
||||
type.html { render :text => "HTML" }
|
||||
type.xml { render :text => "XML" }
|
||||
type.rss { render :text => "RSS" }
|
||||
type.all { render :text => "Nothing" }
|
||||
end
|
||||
end
|
||||
|
||||
def js_or_html
|
||||
respond_to do |type|
|
||||
type.html { render :text => "HTML" }
|
||||
type.js { render :text => "JS" }
|
||||
type.all { render :text => "Nothing" }
|
||||
end
|
||||
end
|
||||
|
||||
def json_or_yaml
|
||||
respond_to do |type|
|
||||
type.json { render :text => "JSON" }
|
||||
type.yaml { render :text => "YAML" }
|
||||
end
|
||||
end
|
||||
|
||||
def html_or_xml
|
||||
respond_to do |type|
|
||||
type.html { render :text => "HTML" }
|
||||
type.xml { render :text => "XML" }
|
||||
type.all { render :text => "Nothing" }
|
||||
end
|
||||
end
|
||||
|
||||
def json_xml_or_html
|
||||
respond_to do |type|
|
||||
type.json { render :text => 'JSON' }
|
||||
type.xml { render :xml => 'XML' }
|
||||
type.html { render :text => 'HTML' }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def forced_xml
|
||||
request.format = :xml
|
||||
|
||||
respond_to do |type|
|
||||
type.html { render :text => "HTML" }
|
||||
type.xml { render :text => "XML" }
|
||||
end
|
||||
end
|
||||
|
||||
def just_xml
|
||||
respond_to do |type|
|
||||
type.xml { render :text => "XML" }
|
||||
end
|
||||
end
|
||||
|
||||
def using_defaults
|
||||
respond_to do |type|
|
||||
type.html
|
||||
type.xml
|
||||
end
|
||||
end
|
||||
|
||||
def using_defaults_with_type_list
|
||||
respond_to(:html, :xml)
|
||||
end
|
||||
|
||||
def using_defaults_with_all
|
||||
respond_to do |type|
|
||||
type.html
|
||||
type.all{ render text: "ALL" }
|
||||
end
|
||||
end
|
||||
|
||||
def made_for_content_type
|
||||
respond_to do |type|
|
||||
type.rss { render :text => "RSS" }
|
||||
type.atom { render :text => "ATOM" }
|
||||
type.all { render :text => "Nothing" }
|
||||
end
|
||||
end
|
||||
|
||||
def custom_type_handling
|
||||
respond_to do |type|
|
||||
type.html { render :text => "HTML" }
|
||||
type.custom("application/crazy-xml") { render :text => "Crazy XML" }
|
||||
type.all { render :text => "Nothing" }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def custom_constant_handling
|
||||
respond_to do |type|
|
||||
type.html { render :text => "HTML" }
|
||||
type.mobile { render :text => "Mobile" }
|
||||
end
|
||||
end
|
||||
|
||||
def custom_constant_handling_without_block
|
||||
respond_to do |type|
|
||||
type.html { render :text => "HTML" }
|
||||
type.mobile
|
||||
end
|
||||
end
|
||||
|
||||
def handle_any
|
||||
respond_to do |type|
|
||||
type.html { render :text => "HTML" }
|
||||
type.any(:js, :xml) { render :text => "Either JS or XML" }
|
||||
end
|
||||
end
|
||||
|
||||
def handle_any_any
|
||||
respond_to do |type|
|
||||
type.html { render :text => 'HTML' }
|
||||
type.any { render :text => 'Whatever you ask for, I got it' }
|
||||
end
|
||||
end
|
||||
|
||||
def all_types_with_layout
|
||||
respond_to do |type|
|
||||
type.html
|
||||
end
|
||||
end
|
||||
|
||||
def iphone_with_html_response_type
|
||||
request.format = :iphone if request.env["HTTP_ACCEPT"] == "text/iphone"
|
||||
|
||||
respond_to do |type|
|
||||
type.html { @type = "Firefox" }
|
||||
type.iphone { @type = "iPhone" }
|
||||
end
|
||||
end
|
||||
|
||||
def iphone_with_html_response_type_without_layout
|
||||
request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone"
|
||||
|
||||
respond_to do |type|
|
||||
type.html { @type = "Firefox"; render :action => "iphone_with_html_response_type" }
|
||||
type.iphone { @type = "iPhone" ; render :action => "iphone_with_html_response_type" }
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def set_layout
|
||||
case action_name
|
||||
when "all_types_with_layout", "iphone_with_html_response_type"
|
||||
"respond_to/layouts/standard"
|
||||
when "iphone_with_html_response_type_without_layout"
|
||||
"respond_to/layouts/missing"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class StarStarMimeControllerTest < ActionController::TestCase
|
||||
tests StarStarMimeController
|
||||
|
||||
def test_javascript_with_format
|
||||
@request.accept = "text/javascript"
|
||||
get :index, :format => 'js'
|
||||
assert_match "function addition(a,b){ return a+b; }", @response.body
|
||||
end
|
||||
|
||||
def test_javascript_with_no_format
|
||||
@request.accept = "text/javascript"
|
||||
get :index
|
||||
assert_match "function addition(a,b){ return a+b; }", @response.body
|
||||
end
|
||||
|
||||
def test_javascript_with_no_format_only_star_star
|
||||
@request.accept = "*/*"
|
||||
get :index
|
||||
assert_match "function addition(a,b){ return a+b; }", @response.body
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class RespondToControllerTest < ActionController::TestCase
|
||||
tests RespondToController
|
||||
|
||||
def setup
|
||||
super
|
||||
@request.host = "www.example.com"
|
||||
Mime::Type.register_alias("text/html", :iphone)
|
||||
Mime::Type.register("text/x-mobile", :mobile)
|
||||
end
|
||||
|
||||
def teardown
|
||||
super
|
||||
Mime::Type.unregister(:iphone)
|
||||
Mime::Type.unregister(:mobile)
|
||||
end
|
||||
|
||||
def test_html
|
||||
@request.accept = "text/html"
|
||||
get :js_or_html
|
||||
assert_equal 'HTML', @response.body
|
||||
|
||||
get :html_or_xml
|
||||
assert_equal 'HTML', @response.body
|
||||
|
||||
assert_raises(ActionController::UnknownFormat) do
|
||||
get :just_xml
|
||||
end
|
||||
end
|
||||
|
||||
def test_all
|
||||
@request.accept = "*/*"
|
||||
get :js_or_html
|
||||
assert_equal 'HTML', @response.body # js is not part of all
|
||||
|
||||
get :html_or_xml
|
||||
assert_equal 'HTML', @response.body
|
||||
|
||||
get :just_xml
|
||||
assert_equal 'XML', @response.body
|
||||
end
|
||||
|
||||
def test_xml
|
||||
@request.accept = "application/xml"
|
||||
get :html_xml_or_rss
|
||||
assert_equal 'XML', @response.body
|
||||
end
|
||||
|
||||
def test_js_or_html
|
||||
@request.accept = "text/javascript, text/html"
|
||||
xhr :get, :js_or_html
|
||||
assert_equal 'JS', @response.body
|
||||
|
||||
@request.accept = "text/javascript, text/html"
|
||||
xhr :get, :html_or_xml
|
||||
assert_equal 'HTML', @response.body
|
||||
|
||||
@request.accept = "text/javascript, text/html"
|
||||
|
||||
assert_raises(ActionController::UnknownFormat) do
|
||||
xhr :get, :just_xml
|
||||
end
|
||||
end
|
||||
|
||||
def test_json_or_yaml_with_leading_star_star
|
||||
@request.accept = "*/*, application/json"
|
||||
get :json_xml_or_html
|
||||
assert_equal 'HTML', @response.body
|
||||
|
||||
@request.accept = "*/* , application/json"
|
||||
get :json_xml_or_html
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_json_or_yaml
|
||||
xhr :get, :json_or_yaml
|
||||
assert_equal 'JSON', @response.body
|
||||
|
||||
get :json_or_yaml, :format => 'json'
|
||||
assert_equal 'JSON', @response.body
|
||||
|
||||
get :json_or_yaml, :format => 'yaml'
|
||||
assert_equal 'YAML', @response.body
|
||||
|
||||
{ 'YAML' => %w(text/yaml),
|
||||
'JSON' => %w(application/json text/x-json)
|
||||
}.each do |body, content_types|
|
||||
content_types.each do |content_type|
|
||||
@request.accept = content_type
|
||||
get :json_or_yaml
|
||||
assert_equal body, @response.body
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_js_or_anything
|
||||
@request.accept = "text/javascript, */*"
|
||||
xhr :get, :js_or_html
|
||||
assert_equal 'JS', @response.body
|
||||
|
||||
xhr :get, :html_or_xml
|
||||
assert_equal 'HTML', @response.body
|
||||
|
||||
xhr :get, :just_xml
|
||||
assert_equal 'XML', @response.body
|
||||
end
|
||||
|
||||
def test_using_defaults
|
||||
@request.accept = "*/*"
|
||||
get :using_defaults
|
||||
assert_equal "text/html", @response.content_type
|
||||
assert_equal 'Hello world!', @response.body
|
||||
|
||||
@request.accept = "application/xml"
|
||||
get :using_defaults
|
||||
assert_equal "application/xml", @response.content_type
|
||||
assert_equal "<p>Hello world!</p>\n", @response.body
|
||||
end
|
||||
|
||||
def test_using_defaults_with_all
|
||||
@request.accept = "*/*"
|
||||
get :using_defaults_with_all
|
||||
assert_equal "HTML!", @response.body.strip
|
||||
|
||||
@request.accept = "text/html"
|
||||
get :using_defaults_with_all
|
||||
assert_equal "HTML!", @response.body.strip
|
||||
|
||||
@request.accept = "application/json"
|
||||
get :using_defaults_with_all
|
||||
assert_equal "ALL", @response.body
|
||||
end
|
||||
|
||||
def test_using_defaults_with_type_list
|
||||
@request.accept = "*/*"
|
||||
get :using_defaults_with_type_list
|
||||
assert_equal "text/html", @response.content_type
|
||||
assert_equal 'Hello world!', @response.body
|
||||
|
||||
@request.accept = "application/xml"
|
||||
get :using_defaults_with_type_list
|
||||
assert_equal "application/xml", @response.content_type
|
||||
assert_equal "<p>Hello world!</p>\n", @response.body
|
||||
end
|
||||
|
||||
def test_with_atom_content_type
|
||||
@request.accept = ""
|
||||
@request.env["CONTENT_TYPE"] = "application/atom+xml"
|
||||
xhr :get, :made_for_content_type
|
||||
assert_equal "ATOM", @response.body
|
||||
end
|
||||
|
||||
def test_with_rss_content_type
|
||||
@request.accept = ""
|
||||
@request.env["CONTENT_TYPE"] = "application/rss+xml"
|
||||
xhr :get, :made_for_content_type
|
||||
assert_equal "RSS", @response.body
|
||||
end
|
||||
|
||||
def test_synonyms
|
||||
@request.accept = "application/javascript"
|
||||
get :js_or_html
|
||||
assert_equal 'JS', @response.body
|
||||
|
||||
@request.accept = "application/x-xml"
|
||||
get :html_xml_or_rss
|
||||
assert_equal "XML", @response.body
|
||||
end
|
||||
|
||||
def test_custom_types
|
||||
@request.accept = "application/crazy-xml"
|
||||
get :custom_type_handling
|
||||
assert_equal "application/crazy-xml", @response.content_type
|
||||
assert_equal 'Crazy XML', @response.body
|
||||
|
||||
@request.accept = "text/html"
|
||||
get :custom_type_handling
|
||||
assert_equal "text/html", @response.content_type
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_xhtml_alias
|
||||
@request.accept = "application/xhtml+xml,application/xml"
|
||||
get :html_or_xml
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_firefox_simulation
|
||||
@request.accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
|
||||
get :html_or_xml
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_handle_any
|
||||
@request.accept = "*/*"
|
||||
get :handle_any
|
||||
assert_equal 'HTML', @response.body
|
||||
|
||||
@request.accept = "text/javascript"
|
||||
get :handle_any
|
||||
assert_equal 'Either JS or XML', @response.body
|
||||
|
||||
@request.accept = "text/xml"
|
||||
get :handle_any
|
||||
assert_equal 'Either JS or XML', @response.body
|
||||
end
|
||||
|
||||
def test_handle_any_any
|
||||
@request.accept = "*/*"
|
||||
get :handle_any_any
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_handle_any_any_parameter_format
|
||||
get :handle_any_any, {:format=>'html'}
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_handle_any_any_explicit_html
|
||||
@request.accept = "text/html"
|
||||
get :handle_any_any
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_handle_any_any_javascript
|
||||
@request.accept = "text/javascript"
|
||||
get :handle_any_any
|
||||
assert_equal 'Whatever you ask for, I got it', @response.body
|
||||
end
|
||||
|
||||
def test_handle_any_any_xml
|
||||
@request.accept = "text/xml"
|
||||
get :handle_any_any
|
||||
assert_equal 'Whatever you ask for, I got it', @response.body
|
||||
end
|
||||
|
||||
def test_browser_check_with_any_any
|
||||
@request.accept = "application/json, application/xml"
|
||||
get :json_xml_or_html
|
||||
assert_equal 'JSON', @response.body
|
||||
|
||||
@request.accept = "application/json, application/xml, */*"
|
||||
get :json_xml_or_html
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_html_type_with_layout
|
||||
@request.accept = "text/html"
|
||||
get :all_types_with_layout
|
||||
assert_equal '<html><div id="html">HTML for all_types_with_layout</div></html>', @response.body
|
||||
end
|
||||
|
||||
def test_xhr
|
||||
xhr :get, :js_or_html
|
||||
assert_equal 'JS', @response.body
|
||||
end
|
||||
|
||||
def test_custom_constant
|
||||
get :custom_constant_handling, :format => "mobile"
|
||||
assert_equal "text/x-mobile", @response.content_type
|
||||
assert_equal "Mobile", @response.body
|
||||
end
|
||||
|
||||
def test_custom_constant_handling_without_block
|
||||
get :custom_constant_handling_without_block, :format => "mobile"
|
||||
assert_equal "text/x-mobile", @response.content_type
|
||||
assert_equal "Mobile", @response.body
|
||||
end
|
||||
|
||||
def test_forced_format
|
||||
get :html_xml_or_rss
|
||||
assert_equal "HTML", @response.body
|
||||
|
||||
get :html_xml_or_rss, :format => "html"
|
||||
assert_equal "HTML", @response.body
|
||||
|
||||
get :html_xml_or_rss, :format => "xml"
|
||||
assert_equal "XML", @response.body
|
||||
|
||||
get :html_xml_or_rss, :format => "rss"
|
||||
assert_equal "RSS", @response.body
|
||||
end
|
||||
|
||||
def test_internally_forced_format
|
||||
get :forced_xml
|
||||
assert_equal "XML", @response.body
|
||||
|
||||
get :forced_xml, :format => "html"
|
||||
assert_equal "XML", @response.body
|
||||
end
|
||||
|
||||
def test_extension_synonyms
|
||||
get :html_xml_or_rss, :format => "xhtml"
|
||||
assert_equal "HTML", @response.body
|
||||
end
|
||||
|
||||
def test_render_action_for_html
|
||||
@controller.instance_eval do
|
||||
def render(*args)
|
||||
@action = args.first[:action] unless args.empty?
|
||||
@action ||= action_name
|
||||
|
||||
response.body = "#{@action} - #{formats}"
|
||||
end
|
||||
end
|
||||
|
||||
get :using_defaults
|
||||
assert_equal "using_defaults - #{[:html].to_s}", @response.body
|
||||
|
||||
get :using_defaults, :format => "xml"
|
||||
assert_equal "using_defaults - #{[:xml].to_s}", @response.body
|
||||
end
|
||||
|
||||
def test_format_with_custom_response_type
|
||||
get :iphone_with_html_response_type
|
||||
assert_equal '<html><div id="html">Hello future from Firefox!</div></html>', @response.body
|
||||
|
||||
get :iphone_with_html_response_type, :format => "iphone"
|
||||
assert_equal "text/html", @response.content_type
|
||||
assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body
|
||||
end
|
||||
|
||||
def test_format_with_custom_response_type_and_request_headers
|
||||
@request.accept = "text/iphone"
|
||||
get :iphone_with_html_response_type
|
||||
assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body
|
||||
assert_equal "text/html", @response.content_type
|
||||
end
|
||||
|
||||
def test_invalid_format
|
||||
assert_raises(ActionController::UnknownFormat) do
|
||||
get :using_defaults, :format => "invalidformat"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class RespondWithController < ActionController::Base
|
||||
respond_to :html, :json, :touch
|
||||
|
@ -631,6 +107,20 @@ class RenderJsonRespondWithController < RespondWithController
|
|||
end
|
||||
end
|
||||
|
||||
class CsvRespondWithController < ActionController::Base
|
||||
respond_to :csv
|
||||
|
||||
class RespondWithCsv
|
||||
def to_csv
|
||||
"c,s,v"
|
||||
end
|
||||
end
|
||||
|
||||
def index
|
||||
respond_with(RespondWithCsv.new)
|
||||
end
|
||||
end
|
||||
|
||||
class EmptyRespondWithController < ActionController::Base
|
||||
def index
|
||||
respond_with(Customer.new("david", 13))
|
||||
|
@ -847,6 +337,7 @@ class RespondWithControllerTest < ActionController::TestCase
|
|||
errors = { :name => :invalid }
|
||||
Customer.any_instance.stubs(:errors).returns(errors)
|
||||
put :using_resource
|
||||
|
||||
assert_equal "text/html", @response.content_type
|
||||
assert_equal 200, @response.status
|
||||
assert_equal "Edit world!\n", @response.body
|
||||
|
@ -1131,6 +622,23 @@ class RespondWithControllerTest < ActionController::TestCase
|
|||
RespondWithController.responder = ActionController::Responder
|
||||
end
|
||||
|
||||
def test_uses_renderer_if_an_api_behavior
|
||||
ActionController::Renderers.add :csv do |obj, options|
|
||||
send_data obj.to_csv, type: Mime::CSV
|
||||
end
|
||||
@controller = CsvRespondWithController.new
|
||||
get :index, format: 'csv'
|
||||
assert_equal Mime::CSV, @response.content_type
|
||||
assert_equal "c,s,v", @response.body
|
||||
end
|
||||
|
||||
def test_raises_missing_renderer_if_an_api_behavior_with_no_renderer
|
||||
@controller = CsvRespondWithController.new
|
||||
assert_raise ActionController::MissingRenderer do
|
||||
get :index, format: 'csv'
|
||||
end
|
||||
end
|
||||
|
||||
def test_error_is_raised_if_no_respond_to_is_declared_and_respond_with_is_called
|
||||
@controller = EmptyRespondWithController.new
|
||||
@request.accept = "*/*"
|
||||
|
@ -1154,69 +662,6 @@ class RespondWithControllerTest < ActionController::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
class AbstractPostController < ActionController::Base
|
||||
self.view_paths = File.dirname(__FILE__) + "/../fixtures/post_test/"
|
||||
end
|
||||
|
||||
# For testing layouts which are set automatically
|
||||
class PostController < AbstractPostController
|
||||
around_action :with_iphone
|
||||
|
||||
def index
|
||||
respond_to(:html, :iphone, :js)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def with_iphone
|
||||
request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone"
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
class SuperPostController < PostController
|
||||
end
|
||||
|
||||
class MimeControllerLayoutsTest < ActionController::TestCase
|
||||
tests PostController
|
||||
|
||||
def setup
|
||||
super
|
||||
@request.host = "www.example.com"
|
||||
Mime::Type.register_alias("text/html", :iphone)
|
||||
end
|
||||
|
||||
def teardown
|
||||
super
|
||||
Mime::Type.unregister(:iphone)
|
||||
end
|
||||
|
||||
def test_missing_layout_renders_properly
|
||||
get :index
|
||||
assert_equal '<html><div id="html">Hello Firefox</div></html>', @response.body
|
||||
|
||||
@request.accept = "text/iphone"
|
||||
get :index
|
||||
assert_equal 'Hello iPhone', @response.body
|
||||
end
|
||||
|
||||
def test_format_with_inherited_layouts
|
||||
@controller = SuperPostController.new
|
||||
|
||||
get :index
|
||||
assert_equal '<html><div id="html">Super Firefox</div></html>', @response.body
|
||||
|
||||
@request.accept = "text/iphone"
|
||||
get :index
|
||||
assert_equal '<html><div id="super_iphone">Super iPhone</div></html>', @response.body
|
||||
end
|
||||
|
||||
def test_non_navigational_format_with_no_template_fallbacks_to_html_template_with_no_layout
|
||||
get :index, :format => :js
|
||||
assert_equal "Hello Firefox", @response.body
|
||||
end
|
||||
end
|
||||
|
||||
class FlashResponder < ActionController::Responder
|
||||
def initialize(controller, resources, options={})
|
||||
super
|
File diff suppressed because it is too large
Load Diff
|
@ -128,6 +128,47 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
|
|||
assert_match(/ActionController::ParameterMissing/, body)
|
||||
end
|
||||
|
||||
test "rescue with text error for xhr request" do
|
||||
@app = DevelopmentApp
|
||||
xhr_request_env = {'action_dispatch.show_exceptions' => true, 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest'}
|
||||
|
||||
get "/", {}, xhr_request_env
|
||||
assert_response 500
|
||||
assert_no_match(/<body>/, body)
|
||||
assert_equal response.content_type, "text/plain"
|
||||
assert_match(/puke/, body)
|
||||
|
||||
get "/not_found", {}, xhr_request_env
|
||||
assert_response 404
|
||||
assert_no_match(/<body>/, body)
|
||||
assert_equal response.content_type, "text/plain"
|
||||
assert_match(/#{AbstractController::ActionNotFound.name}/, body)
|
||||
|
||||
get "/method_not_allowed", {}, xhr_request_env
|
||||
assert_response 405
|
||||
assert_no_match(/<body>/, body)
|
||||
assert_equal response.content_type, "text/plain"
|
||||
assert_match(/ActionController::MethodNotAllowed/, body)
|
||||
|
||||
get "/unknown_http_method", {}, xhr_request_env
|
||||
assert_response 405
|
||||
assert_no_match(/<body>/, body)
|
||||
assert_equal response.content_type, "text/plain"
|
||||
assert_match(/ActionController::UnknownHttpMethod/, body)
|
||||
|
||||
get "/bad_request", {}, xhr_request_env
|
||||
assert_response 400
|
||||
assert_no_match(/<body>/, body)
|
||||
assert_equal response.content_type, "text/plain"
|
||||
assert_match(/ActionController::BadRequest/, body)
|
||||
|
||||
get "/parameter_missing", {}, xhr_request_env
|
||||
assert_response 400
|
||||
assert_no_match(/<body>/, body)
|
||||
assert_equal response.content_type, "text/plain"
|
||||
assert_match(/ActionController::ParameterMissing/, body)
|
||||
end
|
||||
|
||||
test "does not show filtered parameters" do
|
||||
@app = DevelopmentApp
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../../symlink_parent
|
|
@ -28,12 +28,6 @@ class Customer < Struct.new(:name, :id)
|
|||
end
|
||||
end
|
||||
|
||||
class BadCustomer < Customer
|
||||
end
|
||||
|
||||
class GoodCustomer < Customer
|
||||
end
|
||||
|
||||
class ValidatedCustomer < Customer
|
||||
def errors
|
||||
if name =~ /Sikachu/i
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
* Only cache template digests if `config.cache_template_loading` id true.
|
||||
|
||||
*Josh Lauer*, *Justin Ridgewell*
|
||||
|
||||
* Added an `extname` hash option for `javascript_include_tag` method.
|
||||
|
||||
Before:
|
||||
|
|
|
@ -7,14 +7,7 @@ task :default => :test
|
|||
# Run the unit tests
|
||||
|
||||
desc "Run all unit tests"
|
||||
task :test => [:test_action_view, :test_active_record_integration]
|
||||
|
||||
Rake::TestTask.new(:test_action_view) do |t|
|
||||
t.libs << 'test'
|
||||
t.test_files = Dir.glob('test/template/**/*_test.rb').sort
|
||||
t.warning = true
|
||||
t.verbose = true
|
||||
end
|
||||
task :test => ["test:template", "test:integration:action_pack", "test:integration:active_record"]
|
||||
|
||||
namespace :test do
|
||||
task :isolated do
|
||||
|
@ -25,16 +18,28 @@ namespace :test do
|
|||
|
||||
Rake::TestTask.new(:template) do |t|
|
||||
t.libs << 'test'
|
||||
t.pattern = 'test/template/**/*.rb'
|
||||
t.test_files = Dir.glob('test/template/**/*_test.rb').sort
|
||||
t.warning = true
|
||||
t.verbose = true
|
||||
end
|
||||
end
|
||||
|
||||
desc 'ActiveRecord Integration Tests'
|
||||
Rake::TestTask.new(:test_active_record_integration) do |t|
|
||||
t.libs << 'test'
|
||||
t.test_files = Dir.glob("test/activerecord/*_test.rb")
|
||||
t.warning = true
|
||||
t.verbose = true
|
||||
namespace :integration do
|
||||
desc 'ActiveRecord Integration Tests'
|
||||
Rake::TestTask.new(:active_record) do |t|
|
||||
t.libs << 'test'
|
||||
t.test_files = Dir.glob("test/activerecord/*_test.rb")
|
||||
t.warning = true
|
||||
t.verbose = true
|
||||
end
|
||||
|
||||
desc 'ActionPack Integration Tests'
|
||||
Rake::TestTask.new(:action_pack) do |t|
|
||||
t.libs << 'test'
|
||||
t.test_files = Dir.glob("test/actionpack/**/*_test.rb")
|
||||
t.warning = true
|
||||
t.verbose = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
spec = eval(File.read('actionview.gemspec'))
|
||||
|
|
|
@ -34,10 +34,13 @@ module ActionView
|
|||
autoload :Digestor
|
||||
autoload :Helpers
|
||||
autoload :LookupContext
|
||||
autoload :Layouts
|
||||
autoload :PathSet
|
||||
autoload :RecordIdentifier
|
||||
autoload :Rendering
|
||||
autoload :RoutingUrlFor
|
||||
autoload :Template
|
||||
autoload :ViewPaths
|
||||
|
||||
autoload_under "renderer" do
|
||||
autoload :Renderer
|
||||
|
@ -82,6 +85,7 @@ module ActionView
|
|||
def self.eager_load!
|
||||
super
|
||||
ActionView::Template.eager_load!
|
||||
HTML.eager_load!
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,6 +2,10 @@ require 'active_support/core_ext/module/attr_internal'
|
|||
require 'active_support/core_ext/class/attribute_accessors'
|
||||
require 'active_support/ordered_options'
|
||||
require 'action_view/log_subscriber'
|
||||
require 'action_view/helpers'
|
||||
require 'action_view/context'
|
||||
require 'action_view/template'
|
||||
require 'action_view/lookup_context'
|
||||
|
||||
module ActionView #:nodoc:
|
||||
# = Action View Base
|
||||
|
|
|
@ -32,9 +32,13 @@ module ActionView
|
|||
Digestor
|
||||
end
|
||||
|
||||
@@cache[cache_key] = digest = klass.new(name, format, finder, options).digest # Store the actual digest
|
||||
ensure
|
||||
@@cache.delete_pair(cache_key, false) if pre_stored && !digest # something went wrong, make sure not to corrupt the @@cache
|
||||
# Store the actual digest if config.cache_template_loading is true
|
||||
klass.new(name, format, finder, options).digest.tap do |digest|
|
||||
@@cache[cache_key] = digest if ActionView::Resolver.caching?
|
||||
end
|
||||
rescue Exception
|
||||
@@cache.delete_pair(cache_key, false) if pre_stored # something went wrong, make sure not to corrupt the @@cache
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require 'action_view/record_identifier'
|
||||
|
||||
module ActionView
|
||||
# = Action View Record Tag Helpers
|
||||
module Helpers
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
require "action_view/rendering"
|
||||
require "active_support/core_ext/module/remove_method"
|
||||
|
||||
module AbstractController
|
||||
module ActionView
|
||||
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
|
||||
# repeated setups. The inclusion pattern has pages that look like this:
|
||||
#
|
||||
|
@ -200,7 +201,7 @@ module AbstractController
|
|||
module Layouts
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include Rendering
|
||||
include ActionView::Rendering
|
||||
|
||||
included do
|
||||
class_attribute :_layout, :_layout_conditions, :instance_accessor => false
|
|
@ -1,3 +1,5 @@
|
|||
require 'active_support/log_subscriber'
|
||||
|
||||
module ActionView
|
||||
# = Action View Log Subscriber
|
||||
#
|
||||
|
|
|
@ -35,5 +35,18 @@ module ActionView
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
initializer "action_view.setup_action_pack", before: :add_view_paths do |app|
|
||||
ActiveSupport.on_load(:action_controller) do
|
||||
ActionController::Base.superclass.send(:include, ActionView::Layouts)
|
||||
ActionView::RoutingUrlFor.send(:include, ActionDispatch::Routing::UrlFor)
|
||||
end
|
||||
end
|
||||
|
||||
initializer "action_view.setup_action_mailer", before: :add_view_paths do |app|
|
||||
ActiveSupport.on_load(:action_mailer) do
|
||||
ActionMailer::Base.send(:include, ActionView::Layouts)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
require "action_view/view_paths"
|
||||
|
||||
module ActionView
|
||||
# This is a class to fix I18n global state. Whenever you provide I18n.locale during a request,
|
||||
# it will trigger the lookup_context and consequently expire the cache.
|
||||
class I18nProxy < ::I18n::Config #:nodoc:
|
||||
attr_reader :original_config, :lookup_context
|
||||
|
||||
def initialize(original_config, lookup_context)
|
||||
original_config = original_config.original_config if original_config.respond_to?(:original_config)
|
||||
@original_config, @lookup_context = original_config, lookup_context
|
||||
end
|
||||
|
||||
def locale
|
||||
@original_config.locale
|
||||
end
|
||||
|
||||
def locale=(value)
|
||||
@lookup_context.locale = value
|
||||
end
|
||||
end
|
||||
|
||||
module Rendering
|
||||
extend ActiveSupport::Concern
|
||||
include ActionView::ViewPaths
|
||||
|
||||
# Overwrite process to setup I18n proxy.
|
||||
def process(*) #:nodoc:
|
||||
old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
|
||||
super
|
||||
ensure
|
||||
I18n.config = old_config
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def view_context_class
|
||||
@view_context_class ||= begin
|
||||
routes = respond_to?(:_routes) && _routes
|
||||
helpers = respond_to?(:_helpers) && _helpers
|
||||
|
||||
Class.new(ActionView::Base) do
|
||||
if routes
|
||||
include routes.url_helpers
|
||||
include routes.mounted_helpers
|
||||
end
|
||||
|
||||
if helpers
|
||||
include helpers
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
attr_internal_writer :view_context_class
|
||||
|
||||
def view_context_class
|
||||
@_view_context_class ||= self.class.view_context_class
|
||||
end
|
||||
|
||||
# An instance of a view class. The default view class is ActionView::Base
|
||||
#
|
||||
# The view class must have the following methods:
|
||||
# View.new[lookup_context, assigns, controller]
|
||||
# Create a new ActionView instance for a controller
|
||||
# View#render[options]
|
||||
# Returns String with the rendered template
|
||||
#
|
||||
# Override this method in a module to change the default behavior.
|
||||
def view_context
|
||||
view_context_class.new(view_renderer, view_assigns, self)
|
||||
end
|
||||
|
||||
# Returns an object that is able to render templates.
|
||||
# :api: private
|
||||
def view_renderer
|
||||
@_view_renderer ||= ActionView::Renderer.new(lookup_context)
|
||||
end
|
||||
|
||||
# Render template to response_body
|
||||
# :api: public
|
||||
def render(*args, &block)
|
||||
options = _normalize_render(*args, &block)
|
||||
self.response_body = render_to_body(options)
|
||||
end
|
||||
|
||||
# Raw rendering of a template to a string.
|
||||
# :api: public
|
||||
def render_to_string(*args, &block)
|
||||
options = _normalize_render(*args, &block)
|
||||
render_to_body(options)
|
||||
end
|
||||
|
||||
# Raw rendering of a template.
|
||||
# :api: public
|
||||
def render_to_body(options = {})
|
||||
_process_options(options)
|
||||
_render_template(options)
|
||||
end
|
||||
|
||||
# Find and renders a template based on the options given.
|
||||
# :api: private
|
||||
def _render_template(options) #:nodoc:
|
||||
lookup_context.rendered_format = nil if options[:formats]
|
||||
view_renderer.render(view_context, options)
|
||||
end
|
||||
|
||||
def rendered_format
|
||||
Mime[lookup_context.rendered_format]
|
||||
end
|
||||
|
||||
def default_protected_instance_vars
|
||||
super.concat([:@_view_context_class, :@_view_renderer, :@_lookup_context])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Normalize args and options.
|
||||
# :api: private
|
||||
def _normalize_render(*args, &block)
|
||||
options = _normalize_args(*args, &block)
|
||||
_normalize_options(options)
|
||||
options
|
||||
end
|
||||
|
||||
# Normalize args by converting render "foo" to render :action => "foo" and
|
||||
# render "foo/bar" to render :file => "foo/bar".
|
||||
# :api: private
|
||||
def _normalize_args(action=nil, options={})
|
||||
options = super(action, options)
|
||||
case action
|
||||
when NilClass
|
||||
when Hash
|
||||
options = action
|
||||
when String, Symbol
|
||||
action = action.to_s
|
||||
key = action.include?(?/) ? :file : :action
|
||||
options[key] = action
|
||||
else
|
||||
options[:partial] = action
|
||||
end
|
||||
|
||||
options
|
||||
end
|
||||
|
||||
# Normalize options.
|
||||
# :api: private
|
||||
def _normalize_options(options)
|
||||
options = super(options)
|
||||
if options[:partial] == true
|
||||
options[:partial] = action_name
|
||||
end
|
||||
|
||||
if (options.keys & [:partial, :file, :template]).empty?
|
||||
options[:prefixes] ||= _prefixes
|
||||
end
|
||||
|
||||
options[:template] ||= (options[:action] || action_name).to_s
|
||||
options
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,6 +1,6 @@
|
|||
require 'action_view/base'
|
||||
|
||||
module AbstractController
|
||||
module ActionView
|
||||
module ViewPaths
|
||||
extend ActiveSupport::Concern
|
||||
|
|
@ -24,7 +24,6 @@ require 'action_dispatch'
|
|||
require 'active_support/dependencies'
|
||||
require 'active_model'
|
||||
require 'active_record'
|
||||
require 'action_controller/caching'
|
||||
|
||||
require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late
|
||||
|
||||
|
@ -268,6 +267,10 @@ class Rack::TestCase < ActionDispatch::IntegrationTest
|
|||
end
|
||||
end
|
||||
|
||||
# Emulate AV railtie.
|
||||
ActionController::Base.superclass.send(:include, ActionView::Layouts)
|
||||
ActionView::RoutingUrlFor.send(:include, ActionDispatch::Routing::UrlFor)
|
||||
|
||||
module ActionController
|
||||
class Base
|
||||
include ActionController::Testing
|
||||
|
@ -290,9 +293,6 @@ module ActionController
|
|||
end
|
||||
end
|
||||
|
||||
class ::ApplicationController < ActionController::Base
|
||||
end
|
||||
|
||||
module ActionView
|
||||
class TestCase
|
||||
# Must repeat the setup because AV::TestCase is a duplication
|
||||
|
@ -330,53 +330,3 @@ module ActionDispatch
|
|||
end
|
||||
end
|
||||
|
||||
module ActionDispatch
|
||||
module RoutingVerbs
|
||||
def get(uri_or_host, path = nil)
|
||||
host = uri_or_host.host unless path
|
||||
path ||= uri_or_host.path
|
||||
|
||||
params = {'PATH_INFO' => path,
|
||||
'REQUEST_METHOD' => 'GET',
|
||||
'HTTP_HOST' => host}
|
||||
|
||||
routes.call(params)[2].join
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module RoutingTestHelpers
|
||||
def url_for(set, options, recall = nil)
|
||||
set.send(:url_for, options.merge(:only_path => true, :_recall => recall))
|
||||
end
|
||||
end
|
||||
|
||||
class ResourcesController < ActionController::Base
|
||||
def index() render :nothing => true end
|
||||
alias_method :show, :index
|
||||
end
|
||||
|
||||
class ThreadsController < ResourcesController; end
|
||||
class MessagesController < ResourcesController; end
|
||||
class CommentsController < ResourcesController; end
|
||||
class ReviewsController < ResourcesController; end
|
||||
class AuthorsController < ResourcesController; end
|
||||
class LogosController < ResourcesController; end
|
||||
|
||||
class AccountsController < ResourcesController; end
|
||||
class AdminController < ResourcesController; end
|
||||
class ProductsController < ResourcesController; end
|
||||
class ImagesController < ResourcesController; end
|
||||
class PreferencesController < ResourcesController; end
|
||||
|
||||
module Backoffice
|
||||
class ProductsController < ResourcesController; end
|
||||
class TagsController < ResourcesController; end
|
||||
class ManufacturersController < ResourcesController; end
|
||||
class ImagesController < ResourcesController; end
|
||||
|
||||
module Admin
|
||||
class ProductsController < ResourcesController; end
|
||||
class ImagesController < ResourcesController; end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -30,6 +30,7 @@ module AbstractController
|
|||
# ====
|
||||
class RenderingController < AbstractController::Base
|
||||
include AbstractController::Rendering
|
||||
include ActionView::Rendering
|
||||
|
||||
def _prefixes
|
||||
[]
|
||||
|
@ -153,7 +154,7 @@ module AbstractController
|
|||
# ====
|
||||
# self._layout is used when defined
|
||||
class WithLayouts < PrefixedViews
|
||||
include AbstractController::Layouts
|
||||
include ActionView::Layouts
|
||||
|
||||
private
|
||||
def self.layout(formats)
|
|
@ -1,13 +1,14 @@
|
|||
require 'abstract_unit'
|
||||
|
||||
ActionController::Base.helpers_path = File.expand_path('../../fixtures/helpers', __FILE__)
|
||||
ActionController::Base.helpers_path = File.expand_path('../../../fixtures/helpers', __FILE__)
|
||||
|
||||
module AbstractController
|
||||
module Testing
|
||||
|
||||
class ControllerWithHelpers < AbstractController::Base
|
||||
include AbstractController::Rendering
|
||||
include AbstractController::Helpers
|
||||
include AbstractController::Rendering
|
||||
include ActionView::Rendering
|
||||
|
||||
def with_module
|
||||
render :inline => "Module <%= included_method %>"
|
||||
|
@ -51,7 +52,7 @@ module AbstractController
|
|||
class AbstractInvalidHelpers < AbstractHelpers
|
||||
include ActionController::Helpers
|
||||
|
||||
path = File.join(File.expand_path('../../fixtures', __FILE__), "helpers_missing")
|
||||
path = File.join(File.expand_path('../../../fixtures', __FILE__), "helpers_missing")
|
||||
$:.unshift(path)
|
||||
self.helpers_path = path
|
||||
end
|
|
@ -6,7 +6,8 @@ module AbstractControllerTests
|
|||
# Base controller for these tests
|
||||
class Base < AbstractController::Base
|
||||
include AbstractController::Rendering
|
||||
include AbstractController::Layouts
|
||||
include ActionView::Rendering
|
||||
include ActionView::Layouts
|
||||
|
||||
abstract!
|
||||
|
|
@ -5,6 +5,7 @@ module AbstractController
|
|||
|
||||
class ControllerRenderer < AbstractController::Base
|
||||
include AbstractController::Rendering
|
||||
include ActionView::Rendering
|
||||
|
||||
def _prefixes
|
||||
%w[renderer]
|
|
@ -2,6 +2,8 @@ require 'abstract_unit'
|
|||
require 'active_support/logger'
|
||||
|
||||
class CaptureController < ActionController::Base
|
||||
self.view_paths = [ File.dirname(__FILE__) + '/../../fixtures/actionpack' ]
|
||||
|
||||
def self.controller_name; "test"; end
|
||||
def self.controller_path; "test"; end
|
||||
|
|
@ -9,7 +9,7 @@ old_load_paths = ActionController::Base.view_paths
|
|||
ActionView::Template::register_template_handler :mab,
|
||||
lambda { |template| template.source.inspect }
|
||||
|
||||
ActionController::Base.view_paths = [ File.dirname(__FILE__) + '/../fixtures/layout_tests/' ]
|
||||
ActionController::Base.view_paths = [ File.dirname(__FILE__) + '/../../fixtures/actionpack/layout_tests/' ]
|
||||
|
||||
class LayoutTest < ActionController::Base
|
||||
def self.controller_path; 'views' end
|
||||
|
@ -87,7 +87,7 @@ class StreamingLayoutController < LayoutTest
|
|||
end
|
||||
|
||||
class AbsolutePathLayoutController < LayoutTest
|
||||
layout File.expand_path(File.expand_path(__FILE__) + '/../../fixtures/layout_tests/layouts/layout_test')
|
||||
layout File.expand_path(File.expand_path(__FILE__) + '/../../../fixtures/actionpack/layout_tests/layouts/layout_test')
|
||||
end
|
||||
|
||||
class HasOwnLayoutController < LayoutTest
|
||||
|
@ -108,7 +108,7 @@ end
|
|||
|
||||
class PrependsViewPathController < LayoutTest
|
||||
def hello
|
||||
prepend_view_path File.dirname(__FILE__) + '/../fixtures/layout_tests/alt/'
|
||||
prepend_view_path File.dirname(__FILE__) + '/../../fixtures/actionpack/layout_tests/alt/'
|
||||
render :layout => 'alt'
|
||||
end
|
||||
end
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1 @@
|
|||
<%= greeting %> bad customer: <%= bad_customer.name %><%= bad_customer_counter %>
|
5
actionview/test/fixtures/actionpack/layout_tests/layouts/symlinked/symlinked_layout.erb
vendored
Normal file
5
actionview/test/fixtures/actionpack/layout_tests/layouts/symlinked/symlinked_layout.erb
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
This is my layout
|
||||
|
||||
<%= yield %>
|
||||
|
||||
End.
|
|
@ -0,0 +1,2 @@
|
|||
<div id="column"><%= yield :column %></div>
|
||||
<div id="content"><%= yield %></div>
|
|
@ -0,0 +1 @@
|
|||
<title><%= yield Struct.new(:name).new("David") %></title>
|
|
@ -0,0 +1,2 @@
|
|||
<%= render :partial => 'test/partial' %>
|
||||
<%= yield %>
|
|
@ -0,0 +1 @@
|
|||
<%= yield %>
|
|
@ -0,0 +1 @@
|
|||
<%= yield 'Yield!' %>
|
|
@ -0,0 +1,3 @@
|
|||
<%= render(:layout => "layout_for_partial", :locals => { :name => "Anthony" }) do %>Inside from first block in layout<% "Return value should be discarded" %><% end %>
|
||||
<%= yield %>
|
||||
<%= render(:layout => "layout_for_partial", :locals => { :name => "Ramm" }) do %>Inside from second block in layout<% end %>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue