mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge branch 'master' into nested_has_many_through
This commit is contained in:
commit
67b17d029a
25 changed files with 193 additions and 44 deletions
|
@ -1,4 +1,5 @@
|
|||
Description:
|
||||
============
|
||||
Stubs out a new mailer and its views. Pass the mailer name, either
|
||||
CamelCased or under_scored, and an optional list of emails as arguments.
|
||||
|
||||
|
@ -6,10 +7,12 @@ Description:
|
|||
engine and test framework generators.
|
||||
|
||||
Example:
|
||||
`rails generate mailer Notifications signup forgot_password invoice`
|
||||
========
|
||||
rails generate mailer Notifications signup forgot_password invoice
|
||||
|
||||
creates a Notifications mailer class, views, test, and fixtures:
|
||||
Mailer: app/mailers/notifications.rb
|
||||
Views: app/views/notifications/signup.erb [...]
|
||||
Test: test/functional/notifications_test.rb
|
||||
Fixtures: test/fixtures/notifications/signup [...]
|
||||
|
||||
|
|
|
@ -153,8 +153,8 @@ class BaseTest < ActiveSupport::TestCase
|
|||
assert_equal(2, email.parts.length)
|
||||
assert_equal("multipart/related", email.mime_type)
|
||||
assert_equal("multipart/alternative", email.parts[0].mime_type)
|
||||
assert_equal("text/plain", email.parts[0].parts[0].mime_type)
|
||||
assert_equal("text/html", email.parts[0].parts[1].mime_type)
|
||||
assert_equal("text/plain", email.parts[0].parts[0].mime_type)
|
||||
assert_equal("text/html", email.parts[0].parts[1].mime_type)
|
||||
assert_equal("logo.png", email.parts[1].filename)
|
||||
end
|
||||
|
||||
|
|
|
@ -172,6 +172,10 @@ module ActionController
|
|||
end
|
||||
|
||||
def recycle!
|
||||
write_cookies!
|
||||
@env.delete('HTTP_COOKIE') if @cookies.blank?
|
||||
@env.delete('action_dispatch.cookies')
|
||||
@cookies = nil
|
||||
@formats = nil
|
||||
@env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
|
||||
@env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
|
||||
|
@ -301,7 +305,11 @@ module ActionController
|
|||
# and cookies, though. For sessions, you just do:
|
||||
#
|
||||
# @request.session[:key] = "value"
|
||||
# @request.cookies["key"] = "value"
|
||||
# @request.cookies[:key] = "value"
|
||||
#
|
||||
# To clear the cookies for a test just clear the request's cookies hash:
|
||||
#
|
||||
# @request.cookies.clear
|
||||
#
|
||||
# == \Testing named routes
|
||||
#
|
||||
|
@ -416,6 +424,7 @@ module ActionController
|
|||
@controller.process_with_new_base_test(@request, @response)
|
||||
@assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {}
|
||||
@request.session.delete('flash') if @request.session['flash'].blank?
|
||||
@request.cookies.merge!(@response.cookies)
|
||||
@response
|
||||
end
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
require 'rack/mount'
|
||||
require 'forwardable'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'active_support/core_ext/object/to_query'
|
||||
require 'active_support/core_ext/hash/slice'
|
||||
|
||||
|
@ -330,6 +331,7 @@ module ActionDispatch
|
|||
end
|
||||
|
||||
def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true)
|
||||
raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
|
||||
route = Route.new(self, app, conditions, requirements, defaults, name, anchor)
|
||||
@set.add_route(*route)
|
||||
named_routes[name] = route if name
|
||||
|
|
|
@ -22,7 +22,7 @@ module ActionDispatch
|
|||
end
|
||||
|
||||
def cookies
|
||||
HashWithIndifferentAccess.new(@request.cookies.merge(@response.cookies))
|
||||
@request.cookies.merge(@response.cookies).with_indifferent_access
|
||||
end
|
||||
|
||||
def redirect_to_url
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
require 'active_support/core_ext/object/blank'
|
||||
require 'active_support/core_ext/hash/reverse_merge'
|
||||
require 'rack/utils'
|
||||
|
||||
module ActionDispatch
|
||||
class TestRequest < Request
|
||||
|
@ -77,10 +78,14 @@ module ActionDispatch
|
|||
private
|
||||
def write_cookies!
|
||||
unless @cookies.blank?
|
||||
@env['HTTP_COOKIE'] = @cookies.map { |name, value| "#{name}=#{value};" }.join(' ')
|
||||
@env['HTTP_COOKIE'] = @cookies.map { |name, value| escape_cookie(name, value) }.join('; ')
|
||||
end
|
||||
end
|
||||
|
||||
def escape_cookie(name, value)
|
||||
"#{Rack::Utils.escape(name)}=#{Rack::Utils.escape(value)}"
|
||||
end
|
||||
|
||||
def delete_nil_values!
|
||||
@env.delete_if { |k, v| v.nil? }
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@ require 'active_support/core_ext/class/attribute'
|
|||
require 'active_support/core_ext/hash/slice'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'active_support/core_ext/string/output_safety'
|
||||
require 'active_support/core_ext/array/extract_options'
|
||||
|
||||
module ActionView
|
||||
# = Action View Form Helpers
|
||||
|
@ -880,9 +881,9 @@ module ActionView
|
|||
|
||||
private
|
||||
|
||||
def instantiate_builder(record, record_object = nil, options = nil, &block)
|
||||
options, record_object = record_object, nil if record_object.is_a?(Hash)
|
||||
options ||= {}
|
||||
def instantiate_builder(record, *args, &block)
|
||||
options = args.extract_options!
|
||||
record_object = args.shift
|
||||
|
||||
case record
|
||||
when String, Symbol
|
||||
|
|
|
@ -124,6 +124,20 @@ class CookiesTest < ActionController::TestCase
|
|||
cookies['user_name'] = "david"
|
||||
head :ok
|
||||
end
|
||||
|
||||
def symbol_key_mock
|
||||
cookies[:user_name] = "david" if cookies[:user_name] == "andrew"
|
||||
head :ok
|
||||
end
|
||||
|
||||
def string_key_mock
|
||||
cookies['user_name'] = "david" if cookies['user_name'] == "andrew"
|
||||
head :ok
|
||||
end
|
||||
|
||||
def noop
|
||||
head :ok
|
||||
end
|
||||
end
|
||||
|
||||
tests TestController
|
||||
|
@ -411,6 +425,57 @@ class CookiesTest < ActionController::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_setting_request_cookies_is_indifferent_access
|
||||
@request.cookies.clear
|
||||
@request.cookies[:user_name] = "andrew"
|
||||
get :string_key_mock
|
||||
assert_equal "david", cookies[:user_name]
|
||||
|
||||
@request.cookies.clear
|
||||
@request.cookies['user_name'] = "andrew"
|
||||
get :symbol_key_mock
|
||||
assert_equal "david", cookies['user_name']
|
||||
end
|
||||
|
||||
def test_cookies_retained_across_requests
|
||||
get :symbol_key
|
||||
assert_equal "user_name=david; path=/", @response.headers["Set-Cookie"]
|
||||
assert_equal "david", cookies[:user_name]
|
||||
|
||||
get :noop
|
||||
assert_nil @response.headers["Set-Cookie"]
|
||||
assert_equal "user_name=david", @request.env['HTTP_COOKIE']
|
||||
assert_equal "david", cookies[:user_name]
|
||||
|
||||
get :noop
|
||||
assert_nil @response.headers["Set-Cookie"]
|
||||
assert_equal "user_name=david", @request.env['HTTP_COOKIE']
|
||||
assert_equal "david", cookies[:user_name]
|
||||
end
|
||||
|
||||
def test_cookies_can_be_cleared
|
||||
get :symbol_key
|
||||
assert_equal "user_name=david; path=/", @response.headers["Set-Cookie"]
|
||||
assert_equal "david", cookies[:user_name]
|
||||
|
||||
@request.cookies.clear
|
||||
get :noop
|
||||
assert_nil @response.headers["Set-Cookie"]
|
||||
assert_nil @request.env['HTTP_COOKIE']
|
||||
assert_nil cookies[:user_name]
|
||||
|
||||
get :symbol_key
|
||||
assert_equal "user_name=david; path=/", @response.headers["Set-Cookie"]
|
||||
assert_equal "david", cookies[:user_name]
|
||||
end
|
||||
|
||||
def test_cookies_are_escaped
|
||||
@request.cookies[:user_ids] = '1;2'
|
||||
get :noop
|
||||
assert_equal "user_ids=1%3B2", @request.env['HTTP_COOKIE']
|
||||
assert_equal "1;2", cookies[:user_ids]
|
||||
end
|
||||
|
||||
private
|
||||
def assert_cookie_header(expected)
|
||||
header = @response.headers["Set-Cookie"]
|
||||
|
|
|
@ -2313,6 +2313,38 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
|
|||
end
|
||||
end
|
||||
|
||||
def test_invalid_route_name_raises_error
|
||||
assert_raise(ArgumentError) do
|
||||
self.class.stub_controllers do |routes|
|
||||
routes.draw { get '/products', :to => 'products#index', :as => 'products ' }
|
||||
end
|
||||
end
|
||||
|
||||
assert_raise(ArgumentError) do
|
||||
self.class.stub_controllers do |routes|
|
||||
routes.draw { get '/products', :to => 'products#index', :as => ' products' }
|
||||
end
|
||||
end
|
||||
|
||||
assert_raise(ArgumentError) do
|
||||
self.class.stub_controllers do |routes|
|
||||
routes.draw { get '/products', :to => 'products#index', :as => 'products!' }
|
||||
end
|
||||
end
|
||||
|
||||
assert_raise(ArgumentError) do
|
||||
self.class.stub_controllers do |routes|
|
||||
routes.draw { get '/products', :to => 'products#index', :as => 'products index' }
|
||||
end
|
||||
end
|
||||
|
||||
assert_raise(ArgumentError) do
|
||||
self.class.stub_controllers do |routes|
|
||||
routes.draw { get '/products', :to => 'products#index', :as => '1products' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_nested_route_in_nested_resource
|
||||
get "/posts/1/comments/2/views"
|
||||
assert_equal "comments#views", @response.body
|
||||
|
|
|
@ -36,10 +36,10 @@ class TestRequestTest < ActiveSupport::TestCase
|
|||
|
||||
req.cookies["user_name"] = "david"
|
||||
assert_equal({"user_name" => "david"}, req.cookies)
|
||||
assert_equal "user_name=david;", req.env["HTTP_COOKIE"]
|
||||
assert_equal "user_name=david", req.env["HTTP_COOKIE"]
|
||||
|
||||
req.cookies["login"] = "XJ-122"
|
||||
assert_equal({"user_name" => "david", "login" => "XJ-122"}, req.cookies)
|
||||
assert_equal %w(login=XJ-122 user_name=david), req.env["HTTP_COOKIE"].split(/; ?/).sort
|
||||
assert_equal %w(login=XJ-122 user_name=david), req.env["HTTP_COOKIE"].split(/; /).sort
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
require 'abstract_unit'
|
||||
require 'tzinfo'
|
||||
|
||||
class Map < Hash
|
||||
def category
|
||||
"<mus>"
|
||||
end
|
||||
end
|
||||
|
||||
TZInfo::Timezone.cattr_reader :loaded_zones
|
||||
|
||||
class FormOptionsHelperTest < ActionView::TestCase
|
||||
|
@ -394,6 +400,19 @@ class FormOptionsHelperTest < ActionView::TestCase
|
|||
)
|
||||
end
|
||||
|
||||
def test_fields_for_with_record_inherited_from_hash
|
||||
map = Map.new
|
||||
|
||||
output_buffer = fields_for :map, map do |f|
|
||||
concat f.select(:category, %w( abe <mus> hest))
|
||||
end
|
||||
|
||||
assert_dom_equal(
|
||||
"<select id=\"map_category\" name=\"map[category]\"><option value=\"abe\">abe</option>\n<option value=\"<mus>\" selected=\"selected\"><mus></option>\n<option value=\"hest\">hest</option></select>",
|
||||
output_buffer
|
||||
)
|
||||
end
|
||||
|
||||
def test_select_under_fields_for_with_index
|
||||
@post = Post.new
|
||||
@post.category = "<mus>"
|
||||
|
|
|
@ -72,7 +72,7 @@ module ActiveModel
|
|||
def instantiate_observer(observer) #:nodoc:
|
||||
# string/symbol
|
||||
if observer.respond_to?(:to_sym)
|
||||
observer = observer.to_s.camelize.constantize.instance
|
||||
observer.to_s.camelize.constantize.instance
|
||||
elsif observer.respond_to?(:instance)
|
||||
observer.instance
|
||||
else
|
||||
|
|
|
@ -8,7 +8,7 @@ module ActiveSupport
|
|||
# filter or modify the paths of any lines of the backtrace, you can call BacktraceCleaner#remove_filters! These two methods
|
||||
# will give you a completely untouched backtrace.
|
||||
#
|
||||
# Example:
|
||||
# ==== Example:
|
||||
#
|
||||
# bc = BacktraceCleaner.new
|
||||
# bc.add_filter { |line| line.gsub(Rails.root, '') }
|
||||
|
|
|
@ -484,19 +484,20 @@ module ActiveSupport
|
|||
# object responds to +cache_key+. Otherwise, to_param method will be
|
||||
# called. If the key is a Hash, then keys will be sorted alphabetically.
|
||||
def expanded_key(key) # :nodoc:
|
||||
if key.respond_to?(:cache_key)
|
||||
key = key.cache_key.to_s
|
||||
elsif key.is_a?(Array)
|
||||
return key.cache_key.to_s if key.respond_to?(:cache_key)
|
||||
|
||||
case key
|
||||
when Array
|
||||
if key.size > 1
|
||||
key.collect{|element| expanded_key(element)}.to_param
|
||||
key = key.collect{|element| expanded_key(element)}
|
||||
else
|
||||
key.first.to_param
|
||||
key = key.first
|
||||
end
|
||||
elsif key.is_a?(Hash)
|
||||
key = key.to_a.sort{|a,b| a.first.to_s <=> b.first.to_s}.collect{|k,v| "#{k}=#{v}"}.to_param
|
||||
else
|
||||
key = key.to_param
|
||||
when Hash
|
||||
key = key.sort_by { |k,_| k.to_s }.collect{|k,v| "#{k}=#{v}"}
|
||||
end
|
||||
|
||||
key.to_param
|
||||
end
|
||||
|
||||
# Prefix a key with the namespace. Namespace and key will be delimited with a colon.
|
||||
|
@ -589,11 +590,7 @@ module ActiveSupport
|
|||
# Check if the entry is expired. The +expires_in+ parameter can override the
|
||||
# value set when the entry was created.
|
||||
def expired?
|
||||
if @expires_in && @created_at + @expires_in <= Time.now.to_f
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
@expires_in && @created_at + @expires_in <= Time.now.to_f
|
||||
end
|
||||
|
||||
# Set a new time when the entry will expire.
|
||||
|
|
|
@ -64,21 +64,21 @@ module ActiveSupport
|
|||
end
|
||||
|
||||
# Reads and writes attributes from a configuration <tt>OrderedHash</tt>.
|
||||
#
|
||||
# require 'active_support/configurable'
|
||||
#
|
||||
#
|
||||
# require 'active_support/configurable'
|
||||
#
|
||||
# class User
|
||||
# include ActiveSupport::Configurable
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# user = User.new
|
||||
#
|
||||
#
|
||||
# user.config.allowed_access = true
|
||||
# user.config.level = 1
|
||||
#
|
||||
# user.config.allowed_access # => true
|
||||
# user.config.level # => 1
|
||||
#
|
||||
#
|
||||
def config
|
||||
@_config ||= self.class.config.inheritable_copy
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require 'active_support/core_ext/hash/keys'
|
||||
|
||||
# This class has dubious semantics and we only have it so that
|
||||
# people can write params[:key] instead of params['key']
|
||||
# people can write <tt>params[:key]</tt> instead of <tt>params['key']</tt>
|
||||
# and they get the same value for both keys.
|
||||
|
||||
module ActiveSupport
|
||||
|
@ -109,7 +109,7 @@ module ActiveSupport
|
|||
end
|
||||
|
||||
# Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
|
||||
# This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess.
|
||||
# This overloaded definition prevents returning a regular hash, if reverse_merge is called on a <tt>HashWithDifferentAccess</tt>.
|
||||
def reverse_merge(other_hash)
|
||||
super self.class.new_from_hash_copying_default(other_hash)
|
||||
end
|
||||
|
|
|
@ -14,7 +14,7 @@ module I18n
|
|||
@reloader ||= ActiveSupport::FileUpdateChecker.new([]){ I18n.reload! }
|
||||
end
|
||||
|
||||
# Add I18n::Railtie.reloader to ActionDispatch callbacks. Since, at this
|
||||
# Add <tt>I18n::Railtie.reloader</tt> to ActionDispatch callbacks. Since, at this
|
||||
# point, no path was added to the reloader, I18n.reload! is not triggered
|
||||
# on to_prepare callbacks. This will only happen on the config.after_initialize
|
||||
# callback below.
|
||||
|
|
|
@ -21,7 +21,7 @@ module ActiveSupport
|
|||
# ActiveRecord::LogSubscriber.attach_to :active_record
|
||||
#
|
||||
# Since we need to know all instance methods before attaching the log subscriber,
|
||||
# the line above should be called after your ActiveRecord::LogSubscriber definition.
|
||||
# the line above should be called after your <tt>ActiveRecord::LogSubscriber</tt> definition.
|
||||
#
|
||||
# After configured, whenever a "sql.active_record" notification is published,
|
||||
# it will properly dispatch the event (ActiveSupport::Notifications::Event) to
|
||||
|
|
|
@ -7,7 +7,7 @@ module ActiveSupport
|
|||
#
|
||||
# The cipher text and initialization vector are base64 encoded and returned to you.
|
||||
#
|
||||
# This can be used in situations similar to the MessageVerifier, but where you don't
|
||||
# This can be used in situations similar to the <tt>MessageVerifier</tt>, but where you don't
|
||||
# want users to be able to determine the value of the payload.
|
||||
class MessageEncryptor
|
||||
class InvalidMessage < StandardError; end
|
||||
|
|
|
@ -2,7 +2,7 @@ require 'active_support/base64'
|
|||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActiveSupport
|
||||
# MessageVerifier makes it easy to generate and verify messages which are signed
|
||||
# +MessageVerifier+ makes it easy to generate and verify messages which are signed
|
||||
# to prevent tampering.
|
||||
#
|
||||
# This is useful for cases like remember-me tokens and auto-unsubscribe links where the
|
||||
|
|
|
@ -37,7 +37,7 @@ class NilClass
|
|||
|
||||
# Raises a RuntimeError when you attempt to call +id+ on +nil+.
|
||||
def id
|
||||
raise RuntimeError, "Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id", caller
|
||||
raise RuntimeError, "Called id for nil, which would mistakenly be #{object_id} -- if you really wanted the id of nil, use object_id", caller
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -679,12 +679,12 @@ class CacheEntryTest < ActiveSupport::TestCase
|
|||
|
||||
def test_expired
|
||||
entry = ActiveSupport::Cache::Entry.new("value")
|
||||
assert_equal false, entry.expired?
|
||||
assert !entry.expired?, 'entry not expired'
|
||||
entry = ActiveSupport::Cache::Entry.new("value", :expires_in => 60)
|
||||
assert_equal false, entry.expired?
|
||||
assert !entry.expired?, 'entry not expired'
|
||||
time = Time.now + 61
|
||||
Time.stubs(:now).returns(time)
|
||||
assert_equal true, entry.expired?
|
||||
assert entry.expired?, 'entry is expired'
|
||||
end
|
||||
|
||||
def test_compress_values
|
||||
|
|
|
@ -33,9 +33,11 @@ class WhinyNilTest < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_id
|
||||
nil.stubs(:object_id).returns(999)
|
||||
nil.id
|
||||
rescue RuntimeError => nme
|
||||
assert_no_match(/nil:NilClass/, nme.message)
|
||||
assert_match(/999/, nme.message)
|
||||
end
|
||||
|
||||
def test_no_to_ary_coercion
|
||||
|
|
|
@ -420,7 +420,7 @@ Client.limit(5).offset(30)
|
|||
will return instead a maximum of 5 clients beginning with the 31st. The SQL looks like:
|
||||
|
||||
<sql>
|
||||
SELECT * FROM clients LIMIT 5, 30
|
||||
SELECT * FROM clients LIMIT 5 OFFSET 30
|
||||
</sql>
|
||||
|
||||
h3. Group
|
||||
|
|
|
@ -364,6 +364,20 @@ Please make sure the patch does not introduce whitespace errors:
|
|||
$ git apply --whitespace=error-all mynew_patch.diff
|
||||
</shell>
|
||||
|
||||
You can check your patches by applying your patch to an different dedicated branch:
|
||||
|
||||
<shell>
|
||||
$ git checkout -b testing_branch
|
||||
$ git apply --check my_new_patch.diff
|
||||
</shell>
|
||||
|
||||
You can make sure your patches don't add any whitespace by applying it yourself using the --whitespace=error-all option. Make sure you are on your dedicated test branche and:
|
||||
|
||||
<shell>
|
||||
$ git apply --whitespace=error-all mynew_patch.diff
|
||||
</shell>
|
||||
|
||||
|
||||
h4. Create a Lighthouse Ticket
|
||||
|
||||
Now create a ticket with your patch. Go to the "new ticket":http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/new page at Lighthouse. Fill in a reasonable title and description, remember to attach your patch file, and tag the ticket with the ‘patch’ tag and whatever other subject area tags make sense.
|
||||
|
|
Loading…
Reference in a new issue