Vendoring Rails 2.3.5

This commit is contained in:
Eric Allen 2009-12-07 12:42:42 -05:00
parent 3e83d19299
commit f8779795ce
943 changed files with 56503 additions and 61351 deletions

View file

@ -0,0 +1,8 @@
# For forward compatibility with Rails 3.
#
# require 'active_support' loads a very bare minumum in Rails 3.
# require 'active_support/all' loads the whole suite like Rails 2 did.
#
# To prepare for Rails 3, switch to require 'active_support/all' now.
require 'active_support'

View file

@ -0,0 +1,72 @@
module ActiveSupport
# Many backtraces include too much information that's not relevant for the context. This makes it hard to find the signal
# in the backtrace and adds debugging time. With a BacktraceCleaner, you can setup filters and silencers for your particular
# context, so only the relevant lines are included.
#
# If you need to reconfigure an existing BacktraceCleaner, like the one in Rails, to show as much as possible, you can always
# call BacktraceCleaner#remove_silencers!
#
# Example:
#
# bc = BacktraceCleaner.new
# bc.add_filter { |line| line.gsub(Rails.root, '') }
# bc.add_silencer { |line| line =~ /mongrel|rubygems/ }
# bc.clean(exception.backtrace) # will strip the Rails.root prefix and skip any lines from mongrel or rubygems
#
# Inspired by the Quiet Backtrace gem by Thoughtbot.
class BacktraceCleaner
def initialize
@filters, @silencers = [], []
end
# Returns the backtrace after all filters and silencers has been run against it. Filters run first, then silencers.
def clean(backtrace)
silence(filter(backtrace))
end
# Adds a filter from the block provided. Each line in the backtrace will be mapped against this filter.
#
# Example:
#
# # Will turn "/my/rails/root/app/models/person.rb" into "/app/models/person.rb"
# backtrace_cleaner.add_filter { |line| line.gsub(Rails.root, '') }
def add_filter(&block)
@filters << block
end
# Adds a silencer from the block provided. If the silencer returns true for a given line, it'll be excluded from the
# clean backtrace.
#
# Example:
#
# # Will reject all lines that include the word "mongrel", like "/gems/mongrel/server.rb" or "/app/my_mongrel_server/rb"
# backtrace_cleaner.add_silencer { |line| line =~ /mongrel/ }
def add_silencer(&block)
@silencers << block
end
# Will remove all silencers, but leave in the filters. This is useful if your context of debugging suddenly expands as
# you suspect a bug in the libraries you use.
def remove_silencers!
@silencers = []
end
private
def filter(backtrace)
@filters.each do |f|
backtrace = backtrace.map { |line| f.call(line) }
end
backtrace
end
def silence(backtrace)
@silencers.each do |s|
backtrace = backtrace.reject { |line| s.call(line) }
end
backtrace
end
end
end

View file

@ -13,6 +13,8 @@ module ActiveSupport
MAX_BUFFER_SIZE = 1000
##
# :singleton-method:
# Set to false to disable the silencer
cattr_accessor :silencer
self.silencer = true
@ -66,13 +68,13 @@ module ActiveSupport
for severity in Severity.constants
class_eval <<-EOT, __FILE__, __LINE__
def #{severity.downcase}(message = nil, progname = nil, &block)
add(#{severity}, message, progname, &block)
end
def #{severity.downcase}?
#{severity} >= @level
end
def #{severity.downcase}(message = nil, progname = nil, &block) # def debug(message = nil, progname = nil, &block)
add(#{severity}, message, progname, &block) # add(DEBUG, message, progname, &block)
end # end
#
def #{severity.downcase}? # def debug?
#{severity} >= @level # DEBUG >= @level
end # end
EOT
end
@ -94,9 +96,12 @@ module ActiveSupport
@guard.synchronize do
unless buffer.empty?
old_buffer = buffer
clear_buffer
@log.write(old_buffer.join)
end
# Important to do this even if buffer was empty or else @buffer will
# accumulate empty arrays for each request where nothing was logged.
clear_buffer
end
end

View file

@ -3,6 +3,17 @@ require 'benchmark'
module ActiveSupport
# See ActiveSupport::Cache::Store for documentation.
module Cache
autoload :FileStore, 'active_support/cache/file_store'
autoload :MemoryStore, 'active_support/cache/memory_store'
autoload :SynchronizedMemoryStore, 'active_support/cache/synchronized_memory_store'
autoload :DRbStore, 'active_support/cache/drb_store'
autoload :MemCacheStore, 'active_support/cache/mem_cache_store'
autoload :CompressedMemCacheStore, 'active_support/cache/compressed_mem_cache_store'
module Strategy
autoload :LocalCache, 'active_support/cache/strategy/local_cache'
end
# Creates a new CacheStore object according to the given options.
#
# If no arguments are passed to this method, then a new
@ -80,11 +91,23 @@ module ActiveSupport
class Store
cattr_accessor :logger
attr_reader :silence, :logger_off
def silence!
@silence = true
self
end
alias silence? silence
alias logger_off? logger_off
def mute
previous_silence, @silence = defined?(@silence) && @silence, true
yield
ensure
@silence = previous_silence
end
# Fetches data from the cache, using the given key. If there is data in
# the cache with the given key, then that data is returned.
#
@ -136,13 +159,13 @@ module ActiveSupport
log("miss", key, options)
value = nil
seconds = Benchmark.realtime { value = yield }
ms = Benchmark.ms { value = yield }
@logger_off = true
write(key, value, options)
@logger_off = false
log("write (will save #{'%.2f' % (seconds * 1000)}ms)", key, nil)
log('write (will save %.2fms)' % ms, key, nil)
value
end
@ -209,15 +232,17 @@ module ActiveSupport
end
private
def expires_in(options)
expires_in = options && options[:expires_in]
raise ":expires_in must be a number" if expires_in && !expires_in.is_a?(Numeric)
expires_in || 0
end
def log(operation, key, options)
logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") if logger && !@silence && !@logger_off
logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") if logger && !silence? && !logger_off?
end
end
end
end
require 'active_support/cache/file_store'
require 'active_support/cache/memory_store'
require 'active_support/cache/drb_store'
require 'active_support/cache/mem_cache_store'
require 'active_support/cache/compressed_mem_cache_store'

View file

@ -1,15 +1,14 @@
require 'drb'
module ActiveSupport
module Cache
class DRbStore < MemoryStore #:nodoc:
attr_reader :address
def initialize(address = 'druby://localhost:9192')
require 'drb' unless defined?(DRbObject)
super()
@address = address
@data = DRbObject.new(nil, address)
end
end
end
end
end

View file

@ -13,6 +13,7 @@ module ActiveSupport
# server goes down, then MemCacheStore will ignore it until it goes back
# online.
# - Time-based expiry support. See #write and the +:expires_in+ option.
# - Per-request in memory cache for all communication with the MemCache server(s).
class MemCacheStore < Store
module Response # :nodoc:
STORED = "STORED\r\n"
@ -22,7 +23,12 @@ module ActiveSupport
DELETED = "DELETED\r\n"
end
attr_reader :addresses
def self.build_mem_cache(*addresses)
addresses = addresses.flatten
options = addresses.extract_options!
addresses = ["localhost"] if addresses.empty?
MemCache.new(addresses, options)
end
# Creates a new MemCacheStore object, with the given memcached server
# addresses. Each address is either a host name, or a host-with-port string
@ -32,12 +38,24 @@ module ActiveSupport
#
# If no addresses are specified, then MemCacheStore will connect to
# localhost port 11211 (the default memcached port).
#
# Instead of addresses one can pass in a MemCache-like object. For example:
#
# require 'memcached' # gem install memcached; uses C bindings to libmemcached
# ActiveSupport::Cache::MemCacheStore.new(Memcached::Rails.new("localhost:11211"))
def initialize(*addresses)
addresses = addresses.flatten
options = addresses.extract_options!
addresses = ["localhost"] if addresses.empty?
@addresses = addresses
@data = MemCache.new(addresses, options)
if addresses.first.respond_to?(:get)
@data = addresses.first
else
@data = self.class.build_mem_cache(*addresses)
end
extend Strategy::LocalCache
end
# Reads multiple keys from the cache.
def read_multi(*keys)
@data.get_multi keys
end
def read(key, options = nil) # :nodoc:
@ -80,6 +98,7 @@ module ActiveSupport
def exist?(key, options = nil) # :nodoc:
# Doesn't call super, cause exist? in memcache is in fact a read
# But who cares? Reading is very fast anyway
# Local cache is checked first, if it doesn't know then memcache itself is read from
!read(key, options).nil?
end
@ -94,7 +113,6 @@ module ActiveSupport
def decrement(key, amount = 1) # :nodoc:
log("decrement", key, amount)
response = @data.decr(key, amount)
response == Response::NOT_FOUND ? nil : response
rescue MemCache::MemCacheError
@ -102,6 +120,8 @@ module ActiveSupport
end
def delete_matched(matcher, options = nil) # :nodoc:
# don't do any local caching at present, just pass
# through and let the error happen
super
raise "Not supported by Memcache"
end
@ -115,10 +135,6 @@ module ActiveSupport
end
private
def expires_in(options)
(options && options[:expires_in]) || 0
end
def raw?(options)
options && options[:raw]
end

View file

@ -0,0 +1,104 @@
module ActiveSupport
module Cache
module Strategy
module LocalCache
# this allows caching of the fact that there is nothing in the remote cache
NULL = 'remote_cache_store:null'
def with_local_cache
Thread.current[thread_local_key] = MemoryStore.new
yield
ensure
Thread.current[thread_local_key] = nil
end
def middleware
@middleware ||= begin
klass = Class.new
klass.class_eval(<<-EOS, __FILE__, __LINE__)
def initialize(app)
@app = app
end
def call(env)
Thread.current[:#{thread_local_key}] = MemoryStore.new
@app.call(env)
ensure
Thread.current[:#{thread_local_key}] = nil
end
EOS
klass
end
end
def read(key, options = nil)
value = local_cache && local_cache.read(key)
if value == NULL
nil
elsif value.nil?
value = super
local_cache.mute { local_cache.write(key, value || NULL) } if local_cache
value.duplicable? ? value.dup : value
else
# forcing the value to be immutable
value.duplicable? ? value.dup : value
end
end
def write(key, value, options = nil)
value = value.to_s if respond_to?(:raw?) && raw?(options)
local_cache.mute { local_cache.write(key, value || NULL) } if local_cache
super
end
def delete(key, options = nil)
local_cache.mute { local_cache.write(key, NULL) } if local_cache
super
end
def exist(key, options = nil)
value = local_cache.read(key) if local_cache
if value == NULL
false
elsif value
true
else
super
end
end
def increment(key, amount = 1)
if value = super
local_cache.mute { local_cache.write(key, value.to_s) } if local_cache
value
else
nil
end
end
def decrement(key, amount = 1)
if value = super
local_cache.mute { local_cache.write(key, value.to_s) } if local_cache
value
else
nil
end
end
def clear
local_cache.clear if local_cache
super
end
private
def thread_local_key
@thread_local_key ||= "#{self.class.name.underscore}_local_cache".gsub("/", "_").to_sym
end
def local_cache
Thread.current[thread_local_key]
end
end
end
end
end

View file

@ -192,13 +192,8 @@ module ActiveSupport
end
def should_run_callback?(*args)
if options[:if]
evaluate_method(options[:if], *args)
elsif options[:unless]
!evaluate_method(options[:unless], *args)
else
true
end
[options[:if]].flatten.compact.all? { |a| evaluate_method(a, *args) } &&
![options[:unless]].flatten.compact.any? { |a| evaluate_method(a, *args) }
end
end
@ -210,20 +205,24 @@ module ActiveSupport
def define_callbacks(*callbacks)
callbacks.each do |callback|
class_eval <<-"end_eval"
def self.#{callback}(*methods, &block)
callbacks = CallbackChain.build(:#{callback}, *methods, &block)
(@#{callback}_callbacks ||= CallbackChain.new).concat callbacks
end
def self.#{callback}_callback_chain
@#{callback}_callbacks ||= CallbackChain.new
if superclass.respond_to?(:#{callback}_callback_chain)
CallbackChain.new(superclass.#{callback}_callback_chain + @#{callback}_callbacks)
else
@#{callback}_callbacks
end
end
def self.#{callback}(*methods, &block) # def self.before_save(*methods, &block)
callbacks = CallbackChain.build(:#{callback}, *methods, &block) # callbacks = CallbackChain.build(:before_save, *methods, &block)
@#{callback}_callbacks ||= CallbackChain.new # @before_save_callbacks ||= CallbackChain.new
@#{callback}_callbacks.concat callbacks # @before_save_callbacks.concat callbacks
end # end
#
def self.#{callback}_callback_chain # def self.before_save_callback_chain
@#{callback}_callbacks ||= CallbackChain.new # @before_save_callbacks ||= CallbackChain.new
#
if superclass.respond_to?(:#{callback}_callback_chain) # if superclass.respond_to?(:before_save_callback_chain)
CallbackChain.new( # CallbackChain.new(
superclass.#{callback}_callback_chain + # superclass.before_save_callback_chain +
@#{callback}_callbacks # @before_save_callbacks
) # )
else # else
@#{callback}_callbacks # @before_save_callbacks
end # end
end # end
end_eval
end
end

View file

@ -1,4 +1,8 @@
Dir[File.dirname(__FILE__) + "/core_ext/*.rb"].sort.each do |path|
filename = File.basename(path)
require "active_support/core_ext/#{filename}"
filenames = Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].sort.map do |path|
File.basename(path, '.rb')
end
# deprecated
filenames -= %w(blank)
filenames.each { |filename| require "active_support/core_ext/#{filename}" }

View file

@ -3,6 +3,7 @@ require 'active_support/core_ext/array/conversions'
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/array/grouping'
require 'active_support/core_ext/array/random_access'
require 'active_support/core_ext/array/wrapper'
class Array #:nodoc:
include ActiveSupport::CoreExtensions::Array::Access
@ -10,4 +11,5 @@ class Array #:nodoc:
include ActiveSupport::CoreExtensions::Array::ExtractOptions
include ActiveSupport::CoreExtensions::Array::Grouping
include ActiveSupport::CoreExtensions::Array::RandomAccess
extend ActiveSupport::CoreExtensions::Array::Wrapper
end

View file

@ -1,29 +1,41 @@
require 'builder'
module ActiveSupport #:nodoc:
module CoreExtensions #:nodoc:
module Array #:nodoc:
module Conversions
# Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options:
# * <tt>:connector</tt> - The word used to join the last element in arrays with two or more elements (default: "and")
# * <tt>:skip_last_comma</tt> - Set to true to return "a, b and c" instead of "a, b, and c".
def to_sentence(options = {})
options.assert_valid_keys(:connector, :skip_last_comma, :locale)
default = I18n.translate(:'support.array.sentence_connector', :locale => options[:locale])
default_skip_last_comma = I18n.translate(:'support.array.skip_last_comma', :locale => options[:locale])
options.reverse_merge! :connector => default, :skip_last_comma => default_skip_last_comma
options[:connector] = "#{options[:connector]} " unless options[:connector].nil? || options[:connector].strip == ''
# * <tt>:words_connector</tt> - The sign or word used to join the elements in arrays with two or more elements (default: ", ")
# * <tt>:two_words_connector</tt> - The sign or word used to join the elements in arrays with two elements (default: " and ")
# * <tt>:last_word_connector</tt> - The sign or word used to join the last element in arrays with three or more elements (default: ", and ")
def to_sentence(options = {})
default_words_connector = I18n.translate(:'support.array.words_connector', :locale => options[:locale])
default_two_words_connector = I18n.translate(:'support.array.two_words_connector', :locale => options[:locale])
default_last_word_connector = I18n.translate(:'support.array.last_word_connector', :locale => options[:locale])
# Try to emulate to_senteces previous to 2.3
if options.has_key?(:connector) || options.has_key?(:skip_last_comma)
::ActiveSupport::Deprecation.warn(":connector has been deprecated. Use :words_connector instead", caller) if options.has_key? :connector
::ActiveSupport::Deprecation.warn(":skip_last_comma has been deprecated. Use :last_word_connector instead", caller) if options.has_key? :skip_last_comma
skip_last_comma = options.delete :skip_last_comma
if connector = options.delete(:connector)
options[:last_word_connector] ||= skip_last_comma ? connector : ", #{connector}"
else
options[:last_word_connector] ||= skip_last_comma ? default_two_words_connector : default_last_word_connector
end
end
options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
options.reverse_merge! :words_connector => default_words_connector, :two_words_connector => default_two_words_connector, :last_word_connector => default_last_word_connector
case length
when 0
""
when 1
self[0].to_s
when 2
"#{self[0]} #{options[:connector]}#{self[1]}"
"#{self[0]}#{options[:two_words_connector]}#{self[1]}"
else
"#{self[0...-1].join(', ')}#{options[:skip_last_comma] ? '' : ','} #{options[:connector]}#{self[-1]}"
"#{self[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{self[-1]}"
end
end
@ -149,7 +161,9 @@ module ActiveSupport #:nodoc:
#
def to_xml(options = {})
raise "Not all elements respond to to_xml" unless all? { |e| e.respond_to? :to_xml }
require 'builder' unless defined?(Builder)
options = options.dup
options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? first.class.to_s.underscore.pluralize : "records"
options[:children] ||= options[:root].singularize
options[:indent] ||= 2

View file

@ -0,0 +1,24 @@
module ActiveSupport #:nodoc:
module CoreExtensions #:nodoc:
module Array #:nodoc:
module Wrapper
# Wraps the object in an Array unless it's an Array. Converts the
# object to an Array using #to_ary if it implements that.
def wrap(object)
case object
when nil
[]
when self
object
else
if object.respond_to?(:to_ary)
object.to_ary
else
[object]
end
end
end
end
end
end
end

View file

@ -1,12 +1,19 @@
require 'benchmark'
class << Benchmark
remove_method :realtime
# Earlier Ruby had a slower implementation.
if RUBY_VERSION < '1.8.7'
remove_method :realtime
def realtime
r0 = Time.now
yield
r1 = Time.now
r1.to_f - r0.to_f
def realtime
r0 = Time.now
yield
r1 = Time.now
r1.to_f - r0.to_f
end
end
def ms
1000 * realtime { yield }
end
end

View file

@ -1,58 +1,2 @@
class Object
# An object is blank if it's false, empty, or a whitespace string.
# For example, "", " ", +nil+, [], and {} are blank.
#
# This simplifies
#
# if !address.nil? && !address.empty?
#
# to
#
# if !address.blank?
def blank?
respond_to?(:empty?) ? empty? : !self
end
# An object is present if it's not blank.
def present?
!blank?
end
end
class NilClass #:nodoc:
def blank?
true
end
end
class FalseClass #:nodoc:
def blank?
true
end
end
class TrueClass #:nodoc:
def blank?
false
end
end
class Array #:nodoc:
alias_method :blank?, :empty?
end
class Hash #:nodoc:
alias_method :blank?, :empty?
end
class String #:nodoc:
def blank?
self !~ /\S/
end
end
class Numeric #:nodoc:
def blank?
false
end
end
require 'active_support/core_ext/object/blank'
ActiveSupport::Deprecation.warn 'require "active_support/core_ext/blank" is deprecated and will be removed in Rails 3. Use require "active_support/core_ext/object/blank" instead.'

View file

@ -2,11 +2,20 @@ module ActiveSupport #:nodoc:
module CoreExtensions #:nodoc:
module CGI #:nodoc:
module EscapeSkippingSlashes #:nodoc:
def escape_skipping_slashes(str)
str = str.join('/') if str.respond_to? :join
str.gsub(/([^ \/a-zA-Z0-9_.-])/n) do
"%#{$1.unpack('H2').first.upcase}"
end.tr(' ', '+')
if RUBY_VERSION >= '1.9'
def escape_skipping_slashes(str)
str = str.join('/') if str.respond_to? :join
str.gsub(/([^ \/a-zA-Z0-9_.-])/n) do
"%#{$1.unpack('H2' * $1.bytesize).join('%').upcase}"
end.tr(' ', '+')
end
else
def escape_skipping_slashes(str)
str = str.join('/') if str.respond_to? :join
str.gsub(/([^ \/a-zA-Z0-9_.-])/n) do
"%#{$1.unpack('H2').first.upcase}"
end.tr(' ', '+')
end
end
end
end

View file

@ -11,17 +11,17 @@ class Class
syms.flatten.each do |sym|
next if sym.is_a?(Hash)
class_eval(<<-EOS, __FILE__, __LINE__)
unless defined? @@#{sym}
@@#{sym} = nil
end
def self.#{sym}
@@#{sym}
end
def #{sym}
@@#{sym}
end
unless defined? @@#{sym} # unless defined? @@hair_colors
@@#{sym} = nil # @@hair_colors = nil
end # end
#
def self.#{sym} # def self.hair_colors
@@#{sym} # @@hair_colors
end # end
#
def #{sym} # def hair_colors
@@#{sym} # @@hair_colors
end # end
EOS
end
end
@ -30,19 +30,19 @@ class Class
options = syms.extract_options!
syms.flatten.each do |sym|
class_eval(<<-EOS, __FILE__, __LINE__)
unless defined? @@#{sym}
@@#{sym} = nil
end
def self.#{sym}=(obj)
@@#{sym} = obj
end
#{"
def #{sym}=(obj)
@@#{sym} = obj
end
" unless options[:instance_writer] == false }
unless defined? @@#{sym} # unless defined? @@hair_colors
@@#{sym} = nil # @@hair_colors = nil
end # end
#
def self.#{sym}=(obj) # def self.hair_colors=(obj)
@@#{sym} = obj # @@hair_colors = obj
end # end
#
#{" #
def #{sym}=(obj) # def hair_colors=(obj)
@@#{sym} = obj # @@hair_colors = obj
end # end
" unless options[:instance_writer] == false } # # instance writer above is generated unless options[:instance_writer] == false
EOS
end
end

View file

@ -9,22 +9,23 @@ class Class
class_name_to_stop_searching_on = self.superclass.name.blank? ? "Object" : self.superclass.name
names.each do |name|
class_eval <<-EOS
def self.#{name}
if defined?(@#{name})
@#{name}
elsif superclass < #{class_name_to_stop_searching_on} && superclass.respond_to?(:#{name})
superclass.#{name}
end
end
def #{name}
self.class.#{name}
end
def self.#{name}?
!!#{name}
end
def #{name}?
!!#{name}
end
def self.#{name} # def self.only_reader
if defined?(@#{name}) # if defined?(@only_reader)
@#{name} # @only_reader
elsif superclass < #{class_name_to_stop_searching_on} && # elsif superclass < Object &&
superclass.respond_to?(:#{name}) # superclass.respond_to?(:only_reader)
superclass.#{name} # superclass.only_reader
end # end
end # end
def #{name} # def only_reader
self.class.#{name} # self.class.only_reader
end # end
def self.#{name}? # def self.only_reader?
!!#{name} # !!only_reader
end # end
def #{name}? # def only_reader?
!!#{name} # !!only_reader
end # end
EOS
end
end
@ -32,9 +33,9 @@ class Class
def superclass_delegating_writer(*names)
names.each do |name|
class_eval <<-EOS
def self.#{name}=(value)
@#{name} = value
end
def self.#{name}=(value) # def self.only_writer=(value)
@#{name} = value # @only_writer = value
end # end
EOS
end
end

View file

@ -11,13 +11,13 @@ class Class # :nodoc:
syms.each do |sym|
next if sym.is_a?(Hash)
class_eval <<-EOS
def self.#{sym}
read_inheritable_attribute(:#{sym})
end
def #{sym}
self.class.#{sym}
end
def self.#{sym} # def self.before_add_for_comments
read_inheritable_attribute(:#{sym}) # read_inheritable_attribute(:before_add_for_comments)
end # end
#
def #{sym} # def before_add_for_comments
self.class.#{sym} # self.class.before_add_for_comments
end # end
EOS
end
end
@ -26,15 +26,15 @@ class Class # :nodoc:
options = syms.extract_options!
syms.each do |sym|
class_eval <<-EOS
def self.#{sym}=(obj)
write_inheritable_attribute(:#{sym}, obj)
end
#{"
def #{sym}=(obj)
self.class.#{sym} = obj
end
" unless options[:instance_writer] == false }
def self.#{sym}=(obj) # def self.color=(obj)
write_inheritable_attribute(:#{sym}, obj) # write_inheritable_attribute(:color, obj)
end # end
#
#{" #
def #{sym}=(obj) # def color=(obj)
self.class.#{sym} = obj # self.class.color = obj
end # end
" unless options[:instance_writer] == false } # # the writer above is generated unless options[:instance_writer] == false
EOS
end
end
@ -43,15 +43,15 @@ class Class # :nodoc:
options = syms.extract_options!
syms.each do |sym|
class_eval <<-EOS
def self.#{sym}=(obj)
write_inheritable_array(:#{sym}, obj)
end
#{"
def #{sym}=(obj)
self.class.#{sym} = obj
end
" unless options[:instance_writer] == false }
def self.#{sym}=(obj) # def self.levels=(obj)
write_inheritable_array(:#{sym}, obj) # write_inheritable_array(:levels, obj)
end # end
#
#{" #
def #{sym}=(obj) # def levels=(obj)
self.class.#{sym} = obj # self.class.levels = obj
end # end
" unless options[:instance_writer] == false } # # the writer above is generated unless options[:instance_writer] == false
EOS
end
end
@ -60,15 +60,15 @@ class Class # :nodoc:
options = syms.extract_options!
syms.each do |sym|
class_eval <<-EOS
def self.#{sym}=(obj)
write_inheritable_hash(:#{sym}, obj)
end
#{"
def #{sym}=(obj)
self.class.#{sym} = obj
end
" unless options[:instance_writer] == false }
def self.#{sym}=(obj) # def self.nicknames=(obj)
write_inheritable_hash(:#{sym}, obj) # write_inheritable_hash(:nicknames, obj)
end # end
#
#{" #
def #{sym}=(obj) # def nicknames=(obj)
self.class.#{sym} = obj # self.class.nicknames = obj
end # end
" unless options[:instance_writer] == false } # # the writer above is generated unless options[:instance_writer] == false
EOS
end
end

View file

@ -1,7 +1,7 @@
module ActiveSupport #:nodoc:
module CoreExtensions #:nodoc:
module Date #:nodoc:
# Enables the use of time calculations within Time itself
# Enables the use of time calculations within Date itself
module Calculations
def self.included(base) #:nodoc:
base.extend ClassMethods
@ -92,6 +92,7 @@ module ActiveSupport #:nodoc:
# Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with
# any of these keys: <tt>:years</tt>, <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>.
def advance(options)
options = options.dup
d = self
d = d >> options.delete(:years) * 12 if options[:years]
d = d >> options.delete(:months) if options[:months]

View file

@ -31,7 +31,7 @@ module ActiveSupport #:nodoc:
#
# This method is aliased to <tt>to_s</tt>.
#
# ==== Examples:
# ==== Examples
# date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
#
# date.to_formatted_s(:db) # => "2007-11-10"
@ -76,7 +76,7 @@ module ActiveSupport #:nodoc:
# Converts a Date instance to a Time, where the time is set to the beginning of the day.
# The timezone can be either :local or :utc (default :local).
#
# ==== Examples:
# ==== Examples
# date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
#
# date.to_time # => Sat Nov 10 00:00:00 0800 2007
@ -90,7 +90,7 @@ module ActiveSupport #:nodoc:
# Converts a Date instance to a DateTime, where the time is set to the beginning of the day
# and UTC offset is set to 0.
#
# ==== Example:
# ==== Examples
# date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
#
# date.to_datetime # => Sat, 10 Nov 2007 00:00:00 0000

View file

@ -25,7 +25,7 @@ module ActiveSupport #:nodoc:
#
# This method is aliased to <tt>to_s</tt>.
#
# === Examples:
# === Examples
# datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0) # => Tue, 04 Dec 2007 00:00:00 +0000
#
# datetime.to_formatted_s(:db) # => "2007-12-04 00:00:00"

View file

@ -55,12 +55,10 @@ module Enumerable
# [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
#
def sum(identity = 0, &block)
return identity unless size > 0
if block_given?
map(&block).sum
map(&block).sum(identity)
else
inject { |sum, element| sum + element }
inject { |sum, element| sum + element } || identity
end
end
@ -104,4 +102,13 @@ module Enumerable
size = block_given? ? select(&block).size : self.size
size > 1
end
# Returns true if none of the elements match the given block.
#
# success = responses.none? {|r| r.status / 100 == 5 }
#
# This is a builtin method in Ruby 1.8.7 and later.
def none?(&block)
!any?(&block)
end unless [].respond_to?(:none?)
end

View file

@ -6,14 +6,16 @@ module ActiveSupport
end
end
# TODO: Turn all this into using the BacktraceCleaner.
class Exception # :nodoc:
def clean_message
Pathname.clean_within message
end
TraceSubstitutions = []
FrameworkRegexp = /generated|vendor|dispatch|ruby|script\/\w+/
FrameworkStart = /action_controller\/dispatcher\.rb/.freeze
FrameworkRegexp = /generated|vendor|dispatch|ruby|script\/\w+/.freeze
def clean_backtrace
backtrace.collect do |line|
Pathname.clean_within(TraceSubstitutions.inject(line) do |result, (regexp, sub)|
@ -21,20 +23,22 @@ class Exception # :nodoc:
end)
end
end
def application_backtrace
before_framework_frame = nil
before_application_frame = true
trace = clean_backtrace.reject do |line|
before_framework_frame ||= (line =~ FrameworkStart)
non_app_frame = (line =~ FrameworkRegexp)
before_application_frame = false unless non_app_frame
non_app_frame && ! before_application_frame
before_framework_frame || (non_app_frame && !before_application_frame)
end
# If we didn't find any application frames, return an empty app trace.
before_application_frame ? [] : trace
end
def framework_backtrace
clean_backtrace.grep FrameworkRegexp
end

View file

@ -1,5 +1,3 @@
require 'tempfile'
module ActiveSupport #:nodoc:
module CoreExtensions #:nodoc:
module File #:nodoc:
@ -18,6 +16,8 @@ module ActiveSupport #:nodoc:
# file.write("hello")
# end
def atomic_write(file_name, temp_dir = Dir.tmpdir)
require 'tempfile' unless defined?(Tempfile)
temp_file = Tempfile.new(basename(file_name), temp_dir)
yield temp_file
temp_file.close
@ -27,7 +27,7 @@ module ActiveSupport #:nodoc:
old_stat = stat(file_name)
rescue Errno::ENOENT
# No old permissions, write a temp file to determine the defaults
check_name = ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}"
check_name = join(dirname(file_name), ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}")
open(check_name, "w") { }
old_stat = stat(check_name)
unlink(check_name)

View file

@ -1,47 +1,30 @@
require 'date'
require 'cgi'
require 'builder'
require 'xmlsimple'
# Locked down XmlSimple#xml_in_string
class XmlSimple
# Same as xml_in but doesn't try to smartly shoot itself in the foot.
def xml_in_string(string, options = nil)
handle_options('in', options)
@doc = parse(string)
result = collapse(@doc.root)
if @options['keeproot']
merge({}, @doc.root.name, result)
else
result
end
end
def self.xml_in_string(string, options = nil)
new.xml_in_string(string, options)
end
end
# This module exists to decorate files deserialized using Hash.from_xml with
# the <tt>original_filename</tt> and <tt>content_type</tt> methods.
module FileLike #:nodoc:
attr_writer :original_filename, :content_type
def original_filename
@original_filename || 'untitled'
end
def content_type
@content_type || 'application/octet-stream'
end
end
require 'active_support/core_ext/module/attribute_accessors'
module ActiveSupport #:nodoc:
# these accessors are here because people using ActiveResource and REST to integrate with other systems
# have to be able to control the default behavior of rename_key. dasherize_xml is set to true to emulate
# existing behavior. In a future version it should be set to false by default.
mattr_accessor :dasherize_xml
mattr_accessor :camelize_xml
self.dasherize_xml = true
self.camelize_xml = false
module CoreExtensions #:nodoc:
module Hash #:nodoc:
module Conversions
# This module exists to decorate files deserialized using Hash.from_xml with
# the <tt>original_filename</tt> and <tt>content_type</tt> methods.
module FileLike #:nodoc:
attr_writer :original_filename, :content_type
def original_filename
@original_filename || 'untitled'
end
def content_type
@content_type || 'application/octet-stream'
end
end
XML_TYPE_NAMES = {
"Symbol" => "symbol",
@ -49,11 +32,12 @@ module ActiveSupport #:nodoc:
"Bignum" => "integer",
"BigDecimal" => "decimal",
"Float" => "float",
"TrueClass" => "boolean",
"FalseClass" => "boolean",
"Date" => "date",
"DateTime" => "datetime",
"Time" => "datetime",
"TrueClass" => "boolean",
"FalseClass" => "boolean"
"ActiveSupport::TimeWithZone" => "datetime"
} unless defined?(XML_TYPE_NAMES)
XML_FORMATTING = {
@ -100,7 +84,7 @@ module ActiveSupport #:nodoc:
# Converts a hash into a string suitable for use as a URL query string. An optional <tt>namespace</tt> can be
# passed to enclose the param names (see example below).
#
# ==== Example:
# ==== Examples
# { :name => 'David', :nationality => 'Danish' }.to_query # => "name=David&nationality=Danish"
#
# { :name => 'David', :nationality => 'Danish' }.to_query('user') # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
@ -113,12 +97,14 @@ module ActiveSupport #:nodoc:
alias_method :to_param, :to_query
def to_xml(options = {})
require 'builder' unless defined?(Builder)
options = options.dup
options[:indent] ||= 2
options.reverse_merge!({ :builder => Builder::XmlMarkup.new(:indent => options[:indent]),
:root => "hash" })
options[:builder].instruct! unless options.delete(:skip_instruct)
dasherize = !options.has_key?(:dasherize) || options[:dasherize]
root = dasherize ? options[:root].to_s.dasherize : options[:root].to_s
root = rename_key(options[:root].to_s, options)
options[:builder].__send__(:method_missing, root) do
each do |key, value|
@ -145,7 +131,7 @@ module ActiveSupport #:nodoc:
else
type_name = XML_TYPE_NAMES[value.class.name]
key = dasherize ? key.to_s.dasherize : key.to_s
key = rename_key(key.to_s, options)
attributes = options[:skip_types] || value.nil? || type_name.nil? ? { } : { :type => type_name }
if value.nil?
@ -165,15 +151,17 @@ module ActiveSupport #:nodoc:
end
def rename_key(key, options = {})
camelize = options.has_key?(:camelize) ? options[:camelize] : ActiveSupport.camelize_xml
dasherize = options.has_key?(:dasherize) ? options[:dasherize] : ActiveSupport.dasherize_xml
key = key.camelize if camelize
key = key.dasherize if dasherize
key
end
module ClassMethods
def from_xml(xml)
# TODO: Refactor this into something much cleaner that doesn't rely on XmlSimple
typecast_xml_value(undasherize_keys(XmlSimple.xml_in_string(xml,
'forcearray' => false,
'forcecontent' => true,
'keeproot' => true,
'contentkey' => '__content__')
))
typecast_xml_value(unrename_keys(XmlMini.parse(xml)))
end
private
@ -239,15 +227,15 @@ module ActiveSupport #:nodoc:
end
end
def undasherize_keys(params)
def unrename_keys(params)
case params.class.to_s
when "Hash"
params.inject({}) do |h,(k,v)|
h[k.to_s.tr("-", "_")] = undasherize_keys(v)
h[k.to_s.tr("-", "_")] = unrename_keys(v)
h
end
when "Array"
params.map { |v| undasherize_keys(v) }
params.map { |v| unrename_keys(v) }
else
params
end

View file

@ -91,6 +91,12 @@ class HashWithIndifferentAccess < Hash
self.dup.update(hash)
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.
def reverse_merge(other_hash)
super other_hash.with_indifferent_access
end
# Removes a specified key from the hash.
def delete(key)
super(convert_key(key))

View file

@ -38,7 +38,7 @@ module ActiveSupport #:nodoc:
# Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols
# as keys, this will fail.
#
# ==== Examples:
# ==== Examples
# { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
# { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age"
# { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing

View file

@ -24,10 +24,17 @@ module ActiveSupport #:nodoc:
end
# Replaces the hash with only the given keys.
# Returns a hash contained the removed key/value pairs
# {:a => 1, :b => 2, :c => 3, :d => 4}.slice!(:a, :b) # => {:c => 3, :d =>4}
def slice!(*keys)
replace(slice(*keys))
keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
omit = slice(*self.keys - keys)
hash = slice(*keys)
replace(hash)
omit
end
end
end
end
end

View file

@ -2,12 +2,15 @@ module Kernel
unless respond_to?(:debugger)
# Starts a debugging session if ruby-debug has been loaded (call script/server --debugger to do load it).
def debugger
Rails.logger.info "\n***** Debugger requested, but was not available: Start server with --debugger to enable *****\n"
message = "\n***** Debugger requested, but was not available: Start server with --debugger to enable *****\n"
defined?(Rails) ? Rails.logger.info(message) : $stderr.puts(message)
end
end
undef :breakpoint if respond_to?(:breakpoint)
def breakpoint
Rails.logger.info "\n***** The 'breakpoint' command has been renamed 'debugger' -- please change *****\n"
message = "\n***** The 'breakpoint' command has been renamed 'debugger' -- please change *****\n"
defined?(Rails) ? Rails.logger.info(message) : $stderr.puts(message)
debugger
end
end

View file

@ -3,12 +3,12 @@
class Logger
def self.define_around_helper(level)
module_eval <<-end_eval
def around_#{level}(before_message, after_message, &block)
self.#{level}(before_message)
return_value = block.call(self)
self.#{level}(after_message)
return return_value
end
def around_#{level}(before_message, after_message, &block) # def around_debug(before_message, after_message, &block)
self.#{level}(before_message) # self.debug(before_message)
return_value = block.call(self) # return_value = block.call(self)
self.#{level}(after_message) # self.debug(after_message)
return return_value # return return_value
end # end
end_eval
end
[:debug, :info, :error, :fatal].each {|level| define_around_helper(level) }
@ -30,6 +30,8 @@ require 'logger'
#
# Note: This logger is deprecated in favor of ActiveSupport::BufferedLogger
class Logger
##
# :singleton-method:
# Set to false to disable the silencer
cattr_accessor :silencer
self.silencer = true

View file

@ -64,9 +64,9 @@ module ActiveSupport
# e.title # => "Megastars"
def alias_attribute(new_name, old_name)
module_eval <<-STR, __FILE__, __LINE__+1
def #{new_name}; self.#{old_name}; end
def #{new_name}?; self.#{old_name}?; end
def #{new_name}=(v); self.#{old_name} = v; end
def #{new_name}; self.#{old_name}; end # def subject; self.title; end
def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end
def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end
STR
end
end

View file

@ -22,10 +22,10 @@ class Module
raise 'Default value or block required' unless !default.nil? || block
define_method(sym, block_given? ? block : Proc.new { default })
module_eval(<<-EVAL, __FILE__, __LINE__)
def #{sym}=(value)
class << self; attr_reader :#{sym} end
@#{sym} = value
end
def #{sym}=(value) # def age=(value)
class << self; attr_reader :#{sym} end # class << self; attr_reader :age end
@#{sym} = value # @age = value
end # end
EVAL
end
end

View file

@ -1,3 +1,5 @@
require "active_support/core_ext/array"
# Extends the module object with module and instance accessors for class attributes,
# just like the native attr* accessors for instance attributes.
#
@ -15,17 +17,17 @@ class Module
syms.each do |sym|
next if sym.is_a?(Hash)
class_eval(<<-EOS, __FILE__, __LINE__)
unless defined? @@#{sym}
@@#{sym} = nil
end
def self.#{sym}
@@#{sym}
end
def #{sym}
@@#{sym}
end
unless defined? @@#{sym} # unless defined? @@pagination_options
@@#{sym} = nil # @@pagination_options = nil
end # end
#
def self.#{sym} # def self.pagination_options
@@#{sym} # @@pagination_options
end # end
#
def #{sym} # def pagination_options
@@#{sym} # @@pagination_options
end # end
EOS
end
end
@ -34,19 +36,19 @@ class Module
options = syms.extract_options!
syms.each do |sym|
class_eval(<<-EOS, __FILE__, __LINE__)
unless defined? @@#{sym}
@@#{sym} = nil
end
def self.#{sym}=(obj)
@@#{sym} = obj
end
#{"
def #{sym}=(obj)
@@#{sym} = obj
end
" unless options[:instance_writer] == false }
unless defined? @@#{sym} # unless defined? @@pagination_options
@@#{sym} = nil # @@pagination_options = nil
end # end
#
def self.#{sym}=(obj) # def self.pagination_options=(obj)
@@#{sym} = obj # @@pagination_options = obj
end # end
#
#{" #
def #{sym}=(obj) # def pagination_options=(obj)
@@#{sym} = obj # @@pagination_options = obj
end # end
" unless options[:instance_writer] == false } # # instance writer above is generated unless options[:instance_writer] == false
EOS
end
end

View file

@ -72,6 +72,30 @@ class Module
# invoice.customer_name # => "John Doe"
# invoice.customer_address # => "Vimmersvej 13"
#
# If the object to which you delegate can be nil, you may want to use the
# :allow_nil option. In that case, it returns nil instead of raising a
# NoMethodError exception:
#
# class Foo
# attr_accessor :bar
# def initialize(bar = nil)
# @bar = bar
# end
# delegate :zoo, :to => :bar
# end
#
# Foo.new.zoo # raises NoMethodError exception (you called nil.zoo)
#
# class Foo
# attr_accessor :bar
# def initialize(bar = nil)
# @bar = bar
# end
# delegate :zoo, :to => :bar, :allow_nil => true
# end
#
# Foo.new.zoo # returns nil
#
def delegate(*methods)
options = methods.pop
unless options.is_a?(Hash) && to = options[:to]
@ -84,11 +108,27 @@ class Module
prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_"
file, line = caller.first.split(':', 2)
line = line.to_i
methods.each do |method|
module_eval(<<-EOS, "(__DELEGATION__)", 1)
def #{prefix}#{method}(*args, &block)
#{to}.__send__(#{method.inspect}, *args, &block)
on_nil =
if options[:allow_nil]
'return'
else
%(raise "#{prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
end
module_eval(<<-EOS, file, line)
def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block)
#{to}.__send__(#{method.inspect}, *args, &block) # client.__send__(:name, *args, &block)
rescue NoMethodError # rescue NoMethodError
if #{to}.nil? # if client.nil?
#{on_nil}
else # else
raise # raise
end # end
end # end
EOS
end
end

View file

@ -1,13 +1,15 @@
module ActiveSupport
class ModelName < String
attr_reader :singular, :plural, :cache_key, :partial_path
attr_reader :singular, :plural, :element, :collection, :partial_path
alias_method :cache_key, :collection
def initialize(name)
super
@singular = underscore.tr('/', '_').freeze
@plural = @singular.pluralize.freeze
@cache_key = tableize.freeze
@partial_path = "#{@cache_key}/#{demodulize.underscore}".freeze
@singular = ActiveSupport::Inflector.underscore(self).tr('/', '_').freeze
@plural = ActiveSupport::Inflector.pluralize(@singular).freeze
@element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).freeze
@collection = ActiveSupport::Inflector.tableize(self).freeze
@partial_path = "#{@collection}/#{@element}".freeze
end
end
@ -16,7 +18,7 @@ module ActiveSupport
# Returns an ActiveSupport::ModelName object for module. It can be
# used to retrieve all kinds of naming-related information.
def model_name
@model_name ||= ModelName.new(name)
@model_name ||= ::ActiveSupport::ModelName.new(name)
end
end
end

View file

@ -26,11 +26,11 @@ class Module
end
module_eval(<<-EOS, __FILE__, __LINE__)
def #{aliased_method}_with_synchronization#{punctuation}(*args, &block)
#{with}.synchronize do
#{aliased_method}_without_synchronization#{punctuation}(*args, &block)
end
end
def #{aliased_method}_with_synchronization#{punctuation}(*args, &block) # def expire_with_synchronization(*args, &block)
#{with}.synchronize do # @@lock.synchronize do
#{aliased_method}_without_synchronization#{punctuation}(*args, &block) # expire_without_synchronization(*args, &block)
end # end
end # end
EOS
alias_method_chain method, :synchronization

View file

@ -2,7 +2,9 @@
class NameError #:nodoc:
# Add a method to obtain the missing name from a NameError.
def missing_name
$1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ message
if /undefined local variable or method/ !~ message
$1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ message
end
end
# Was this exception raised because the given name was missing?

View file

@ -3,41 +3,47 @@ module ActiveSupport #:nodoc:
module Numeric #:nodoc:
# Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes
module Bytes
KILOBYTE = 1024
MEGABYTE = KILOBYTE * 1024
GIGABYTE = MEGABYTE * 1024
TERABYTE = GIGABYTE * 1024
PETABYTE = TERABYTE * 1024
EXABYTE = PETABYTE * 1024
def bytes
self
end
alias :byte :bytes
def kilobytes
self * 1024
self * KILOBYTE
end
alias :kilobyte :kilobytes
def megabytes
self * 1024.kilobytes
self * MEGABYTE
end
alias :megabyte :megabytes
def gigabytes
self * 1024.megabytes
self * GIGABYTE
end
alias :gigabyte :gigabytes
def terabytes
self * 1024.gigabytes
self * TERABYTE
end
alias :terabyte :terabytes
def petabytes
self * 1024.terabytes
self * PETABYTE
end
alias :petabyte :petabytes
def exabytes
self * 1024.petabytes
self * EXABYTE
end
alias :exabyte :exabytes
end
end
end

View file

@ -1,3 +1,4 @@
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext/object/extending'
require 'active_support/core_ext/object/instance_variables'

View file

@ -0,0 +1,58 @@
class Object
# An object is blank if it's false, empty, or a whitespace string.
# For example, "", " ", +nil+, [], and {} are blank.
#
# This simplifies
#
# if !address.nil? && !address.empty?
#
# to
#
# if !address.blank?
def blank?
respond_to?(:empty?) ? empty? : !self
end
# An object is present if it's not blank.
def present?
!blank?
end
end
class NilClass #:nodoc:
def blank?
true
end
end
class FalseClass #:nodoc:
def blank?
true
end
end
class TrueClass #:nodoc:
def blank?
false
end
end
class Array #:nodoc:
alias_method :blank?, :empty?
end
class Hash #:nodoc:
alias_method :blank?, :empty?
end
class String #:nodoc:
def blank?
self !~ /\S/
end
end
class Numeric #:nodoc:
def blank?
false
end
end

View file

@ -9,6 +9,7 @@ class Object
#
# Note: This method is defined as a default implementation for all Objects for Hash#to_query to work.
def to_query(key)
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
"#{CGI.escape(key.to_s)}=#{CGI.escape(to_param.to_s)}"
end
end
end

View file

@ -40,6 +40,21 @@ class Object
value
end
# Yields <code>x</code> to the block, and then returns <code>x</code>.
# The primary purpose of this method is to "tap into" a method chain,
# in order to perform operations on intermediate results within the chain.
#
# (1..10).tap { |x| puts "original: #{x.inspect}" }.to_a.
# tap { |x| puts "array: #{x.inspect}" }.
# select { |x| x%2 == 0 }.
# tap { |x| puts "evens: #{x.inspect}" }.
# map { |x| x*x }.
# tap { |x| puts "squares: #{x.inspect}" }
def tap
yield self
self
end unless Object.respond_to?(:tap)
# An elegant way to factor duplication out of options passed to a series of
# method calls. Each method called in the block, with the block variable as
# the receiver, will have its options merged with the default +options+ hash
@ -71,4 +86,5 @@ class Object
def acts_like?(duck)
respond_to? "acts_like_#{duck}?"
end
end

View file

@ -15,7 +15,7 @@ module ActiveSupport #:nodoc:
end
# Gives a human readable format of the range.
#
# ==== Example:
# ==== Example
#
# [1..100].to_formatted_s # => "1..100"
def to_formatted_s(format = :default)

View file

@ -1,34 +1,39 @@
require 'rexml/document'
require 'rexml/entity'
# Fixes the rexml vulnerability disclosed at:
# http://www.ruby-lang.org/en/news/2008/08/23/dos-vulnerability-in-rexml/
# This fix is identical to rexml-expansion-fix version 1.0.1
require 'rexml/rexml'
# Earlier versions of rexml defined REXML::Version, newer ones REXML::VERSION
unless REXML::Document.respond_to?(:entity_expansion_limit=)
module REXML
class Entity < Child
undef_method :unnormalized
def unnormalized
document.record_entity_expansion! if document
v = value()
return nil if v.nil?
@unnormalized = Text::unnormalize(v, parent)
@unnormalized
end
end
class Document < Element
@@entity_expansion_limit = 10_000
def self.entity_expansion_limit= val
@@entity_expansion_limit = val
end
unless (defined?(REXML::VERSION) ? REXML::VERSION : REXML::Version) > "3.1.7.2"
require 'rexml/document'
def record_entity_expansion!
@number_of_expansions ||= 0
@number_of_expansions += 1
if @number_of_expansions > @@entity_expansion_limit
raise "Number of entity expansions exceeded, processing aborted."
# REXML in 1.8.7 has the patch but didn't update Version from 3.1.7.2.
unless REXML::Document.respond_to?(:entity_expansion_limit=)
require 'rexml/entity'
module REXML
class Entity < Child
undef_method :unnormalized
def unnormalized
document.record_entity_expansion! if document
v = value()
return nil if v.nil?
@unnormalized = Text::unnormalize(v, parent)
@unnormalized
end
end
class Document < Element
@@entity_expansion_limit = 10_000
def self.entity_expansion_limit= val
@@entity_expansion_limit = val
end
def record_entity_expansion!
@number_of_expansions ||= 0
@number_of_expansions += 1
if @number_of_expansions > @@entity_expansion_limit
raise "Number of entity expansions exceeded, processing aborted."
end
end
end
end

View file

@ -1,6 +1,7 @@
# encoding: utf-8
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/string/bytesize'
require 'active_support/core_ext/string/conversions'
require 'active_support/core_ext/string/access'
require 'active_support/core_ext/string/starts_ends_with'
@ -9,6 +10,7 @@ require 'active_support/core_ext/string/multibyte'
require 'active_support/core_ext/string/xchar'
require 'active_support/core_ext/string/filters'
require 'active_support/core_ext/string/behavior'
require 'active_support/core_ext/string/output_safety'
class String #:nodoc:
include ActiveSupport::CoreExtensions::String::Access
@ -19,4 +21,5 @@ class String #:nodoc:
include ActiveSupport::CoreExtensions::String::Iterators
include ActiveSupport::CoreExtensions::String::Behavior
include ActiveSupport::CoreExtensions::String::Multibyte
include ActiveSupport::CoreExtensions::String::OutputSafety
end

View file

@ -41,9 +41,15 @@ module ActiveSupport #:nodoc:
# "hello".first(2) # => "he"
# "hello".first(10) # => "hello"
def first(limit = 1)
mb_chars[0..(limit - 1)].to_s
if limit == 0
''
elsif limit >= size
self
else
mb_chars[0...limit].to_s
end
end
# Returns the last character of the string or the last +limit+ characters.
#
# Examples:
@ -51,7 +57,13 @@ module ActiveSupport #:nodoc:
# "hello".last(2) # => "lo"
# "hello".last(10) # => "hello"
def last(limit = 1)
(mb_chars[(-limit)..-1] || self).to_s
if limit == 0
''
elsif limit >= size
self
else
mb_chars[(-limit)..-1].to_s
end
end
end
else
@ -69,11 +81,23 @@ module ActiveSupport #:nodoc:
end
def first(limit = 1)
self[0..(limit - 1)]
if limit == 0
''
elsif limit >= size
self
else
to(limit - 1)
end
end
def last(limit = 1)
from(-limit) || self
if limit == 0
''
elsif limit >= size
self
else
from(-limit)
end
end
end
end

View file

@ -0,0 +1,5 @@
unless '1.9'.respond_to?(:bytesize)
class String
alias :bytesize :size
end
end

View file

@ -1,4 +1,4 @@
require 'active_support/inflector'
require 'active_support/inflector' unless defined?(ActiveSupport::Inflector)
module ActiveSupport #:nodoc:
module CoreExtensions #:nodoc:
@ -102,8 +102,8 @@ module ActiveSupport #:nodoc:
#
# <%= link_to(@person.name, person_path %>
# # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
def parameterize
Inflector.parameterize(self)
def parameterize(sep = '-')
Inflector.parameterize(self, sep)
end
# Creates the name of a table like Rails does for models to table names. This method

View file

@ -13,7 +13,9 @@ module ActiveSupport #:nodoc:
# When $KCODE = 'UTF8', multi-byte characters are yielded appropriately.
def each_char
scanner, char = StringScanner.new(self), /./mu
loop { yield(scanner.scan(char) || break) }
while c = scanner.scan(char)
yield c
end
end
end
end

View file

@ -0,0 +1,48 @@
module ActiveSupport #:nodoc:
module CoreExtensions #:nodoc:
module String #:nodoc:
module OutputSafety
def self.included(base)
base.class_eval do
alias_method :add_without_safety, :+
alias_method :+, :add_with_safety
alias_method_chain :concat, :safety
undef_method :<<
alias_method :<<, :concat_with_safety
end
end
def html_safe?
defined?(@_rails_html_safe) && @_rails_html_safe
end
def html_safe!
@_rails_html_safe = true
self
end
def add_with_safety(other)
result = add_without_safety(other)
if html_safe? && also_html_safe?(other)
result.html_safe!
else
result
end
end
def concat_with_safety(other_or_fixnum)
result = concat_without_safety(other_or_fixnum)
unless html_safe? && also_html_safe?(other_or_fixnum)
@_rails_html_safe = false
end
result
end
private
def also_html_safe?(other)
other.respond_to?(:html_safe?) && other.html_safe?
end
end
end
end
end

View file

@ -201,15 +201,16 @@ module ActiveSupport #:nodoc:
# Returns a new Time representing the start of the day (0:00)
def beginning_of_day
(self - self.seconds_since_midnight).change(:usec => 0)
#(self - seconds_since_midnight).change(:usec => 0)
change(:hour => 0, :min => 0, :sec => 0, :usec => 0)
end
alias :midnight :beginning_of_day
alias :at_midnight :beginning_of_day
alias :at_beginning_of_day :beginning_of_day
# Returns a new Time representing the end of the day (23:59:59)
# Returns a new Time representing the end of the day, 23:59:59.999999 (.999999999 in ruby1.9)
def end_of_day
change(:hour => 23, :min => 59, :sec => 59)
change(:hour => 23, :min => 59, :sec => 59, :usec => 999999.999)
end
# Returns a new Time representing the start of the month (1st of the month, 0:00)
@ -219,11 +220,11 @@ module ActiveSupport #:nodoc:
end
alias :at_beginning_of_month :beginning_of_month
# Returns a new Time representing the end of the month (last day of the month, 0:00)
# Returns a new Time representing the end of the month (end of the last day of the month)
def end_of_month
#self - ((self.mday-1).days + self.seconds_since_midnight)
last_day = ::Time.days_in_month( self.month, self.year )
change(:day => last_day, :hour => 23, :min => 59, :sec => 59, :usec => 0)
change(:day => last_day, :hour => 23, :min => 59, :sec => 59, :usec => 999999.999)
end
alias :at_end_of_month :end_of_month
@ -233,7 +234,7 @@ module ActiveSupport #:nodoc:
end
alias :at_beginning_of_quarter :beginning_of_quarter
# Returns a new Time representing the end of the quarter (last day of march, june, september, december, 23:59:59)
# Returns a new Time representing the end of the quarter (end of the last day of march, june, september, december)
def end_of_quarter
beginning_of_month.change(:month => [3, 6, 9, 12].detect { |m| m >= self.month }).end_of_month
end
@ -245,9 +246,9 @@ module ActiveSupport #:nodoc:
end
alias :at_beginning_of_year :beginning_of_year
# Returns a new Time representing the end of the year (31st of december, 23:59:59)
# Returns a new Time representing the end of the year (end of the 31st of december)
def end_of_year
change(:month => 12,:day => 31,:hour => 23, :min => 59, :sec => 59)
change(:month => 12, :day => 31, :hour => 23, :min => 59, :sec => 59, :usec => 999999.999)
end
alias :at_end_of_year :end_of_year

View file

@ -10,7 +10,7 @@ module ActiveSupport #:nodoc:
:short => "%d %b %H:%M",
:long => "%B %d, %Y %H:%M",
:long_ordinal => lambda { |time| time.strftime("%B #{time.day.ordinalize}, %Y %H:%M") },
:rfc822 => "%a, %d %b %Y %H:%M:%S %z"
:rfc822 => lambda { |time| time.strftime("%a, %d %b %Y %H:%M:%S #{time.formatted_offset(false)}") }
}
def self.included(base) #:nodoc:

View file

@ -0,0 +1,36 @@
class Object
# Invokes the method identified by the symbol +method+, passing it any arguments
# and/or the block specified, just like the regular Ruby <tt>Object#send</tt> does.
#
# *Unlike* that method however, a +NoMethodError+ exception will *not* be raised
# and +nil+ will be returned instead, if the receiving object is a +nil+ object or NilClass.
#
# ==== Examples
#
# Without try
# @person && @person.name
# or
# @person ? @person.name : nil
#
# With try
# @person.try(:name)
#
# +try+ also accepts arguments and/or a block, for the method it is trying
# Person.try(:find, 1)
# @people.try(:collect) {|p| p.name}
#--
# This method definition below is for rdoc purposes only. The alias_method call
# below overrides it as an optimization since +try+ behaves like +Object#send+,
# unless called on +NilClass+.
def try(method, *args, &block)
send(method, *args, &block)
end
remove_method :try
alias_method :try, :__send__
end
class NilClass
def try(*args)
nil
end
end

View file

@ -0,0 +1,16 @@
if RUBY_VERSION >= '1.9'
require 'uri'
str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
str.force_encoding(Encoding::UTF_8) if str.respond_to?(:force_encoding)
unless str == URI.unescape(URI.escape(str))
URI::Parser.class_eval do
remove_method :unescape
def unescape(str, escaped = @regexp[:ESCAPED])
enc = (str.encoding == Encoding::US_ASCII) ? Encoding::UTF_8 : str.encoding
str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc)
end
end
end
end

View file

@ -51,6 +51,9 @@ module ActiveSupport #:nodoc:
mattr_accessor :constant_watch_stack
self.constant_watch_stack = []
mattr_accessor :constant_watch_stack_mutex
self.constant_watch_stack_mutex = Mutex.new
# Module includes this module
module ModuleConstMissing #:nodoc:
def self.included(base) #:nodoc:
@ -319,12 +322,7 @@ module ActiveSupport #:nodoc:
rescue NameError
next
end
[
nesting.camelize,
# Special case: application.rb might define ApplicationControlller.
('ApplicationController' if nesting == 'application')
]
[ nesting_camel ]
end.flatten.compact.uniq
end
@ -514,7 +512,9 @@ module ActiveSupport #:nodoc:
[mod_name, initial_constants]
end
constant_watch_stack.concat watch_frames
constant_watch_stack_mutex.synchronize do
constant_watch_stack.concat watch_frames
end
aborting = true
begin
@ -531,8 +531,10 @@ module ActiveSupport #:nodoc:
new_constants = mod.local_constant_names - prior_constants
# Make sure no other frames takes credit for these constants.
constant_watch_stack.each do |frame_name, constants|
constants.concat new_constants if frame_name == mod_name
constant_watch_stack_mutex.synchronize do
constant_watch_stack.each do |frame_name, constants|
constants.concat new_constants if frame_name == mod_name
end
end
new_constants.collect do |suffix|
@ -554,8 +556,10 @@ module ActiveSupport #:nodoc:
# Remove the stack frames that we added.
if defined?(watch_frames) && ! watch_frames.blank?
frame_ids = watch_frames.collect { |frame| frame.object_id }
constant_watch_stack.delete_if do |watch_frame|
frame_ids.include? watch_frame.object_id
constant_watch_stack_mutex.synchronize do
constant_watch_stack.delete_if do |watch_frame|
frame_ids.include? watch_frame.object_id
end
end
end
end
@ -564,9 +568,9 @@ module ActiveSupport #:nodoc:
# Old style environment.rb referenced this method directly. Please note, it doesn't
# actually *do* anything any more.
def self.root(*args)
if defined?(RAILS_DEFAULT_LOGGER)
RAILS_DEFAULT_LOGGER.warn "Your environment.rb uses the old syntax, it may not continue to work in future releases."
RAILS_DEFAULT_LOGGER.warn "For upgrade instructions please see: http://manuals.rubyonrails.com/read/book/19"
if defined?(Rails) && Rails.logger
Rails.logger.warn "Your environment.rb uses the old syntax, it may not continue to work in future releases."
Rails.logger.warn "For upgrade instructions please see: http://manuals.rubyonrails.com/read/book/19"
end
end
end

View file

@ -13,7 +13,7 @@ module ActiveSupport
$stderr.puts callstack.join("\n ") if debug
},
'development' => Proc.new { |message, callstack|
logger = defined?(::RAILS_DEFAULT_LOGGER) ? ::RAILS_DEFAULT_LOGGER : Logger.new($stderr)
logger = (defined?(Rails) && Rails.logger) ? Rails.logger : Logger.new($stderr)
logger.warn message
logger.debug callstack.join("\n ") if debug
}
@ -90,10 +90,15 @@ module ActiveSupport
method_names.each do |method_name|
alias_method_chain(method_name, :deprecation) do |target, punctuation|
class_eval(<<-EOS, __FILE__, __LINE__)
def #{target}_with_deprecation#{punctuation}(*args, &block)
::ActiveSupport::Deprecation.warn(self.class.deprecated_method_warning(:#{method_name}, #{options[method_name].inspect}), caller)
#{target}_without_deprecation#{punctuation}(*args, &block)
end
def #{target}_with_deprecation#{punctuation}(*args, &block) # def generate_secret_with_deprecation(*args, &block)
::ActiveSupport::Deprecation.warn( # ::ActiveSupport::Deprecation.warn(
self.class.deprecated_method_warning( # self.class.deprecated_method_warning(
:#{method_name}, # :generate_secret,
#{options[method_name].inspect}), # "You should use ActiveSupport::SecureRandom.hex(64)"),
caller # caller
) # )
send(:#{target}_without_deprecation#{punctuation}, *args, &block) # send(:generate_secret_without_deprecation, *args, &block)
end # end
EOS
end
end
@ -113,37 +118,6 @@ module ActiveSupport
end
end
module Assertions #:nodoc:
def assert_deprecated(match = nil, &block)
result, warnings = collect_deprecations(&block)
assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
if match
match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp)
assert warnings.any? { |w| w =~ match }, "No deprecation warning matched #{match}: #{warnings.join(', ')}"
end
result
end
def assert_not_deprecated(&block)
result, deprecations = collect_deprecations(&block)
assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
result
end
private
def collect_deprecations
old_behavior = ActiveSupport::Deprecation.behavior
deprecations = []
ActiveSupport::Deprecation.behavior = Proc.new do |message, callstack|
deprecations << message
end
result = yield
[result, deprecations]
ensure
ActiveSupport::Deprecation.behavior = old_behavior
end
end
class DeprecationProxy #:nodoc:
silence_warnings do
instance_methods.each { |m| undef_method m unless m =~ /^__/ }
@ -220,24 +194,3 @@ end
class Module
include ActiveSupport::Deprecation::ClassMethods
end
require 'test/unit/error'
module Test
module Unit
class TestCase
include ActiveSupport::Deprecation::Assertions
end
class Error # :nodoc:
# Silence warnings when reporting test errors.
def message_with_silenced_deprecation
ActiveSupport::Deprecation.silence do
message_without_silenced_deprecation
end
end
alias_method_chain :message, :silenced_deprecation
end
end
end

View file

@ -1,3 +1,5 @@
require 'active_support/basic_object'
module ActiveSupport
# Provides accurate date and time measurements using Date#advance and
# Time#advance, respectively. It mainly supports the methods on Numeric,
@ -65,10 +67,12 @@ module ActiveSupport
def inspect #:nodoc:
consolidated = parts.inject(::Hash.new(0)) { |h,part| h[part.first] += part.last; h }
[:years, :months, :days, :minutes, :seconds].map do |length|
parts = [:years, :months, :days, :minutes, :seconds].map do |length|
n = consolidated[length]
"#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
end.compact.to_sentence
end.compact
parts = ["0 seconds"] if parts.empty?
parts.to_sentence(:locale => :en)
end
protected

View file

@ -42,6 +42,7 @@ module ActiveSupport
inflect.singular(/(vert|ind)ices$/i, '\1ex')
inflect.singular(/(matr)ices$/i, '\1ix')
inflect.singular(/(quiz)zes$/i, '\1')
inflect.singular(/(database)s$/i, '\1')
inflect.irregular('person', 'people')
inflect.irregular('man', 'men')

View file

@ -254,18 +254,20 @@ module ActiveSupport
# @person = Person.find(1)
# # => #<Person id: 1, name: "Donald E. Knuth">
#
# <%= link_to(@person.name, person_path %>
# <%= link_to(@person.name, person_path(@person)) %>
# # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
def parameterize(string, sep = '-')
re_sep = Regexp.escape(sep)
# replace accented chars with ther ascii equivalents
parameterized_string = transliterate(string)
# Turn unwanted chars into the seperator
parameterized_string.gsub!(/[^a-z0-9\-_\+]+/i, sep)
# No more than one of the separator in a row.
parameterized_string.squeeze!(sep)
# Remove leading/trailing separator.
parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/i, '')
unless sep.blank?
re_sep = Regexp.escape(sep)
# No more than one of the separator in a row.
parameterized_string.gsub!(/#{re_sep}{2,}/, sep)
# Remove leading/trailing separator.
parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/i, '')
end
parameterized_string.downcase
end
@ -275,9 +277,16 @@ module ActiveSupport
Iconv.iconv('ascii//ignore//translit', 'utf-8', string).to_s
end
if RUBY_VERSION >= '1.9'
undef_method :transliterate
def transliterate(string)
warn "Ruby 1.9 doesn't support Unicode normalization yet"
string.dup
end
# The iconv transliteration code doesn't function correctly
# on some platforms, but it's very fast where it does function.
if "foo" != Inflector.transliterate("föö")
elsif "foo" != (Inflector.transliterate("föö") rescue nil)
undef_method :transliterate
def transliterate(string)
string.mb_chars.normalize(:kd). # Decompose accented characters

View file

@ -1,23 +1,2 @@
module ActiveSupport
# If true, use ISO 8601 format for dates and times. Otherwise, fall back to the Active Support legacy format.
mattr_accessor :use_standard_json_time_format
class << self
def escape_html_entities_in_json
@escape_html_entities_in_json
end
def escape_html_entities_in_json=(value)
ActiveSupport::JSON::Encoding.escape_regex = \
if value
/[\010\f\n\r\t"\\><&]/
else
/[\010\f\n\r\t"\\]/
end
@escape_html_entities_in_json = value
end
end
end
require 'active_support/json/encoding'
require 'active_support/json/decoding'
require 'active_support/json/encoding'

View file

@ -0,0 +1,37 @@
require 'json' unless defined?(JSON)
module ActiveSupport
module JSON
module Backends
module JSONGem
ParseError = ::JSON::ParserError
extend self
# Converts a JSON string into a Ruby object.
def decode(json)
data = ::JSON.parse(json)
if ActiveSupport.parse_json_times
convert_dates_from(data)
else
data
end
end
private
def convert_dates_from(data)
case data
when DATE_REGEX
DateTime.parse(data)
when Array
data.map! { |d| convert_dates_from(d) }
when Hash
data.each do |key, value|
data[key] = convert_dates_from(value)
end
else data
end
end
end
end
end
end

View file

@ -0,0 +1,84 @@
require 'active_support/core_ext/string/starts_ends_with'
module ActiveSupport
module JSON
module Backends
module Yaml
ParseError = ::StandardError
extend self
# Converts a JSON string into a Ruby object.
def decode(json)
YAML.load(convert_json_to_yaml(json))
rescue ArgumentError => e
raise ParseError, "Invalid JSON string"
end
protected
# Ensure that ":" and "," are always followed by a space
def convert_json_to_yaml(json) #:nodoc:
require 'strscan' unless defined? ::StringScanner
scanner, quoting, marks, pos, times = ::StringScanner.new(json), false, [], nil, []
while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/)
case char = scanner[1]
when '"', "'"
if !quoting
quoting = char
pos = scanner.pos
elsif quoting == char
if json[pos..scanner.pos-2] =~ DATE_REGEX
# found a date, track the exact positions of the quotes so we can remove them later.
# oh, and increment them for each current mark, each one is an extra padded space that bumps
# the position in the final YAML output
total_marks = marks.size
times << pos+total_marks << scanner.pos+total_marks
end
quoting = false
end
when ":",","
marks << scanner.pos - 1 unless quoting
when "\\"
scanner.skip(/\\/)
end
end
if marks.empty?
json.gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do
ustr = $1
if ustr.start_with?('u')
[ustr[1..-1].to_i(16)].pack("U")
elsif ustr == '\\'
'\\\\'
else
ustr
end
end
else
left_pos = [-1].push(*marks)
right_pos = marks << scanner.pos + scanner.rest_size
output = []
left_pos.each_with_index do |left, i|
scanner.pos = left.succ
output << scanner.peek(right_pos[i] - scanner.pos + 1).gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do
ustr = $1
if ustr.start_with?('u')
[ustr[1..-1].to_i(16)].pack("U")
elsif ustr == '\\'
'\\\\'
else
ustr
end
end
end
output = output * " "
times.each { |i| output[i-1] = ' ' }
output.gsub!(/\\\//, '/')
output
end
end
end
end
end
end

View file

@ -1,63 +1,35 @@
require 'yaml'
require 'strscan'
require 'active_support/core_ext/module/attribute_accessors'
module ActiveSupport
# Look for and parse json strings that look like ISO 8601 times.
mattr_accessor :parse_json_times
module JSON
class ParseError < StandardError
end
class << self
# Converts a JSON string into a Ruby object.
def decode(json)
YAML.load(convert_json_to_yaml(json))
rescue ArgumentError => e
raise ParseError, "Invalid JSON string"
attr_reader :parse_error
delegate :decode, :to => :backend
def backend
self.backend = "Yaml" unless defined?(@backend)
@backend
end
protected
# matches YAML-formatted dates
DATE_REGEX = /^\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?$/
# Ensure that ":" and "," are always followed by a space
def convert_json_to_yaml(json) #:nodoc:
scanner, quoting, marks, pos, times = StringScanner.new(json), false, [], nil, []
while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/)
case char = scanner[1]
when '"', "'"
if !quoting
quoting = char
pos = scanner.pos
elsif quoting == char
if json[pos..scanner.pos-2] =~ DATE_REGEX
# found a date, track the exact positions of the quotes so we can remove them later.
# oh, and increment them for each current mark, each one is an extra padded space that bumps
# the position in the final YAML output
total_marks = marks.size
times << pos+total_marks << scanner.pos+total_marks
end
quoting = false
end
when ":",","
marks << scanner.pos - 1 unless quoting
end
end
if marks.empty?
json.gsub(/\\\//, '/')
else
left_pos = [-1].push(*marks)
right_pos = marks << json.length
output = []
left_pos.each_with_index do |left, i|
output << json[left.succ..right_pos[i]]
end
output = output * " "
times.each { |i| output[i-1] = ' ' }
output.gsub!(/\\\//, '/')
output
end
def backend=(name)
if name.is_a?(Module)
@backend = name
else
require "active_support/json/backends/#{name.to_s.downcase}.rb"
@backend = ActiveSupport::JSON::Backends::const_get(name)
end
@parse_error = @backend::ParseError
end
def with_backend(name)
old_backend, self.backend = backend, name
yield
ensure
self.backend = old_backend
end
end
end
end

View file

@ -1,21 +1,22 @@
class Date
# Returns a JSON string representing the date. If ActiveSupport.use_standard_json_time_format is set to true, the
# ISO 8601 format is used.
# Coerces the date to a string for JSON encoding.
#
# ==== Examples:
# ISO 8601 format is used if ActiveSupport::JSON::Encoding.use_standard_json_time_format is set.
#
# # With ActiveSupport.use_standard_json_time_format = true
# ==== Examples
#
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
# Date.new(2005,2,1).to_json
# # => "2005-02-01"
#
# # With ActiveSupport.use_standard_json_time_format = false
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
# Date.new(2005,2,1).to_json
# # => "2005/02/01"
def to_json(options = nil)
if ActiveSupport.use_standard_json_time_format
%("#{strftime("%Y-%m-%d")}")
def as_json(options = nil)
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
strftime("%Y-%m-%d")
else
%("#{strftime("%Y/%m/%d")}")
strftime("%Y/%m/%d")
end
end
end

View file

@ -1,21 +1,22 @@
class DateTime
# Returns a JSON string representing the datetime. If ActiveSupport.use_standard_json_time_format is set to true, the
# ISO 8601 format is used.
# Coerces the datetime to a string for JSON encoding.
#
# ==== Examples:
# ISO 8601 format is used if ActiveSupport::JSON::Encoding.use_standard_json_time_format is set.
#
# # With ActiveSupport.use_standard_json_time_format = true
# ==== Examples
#
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
# DateTime.civil(2005,2,1,15,15,10).to_json
# # => "2005-02-01T15:15:10+00:00"
#
# # With ActiveSupport.use_standard_json_time_format = false
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
# DateTime.civil(2005,2,1,15,15,10).to_json
# # => "2005/02/01 15:15:10 +0000"
def to_json(options = nil)
if ActiveSupport.use_standard_json_time_format
xmlschema.inspect
def as_json(options = nil)
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
xmlschema
else
strftime('"%Y/%m/%d %H:%M:%S %z"')
strftime('%Y/%m/%d %H:%M:%S %z')
end
end
end

View file

@ -1,12 +1,17 @@
module Enumerable
# Returns a JSON string representing the enumerable. Any +options+
# given will be passed on to its elements. For example:
#
# users = User.find(:all)
# # => users.to_json(:only => :name)
#
# will pass the <tt>:only => :name</tt> option to each user.
def to_json(options = {}) #:nodoc:
"[#{map { |value| ActiveSupport::JSON.encode(value, options) } * ', '}]"
# Coerces the enumerable to an array for JSON encoding.
def as_json(options = nil) #:nodoc:
to_a
end
end
class Array
# Returns a JSON string representing the Array. +options+ are passed to each element.
def to_json(options = nil) #:nodoc:
"[#{map { |value| ActiveSupport::JSON.encode(value, options) } * ','}]"
end
def as_json(options = nil) #:nodoc:
self
end
end

View file

@ -1,5 +1,7 @@
class FalseClass
def to_json(options = nil) #:nodoc:
'false'
AS_JSON = ActiveSupport::JSON::Variable.new('false').freeze
def as_json(options = nil) #:nodoc:
AS_JSON
end
end

View file

@ -1,3 +1,5 @@
require 'active_support/core_ext/array/wrapper'
class Hash
# Returns a JSON string representing the hash.
#
@ -5,7 +7,7 @@ class Hash
# the hash keys. For example:
#
# { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json
# # => {"name": "Konata Izumi", 1: 2, "age": 16}
# # => {"name": "Konata Izumi", "1": 2, "age": 16}
#
# The keys in the JSON string are unordered due to the nature of hashes.
#
@ -28,20 +30,27 @@ class Hash
# would pass the <tt>:include => :posts</tt> option to <tt>users</tt>,
# allowing the posts association in the User model to be converted to JSON
# as well.
def to_json(options = {}) #:nodoc:
hash_keys = self.keys
def to_json(options = nil) #:nodoc:
hash = as_json(options)
if options[:except]
hash_keys = hash_keys - Array(options[:except])
elsif options[:only]
hash_keys = hash_keys & Array(options[:only])
end
result = '{'
result << hash.map do |key, value|
"#{ActiveSupport::JSON.encode(key.to_s)}:#{ActiveSupport::JSON.encode(value, options)}"
end * ','
result << '}'
end
returning result = '{' do
result << hash_keys.map do |key|
"#{ActiveSupport::JSON.encode(key)}: #{ActiveSupport::JSON.encode(self[key], options)}"
end * ', '
result << '}'
def as_json(options = nil) #:nodoc:
if options
if attrs = options[:except]
except(*Array.wrap(attrs))
elsif attrs = options[:only]
slice(*Array.wrap(attrs))
else
self
end
else
self
end
end
end

View file

@ -1,5 +1,7 @@
class NilClass
def to_json(options = nil) #:nodoc:
'null'
AS_JSON = ActiveSupport::JSON::Variable.new('null').freeze
def as_json(options = nil) #:nodoc:
AS_JSON
end
end

View file

@ -2,4 +2,20 @@ class Numeric
def to_json(options = nil) #:nodoc:
to_s
end
def as_json(options = nil) #:nodoc:
self
end
end
class Float
def to_json(options = nil) #:nodoc:
to_s
end
end
class Integer
def to_json(options = nil) #:nodoc:
to_s
end
end

View file

@ -1,6 +1,10 @@
class Object
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
def to_json(options = {})
ActiveSupport::JSON.encode(instance_values, options)
def to_json(options = nil)
ActiveSupport::JSON.encode(as_json(options))
end
def as_json(options = nil)
instance_values
end
end

View file

@ -2,4 +2,8 @@ class Regexp
def to_json(options = nil) #:nodoc:
inspect
end
def as_json(options = nil) #:nodoc:
self
end
end

View file

@ -1,36 +1,9 @@
module ActiveSupport
module JSON
module Encoding
mattr_accessor :escape_regex
ESCAPED_CHARS = {
"\010" => '\b',
"\f" => '\f',
"\n" => '\n',
"\r" => '\r',
"\t" => '\t',
'"' => '\"',
'\\' => '\\\\',
'>' => '\u003E',
'<' => '\u003C',
'&' => '\u0026'
}
end
end
end
ActiveSupport.escape_html_entities_in_json = true
class String
def to_json(options = nil) #:nodoc:
json = '"' + gsub(ActiveSupport::JSON::Encoding.escape_regex) { |s|
ActiveSupport::JSON::Encoding::ESCAPED_CHARS[s]
}
json.force_encoding('ascii-8bit') if respond_to?(:force_encoding)
json.gsub(/([\xC0-\xDF][\x80-\xBF]|
[\xE0-\xEF][\x80-\xBF]{2}|
[\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s|
s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&')
} + '"'
ActiveSupport::JSON::Encoding.escape(self)
end
def as_json(options = nil) #:nodoc:
self
end
end

View file

@ -1,5 +1,5 @@
class Symbol
def to_json(options = {}) #:nodoc:
ActiveSupport::JSON.encode(to_s, options)
def as_json(options = nil) #:nodoc:
to_s
end
end

View file

@ -1,21 +1,22 @@
class Time
# Returns a JSON string representing the time. If ActiveSupport.use_standard_json_time_format is set to true, the
# ISO 8601 format is used.
# Coerces the time to a string for JSON encoding.
#
# ==== Examples:
# ISO 8601 format is used if ActiveSupport::JSON::Encoding.use_standard_json_time_format is set.
#
# # With ActiveSupport.use_standard_json_time_format = true
# ==== Examples
#
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
# Time.utc(2005,2,1,15,15,10).to_json
# # => "2005-02-01T15:15:10Z"
#
# # With ActiveSupport.use_standard_json_time_format = false
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
# Time.utc(2005,2,1,15,15,10).to_json
# # => "2005/02/01 15:15:10 +0000"
def to_json(options = nil)
if ActiveSupport.use_standard_json_time_format
xmlschema.inspect
def as_json(options = nil)
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
xmlschema
else
%("#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}")
%(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
end
end
end

View file

@ -1,5 +1,7 @@
class TrueClass
def to_json(options = nil) #:nodoc:
'true'
AS_JSON = ActiveSupport::JSON::Variable.new('true').freeze
def as_json(options = nil) #:nodoc:
AS_JSON
end
end

View file

@ -1,37 +1,102 @@
require 'active_support/json/variable'
require 'active_support/json/encoders/object' # Require explicitly for rdoc.
Dir["#{File.dirname(__FILE__)}/encoders/**/*.rb"].each do |file|
basename = File.basename(file, '.rb')
unless basename == 'object'
require "active_support/json/encoders/#{basename}"
end
end
# encoding: utf-8
require 'active_support/core_ext/module/delegation'
require 'active_support/deprecation'
module ActiveSupport
class << self
delegate :use_standard_json_time_format, :use_standard_json_time_format=,
:escape_html_entities_in_json, :escape_html_entities_in_json=,
:to => :'ActiveSupport::JSON::Encoding'
end
module JSON
class CircularReferenceError < StandardError
end
# matches YAML-formatted dates
DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/
class << self
REFERENCE_STACK_VARIABLE = :json_reference_stack #:nodoc:
delegate :encode, :to => :'ActiveSupport::JSON::Encoding'
end
# Converts a Ruby object into a JSON string.
def encode(value, options = {})
raise_on_circular_reference(value) do
value.send(:to_json, options)
module Encoding #:nodoc:
class CircularReferenceError < StandardError
end
ESCAPED_CHARS = {
"\010" => '\b',
"\f" => '\f',
"\n" => '\n',
"\r" => '\r',
"\t" => '\t',
'"' => '\"',
'\\' => '\\\\',
'>' => '\u003E',
'<' => '\u003C',
'&' => '\u0026' }
class << self
# If true, use ISO 8601 format for dates and times. Otherwise, fall back to the Active Support legacy format.
attr_accessor :use_standard_json_time_format
attr_accessor :escape_regex
attr_reader :escape_html_entities_in_json
def escape_html_entities_in_json=(value)
self.escape_regex = \
if @escape_html_entities_in_json = value
/[\010\f\n\r\t"\\><&]/
else
/[\010\f\n\r\t"\\]/
end
end
def escape(string)
string = string.dup.force_encoding(::Encoding::BINARY) if string.respond_to?(:force_encoding)
json = string.
gsub(escape_regex) { |s| ESCAPED_CHARS[s] }.
gsub(/([\xC0-\xDF][\x80-\xBF]|
[\xE0-\xEF][\x80-\xBF]{2}|
[\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s|
s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/n, '\\\\u\&')
}
%("#{json}")
end
# Converts a Ruby object into a JSON string.
def encode(value, options = nil)
options = {} unless Hash === options
seen = (options[:seen] ||= [])
raise CircularReferenceError, 'object references itself' if seen.include?(value)
seen << value
value.to_json(options)
ensure
seen.pop
end
end
protected
def raise_on_circular_reference(value) #:nodoc:
stack = Thread.current[REFERENCE_STACK_VARIABLE] ||= []
raise CircularReferenceError, 'object references itself' if
stack.include? value
stack << value
yield
ensure
stack.pop
end
self.escape_html_entities_in_json = true
end
CircularReferenceError = Deprecation::DeprecatedConstantProxy.new('ActiveSupport::JSON::CircularReferenceError', Encoding::CircularReferenceError)
end
end
# Hack to load json gem first so we can overwrite its to_json.
begin
require 'json'
rescue LoadError
end
require 'active_support/json/variable'
require 'active_support/json/encoders/date'
require 'active_support/json/encoders/date_time'
require 'active_support/json/encoders/enumerable'
require 'active_support/json/encoders/false_class'
require 'active_support/json/encoders/hash'
require 'active_support/json/encoders/nil_class'
require 'active_support/json/encoders/numeric'
require 'active_support/json/encoders/object'
require 'active_support/json/encoders/regexp'
require 'active_support/json/encoders/string'
require 'active_support/json/encoders/symbol'
require 'active_support/json/encoders/time'
require 'active_support/json/encoders/true_class'

View file

@ -28,5 +28,6 @@ en:
# Used in array.to_sentence.
support:
array:
sentence_connector: "and"
skip_last_comma: false
words_connector: ", "
two_words_connector: " and "
last_word_connector: ", and "

View file

@ -1,10 +1,10 @@
module ActiveSupport
module Memoizable
MEMOIZED_IVAR = Proc.new do |symbol|
def self.memoized_ivar_for(symbol)
"@_memoized_#{symbol.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}".to_sym
end
module Freezable
module InstanceMethods
def self.included(base)
base.class_eval do
unless base.method_defined?(:freeze_without_memoizable)
@ -19,23 +19,35 @@ module ActiveSupport
end
def memoize_all
methods.each do |m|
if m.to_s =~ /^_unmemoized_(.*)/
if method(m).arity == 0
__send__($1)
else
ivar = MEMOIZED_IVAR.call($1)
instance_variable_set(ivar, {})
prime_cache ".*"
end
def unmemoize_all
flush_cache ".*"
end
def prime_cache(*syms)
syms.each do |sym|
methods.each do |m|
if m.to_s =~ /^_unmemoized_(#{sym})/
if method(m).arity == 0
__send__($1)
else
ivar = ActiveSupport::Memoizable.memoized_ivar_for($1)
instance_variable_set(ivar, {})
end
end
end
end
end
def unmemoize_all
methods.each do |m|
if m.to_s =~ /^_unmemoized_(.*)/
ivar = MEMOIZED_IVAR.call($1)
instance_variable_get(ivar).clear if instance_variable_defined?(ivar)
def flush_cache(*syms, &block)
syms.each do |sym|
(methods + private_methods + protected_methods).each do |m|
if m.to_s =~ /^_unmemoized_(#{sym})/
ivar = ActiveSupport::Memoizable.memoized_ivar_for($1)
instance_variable_get(ivar).clear if instance_variable_defined?(ivar)
end
end
end
end
@ -44,37 +56,43 @@ module ActiveSupport
def memoize(*symbols)
symbols.each do |symbol|
original_method = :"_unmemoized_#{symbol}"
memoized_ivar = MEMOIZED_IVAR.call(symbol)
memoized_ivar = ActiveSupport::Memoizable.memoized_ivar_for(symbol)
class_eval <<-EOS, __FILE__, __LINE__
include Freezable
raise "Already memoized #{symbol}" if method_defined?(:#{original_method})
alias #{original_method} #{symbol}
if instance_method(:#{symbol}).arity == 0
def #{symbol}(reload = false)
if reload || !defined?(#{memoized_ivar}) || #{memoized_ivar}.empty?
#{memoized_ivar} = [#{original_method}.freeze]
end
#{memoized_ivar}[0]
end
else
def #{symbol}(*args)
#{memoized_ivar} ||= {} unless frozen?
reload = args.pop if args.last == true || args.last == :reload
if defined?(#{memoized_ivar}) && #{memoized_ivar}
if !reload && #{memoized_ivar}.has_key?(args)
#{memoized_ivar}[args]
elsif #{memoized_ivar}
#{memoized_ivar}[args] = #{original_method}(*args).freeze
end
else
#{original_method}(*args)
end
end
end
include InstanceMethods # include InstanceMethods
#
if method_defined?(:#{original_method}) # if method_defined?(:_unmemoized_mime_type)
raise "Already memoized #{symbol}" # raise "Already memoized mime_type"
end # end
alias #{original_method} #{symbol} # alias _unmemoized_mime_type mime_type
#
if instance_method(:#{symbol}).arity == 0 # if instance_method(:mime_type).arity == 0
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?
#{memoized_ivar} = [#{original_method}.freeze] # @_memoized_mime_type = [_unmemoized_mime_type.freeze]
end # end
#{memoized_ivar}[0] # @_memoized_mime_type[0]
end # end
else # else
def #{symbol}(*args) # def mime_type(*args)
#{memoized_ivar} ||= {} unless frozen? # @_memoized_mime_type ||= {} unless frozen?
reload = args.pop if args.last == true || args.last == :reload # reload = args.pop if args.last == true || args.last == :reload
#
if defined?(#{memoized_ivar}) && #{memoized_ivar} # if defined?(@_memoized_mime_type) && @_memoized_mime_type
if !reload && #{memoized_ivar}.has_key?(args) # if !reload && @_memoized_mime_type.has_key?(args)
#{memoized_ivar}[args] # @_memoized_mime_type[args]
elsif #{memoized_ivar} # elsif @_memoized_mime_type
#{memoized_ivar}[args] = #{original_method}(*args).freeze # @_memoized_mime_type[args] = _unmemoized_mime_type(*args).freeze
end # end
else # else
#{original_method}(*args) # _unmemoized_mime_type(*args)
end # end
end # end
end # end
#
if private_method_defined?(#{original_method.inspect}) # if private_method_defined?(:_unmemoized_mime_type)
private #{symbol.inspect} # private :mime_type
end # end
EOS
end
end

View file

@ -0,0 +1,70 @@
require 'openssl'
module ActiveSupport
# MessageEncryptor is a simple way to encrypt values which get stored somewhere
# you don't trust.
#
# 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
# want users to be able to determine the value of the payload.
class MessageEncryptor
class InvalidMessage < StandardError; end
OpenSSLCipherError = OpenSSL::Cipher.const_defined?(:CipherError) ? OpenSSL::Cipher::CipherError : OpenSSL::CipherError
def initialize(secret, cipher = 'aes-256-cbc')
@secret = secret
@cipher = cipher
end
def encrypt(value)
cipher = new_cipher
# Rely on OpenSSL for the initialization vector
iv = cipher.random_iv
cipher.encrypt
cipher.key = @secret
cipher.iv = iv
encrypted_data = cipher.update(Marshal.dump(value))
encrypted_data << cipher.final
[encrypted_data, iv].map {|v| ActiveSupport::Base64.encode64s(v)}.join("--")
end
def decrypt(encrypted_message)
cipher = new_cipher
encrypted_data, iv = encrypted_message.split("--").map {|v| ActiveSupport::Base64.decode64(v)}
cipher.decrypt
cipher.key = @secret
cipher.iv = iv
decrypted_data = cipher.update(encrypted_data)
decrypted_data << cipher.final
Marshal.load(decrypted_data)
rescue OpenSSLCipherError, TypeError
raise InvalidMessage
end
def encrypt_and_sign(value)
verifier.generate(encrypt(value))
end
def decrypt_and_verify(value)
decrypt(verifier.verify(value))
end
private
def new_cipher
OpenSSL::Cipher::Cipher.new(@cipher)
end
def verifier
MessageVerifier.new(@secret)
end
end
end

View file

@ -0,0 +1,79 @@
module ActiveSupport
# 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
# session store isn't suitable or available.
#
# Remember Me:
# cookies[:remember_me] = @verifier.generate([@user.id, 2.weeks.from_now])
#
# In the authentication filter:
#
# id, time = @verifier.verify(cookies[:remember_me])
# if time < Time.now
# self.current_user = User.find(id)
# end
#
class MessageVerifier
class InvalidSignature < StandardError; end
def initialize(secret, digest = 'SHA1')
@secret = secret
@digest = digest
end
def verify(signed_message)
raise InvalidSignature if signed_message.blank?
data, digest = signed_message.split("--")
if data.present? && digest.present? && secure_compare(digest, generate_digest(data))
Marshal.load(ActiveSupport::Base64.decode64(data))
else
raise InvalidSignature
end
end
def generate(value)
data = ActiveSupport::Base64.encode64s(Marshal.dump(value))
"#{data}--#{generate_digest(data)}"
end
private
if "foo".respond_to?(:force_encoding)
# constant-time comparison algorithm to prevent timing attacks
def secure_compare(a, b)
a = a.dup.force_encoding(Encoding::BINARY)
b = b.dup.force_encoding(Encoding::BINARY)
if a.length == b.length
result = 0
for i in 0..(a.length - 1)
result |= a[i].ord ^ b[i].ord
end
result == 0
else
false
end
end
else
# For <= 1.8.6
def secure_compare(a, b)
if a.length == b.length
result = 0
for i in 0..(a.length - 1)
result |= a[i] ^ b[i]
end
result == 0
else
false
end
end
end
def generate_digest(data)
require 'openssl' unless defined?(OpenSSL)
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(@digest), @secret, data)
end
end
end

View file

@ -1,9 +1,5 @@
# encoding: utf-8
require 'active_support/multibyte/chars'
require 'active_support/multibyte/exceptions'
require 'active_support/multibyte/unicode_database'
module ActiveSupport #:nodoc:
module Multibyte
# A list of all available normalization forms. See http://www.unicode.org/reports/tr15/tr15-29.html for more
@ -27,7 +23,35 @@ module ActiveSupport #:nodoc:
#
# Example:
# ActiveSupport::Multibyte.proxy_class = CharsForUTF32
mattr_accessor :proxy_class
self.proxy_class = ActiveSupport::Multibyte::Chars
def self.proxy_class=(klass)
@proxy_class = klass
end
# Returns the currect proxy class
def self.proxy_class
@proxy_class ||= ActiveSupport::Multibyte::Chars
end
# Regular expressions that describe valid byte sequences for a character
VALID_CHARACTER = {
# Borrowed from the Kconv library by Shinji KONO - (also as seen on the W3C site)
'UTF-8' => /\A(?:
[\x00-\x7f] |
[\xc2-\xdf] [\x80-\xbf] |
\xe0 [\xa0-\xbf] [\x80-\xbf] |
[\xe1-\xef] [\x80-\xbf] [\x80-\xbf] |
\xf0 [\x90-\xbf] [\x80-\xbf] [\x80-\xbf] |
[\xf1-\xf3] [\x80-\xbf] [\x80-\xbf] [\x80-\xbf] |
\xf4 [\x80-\x8f] [\x80-\xbf] [\x80-\xbf])\z /xn,
# Quick check for valid Shift-JIS characters, disregards the odd-even pairing
'Shift_JIS' => /\A(?:
[\x00-\x7e \xa1-\xdf] |
[\x81-\x9f \xe0-\xef] [\x40-\x7e \x80-\x9e \x9f-\xfc])\z /xn
}
end
end
require 'active_support/multibyte/chars'
require 'active_support/multibyte/exceptions'
require 'active_support/multibyte/unicode_database'
require 'active_support/multibyte/utils'

View file

@ -73,16 +73,7 @@ module ActiveSupport #:nodoc:
UNICODE_TRAILERS_PAT = /(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+\Z/
UNICODE_LEADERS_PAT = /\A(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+/
# Borrowed from the Kconv library by Shinji KONO - (also as seen on the W3C site)
UTF8_PAT = /\A(?:
[\x00-\x7f] |
[\xc2-\xdf] [\x80-\xbf] |
\xe0 [\xa0-\xbf] [\x80-\xbf] |
[\xe1-\xef] [\x80-\xbf] [\x80-\xbf] |
\xf0 [\x90-\xbf] [\x80-\xbf] [\x80-\xbf] |
[\xf1-\xf3] [\x80-\xbf] [\x80-\xbf] [\x80-\xbf] |
\xf4 [\x80-\x8f] [\x80-\xbf] [\x80-\xbf]
)*\z/xn
UTF8_PAT = ActiveSupport::Multibyte::VALID_CHARACTER['UTF-8']
attr_reader :wrapped_string
alias to_s wrapped_string
@ -205,7 +196,22 @@ module ActiveSupport #:nodoc:
# 'Café périferôl'.mb_chars.index('ô') #=> 12
# 'Café périferôl'.mb_chars.index(/\w/u) #=> 0
def index(needle, offset=0)
index = @wrapped_string.index(needle, offset)
wrapped_offset = self.first(offset).wrapped_string.length
index = @wrapped_string.index(needle, wrapped_offset)
index ? (self.class.u_unpack(@wrapped_string.slice(0...index)).size) : nil
end
# Returns the position _needle_ in the string, counting in
# codepoints, searching backward from _offset_ or the end of the
# string. Returns +nil+ if _needle_ isn't found.
#
# Example:
# 'Café périferôl'.mb_chars.rindex('é') #=> 6
# 'Café périferôl'.mb_chars.rindex(/\w/u) #=> 13
def rindex(needle, offset=nil)
offset ||= length
wrapped_offset = self.first(offset).wrapped_string.length
index = @wrapped_string.rindex(needle, wrapped_offset)
index ? (self.class.u_unpack(@wrapped_string.slice(0...index)).size) : nil
end
@ -292,31 +298,31 @@ module ActiveSupport #:nodoc:
def rstrip
chars(@wrapped_string.gsub(UNICODE_TRAILERS_PAT, ''))
end
# Strips entire range of Unicode whitespace from the left of the string.
def lstrip
chars(@wrapped_string.gsub(UNICODE_LEADERS_PAT, ''))
end
# Strips entire range of Unicode whitespace from the right and left of the string.
def strip
rstrip.lstrip
end
# Returns the number of codepoints in the string
def size
self.class.u_unpack(@wrapped_string).size
end
alias_method :length, :size
# Reverses all characters in the string.
#
# Example:
# 'Café'.mb_chars.reverse.to_s #=> 'éfaC'
def reverse
chars(self.class.u_unpack(@wrapped_string).reverse.pack('U*'))
chars(self.class.g_unpack(@wrapped_string).reverse.flatten.pack('U*'))
end
# Implements Unicode-aware slice with codepoints. Slicing on one point returns the codepoints for that
# character.
#
@ -344,6 +350,26 @@ module ActiveSupport #:nodoc:
end
alias_method :[], :slice
# Like <tt>String#slice!</tt>, except instead of byte offsets you specify character offsets.
#
# Example:
# s = 'こんにちは'
# s.mb_chars.slice!(2..3).to_s #=> "にち"
# s #=> "こんは"
def slice!(*args)
slice = self[*args]
self[*args] = ''
slice
end
# Returns the codepoint of the first character in the string.
#
# Example:
# 'こんにちは'.mb_chars.ord #=> 12371
def ord
self.class.u_unpack(@wrapped_string)[0]
end
# Convert characters in the string to uppercase.
#
# Example:
@ -424,7 +450,7 @@ module ActiveSupport #:nodoc:
chars(self.class.tidy_bytes(@wrapped_string))
end
%w(lstrip rstrip strip reverse upcase downcase slice tidy_bytes capitalize).each do |method|
%w(lstrip rstrip strip reverse upcase downcase tidy_bytes capitalize).each do |method|
define_method("#{method}!") do |*args|
unless args.nil?
@wrapped_string = send(method, *args).to_s
@ -609,7 +635,9 @@ module ActiveSupport #:nodoc:
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent resulting in a valid UTF-8 string.
def tidy_bytes(string)
string.split(//u).map do |c|
if !UTF8_PAT.match(c)
c.force_encoding(Encoding::ASCII) if c.respond_to?(:force_encoding)
if !ActiveSupport::Multibyte::VALID_CHARACTER['UTF-8'].match(c)
n = c.unpack('C')[0]
n < 128 ? n.chr :
n < 160 ? [UCD.cp1252[n] || n].pack('U') :

View file

@ -24,10 +24,10 @@ module ActiveSupport #:nodoc:
# Lazy load the Unicode database so it's only loaded when it's actually used
ATTRIBUTES.each do |attr_name|
class_eval(<<-EOS, __FILE__, __LINE__)
def #{attr_name}
load
@#{attr_name}
end
def #{attr_name} # def codepoints
load # load
@#{attr_name} # @codepoints
end # end
EOS
end

View file

@ -0,0 +1,61 @@
# encoding: utf-8
module ActiveSupport #:nodoc:
module Multibyte #:nodoc:
if Kernel.const_defined?(:Encoding)
# Returns a regular expression that matches valid characters in the current encoding
def self.valid_character
VALID_CHARACTER[Encoding.default_external.to_s]
end
else
def self.valid_character
case $KCODE
when 'UTF8'
VALID_CHARACTER['UTF-8']
when 'SJIS'
VALID_CHARACTER['Shift_JIS']
end
end
end
if 'string'.respond_to?(:valid_encoding?)
# Verifies the encoding of a string
def self.verify(string)
string.valid_encoding?
end
else
def self.verify(string)
if expression = valid_character
for c in string.split(//)
return false unless expression.match(c)
end
end
true
end
end
# Verifies the encoding of the string and raises an exception when it's not valid
def self.verify!(string)
raise EncodingError.new("Found characters with invalid encoding") unless verify(string)
end
if 'string'.respond_to?(:force_encoding)
# Removes all invalid characters from the string.
#
# Note: this method is a no-op in Ruby 1.9
def self.clean(string)
string
end
else
def self.clean(string)
if expression = valid_character
stripped = []; for c in string.split(//)
stripped << c if expression.match(c)
end; stripped.join
else
string
end
end
end
end
end

View file

@ -10,7 +10,13 @@ module ActiveSupport
private
def method_missing(method, *arguments, &block)
arguments << (arguments.last.respond_to?(:to_hash) ? @options.deep_merge(arguments.pop) : @options.dup)
if arguments.last.is_a?(Proc)
proc = arguments.pop
arguments << lambda { |*args| @options.deep_merge(proc.call(*args)) }
else
arguments << (arguments.last.respond_to?(:to_hash) ? @options.deep_merge(arguments.pop) : @options.dup)
end
@context.__send__(method, *arguments, &block)
end
end

View file

@ -4,55 +4,138 @@ module ActiveSupport
if RUBY_VERSION >= '1.9'
OrderedHash = ::Hash
else
class OrderedHash < Array #:nodoc:
def []=(key, value)
if pair = assoc(key)
pair.pop
pair << value
else
self << [key, value]
end
value
class OrderedHash < Hash #:nodoc:
def initialize(*args, &block)
super
@keys = []
end
def [](key)
pair = assoc(key)
pair ? pair.last : nil
def self.[](*args)
ordered_hash = new
if (args.length == 1 && args.first.is_a?(Array))
args.first.each do |key_value_pair|
next unless (key_value_pair.is_a?(Array))
ordered_hash[key_value_pair[0]] = key_value_pair[1]
end
return ordered_hash
end
unless (args.size % 2 == 0)
raise ArgumentError.new("odd number of arguments for Hash")
end
args.each_with_index do |val, ind|
next if (ind % 2 != 0)
ordered_hash[val] = args[ind + 1]
end
ordered_hash
end
def initialize_copy(other)
super
# make a deep copy of keys
@keys = other.keys
end
def []=(key, value)
@keys << key if !has_key?(key)
super
end
def delete(key)
pair = assoc(key)
pair ? array_index = index(pair) : nil
array_index ? delete_at(array_index).last : nil
if has_key? key
index = @keys.index(key)
@keys.delete_at index
end
super
end
def delete_if
super
sync_keys!
self
end
def reject!
super
sync_keys!
self
end
def reject(&block)
dup.reject!(&block)
end
def keys
collect { |key, value| key }
@keys.dup
end
def values
collect { |key, value| value }
@keys.collect { |key| self[key] }
end
def to_hash
returning({}) do |hash|
each { |array| hash[array[0]] = array[1] }
end
self
end
def has_key?(k)
!assoc(k).nil?
def to_a
@keys.map { |key| [ key, self[key] ] }
end
alias_method :key?, :has_key?
alias_method :include?, :has_key?
alias_method :member?, :has_key?
def has_value?(v)
any? { |key, value| value == v }
def each_key
@keys.each { |key| yield key }
end
alias_method :value?, :has_value?
def each_value
@keys.each { |key| yield self[key]}
end
def each
@keys.each {|key| yield [key, self[key]]}
end
alias_method :each_pair, :each
def clear
super
@keys.clear
self
end
def shift
k = @keys.first
v = delete(k)
[k, v]
end
def merge!(other_hash)
other_hash.each {|k,v| self[k] = v }
self
end
def merge(other_hash)
dup.merge!(other_hash)
end
# When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not.
def replace(other)
super
@keys = other.keys
self
end
def inspect
"#<OrderedHash #{super}>"
end
private
def sync_keys!
@keys.delete_if {|k| !has_key?(k)}
end
end
end
end

View file

@ -1,8 +1,3 @@
begin
require 'openssl'
rescue LoadError
end
begin
require 'securerandom'
rescue LoadError
@ -10,7 +5,7 @@ end
module ActiveSupport
if defined?(::SecureRandom)
# Use Ruby 1.9's SecureRandom library whenever possible.
# Use Ruby's SecureRandom library if available.
SecureRandom = ::SecureRandom # :nodoc:
else
# = Secure random number generator interface.
@ -64,6 +59,13 @@ module ActiveSupport
def self.random_bytes(n=nil)
n ||= 16
unless defined? OpenSSL
begin
require 'openssl'
rescue LoadError
end
end
if defined? OpenSSL::Random
return OpenSSL::Random.random_bytes(n)
end

View file

@ -1,24 +1,40 @@
require 'test/unit/testcase'
require 'active_support/testing/default'
require 'active_support/testing/core_ext/test'
require 'active_support/testing/setup_and_teardown'
require 'active_support/testing/assertions'
require 'active_support/testing/deprecation'
require 'active_support/testing/declarative'
begin
gem 'mocha', ">= 0.9.7"
require 'mocha'
rescue LoadError
# Fake Mocha::ExpectationError so we can rescue it in #run. Bleh.
Object.const_set :Mocha, Module.new
Mocha.const_set :ExpectationError, Class.new(StandardError)
end
module ActiveSupport
class TestCase < Test::Unit::TestCase
# test "verify something" do
# ...
# end
def self.test(name, &block)
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
defined = instance_method(test_name) rescue false
raise "#{test_name} is already defined in #{self}" if defined
if block_given?
define_method(test_name, &block)
else
define_method(test_name) do
flunk "No implementation provided for #{name}"
end
class TestCase < ::Test::Unit::TestCase
if defined? MiniTest
Assertion = MiniTest::Assertion
alias_method :method_name, :name if method_defined? :name
alias_method :method_name, :__name__ if method_defined? :__name__
else
# TODO: Figure out how to get the Rails::BacktraceFilter into minitest/unit
if defined?(Rails) && ENV['BACKTRACE'].nil?
require 'rails/backtrace_cleaner'
Test::Unit::Util::BacktraceFilter.module_eval { include Rails::BacktraceFilterForTestUnit }
end
Assertion = Test::Unit::AssertionFailedError
require 'active_support/testing/default'
include ActiveSupport::Testing::Default
end
include ActiveSupport::Testing::SetupAndTeardown
include ActiveSupport::Testing::Assertions
include ActiveSupport::Testing::Deprecation
extend ActiveSupport::Testing::Declarative
end
end

View file

@ -1,9 +1,5 @@
require 'test/unit/assertions'
module Test
module Unit
#--
# FIXME: no Proc#binding in Ruby 2, must change this API
#++
module ActiveSupport
module Testing
module Assertions
# Test numeric difference between the return value of an expression as a result of what is evaluated
# in the yielded block.
@ -35,20 +31,17 @@ module Test
# assert_difference 'Article.count', -1, "An Article should be destroyed" do
# post :delete, :id => ...
# end
def assert_difference(expressions, difference = 1, message = nil, &block)
expression_evaluations = Array(expressions).map do |expression|
[expression, lambda do
eval(expression, block.__send__(:binding))
end]
end
def assert_difference(expression, difference = 1, message = nil, &block)
b = block.send(:binding)
exps = Array.wrap(expression)
before = exps.map { |e| eval(e, b) }
original_values = expression_evaluations.inject([]) { |memo, expression| memo << expression[1].call }
yield
expression_evaluations.each_with_index do |expression, i|
full_message = ""
full_message << "#{message}.\n" if message
full_message << "<#{expression[0]}> was the expression that failed"
assert_equal original_values[i] + difference, expression[1].call, full_message
exps.each_with_index do |e, i|
error = "#{e.inspect} didn't change by #{difference}"
error = "#{message}.\n#{error}" if message
assert_equal(before[i] + difference, eval(e, b), error)
end
end
@ -64,8 +57,8 @@ module Test
# assert_no_difference 'Article.count', "An Article should not be destroyed" do
# post :create, :article => invalid_attributes
# end
def assert_no_difference(expressions, message = nil, &block)
assert_difference expressions, 0, message, &block
def assert_no_difference(expression, message = nil, &block)
assert_difference expression, 0, message, &block
end
end
end

View file

@ -1,6 +0,0 @@
require 'active_support/testing/core_ext/test/unit/assertions'
require 'active_support/testing/setup_and_teardown'
class Test::Unit::TestCase #:nodoc:
include ActiveSupport::Testing::SetupAndTeardown
end

View file

@ -0,0 +1,21 @@
module ActiveSupport
module Testing
module Declarative
# test "verify something" do
# ...
# end
def test(name, &block)
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
defined = instance_method(test_name) rescue false
raise "#{test_name} is already defined in #{self}" if defined
if block_given?
define_method(test_name, &block)
else
define_method(test_name) do
flunk "No implementation provided for #{name}"
end
end
end
end
end
end

View file

@ -0,0 +1,57 @@
require "active_support/core_ext/module"
module ActiveSupport
module Testing
module Deprecation #:nodoc:
def assert_deprecated(match = nil, &block)
result, warnings = collect_deprecations(&block)
assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
if match
match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp)
assert warnings.any? { |w| w =~ match }, "No deprecation warning matched #{match}: #{warnings.join(', ')}"
end
result
end
def assert_not_deprecated(&block)
result, deprecations = collect_deprecations(&block)
assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
result
end
private
def collect_deprecations
old_behavior = ActiveSupport::Deprecation.behavior
deprecations = []
ActiveSupport::Deprecation.behavior = Proc.new do |message, callstack|
deprecations << message
end
result = yield
[result, deprecations]
ensure
ActiveSupport::Deprecation.behavior = old_behavior
end
end
end
end
begin
require 'test/unit/error'
module Test
module Unit
class Error # :nodoc:
# Silence warnings when reporting test errors.
def message_with_silenced_deprecation
ActiveSupport::Deprecation.silence do
message_without_silenced_deprecation
end
end
alias_method_chain :message, :silenced_deprecation
end
end
end
rescue LoadError
# Using miniunit, ignore.
end

View file

@ -12,7 +12,7 @@ module ActiveSupport
if benchmark = ARGV.include?('--benchmark') # HAX for rake test
{ :benchmark => true,
:runs => 4,
:metrics => [:process_time, :memory, :objects, :gc_runs, :gc_time],
:metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time],
:output => 'tmp/performance' }
else
{ :benchmark => false,

View file

@ -1,119 +1,90 @@
require 'active_support/callbacks'
module ActiveSupport
module Testing
module SetupAndTeardown
# For compatibility with Ruby < 1.8.6
PASSTHROUGH_EXCEPTIONS =
if defined?(Test::Unit::TestCase::PASSTHROUGH_EXCEPTIONS)
Test::Unit::TestCase::PASSTHROUGH_EXCEPTIONS
else
[NoMemoryError, SignalException, Interrupt, SystemExit]
end
def self.included(base)
base.class_eval do
include ActiveSupport::Callbacks
define_callbacks :setup, :teardown
if defined?(::Mini)
undef_method :run
alias_method :run, :run_with_callbacks_and_miniunit
if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions
include ForMiniTest
else
begin
require 'mocha'
undef_method :run
alias_method :run, :run_with_callbacks_and_mocha
rescue LoadError
undef_method :run
alias_method :run, :run_with_callbacks_and_testunit
end
include ForClassicTestUnit
end
end
end
def run_with_callbacks_and_miniunit(runner)
result = '.'
begin
run_callbacks :setup
result = super
rescue Exception => e
result = runner.puke(self.class, self.name, e)
ensure
begin
teardown
run_callbacks :teardown, :enumerator => :reverse_each
rescue Exception => e
result = runner.puke(self.class, self.name, e)
end
end
result
end
# This redefinition is unfortunate but test/unit shows us no alternative.
def run_with_callbacks_and_testunit(result) #:nodoc:
return if @method_name.to_s == "default_test"
yield(Test::Unit::TestCase::STARTED, name)
@_result = result
begin
run_callbacks :setup
setup
__send__(@method_name)
rescue Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
rescue *PASSTHROUGH_EXCEPTIONS
raise
rescue Exception
add_error($!)
ensure
begin
teardown
run_callbacks :teardown, :enumerator => :reverse_each
rescue Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
rescue *PASSTHROUGH_EXCEPTIONS
raise
rescue Exception
add_error($!)
end
end
result.add_run
yield(Test::Unit::TestCase::FINISHED, name)
end
# Doubly unfortunate: mocha does the same so we have to hax their hax.
def run_with_callbacks_and_mocha(result)
return if @method_name.to_s == "default_test"
yield(Test::Unit::TestCase::STARTED, name)
@_result = result
begin
mocha_setup
module ForMiniTest
def run(runner)
result = '.'
begin
run_callbacks :setup
setup
__send__(@method_name)
mocha_verify { add_assertion }
rescue Mocha::ExpectationError => e
add_failure(e.message, e.backtrace)
rescue Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
rescue StandardError, ScriptError
add_error($!)
result = super
rescue Exception => e
result = runner.puke(self.class, self.name, e)
ensure
begin
teardown
run_callbacks :teardown, :enumerator => :reverse_each
rescue Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
rescue StandardError, ScriptError
add_error($!)
rescue Exception => e
result = runner.puke(self.class, self.name, e)
end
end
ensure
mocha_teardown
result
end
end
module ForClassicTestUnit
# For compatibility with Ruby < 1.8.6
PASSTHROUGH_EXCEPTIONS = Test::Unit::TestCase::PASSTHROUGH_EXCEPTIONS rescue [NoMemoryError, SignalException, Interrupt, SystemExit]
# This redefinition is unfortunate but test/unit shows us no alternative.
# Doubly unfortunate: hax to support Mocha's hax.
def run(result)
return if @method_name.to_s == "default_test"
if using_mocha = respond_to?(:mocha_verify)
assertion_counter_klass = if defined?(Mocha::TestCaseAdapter::AssertionCounter)
Mocha::TestCaseAdapter::AssertionCounter
else
Mocha::Integration::TestUnit::AssertionCounter
end
assertion_counter = assertion_counter_klass.new(result)
end
yield(Test::Unit::TestCase::STARTED, name)
@_result = result
begin
begin
run_callbacks :setup
setup
__send__(@method_name)
mocha_verify(assertion_counter) if using_mocha
rescue Mocha::ExpectationError => e
add_failure(e.message, e.backtrace)
rescue Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
rescue Exception => e
raise if PASSTHROUGH_EXCEPTIONS.include?(e.class)
add_error(e)
ensure
begin
teardown
run_callbacks :teardown, :enumerator => :reverse_each
rescue Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
rescue Exception => e
raise if PASSTHROUGH_EXCEPTIONS.include?(e.class)
add_error(e)
end
end
ensure
mocha_teardown if using_mocha
end
result.add_run
yield(Test::Unit::TestCase::FINISHED, name)
end
result.add_run
yield(Test::Unit::TestCase::FINISHED, name)
end
end
end

View file

@ -1,4 +1,5 @@
require 'tzinfo'
module ActiveSupport
# A Time-like class that can represent a time in any time zone. Necessary because standard Ruby Time instances are
# limited to UTC and the system's <tt>ENV['TZ']</tt> zone.
@ -98,28 +99,33 @@ module ActiveSupport
"#{time.strftime('%a, %d %b %Y %H:%M:%S')} #{zone} #{formatted_offset}"
end
def xmlschema
"#{time.strftime("%Y-%m-%dT%H:%M:%S")}#{formatted_offset(true, 'Z')}"
def xmlschema(fraction_digits = 0)
fraction = if fraction_digits > 0
".%i" % time.usec.to_s[0, fraction_digits]
end
"#{time.strftime("%Y-%m-%dT%H:%M:%S")}#{fraction}#{formatted_offset(true, 'Z')}"
end
alias_method :iso8601, :xmlschema
# Returns a JSON string representing the TimeWithZone. If ActiveSupport.use_standard_json_time_format is set to
# true, the ISO 8601 format is used.
# Coerces the date to a string for JSON encoding.
#
# ==== Examples:
# ISO 8601 format is used if ActiveSupport::JSON::Encoding.use_standard_json_time_format is set.
#
# # With ActiveSupport.use_standard_json_time_format = true
# ==== Examples
#
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
# Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
# # => "2005-02-01T15:15:10Z"
#
# # With ActiveSupport.use_standard_json_time_format = false
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
# Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
# # => "2005/02/01 15:15:10 +0000"
def to_json(options = nil)
if ActiveSupport.use_standard_json_time_format
xmlschema.inspect
def as_json(options = nil)
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
xmlschema
else
%("#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}")
%(#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
end
end
@ -150,6 +156,7 @@ module ActiveSupport
"#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby 1.9 Time#to_s format
end
end
alias_method :to_formatted_s, :to_s
# Replaces <tt>%Z</tt> and <tt>%z</tt> directives with +zone+ and +formatted_offset+, respectively, before passing to
# Time#strftime, so that zone information is correct
@ -198,7 +205,7 @@ module ActiveSupport
# If we're subtracting a Duration of variable length (i.e., years, months, days), move backwards from #time,
# otherwise move backwards #utc, for accuracy when moving across DST boundaries
if other.acts_like?(:time)
utc - other
utc.to_f - other.to_f
elsif duration_of_variable_length?(other)
method_missing(:-, other)
else
@ -224,7 +231,7 @@ module ActiveSupport
def advance(options)
# If we're advancing a value of variable length (i.e., years, weeks, months, days), advance from #time,
# otherwise advance from #utc, for accuracy when moving across DST boundaries
if options.detect {|k,v| [:years, :weeks, :months, :days].include? k}
if options.values_at(:years, :weeks, :months, :days).any?
method_missing(:advance, options)
else
utc.advance(options).in_time_zone(time_zone)
@ -233,9 +240,9 @@ module ActiveSupport
%w(year mon month day mday wday yday hour min sec to_date).each do |method_name|
class_eval <<-EOV
def #{method_name}
time.#{method_name}
end
def #{method_name} # def year
time.#{method_name} # time.year
end # end
EOV
end
@ -322,7 +329,7 @@ module ActiveSupport
end
def duration_of_variable_length?(obj)
ActiveSupport::Duration === obj && obj.parts.flatten.detect {|p| [:years, :months, :days].include? p }
ActiveSupport::Duration === obj && obj.parts.any? {|p| [:years, :months, :days].include? p[0] }
end
end
end

View file

@ -288,6 +288,7 @@ module ActiveSupport
# TODO: Preload instead of lazy load for thread safety
def tzinfo
require 'tzinfo' unless defined?(TZInfo)
@tzinfo ||= TZInfo::Timezone.get(MAPPING[name])
end

View file

@ -6,17 +6,12 @@ begin
rescue Gem::LoadError
$:.unshift "#{File.dirname(__FILE__)}/vendor/builder-2.1.2"
end
require 'builder'
begin
gem 'xml-simple', '~> 1.0.11'
gem 'memcache-client', '>= 1.7.4'
rescue Gem::LoadError
$:.unshift "#{File.dirname(__FILE__)}/vendor/xml-simple-1.0.11"
end
begin
gem 'memcache-client', '~> 1.5.1'
rescue Gem::LoadError
$:.unshift "#{File.dirname(__FILE__)}/vendor/memcache-client-1.5.1"
$:.unshift "#{File.dirname(__FILE__)}/vendor/memcache-client-1.7.4"
end
begin
@ -25,10 +20,9 @@ rescue Gem::LoadError
$:.unshift "#{File.dirname(__FILE__)}/vendor/tzinfo-0.3.12"
end
# TODO I18n gem has not been released yet
# begin
# gem 'i18n', '~> 0.0.1'
# rescue Gem::LoadError
$:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.0.1"
require 'i18n'
# end
begin
gem 'i18n', '>= 0.1.3'
rescue Gem::LoadError
$:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.1.3/lib"
end
require 'i18n'

View file

@ -0,0 +1,20 @@
Copyright (c) 2008 The Ruby I18n team
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,20 @@
h1. Ruby I18n gem
I18n and localization solution for Ruby.
For information please refer to http://rails-i18n.org
h2. Authors
* "Matt Aimonetti":http://railsontherun.com
* "Sven Fuchs":http://www.artweb-design.de
* "Joshua Harvey":http://www.workingwithrails.com/person/759-joshua-harvey
* "Saimon Moore":http://saimonmoore.net
* "Stephan Soller":http://www.arkanis-development.de
h2. License
MIT License. See the included MIT-LICENCE file.

Some files were not shown because too many files have changed in this diff Show more