Commit 14e50388 authored by Antonio Terceiro's avatar Antonio Terceiro

Imported Upstream version 4.2.5.1

parent 4aa4a9bd
......@@ -8,7 +8,7 @@ module VERSION
MAJOR = 4
MINOR = 2
TINY = 5
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
......@@ -77,7 +77,13 @@ def view_assigns
# render "foo/bar" to render :file => "foo/bar".
# :api: plugin
def _normalize_args(action=nil, options={})
if action.is_a? Hash
case action
when ActionController::Parameters
unless action.permitted?
raise ArgumentError, "render parameters are not permitted"
end
action
when Hash
action
else
options
......
require 'base64'
require 'active_support/security_utils'
module ActionController
# Makes it dead easy to do HTTP Basic, Digest and Token authentication.
......@@ -68,7 +69,11 @@ module ClassMethods
def http_basic_authenticate_with(options = {})
before_action(options.except(:name, :password, :realm)) do
authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
name == options[:name] && password == options[:password]
# This comparison uses & so that it doesn't short circuit and
# uses `variable_size_secure_compare` so that length information
# isn't leaked.
ActiveSupport::SecurityUtils.variable_size_secure_compare(name, options[:name]) &
ActiveSupport::SecurityUtils.variable_size_secure_compare(password, options[:password])
end
end
end
......
......@@ -23,7 +23,7 @@ def #{method}(*)
SET = Mimes.new
EXTENSION_LOOKUP = {}
LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
LOOKUP = {}
class << self
def [](type)
......@@ -146,7 +146,7 @@ def register_callback(&block)
end
def lookup(string)
LOOKUP[string]
LOOKUP[string] || Type.new(string)
end
def lookup_by_extension(extension)
......@@ -225,9 +225,12 @@ def unregister(symbol)
end
end
attr_reader :hash
def initialize(string, symbol = nil, synonyms = [])
@symbol, @synonyms = symbol, synonyms
@string = string
@hash = [@string, @synonyms, @symbol].hash
end
def to_s
......@@ -261,6 +264,13 @@ def ==(mime_type)
end
end
def eql?(other)
super || (self.class == other.class &&
@string == other.string &&
@synonyms == other.synonyms &&
@symbol == other.symbol)
end
def =~(mime_type)
return false if mime_type.blank?
regexp = Regexp.new(Regexp.quote(mime_type.to_s))
......@@ -274,6 +284,10 @@ def html?
end
protected
attr_reader :string, :synonyms
private
def to_ary; end
......
require 'action_dispatch/journey'
require 'forwardable'
require 'thread_safe'
require 'active_support/concern'
require 'active_support/core_ext/object/to_query'
require 'active_support/core_ext/hash/slice'
......@@ -26,7 +25,6 @@ class RouteSet
class Dispatcher < Routing::Endpoint
def initialize(defaults)
@defaults = defaults
@controller_class_names = ThreadSafe::Cache.new
end
def dispatcher?; true; end
......@@ -68,7 +66,7 @@ def controller(params, default_controller=true)
private
def controller_reference(controller_param)
const_name = @controller_class_names[controller_param] ||= "#{controller_param.camelize}Controller"
const_name = "#{controller_param.camelize}Controller"
ActiveSupport::Dependencies.constantize(const_name)
end
......
......@@ -8,7 +8,7 @@ module VERSION
MAJOR = 4
MINOR = 2
TINY = 5
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
......@@ -58,6 +58,16 @@ def conditional_hello_with_record
end
end
def dynamic_render
render params[:id] # => String, AC:Params
end
def dynamic_render_with_file
# This is extremely bad, but should be possible to do.
file = params[:id] # => String, AC:Params
render file: file
end
def conditional_hello_with_public_header
if stale?(:last_modified => Time.now.utc.beginning_of_day, :etag => [:foo, 123], :public => true)
render :action => 'hello_world'
......@@ -273,6 +283,27 @@ def accessing_logger_in_template
class ExpiresInRenderTest < ActionController::TestCase
tests TestController
def test_dynamic_render_with_file
# This is extremely bad, but should be possible to do.
assert File.exist?(File.join(File.dirname(__FILE__), '../../test/abstract_unit.rb'))
response = get :dynamic_render_with_file, { id: '../\\../test/abstract_unit.rb' }
assert_equal File.read(File.join(File.dirname(__FILE__), '../../test/abstract_unit.rb')),
response.body
end
def test_dynamic_render
assert File.exist?(File.join(File.dirname(__FILE__), '../../test/abstract_unit.rb'))
assert_raises ActionView::MissingTemplate do
get :dynamic_render, { id: '../\\../test/abstract_unit.rb' }
end
end
def test_dynamic_render_file_hash
assert_raises ArgumentError do
get :dynamic_render, { id: { file: '../\\../test/abstract_unit.rb' } }
end
end
def test_expires_in_header
get :conditional_hello_with_expires_in
assert_equal "max-age=60, private", @response.headers["Cache-Control"]
......
......@@ -8,7 +8,7 @@ module VERSION
MAJOR = 4
MINOR = 2
TINY = 5
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
......@@ -122,6 +122,10 @@ def find(name, prefixes = [], partial = false, keys = [], options = {})
end
alias :find_template :find
def find_file(name, prefixes = [], partial = false, keys = [], options = {})
@view_paths.find_file(*args_for_lookup(name, prefixes, partial, keys, options))
end
def find_all(name, prefixes = [], partial = false, keys = [], options = {})
@view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys, options))
end
......
......@@ -46,23 +46,35 @@ def find(*args)
find_all(*args).first || raise(MissingTemplate.new(self, *args))
end
def find_file(path, prefixes = [], *args)
_find_all(path, prefixes, args, true).first || raise(MissingTemplate.new(self, path, prefixes, *args))
end
def find_all(path, prefixes = [], *args)
_find_all path, prefixes, args, false
end
def exists?(path, prefixes, *args)
find_all(path, prefixes, *args).any?
end
private
def _find_all(path, prefixes, args, outside_app)
prefixes = [prefixes] if String === prefixes
prefixes.each do |prefix|
paths.each do |resolver|
templates = resolver.find_all(path, prefix, *args)
if outside_app
templates = resolver.find_all_anywhere(path, prefix, *args)
else
templates = resolver.find_all(path, prefix, *args)
end
return templates unless templates.empty?
end
end
[]
end
def exists?(path, prefixes, *args)
find_all(path, prefixes, *args).any?
end
private
def typecast(paths)
paths.map do |path|
case path
......
......@@ -15,7 +15,7 @@ module ActionView
# that new object is called in turn. This abstracts the setup and rendering
# into a separate classes for partials and templates.
class AbstractRenderer #:nodoc:
delegate :find_template, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
delegate :find_template, :find_file, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
def initialize(lookup_context)
@lookup_context = lookup_context
......
......@@ -29,7 +29,7 @@ def determine_template(options)
elsif options.key?(:html)
Template::HTML.new(options[:html], formats.first)
elsif options.key?(:file)
with_fallbacks { find_template(options[:file], nil, false, keys, @details) }
with_fallbacks { find_file(options[:file], nil, false, keys, @details) }
elsif options.key?(:inline)
handler = Template.handler_for_extension(options[:type] || "erb")
Template.new(options[:inline], "inline template", handler, :locals => keys)
......
......@@ -113,7 +113,13 @@ def clear_cache
# Normalizes the arguments and passes it on to find_templates.
def find_all(name, prefix=nil, partial=false, details={}, key=nil, locals=[])
cached(key, [name, prefix, partial], details, locals) do
find_templates(name, prefix, partial, details)
find_templates(name, prefix, partial, details, false)
end
end
def find_all_anywhere(name, prefix, partial=false, details={}, key=nil, locals=[])
cached(key, [name, prefix, partial], details, locals) do
find_templates(name, prefix, partial, details, true)
end
end
......@@ -174,15 +180,16 @@ def initialize(pattern=nil)
private
def find_templates(name, prefix, partial, details)
def find_templates(name, prefix, partial, details, outside_app_allowed = false)
path = Path.build(name, prefix, partial)
query(path, details, details[:formats])
query(path, details, details[:formats], outside_app_allowed)
end
def query(path, details, formats)
def query(path, details, formats, outside_app_allowed)
query = build_query(path, details)
template_paths = find_template_paths query
template_paths = reject_files_external_to_app(template_paths) unless outside_app_allowed
template_paths.map { |template|
handler, format, variant = extract_handler_and_format_and_variant(template, formats)
......@@ -197,6 +204,10 @@ def query(path, details, formats)
}
end
def reject_files_external_to_app(files)
files.reject { |filename| !inside_path?(@path, filename) }
end
if RUBY_VERSION >= '2.2.0'
def find_template_paths(query)
Dir[query].reject { |filename|
......@@ -217,6 +228,12 @@ def find_template_paths(query)
end
end
def inside_path?(path, filename)
filename = File.expand_path(filename)
path = File.join(path, '')
filename.start_with?(path)
end
# Helper for building query glob string based on resolver's pattern.
def build_query(path, details)
query = @pattern.dup
......
......@@ -19,7 +19,7 @@ def to_s
private
def query(path, exts, formats)
def query(path, exts, formats, _)
query = ""
EXTENSIONS.each_key do |ext|
query << '(' << exts[ext].map {|e| e && Regexp.escape(".#{e}") }.join('|') << '|)'
......@@ -44,7 +44,7 @@ def query(path, exts, formats)
end
class NullResolver < PathResolver
def query(path, exts, formats)
def query(path, exts, formats, _)
handler, format, variant = extract_handler_and_format_and_variant(path, formats)
[ActionView::Template.new("Template generated by Null Resolver", path, handler, :virtual_path => path, :format => format, :variant => variant)]
end
......
......@@ -141,6 +141,13 @@ def test_render_partial_from_default
assert_equal "only partial", @view.render("test/partial_only")
end
def test_render_outside_path
assert File.exist?(File.join(File.dirname(__FILE__), '../../test/abstract_unit.rb'))
assert_raises ActionView::MissingTemplate do
@view.render(:template => "../\\../test/abstract_unit.rb")
end
end
def test_render_partial
assert_equal "only partial", @view.render(:partial => "test/partial_only")
end
......
......@@ -8,7 +8,7 @@ module VERSION
MAJOR = 4
MINOR = 2
TINY = 5
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
......@@ -8,7 +8,7 @@ module VERSION
MAJOR = 4
MINOR = 2
TINY = 5
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
......@@ -10,7 +10,7 @@ module JSON
included do
extend ActiveModel::Naming
class_attribute :include_root_in_json
class_attribute :include_root_in_json, instance_writer: false
self.include_root_in_json = false
end
......
......@@ -47,9 +47,10 @@ module Validations
include HelperMethods
attr_accessor :validation_context
private :validation_context=
define_callbacks :validate, scope: :name
class_attribute :_validators
class_attribute :_validators, instance_writer: false
self._validators = Hash.new { |h,k| h[k] = [] }
end
......
......@@ -69,7 +69,7 @@ module ActiveRecord
# Where conditions on an enum attribute must use the ordinal value of an enum.
module Enum
def self.extended(base) # :nodoc:
base.class_attribute(:defined_enums)
base.class_attribute(:defined_enums, instance_writer: false)
base.defined_enums = {}
end
......
......@@ -8,7 +8,7 @@ module VERSION
MAJOR = 4
MINOR = 2
TINY = 5
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
......@@ -523,7 +523,7 @@ def has_destroy_flag?(hash)
# has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
# association and evaluates to +true+.
def reject_new_record?(association_name, attributes)
has_destroy_flag?(attributes) || call_reject_if(association_name, attributes)
will_be_destroyed?(association_name, attributes) || call_reject_if(association_name, attributes)
end
# Determines if a record with the particular +attributes+ should be
......@@ -532,7 +532,8 @@ def reject_new_record?(association_name, attributes)
#
# Returns false if there is a +destroy_flag+ on the attributes.
def call_reject_if(association_name, attributes)
return false if has_destroy_flag?(attributes)
return false if will_be_destroyed?(association_name, attributes)
case callback = self.nested_attributes_options[association_name][:reject_if]
when Symbol
method(callback).arity == 0 ? send(callback) : send(callback, attributes)
......@@ -541,6 +542,15 @@ def call_reject_if(association_name, attributes)
end
end
# Only take into account the destroy flag if <tt>:allow_destroy</tt> is true
def will_be_destroyed?(association_name, attributes)
allow_destroy?(association_name) && has_destroy_flag?(attributes)
end
def allow_destroy?(association_name)
self.nested_attributes_options[association_name][:allow_destroy]
end
def raise_nested_attributes_record_not_found!(association_name, record_id)
raise RecordNotFound, "Couldn't find #{self.class._reflect_on_association(association_name).klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
end
......
......@@ -7,8 +7,8 @@ module Reflection # :nodoc:
extend ActiveSupport::Concern
included do
class_attribute :_reflections
class_attribute :aggregate_reflections
class_attribute :_reflections, instance_writer: false
class_attribute :aggregate_reflections, instance_writer: false
self._reflections = {}
self.aggregate_reflections = {}
end
......
......@@ -146,6 +146,19 @@ def test_destroy_works_independent_of_reject_if
assert man.reload.interests.empty?
end
def test_reject_if_is_not_short_circuited_if_allow_destroy_is_false
Pirate.accepts_nested_attributes_for :ship, reject_if: ->(a) { a[:name] == "The Golden Hind" }, allow_destroy: false
pirate = Pirate.create!(catchphrase: "Stop wastin' me time", ship_attributes: { name: "White Pearl", _destroy: "1" })
assert_equal "White Pearl", pirate.reload.ship.name
pirate.update!(ship_attributes: { id: pirate.ship.id, name: "The Golden Hind", _destroy: "1" })
assert_equal "White Pearl", pirate.reload.ship.name
pirate.update!(ship_attributes: { id: pirate.ship.id, name: "Black Pearl", _destroy: "1" })
assert_equal "Black Pearl", pirate.reload.ship.name
end
def test_has_many_association_updating_a_single_record
Man.accepts_nested_attributes_for(:interests)
man = Man.create(name: 'John')
......
......@@ -770,7 +770,7 @@ def define_callbacks(*names)
options = names.extract_options!
names.each do |name|
class_attribute "_#{name}_callbacks"
class_attribute "_#{name}_callbacks", instance_writer: false
set_callbacks name, CallbackChain.new(name, options)
module_eval <<-RUBY, __FILE__, __LINE__ + 1
......
......@@ -8,7 +8,7 @@ module VERSION
MAJOR = 4
MINOR = 2
TINY = 5
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
require 'digest'
module ActiveSupport
module SecurityUtils
# Constant time string comparison.
......@@ -16,5 +18,10 @@ def secure_compare(a, b)
res == 0
end
module_function :secure_compare
def variable_size_secure_compare(a, b) # :nodoc:
secure_compare(::Digest::SHA256.hexdigest(a), ::Digest::SHA256.hexdigest(b))
end
module_function :variable_size_secure_compare
end
end
......@@ -8,7 +8,7 @@ module VERSION
MAJOR = 4
MINOR = 2
TINY = 5
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
......@@ -8,7 +8,7 @@ module VERSION
MAJOR = 4
MINOR = 2
TINY = 5
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment