Compare commits

...

25 Commits

Author SHA1 Message Date
Charlie Somerville
b6f150c40c 3.2.19.github7 2014-10-07 16:41:52 +11:00
Charlie Somerville
76ad4030e5 whoops, we don't want to create an output_buffer local 2014-10-07 16:35:37 +11:00
Charlie Somerville
0a3c7ba903 3.2.19.github6 2014-10-07 16:29:46 +11:00
Charlie Somerville
d69e65ab34 use bytesize and byteslice rather than length and slice! 2014-10-07 16:29:14 +11:00
Charlie Somerville
56d2614309 3.2.19.github5 2014-09-26 16:58:46 +10:00
Rob Sanheim
0afd326c36 Move commit exception handling to a method so we can override
It looks like these methods all get included into the AbstractAdapter,
and then in github/github we have our own Adapter as an internal gem.

So this should be easy enough to override in our vendor'ed adapter.

This will allow us to still grab errors that happen in `after_commit`
which would normally be swallowed in Rails 3.0 (and beyond).

Conflicts:
	activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
2014-09-26 16:25:08 +10:00
Rob Sanheim
c957f5f609 only _run_commit_callbacks when destroyed or persisted:
should fix issues with commit callbacks getting called when the record
is not persisted (yet) in a inner transaction or due to some other edge
case.

see also caabed6c76

Conflicts:
	activerecord/lib/active_record/transactions.rb
2014-09-26 16:19:59 +10:00
Charlie Somerville
90ded51cc1 3.2.19.github4 2014-09-26 15:42:58 +10:00
Charlie Somerville
74b7cb9868 Merge pull request #31 from github/3-2-github+json-sessions
[3.2] JSON sessions
2014-09-26 15:32:46 +10:00
Charlie Somerville
bce8b07309 ignore nil session["flash"] 2014-09-26 15:26:13 +10:00
Greg Ose
3fe553fa31 more like 3.2 2014-09-24 13:25:13 -05:00
Greg Ose
e1c7a232ca update FlashHash for 3.2 2014-09-24 12:39:03 -05:00
Charlie Somerville
283923f530 we don't care about deserializing old marshal sessions anymore 2014-09-23 16:41:38 +10:00
Greg Ose
18d37237c4 attribute flash hash test source 2014-09-23 16:22:28 +10:00
Greg Ose
b30301bb6d clearer comment on session value format 2014-09-23 16:22:25 +10:00
Greg Ose
d12475ba60 support old marshalled cookies 2014-09-23 16:22:15 +10:00
Greg Ose
d106a28675 not until 4.0 2014-09-23 16:22:10 +10:00
Greg Ose
67b3fd5cb8 Custom cookie serializer test
Adapted from
b23ffd0dac/actionpack/test/dispatch/cookies_test.rb (L382-L413)
2014-09-23 16:21:49 +10:00
Greg Ose
0342deaa22 update flash hash test for 3.0 backport 2014-09-23 16:21:44 +10:00
Greg Ose
91bbe59e17 add flash hash tests 2014-09-23 16:21:37 +10:00
Greg Ose
f0895f838f 📗? Update tests and FlashHash cleanup
* Make FlashHash more like 4.1 (654a2de7a9)
* Move tests to stringified keys (b97e087321)
* Fix tests to properly load / store to session
2014-09-23 16:19:09 +10:00
Greg Ose
c9a54ce81d actually backport flash to 3.0 instead of 3.2 2014-09-23 16:16:54 +10:00
Greg Ose
f6844fc683 move serialization option from cookie option to global env option 2014-09-23 16:14:57 +10:00
Greg Ose
b09eac885e initial conversion to rails 4 flash hash, debugging marshalling issue 2014-09-23 16:13:23 +10:00
Greg Ose
1edd8b587b serializer option for cookie store 2014-09-23 16:07:59 +10:00
11 changed files with 190 additions and 33 deletions

View File

@@ -1 +1 @@
3.2.19.github3
3.2.19.github7

View File

@@ -489,6 +489,7 @@ module ActionController
@controller.recycle!
@controller.process_with_new_base_test(@request, @response)
@assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {}
@request.session['flash'] = @request.flash.to_session_value
@request.session.delete('flash') if @request.session['flash'].blank?
@response
end

View File

@@ -81,6 +81,7 @@ module ActionDispatch
class Cookies
HTTP_HEADER = "Set-Cookie".freeze
TOKEN_KEY = "action_dispatch.secret_token".freeze
SESSION_SERIALIZER = "action_dispatch.session_serializer".freeze
# Raised when storing more than 4K of session data.
class CookieOverflow < StandardError; end
@@ -106,13 +107,14 @@ module ActionDispatch
secret = request.env[TOKEN_KEY]
host = request.host
secure = request.ssl?
serializer = request.env[SESSION_SERIALIZER]
new(secret, host, secure).tap do |hash|
new(secret, host, secure, serializer).tap do |hash|
hash.update(request.cookies)
end
end
def initialize(secret = nil, host = nil, secure = false)
def initialize(secret = nil, host = nil, secure = false, serializer = nil)
@secret = secret
@set_cookies = {}
@delete_cookies = {}
@@ -120,6 +122,7 @@ module ActionDispatch
@secure = secure
@closed = false
@cookies = {}
@serializer = serializer
end
def each(&block)
@@ -211,7 +214,7 @@ module ActionDispatch
# cookies.permanent.signed[:remember_me] = current_user.id
# # => Set-Cookie: remember_me=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
def permanent
@permanent ||= PermanentCookieJar.new(self, @secret)
@permanent ||= PermanentCookieJar.new(self, @secret, @serializer)
end
# Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from
@@ -228,7 +231,7 @@ module ActionDispatch
#
# cookies.signed[:discount] # => 45
def signed
@signed ||= SignedCookieJar.new(self, @secret)
@signed ||= SignedCookieJar.new(self, @secret, @serializer)
end
def write(headers)
@@ -252,8 +255,8 @@ module ActionDispatch
end
class PermanentCookieJar < CookieJar #:nodoc:
def initialize(parent_jar, secret)
@parent_jar, @secret = parent_jar, secret
def initialize(parent_jar, secret, serializer)
@parent_jar, @secret, @serializer = parent_jar, secret, serializer
end
def []=(key, options)
@@ -268,7 +271,7 @@ module ActionDispatch
end
def signed
@signed ||= SignedCookieJar.new(self, @secret)
@signed ||= SignedCookieJar.new(self, @secret, @serializer)
end
def method_missing(method, *arguments, &block)
@@ -280,10 +283,10 @@ module ActionDispatch
MAX_COOKIE_SIZE = 4096 # Cookies can typically store 4096 bytes.
SECRET_MIN_LENGTH = 30 # Characters
def initialize(parent_jar, secret)
def initialize(parent_jar, secret, serializer)
ensure_secret_secure(secret)
@parent_jar = parent_jar
@verifier = ActiveSupport::MessageVerifier.new(secret)
@verifier = ActiveSupport::MessageVerifier.new(secret, :serializer => serializer)
end
def [](name)

View File

@@ -4,7 +4,7 @@ module ActionDispatch
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
# to put a new one.
def flash
@env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new)
@env[Flash::KEY] ||= Flash::FlashHash.from_session_value(session["flash"])
end
end
@@ -77,10 +77,24 @@ module ActionDispatch
class FlashHash
include Enumerable
def initialize #:nodoc:
@used = Set.new
def self.from_session_value(value)
case value
when Hash # After github/github-rails#9, only plain Hashes are in the session
new(value['flashes'], value['discard'])
else
new
end
end
def to_session_value
return nil if empty?
{'discard' => @used.to_a, 'flashes' => @flashes}
end
def initialize(flashes = {}, discard = []) #:nodoc:
@used = Set.new(discard)
@closed = false
@flashes = {}
@flashes = flashes
@now = nil
end
@@ -93,15 +107,17 @@ module ActionDispatch
end
def []=(k, v) #:nodoc:
k = k.to_s
keep(k)
@flashes[k] = v
end
def [](k)
@flashes[k]
@flashes[k.to_s]
end
def update(h) #:nodoc:
h.stringify_keys!
h.keys.each { |k| keep(k) }
@flashes.update h
self
@@ -112,11 +128,11 @@ module ActionDispatch
end
def key?(name)
@flashes.key? name
@flashes.key? name.to_s
end
def delete(key)
@flashes.delete key
@flashes.delete key.to_s
self
end
@@ -140,7 +156,7 @@ module ActionDispatch
def replace(h) #:nodoc:
@used = Set.new
@flashes.replace h
@flashes.replace h.stringify_keys
self
end
@@ -235,8 +251,9 @@ module ActionDispatch
end
def call(env)
if (session = env['rack.session']) && (flash = session['flash'])
if (session = env['rack.session']) && (flash = Flash::FlashHash.from_session_value(session["flash"]))
flash.sweep
env[KEY] = flash
end
@app.call(env)
@@ -246,7 +263,7 @@ module ActionDispatch
if flash_hash
if !flash_hash.empty? || session.key?('flash')
session["flash"] = flash_hash
session["flash"] = flash_hash.to_session_value
new_hash = flash_hash.dup
else
new_hash = flash_hash
@@ -255,7 +272,7 @@ module ActionDispatch
env[KEY] = new_hash
end
if session.key?('flash') && session['flash'].empty?
if session['flash'] && session['flash'].empty?
session.delete('flash')
end
end

View File

@@ -2,11 +2,11 @@ module ActionView
# = Action View Cache Helper
module Helpers
module CacheHelper
# This helper exposes a method for caching fragments of a view
# This helper exposes a method for caching fragments of a view
# rather than an entire action or page. This technique is useful
# caching pieces like menus, lists of newstopics, static HTML
# fragments, and so on. This method takes a block that contains
# the content you wish to cache.
# the content you wish to cache.
#
# See ActionController::Caching::Fragments for usage instructions.
#
@@ -23,7 +23,7 @@ module ActionView
# <p>Hello users! Welcome to our website!</p>
# <% end %>
#
# Static content with embedded ruby content can be cached as
# Static content with embedded ruby content can be cached as
# well:
#
# <% cache do %>
@@ -49,10 +49,11 @@ module ActionView
else
# VIEW TODO: Make #capture usable outside of ERB
# This dance is needed because Builder can't use capture
pos = output_buffer.length
pos = output_buffer.bytesize
yield
output_safe = output_buffer.html_safe?
fragment = output_buffer.slice!(pos..-1)
fragment = output_buffer.byteslice(pos..-1)
self.output_buffer = output_buffer.byteslice(0...pos)
if output_safe
self.output_buffer = output_buffer.class.new(output_buffer)
end

View File

@@ -1,3 +1,5 @@
# Upstream FlashHash tests from:
# https://github.com/rails/rails/blob/a6ce984b49519de7701aa13d04300c9d03cf8f72/actionpack/test/controller/flash_hash_test.rb
require 'abstract_unit'
module ActionDispatch
@@ -46,6 +48,31 @@ module ActionDispatch
assert_equal({'foo' => 'bar'}, @hash.to_hash)
end
def test_to_session_value
@hash['foo'] = 'bar'
assert_equal({'flashes' => {'foo' => 'bar'}, 'discard' => []}, @hash.to_session_value)
@hash.discard('foo')
assert_equal({'flashes' => {'foo' => 'bar'}, 'discard' => %w[foo]}, @hash.to_session_value)
@hash.now['qux'] = 1
assert_equal({'flashes' => {'foo' => 'bar', 'qux' => 1}, 'discard' => %w[foo qux]}, @hash.to_session_value)
@hash.sweep
assert_equal(nil, @hash.to_session_value)
end
def test_from_session_value_on_json_serializer
decrypted_data = "{ \"session_id\":\"d98bdf6d129618fc2548c354c161cfb5\", \"flash\":{\"discard\":[], \"flashes\":{\"message\":\"hey you\"}} }"
session = ActiveSupport::JSON.decode(decrypted_data)
hash = Flash::FlashHash.from_session_value(session['flash'])
hash.sweep
assert_equal({'discard' => %w[message], 'flashes' => { 'message' => 'hey you'}}, hash.to_session_value)
assert_equal "hey you", hash[:message]
assert_equal "hey you", hash["message"]
end
def test_empty?
assert @hash.empty?
@hash['zomg'] = 'bears'
@@ -75,6 +102,7 @@ module ActionDispatch
def test_discard_no_args
@hash['hello'] = 'world'
@hash.discard
@hash.sweep
assert_equal({}, @hash.to_hash)
end
@@ -83,8 +111,88 @@ module ActionDispatch
@hash['hello'] = 'world'
@hash['omg'] = 'world'
@hash.discard 'hello'
@hash.sweep
assert_equal({'omg' => 'world'}, @hash.to_hash)
end
def test_keep_sweep
@hash['hello'] = 'world'
@hash.sweep
assert_equal({'hello' => 'world'}, @hash.to_hash)
end
def test_update_sweep
@hash['hello'] = 'world'
@hash.update({'hi' => 'mom'})
@hash.sweep
assert_equal({'hello' => 'world', 'hi' => 'mom'}, @hash.to_hash)
end
def test_update_delete_sweep
@hash['hello'] = 'world'
@hash.delete 'hello'
@hash.update({'hello' => 'mom'})
@hash.sweep
assert_equal({'hello' => 'mom'}, @hash.to_hash)
end
def test_delete_sweep
@hash['hello'] = 'world'
@hash['hi'] = 'mom'
@hash.delete 'hi'
@hash.sweep
assert_equal({'hello' => 'world'}, @hash.to_hash)
end
def test_clear_sweep
@hash['hello'] = 'world'
@hash.clear
@hash.sweep
assert_equal({}, @hash.to_hash)
end
def test_replace_sweep
@hash['hello'] = 'world'
@hash.replace({'hi' => 'mom'})
@hash.sweep
assert_equal({'hi' => 'mom'}, @hash.to_hash)
end
def test_discard_then_add
@hash['hello'] = 'world'
@hash['omg'] = 'world'
@hash.discard 'hello'
@hash['hello'] = 'world'
@hash.sweep
assert_equal({'omg' => 'world', 'hello' => 'world'}, @hash.to_hash)
end
def test_keep_all_sweep
@hash['hello'] = 'world'
@hash['omg'] = 'world'
@hash.discard 'hello'
@hash.keep
@hash.sweep
assert_equal({'omg' => 'world', 'hello' => 'world'}, @hash.to_hash)
end
def test_double_sweep
@hash['hello'] = 'world'
@hash.sweep
assert_equal({'hello' => 'world'}, @hash.to_hash)
@hash.sweep
assert_equal({}, @hash.to_hash)
end
end
end

View File

@@ -174,13 +174,13 @@ class FlashTest < ActionController::TestCase
assert_equal(:foo_indeed, flash.discard(:foo)) # valid key passed
assert_nil flash.discard(:unknown) # non existant key passed
assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard().to_hash) # nothing passed
assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard(nil).to_hash) # nothing passed
assert_equal({"foo" => :foo_indeed, "bar" => :bar_indeed}, flash.discard().to_hash) # nothing passed
assert_equal({"foo" => :foo_indeed, "bar" => :bar_indeed}, flash.discard(nil).to_hash) # nothing passed
assert_equal(:foo_indeed, flash.keep(:foo)) # valid key passed
assert_nil flash.keep(:unknown) # non existant key passed
assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep().to_hash) # nothing passed
assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep(nil).to_hash) # nothing passed
assert_equal({"foo" => :foo_indeed, "bar" => :bar_indeed}, flash.keep().to_hash) # nothing passed
assert_equal({"foo" => :foo_indeed, "bar" => :bar_indeed}, flash.keep(nil).to_hash) # nothing passed
end
def test_redirect_to_with_alert

View File

@@ -63,6 +63,11 @@ class CookiesTest < ActionController::TestCase
head :ok
end
def set_signed_string_cookie
cookies.signed[:foo] = 'bar'
head :ok
end
def raise_data_overflow
cookies.signed[:foo] = 'bye!' * 1024
head :ok
@@ -332,6 +337,22 @@ class CookiesTest < ActionController::TestCase
}
end
class ActionDispatch::Session::CustomJsonSerializer
def self.load(value)
JSON.load(value) + " and loaded"
end
def self.dump(value)
JSON.dump(value + " was dumped")
end
end
def test_signed_cookie_using_serializer_object
@request.env["action_dispatch.session_serializer"] = ActionDispatch::Session::CustomJsonSerializer
get :set_signed_string_cookie
assert_equal 'bar was dumped and loaded', @controller.send(:cookies).signed[:foo]
end
def test_cookie_with_all_domain_option
get :set_cookie_with_domain
assert_response :success

View File

@@ -371,7 +371,7 @@ module ActiveRecord
begin
record.committed!
rescue Exception => e
record.logger.error(e) if record.respond_to?(:logger) && record.logger
handle_commit_exceptions(record, e)
end
end
end
@@ -385,6 +385,11 @@ module ActiveRecord
row = result.rows.first
row && row.first
end
# Handle any exceptions caught trying to send the commit message to a record
def handle_commit_exceptions(record, e)
record.logger.error(e) if record.respond_to?(:logger) && record.logger
end
end
end
end

View File

@@ -278,7 +278,7 @@ module ActiveRecord
# Call the after_commit callbacks
def committed! #:nodoc:
run_callbacks :commit
run_callbacks :commit if destroyed? || persisted?
ensure
@_start_transaction_state.clear
end

View File

@@ -181,7 +181,8 @@ module Rails
"action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions,
"action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local,
"action_dispatch.logger" => Rails.logger,
"action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner
"action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner,
"action_dispatch.session_serializer" => config.session_options[:serializer],
})
end