aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib')
-rw-r--r--actionpack/lib/abstract_controller/helpers.rb6
-rw-r--r--actionpack/lib/action_controller.rb6
-rw-r--r--actionpack/lib/action_controller/base.rb9
-rw-r--r--actionpack/lib/action_controller/caching.rb25
-rw-r--r--actionpack/lib/action_controller/metal/conditional_get.rb43
-rw-r--r--actionpack/lib/action_controller/metal/exceptions.rb3
-rw-r--r--actionpack/lib/action_controller/metal/instrumentation.rb1
-rw-r--r--actionpack/lib/action_controller/record_identifier.rb83
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner.rb23
-rw-r--r--actionpack/lib/action_dispatch.rb5
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb28
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/dom.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/selector.rb3
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/tag.rb2
-rw-r--r--actionpack/lib/action_view.rb4
-rw-r--r--actionpack/lib/action_view/asset_paths.rb3
-rw-r--r--actionpack/lib/action_view/base.rb5
-rw-r--r--actionpack/lib/action_view/digestor.rb104
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb8
-rw-r--r--actionpack/lib/action_view/helpers/cache_helper.rb102
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb14
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb6
-rw-r--r--actionpack/lib/action_view/helpers/record_tag_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/sanitize_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb114
-rw-r--r--actionpack/lib/action_view/lookup_context.rb2
-rw-r--r--actionpack/lib/action_view/model_naming.rb12
-rw-r--r--actionpack/lib/action_view/record_identifier.rb84
-rw-r--r--actionpack/lib/action_view/renderer/streaming_template_renderer.rb2
-rw-r--r--actionpack/lib/action_view/routing_url_for.rb107
-rw-r--r--actionpack/lib/action_view/template.rb9
-rw-r--r--actionpack/lib/action_view/template/error.rb3
-rw-r--r--actionpack/lib/action_view/template/handlers/builder.rb2
-rw-r--r--actionpack/lib/action_view/template/resolver.rb2
-rw-r--r--actionpack/lib/action_view/template/text.rb12
-rw-r--r--actionpack/lib/action_view/template/types.rb58
-rw-r--r--actionpack/lib/action_view/test_case.rb3
-rw-r--r--actionpack/lib/action_view/vendor/html-scanner.rb20
-rw-r--r--actionpack/lib/action_view/vendor/html-scanner/html/document.rb (renamed from actionpack/lib/action_controller/vendor/html-scanner/html/document.rb)0
-rw-r--r--actionpack/lib/action_view/vendor/html-scanner/html/node.rb (renamed from actionpack/lib/action_controller/vendor/html-scanner/html/node.rb)0
-rw-r--r--actionpack/lib/action_view/vendor/html-scanner/html/sanitizer.rb (renamed from actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb)0
-rw-r--r--actionpack/lib/action_view/vendor/html-scanner/html/selector.rb (renamed from actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb)0
-rw-r--r--actionpack/lib/action_view/vendor/html-scanner/html/tokenizer.rb (renamed from actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb)0
-rw-r--r--actionpack/lib/action_view/vendor/html-scanner/html/version.rb (renamed from actionpack/lib/action_controller/vendor/html-scanner/html/version.rb)0
46 files changed, 649 insertions, 276 deletions
diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb
index d63d17f4c5..d3929b685c 100644
--- a/actionpack/lib/abstract_controller/helpers.rb
+++ b/actionpack/lib/abstract_controller/helpers.rb
@@ -32,9 +32,9 @@ module AbstractController
# @current_user ||= User.find_by_id(session[:user])
# end
#
- # def logged_in?
- # current_user != nil
- # end
+ # def logged_in?
+ # current_user != nil
+ # end
# end
#
# In a view:
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index 31df9d605c..8c1f548e47 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -58,7 +58,11 @@ end
# All of these simply register additional autoloads
require 'action_view'
-require 'action_controller/vendor/html-scanner'
+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'
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 71425cd542..f829f5e8a2 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -88,15 +88,6 @@ module ActionController
#
# Do not put secret information in cookie-based sessions!
#
- # Other options for session storage:
- #
- # * ActiveRecord::SessionStore - Sessions are stored in your database, which works better than PStore with multiple app servers and,
- # unlike CookieStore, hides your session contents from the user. To use ActiveRecord::SessionStore, set
- #
- # MyApplication::Application.config.session_store :active_record_store
- #
- # in your <tt>config/initializers/session_store.rb</tt> and run <tt>script/rails g session_migration</tt>.
- #
# == Responses
#
# Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index 9118806059..be29099fbe 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -48,11 +48,10 @@ module ActionController #:nodoc:
config.cache_store = ActiveSupport::Cache.lookup_store(store)
end
- private
-
- def cache_configured?
- perform_caching && cache_store
- end
+ private
+ def cache_configured?
+ perform_caching && cache_store
+ end
end
include RackDelegation
@@ -73,14 +72,14 @@ module ActionController #:nodoc:
request.get? && response.status == 200
end
- protected
- # Convenience accessor
- def cache(key, options = {}, &block)
- if cache_configured?
- cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
- else
- yield
+ protected
+ # Convenience accessor
+ def cache(key, options = {}, &block)
+ if cache_configured?
+ cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
+ else
+ yield
+ end
end
- end
end
end
diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb
index 2193dde667..7521deaaca 100644
--- a/actionpack/lib/action_controller/metal/conditional_get.rb
+++ b/actionpack/lib/action_controller/metal/conditional_get.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/class/attribute'
+
module ActionController
module ConditionalGet
extend ActiveSupport::Concern
@@ -5,6 +7,33 @@ module ActionController
include RackDelegation
include Head
+ included do
+ class_attribute :etaggers
+ self.etaggers = []
+ end
+
+ module ClassMethods
+ # Allows you to consider additional controller-wide information when generating an etag.
+ # For example, if you serve pages tailored depending on who's logged in at the moment, you
+ # may want to add the current user id to be part of the etag to prevent authorized displaying
+ # of cached pages.
+ #
+ # === Example
+ #
+ # class InvoicesController < ApplicationController
+ # etag { current_user.try :id }
+ #
+ # def show
+ # # Etag will differ even for the same invoice when it's viewed by a different current_user
+ # @invoice = Invoice.find(params[:id])
+ # fresh_when(@invoice)
+ # end
+ # end
+ def etag(&etagger)
+ self.etaggers += [etagger]
+ end
+ end
+
# Sets the etag, last_modified, or both on the response and renders a
# <tt>304 Not Modified</tt> response if the request is already fresh.
#
@@ -42,12 +71,12 @@ module ActionController
options.assert_valid_keys(:etag, :last_modified, :public)
else
record = record_or_options
- options = { :etag => record, :last_modified => record.try(:updated_at) }.merge(additional_options)
+ options = { etag: record, last_modified: record.try(:updated_at) }.merge(additional_options)
end
- response.etag = options[:etag] if options[:etag]
- response.last_modified = options[:last_modified] if options[:last_modified]
- response.cache_control[:public] = true if options[:public]
+ response.etag = combine_etags(options[:etag]) if options[:etag]
+ response.last_modified = options[:last_modified] if options[:last_modified]
+ response.cache_control[:public] = true if options[:public]
head :not_modified if request.fresh?(response)
end
@@ -133,5 +162,11 @@ module ActionController
def expires_now #:doc:
response.cache_control.replace(:no_cache => true)
end
+
+
+ private
+ def combine_etags(etag)
+ [ etag, *etaggers.map { |etagger| instance_exec(&etagger) }.compact ]
+ end
end
end
diff --git a/actionpack/lib/action_controller/metal/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb
index 8fd8f4797c..3c9d0c86a7 100644
--- a/actionpack/lib/action_controller/metal/exceptions.rb
+++ b/actionpack/lib/action_controller/metal/exceptions.rb
@@ -16,6 +16,9 @@ module ActionController
end
end
+ class ActionController::UrlGenerationError < RoutingError #:nodoc:
+ end
+
class MethodNotAllowed < ActionControllerError #:nodoc:
def initialize(*allowed_methods)
super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.")
diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb
index 640ebf5f00..ca4ae532ca 100644
--- a/actionpack/lib/action_controller/metal/instrumentation.rb
+++ b/actionpack/lib/action_controller/metal/instrumentation.rb
@@ -11,6 +11,7 @@ module ActionController
extend ActiveSupport::Concern
include AbstractController::Logger
+ include ActionController::RackDelegation
attr_internal :view_runtime
diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb
index d3ac406618..bffd2a02d0 100644
--- a/actionpack/lib/action_controller/record_identifier.rb
+++ b/actionpack/lib/action_controller/record_identifier.rb
@@ -1,83 +1,20 @@
-require 'active_support/core_ext/module'
-require 'action_controller/model_naming'
+require 'active_support/deprecation'
+require 'action_view/record_identifier'
module ActionController
- # The record identifier encapsulates a number of naming conventions for dealing with records, like Active Records or
- # pretty much any other model type that has an id. These patterns are then used to try elevate the view actions to
- # a higher logical level.
- #
- # # routes
- # resources :posts
- #
- # # view
- # <%= div_for(post) do %> <div id="post_45" class="post">
- # <%= post.body %> What a wonderful world!
- # <% end %> </div>
- #
- # # controller
- # def update
- # post = Post.find(params[:id])
- # post.update_attributes(params[:post])
- #
- # redirect_to(post) # Calls polymorphic_url(post) which in turn calls post_url(post)
- # end
- #
- # As the example above shows, you can stop caring to a large extent what the actual id of the post is.
- # You just know that one is being assigned and that the subsequent calls in redirect_to expect that
- # same naming convention and allows you to write less code if you follow it.
module RecordIdentifier
- extend self
+ MESSAGE = 'method will no longer be included by default in controllers since Rails 4.1. ' +
+ 'If you would like to use it in controllers, please include ' +
+ 'ActionView::RecodIdentifier module.'
- include ModelNaming
-
- JOIN = '_'.freeze
- NEW = 'new'.freeze
-
- # The DOM class convention is to use the singular form of an object or class.
- #
- # dom_class(post) # => "post"
- # dom_class(Person) # => "person"
- #
- # If you need to address multiple instances of the same class in the same view, you can prefix the dom_class:
- #
- # dom_class(post, :edit) # => "edit_post"
- # dom_class(Person, :edit) # => "edit_person"
- def dom_class(record_or_class, prefix = nil)
- singular = model_name_from_record_or_class(record_or_class).param_key
- prefix ? "#{prefix}#{JOIN}#{singular}" : singular
- end
-
- # The DOM id convention is to use the singular form of an object or class with the id following an underscore.
- # If no id is found, prefix with "new_" instead.
- #
- # dom_id(Post.find(45)) # => "post_45"
- # dom_id(Post.new) # => "new_post"
- #
- # If you need to address multiple instances of the same class in the same view, you can prefix the dom_id:
- #
- # dom_id(Post.find(45), :edit) # => "edit_post_45"
- # dom_id(Post.new, :custom) # => "custom_post"
def dom_id(record, prefix = nil)
- if record_id = record_key_for_dom_id(record)
- "#{dom_class(record, prefix)}#{JOIN}#{record_id}"
- else
- dom_class(record, prefix || NEW)
- end
+ ActiveSupport::Deprecation.warn 'dom_id ' + MESSAGE
+ ActionView::RecordIdentifier.dom_id(record, prefix)
end
- protected
-
- # Returns a string representation of the key attribute(s) that is suitable for use in an HTML DOM id.
- # This can be overwritten to customize the default generated string representation if desired.
- # If you need to read back a key from a dom_id in order to query for the underlying database record,
- # you should write a helper like 'person_record_from_dom_id' that will extract the key either based
- # on the default implementation (which just joins all key attributes with '_') or on your own
- # overwritten version of the method. By default, this implementation passes the key string through a
- # method that replaces all characters that are invalid inside DOM ids, with valid ones. You need to
- # make sure yourself that your dom ids are valid, in case you overwrite this method.
- def record_key_for_dom_id(record)
- key = convert_to_model(record).to_key
- key ? key.join('_') : key
+ def dom_class(record, prefix = nil)
+ ActiveSupport::Deprecation.warn 'dom_class ' + MESSAGE
+ ActionView::RecordIdentifier.dom_class(record, prefix)
end
end
end
diff --git a/actionpack/lib/action_controller/vendor/html-scanner.rb b/actionpack/lib/action_controller/vendor/html-scanner.rb
index 879b31e60e..896208bc05 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner.rb
@@ -1,20 +1,5 @@
-$LOAD_PATH << "#{File.dirname(__FILE__)}/html-scanner"
+require 'action_view/vendor/html-scanner'
+require 'active_support/deprecation'
-module HTML
- extend ActiveSupport::Autoload
-
- eager_autoload do
- autoload :CDATA, 'html/node'
- autoload :Document, 'html/document'
- autoload :FullSanitizer, 'html/sanitizer'
- autoload :LinkSanitizer, 'html/sanitizer'
- autoload :Node, 'html/node'
- autoload :Sanitizer, 'html/sanitizer'
- autoload :Selector, 'html/selector'
- autoload :Tag, 'html/node'
- autoload :Text, 'html/node'
- autoload :Tokenizer, 'html/tokenizer'
- autoload :Version, 'html/version'
- autoload :WhiteListSanitizer, 'html/sanitizer'
- end
-end
+ActiveSupport::Deprecation.warn 'Vendored html-scanner was moved to action_view, please require "action_view/vendor/html-scanner" instead. ' +
+ 'This file will be removed in Rails 4.1'
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index 57b4678add..0ec355246e 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -101,3 +101,8 @@ module ActionDispatch
end
autoload :Mime, 'action_dispatch/http/mime_type'
+
+ActiveSupport.on_load(:action_view) do
+ ActionView::Base.default_formats ||= Mime::SET.symbols
+ ActionView::Template::Types.delegate_to Mime
+end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index f64cff8394..b52f66faf1 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -757,7 +757,7 @@ module ActionDispatch
#
# Routes can also be constrained to an IP or a certain range of IP addresses:
#
- # constraints(:ip => /192.168.\d+.\d+/) do
+ # constraints(:ip => /192\.168\.\d+\.\d+/) do
# resources :posts
# end
#
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 32d267d1d6..1fe40d8458 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -438,12 +438,11 @@ module ActionDispatch
attr_reader :options, :recall, :set, :named_route
- def initialize(options, recall, set, extras = false)
+ def initialize(options, recall, set)
@named_route = options.delete(:use_route)
@options = options.dup
@recall = recall.dup
@set = set
- @extras = extras
normalize_options!
normalize_controller_action_id!
@@ -526,20 +525,12 @@ module ActionDispatch
recall[:action] = options.delete(:action) if options[:action] == 'index'
end
+ # Generates a path from routes, returns [path, params]
+ # if no path is returned the formatter will raise Journey::Router::RoutingError
def generate
- path, params = @set.formatter.generate(:path_info, named_route, options, recall, PARAMETERIZE)
-
- raise_routing_error unless path
-
- return [path, params.keys] if @extras
-
- [path, params]
- rescue Journey::Router::RoutingError
- raise_routing_error
- end
-
- def raise_routing_error
- raise ActionController::RoutingError, "No route matches #{options.inspect}"
+ @set.formatter.generate(:path_info, named_route, options, recall, PARAMETERIZE)
+ rescue Journey::Router::RoutingError => e
+ raise ActionController::UrlGenerationError, "No route matches #{options.inspect} #{e.message}"
end
def different_controller?
@@ -564,11 +555,12 @@ module ActionDispatch
end
def generate_extras(options, recall={})
- generate(options, recall, true)
+ path, params = generate(options, recall)
+ return path, params.keys
end
- def generate(options, recall = {}, extras = false)
- Generator.new(options, recall, self, extras).generate
+ def generate(options, recall = {})
+ Generator.new(options, recall, self).generate
end
RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
index f4c708ea33..d4cd537048 100644
--- a/actionpack/lib/action_dispatch/routing/url_for.rb
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -95,6 +95,8 @@ module ActionDispatch
self.default_url_options = {}
end
+
+ include(*_url_for_modules) if respond_to?(:_url_for_modules)
end
def initialize(*)
diff --git a/actionpack/lib/action_dispatch/testing/assertions/dom.rb b/actionpack/lib/action_dispatch/testing/assertions/dom.rb
index 7dc3d0f97c..6c61d4e61a 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/dom.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/dom.rb
@@ -1,4 +1,4 @@
-require 'action_controller/vendor/html-scanner'
+require 'action_view/vendor/html-scanner'
module ActionDispatch
module Assertions
diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
index d19d116a1f..9388d44eef 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
@@ -1,4 +1,5 @@
-require 'action_controller/vendor/html-scanner'
+require 'action_view/vendor/html-scanner'
+require 'active_support/core_ext/object/inclusion'
#--
# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
diff --git a/actionpack/lib/action_dispatch/testing/assertions/tag.rb b/actionpack/lib/action_dispatch/testing/assertions/tag.rb
index 68f1347e7c..2e38266aba 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/tag.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/tag.rb
@@ -1,4 +1,4 @@
-require 'action_controller/vendor/html-scanner'
+require 'action_view/vendor/html-scanner'
module ActionDispatch
module Assertions
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index 9d11c284f5..091b0d8cd2 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -33,9 +33,12 @@ module ActionView
autoload :Base
autoload :Context
autoload :CompiledTemplates, "action_view/context"
+ autoload :Digestor
autoload :Helpers
autoload :LookupContext
autoload :PathSet
+ autoload :RecordIdentifier
+ autoload :RoutingUrlFor
autoload :Template
autoload_under "renderer" do
@@ -68,6 +71,7 @@ module ActionView
autoload :MissingTemplate
autoload :ActionViewError
autoload :EncodingError
+ autoload :MissingRequestError
autoload :TemplateError
autoload :WrongEncodingError
end
diff --git a/actionpack/lib/action_view/asset_paths.rb b/actionpack/lib/action_view/asset_paths.rb
index 81880d17ea..4bbb31b3ee 100644
--- a/actionpack/lib/action_view/asset_paths.rb
+++ b/actionpack/lib/action_view/asset_paths.rb
@@ -1,6 +1,5 @@
require 'zlib'
require 'active_support/core_ext/file'
-require 'action_controller/metal/exceptions'
module ActionView
class AssetPaths #:nodoc:
@@ -98,7 +97,7 @@ module ActionView
end
def invalid_asset_host!(help_message)
- raise ActionController::RoutingError, "This asset host cannot be computed without a request in scope. #{help_message}"
+ raise ActionView::MissingRequestError, "This asset host cannot be computed without a request in scope. #{help_message}"
end
# Pick an asset host for this source. Returns +nil+ if no host is set,
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 749332eca7..3464ec523e 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -146,6 +146,9 @@ module ActionView #:nodoc:
cattr_accessor :prefix_partial_path_with_controller_namespace
@@prefix_partial_path_with_controller_namespace = true
+ # Specify default_formats that can be rendered.
+ cattr_accessor :default_formats
+
class_attribute :_routes
class_attribute :logger
@@ -178,8 +181,6 @@ module ActionView #:nodoc:
def initialize(context = nil, assigns = {}, controller = nil, formats = nil) #:nodoc:
@_config = ActiveSupport::InheritableOptions.new
- # Handle all these for backwards compatibility.
- # TODO Provide a new API for AV::Base and deprecate this one.
if context.is_a?(ActionView::Renderer)
@view_renderer = context
else
diff --git a/actionpack/lib/action_view/digestor.rb b/actionpack/lib/action_view/digestor.rb
new file mode 100644
index 0000000000..899100d06c
--- /dev/null
+++ b/actionpack/lib/action_view/digestor.rb
@@ -0,0 +1,104 @@
+require 'active_support/core_ext'
+require 'logger'
+
+module ActionView
+ class Digestor
+ EXPLICIT_DEPENDENCY = /# Template Dependency: ([^ ]+)/
+
+ # Matches:
+ # render partial: "comments/comment", collection: commentable.comments
+ # render "comments/comments"
+ # render 'comments/comments'
+ # render('comments/comments')
+ #
+ # render(@topic) => render("topics/topic")
+ # render(topics) => render("topics/topic")
+ # render(message.topics) => render("topics/topic")
+ RENDER_DEPENDENCY = /
+ render\s* # render, followed by optional whitespace
+ \(? # start an optional parenthesis for the render call
+ (partial:|:partial\s+=>)?\s* # naming the partial, used with collection -- 1st capture
+ ([@a-z"'][@a-z_\/\."']+) # the template name itself -- 2nd capture
+ /x
+
+ cattr_accessor(:cache) { Hash.new }
+ cattr_accessor(:logger, instance_reader: true) { ActionView::Base.logger }
+
+ def self.digest(name, format, finder, options = {})
+ cache["#{name}.#{format}"] ||= new(name, format, finder, options).digest
+ end
+
+ attr_reader :name, :format, :finder, :options
+
+ def initialize(name, format, finder, options = {})
+ @name, @format, @finder, @options = name, format, finder, options
+ end
+
+ def digest
+ Digest::MD5.hexdigest("#{name}.#{format}-#{source}-#{dependency_digest}").tap do |digest|
+ logger.try :info, "Cache digest for #{name}.#{format}: #{digest}"
+ end
+ rescue ActionView::MissingTemplate
+ logger.try :error, "Couldn't find template for digesting: #{name}.#{format}"
+ ''
+ end
+
+ def dependencies
+ render_dependencies + explicit_dependencies
+ rescue ActionView::MissingTemplate
+ [] # File doesn't exist, so no dependencies
+ end
+
+ def nested_dependencies
+ dependencies.collect do |dependency|
+ dependencies = Digestor.new(dependency, format, finder, partial: true).nested_dependencies
+ dependencies.any? ? { dependency => dependencies } : dependency
+ end
+ end
+
+
+ private
+ def logical_name
+ name.gsub(%r|/_|, "/")
+ end
+
+ def directory
+ name.split("/").first
+ end
+
+ def partial?
+ options[:partial] || name.include?("/_")
+ end
+
+ def source
+ @source ||= finder.find(logical_name, [], partial?, formats: [ format ]).source
+ end
+
+
+ def dependency_digest
+ dependencies.collect do |template_name|
+ Digestor.digest(template_name, format, finder, partial: true)
+ end.join("-")
+ end
+
+ def render_dependencies
+ source.scan(RENDER_DEPENDENCY).
+ collect(&:second).uniq.
+
+ # render(@topic) => render("topics/topic")
+ # render(topics) => render("topics/topic")
+ # render(message.topics) => render("topics/topic")
+ collect { |name| name.sub(/\A@?([a-z]+\.)*([a-z_]+)\z/) { "#{$2.pluralize}/#{$2.singularize}" } }.
+
+ # render("headline") => render("message/headline")
+ collect { |name| name.include?("/") ? name : "#{directory}/#{name}" }.
+
+ # replace quotes from string renders
+ collect { |name| name.gsub(/["']/, "") }
+ end
+
+ def explicit_dependencies
+ source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index ceb56824fa..db4da6f9c8 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -222,6 +222,14 @@ module ActionView
# auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {:title => "Example RSS"})
# # => <link rel="alternate" type="application/rss+xml" title="Example RSS" href="http://www.example.com/feed" />
def auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {})
+ if !(type == :rss || type == :atom) && tag_options[:type].blank?
+ message = "You have passed type other than :rss or :atom to auto_discovery_link_tag and haven't supplied " +
+ "the :type option key. This behavior is deprecated and will be remove in Rails 4.1. You should pass " +
+ ":type option explicitly if you want to use other types, for example: " +
+ "auto_discovery_link_tag(:xml, '/feed.xml', :type => 'application/xml')"
+ ActiveSupport::Deprecation.warn message
+ end
+
tag(
"link",
"rel" => tag_options[:rel] || "alternate",
diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb
index 39518268df..59e1015976 100644
--- a/actionpack/lib/action_view/helpers/cache_helper.rb
+++ b/actionpack/lib/action_view/helpers/cache_helper.rb
@@ -13,10 +13,9 @@ module ActionView
# kick out old entries. For more on key-based expiration, see:
# http://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works
#
- # When using this method, you list the cache dependencies as part of
- # the name of the cache, like so:
+ # When using this method, you list the cache dependency as the name of the cache, like so:
#
- # <% cache [ "v1", project ] do %>
+ # <% cache project do %>
# <b>All the topics on this project</b>
# <%= render project.topics %>
# <% end %>
@@ -24,15 +23,89 @@ module ActionView
# This approach will assume that when a new topic is added, you'll touch
# the project. The cache key generated from this call will be something like:
#
- # views/v1/projects/123-20120806214154
- # ^class ^id ^updated_at
+ # views/projects/123-20120806214154/7a1156131a6928cb0026877f8b749ac9
+ # ^class ^id ^updated_at ^template tree digest
#
- # If you update the rendering of topics, you just bump the version to v2.
- # Otherwise the cache is automatically bumped whenever the project updated_at
- # is touched.
+ # The cache is thus automatically bumped whenever the project updated_at is touched.
+ #
+ # If your template cache depends on multiple sources (try to avoid this to keep things simple),
+ # you can name all these dependencies as part of an array:
+ #
+ # <% cache [ project, current_user ] do %>
+ # <b>All the topics on this project</b>
+ # <%= render project.topics %>
+ # <% end %>
+ #
+ # This will include both records as part of the cache key and updating either of them will
+ # expire the cache.
+ #
+ # ==== Template digest
+ #
+ # The template digest that's added to the cache key is computed by taking an md5 of the
+ # contents of the entire template file. This ensures that your caches will automatically
+ # expire when you change the template file.
+ #
+ # Note that the md5 is taken of the entire template file, not just what's within the
+ # cache do/end call. So it's possible that changing something outside of that call will
+ # still expire the cache.
+ #
+ # Additionally, the digestor will automatically look through your template file for
+ # explicit and implicit dependencies, and include those as part of the digest.
+ #
+ # ==== Implicit dependencies
+ #
+ # Most template dependencies can be derived from calls to render in the template itself.
+ # Here are some examples of render calls that Cache Digests knows how to decode:
+ #
+ # render partial: "comments/comment", collection: commentable.comments
+ # render "comments/comments"
+ # render 'comments/comments'
+ # render('comments/comments')
+ #
+ # render "header" => render("comments/header")
+ #
+ # render(@topic) => render("topics/topic")
+ # render(topics) => render("topics/topic")
+ # render(message.topics) => render("topics/topic")
+ #
+ # It's not possible to derive all render calls like that, though. Here are a few examples of things that can't be derived:
+ #
+ # render group_of_attachments
+ # render @project.documents.where(published: true).order('created_at')
+ #
+ # You will have to rewrite those to the explicit form:
+ #
+ # render partial: 'attachments/attachment', collection: group_of_attachments
+ # render partial: 'documents/document', collection: @project.documents.where(published: true).order('created_at')
+ #
+ # === Explicit dependencies
+ #
+ # Some times you'll have template dependencies that can't be derived at all. This is typically
+ # the case when you have template rendering that happens in helpers. Here's an example:
+ #
+ # <%= render_sortable_todolists @project.todolists %>
+ #
+ # You'll need to use a special comment format to call those out:
+ #
+ # <%# Template Dependency: todolists/todolist %>
+ # <%= render_sortable_todolists @project.todolists %>
+ #
+ # The pattern used to match these is /# Template Dependency: ([^ ]+)/, so it's important that you type it out just so.
+ # You can only declare one template dependency per line.
+ #
+ # === External dependencies
+ #
+ # If you use a helper method, for example, inside of a cached block and you then update that helper,
+ # you'll have to bump the cache as well. It doesn't really matter how you do it, but the md5 of the template file
+ # must change. One recommendation is to simply be explicit in a comment, like:
+ #
+ # <%# Helper Dependency Updated: May 6, 2012 at 6pm %>
+ # <%= some_helper_method(person) %>
+ #
+ # Now all you'll have to do is change that timestamp when the helper method changes.
def cache(name = {}, options = nil, &block)
if controller.perform_caching
- safe_concat(fragment_for(name, options, &block))
+ safe_concat(fragment_for(fragment_name_with_digest(name), options, &block))
else
yield
end
@@ -58,6 +131,17 @@ module ActionView
controller.write_fragment(name, fragment, options)
end
end
+
+ def fragment_name_with_digest(name)
+ if @virtual_path
+ [
+ *Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name),
+ Digestor.digest(@virtual_path, formats.last.to_sym, lookup_context)
+ ]
+ else
+ name
+ end
+ end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index dea2aa69dd..795440cacc 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -140,12 +140,19 @@ module ActionView
# Like <tt>distance_of_time_in_words</tt>, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
#
# time_ago_in_words(3.minutes.from_now) # => 3 minutes
+ # time_ago_in_words(3.minutes.ago) # => 3 minutes
# time_ago_in_words(Time.now - 15.hours) # => about 15 hours
# time_ago_in_words(Time.now) # => less than a minute
# time_ago_in_words(Time.now, :include_seconds => true) # => less than 5 seconds
#
# from_time = Time.now - 3.days - 14.minutes - 25.seconds
# time_ago_in_words(from_time) # => 3 days
+ #
+ # from_time = (3.days + 14.minutes + 25.seconds).ago
+ # time_ago_in_words(from_time) # => 3 days
+ #
+ # Note that you cannot pass a <tt>Numeric</tt> value to <tt>time_ago_in_words</tt>.
+ #
def time_ago_in_words(from_time, include_seconds_or_options = {})
distance_of_time_in_words(from_time, Time.now, include_seconds_or_options)
end
@@ -956,12 +963,15 @@ module ActionView
# build_hidden(:year, 2008)
# => "<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="2008" />"
def build_hidden(type, value)
- (tag(:input, {
+ select_options = {
:type => "hidden",
:id => input_id_from_type(type),
:name => input_name_from_type(type),
:value => value
- }.merge(@html_options.slice(:disabled))) + "\n").html_safe
+ }.merge(@html_options.slice(:disabled))
+ select_options.merge!(:disabled => 'disabled') if @options[:disabled]
+
+ tag(:input, select_options) + "\n".html_safe
end
# Returns the name attribute for the input tag.
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index b79577bcd3..17fe7dc1b4 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -4,12 +4,12 @@ require 'action_view/helpers/tag_helper'
require 'action_view/helpers/form_tag_helper'
require 'action_view/helpers/active_model_helper'
require 'action_view/helpers/tags'
+require 'action_view/model_naming'
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/string/output_safety'
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/string/inflections'
-require 'action_controller/model_naming'
module ActionView
# = Action View Form Helpers
@@ -115,7 +115,7 @@ module ActionView
include FormTagHelper
include UrlHelper
- include ActionController::ModelNaming
+ include ModelNaming
# Creates a form that allows the user to create or update the attributes
# of a specific model object.
@@ -1156,7 +1156,7 @@ module ActionView
end
class FormBuilder
- include ActionController::ModelNaming
+ include ModelNaming
# The methods which wrap a form helper call.
class_attribute :field_helpers
diff --git a/actionpack/lib/action_view/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb
index 9b35f076e5..dded9aab7c 100644
--- a/actionpack/lib/action_view/helpers/record_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb
@@ -1,10 +1,8 @@
-require 'action_controller/record_identifier'
-
module ActionView
# = Action View Record Tag Helpers
module Helpers
module RecordTagHelper
- include ActionController::RecordIdentifier
+ include ActionView::RecordIdentifier
# Produces a wrapper DIV element with id and class parameters that
# relate to the specified Active Record object. Usage example:
diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb
index aaf0e0344a..9c76c26ace 100644
--- a/actionpack/lib/action_view/helpers/sanitize_helper.rb
+++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb
@@ -1,5 +1,5 @@
require 'active_support/core_ext/object/try'
-require 'action_controller/vendor/html-scanner'
+require 'action_view/vendor/html-scanner'
module ActionView
# = Action View Sanitize Helpers
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index fe3240fdc1..3f65791aa0 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -2,7 +2,6 @@ require 'action_view/helpers/javascript_helper'
require 'active_support/core_ext/array/access'
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/string/output_safety'
-require 'action_dispatch'
module ActionView
# = Action View URL Helpers
@@ -20,115 +19,34 @@ module ActionView
extend ActiveSupport::Concern
- include ActionDispatch::Routing::UrlFor
include TagHelper
- # We need to override url_options, _routes_context
- # and optimize_routes_generation? to consider the controller.
-
- def url_options #:nodoc:
- return super unless controller.respond_to?(:url_options)
- controller.url_options
- end
-
- def _routes_context #:nodoc:
- controller
- end
- protected :_routes_context
-
- def optimize_routes_generation? #:nodoc:
- controller.respond_to?(:optimize_routes_generation?) ?
- controller.optimize_routes_generation? : super
+ module ClassMethods
+ def _url_for_modules
+ ActionView::RoutingUrlFor
+ end
end
- protected :optimize_routes_generation?
- # Returns the URL for the set of +options+ provided. This takes the
- # same options as +url_for+ in Action Controller (see the
- # documentation for <tt>ActionController::Base#url_for</tt>). Note that by default
- # <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative "/controller/action"
- # instead of the fully qualified URL like "http://example.com/controller/action".
- #
- # ==== Options
- # * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path.
- # * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified).
- # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2005/". Note that this
- # is currently not recommended since it breaks caching.
- # * <tt>:host</tt> - Overrides the default (current) host if provided.
- # * <tt>:protocol</tt> - Overrides the default (current) protocol if provided.
- # * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
- # * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
- #
- # ==== Relying on named routes
- #
- # Passing a record (like an Active Record) instead of a hash as the options parameter will
- # trigger the named route for that record. The lookup will happen on the name of the class. So passing a
- # Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as
- # +admin_workshop_path+ you'll have to call that explicitly (it's impossible for +url_for+ to guess that route).
- #
- # ==== Implicit Controller Namespacing
- #
- # Controllers passed in using the +:controller+ option will retain their namespace unless it is an absolute one.
- #
- # ==== Examples
- # <%= url_for(:action => 'index') %>
- # # => /blog/
- #
- # <%= url_for(:action => 'find', :controller => 'books') %>
- # # => /books/find
- #
- # <%= url_for(:action => 'login', :controller => 'members', :only_path => false, :protocol => 'https') %>
- # # => https://www.example.com/members/login/
- #
- # <%= url_for(:action => 'play', :anchor => 'player') %>
- # # => /messages/play/#player
- #
- # <%= url_for(:action => 'jump', :anchor => 'tax&ship') %>
- # # => /testing/jump/#tax&ship
- #
- # <%= url_for(Workshop.new) %>
- # # relies on Workshop answering a persisted? call (and in this case returning false)
- # # => /workshops
- #
- # <%= url_for(@workshop) %>
- # # calls @workshop.to_param which by default returns the id
- # # => /workshops/5
- #
- # # to_param can be re-defined in a model to provide different URL names:
- # # => /workshops/1-workshop-name
- #
- # <%= url_for("http://www.example.com") %>
- # # => http://www.example.com
- #
- # <%= url_for(:back) %>
- # # if request.env["HTTP_REFERER"] is set to "http://www.example.com"
- # # => http://www.example.com
- #
- # <%= url_for(:back) %>
- # # if request.env["HTTP_REFERER"] is not set or is blank
- # # => javascript:history.back()
- #
- # <%= url_for(:action => 'index', :controller => 'users') %>
- # # Assuming an "admin" namespace
- # # => /admin/users
- #
- # <%= url_for(:action => 'index', :controller => '/users') %>
- # # Specify absolute path with beginning slash
- # # => /users
- def url_for(options = nil)
+ # Basic implementation of url_for to allow use helpers without routes existence
+ def url_for(options = nil) # :nodoc:
case options
when String
options
- when nil, Hash
- options ||= {}
- options = { :only_path => options[:host].nil? }.merge!(options.symbolize_keys)
- super
when :back
- controller.request.env["HTTP_REFERER"] || 'javascript:history.back()'
+ _back_url
else
- polymorphic_path(options)
+ raise ArgumentError, "arguments passed to url_for can't be handled. Please require " +
+ "routes or provide your own implementation"
end
end
+ def _back_url # :nodoc:
+ referrer = controller.respond_to?(:request) && controller.request.env["HTTP_REFERER"]
+ referrer || 'javascript:history.back()'
+ end
+ protected :_back_url
+
+
# Creates a link tag of the given +name+ using a URL created by the set of +options+.
# See the valid options in the documentation for +url_for+. It's also possible to
# pass a String instead of an options hash, which generates a link tag that uses the
diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
index f0ea92b018..af367ac28d 100644
--- a/actionpack/lib/action_view/lookup_context.rb
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -43,7 +43,7 @@ module ActionView
end
register_detail(:locale) { [I18n.locale, I18n.default_locale].uniq }
- register_detail(:formats) { Mime::SET.symbols }
+ register_detail(:formats) { ActionView::Base.default_formats || [:html, :text, :js, :css, :xml, :json] }
register_detail(:handlers){ Template::Handlers.extensions }
class DetailsKey #:nodoc:
diff --git a/actionpack/lib/action_view/model_naming.rb b/actionpack/lib/action_view/model_naming.rb
new file mode 100644
index 0000000000..e09ebd60df
--- /dev/null
+++ b/actionpack/lib/action_view/model_naming.rb
@@ -0,0 +1,12 @@
+module ActionView
+ module ModelNaming
+ # Converts the given object to an ActiveModel compliant one.
+ def convert_to_model(object)
+ object.respond_to?(:to_model) ? object.to_model : object
+ end
+
+ def model_name_from_record_or_class(record_or_class)
+ (record_or_class.is_a?(Class) ? record_or_class : convert_to_model(record_or_class).class).model_name
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/record_identifier.rb b/actionpack/lib/action_view/record_identifier.rb
new file mode 100644
index 0000000000..2953654972
--- /dev/null
+++ b/actionpack/lib/action_view/record_identifier.rb
@@ -0,0 +1,84 @@
+require 'active_support/core_ext/module'
+require 'action_view/model_naming'
+
+module ActionView
+ # The record identifier encapsulates a number of naming conventions for dealing with records, like Active Records or
+ # pretty much any other model type that has an id. These patterns are then used to try elevate the view actions to
+ # a higher logical level.
+ #
+ # # routes
+ # resources :posts
+ #
+ # # view
+ # <%= div_for(post) do %> <div id="post_45" class="post">
+ # <%= post.body %> What a wonderful world!
+ # <% end %> </div>
+ #
+ # # controller
+ # def update
+ # post = Post.find(params[:id])
+ # post.update_attributes(params[:post])
+ #
+ # redirect_to(post) # Calls polymorphic_url(post) which in turn calls post_url(post)
+ # end
+ #
+ # As the example above shows, you can stop caring to a large extent what the actual id of the post is.
+ # You just know that one is being assigned and that the subsequent calls in redirect_to expect that
+ # same naming convention and allows you to write less code if you follow it.
+ module RecordIdentifier
+ extend self
+ extend ModelNaming
+
+ include ModelNaming
+
+ JOIN = '_'.freeze
+ NEW = 'new'.freeze
+
+ # The DOM class convention is to use the singular form of an object or class.
+ #
+ # dom_class(post) # => "post"
+ # dom_class(Person) # => "person"
+ #
+ # If you need to address multiple instances of the same class in the same view, you can prefix the dom_class:
+ #
+ # dom_class(post, :edit) # => "edit_post"
+ # dom_class(Person, :edit) # => "edit_person"
+ def dom_class(record_or_class, prefix = nil)
+ singular = model_name_from_record_or_class(record_or_class).param_key
+ prefix ? "#{prefix}#{JOIN}#{singular}" : singular
+ end
+
+ # The DOM id convention is to use the singular form of an object or class with the id following an underscore.
+ # If no id is found, prefix with "new_" instead.
+ #
+ # dom_id(Post.find(45)) # => "post_45"
+ # dom_id(Post.new) # => "new_post"
+ #
+ # If you need to address multiple instances of the same class in the same view, you can prefix the dom_id:
+ #
+ # dom_id(Post.find(45), :edit) # => "edit_post_45"
+ # dom_id(Post.new, :custom) # => "custom_post"
+ def dom_id(record, prefix = nil)
+ if record_id = record_key_for_dom_id(record)
+ "#{dom_class(record, prefix)}#{JOIN}#{record_id}"
+ else
+ dom_class(record, prefix || NEW)
+ end
+ end
+
+ protected
+
+ # Returns a string representation of the key attribute(s) that is suitable for use in an HTML DOM id.
+ # This can be overwritten to customize the default generated string representation if desired.
+ # If you need to read back a key from a dom_id in order to query for the underlying database record,
+ # you should write a helper like 'person_record_from_dom_id' that will extract the key either based
+ # on the default implementation (which just joins all key attributes with '_') or on your own
+ # overwritten version of the method. By default, this implementation passes the key string through a
+ # method that replaces all characters that are invalid inside DOM ids, with valid ones. You need to
+ # make sure yourself that your dom ids are valid, in case you overwrite this method.
+ def record_key_for_dom_id(record)
+ key = convert_to_model(record).to_key
+ key ? key.join('_') : key
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/renderer/streaming_template_renderer.rb b/actionpack/lib/action_view/renderer/streaming_template_renderer.rb
index f46aabd2be..9cf6eb0c65 100644
--- a/actionpack/lib/action_view/renderer/streaming_template_renderer.rb
+++ b/actionpack/lib/action_view/renderer/streaming_template_renderer.rb
@@ -30,7 +30,7 @@ module ActionView
# This is the same logging logic as in ShowExceptions middleware.
# TODO Once "exceptron" is in, refactor this piece to simply re-use exceptron.
def log_error(exception) #:nodoc:
- logger = ActionController::Base.logger
+ logger = ActionView::Base.logger
return unless logger
message = "\n#{exception.class} (#{exception.message}):\n"
diff --git a/actionpack/lib/action_view/routing_url_for.rb b/actionpack/lib/action_view/routing_url_for.rb
new file mode 100644
index 0000000000..13fb6406e1
--- /dev/null
+++ b/actionpack/lib/action_view/routing_url_for.rb
@@ -0,0 +1,107 @@
+module ActionView
+ module RoutingUrlFor
+
+ # Returns the URL for the set of +options+ provided. This takes the
+ # same options as +url_for+ in Action Controller (see the
+ # documentation for <tt>ActionController::Base#url_for</tt>). Note that by default
+ # <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative "/controller/action"
+ # instead of the fully qualified URL like "http://example.com/controller/action".
+ #
+ # ==== Options
+ # * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path.
+ # * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified).
+ # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2005/". Note that this
+ # is currently not recommended since it breaks caching.
+ # * <tt>:host</tt> - Overrides the default (current) host if provided.
+ # * <tt>:protocol</tt> - Overrides the default (current) protocol if provided.
+ # * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
+ # * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
+ #
+ # ==== Relying on named routes
+ #
+ # Passing a record (like an Active Record) instead of a hash as the options parameter will
+ # trigger the named route for that record. The lookup will happen on the name of the class. So passing a
+ # Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as
+ # +admin_workshop_path+ you'll have to call that explicitly (it's impossible for +url_for+ to guess that route).
+ #
+ # ==== Implicit Controller Namespacing
+ #
+ # Controllers passed in using the +:controller+ option will retain their namespace unless it is an absolute one.
+ #
+ # ==== Examples
+ # <%= url_for(:action => 'index') %>
+ # # => /blog/
+ #
+ # <%= url_for(:action => 'find', :controller => 'books') %>
+ # # => /books/find
+ #
+ # <%= url_for(:action => 'login', :controller => 'members', :only_path => false, :protocol => 'https') %>
+ # # => https://www.example.com/members/login/
+ #
+ # <%= url_for(:action => 'play', :anchor => 'player') %>
+ # # => /messages/play/#player
+ #
+ # <%= url_for(:action => 'jump', :anchor => 'tax&ship') %>
+ # # => /testing/jump/#tax&ship
+ #
+ # <%= url_for(Workshop.new) %>
+ # # relies on Workshop answering a persisted? call (and in this case returning false)
+ # # => /workshops
+ #
+ # <%= url_for(@workshop) %>
+ # # calls @workshop.to_param which by default returns the id
+ # # => /workshops/5
+ #
+ # # to_param can be re-defined in a model to provide different URL names:
+ # # => /workshops/1-workshop-name
+ #
+ # <%= url_for("http://www.example.com") %>
+ # # => http://www.example.com
+ #
+ # <%= url_for(:back) %>
+ # # if request.env["HTTP_REFERER"] is set to "http://www.example.com"
+ # # => http://www.example.com
+ #
+ # <%= url_for(:back) %>
+ # # if request.env["HTTP_REFERER"] is not set or is blank
+ # # => javascript:history.back()
+ #
+ # <%= url_for(:action => 'index', :controller => 'users') %>
+ # # Assuming an "admin" namespace
+ # # => /admin/users
+ #
+ # <%= url_for(:action => 'index', :controller => '/users') %>
+ # # Specify absolute path with beginning slash
+ # # => /users
+ def url_for(options = nil)
+ case options
+ when String
+ options
+ when nil, Hash
+ options ||= {}
+ options = { :only_path => options[:host].nil? }.merge!(options.symbolize_keys)
+ super
+ when :back
+ _back_url
+ else
+ polymorphic_path(options)
+ end
+ end
+
+ def url_options #:nodoc:
+ return super unless controller.respond_to?(:url_options)
+ controller.url_options
+ end
+
+ def _routes_context #:nodoc:
+ controller
+ end
+ protected :_routes_context
+
+ def optimize_routes_generation? #:nodoc:
+ controller.respond_to?(:optimize_routes_generation?) ?
+ controller.optimize_routes_generation? : super
+ end
+ protected :optimize_routes_generation?
+ end
+end
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index a04eac1d3f..379cdc8a25 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/object/try'
require 'active_support/core_ext/kernel/singleton_class'
+require 'active_support/deprecation'
require 'thread'
module ActionView
@@ -92,6 +93,7 @@ module ActionView
autoload :Error
autoload :Handlers
autoload :Text
+ autoload :Types
end
extend Template::Handlers
@@ -121,7 +123,7 @@ module ActionView
@locals = details[:locals] || []
@virtual_path = details[:virtual_path]
@updated_at = details[:updated_at] || Time.now
- @formats = Array(format).map { |f| f.is_a?(Mime::Type) ? f.ref : f }
+ @formats = Array(format).map { |f| f.respond_to?(:ref) ? f.ref : f }
@compile_mutex = Mutex.new
end
@@ -147,9 +149,14 @@ module ActionView
end
def mime_type
+ ActiveSupport::Deprecation.warn 'Template#mime_type is deprecated and will be removed in Rails 4.1. Please use type method instead.'
@mime_type ||= Mime::Type.lookup_by_extension(@formats.first.to_s) if @formats.first
end
+ def type
+ @type ||= Types[@formats.first] if @formats.first
+ end
+
# Receives a view object and return a template similar to self by using @virtual_path.
#
# This method is useful if you have a template object but it does not contain its source
diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb
index f2bef4bded..e00056781d 100644
--- a/actionpack/lib/action_view/template/error.rb
+++ b/actionpack/lib/action_view/template/error.rb
@@ -8,6 +8,9 @@ module ActionView
class EncodingError < StandardError #:nodoc:
end
+ class MissingRequestError < StandardError #:nodoc:
+ end
+
class WrongEncodingError < EncodingError #:nodoc:
def initialize(string, encoding)
@string, @encoding = string, encoding
diff --git a/actionpack/lib/action_view/template/handlers/builder.rb b/actionpack/lib/action_view/template/handlers/builder.rb
index 34397c3bcf..d90b0c6378 100644
--- a/actionpack/lib/action_view/template/handlers/builder.rb
+++ b/actionpack/lib/action_view/template/handlers/builder.rb
@@ -3,7 +3,7 @@ module ActionView
class Builder
# Default format used by Builder.
class_attribute :default_format
- self.default_format = Mime::XML
+ self.default_format = :xml
def call(template)
require_engine
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index 2bb656fac9..25c6fd4aa8 100644
--- a/actionpack/lib/action_view/template/resolver.rb
+++ b/actionpack/lib/action_view/template/resolver.rb
@@ -235,7 +235,7 @@ module ActionView
extension = pieces.pop
ActiveSupport::Deprecation.warn "The file #{path} did not specify a template handler. The default is currently ERB, but will change to RAW in the future." unless extension
handler = Template.handler_for_extension(extension)
- format = pieces.last && Mime[pieces.last]
+ format = pieces.last && Template::Types[pieces.last]
[handler, format]
end
end
diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb
index 3af76dfcdb..859c7bc3ce 100644
--- a/actionpack/lib/action_view/template/text.rb
+++ b/actionpack/lib/action_view/template/text.rb
@@ -2,12 +2,12 @@ module ActionView #:nodoc:
# = Action View Text Template
class Template
class Text #:nodoc:
- attr_accessor :mime_type
+ attr_accessor :type
- def initialize(string, mime_type = nil)
- @string = string.to_s
- @mime_type = Mime[mime_type] || mime_type if mime_type
- @mime_type ||= Mime::TEXT
+ def initialize(string, type = nil)
+ @string = string.to_s
+ @type = Types[type] || type if type
+ @type ||= Types[:text]
end
def identifier
@@ -27,7 +27,7 @@ module ActionView #:nodoc:
end
def formats
- [@mime_type.to_sym]
+ [@type.to_sym]
end
end
end
diff --git a/actionpack/lib/action_view/template/types.rb b/actionpack/lib/action_view/template/types.rb
new file mode 100644
index 0000000000..7611c9e708
--- /dev/null
+++ b/actionpack/lib/action_view/template/types.rb
@@ -0,0 +1,58 @@
+require 'set'
+require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/object/blank'
+
+module ActionView
+ class Template
+ class Types
+ class Type
+ cattr_accessor :types
+ self.types = Set.new
+
+ def self.register(*t)
+ types.merge(t.map { |type| type.to_s })
+ end
+
+ register :html, :text, :js, :css, :xml, :json
+
+ def self.[](type)
+ return type if type.is_a?(self)
+
+ if type.is_a?(Symbol) || types.member?(type.to_s)
+ new(type)
+ end
+ end
+
+ attr_reader :symbol
+
+ def initialize(symbol)
+ @symbol = symbol.to_sym
+ end
+
+ delegate :to_s, :to_sym, :to => :symbol
+ alias to_str to_s
+
+ def ref
+ to_sym || to_s
+ end
+
+ def ==(type)
+ return false if type.blank?
+ symbol.to_sym == type.to_sym
+ end
+ end
+
+ cattr_accessor :type_klass
+
+ def self.delegate_to(klass)
+ self.type_klass = klass
+ end
+
+ delegate_to Type
+
+ def self.[](type)
+ type_klass[type]
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index 55f79bf761..434fc8cc14 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -38,10 +38,11 @@ module ActionView
include ActionView::Context
include ActionDispatch::Routing::PolymorphicRoutes
- include ActionController::RecordIdentifier
include AbstractController::Helpers
include ActionView::Helpers
+ include ActionView::RecordIdentifier
+ include ActionView::RoutingUrlFor
delegate :lookup_context, :to => :controller
attr_accessor :controller, :output_buffer, :rendered
diff --git a/actionpack/lib/action_view/vendor/html-scanner.rb b/actionpack/lib/action_view/vendor/html-scanner.rb
new file mode 100644
index 0000000000..879b31e60e
--- /dev/null
+++ b/actionpack/lib/action_view/vendor/html-scanner.rb
@@ -0,0 +1,20 @@
+$LOAD_PATH << "#{File.dirname(__FILE__)}/html-scanner"
+
+module HTML
+ extend ActiveSupport::Autoload
+
+ eager_autoload do
+ autoload :CDATA, 'html/node'
+ autoload :Document, 'html/document'
+ autoload :FullSanitizer, 'html/sanitizer'
+ autoload :LinkSanitizer, 'html/sanitizer'
+ autoload :Node, 'html/node'
+ autoload :Sanitizer, 'html/sanitizer'
+ autoload :Selector, 'html/selector'
+ autoload :Tag, 'html/node'
+ autoload :Text, 'html/node'
+ autoload :Tokenizer, 'html/tokenizer'
+ autoload :Version, 'html/version'
+ autoload :WhiteListSanitizer, 'html/sanitizer'
+ end
+end
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/document.rb b/actionpack/lib/action_view/vendor/html-scanner/html/document.rb
index 386820300a..386820300a 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/document.rb
+++ b/actionpack/lib/action_view/vendor/html-scanner/html/document.rb
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/node.rb b/actionpack/lib/action_view/vendor/html-scanner/html/node.rb
index 4e1f016431..4e1f016431 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/node.rb
+++ b/actionpack/lib/action_view/vendor/html-scanner/html/node.rb
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb b/actionpack/lib/action_view/vendor/html-scanner/html/sanitizer.rb
index 6b4ececda2..6b4ececda2 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
+++ b/actionpack/lib/action_view/vendor/html-scanner/html/sanitizer.rb
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb b/actionpack/lib/action_view/vendor/html-scanner/html/selector.rb
index 1eadfc0390..1eadfc0390 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
+++ b/actionpack/lib/action_view/vendor/html-scanner/html/selector.rb
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb b/actionpack/lib/action_view/vendor/html-scanner/html/tokenizer.rb
index 8ac8d34430..8ac8d34430 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb
+++ b/actionpack/lib/action_view/vendor/html-scanner/html/tokenizer.rb
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/version.rb b/actionpack/lib/action_view/vendor/html-scanner/html/version.rb
index 6d645c3e14..6d645c3e14 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/version.rb
+++ b/actionpack/lib/action_view/vendor/html-scanner/html/version.rb