mirror of
https://github.com/github/rails.git
synced 2026-04-26 03:00:59 -04:00
Merge branch 'master' of github.com:rails/rails
This commit is contained in:
1
Rakefile
1
Rakefile
@@ -49,6 +49,7 @@ end
|
||||
|
||||
desc "Install gems for all projects."
|
||||
task :install => :gem do
|
||||
require File.expand_path("../actionpack/lib/action_pack/version", __FILE__)
|
||||
(PROJECTS - ["railties"]).each do |project|
|
||||
puts "INSTALLING #{project}"
|
||||
system("gem install #{project}/pkg/#{project}-#{ActionPack::VERSION::STRING}.gem --no-ri --no-rdoc")
|
||||
|
||||
@@ -617,9 +617,9 @@ module ActionMailer #:nodoc:
|
||||
def each_template(paths, name, &block) #:nodoc:
|
||||
Array(paths).each do |path|
|
||||
templates = lookup_context.find_all(name, path)
|
||||
templates = templates.uniq_by { |t| t.formats }
|
||||
|
||||
unless templates.empty?
|
||||
templates = templates.uniq_by { |t| t.details[:formats] }
|
||||
templates.each(&block)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -7,6 +7,7 @@ require 'active_support/core_ext/class/attribute'
|
||||
require 'active_support/core_ext/module/attr_internal'
|
||||
require 'active_support/core_ext/module/delegation'
|
||||
require 'active_support/core_ext/module/anonymous'
|
||||
require 'active_support/i18n'
|
||||
|
||||
module AbstractController
|
||||
extend ActiveSupport::Autoload
|
||||
|
||||
@@ -9,10 +9,38 @@ 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.
|
||||
# TODO Add some deprecation warnings to remove I18n.locale from controllers
|
||||
class I18nProxy < ::I18n::Config #:nodoc:
|
||||
attr_reader :i18n_config, :lookup_context
|
||||
|
||||
def initialize(i18n_config, lookup_context)
|
||||
@i18n_config, @lookup_context = i18n_config, lookup_context
|
||||
end
|
||||
|
||||
def locale
|
||||
@i18n_config.locale
|
||||
end
|
||||
|
||||
def locale=(value)
|
||||
@i18n_config.locale = value
|
||||
@lookup_context.update_details(:locale => @i18n_config.locale)
|
||||
end
|
||||
end
|
||||
|
||||
module Rendering
|
||||
extend ActiveSupport::Concern
|
||||
include AbstractController::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
|
||||
|
||||
# An instance of a view class. The default view class is ActionView::Base
|
||||
#
|
||||
# The view class must have the following methods:
|
||||
@@ -108,7 +136,7 @@ module AbstractController
|
||||
end
|
||||
|
||||
def _with_template_hook(template)
|
||||
self.formats = template.details[:formats]
|
||||
self.formats = template.formats
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,7 +7,8 @@ module AbstractController
|
||||
self._view_paths = ActionView::PathSet.new
|
||||
end
|
||||
|
||||
delegate :template_exists?, :view_paths, :formats, :formats=, :to => :lookup_context
|
||||
delegate :template_exists?, :view_paths, :formats, :formats=,
|
||||
:locale, :locale=, :to => :lookup_context
|
||||
|
||||
# LookupContext is the object responsible to hold all information required to lookup
|
||||
# templates, i.e. view paths and details. Check ActionView::LookupContext for more
|
||||
|
||||
@@ -124,7 +124,7 @@ module ActionController
|
||||
end
|
||||
|
||||
def authenticate(request, &login_procedure)
|
||||
unless authorization(request).blank?
|
||||
unless request.authorization.blank?
|
||||
login_procedure.call(*user_name_and_password(request))
|
||||
end
|
||||
end
|
||||
@@ -133,15 +133,8 @@ module ActionController
|
||||
decode_credentials(request).split(/:/, 2)
|
||||
end
|
||||
|
||||
def authorization(request)
|
||||
request.env['HTTP_AUTHORIZATION'] ||
|
||||
request.env['X-HTTP_AUTHORIZATION'] ||
|
||||
request.env['X_HTTP_AUTHORIZATION'] ||
|
||||
request.env['REDIRECT_X_HTTP_AUTHORIZATION']
|
||||
end
|
||||
|
||||
def decode_credentials(request)
|
||||
ActiveSupport::Base64.decode64(authorization(request).split(' ', 2).last || '')
|
||||
ActiveSupport::Base64.decode64(request.authorization.split(' ', 2).last || '')
|
||||
end
|
||||
|
||||
def encode_credentials(user_name, password)
|
||||
@@ -176,14 +169,7 @@ module ActionController
|
||||
|
||||
# Returns false on a valid response, true otherwise
|
||||
def authenticate(secret_key, request, realm, &password_procedure)
|
||||
authorization(request) && validate_digest_response(secret_key, request, realm, &password_procedure)
|
||||
end
|
||||
|
||||
def authorization(request)
|
||||
request.env['HTTP_AUTHORIZATION'] ||
|
||||
request.env['X-HTTP_AUTHORIZATION'] ||
|
||||
request.env['X_HTTP_AUTHORIZATION'] ||
|
||||
request.env['REDIRECT_X_HTTP_AUTHORIZATION']
|
||||
request.authorization && validate_digest_response(secret_key, request, realm, &password_procedure)
|
||||
end
|
||||
|
||||
# Returns false unless the request credentials response value matches the expected value.
|
||||
@@ -226,7 +212,7 @@ module ActionController
|
||||
end
|
||||
|
||||
def decode_credentials_header(request)
|
||||
decode_credentials(authorization(request))
|
||||
decode_credentials(request.authorization)
|
||||
end
|
||||
|
||||
def decode_credentials(header)
|
||||
|
||||
@@ -67,21 +67,6 @@ module ActionDispatch
|
||||
@env["action_dispatch.request.formats"] = [Mime::Type.lookup_by_extension(parameters[:format])]
|
||||
end
|
||||
|
||||
# Returns a symbolized version of the <tt>:format</tt> parameter of the request.
|
||||
# If no \format is given it returns <tt>:js</tt>for Ajax requests and <tt>:html</tt>
|
||||
# otherwise.
|
||||
def template_format
|
||||
parameter_format = parameters[:format]
|
||||
|
||||
if parameter_format
|
||||
parameter_format
|
||||
elsif xhr?
|
||||
:js
|
||||
else
|
||||
:html
|
||||
end
|
||||
end
|
||||
|
||||
# Receives an array of mimes and return the first user sent mime that
|
||||
# matches the order array.
|
||||
#
|
||||
|
||||
@@ -58,6 +58,7 @@ module ActionView
|
||||
autoload :TestCase, 'action_view/test_case'
|
||||
end
|
||||
|
||||
require 'active_support/i18n'
|
||||
require 'active_support/core_ext/string/output_safety'
|
||||
|
||||
I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml"
|
||||
|
||||
@@ -194,7 +194,7 @@ module ActionView #:nodoc:
|
||||
attr_accessor :base_path, :assigns, :template_extension, :lookup_context
|
||||
attr_internal :captures, :request, :layout, :controller, :template, :config
|
||||
|
||||
delegate :find, :exists?, :formats, :formats=,
|
||||
delegate :find, :exists?, :formats, :formats=, :locale, :locale=,
|
||||
:view_paths, :view_paths=, :with_fallbacks, :update_details, :to => :lookup_context
|
||||
|
||||
delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
require 'active_support/core_ext/object/try'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionView
|
||||
# LookupContext is the object responsible to hold all information required to lookup
|
||||
@@ -14,16 +15,14 @@ module ActionView
|
||||
|
||||
def self.register_detail(name, options = {})
|
||||
registered_details[name] = lambda do |value|
|
||||
value = (value.blank? || options[:accessible] == false) ?
|
||||
Array(yield) : Array(value)
|
||||
value = Array(value.presence || yield)
|
||||
value |= [nil] unless options[:allow_nil] == false
|
||||
value
|
||||
end
|
||||
end
|
||||
|
||||
register_detail(:formats) { Mime::SET.symbols }
|
||||
register_detail(:locale, :accessible => false) { [I18n.locale] }
|
||||
register_detail(:handlers, :accessible => false) { Template::Handlers.extensions }
|
||||
register_detail(:locale) { [I18n.locale] }
|
||||
|
||||
class DetailsKey #:nodoc:
|
||||
attr_reader :details
|
||||
@@ -38,16 +37,12 @@ module ActionView
|
||||
def initialize(details)
|
||||
@details, @hash = details, details.hash
|
||||
end
|
||||
|
||||
def outdated?(details)
|
||||
@details != details
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(view_paths, details = {})
|
||||
@details_key = nil
|
||||
self.view_paths = view_paths
|
||||
self.details = details
|
||||
@details_key = nil
|
||||
end
|
||||
|
||||
module ViewPaths
|
||||
@@ -60,18 +55,15 @@ module ActionView
|
||||
end
|
||||
|
||||
def find(name, prefix = nil, partial = false)
|
||||
key = details_key
|
||||
@view_paths.find(name, prefix, partial || false, key.details, key)
|
||||
@view_paths.find(name, prefix, partial || false, details, details_key)
|
||||
end
|
||||
|
||||
def find_all(name, prefix = nil, partial = false)
|
||||
key = details_key
|
||||
@view_paths.find_all(name, prefix, partial || false, key.details, key)
|
||||
@view_paths.find_all(name, prefix, partial || false, details, details_key)
|
||||
end
|
||||
|
||||
def exists?(name, prefix = nil, partial = false)
|
||||
key = details_key
|
||||
@view_paths.exists?(name, prefix, partial || false, key.details, key)
|
||||
@view_paths.exists?(name, prefix, partial || false, details, details_key)
|
||||
end
|
||||
|
||||
# Add fallbacks to the view paths. Useful in cases you are rendering a file.
|
||||
@@ -89,25 +81,20 @@ module ActionView
|
||||
end
|
||||
|
||||
module Details
|
||||
def details
|
||||
@details = normalize_details(@details)
|
||||
attr_reader :details
|
||||
|
||||
def details=(details)
|
||||
@details = normalize_details(details)
|
||||
@details_key = nil if @details_key && @details_key.details != @details
|
||||
end
|
||||
|
||||
def details=(new_details)
|
||||
@details = new_details
|
||||
details
|
||||
end
|
||||
|
||||
# TODO This is too expensive. Revisit this.
|
||||
def details_key
|
||||
latest_details = self.details
|
||||
@details_key = nil if @details_key.try(:outdated?, latest_details)
|
||||
@details_key ||= DetailsKey.get(latest_details)
|
||||
@details_key ||= DetailsKey.get(@details)
|
||||
end
|
||||
|
||||
# Shortcut to read formats from details.
|
||||
def formats
|
||||
self.details[:formats]
|
||||
@details[:formats].compact
|
||||
end
|
||||
|
||||
# Shortcut to set formats in details.
|
||||
@@ -115,11 +102,25 @@ module ActionView
|
||||
self.details = @details.merge(:formats => value)
|
||||
end
|
||||
|
||||
# Shortcut to read locale.
|
||||
def locale
|
||||
I18n.locale
|
||||
end
|
||||
|
||||
# Shortcut to set locale in details and I18n.
|
||||
def locale=(value)
|
||||
I18n.locale = value
|
||||
|
||||
unless I18n.config.respond_to?(:lookup_context)
|
||||
self.details = @details.merge(:locale => value)
|
||||
end
|
||||
end
|
||||
|
||||
# Update the details keys by merging the given hash into the current
|
||||
# details hash. If a block is given, the details are modified just during
|
||||
# the execution of the block and reverted to the previous value after.
|
||||
def update_details(new_details)
|
||||
old_details = self.details
|
||||
old_details = @details
|
||||
self.details = old_details.merge(new_details)
|
||||
|
||||
if block_given?
|
||||
@@ -140,7 +141,7 @@ module ActionView
|
||||
self.class.registered_details.each do |k, v|
|
||||
details[k] = v.call(details[k])
|
||||
end
|
||||
details
|
||||
details.freeze
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -16,23 +16,22 @@ module ActionView
|
||||
end
|
||||
|
||||
extend Template::Handlers
|
||||
attr_reader :source, :identifier, :handler, :mime_type, :formats, :details
|
||||
|
||||
attr_reader :source, :identifier, :handler, :virtual_path, :formats
|
||||
|
||||
def initialize(source, identifier, handler, details)
|
||||
@source = source
|
||||
@identifier = identifier
|
||||
@handler = handler
|
||||
@details = details
|
||||
|
||||
@partial = details[:partial]
|
||||
@virtual_path = details[:virtual_path]
|
||||
@method_names = {}
|
||||
|
||||
format = details.delete(:format) || begin
|
||||
# TODO: Clean this up
|
||||
handler.respond_to?(:default_format) ? handler.default_format.to_sym.to_s : "html"
|
||||
end
|
||||
@mime_type = Mime::Type.lookup_by_extension(format.to_s)
|
||||
@formats = [format.to_sym]
|
||||
@formats << :html if format == :js
|
||||
@details[:formats] = Array.wrap(format.to_sym)
|
||||
format = details[:format]
|
||||
format ||= handler.default_format.to_sym if handler.respond_to?(:default_format)
|
||||
format ||= :html
|
||||
@formats = [format.to_sym]
|
||||
end
|
||||
|
||||
def render(view, locals, &block)
|
||||
@@ -47,19 +46,20 @@ module ActionView
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Figure out how to abstract this
|
||||
def variable_name
|
||||
@variable_name ||= identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym
|
||||
def mime_type
|
||||
@mime_type ||= Mime::Type.lookup_by_extension(@formats.first.to_s) if @formats.first
|
||||
end
|
||||
|
||||
def variable_name
|
||||
@variable_name ||= @virtual_path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
|
||||
end
|
||||
|
||||
# TODO: Figure out how to abstract this
|
||||
def counter_name
|
||||
@counter_name ||= "#{variable_name}_counter".to_sym
|
||||
end
|
||||
|
||||
# TODO: kill hax
|
||||
def partial?
|
||||
@details[:partial]
|
||||
@partial
|
||||
end
|
||||
|
||||
def inspect
|
||||
@@ -87,7 +87,7 @@ module ActionView
|
||||
|
||||
source = <<-end_src
|
||||
def #{method_name}(local_assigns)
|
||||
_old_virtual_path, @_virtual_path = @_virtual_path, #{@details[:virtual_path].inspect};_old_output_buffer = output_buffer;#{locals_code};#{code}
|
||||
_old_virtual_path, @_virtual_path = @_virtual_path, #{@virtual_path.inspect};_old_output_buffer = output_buffer;#{locals_code};#{code}
|
||||
ensure
|
||||
@_virtual_path, self.output_buffer = _old_virtual_path, _old_output_buffer
|
||||
end
|
||||
|
||||
@@ -21,6 +21,7 @@ module ActionView
|
||||
# Normalizes the arguments and passes it on to find_template.
|
||||
def find_all(name, prefix=nil, partial=false, details={}, key=nil)
|
||||
name, prefix = normalize_name(name, prefix)
|
||||
details = details.merge(:handlers => default_handlers)
|
||||
|
||||
cached(key, prefix, name, partial) do
|
||||
find_templates(name, prefix, partial, details)
|
||||
@@ -33,6 +34,10 @@ module ActionView
|
||||
@caching ||= !defined?(Rails.application) || Rails.application.config.cache_classes
|
||||
end
|
||||
|
||||
def default_handlers
|
||||
Template::Handlers.extensions + [nil]
|
||||
end
|
||||
|
||||
# This is what child classes implement. No defaults are needed
|
||||
# because Resolver guarantees that the arguments are present and
|
||||
# normalized.
|
||||
@@ -74,7 +79,7 @@ module ActionView
|
||||
|
||||
def find_templates(name, prefix, partial, details)
|
||||
path = build_path(name, prefix, partial, details)
|
||||
query(path, EXTENSION_ORDER.map { |ext| details[ext] })
|
||||
query(partial, path, EXTENSION_ORDER.map { |ext| details[ext] })
|
||||
end
|
||||
|
||||
def build_path(name, prefix, partial, details)
|
||||
@@ -84,34 +89,27 @@ module ActionView
|
||||
path
|
||||
end
|
||||
|
||||
def query(path, exts)
|
||||
def query(partial, path, exts)
|
||||
query = File.join(@path, path)
|
||||
|
||||
exts.each do |ext|
|
||||
query << '{' << ext.map {|e| e && ".#{e}" }.join(',') << '}'
|
||||
end
|
||||
|
||||
Dir[query].reject { |p| File.directory?(p) }.map do |p|
|
||||
Template.new(File.read(p), File.expand_path(p), *path_to_details(p))
|
||||
handler, format = extract_handler_and_format(p)
|
||||
Template.new(File.read(p), File.expand_path(p), handler,
|
||||
:partial => partial, :virtual_path => path, :format => format)
|
||||
end
|
||||
end
|
||||
|
||||
# # TODO: fix me
|
||||
# # :api: plugin
|
||||
def path_to_details(path)
|
||||
# [:erb, :format => :html, :locale => :en, :partial => true/false]
|
||||
if m = path.match(%r'((^|.*/)(_)?[\w-]+)((?:\.[\w-]+)*)\.(\w+)$')
|
||||
partial = m[3] == '_'
|
||||
details = (m[4]||"").split('.').reject { |e| e.empty? }
|
||||
handler = Template.handler_class_for_extension(m[5])
|
||||
def extract_handler_and_format(path)
|
||||
pieces = File.basename(path).split(".")
|
||||
pieces.shift
|
||||
|
||||
format = Mime[details.last] && details.pop.to_sym
|
||||
locale = details.last && details.pop.to_sym
|
||||
|
||||
virtual_path = (m[1].gsub("#{@path}/", "") << details.join("."))
|
||||
|
||||
return handler, :format => format, :locale => locale, :partial => partial,
|
||||
:virtual_path => virtual_path
|
||||
end
|
||||
handler = Template.handler_class_for_extension(pieces.pop)
|
||||
format = pieces.last && Mime[pieces.last] && pieces.pop.to_sym
|
||||
[handler, format]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -7,10 +7,6 @@ module ActionView #:nodoc:
|
||||
@content_type ||= Mime::TEXT
|
||||
end
|
||||
|
||||
def details
|
||||
{:formats => [@content_type.to_sym]}
|
||||
end
|
||||
|
||||
def identifier
|
||||
'text template'
|
||||
end
|
||||
@@ -28,7 +24,7 @@ module ActionView #:nodoc:
|
||||
end
|
||||
|
||||
def formats
|
||||
[mime_type]
|
||||
[@content_type.to_sym]
|
||||
end
|
||||
|
||||
def partial?
|
||||
|
||||
@@ -7,7 +7,7 @@ module ActionView #:nodoc:
|
||||
|
||||
private
|
||||
|
||||
def query(path, exts)
|
||||
def query(partial, path, exts)
|
||||
query = Regexp.escape(path)
|
||||
exts.each do |ext|
|
||||
query << '(?:' << ext.map {|e| e && Regexp.escape(".#{e}") }.join('|') << ')'
|
||||
@@ -15,9 +15,11 @@ module ActionView #:nodoc:
|
||||
|
||||
templates = []
|
||||
@hash.select { |k,v| k =~ /^#{query}$/ }.each do |path, source|
|
||||
templates << Template.new(source, path, *path_to_details(path))
|
||||
handler, format = extract_handler_and_format(path)
|
||||
templates << Template.new(source, path, handler,
|
||||
:partial => partial, :virtual_path => path, :format => format)
|
||||
end
|
||||
templates.sort_by {|t| -t.details.values.compact.size }
|
||||
templates.sort_by {|t| -t.formats.size }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -14,9 +14,6 @@ class CompiledTemplatesTest < Test::Unit::TestCase
|
||||
assert_equal "two", render(:file => "test/render_file_with_locals_and_default.erb", :locals => { :secret => "two" })
|
||||
end
|
||||
|
||||
# This is broken in 1.8.6 (not supported in Rails 3.0) because the cache uses a Hash
|
||||
# key. Since Ruby 1.8.6 implements Hash#hash using the hash's object_id, it will never
|
||||
# successfully get a cache hit here.
|
||||
def test_template_changes_are_not_reflected_with_cached_templates
|
||||
assert_equal "Hello world!", render(:file => "test/hello_world.erb")
|
||||
modify_template "test/hello_world.erb", "Goodbye world!" do
|
||||
|
||||
@@ -33,17 +33,17 @@ module RenderTestCases
|
||||
end
|
||||
|
||||
def test_render_file_with_localization
|
||||
old_locale, I18n.locale = I18n.locale, :da
|
||||
old_locale, @view.locale = @view.locale, :da
|
||||
assert_equal "Hey verden", @view.render(:file => "test/hello_world")
|
||||
ensure
|
||||
I18n.locale = old_locale
|
||||
@view.locale = old_locale
|
||||
end
|
||||
|
||||
def test_render_file_with_dashed_locale
|
||||
old_locale, I18n.locale = I18n.locale, :"pt-BR"
|
||||
old_locale, @view.locale = @view.locale, :"pt-BR"
|
||||
assert_equal "Ola mundo", @view.render(:file => "test/hello_world")
|
||||
ensure
|
||||
I18n.locale = old_locale
|
||||
@view.locale = old_locale
|
||||
end
|
||||
|
||||
def test_render_file_at_top_level
|
||||
|
||||
@@ -20,7 +20,9 @@ module ActiveRecord
|
||||
table = Arel::Table.new(table_name, :engine => @engine)
|
||||
end
|
||||
|
||||
attribute = table[column] || Arel::Attribute.new(table, column.to_sym)
|
||||
# TODO : Arel::Table#[] should fallback to using Arel::Attribute if the table/column doesn't exist
|
||||
# attribute = table[column]
|
||||
attribute = Arel::Attribute.new(table, column.to_sym)
|
||||
|
||||
case value
|
||||
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope
|
||||
|
||||
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
|
||||
|
||||
s.has_rdoc = true
|
||||
|
||||
s.add_dependency('i18n', '~> 0.3.4')
|
||||
s.add_dependency('i18n', '~> 0.3.6.pre')
|
||||
s.add_dependency('tzinfo', '~> 0.3.16')
|
||||
s.add_dependency('builder', '~> 2.1.2')
|
||||
s.add_dependency('memcache-client', '~> 1.7.5')
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<body>
|
||||
|
||||
<p class="notice"><%%= notice %></p>
|
||||
<p class="alert"><%%= alert %></p>
|
||||
|
||||
<%%= yield %>
|
||||
|
||||
|
||||
@@ -24,6 +24,10 @@ div.field, div.actions {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.alert {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.fieldWithErrors {
|
||||
padding: 2px;
|
||||
background-color: red;
|
||||
|
||||
Reference in New Issue
Block a user