Compare commits

..

31 Commits

Author SHA1 Message Date
Charlie Somerville
4fc14b1f71 3.2.19.github9 2014-11-10 15:30:19 +11:00
Charlie Somerville
7c89fc489d Merge pull request #36 from github/3-2-github+ambiguous-times
Force a period when local times are ambiguous
2014-11-10 15:26:47 +11:00
John Barnette
7a559d389c Force a period when local times are ambiguous
This commit includes the essence of
https://github.com/rails/rails/pull/17409.
2014-11-10 12:56:35 +11:00
Dirkjan Bussink
726e996306 New version because of Proc#bind removal 2014-10-09 08:42:43 +02:00
Dirkjan Bussink
eadedf7189 Merge pull request #34 from github/dbussink/rails32-proc-bind
Remove Proc#bind from Rails 3.2
2014-10-09 08:42:11 +02:00
Dirkjan Bussink
c831f67d8b Remove Proc#bind from Rails 3.2 2014-10-08 14:08:40 +02:00
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
18 changed files with 212 additions and 68 deletions

View File

@@ -1 +1 @@
3.2.19.github3
3.2.19.github9

View File

@@ -2,7 +2,6 @@ require 'mail'
require 'action_mailer/collector'
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/proc'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/module/anonymous'
@@ -627,7 +626,7 @@ module ActionMailer #:nodoc:
# Call all the procs (if any)
default_values = self.class.default.merge(self.class.default) do |k,v|
v.respond_to?(:call) ? v.bind(self).call : v
v.respond_to?(:call) ? instance_eval(&v) : v
end
# Handle defaults

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

@@ -1,14 +0,0 @@
require "active_support/core_ext/kernel/singleton_class"
class Proc #:nodoc:
def bind(object)
block, time = self, Time.now
object.class_eval do
method_name = "__bind_#{time.to_i}_#{time.usec}"
define_method(method_name, &block)
method = instance_method(method_name)
remove_method(method_name)
method
end.bind(object)
end
end

View File

@@ -1,6 +1,5 @@
require 'active_support/concern'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/proc'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/array/extract_options'
@@ -108,7 +107,11 @@ module ActiveSupport
when Symbol
method(rescuer)
when Proc
rescuer.bind(self)
if rescuer.arity == 0
Proc.new { instance_exec(&rescuer) }
else
Proc.new { |_exception| instance_exec(_exception, &rescuer) }
end
end
end
end

View File

@@ -338,7 +338,7 @@ module ActiveSupport
# so transfer time values to a utc constructor if necessary
@time = transfer_time_values_to_utc_constructor(@time) unless @time.utc?
begin
@time_zone.period_for_local(@time)
@time_zone.period_for_local(@time) { |periods| periods.first }
rescue ::TZInfo::PeriodNotFound
# time is in the "spring forward" hour gap, so we're moving the time forward one hour and trying again
@time += 1.hour

View File

@@ -309,8 +309,8 @@ module ActiveSupport
end
# Adjust the given time to the simultaneous time in UTC. Returns a Time.utc() instance.
def local_to_utc(time, dst=true)
tzinfo.local_to_utc(time, dst)
def local_to_utc(time, dst=true, &block)
tzinfo.local_to_utc(time, dst, &block)
end
# Available so that TimeZone instances respond like TZInfo::Timezone instances
@@ -319,8 +319,8 @@ module ActiveSupport
end
# Available so that TimeZone instances respond like TZInfo::Timezone instances
def period_for_local(time, dst=true)
tzinfo.period_for_local(time, dst)
def period_for_local(time, dst=true, &block)
tzinfo.period_for_local(time, dst, &block)
end
def self.find_tzinfo(name)

View File

@@ -1,12 +0,0 @@
require 'abstract_unit'
require 'active_support/core_ext/proc'
class ProcTests < Test::Unit::TestCase
def test_bind_returns_method_with_changed_self
block = Proc.new { self }
assert_equal self, block.call
bound_block = block.bind("hello")
assert_not_equal block, bound_block
assert_equal "hello", bound_block.call
end
end

View File

@@ -136,6 +136,17 @@ class TimeZoneTest < Test::Unit::TestCase
assert_equal 'EDT', twz.zone
end
def test_local_handles_non_dst_clock_set_back
# If the clocks are set back during a transition from one non-DST observance
# to another, the time of the transition will be ambiguous. The earlier
# observance will be selected to mirror behaviour of DST to non-DST transitions.
# Such a transition occurred at 02:00 local time in Moscow on 26 October 2014.
zone = ActiveSupport::TimeZone['Moscow']
twz = zone.local(2014,10,26,1)
assert_equal Time.utc(2014,10,26,1), twz.time
assert_equal Time.utc(2014,10,25,21), twz.utc
end
def test_at
zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
secs = 946684800.0

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