Compare commits

...

32 Commits

Author SHA1 Message Date
Greg Ose
730e6a273c Merge pull request #64 from github/json-sessions
Support custom serialization for Session::CookieStore
2014-05-16 00:06:44 -05:00
Greg Ose
aa1b6d1284 bump rails version 2014-05-15 22:49:19 -05:00
Greg Ose
5f6c95e29e Merge branch '2-3-github' into json-sessions 2014-05-13 15:29:47 -05:00
Charlie Somerville
7403667b89 Merge pull request #67 from github/2-3-github+cve-2014-0130
CVE-2014-0130 protection
2014-05-10 00:52:08 +10:00
Charlie Somerville
1a45ec57bf CVE-2014-0130 protection 2014-05-09 23:55:20 +10:00
Greg Ose
9070fbcffe Revert nested hash indifference, swap delete order
Upstream doesnt support nested hashes having indifferent access, we
should stay consistent. Swap order for returned value in session hash.
2014-05-07 14:27:52 -05:00
Greg Ose
364b534815 support indifferent access for hashes stored within FlashHash 2014-04-30 13:18:28 -05:00
Greg Ose
14da203564 indifferent delete 2014-04-29 10:37:53 -05:00
Greg Ose
f46a4bab08 indifferent access to flash hash 2014-04-29 10:21:16 -05:00
Greg Ose
198aa6ef99 Update tests to load flash from session value 2014-04-28 15:07:52 -05:00
Greg Ose
b3ae51c9fc Add serializer option to cookie store and use Rails 4 Hash flash
Backport for Rails 4 flash hash based on https://github.com/envato/rails_4_session_flash_backport
2014-04-28 14:42:24 -05:00
Patrick Toomey
1e6e438f6e Update RAILS_VERSION 2014-04-24 10:45:25 -05:00
Patrick Toomey
2b01f832a3 Merge pull request #63 from github/memoize-nil-check
make sure nil isn't memoized and frozen
2014-04-24 10:44:47 -05:00
Patrick Toomey
1e5fda763e backport memoiziation from 3.0 2014-04-24 10:24:04 -05:00
Dirkjan Bussink
7c3d4ec43c Bump version 2014-03-31 13:43:08 +02:00
Dirkjan Bussink
7343ed7b05 Merge pull request #53 from github/dbussink/no-toplevel-exception-rescue
We shouldn't try to rescue every type of exception here
2014-03-31 11:42:02 +00:00
Dirkjan Bussink
2a70c9691d We shouldn't try to rescue every type of exception here 2014-03-31 13:35:28 +02:00
Dirkjan Bussink
a141d9de0d bump 2.3.14.github41 2014-03-27 13:55:26 +01:00
Dirkjan Bussink
74492f43a8 Merge pull request #51 from github/dbussink/fix-logging-frozen-string-query
Dup string before changing encoding because it might be frozen
2014-03-27 12:53:25 +00:00
Dirkjan Bussink
c2894170bf Dup string before changing encoding because it might be frozen
Calling String#force_encoding! on a frozen string throws an exception.
By dupping the string we prevent this from happening.
2014-03-27 13:47:08 +01:00
Charlie Somerville
057aed6e18 Merge pull request #48 from github/2-3-kill-backtick-monkey-patch
[2.3] Kill Object#` monkey patch
2014-02-23 23:20:47 +11:00
Charlie Somerville
02fc012b42 kill Object#` monkey patch 2014-02-23 23:17:44 +11:00
Mastahyeti
4fdaf21b28 bump 2.3.14.github40 2014-02-18 15:28:32 -06:00
Ben Toews
35b871fbcd Merge pull request #47 from github/CVE-2014-0081
CVE-2014-0081
2014-02-18 15:28:00 -06:00
Mastahyeti
a5697840d6 escape format for CVE-2014-0081 2014-02-18 15:25:05 -06:00
Andy Lindeman
d0e554d231 Bumps to github39 2014-02-14 00:12:09 -05:00
Andy Lindeman
d38b7664cc github38 2014-02-13 22:36:14 -05:00
Andy Lindeman
e4cd9caf02 Merge pull request #46 from github/runtime_header
Removes the X-Runtime header from ActionController::Benchmarking
2014-02-13 22:34:18 -05:00
Andy Lindeman
89e4514704 Removes the X-Runtime header from ActionController::Benchmarking
The `Rack::Runtime` middleware now provides this header
2014-02-13 22:25:27 -05:00
Joshua Peek
0a0d975f51 github37 2014-02-11 23:36:30 -06:00
Joshua Peek
62daf4cb6f Merge pull request #45 from github/rack-session-skip
Backport Rack session skip
2014-02-11 23:34:52 -06:00
Joshua Peek
24711e1e29 Backport env['rack.session.options'][:skip] 2014-02-11 23:22:39 -06:00
15 changed files with 132 additions and 72 deletions

View File

@@ -1 +1 @@
2.3.14.github36 2.3.14.github45

View File

@@ -1320,7 +1320,14 @@ module ActionController #:nodoc:
render render
end end
CVE_2014_0310 = Class.new(StandardError)
def perform_action def perform_action
# CVE-2014-0130 protection
if action_name.include? "/"
raise CVE_2014_0310
end
if action_methods.include?(action_name) if action_methods.include?(action_name)
send(action_name) send(action_name)
default_render unless performed? default_render unless performed?

View File

@@ -87,7 +87,6 @@ module ActionController #:nodoc:
log_message << " [#{complete_request_uri rescue "unknown"}]" log_message << " [#{complete_request_uri rescue "unknown"}]"
logger.info(log_message) logger.info(log_message)
response.headers["X-Runtime"] = "%.0f" % ms
else else
perform_action_without_benchmark perform_action_without_benchmark
end end

View File

@@ -45,37 +45,74 @@ module ActionController #:nodoc:
end end
def []=(k, v) def []=(k, v)
k = k.to_s
@flash[k] = v @flash[k] = v
@flash.discard(k) @flash.discard(k)
v v
end end
def [](k) def [](k)
@flash[k] @flash[k.to_s]
end end
end end
class FlashHash < Hash class FlashHash < Hash
def self.from_session_value(value)
flash = case value
when FlashHash # Rails 2.3
value
when Hash # Rails 4.0
flashes = value['flashes'] || {}
flashes.stringify_keys!
discard = value['discard'] || []
discard = discard.map do |item|
item.kind_of?(Symbol) ? item.to_s : item
end
used = Hash[flashes.keys.map{|k| [k, discard.include?(k)] }]
new_from_values(flashes, used)
else
new
end
flash
end
def initialize #:nodoc: def initialize #:nodoc:
super super
@used = {} @used = {}
end end
def to_session_value
return nil if empty?
rails_3_discard_list = @used.map{|k,v| k if v}.compact
{'discard' => rails_3_discard_list, 'flashes' => Hash[to_a]}
end
def []=(k, v) #:nodoc: def []=(k, v) #:nodoc:
k = k.to_s
keep(k) keep(k)
super super(k, v)
end
def [](k)
super(k.to_s)
end
def delete(k)
super(k.to_s)
end end
def update(h) #:nodoc: def update(h) #:nodoc:
h.stringify_keys!
h.keys.each { |k| keep(k) } h.keys.each { |k| keep(k) }
super super(h)
end end
alias :merge! :update alias :merge! :update
def replace(h) #:nodoc: def replace(h) #:nodoc:
@used = {} @used = {}
super super(h.stringify_keys)
end end
# Sets a flash that will not be available to the next action, only to the current. # Sets a flash that will not be available to the next action, only to the current.
@@ -126,8 +163,7 @@ module ActionController #:nodoc:
end end
def store(session, key = "flash") def store(session, key = "flash")
return if self.empty? session[key] = to_session_value
session[key] = self
end end
private private
@@ -138,11 +174,20 @@ module ActionController #:nodoc:
# use('msg', false) # marks the "msg" entry as unused (keeps it around for one more action) # use('msg', false) # marks the "msg" entry as unused (keeps it around for one more action)
def use(k=nil, v=true) def use(k=nil, v=true)
unless k.nil? unless k.nil?
@used[k] = v @used[k.to_s] = v
else else
keys.each{ |key| use(key, v) } keys.each{ |key| use(key, v) }
end end
end end
def self.new_from_values(flashes, used)
new.tap do |flash_hash|
flashes.each do |k, v|
flash_hash[k] = v
end
flash_hash.instance_variable_set("@used", used)
end
end
end end
module InstanceMethods #:nodoc: module InstanceMethods #:nodoc:
@@ -168,11 +213,11 @@ module ActionController #:nodoc:
if notice = response_status_and_flash.delete(:notice) if notice = response_status_and_flash.delete(:notice)
flash[:notice] = notice flash[:notice] = notice
end end
if other_flashes = response_status_and_flash.delete(:flash) if other_flashes = response_status_and_flash.delete(:flash)
flash.update(other_flashes) flash.update(other_flashes)
end end
redirect_to_without_flash(options, response_status_and_flash) redirect_to_without_flash(options, response_status_and_flash)
end end
@@ -181,19 +226,19 @@ module ActionController #:nodoc:
# to put a new one. # to put a new one.
def flash #:doc: def flash #:doc:
if !defined?(@_flash) if !defined?(@_flash)
@_flash = session["flash"] || FlashHash.new @_flash = Flash::FlashHash.from_session_value(session["flash"])
@_flash.sweep @_flash.sweep
end end
@_flash @_flash
end end
# Convenience accessor for flash[:alert] # Convenience accessor for flash[:alert]
def alert def alert
flash[:alert] flash[:alert]
end end
# Convenience accessor for flash[:alert]= # Convenience accessor for flash[:alert]=
def alert=(message) def alert=(message)
flash[:alert] = message flash[:alert] = message
@@ -203,7 +248,7 @@ module ActionController #:nodoc:
def notice def notice
flash[:notice] flash[:notice]
end end
# Convenience accessor for flash[:notice]= # Convenience accessor for flash[:notice]=
def notice=(message) def notice=(message)
flash[:notice] = message flash[:notice] = message

View File

@@ -2,7 +2,7 @@ require 'rack/utils'
module ActionController module ActionController
module Session module Session
class AbstractStore class AbstractStore
ENV_SESSION_KEY = 'rack.session'.freeze ENV_SESSION_KEY = 'rack.session'.freeze
ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
@@ -55,17 +55,17 @@ module ActionController
def [](key) def [](key)
load_for_read! load_for_read!
super super(key.to_s) || super(key)
end end
def has_key?(key) def has_key?(key)
load_for_read! load_for_read!
super super(key.to_s) || super(key)
end end
def []=(key, value) def []=(key, value)
load_for_write! load_for_write!
super super(key.to_s, value)
end end
def clear def clear
@@ -87,7 +87,9 @@ module ActionController
def delete(key) def delete(key)
load_for_write! load_for_write!
super value = super(key)
string_value = super(key.to_s)
string_value || value
end end
def data def data
@@ -119,7 +121,7 @@ module ActionController
end end
private private
def load_for_read! def load_for_read!
load! if !loaded? && exists? load! if !loaded? && exists?
end end
@@ -183,7 +185,7 @@ module ActionController
request = ActionController::Request.new(env) request = ActionController::Request.new(env)
return response if (options[:secure] && !request.ssl?) return response if (options[:secure] && !request.ssl?)
session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.loaded? session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.loaded?
sid = options[:id] || generate_sid sid = options[:id] || generate_sid
@@ -205,12 +207,12 @@ module ActionController
end end
private private
def prepare!(env) def prepare!(env)
env[ENV_SESSION_KEY] = SessionHash.new(self, env) env[ENV_SESSION_KEY] = SessionHash.new(self, env)
env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options) env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options)
end end
def generate_sid def generate_sid
ActiveSupport::SecureRandom.hex(16) ActiveSupport::SecureRandom.hex(16)
end end
@@ -222,7 +224,7 @@ module ActionController
[sid, session] [sid, session]
end end
end end
def extract_session_id(env) def extract_session_id(env)
stale_session_check! do stale_session_check! do
request = Rack::Request.new(env) request = Rack::Request.new(env)
@@ -235,7 +237,7 @@ module ActionController
def current_session_id(env) def current_session_id(env)
env[ENV_SESSION_OPTIONS_KEY][:id] env[ENV_SESSION_OPTIONS_KEY][:id]
end end
def exists?(env) def exists?(env)
current_session_id(env).present? current_session_id(env).present?
end end
@@ -247,11 +249,11 @@ module ActionController
def set_session(env, sid, session_data) def set_session(env, sid, session_data)
raise '#set_session needs to be implemented.' raise '#set_session needs to be implemented.'
end end
def destroy(env) def destroy(env)
raise '#destroy needs to be implemented.' raise '#destroy needs to be implemented.'
end end
module SessionUtils module SessionUtils
private private
def stale_session_check! def stale_session_check!

View File

@@ -37,7 +37,7 @@ module ActionController
# Note that changing digest or secret invalidates all existing sessions! # Note that changing digest or secret invalidates all existing sessions!
class CookieStore class CookieStore
include AbstractStore::SessionUtils include AbstractStore::SessionUtils
# Cookies can typically store 4096 bytes. # Cookies can typically store 4096 bytes.
MAX = 4096 MAX = 4096
SECRET_MIN_LENGTH = 30 # characters SECRET_MIN_LENGTH = 30 # characters
@@ -86,7 +86,8 @@ module ActionController
@secret = options.delete(:secret).freeze @secret = options.delete(:secret).freeze
@digest = options.delete(:digest) || 'SHA1' @digest = options.delete(:digest) || 'SHA1'
@verifier = verifier_for(@secret, @digest) @serializer = options.delete(:serializer) || Marshal
@verifier = verifier_for(@secret, @digest, @serializer)
@default_options = DEFAULT_OPTIONS.merge(options).freeze @default_options = DEFAULT_OPTIONS.merge(options).freeze
@@ -95,14 +96,21 @@ module ActionController
def call(env) def call(env)
prepare!(env) prepare!(env)
status, headers, body = @app.call(env) status, headers, body = @app.call(env)
session_data = env[ENV_SESSION_KEY] session_data = env[ENV_SESSION_KEY]
options = env[ENV_SESSION_OPTIONS_KEY] options = env[ENV_SESSION_OPTIONS_KEY]
request = ActionController::Request.new(env) request = ActionController::Request.new(env)
if !(options[:secure] && !request.ssl?) && (!session_data.is_a?(AbstractStore::SessionHash) || session_data.loaded? || options[:expire_after]) if !(options[:secure] && !request.ssl?) && (!session_data.is_a?(AbstractStore::SessionHash) || session_data.loaded? || options[:expire_after])
# Backport standard Rack::Session::Cookie behavior
# Skip writing session if env['rack.session.options'][:skip] is set
if options[:skip]
return [status, headers, body]
end
session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.loaded? session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.loaded?
persistent_session_id!(session_data) persistent_session_id!(session_data)
@@ -122,7 +130,7 @@ module ActionController
end end
private private
def prepare!(env) def prepare!(env)
env[ENV_SESSION_KEY] = AbstractStore::SessionHash.new(self, env) env[ENV_SESSION_KEY] = AbstractStore::SessionHash.new(self, env)
env[ENV_SESSION_OPTIONS_KEY] = AbstractStore::OptionsHash.new(self, env, @default_options) env[ENV_SESSION_OPTIONS_KEY] = AbstractStore::OptionsHash.new(self, env, @default_options)
@@ -131,13 +139,13 @@ module ActionController
def load_session(env) def load_session(env)
data = unpacked_cookie_data(env) data = unpacked_cookie_data(env)
data = persistent_session_id!(data) data = persistent_session_id!(data)
[data[:session_id], data] [data["session_id"] || data[:session_id], data]
end end
def extract_session_id(env) def extract_session_id(env)
if data = unpacked_cookie_data(env) if data = unpacked_cookie_data(env)
persistent_session_id!(data) unless data.empty? persistent_session_id!(data) unless data.empty?
data[:session_id] data["session_id"] || data[:session_id]
else else
nil nil
end end
@@ -207,9 +215,9 @@ module ActionController
end end
end end
def verifier_for(secret, digest) def verifier_for(secret, digest, serializer)
key = secret.respond_to?(:call) ? secret.call : secret key = secret.respond_to?(:call) ? secret.call : secret
ActiveSupport::MessageVerifier.new(key, digest: digest) ActiveSupport::MessageVerifier.new(key, digest: digest, serializer: serializer)
end end
def generate_sid def generate_sid
@@ -225,12 +233,12 @@ module ActionController
end end
def inject_persistent_session_id(data) def inject_persistent_session_id(data)
requires_session_id?(data) ? { :session_id => generate_sid } : {} requires_session_id?(data) ? { "session_id" => generate_sid } : {}
end end
def requires_session_id?(data) def requires_session_id?(data)
if data if data
data.respond_to?(:key?) && !data.key?(:session_id) data.respond_to?(:key?) && !(data.key?("session_id") || data.key?(:session_id))
else else
true true
end end

View File

@@ -219,7 +219,7 @@ module ActionController #:nodoc:
# A shortcut to the flash. Returns an empty hash if no session flash exists. # A shortcut to the flash. Returns an empty hash if no session flash exists.
def flash def flash
session['flash'] || {} ActionController::Flash::FlashHash.from_session_value(session["flash"]) || {}
end end
# Do we have a flash? # Do we have a flash?

View File

@@ -73,6 +73,8 @@ module ActionView
def number_to_currency(number, options = {}) def number_to_currency(number, options = {})
options.symbolize_keys! options.symbolize_keys!
options[:format] = ERB::Util.html_escape(options[:format]) if options[:format]
defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {} defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
currency = I18n.translate(:'number.currency.format', :locale => options[:locale], :raise => true) rescue {} currency = I18n.translate(:'number.currency.format', :locale => options[:locale], :raise => true) rescue {}
defaults = defaults.merge(currency) defaults = defaults.merge(currency)

View File

@@ -3,6 +3,12 @@ require 'abstract_unit'
class NumberHelperTest < ActionView::TestCase class NumberHelperTest < ActionView::TestCase
tests ActionView::Helpers::NumberHelper tests ActionView::Helpers::NumberHelper
def test_number_helpers_escape_delimiter_and_separator
assert_equal "$1&lt;script&gt;&lt;/script&gt;01", number_to_currency(1.01, :separator => "<script></script>")
assert_equal "$1&lt;script&gt;&lt;/script&gt;000.00", number_to_currency(1000, :delimiter => "<script></script>")
assert_equal "&lt;script&gt;1,000.00$&lt;/script&gt;", number_to_currency(1000, :format => "<script>%n%u</script>")
end
def test_number_to_phone def test_number_to_phone
assert_equal("555-1234", number_to_phone(5551234)) assert_equal("555-1234", number_to_phone(5551234))
assert_equal("800-555-1212", number_to_phone(8005551212)) assert_equal("800-555-1212", number_to_phone(8005551212))

View File

@@ -195,7 +195,9 @@ module ActiveRecord
def log_info(sql, name, ms) def log_info(sql, name, ms)
if @logger && @logger.debug? if @logger && @logger.debug?
name = '%s (%.1fms)' % [name || 'SQL', ms] name = '%s (%.1fms)' % [name || 'SQL', ms]
sql.force_encoding 'binary' if sql.respond_to?(:force_encoding) if sql.respond_to?(:force_encoding)
sql = sql.dup.force_encoding 'binary'
end
@logger.debug(format_log_entry(name, sql.squeeze(' '))) @logger.debug(format_log_entry(name, sql.squeeze(' ')))
end end
end end
@@ -212,13 +214,7 @@ module ActiveRecord
log_info(sql, name, 0) log_info(sql, name, 0)
nil nil
end end
rescue SystemExit, SignalException, NoMemoryError => e rescue => e
# Don't re-wrap these exceptions. They are probably not being caused by invalid
# sql, but rather some external stimulus beyond the responsibilty of this code.
# Additionaly, wrapping these exceptions with StatementInvalid would lead to
# meaningful loss of data, such as losing SystemExit#status.
raise e
rescue Exception => e
# Log message and raise exception. # Log message and raise exception.
# Set last_verification to 0, so that connection gets verified # Set last_verification to 0, so that connection gets verified
# upon reentering the request loop # upon reentering the request loop

View File

@@ -1,5 +1,4 @@
require 'active_support/core_ext/kernel/daemonizing' require 'active_support/core_ext/kernel/daemonizing'
require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/kernel/agnostics'
require 'active_support/core_ext/kernel/requires' require 'active_support/core_ext/kernel/requires'
require 'active_support/core_ext/kernel/debugger' require 'active_support/core_ext/kernel/debugger'

View File

@@ -1,11 +0,0 @@
class Object
# Makes backticks behave (somewhat more) similarly on all platforms.
# On win32 `nonexistent_command` raises Errno::ENOENT; on Unix, the
# spawned shell prints a message to stderr and sets $?. We emulate
# Unix on the former but not the latter.
def `(command) #:nodoc:
super
rescue Errno::ENOENT => e
STDERR.puts "#$0: #{e}"
end
end

View File

@@ -1,3 +1,6 @@
require 'active_support/core_ext/kernel/singleton_class'
require 'active_support/core_ext/module/aliasing'
module ActiveSupport module ActiveSupport
module Memoizable module Memoizable
def self.memoized_ivar_for(symbol) def self.memoized_ivar_for(symbol)
@@ -41,10 +44,10 @@ module ActiveSupport
end end
end end
def flush_cache(*syms, &block) def flush_cache(*syms)
syms.each do |sym| syms.each do |sym|
(methods + private_methods + protected_methods).each do |m| (methods + private_methods + protected_methods).each do |m|
if m.to_s =~ /^_unmemoized_(#{sym})/ if m.to_s =~ /^_unmemoized_(#{sym.to_s.gsub(/\?\Z/, '\?')})/
ivar = ActiveSupport::Memoizable.memoized_ivar_for($1) ivar = ActiveSupport::Memoizable.memoized_ivar_for($1)
instance_variable_get(ivar).clear if instance_variable_defined?(ivar) instance_variable_get(ivar).clear if instance_variable_defined?(ivar)
end end
@@ -69,7 +72,7 @@ module ActiveSupport
if instance_method(:#{symbol}).arity == 0 # if instance_method(:mime_type).arity == 0 if instance_method(:#{symbol}).arity == 0 # if instance_method(:mime_type).arity == 0
def #{symbol}(reload = false) # def mime_type(reload = false) def #{symbol}(reload = false) # def mime_type(reload = false)
if reload || !defined?(#{memoized_ivar}) || #{memoized_ivar}.empty? # if reload || !defined?(@_memoized_mime_type) || @_memoized_mime_type.empty? if reload || !defined?(#{memoized_ivar}) || #{memoized_ivar}.empty? # if reload || !defined?(@_memoized_mime_type) || @_memoized_mime_type.empty?
#{memoized_ivar} = [#{original_method}.freeze] # @_memoized_mime_type = [_unmemoized_mime_type.freeze] #{memoized_ivar} = [#{original_method}] # @_memoized_mime_type = [_unmemoized_mime_type]
end # end end # end
#{memoized_ivar}[0] # @_memoized_mime_type[0] #{memoized_ivar}[0] # @_memoized_mime_type[0]
end # end end # end
@@ -82,7 +85,7 @@ module ActiveSupport
if !reload && #{memoized_ivar}.has_key?(args) # if !reload && @_memoized_mime_type.has_key?(args) if !reload && #{memoized_ivar}.has_key?(args) # if !reload && @_memoized_mime_type.has_key?(args)
#{memoized_ivar}[args] # @_memoized_mime_type[args] #{memoized_ivar}[args] # @_memoized_mime_type[args]
elsif #{memoized_ivar} # elsif @_memoized_mime_type elsif #{memoized_ivar} # elsif @_memoized_mime_type
#{memoized_ivar}[args] = #{original_method}(*args).freeze # @_memoized_mime_type[args] = _unmemoized_mime_type(*args).freeze #{memoized_ivar}[args] = #{original_method}(*args) # @_memoized_mime_type[args] = _unmemoized_mime_type(*args)
end # end end # end
else # else else # else
#{original_method}(*args) # _unmemoized_mime_type(*args) #{original_method}(*args) # _unmemoized_mime_type(*args)

View File

@@ -1,3 +1,4 @@
require 'abstract_unit'
require 'active_support' require 'active_support'
require 'test/unit' require 'test/unit'

View File

@@ -4,12 +4,13 @@ class MemoizableTest < Test::Unit::TestCase
class Person class Person
extend ActiveSupport::Memoizable extend ActiveSupport::Memoizable
attr_reader :name_calls, :age_calls, :is_developer_calls attr_reader :name_calls, :age_calls, :is_developer_calls, :name_query_calls
def initialize def initialize
@name_calls = 0 @name_calls = 0
@age_calls = 0 @age_calls = 0
@is_developer_calls = 0 @is_developer_calls = 0
@name_query_calls = 0
end end
def name def name
@@ -18,6 +19,7 @@ class MemoizableTest < Test::Unit::TestCase
end end
def name? def name?
@name_query_calls += 1
true true
end end
memoize :name? memoize :name?
@@ -123,6 +125,13 @@ class MemoizableTest < Test::Unit::TestCase
end end
end end
def test_memoization_flush_with_punctuation
assert_equal true, @person.name?
@person.flush_cache(:name?)
3.times { assert_equal true, @person.name? }
assert_equal 2, @person.name_query_calls
end
def test_memoization_with_nil_value def test_memoization_with_nil_value
assert_equal nil, @person.age assert_equal nil, @person.age
assert_equal 1, @person.age_calls assert_equal 1, @person.age_calls
@@ -131,13 +140,7 @@ class MemoizableTest < Test::Unit::TestCase
assert_equal 1, @person.age_calls assert_equal 1, @person.age_calls
end end
def test_memorized_results_are_immutable
assert_equal "Josh", @person.name
assert_raise(ActiveSupport::FrozenObjectError) { @person.name.gsub!("Josh", "Gosh") }
end
def test_reloadable def test_reloadable
counter = @calculator.counter
assert_equal 1, @calculator.counter assert_equal 1, @calculator.counter
assert_equal 2, @calculator.counter(:reload) assert_equal 2, @calculator.counter(:reload)
assert_equal 2, @calculator.counter assert_equal 2, @calculator.counter