Skip to content

Commit

Permalink
Merge branch 'master' of github.com:rails/rails
Browse files Browse the repository at this point in the history
  • Loading branch information
dhh committed Oct 25, 2011
2 parents 5daf077 + 8aabdc6 commit 1d9ab88
Show file tree
Hide file tree
Showing 94 changed files with 1,575 additions and 392 deletions.
47 changes: 22 additions & 25 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
pkg
.bundle
Gemfile.lock
# Don't put *.swp, *.bak, etc here; those belong in a global ~/.gitignore.
# Check out http://help.github.com/ignore-files/ for how to set that up.

debug.log
doc/rdoc
activemodel/doc
activeresource/doc
activerecord/doc
activerecord/sqlnet.log
actionpack/doc
actionmailer/doc
activesupport/doc
activesupport/test/tmp
activemodel/test/fixtures/fixture_database.sqlite3
actionpack/test/tmp
activesupport/test/fixtures/isolation_test
dist
railties/test/500.html
railties/test/fixtures/tmp
railties/test/initializer/root/log
railties/doc
railties/guides/output
railties/tmp
.rvmrc
.rbenv-version
RDOC_MAIN.rdoc
/.bundle
/.rbenv-version
/.rvmrc
/Gemfile.lock
/pkg
/dist
/doc/rdoc
/*/doc
/*/test/tmp
/activerecord/sqlnet.log
/activemodel/test/fixtures/fixture_database.sqlite3
/activesupport/test/fixtures/isolation_test
/railties/test/500.html
/railties/test/fixtures/tmp
/railties/test/initializer/root/log
/railties/doc
/railties/guides/output
/railties/tmp
/RDOC_MAIN.rdoc
7 changes: 1 addition & 6 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,7 @@ end
# it being automatically loaded by sprockets
gem "uglifier", ">= 1.0.3", :require => false

# Temp fix until rake 0.9.3 is out
if RUBY_VERSION >= "1.9.3"
gem "rake", "0.9.3.beta.1"
else
gem "rake", ">= 0.8.7"
end
gem "rake", ">= 0.8.7"
gem "mocha", ">= 0.9.8"

group :doc do
Expand Down
2 changes: 1 addition & 1 deletion actionmailer/lib/action_mailer/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ def attachments
#
# * <tt>:subject</tt> - The subject of the message, if this is omitted, Action Mailer will
# ask the Rails I18n class for a translated <tt>:subject</tt> in the scope of
# <tt>[:actionmailer, mailer_scope, action_name]</tt> or if this is missing, will translate the
# <tt>[mailer_scope, action_name]</tt> or if this is missing, will translate the
# humanized version of the <tt>action_name</tt>
# * <tt>:to</tt> - Who the message is destined for, can be a string of addresses, or an array
# of addresses.
Expand Down
2 changes: 2 additions & 0 deletions actionpack/CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
*Rails 3.2.0 (unreleased)*

* Added ActionDispatch::RequestId middleware that'll make a unique X-Request-Id header available to the response and enables the ActionDispatch::Request#uuid method. This makes it easy to trace requests from end-to-end in the stack and to identify individual requests in mixed logs like Syslog [DHH]

* Limit the number of options for select_year to 1000.

Pass the :max_years_allowed option to set your own limit.
Expand Down
4 changes: 2 additions & 2 deletions actionpack/actionpack.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ Gem::Specification.new do |s|
s.add_dependency('rack-cache', '~> 1.1')
s.add_dependency('builder', '~> 3.0.0')
s.add_dependency('i18n', '~> 0.6')
s.add_dependency('rack', '~> 1.3.2')
s.add_dependency('rack', '~> 1.3.5')
s.add_dependency('rack-test', '~> 0.6.1')
s.add_dependency('journey', '~> 1.0.0')
s.add_dependency('sprockets', '~> 2.0.2')
s.add_dependency('sprockets', '~> 2.0.3')
s.add_dependency('erubis', '~> 2.7.0')

s.add_development_dependency('tzinfo', '~> 0.3.29')
Expand Down
4 changes: 2 additions & 2 deletions actionpack/lib/action_controller/metal/data_streaming.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ module DataStreaming
# If no content type is registered for the extension, default type 'application/octet-stream' will be used.
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
# Valid values are 'inline' and 'attachment' (default).
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
# * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
# the URL, which is necessary for i18n filenames on certain browsers
# (setting <tt>:filename</tt> overrides this option).
Expand Down Expand Up @@ -92,7 +92,7 @@ def send_file(path, options = {}) #:doc:
# If no content type is registered for the extension, default type 'application/octet-stream' will be used.
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
# Valid values are 'inline' and 'attachment' (default).
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
#
# Generic data download:
#
Expand Down
2 changes: 2 additions & 0 deletions actionpack/lib/action_controller/metal/head.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ module Head
#
# head :created, :location => person_path(@person)
#
# head :created, :location => @person
#
# It can also be used to return exceptional conditions:
#
# return head(:method_not_allowed) unless request.post?
Expand Down
4 changes: 2 additions & 2 deletions actionpack/lib/action_controller/record_identifier.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ module ActionController
# <% end %> </div>
#
# # controller
# def destroy
# def update
# post = Post.find(params[:id])
# post.destroy
# post.update_attributes(params[:post])
#
# redirect_to(post) # Calls polymorphic_url(post) which in turn calls post_url(post)
# end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
require 'html/sanitizer'

module HTML #:nodoc:
# A top-level HTMl document. You give it a body of text, and it will parse that
# A top-level HTML document. You give it a body of text, and it will parse that
# text into a tree of nodes.
class Document #:nodoc:

Expand Down
2 changes: 2 additions & 0 deletions actionpack/lib/action_dispatch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ module ActionDispatch
end

autoload_under 'middleware' do
autoload :RequestId
autoload :BestStandardsSupport
autoload :Callbacks
autoload :Cookies
Expand Down Expand Up @@ -82,6 +83,7 @@ module Session
autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store'
autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store'
autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store'
autoload :CacheStore, 'action_dispatch/middleware/session/cache_store'
end

autoload_under 'testing' do
Expand Down
10 changes: 10 additions & 0 deletions actionpack/lib/action_dispatch/http/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,16 @@ def remote_ip
@remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
end

# Returns the unique request id, which is based off either the X-Request-Id header that can
# be generated by a firewall, load balancer, or web server or by the RequestId middleware
# (which sets the action_dispatch.request_id environment variable).
#
# This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.
# This relies on the rack variable set by the ActionDispatch::RequestId middleware.
def uuid
@uuid ||= env["action_dispatch.request_id"]
end

# Returns the lowercase name of the HTTP server software.
def server_software
(@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
Expand Down
2 changes: 1 addition & 1 deletion actionpack/lib/action_dispatch/http/url.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def url_for(options = {})

rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
rewritten_url << "?#{params.to_query}" unless params.empty?
rewritten_url << "##{Journey::Router::Utils.escape_uri(options[:anchor].to_param.to_s)}" if options[:anchor]
rewritten_url << "##{Journey::Router::Utils.escape_fragment(options[:anchor].to_param.to_s)}" if options[:anchor]
rewritten_url
end

Expand Down
2 changes: 1 addition & 1 deletion actionpack/lib/action_dispatch/middleware/cookies.rb
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ def []=(key, options)
options = { :value => value }
end

value = @cookies[key.to_s] = value
@cookies[key.to_s] = value

handle_options(options)

Expand Down
39 changes: 39 additions & 0 deletions actionpack/lib/action_dispatch/middleware/request_id.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require 'securerandom'
require 'active_support/core_ext/string/access'
require 'active_support/core_ext/object/blank'

module ActionDispatch
# Makes a unique request id available to the action_dispatch.request_id env variable (which is then accessible through
# ActionDispatch::Request#uuid) and sends the same id to the client via the X-Request-Id header.
#
# The unique request id is either based off the X-Request-Id header in the request, which would typically be generated
# by a firewall, load balancer, or the web server, or, if this header is not available, a random uuid. If the
# header is accepted from the outside world, we sanitize it to a max of 255 chars and alphanumeric and dashes only.
#
# The unique request id can be used to trace a request end-to-end and would typically end up being part of log files
# from multiple pieces of the stack.
class RequestId
def initialize(app)
@app = app
end

def call(env)
env["action_dispatch.request_id"] = external_request_id(env) || internal_request_id
status, headers, body = @app.call(env)

headers["X-Request-Id"] = env["action_dispatch.request_id"]
[ status, headers, body ]
end

private
def external_request_id(env)
if request_id = env["HTTP_X_REQUEST_ID"].presence
request_id.gsub(/[^\w\-]/, "").first(255)
end
end

def internal_request_id
SecureRandom.hex(16)
end
end
end
50 changes: 50 additions & 0 deletions actionpack/lib/action_dispatch/middleware/session/cache_store.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require 'action_dispatch/middleware/session/abstract_store'
require 'rack/session/memcache'

module ActionDispatch
module Session
# Session store that uses an ActiveSupport::Cache::Store to store the sessions. This store is most useful
# if you don't store critical data in your sessions and you don't need them to live for extended periods
# of time.
class CacheStore < AbstractStore
# Create a new store. The cache to use can be passed in the <tt>:cache</tt> option. If it is
# not specified, <tt>Rails.cache</tt> will be used.
def initialize(app, options = {})
@cache = options[:cache] || Rails.cache
options[:expire_after] ||= @cache.options[:expires_in]
super
end

# Get a session from the cache.
def get_session(env, sid)
sid ||= generate_sid
session = @cache.read(cache_key(sid))
session ||= {}
[sid, session]
end

# Set a session in the cache.
def set_session(env, sid, session, options)
key = cache_key(sid)
if session
@cache.write(key, session, :expires_in => options[:expire_after])
else
@cache.delete(key)
end
sid
end

# Remove a session from the cache.
def destroy_session(env, sid, options)
@cache.delete(cache_key(sid))
generate_sid
end

private
# Turn the session id into a cache key.
def cache_key(sid)
"_session_id:#{sid}"
end
end
end
end
7 changes: 3 additions & 4 deletions actionpack/lib/action_dispatch/routing/route_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -394,10 +394,9 @@ class Generator #:nodoc:
if name == :controller
value
elsif value.is_a?(Array)
value.map { |v| Journey::Router::Utils.escape_uri(v.to_param) }.join('/')
else
return nil unless param = value.to_param
param.split('/').map { |v| Journey::Router::Utils.escape_uri(v) }.join("/")
value.map { |v| v.to_param }.join('/')
elsif param = value.to_param
param
end
end

Expand Down
2 changes: 1 addition & 1 deletion actionpack/lib/action_view/helpers/number_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def number_to_phone(number, options = {})
number.gsub!(/(\d{1,3})(\d{3})(\d{4}$)/,"(\\1) \\2#{delimiter}\\3")
else
number.gsub!(/(\d{0,3})(\d{3})(\d{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
number.slice!(0, 1) if number.starts_with?('-')
number.slice!(0, 1) if number.starts_with?(delimiter) && !delimiter.blank?
end

str = []
Expand Down
4 changes: 3 additions & 1 deletion actionpack/test/controller/mime_responds_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,9 @@ def test_using_hash_resource
@request.accept = "application/json"
get :using_hash_resource
assert_equal "application/json", @response.content_type
assert_equal %Q[{"result":{"name":"david","id":13}}], @response.body
assert @response.body.include?("result")
assert @response.body.include?('"name":"david"')
assert @response.body.include?('"id":13')
end

def test_using_hash_resource_with_post
Expand Down
4 changes: 2 additions & 2 deletions actionpack/test/controller/url_rewriter_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ def test_anchor_should_call_to_param
)
end

def test_anchor_should_be_cgi_escaped
def test_anchor_should_be_uri_escaped
assert_equal(
'http://test.host/c/a/i#anc%2Fhor',
'http://test.host/c/a/i#anc/hor',
@rewriter.rewrite(@routes, :controller => 'c', :action => 'a', :id => 'i', :anchor => Struct.new(:to_param).new('anc/hor'))
)
end
Expand Down
65 changes: 65 additions & 0 deletions actionpack/test/dispatch/request_id_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
require 'abstract_unit'

class RequestIdTest < ActiveSupport::TestCase
test "passing on the request id from the outside" do
assert_equal "external-uu-rid", stub_request('HTTP_X_REQUEST_ID' => 'external-uu-rid').uuid
end

test "ensure that only alphanumeric uurids are accepted" do
assert_equal "X-Hacked-HeaderStuff", stub_request('HTTP_X_REQUEST_ID' => '; X-Hacked-Header: Stuff').uuid
end

test "ensure that 255 char limit on the request id is being enforced" do
assert_equal "X" * 255, stub_request('HTTP_X_REQUEST_ID' => 'X' * 500).uuid
end

test "generating a request id when none is supplied" do
assert_match(/\w+/, stub_request.uuid)
end

private

def stub_request(env = {})
ActionDispatch::RequestId.new(lambda { |environment| [ 200, environment, [] ] }).call(env)
ActionDispatch::Request.new(env)
end
end

class RequestIdResponseTest < ActionDispatch::IntegrationTest
class TestController < ActionController::Base
def index
head :ok
end
end

test "request id is passed all the way to the response" do
with_test_route_set do
get '/'
assert_match(/\w+/, @response.headers["X-Request-Id"])
end
end

test "request id given on request is passed all the way to the response" do
with_test_route_set do
get '/', {}, 'HTTP_X_REQUEST_ID' => 'X' * 500
assert_equal "X" * 255, @response.headers["X-Request-Id"]
end
end


private

def with_test_route_set
with_routing do |set|
set.draw do
match '/', :to => ::RequestIdResponseTest::TestController.action(:index)
end

@app = self.class.build_app(set) do |middleware|
middleware.use ActionDispatch::RequestId
end

yield
end
end
end
Loading

0 comments on commit 1d9ab88

Please sign in to comment.