mirror of
https://github.com/github/rails.git
synced 2026-04-26 03:00:59 -04:00
Merge commit 'mainstream/master'
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
|
||||
*2.2.0 [RC1] (October 24th, 2008)*
|
||||
|
||||
* Add layout functionality to mailers [Pratik]
|
||||
* Add layout functionality to mailers [Pratik Naik]
|
||||
|
||||
Mailer layouts behaves just like controller layouts, except layout names need to
|
||||
have '_mailer' postfix for them to be automatically picked up.
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
* Less verbose mail logging: just recipients for :info log level; the whole email for :debug only. #8000 [iaddict, Tarmo Tänav]
|
||||
|
||||
* Updated TMail to version 1.2.1 [raasdnil]
|
||||
* Updated TMail to version 1.2.1 [Mikel Lindsaar]
|
||||
|
||||
* Fixed that you don't have to call super in ActionMailer::TestCase#setup #10406 [jamesgolick]
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
*2.0.1* (December 7th, 2007)
|
||||
|
||||
* Update ActionMailer so it treats ActionView the same way that ActionController does. Closes #10244 [rick]
|
||||
* Update ActionMailer so it treats ActionView the same way that ActionController does. Closes #10244 [Rick Olson]
|
||||
|
||||
* Pass the template_root as an array as ActionView's view_path
|
||||
* Request templates with the "#{mailer_name}/#{action}" as opposed to just "#{action}"
|
||||
@@ -45,11 +45,11 @@
|
||||
|
||||
* Update README to use new smtp settings configuration API. Closes #10060 [psq]
|
||||
|
||||
* Allow ActionMailer subclasses to individually set their delivery method (so two subclasses can have different delivery methods) #10033 [zdennis]
|
||||
* Allow ActionMailer subclasses to individually set their delivery method (so two subclasses can have different delivery methods) #10033 [Zach Dennis]
|
||||
|
||||
* Update TMail to v1.1.0. Use an updated version of TMail if available. [mikel]
|
||||
* Update TMail to v1.1.0. Use an updated version of TMail if available. [Mikel Lindsaar]
|
||||
|
||||
* Introduce a new base test class for testing Mailers. ActionMailer::TestCase [Koz]
|
||||
* Introduce a new base test class for testing Mailers. ActionMailer::TestCase [Michael Koziarski]
|
||||
|
||||
* Fix silent failure of rxml templates. #9879 [jstewart]
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
|
||||
*1.3.2* (February 5th, 2007)
|
||||
|
||||
* Deprecate server_settings renaming it to smtp_settings, add sendmail_settings to allow you to override the arguments to and location of the sendmail executable. [Koz]
|
||||
* Deprecate server_settings renaming it to smtp_settings, add sendmail_settings to allow you to override the arguments to and location of the sendmail executable. [Michael Koziarski]
|
||||
|
||||
|
||||
*1.3.1* (January 16th, 2007)
|
||||
@@ -104,7 +104,7 @@
|
||||
|
||||
* Tighten rescue clauses. #5985 [james@grayproductions.net]
|
||||
|
||||
* Automatically included ActionController::UrlWriter, such that URL generation can happen within ActionMailer controllers. [DHH]
|
||||
* Automatically included ActionController::UrlWriter, such that URL generation can happen within ActionMailer controllers. [David Heinemeier Hansson]
|
||||
|
||||
* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -56,6 +56,7 @@ module ActionController
|
||||
autoload :Integration, 'action_controller/integration'
|
||||
autoload :IntegrationTest, 'action_controller/integration'
|
||||
autoload :Layout, 'action_controller/layout'
|
||||
autoload :Lock, 'action_controller/lock'
|
||||
autoload :MiddlewareStack, 'action_controller/middleware_stack'
|
||||
autoload :MimeResponds, 'action_controller/mime_responds'
|
||||
autoload :PolymorphicRoutes, 'action_controller/polymorphic_routes'
|
||||
|
||||
@@ -1160,13 +1160,8 @@ module ActionController #:nodoc:
|
||||
def reset_session #:doc:
|
||||
request.reset_session
|
||||
@_session = request.session
|
||||
#http://rails.lighthouseapp.com/projects/8994/tickets/1558-memory-problem-on-reset_session-in-around_filter#ticket-1558-1
|
||||
#MRI appears to have a GC related memory leak to do with the finalizer that is defined on CGI::Session
|
||||
ObjectSpace.undefine_finalizer(@_session)
|
||||
response.session = @_session
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
def render_for_file(template_path, status = nil, layout = nil, locals = {}) #:nodoc:
|
||||
logger.info("Rendering #{template_path}" + (status ? " (#{status})" : '')) if logger
|
||||
|
||||
@@ -67,6 +67,8 @@ module ActionController #:nodoc:
|
||||
cookie = @cookies[name.to_s]
|
||||
if cookie && cookie.respond_to?(:value)
|
||||
cookie.size > 1 ? cookie.value : cookie.value[0]
|
||||
else
|
||||
cookie
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@ module ActionController
|
||||
# Dispatches requests to the appropriate controller and takes care of
|
||||
# reloading the app after each request when Dependencies.load? is true.
|
||||
class Dispatcher
|
||||
@@guard = Mutex.new
|
||||
|
||||
class << self
|
||||
def define_dispatcher_callbacks(cache_classes)
|
||||
unless cache_classes
|
||||
@@ -46,40 +44,49 @@ module ActionController
|
||||
|
||||
cattr_accessor :middleware
|
||||
self.middleware = MiddlewareStack.new do |middleware|
|
||||
middleware.use "ActionController::Lock", :if => lambda {
|
||||
!ActionController::Base.allow_concurrency
|
||||
}
|
||||
middleware.use "ActionController::Failsafe"
|
||||
middleware.use "ActionController::SessionManagement::Middleware"
|
||||
|
||||
["ActionController::Session::CookieStore",
|
||||
"ActionController::Session::MemCacheStore",
|
||||
"ActiveRecord::SessionStore"].each do |store|
|
||||
middleware.use(store, ActionController::Base.session_options,
|
||||
:if => lambda {
|
||||
if session_store = ActionController::Base.session_store
|
||||
session_store.name == store
|
||||
end
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
include ActiveSupport::Callbacks
|
||||
define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch
|
||||
|
||||
# DEPRECATE: Remove arguments
|
||||
# DEPRECATE: Remove arguments, since they are only used by CGI
|
||||
def initialize(output = $stdout, request = nil, response = nil)
|
||||
@output, @request, @response = output, request, response
|
||||
@output = output
|
||||
@app = @@middleware.build(lambda { |env| self.dup._call(env) })
|
||||
end
|
||||
|
||||
def dispatch_unlocked
|
||||
def dispatch
|
||||
begin
|
||||
run_callbacks :before_dispatch
|
||||
handle_request
|
||||
controller = Routing::Routes.recognize(@request)
|
||||
controller.process(@request, @response).to_a
|
||||
rescue Exception => exception
|
||||
failsafe_rescue exception
|
||||
if controller ||= (::ApplicationController rescue Base)
|
||||
controller.process_with_exception(@request, @response, exception).to_a
|
||||
else
|
||||
raise exception
|
||||
end
|
||||
ensure
|
||||
run_callbacks :after_dispatch, :enumerator => :reverse_each
|
||||
end
|
||||
end
|
||||
|
||||
def dispatch
|
||||
if ActionController::Base.allow_concurrency
|
||||
dispatch_unlocked
|
||||
else
|
||||
@@guard.synchronize do
|
||||
dispatch_unlocked
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# DEPRECATE: Remove CGI support
|
||||
def dispatch_cgi(cgi, session_options)
|
||||
CGIHandler.dispatch_cgi(self, cgi, @output)
|
||||
@@ -118,22 +125,8 @@ module ActionController
|
||||
def checkin_connections
|
||||
# Don't return connection (and peform implicit rollback) if this request is a part of integration test
|
||||
# TODO: This callback should have direct access to env
|
||||
return if @request.key?("action_controller.test")
|
||||
return if @request.key?("rack.test")
|
||||
ActiveRecord::Base.clear_active_connections!
|
||||
end
|
||||
|
||||
protected
|
||||
def handle_request
|
||||
@controller = Routing::Routes.recognize(@request)
|
||||
@controller.process(@request, @response).out
|
||||
end
|
||||
|
||||
def failsafe_rescue(exception)
|
||||
if @controller ||= (::ApplicationController rescue Base)
|
||||
@controller.process_with_exception(@request, @response, exception).out
|
||||
else
|
||||
raise exception
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,7 +11,7 @@ module ActionController
|
||||
@app.call(env)
|
||||
rescue Exception => exception
|
||||
# Reraise exception in test environment
|
||||
if env["action_controller.test"]
|
||||
if env["rack.test"]
|
||||
raise exception
|
||||
else
|
||||
failsafe_response(exception)
|
||||
|
||||
@@ -276,6 +276,7 @@ module ActionController
|
||||
"SCRIPT_NAME" => "",
|
||||
|
||||
"REQUEST_URI" => path,
|
||||
"PATH_INFO" => path,
|
||||
"HTTP_HOST" => host,
|
||||
"REMOTE_ADDR" => remote_addr,
|
||||
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
|
||||
@@ -290,7 +291,7 @@ module ActionController
|
||||
"rack.multiprocess" => true,
|
||||
"rack.run_once" => false,
|
||||
|
||||
"action_controller.test" => true
|
||||
"rack.test" => true
|
||||
)
|
||||
|
||||
(headers || {}).each do |key, value|
|
||||
@@ -310,16 +311,6 @@ module ActionController
|
||||
status, headers, body = app.call(env)
|
||||
@request_count += 1
|
||||
|
||||
if @controller = ActionController::Base.last_instantiation
|
||||
@request = @controller.request
|
||||
@response = @controller.response
|
||||
|
||||
# Decorate the response with the standard behavior of the
|
||||
# TestResponse so that things like assert_response can be
|
||||
# used in integration tests.
|
||||
@response.extend(TestResponseBehavior)
|
||||
end
|
||||
|
||||
@html_document = nil
|
||||
|
||||
@status = status.to_i
|
||||
@@ -335,6 +326,22 @@ module ActionController
|
||||
@body = ""
|
||||
body.each { |part| @body << part }
|
||||
|
||||
if @controller = ActionController::Base.last_instantiation
|
||||
@request = @controller.request
|
||||
@response = @controller.response
|
||||
else
|
||||
# Decorate responses from Rack Middleware and Rails Metal
|
||||
# as an AbstractResponse for the purposes of integration testing
|
||||
@response = AbstractResponse.new
|
||||
@response.headers = @headers.merge('Status' => status.to_s)
|
||||
@response.body = @body
|
||||
end
|
||||
|
||||
# Decorate the response with the standard behavior of the
|
||||
# TestResponse so that things like assert_response can be
|
||||
# used in integration tests.
|
||||
@response.extend(TestResponseBehavior)
|
||||
|
||||
return @status
|
||||
rescue MultiPartNeededException
|
||||
boundary = "----------XnJLe9ZIbbGUYtzPQJ16u1"
|
||||
|
||||
16
actionpack/lib/action_controller/lock.rb
Normal file
16
actionpack/lib/action_controller/lock.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
module ActionController
|
||||
class Lock
|
||||
FLAG = 'rack.multithread'.freeze
|
||||
|
||||
def initialize(app, lock = Mutex.new)
|
||||
@app, @lock = app, lock
|
||||
end
|
||||
|
||||
def call(env)
|
||||
old, env[FLAG] = env[FLAG], false
|
||||
@lock.synchronize { @app.call(env) }
|
||||
ensure
|
||||
env[FLAG] = old
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,19 +1,39 @@
|
||||
module ActionController
|
||||
class MiddlewareStack < Array
|
||||
class Middleware
|
||||
attr_reader :klass, :args, :block
|
||||
attr_reader :args, :block
|
||||
|
||||
def initialize(klass, *args, &block)
|
||||
if klass.is_a?(Class)
|
||||
@klass = klass
|
||||
@klass = klass
|
||||
|
||||
options = args.extract_options!
|
||||
if options.has_key?(:if)
|
||||
@conditional = options.delete(:if)
|
||||
else
|
||||
@klass = klass.to_s.constantize
|
||||
@conditional = true
|
||||
end
|
||||
args << options unless options.empty?
|
||||
|
||||
@args = args
|
||||
@block = block
|
||||
end
|
||||
|
||||
def klass
|
||||
if @klass.is_a?(Class)
|
||||
@klass
|
||||
else
|
||||
@klass.to_s.constantize
|
||||
end
|
||||
end
|
||||
|
||||
def active?
|
||||
if @conditional.respond_to?(:call)
|
||||
@conditional.call
|
||||
else
|
||||
@conditional
|
||||
end
|
||||
end
|
||||
|
||||
def ==(middleware)
|
||||
case middleware
|
||||
when Middleware
|
||||
@@ -50,8 +70,12 @@ module ActionController
|
||||
push(middleware)
|
||||
end
|
||||
|
||||
def active
|
||||
find_all { |middleware| middleware.active? }
|
||||
end
|
||||
|
||||
def build(app)
|
||||
reverse.inject(app) { |a, e| e.build(a) }
|
||||
active.reverse.inject(app) { |a, e| e.build(a) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -83,11 +83,7 @@ module ActionController #:nodoc:
|
||||
@status || super
|
||||
end
|
||||
|
||||
def out(&block)
|
||||
# Nasty hack because CGI sessions are closed after the normal
|
||||
# prepare! statement
|
||||
set_cookies!
|
||||
|
||||
def to_a(&block)
|
||||
@block = block
|
||||
@status = headers.delete("Status")
|
||||
if [204, 304].include?(status.to_i)
|
||||
@@ -97,7 +93,6 @@ module ActionController #:nodoc:
|
||||
[status, headers.to_hash, self]
|
||||
end
|
||||
end
|
||||
alias to_a out
|
||||
|
||||
def each(&callback)
|
||||
if @body.respond_to?(:call)
|
||||
@@ -132,7 +127,7 @@ module ActionController #:nodoc:
|
||||
convert_language!
|
||||
convert_expires!
|
||||
set_status!
|
||||
# set_cookies!
|
||||
set_cookies!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -104,7 +104,7 @@ module ActionController #:nodoc:
|
||||
status = interpret_status(status_code)
|
||||
path = "#{Rails.public_path}/#{status[0,3]}.html"
|
||||
if File.exist?(path)
|
||||
render :file => path, :status => status
|
||||
render :file => path, :status => status, :content_type => Mime::HTML
|
||||
else
|
||||
head status
|
||||
end
|
||||
|
||||
@@ -56,7 +56,7 @@ module ActionController
|
||||
result = recognize_optimized(path, environment) and return result
|
||||
|
||||
# Route was not recognized. Try to find out why (maybe wrong verb).
|
||||
allows = HTTP_METHODS.select { |verb| routes.find { |r| r.recognize(path, :method => verb) } }
|
||||
allows = HTTP_METHODS.select { |verb| routes.find { |r| r.recognize(path, environment.merge(:method => verb)) } }
|
||||
|
||||
if environment[:method] && !HTTP_METHODS.include?(environment[:method])
|
||||
raise NotImplemented.new(*allows)
|
||||
|
||||
@@ -11,6 +11,7 @@ module ActionController
|
||||
|
||||
class SessionHash < Hash
|
||||
def initialize(by, env)
|
||||
super()
|
||||
@by = by
|
||||
@env = env
|
||||
@loaded = false
|
||||
@@ -21,6 +22,13 @@ module ActionController
|
||||
@id
|
||||
end
|
||||
|
||||
def session_id
|
||||
ActiveSupport::Deprecation.warn(
|
||||
"ActionController::Session::AbstractStore::SessionHash#session_id" +
|
||||
"has been deprecated.Please use #id instead.", caller)
|
||||
id
|
||||
end
|
||||
|
||||
def [](key)
|
||||
load! unless @loaded
|
||||
super
|
||||
@@ -37,6 +45,13 @@ module ActionController
|
||||
h
|
||||
end
|
||||
|
||||
def data
|
||||
ActiveSupport::Deprecation.warn(
|
||||
"ActionController::Session::AbstractStore::SessionHash#data" +
|
||||
"has been deprecated.Please use #to_hash instead.", caller)
|
||||
to_hash
|
||||
end
|
||||
|
||||
private
|
||||
def load!
|
||||
@id, session = @by.send(:load_session, @env)
|
||||
@@ -46,7 +61,7 @@ module ActionController
|
||||
end
|
||||
|
||||
DEFAULT_OPTIONS = {
|
||||
:key => 'rack.session',
|
||||
:key => '_session_id',
|
||||
:path => '/',
|
||||
:domain => nil,
|
||||
:expire_after => nil,
|
||||
@@ -56,6 +71,18 @@ module ActionController
|
||||
}
|
||||
|
||||
def initialize(app, options = {})
|
||||
# Process legacy CGI options
|
||||
options = options.symbolize_keys
|
||||
if options.has_key?(:session_path)
|
||||
options[:path] = options.delete(:session_path)
|
||||
end
|
||||
if options.has_key?(:session_key)
|
||||
options[:key] = options.delete(:session_key)
|
||||
end
|
||||
if options.has_key?(:session_http_only)
|
||||
options[:httponly] = options.delete(:session_http_only)
|
||||
end
|
||||
|
||||
@app = app
|
||||
@default_options = DEFAULT_OPTIONS.merge(options)
|
||||
@key = @default_options[:key]
|
||||
|
||||
@@ -41,9 +41,11 @@ module ActionController
|
||||
SECRET_MIN_LENGTH = 30 # characters
|
||||
|
||||
DEFAULT_OPTIONS = {
|
||||
:domain => nil,
|
||||
:path => "/",
|
||||
:expire_after => nil
|
||||
:key => '_session_id',
|
||||
:domain => nil,
|
||||
:path => "/",
|
||||
:expire_after => nil,
|
||||
:httponly => false
|
||||
}.freeze
|
||||
|
||||
ENV_SESSION_KEY = "rack.session".freeze
|
||||
@@ -56,6 +58,18 @@ module ActionController
|
||||
def initialize(app, options = {})
|
||||
options = options.dup
|
||||
|
||||
# Process legacy CGI options
|
||||
options = options.symbolize_keys
|
||||
if options.has_key?(:session_path)
|
||||
options[:path] = options.delete(:session_path)
|
||||
end
|
||||
if options.has_key?(:session_key)
|
||||
options[:key] = options.delete(:session_key)
|
||||
end
|
||||
if options.has_key?(:session_http_only)
|
||||
options[:httponly] = options.delete(:session_http_only)
|
||||
end
|
||||
|
||||
@app = app
|
||||
|
||||
# The session_key option is required.
|
||||
@@ -74,21 +88,12 @@ module ActionController
|
||||
freeze
|
||||
end
|
||||
|
||||
class SessionHash < AbstractStore::SessionHash
|
||||
private
|
||||
def load!
|
||||
session = @by.send(:load_session, @env)
|
||||
replace(session)
|
||||
@loaded = true
|
||||
end
|
||||
end
|
||||
|
||||
def call(env)
|
||||
session_data = SessionHash.new(self, env)
|
||||
session_data = AbstractStore::SessionHash.new(self, env)
|
||||
original_value = session_data.dup
|
||||
|
||||
env[ENV_SESSION_KEY] = session_data
|
||||
env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup
|
||||
env[ENV_SESSION_OPTIONS_KEY] = @default_options
|
||||
|
||||
status, headers, body = @app.call(env)
|
||||
|
||||
@@ -142,17 +147,18 @@ module ActionController
|
||||
def load_session(env)
|
||||
request = Rack::Request.new(env)
|
||||
session_data = request.cookies[@key]
|
||||
unmarshal(session_data) || {}
|
||||
data = unmarshal(session_data) || persistent_session_id!({})
|
||||
[data[:session_id], data]
|
||||
end
|
||||
|
||||
# Marshal a session hash into safe cookie data. Include an integrity hash.
|
||||
def marshal(session)
|
||||
@verifier.generate(session)
|
||||
@verifier.generate( persistent_session_id!(session))
|
||||
end
|
||||
|
||||
# Unmarshal cookie data to a hash and verify its integrity.
|
||||
def unmarshal(cookie)
|
||||
@verifier.verify(cookie) if cookie
|
||||
persistent_session_id!(@verifier.verify(cookie)) if cookie
|
||||
rescue ActiveSupport::MessageVerifier::InvalidSignature
|
||||
nil
|
||||
end
|
||||
@@ -195,6 +201,26 @@ module ActionController
|
||||
key = secret.respond_to?(:call) ? secret.call : secret
|
||||
ActiveSupport::MessageVerifier.new(key, digest)
|
||||
end
|
||||
|
||||
def generate_sid
|
||||
ActiveSupport::SecureRandom.hex(16)
|
||||
end
|
||||
|
||||
def persistent_session_id!(data)
|
||||
(data ||= {}).merge!(inject_persistent_session_id(data))
|
||||
end
|
||||
|
||||
def inject_persistent_session_id(data)
|
||||
requires_session_id?(data) ? { :session_id => generate_sid } : {}
|
||||
end
|
||||
|
||||
def requires_session_id?(data)
|
||||
if data
|
||||
data.respond_to?(:key?) && !data.key?(:session_id)
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,35 +6,6 @@ module ActionController #:nodoc:
|
||||
end
|
||||
end
|
||||
|
||||
class Middleware
|
||||
DEFAULT_OPTIONS = {
|
||||
:path => "/",
|
||||
:key => "_session_id",
|
||||
:httponly => true,
|
||||
}.freeze
|
||||
|
||||
def self.new(app)
|
||||
cgi_options = ActionController::Base.session_options
|
||||
options = cgi_options.symbolize_keys
|
||||
options = DEFAULT_OPTIONS.merge(options)
|
||||
if options.has_key?(:session_path)
|
||||
options[:path] = options.delete(:session_path)
|
||||
end
|
||||
if options.has_key?(:session_key)
|
||||
options[:key] = options.delete(:session_key)
|
||||
end
|
||||
if options.has_key?(:session_http_only)
|
||||
options[:httponly] = options.delete(:session_http_only)
|
||||
end
|
||||
|
||||
if store = ActionController::Base.session_store
|
||||
store.new(app, options)
|
||||
else # Sessions disabled
|
||||
lambda { |env| app.call(env) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Set the session store to be used for keeping the session data between requests.
|
||||
# By default, sessions are stored in browser cookies (<tt>:cookie_store</tt>),
|
||||
|
||||
@@ -98,6 +98,10 @@ module ActionView #:nodoc:
|
||||
end
|
||||
|
||||
private
|
||||
def valid_extension?(extension)
|
||||
Template.template_handler_extensions.include?(extension)
|
||||
end
|
||||
|
||||
def find_full_path(path, load_paths)
|
||||
load_paths = Array(load_paths) + [nil]
|
||||
load_paths.each do |load_path|
|
||||
@@ -111,11 +115,11 @@ module ActionView #:nodoc:
|
||||
# [base_path, name, format, extension]
|
||||
def split(file)
|
||||
if m = file.match(/^(.*\/)?([^\.]+)\.?(\w+)?\.?(\w+)?\.?(\w+)?$/)
|
||||
if Template.valid_extension?(m[5]) # Multipart formats
|
||||
if valid_extension?(m[5]) # Multipart formats
|
||||
[m[1], m[2], "#{m[3]}.#{m[4]}", m[5]]
|
||||
elsif Template.valid_extension?(m[4]) # Single format
|
||||
elsif valid_extension?(m[4]) # Single format
|
||||
[m[1], m[2], m[3], m[4]]
|
||||
elsif Template.valid_extension?(m[3]) # No format
|
||||
elsif valid_extension?(m[3]) # No format
|
||||
[m[1], m[2], nil, m[3]]
|
||||
else # No extension
|
||||
[m[1], m[2], m[3], nil]
|
||||
|
||||
@@ -28,10 +28,6 @@ module ActionView #:nodoc:
|
||||
@@template_handlers[extension.to_sym] = klass
|
||||
end
|
||||
|
||||
def valid_extension?(extension)
|
||||
template_handler_extensions.include?(extension) || init_path_for_extension(extension)
|
||||
end
|
||||
|
||||
def template_handler_extensions
|
||||
@@template_handlers.keys.map(&:to_s).sort
|
||||
end
|
||||
@@ -42,26 +38,7 @@ module ActionView #:nodoc:
|
||||
end
|
||||
|
||||
def handler_class_for_extension(extension)
|
||||
(extension && @@template_handlers[extension.to_sym] || autoload_handler_class(extension)) ||
|
||||
@@default_template_handlers
|
||||
(extension && @@template_handlers[extension.to_sym]) || @@default_template_handlers
|
||||
end
|
||||
|
||||
private
|
||||
def autoload_handler_class(extension)
|
||||
return if Gem.loaded_specs[extension]
|
||||
return unless init_path = init_path_for_extension(extension)
|
||||
Gem.activate(extension)
|
||||
load(init_path)
|
||||
handler_class_for_extension(extension)
|
||||
end
|
||||
|
||||
# Returns the path to the rails/init.rb file for the given extension,
|
||||
# or nil if no gem provides it.
|
||||
def init_path_for_extension(extension)
|
||||
return unless spec = Gem.searcher.find(extension.to_s)
|
||||
returning File.join(spec.full_gem_path, 'rails', 'init.rb') do |path|
|
||||
return unless File.file?(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -50,7 +50,7 @@ class DispatcherTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_failsafe_response
|
||||
Dispatcher.any_instance.expects(:dispatch_unlocked).raises('b00m')
|
||||
Dispatcher.any_instance.expects(:dispatch).raises('b00m')
|
||||
ActionController::Failsafe.any_instance.expects(:log_failsafe_exception)
|
||||
|
||||
assert_nothing_raised do
|
||||
@@ -96,7 +96,9 @@ class DispatcherTest < Test::Unit::TestCase
|
||||
|
||||
private
|
||||
def dispatch(cache_classes = true)
|
||||
Dispatcher.any_instance.stubs(:handle_request).returns([200, {}, 'response'])
|
||||
controller = mock()
|
||||
controller.stubs(:process).returns([200, {}, 'response'])
|
||||
ActionController::Routing::Routes.stubs(:recognize).returns(controller)
|
||||
Dispatcher.define_dispatcher_callbacks(cache_classes)
|
||||
@dispatcher.call({})
|
||||
end
|
||||
|
||||
@@ -373,4 +373,35 @@ class IntegrationProcessTest < ActionController::IntegrationTest
|
||||
end
|
||||
end
|
||||
|
||||
class MetalTest < ActionController::IntegrationTest
|
||||
class Poller
|
||||
def self.call(env)
|
||||
if env["PATH_INFO"] =~ /^\/success/
|
||||
[200, {"Content-Type" => "text/plain"}, "Hello World!"]
|
||||
else
|
||||
[404, {"Content-Type" => "text/plain"}, '']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
@integration_session = ActionController::Integration::Session.new(Poller)
|
||||
end
|
||||
|
||||
def test_successful_get
|
||||
get "/success"
|
||||
assert_response 200
|
||||
assert_response :success
|
||||
assert_response :ok
|
||||
assert_equal "Hello World!", response.body
|
||||
end
|
||||
|
||||
def test_failed_get
|
||||
get "/failure"
|
||||
assert_response 404
|
||||
assert_response :not_found
|
||||
assert_equal '', response.body
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -236,7 +236,7 @@ class RackResponseTest < BaseRackTest
|
||||
@response.body = "Hello, World!"
|
||||
@response.prepare!
|
||||
|
||||
status, headers, body = @response.out
|
||||
status, headers, body = @response.to_a
|
||||
assert_equal "200 OK", status
|
||||
assert_equal({
|
||||
"Content-Type" => "text/html; charset=utf-8",
|
||||
@@ -257,7 +257,7 @@ class RackResponseTest < BaseRackTest
|
||||
end
|
||||
@response.prepare!
|
||||
|
||||
status, headers, body = @response.out
|
||||
status, headers, body = @response.to_a
|
||||
assert_equal "200 OK", status
|
||||
assert_equal({"Content-Type" => "text/html; charset=utf-8", "Cache-Control" => "no-cache", "Set-Cookie" => []}, headers)
|
||||
|
||||
@@ -293,6 +293,6 @@ class RackResponseHeadersTest < BaseRackTest
|
||||
private
|
||||
def response_headers
|
||||
@response.prepare!
|
||||
@response.out[1]
|
||||
@response.to_a[1]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,6 +9,8 @@ class CookieStoreTest < ActionController::IntegrationTest
|
||||
CookieStoreApp = ActionController::Session::CookieStore.new(DispatcherApp,
|
||||
:key => SessionKey, :secret => SessionSecret)
|
||||
|
||||
Verifier = ActiveSupport::MessageVerifier.new(SessionSecret, 'SHA1')
|
||||
|
||||
SignedBar = "BAh7BjoIZm9vIghiYXI%3D--" +
|
||||
"fef868465920f415f2c0652d6910d3af288a0367"
|
||||
|
||||
@@ -17,9 +19,13 @@ class CookieStoreTest < ActionController::IntegrationTest
|
||||
head :ok
|
||||
end
|
||||
|
||||
def persistent_session_id
|
||||
render :text => session[:session_id]
|
||||
end
|
||||
|
||||
def set_session_value
|
||||
session[:foo] = "bar"
|
||||
head :ok
|
||||
render :text => Marshal.dump(session.to_hash)
|
||||
end
|
||||
|
||||
def get_session_value
|
||||
@@ -83,7 +89,8 @@ class CookieStoreTest < ActionController::IntegrationTest
|
||||
with_test_route_set do
|
||||
get '/set_session_value'
|
||||
assert_response :success
|
||||
assert_equal ["_myapp_session=#{SignedBar}; path=/"],
|
||||
session_payload = Verifier.generate( Marshal.load(response.body) )
|
||||
assert_equal ["_myapp_session=#{session_payload}; path=/"],
|
||||
headers['Set-Cookie']
|
||||
end
|
||||
end
|
||||
@@ -132,6 +139,21 @@ class CookieStoreTest < ActionController::IntegrationTest
|
||||
end
|
||||
end
|
||||
|
||||
def test_persistent_session_id
|
||||
with_test_route_set do
|
||||
cookies[SessionKey] = SignedBar
|
||||
get '/persistent_session_id'
|
||||
assert_response :success
|
||||
assert_equal response.body.size, 32
|
||||
session_id = response.body
|
||||
get '/persistent_session_id'
|
||||
assert_equal session_id, response.body
|
||||
reset!
|
||||
get '/persistent_session_id'
|
||||
assert_not_equal session_id, response.body
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def with_test_route_set
|
||||
with_routing do |set|
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
# require 'abstract_unit'
|
||||
#
|
||||
# class SessionFixationTest < ActionController::IntegrationTest
|
||||
# class TestController < ActionController::Base
|
||||
# session :session_key => '_myapp_session_id',
|
||||
# :secret => CGI::Session.generate_unique_id,
|
||||
# :except => :default_session_key
|
||||
#
|
||||
# session :cookie_only => false,
|
||||
# :only => :allow_session_fixation
|
||||
#
|
||||
# def default_session_key
|
||||
# render :text => "default_session_key"
|
||||
# end
|
||||
#
|
||||
# def custom_session_key
|
||||
# render :text => "custom_session_key: #{params[:id]}"
|
||||
# end
|
||||
#
|
||||
# def allow_session_fixation
|
||||
# render :text => "allow_session_fixation"
|
||||
# end
|
||||
#
|
||||
# def rescue_action(e) raise end
|
||||
# end
|
||||
#
|
||||
# def setup
|
||||
# @controller = TestController.new
|
||||
# end
|
||||
#
|
||||
# def test_should_be_able_to_make_a_successful_request
|
||||
# with_test_route_set do
|
||||
# assert_nothing_raised do
|
||||
# get '/custom_session_key', :id => "1"
|
||||
# end
|
||||
# assert_equal 'custom_session_key: 1', @controller.response.body
|
||||
# assert_not_nil @controller.session
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# def test_should_catch_session_fixation_attempt
|
||||
# with_test_route_set do
|
||||
# assert_raises(ActionController::RackRequest::SessionFixationAttempt) do
|
||||
# get '/custom_session_key', :_myapp_session_id => "42"
|
||||
# end
|
||||
# assert_nil @controller.session
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# def test_should_not_catch_session_fixation_attempt_when_cookie_only_setting_is_disabled
|
||||
# with_test_route_set do
|
||||
# assert_nothing_raised do
|
||||
# get '/allow_session_fixation', :_myapp_session_id => "42"
|
||||
# end
|
||||
# assert !@controller.response.body.blank?
|
||||
# assert_not_nil @controller.session
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# def test_should_catch_session_fixation_attempt_with_default_session_key
|
||||
# # using the default session_key is not possible with cookie store
|
||||
# ActionController::Base.session_store = :p_store
|
||||
#
|
||||
# with_test_route_set do
|
||||
# assert_raises ActionController::RackRequest::SessionFixationAttempt do
|
||||
# get '/default_session_key', :_session_id => "42"
|
||||
# end
|
||||
# assert_nil @controller.response
|
||||
# assert_nil @controller.session
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# private
|
||||
# def with_test_route_set
|
||||
# with_routing do |set|
|
||||
# set.draw do |map|
|
||||
# map.with_options :controller => "session_fixation_test/test" do |c|
|
||||
# c.connect "/:action"
|
||||
# end
|
||||
# end
|
||||
# yield
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -204,9 +204,18 @@ module ActiveRecord
|
||||
unless through_records.empty?
|
||||
source = reflection.source_reflection.name
|
||||
through_records.first.class.preload_associations(through_records, source)
|
||||
through_records.each do |through_record|
|
||||
add_preloaded_record_to_collection(id_to_record_map[through_record[through_primary_key].to_s],
|
||||
reflection.name, through_record.send(source))
|
||||
if through_reflection.macro == :belongs_to
|
||||
rev_id_to_record_map, rev_ids = construct_id_map(records, through_primary_key)
|
||||
rev_primary_key = through_reflection.klass.primary_key
|
||||
through_records.each do |through_record|
|
||||
add_preloaded_record_to_collection(rev_id_to_record_map[through_record[rev_primary_key].to_s],
|
||||
reflection.name, through_record.send(source))
|
||||
end
|
||||
else
|
||||
through_records.each do |through_record|
|
||||
add_preloaded_record_to_collection(id_to_record_map[through_record[through_primary_key].to_s],
|
||||
reflection.name, through_record.send(source))
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
@@ -307,6 +316,7 @@ module ActiveRecord
|
||||
|
||||
klasses_and_ids.each do |klass_and_id|
|
||||
klass_name, id_map = *klass_and_id
|
||||
next if id_map.empty?
|
||||
klass = klass_name.constantize
|
||||
|
||||
table_name = klass.quoted_table_name
|
||||
|
||||
@@ -1731,6 +1731,11 @@ module ActiveRecord
|
||||
return sanitize_sql(sql)
|
||||
end
|
||||
|
||||
def tables_in_string(string)
|
||||
return [] if string.blank?
|
||||
string.scan(/([\.a-zA-Z_]+).?\./).flatten
|
||||
end
|
||||
|
||||
def conditions_tables(options)
|
||||
# look in both sets of conditions
|
||||
conditions = [scope(:find, :conditions), options[:conditions]].inject([]) do |all, cond|
|
||||
@@ -1741,37 +1746,55 @@ module ActiveRecord
|
||||
else all << cond
|
||||
end
|
||||
end
|
||||
conditions.join(' ').scan(/([\.a-zA-Z_]+).?\./).flatten
|
||||
tables_in_string(conditions.join(' '))
|
||||
end
|
||||
|
||||
def order_tables(options)
|
||||
order = [options[:order], scope(:find, :order) ].join(", ")
|
||||
return [] unless order && order.is_a?(String)
|
||||
order.scan(/([\.a-zA-Z_]+).?\./).flatten
|
||||
tables_in_string(order)
|
||||
end
|
||||
|
||||
def selects_tables(options)
|
||||
select = options[:select]
|
||||
return [] unless select && select.is_a?(String)
|
||||
select.scan(/"?([\.a-zA-Z_]+)"?.?\./).flatten
|
||||
tables_in_string(select)
|
||||
end
|
||||
|
||||
def joined_tables(options)
|
||||
scope = scope(:find)
|
||||
joins = options[:joins]
|
||||
merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins])
|
||||
[table_name] + case merged_joins
|
||||
when Symbol, Hash, Array
|
||||
if array_of_strings?(merged_joins)
|
||||
tables_in_string(merged_joins.join(' '))
|
||||
else
|
||||
join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil)
|
||||
join_dependency.join_associations.collect {|join_association| [join_association.aliased_join_table_name, join_association.aliased_table_name]}.flatten.compact
|
||||
end
|
||||
else
|
||||
tables_in_string(merged_joins)
|
||||
end
|
||||
end
|
||||
|
||||
# Checks if the conditions reference a table other than the current model table
|
||||
def include_eager_conditions?(options, tables = nil)
|
||||
((tables || conditions_tables(options)) - [table_name]).any?
|
||||
def include_eager_conditions?(options, tables = nil, joined_tables = nil)
|
||||
((tables || conditions_tables(options)) - (joined_tables || joined_tables(options))).any?
|
||||
end
|
||||
|
||||
# Checks if the query order references a table other than the current model's table.
|
||||
def include_eager_order?(options, tables = nil)
|
||||
((tables || order_tables(options)) - [table_name]).any?
|
||||
def include_eager_order?(options, tables = nil, joined_tables = nil)
|
||||
((tables || order_tables(options)) - (joined_tables || joined_tables(options))).any?
|
||||
end
|
||||
|
||||
def include_eager_select?(options)
|
||||
(selects_tables(options) - [table_name]).any?
|
||||
def include_eager_select?(options, joined_tables = nil)
|
||||
(selects_tables(options) - (joined_tables || joined_tables(options))).any?
|
||||
end
|
||||
|
||||
def references_eager_loaded_tables?(options)
|
||||
include_eager_order?(options) || include_eager_conditions?(options) || include_eager_select?(options)
|
||||
joined_tables = joined_tables(options)
|
||||
include_eager_order?(options, nil, joined_tables) || include_eager_conditions?(options, nil, joined_tables) || include_eager_select?(options, joined_tables)
|
||||
end
|
||||
|
||||
def using_limitable_reflections?(reflections)
|
||||
|
||||
@@ -83,7 +83,11 @@ module ActiveRecord
|
||||
|
||||
def to_ary
|
||||
load_target
|
||||
@target.to_ary
|
||||
if @target.is_a?(Array)
|
||||
@target.to_ary
|
||||
else
|
||||
Array(@target)
|
||||
end
|
||||
end
|
||||
|
||||
def reset
|
||||
|
||||
@@ -1828,7 +1828,7 @@ module ActiveRecord #:nodoc:
|
||||
else
|
||||
find(:#{finder}, options.merge(finder_options))
|
||||
end
|
||||
#{'result || raise(RecordNotFound)' if bang}
|
||||
#{'result || raise(RecordNotFound, "Couldn\'t find #{name} with #{attributes.to_a.collect {|pair| "#{pair.first} = #{pair.second}"}.join(\', \')}")' if bang}
|
||||
end
|
||||
}, __FILE__, __LINE__
|
||||
send(method_id, *arguments)
|
||||
|
||||
@@ -308,6 +308,7 @@ module ActiveRecord
|
||||
rows
|
||||
end
|
||||
|
||||
# Executes a SQL query and returns a MySQL::Result object. Note that you have to free the Result object after you're done using it.
|
||||
def execute(sql, name = nil) #:nodoc:
|
||||
log(sql, name) { @connection.query(sql) }
|
||||
rescue ActiveRecord::StatementInvalid => exception
|
||||
@@ -414,7 +415,9 @@ module ActiveRecord
|
||||
|
||||
def tables(name = nil) #:nodoc:
|
||||
tables = []
|
||||
execute("SHOW TABLES", name).each { |field| tables << field[0] }
|
||||
result = execute("SHOW TABLES", name)
|
||||
result.each { |field| tables << field[0] }
|
||||
result.free
|
||||
tables
|
||||
end
|
||||
|
||||
@@ -425,7 +428,8 @@ module ActiveRecord
|
||||
def indexes(table_name, name = nil)#:nodoc:
|
||||
indexes = []
|
||||
current_index = nil
|
||||
execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name).each do |row|
|
||||
result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name)
|
||||
result.each do |row|
|
||||
if current_index != row[2]
|
||||
next if row[2] == "PRIMARY" # skip the primary key
|
||||
current_index = row[2]
|
||||
@@ -434,13 +438,16 @@ module ActiveRecord
|
||||
|
||||
indexes.last.columns << row[4]
|
||||
end
|
||||
result.free
|
||||
indexes
|
||||
end
|
||||
|
||||
def columns(table_name, name = nil)#:nodoc:
|
||||
sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
|
||||
columns = []
|
||||
execute(sql, name).each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
|
||||
result = execute(sql, name)
|
||||
result.each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
|
||||
result.free
|
||||
columns
|
||||
end
|
||||
|
||||
@@ -521,9 +528,11 @@ module ActiveRecord
|
||||
# Returns a table's primary key and belonging sequence.
|
||||
def pk_and_sequence_for(table) #:nodoc:
|
||||
keys = []
|
||||
execute("describe #{quote_table_name(table)}").each_hash do |h|
|
||||
result = execute("describe #{quote_table_name(table)}")
|
||||
result.each_hash do |h|
|
||||
keys << h["Field"]if h["Key"] == "PRI"
|
||||
end
|
||||
result.free
|
||||
keys.length == 1 ? [keys.first, nil] : nil
|
||||
end
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
require "cases/helper"
|
||||
require 'models/post'
|
||||
require 'models/tagging'
|
||||
require 'models/tag'
|
||||
require 'models/comment'
|
||||
require 'models/author'
|
||||
require 'models/category'
|
||||
@@ -145,7 +146,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
|
||||
def test_finding_with_includes_on_null_belongs_to_association_with_same_include_includes_only_once
|
||||
post = posts(:welcome)
|
||||
post.update_attributes!(:author => nil)
|
||||
post = assert_queries(2) { Post.find(post.id, :include => {:author_with_address => :author_address}) } # find the post, then find the author which is null so no query for the address
|
||||
post = assert_queries(1) { Post.find(post.id, :include => {:author_with_address => :author_address}) } # find the post, then find the author which is null so no query for the author or address
|
||||
assert_no_queries do
|
||||
assert_equal nil, post.author_with_address
|
||||
end
|
||||
@@ -705,4 +706,69 @@ class EagerAssociationTest < ActiveRecord::TestCase
|
||||
def test_order_on_join_table_with_include_and_limit
|
||||
assert_equal 5, Developer.find(:all, :include => 'projects', :order => 'developers_projects.joined_on DESC', :limit => 5).size
|
||||
end
|
||||
|
||||
def test_eager_loading_with_order_on_joined_table_preloads
|
||||
posts = assert_queries(2) do
|
||||
Post.find(:all, :joins => :comments, :include => :author, :order => 'comments.id DESC')
|
||||
end
|
||||
assert_equal posts(:eager_other), posts[0]
|
||||
assert_equal authors(:mary), assert_no_queries { posts[0].author}
|
||||
end
|
||||
|
||||
def test_eager_loading_with_conditions_on_joined_table_preloads
|
||||
posts = assert_queries(2) do
|
||||
Post.find(:all, :select => 'distinct posts.*', :include => :author, :joins => [:comments], :conditions => "comments.body like 'Thank you%'", :order => 'posts.id')
|
||||
end
|
||||
assert_equal [posts(:welcome)], posts
|
||||
assert_equal authors(:david), assert_no_queries { posts[0].author}
|
||||
|
||||
posts = assert_queries(2) do
|
||||
Post.find(:all, :select => 'distinct posts.*', :include => :author, :joins => [:comments], :conditions => "comments.body like 'Thank you%'", :order => 'posts.id')
|
||||
end
|
||||
assert_equal [posts(:welcome)], posts
|
||||
assert_equal authors(:david), assert_no_queries { posts[0].author}
|
||||
|
||||
posts = assert_queries(2) do
|
||||
Post.find(:all, :include => :author, :joins => {:taggings => :tag}, :conditions => "tags.name = 'General'")
|
||||
end
|
||||
assert_equal posts(:welcome, :thinking), posts
|
||||
|
||||
posts = assert_queries(2) do
|
||||
Post.find(:all, :include => :author, :joins => {:taggings => {:tag => :taggings}}, :conditions => "taggings_tags.super_tag_id=2")
|
||||
end
|
||||
assert_equal posts(:welcome, :thinking), posts
|
||||
|
||||
end
|
||||
|
||||
def test_eager_loading_with_conditions_on_string_joined_table_preloads
|
||||
posts = assert_queries(2) do
|
||||
Post.find(:all, :select => 'distinct posts.*', :include => :author, :joins => "INNER JOIN comments on comments.post_id = posts.id", :conditions => "comments.body like 'Thank you%'", :order => 'posts.id')
|
||||
end
|
||||
assert_equal [posts(:welcome)], posts
|
||||
assert_equal authors(:david), assert_no_queries { posts[0].author}
|
||||
|
||||
posts = assert_queries(2) do
|
||||
Post.find(:all, :select => 'distinct posts.*', :include => :author, :joins => ["INNER JOIN comments on comments.post_id = posts.id"], :conditions => "comments.body like 'Thank you%'", :order => 'posts.id')
|
||||
end
|
||||
assert_equal [posts(:welcome)], posts
|
||||
assert_equal authors(:david), assert_no_queries { posts[0].author}
|
||||
|
||||
end
|
||||
|
||||
def test_eager_loading_with_select_on_joined_table_preloads
|
||||
posts = assert_queries(2) do
|
||||
Post.find(:all, :select => 'posts.*, authors.name as author_name', :include => :comments, :joins => :author, :order => 'posts.id')
|
||||
end
|
||||
assert_equal 'David', posts[0].author_name
|
||||
assert_equal posts(:welcome).comments, assert_no_queries { posts[0].comments}
|
||||
end
|
||||
|
||||
def test_eager_loading_with_conditions_on_join_model_preloads
|
||||
authors = assert_queries(2) do
|
||||
Author.find(:all, :include => :author_address, :joins => :comments, :conditions => "posts.title like 'Welcome%'")
|
||||
end
|
||||
assert_equal authors(:david), authors[0]
|
||||
assert_equal author_addresses(:david_address), authors[0].author_address
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -3,6 +3,9 @@ require 'models/post'
|
||||
require 'models/person'
|
||||
require 'models/reader'
|
||||
require 'models/comment'
|
||||
require 'models/tag'
|
||||
require 'models/tagging'
|
||||
require 'models/author'
|
||||
|
||||
class HasManyThroughAssociationsTest < ActiveRecord::TestCase
|
||||
fixtures :posts, :readers, :people, :comments, :authors
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
require "cases/helper"
|
||||
require 'models/club'
|
||||
require 'models/member_type'
|
||||
require 'models/member'
|
||||
require 'models/membership'
|
||||
require 'models/sponsor'
|
||||
@@ -7,7 +8,7 @@ require 'models/organization'
|
||||
require 'models/member_detail'
|
||||
|
||||
class HasOneThroughAssociationsTest < ActiveRecord::TestCase
|
||||
fixtures :members, :clubs, :memberships, :sponsors, :organizations
|
||||
fixtures :member_types, :members, :clubs, :memberships, :sponsors, :organizations
|
||||
|
||||
def setup
|
||||
@member = members(:groucho)
|
||||
@@ -158,4 +159,18 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
|
||||
assert @new_organization.members.include?(@member)
|
||||
end
|
||||
|
||||
def test_preloading_has_one_through_on_belongs_to
|
||||
assert_not_nil @member.member_type
|
||||
@organization = organizations(:nsa)
|
||||
@member_detail = MemberDetail.new
|
||||
@member.member_detail = @member_detail
|
||||
@member.organization = @organization
|
||||
@member_details = assert_queries(3) do
|
||||
MemberDetail.find(:all, :include => :member_type)
|
||||
end
|
||||
@new_detail = @member_details[0]
|
||||
assert @new_detail.loaded_member_type?
|
||||
assert_not_nil assert_no_queries { @new_detail.member_type }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -9,6 +9,8 @@ require 'active_record/test_case'
|
||||
require 'active_record/fixtures'
|
||||
require 'connection'
|
||||
|
||||
require 'cases/repair_helper'
|
||||
|
||||
# Show backtraces for deprecated behavior for quicker cleanup.
|
||||
ActiveSupport::Deprecation.debug = true
|
||||
|
||||
@@ -60,6 +62,8 @@ end
|
||||
|
||||
class ActiveSupport::TestCase
|
||||
include ActiveRecord::TestFixtures
|
||||
include ActiveRecord::Testing::RepairHelper
|
||||
|
||||
self.fixture_path = FIXTURES_ROOT
|
||||
self.use_instantiated_fixtures = false
|
||||
self.use_transactional_fixtures = true
|
||||
|
||||
50
activerecord/test/cases/repair_helper.rb
Normal file
50
activerecord/test/cases/repair_helper.rb
Normal file
@@ -0,0 +1,50 @@
|
||||
module ActiveRecord
|
||||
module Testing
|
||||
module RepairHelper
|
||||
def self.included(base)
|
||||
base.class_eval do
|
||||
extend ClassMethods
|
||||
end
|
||||
end
|
||||
|
||||
module Toolbox
|
||||
def self.record_validations(*model_classes)
|
||||
model_classes.inject({}) do |repair, klass|
|
||||
repair[klass] ||= {}
|
||||
[:validate, :validate_on_create, :validate_on_update].each do |callback|
|
||||
the_callback = klass.instance_variable_get("@#{callback.to_s}_callbacks")
|
||||
repair[klass][callback] = (the_callback.nil? ? nil : the_callback.dup)
|
||||
end
|
||||
repair
|
||||
end
|
||||
end
|
||||
|
||||
def self.reset_validations(recorded)
|
||||
recorded.each do |klass, repairs|
|
||||
[:validate, :validate_on_create, :validate_on_update].each do |callback|
|
||||
klass.instance_variable_set("@#{callback.to_s}_callbacks", repairs[callback])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def repair_validations(*model_classes)
|
||||
setup do
|
||||
@validation_repairs = ActiveRecord::Testing::RepairHelper::Toolbox.record_validations(*model_classes)
|
||||
end
|
||||
teardown do
|
||||
ActiveRecord::Testing::RepairHelper::Toolbox.reset_validations(@validation_repairs)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def repair_validations(*model_classes, &block)
|
||||
validation_repairs = ActiveRecord::Testing::RepairHelper::Toolbox.record_validations(*model_classes)
|
||||
return block.call
|
||||
ensure
|
||||
ActiveRecord::Testing::RepairHelper::Toolbox.reset_validations(validation_repairs)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -6,6 +6,8 @@ require 'models/person'
|
||||
require 'models/developer'
|
||||
require 'models/warehouse_thing'
|
||||
require 'models/guid'
|
||||
require 'models/owner'
|
||||
require 'models/pet'
|
||||
|
||||
# The following methods in Topic are used in test_conditional_validation_*
|
||||
class Topic
|
||||
@@ -31,10 +33,6 @@ class UniqueReply < Reply
|
||||
validates_uniqueness_of :content, :scope => 'parent_id'
|
||||
end
|
||||
|
||||
class PlagiarizedReply < Reply
|
||||
validates_acceptance_of :author_name
|
||||
end
|
||||
|
||||
class SillyUniqueReply < UniqueReply
|
||||
end
|
||||
|
||||
@@ -58,11 +56,9 @@ end
|
||||
class ValidationsTest < ActiveRecord::TestCase
|
||||
fixtures :topics, :developers, 'warehouse-things'
|
||||
|
||||
def setup
|
||||
Topic.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
||||
Topic.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
||||
Topic.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
||||
end
|
||||
# Most of the tests mess with the validations of Topic, so lets repair it all the time.
|
||||
# Other classes we mess with will be dealt with in the specific tests
|
||||
repair_validations(Topic)
|
||||
|
||||
def test_single_field_validation
|
||||
r = Reply.new
|
||||
@@ -134,7 +130,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
||||
Reply.create!([ { "title" => "OK" }, { "title" => "Wrong Create" }])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_exception_on_create_bang_with_block
|
||||
assert_raises(ActiveRecord::RecordInvalid) do
|
||||
Reply.create!({ "title" => "OK" }) do |r|
|
||||
@@ -142,7 +138,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_exception_on_create_bang_many_with_block
|
||||
assert_raises(ActiveRecord::RecordInvalid) do
|
||||
Reply.create!([{ "title" => "OK" }, { "title" => "Wrong Create" }]) do |r|
|
||||
@@ -229,21 +225,16 @@ class ValidationsTest < ActiveRecord::TestCase
|
||||
end
|
||||
|
||||
def test_validates_each
|
||||
perform = true
|
||||
hits = 0
|
||||
Topic.validates_each(:title, :content, [:title, :content]) do |record, attr|
|
||||
if perform
|
||||
record.errors.add attr, 'gotcha'
|
||||
hits += 1
|
||||
end
|
||||
record.errors.add attr, 'gotcha'
|
||||
hits += 1
|
||||
end
|
||||
t = Topic.new("title" => "valid", "content" => "whatever")
|
||||
assert !t.save
|
||||
assert_equal 4, hits
|
||||
assert_equal %w(gotcha gotcha), t.errors.on(:title)
|
||||
assert_equal %w(gotcha gotcha), t.errors.on(:content)
|
||||
ensure
|
||||
perform = false
|
||||
end
|
||||
|
||||
def test_no_title_confirmation
|
||||
@@ -315,8 +306,12 @@ class ValidationsTest < ActiveRecord::TestCase
|
||||
end
|
||||
|
||||
def test_validates_acceptance_of_as_database_column
|
||||
reply = PlagiarizedReply.create("author_name" => "Dan Brown")
|
||||
assert_equal "Dan Brown", reply["author_name"]
|
||||
repair_validations(Reply) do
|
||||
Reply.validates_acceptance_of(:author_name)
|
||||
|
||||
reply = Reply.create("author_name" => "Dan Brown")
|
||||
assert_equal "Dan Brown", reply["author_name"]
|
||||
end
|
||||
end
|
||||
|
||||
def test_validates_acceptance_of_with_non_existant_table
|
||||
@@ -372,22 +367,24 @@ class ValidationsTest < ActiveRecord::TestCase
|
||||
end
|
||||
|
||||
def test_validate_uniqueness_with_scope
|
||||
Reply.validates_uniqueness_of(:content, :scope => "parent_id")
|
||||
repair_validations(Reply) do
|
||||
Reply.validates_uniqueness_of(:content, :scope => "parent_id")
|
||||
|
||||
t = Topic.create("title" => "I'm unique!")
|
||||
t = Topic.create("title" => "I'm unique!")
|
||||
|
||||
r1 = t.replies.create "title" => "r1", "content" => "hello world"
|
||||
assert r1.valid?, "Saving r1"
|
||||
r1 = t.replies.create "title" => "r1", "content" => "hello world"
|
||||
assert r1.valid?, "Saving r1"
|
||||
|
||||
r2 = t.replies.create "title" => "r2", "content" => "hello world"
|
||||
assert !r2.valid?, "Saving r2 first time"
|
||||
r2 = t.replies.create "title" => "r2", "content" => "hello world"
|
||||
assert !r2.valid?, "Saving r2 first time"
|
||||
|
||||
r2.content = "something else"
|
||||
assert r2.save, "Saving r2 second time"
|
||||
r2.content = "something else"
|
||||
assert r2.save, "Saving r2 second time"
|
||||
|
||||
t2 = Topic.create("title" => "I'm unique too!")
|
||||
r3 = t2.replies.create "title" => "r3", "content" => "hello world"
|
||||
assert r3.valid?, "Saving r3"
|
||||
t2 = Topic.create("title" => "I'm unique too!")
|
||||
r3 = t2.replies.create "title" => "r3", "content" => "hello world"
|
||||
assert r3.valid?, "Saving r3"
|
||||
end
|
||||
end
|
||||
|
||||
def test_validate_uniqueness_scoped_to_defining_class
|
||||
@@ -406,27 +403,29 @@ class ValidationsTest < ActiveRecord::TestCase
|
||||
end
|
||||
|
||||
def test_validate_uniqueness_with_scope_array
|
||||
Reply.validates_uniqueness_of(:author_name, :scope => [:author_email_address, :parent_id])
|
||||
repair_validations(Reply) do
|
||||
Reply.validates_uniqueness_of(:author_name, :scope => [:author_email_address, :parent_id])
|
||||
|
||||
t = Topic.create("title" => "The earth is actually flat!")
|
||||
t = Topic.create("title" => "The earth is actually flat!")
|
||||
|
||||
r1 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply"
|
||||
assert r1.valid?, "Saving r1"
|
||||
r1 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply"
|
||||
assert r1.valid?, "Saving r1"
|
||||
|
||||
r2 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply again..."
|
||||
assert !r2.valid?, "Saving r2. Double reply by same author."
|
||||
r2 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply again..."
|
||||
assert !r2.valid?, "Saving r2. Double reply by same author."
|
||||
|
||||
r2.author_email_address = "jeremy_alt_email@rubyonrails.com"
|
||||
assert r2.save, "Saving r2 the second time."
|
||||
r2.author_email_address = "jeremy_alt_email@rubyonrails.com"
|
||||
assert r2.save, "Saving r2 the second time."
|
||||
|
||||
r3 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy_alt_email@rubyonrails.com", "title" => "You're wrong", "content" => "It's cubic"
|
||||
assert !r3.valid?, "Saving r3"
|
||||
r3 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy_alt_email@rubyonrails.com", "title" => "You're wrong", "content" => "It's cubic"
|
||||
assert !r3.valid?, "Saving r3"
|
||||
|
||||
r3.author_name = "jj"
|
||||
assert r3.save, "Saving r3 the second time."
|
||||
r3.author_name = "jj"
|
||||
assert r3.save, "Saving r3 the second time."
|
||||
|
||||
r3.author_name = "jeremy"
|
||||
assert !r3.save, "Saving r3 the third time."
|
||||
r3.author_name = "jeremy"
|
||||
assert !r3.save, "Saving r3 the third time."
|
||||
end
|
||||
end
|
||||
|
||||
def test_validate_case_insensitive_uniqueness
|
||||
@@ -523,10 +522,12 @@ class ValidationsTest < ActiveRecord::TestCase
|
||||
end
|
||||
|
||||
def test_validate_uniqueness_with_columns_which_are_sql_keywords
|
||||
Guid.validates_uniqueness_of :key
|
||||
g = Guid.new
|
||||
g.key = "foo"
|
||||
assert_nothing_raised { !g.valid? }
|
||||
repair_validations(Guid) do
|
||||
Guid.validates_uniqueness_of :key
|
||||
g = Guid.new
|
||||
g.key = "foo"
|
||||
assert_nothing_raised { !g.valid? }
|
||||
end
|
||||
end
|
||||
|
||||
def test_validate_straight_inheritance_uniqueness
|
||||
@@ -648,10 +649,12 @@ class ValidationsTest < ActiveRecord::TestCase
|
||||
end
|
||||
|
||||
def test_numericality_with_getter_method
|
||||
Developer.validates_numericality_of( :salary )
|
||||
developer = Developer.new("name" => "michael", "salary" => nil)
|
||||
developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
|
||||
assert developer.valid?
|
||||
repair_validations(Developer) do
|
||||
Developer.validates_numericality_of( :salary )
|
||||
developer = Developer.new("name" => "michael", "salary" => nil)
|
||||
developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
|
||||
assert developer.valid?
|
||||
end
|
||||
end
|
||||
|
||||
def test_validates_length_of_with_allow_nil
|
||||
@@ -684,10 +687,12 @@ class ValidationsTest < ActiveRecord::TestCase
|
||||
end
|
||||
|
||||
def test_numericality_with_allow_nil_and_getter_method
|
||||
Developer.validates_numericality_of( :salary, :allow_nil => true)
|
||||
developer = Developer.new("name" => "michael", "salary" => nil)
|
||||
developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
|
||||
assert developer.valid?
|
||||
repair_validations(Developer) do
|
||||
Developer.validates_numericality_of( :salary, :allow_nil => true)
|
||||
developer = Developer.new("name" => "michael", "salary" => nil)
|
||||
developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
|
||||
assert developer.valid?
|
||||
end
|
||||
end
|
||||
|
||||
def test_validates_exclusion_of
|
||||
@@ -892,26 +897,30 @@ class ValidationsTest < ActiveRecord::TestCase
|
||||
end
|
||||
|
||||
def test_validates_size_of_association
|
||||
assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
|
||||
t = Topic.new('title' => 'noreplies', 'content' => 'whatever')
|
||||
assert !t.save
|
||||
assert t.errors.on(:replies)
|
||||
reply = t.replies.build('title' => 'areply', 'content' => 'whateveragain')
|
||||
assert t.valid?
|
||||
repair_validations(Owner) do
|
||||
assert_nothing_raised { Owner.validates_size_of :pets, :minimum => 1 }
|
||||
o = Owner.new('name' => 'nopets')
|
||||
assert !o.save
|
||||
assert o.errors.on(:pets)
|
||||
pet = o.pets.build('name' => 'apet')
|
||||
assert o.valid?
|
||||
end
|
||||
end
|
||||
|
||||
def test_validates_size_of_association_using_within
|
||||
assert_nothing_raised { Topic.validates_size_of :replies, :within => 1..2 }
|
||||
t = Topic.new('title' => 'noreplies', 'content' => 'whatever')
|
||||
assert !t.save
|
||||
assert t.errors.on(:replies)
|
||||
repair_validations(Owner) do
|
||||
assert_nothing_raised { Owner.validates_size_of :pets, :within => 1..2 }
|
||||
o = Owner.new('name' => 'nopets')
|
||||
assert !o.save
|
||||
assert o.errors.on(:pets)
|
||||
|
||||
reply = t.replies.build('title' => 'areply', 'content' => 'whateveragain')
|
||||
assert t.valid?
|
||||
pet = o.pets.build('name' => 'apet')
|
||||
assert o.valid?
|
||||
|
||||
2.times { t.replies.build('title' => 'areply', 'content' => 'whateveragain') }
|
||||
assert !t.save
|
||||
assert t.errors.on(:replies)
|
||||
2.times { o.pets.build('name' => 'apet') }
|
||||
assert !o.save
|
||||
assert o.errors.on(:pets)
|
||||
end
|
||||
end
|
||||
|
||||
def test_validates_length_of_nasty_params
|
||||
@@ -1102,13 +1111,15 @@ class ValidationsTest < ActiveRecord::TestCase
|
||||
end
|
||||
|
||||
def test_validates_size_of_association_utf8
|
||||
with_kcode('UTF8') do
|
||||
assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
|
||||
t = Topic.new('title' => 'あいうえお', 'content' => 'かきくけこ')
|
||||
assert !t.save
|
||||
assert t.errors.on(:replies)
|
||||
t.replies.build('title' => 'あいうえお', 'content' => 'かきくけこ')
|
||||
assert t.valid?
|
||||
repair_validations(Owner) do
|
||||
with_kcode('UTF8') do
|
||||
assert_nothing_raised { Owner.validates_size_of :pets, :minimum => 1 }
|
||||
o = Owner.new('name' => 'あいうえおかきくけこ')
|
||||
assert !o.save
|
||||
assert o.errors.on(:pets)
|
||||
o.pets.build('name' => 'あいうえおかきくけこ')
|
||||
assert o.valid?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1127,14 +1138,16 @@ class ValidationsTest < ActiveRecord::TestCase
|
||||
end
|
||||
|
||||
def test_validates_associated_one
|
||||
Reply.validates_associated( :topic )
|
||||
Topic.validates_presence_of( :content )
|
||||
r = Reply.new("title" => "A reply", "content" => "with content!")
|
||||
r.topic = Topic.create("title" => "uhohuhoh")
|
||||
assert !r.valid?
|
||||
assert r.errors.on(:topic)
|
||||
r.topic.content = "non-empty"
|
||||
assert r.valid?
|
||||
repair_validations(Reply) do
|
||||
Reply.validates_associated( :topic )
|
||||
Topic.validates_presence_of( :content )
|
||||
r = Reply.new("title" => "A reply", "content" => "with content!")
|
||||
r.topic = Topic.create("title" => "uhohuhoh")
|
||||
assert !r.valid?
|
||||
assert r.errors.on(:topic)
|
||||
r.topic.content = "non-empty"
|
||||
assert r.valid?
|
||||
end
|
||||
end
|
||||
|
||||
def test_validate_block
|
||||
@@ -1158,85 +1171,105 @@ class ValidationsTest < ActiveRecord::TestCase
|
||||
end
|
||||
|
||||
def test_validates_acceptance_of_with_custom_error_using_quotes
|
||||
Developer.validates_acceptance_of :salary, :message=> "This string contains 'single' and \"double\" quotes"
|
||||
d = Developer.new
|
||||
d.salary = "0"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
|
||||
repair_validations(Developer) do
|
||||
Developer.validates_acceptance_of :salary, :message=> "This string contains 'single' and \"double\" quotes"
|
||||
d = Developer.new
|
||||
d.salary = "0"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
|
||||
end
|
||||
end
|
||||
|
||||
def test_validates_confirmation_of_with_custom_error_using_quotes
|
||||
Developer.validates_confirmation_of :name, :message=> "confirm 'single' and \"double\" quotes"
|
||||
d = Developer.new
|
||||
d.name = "John"
|
||||
d.name_confirmation = "Johnny"
|
||||
assert !d.valid?
|
||||
assert_equal "confirm 'single' and \"double\" quotes", d.errors.on(:name)
|
||||
repair_validations(Developer) do
|
||||
Developer.validates_confirmation_of :name, :message=> "confirm 'single' and \"double\" quotes"
|
||||
d = Developer.new
|
||||
d.name = "John"
|
||||
d.name_confirmation = "Johnny"
|
||||
assert !d.valid?
|
||||
assert_equal "confirm 'single' and \"double\" quotes", d.errors.on(:name)
|
||||
end
|
||||
end
|
||||
|
||||
def test_validates_format_of_with_custom_error_using_quotes
|
||||
Developer.validates_format_of :name, :with => /^(A-Z*)$/, :message=> "format 'single' and \"double\" quotes"
|
||||
d = Developer.new
|
||||
d.name = d.name_confirmation = "John 32"
|
||||
assert !d.valid?
|
||||
assert_equal "format 'single' and \"double\" quotes", d.errors.on(:name)
|
||||
repair_validations(Developer) do
|
||||
Developer.validates_format_of :name, :with => /^(A-Z*)$/, :message=> "format 'single' and \"double\" quotes"
|
||||
d = Developer.new
|
||||
d.name = d.name_confirmation = "John 32"
|
||||
assert !d.valid?
|
||||
assert_equal "format 'single' and \"double\" quotes", d.errors.on(:name)
|
||||
end
|
||||
end
|
||||
|
||||
def test_validates_inclusion_of_with_custom_error_using_quotes
|
||||
Developer.validates_inclusion_of :salary, :in => 1000..80000, :message=> "This string contains 'single' and \"double\" quotes"
|
||||
d = Developer.new
|
||||
d.salary = "90,000"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
|
||||
repair_validations(Developer) do
|
||||
Developer.validates_inclusion_of :salary, :in => 1000..80000, :message=> "This string contains 'single' and \"double\" quotes"
|
||||
d = Developer.new
|
||||
d.salary = "90,000"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
|
||||
end
|
||||
end
|
||||
|
||||
def test_validates_length_of_with_custom_too_long_using_quotes
|
||||
Developer.validates_length_of :name, :maximum => 4, :too_long=> "This string contains 'single' and \"double\" quotes"
|
||||
d = Developer.new
|
||||
d.name = "Jeffrey"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
|
||||
repair_validations(Developer) do
|
||||
Developer.validates_length_of :name, :maximum => 4, :too_long=> "This string contains 'single' and \"double\" quotes"
|
||||
d = Developer.new
|
||||
d.name = "Jeffrey"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
|
||||
end
|
||||
end
|
||||
|
||||
def test_validates_length_of_with_custom_too_short_using_quotes
|
||||
Developer.validates_length_of :name, :minimum => 4, :too_short=> "This string contains 'single' and \"double\" quotes"
|
||||
d = Developer.new
|
||||
d.name = "Joe"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
|
||||
repair_validations(Developer) do
|
||||
Developer.validates_length_of :name, :minimum => 4, :too_short=> "This string contains 'single' and \"double\" quotes"
|
||||
d = Developer.new
|
||||
d.name = "Joe"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
|
||||
end
|
||||
end
|
||||
|
||||
def test_validates_length_of_with_custom_message_using_quotes
|
||||
Developer.validates_length_of :name, :minimum => 4, :message=> "This string contains 'single' and \"double\" quotes"
|
||||
d = Developer.new
|
||||
d.name = "Joe"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
|
||||
repair_validations(Developer) do
|
||||
Developer.validates_length_of :name, :minimum => 4, :message=> "This string contains 'single' and \"double\" quotes"
|
||||
d = Developer.new
|
||||
d.name = "Joe"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
|
||||
end
|
||||
end
|
||||
|
||||
def test_validates_presence_of_with_custom_message_using_quotes
|
||||
Developer.validates_presence_of :non_existent, :message=> "This string contains 'single' and \"double\" quotes"
|
||||
d = Developer.new
|
||||
d.name = "Joe"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:non_existent)
|
||||
repair_validations(Developer) do
|
||||
Developer.validates_presence_of :non_existent, :message=> "This string contains 'single' and \"double\" quotes"
|
||||
d = Developer.new
|
||||
d.name = "Joe"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:non_existent)
|
||||
end
|
||||
end
|
||||
|
||||
def test_validates_uniqueness_of_with_custom_message_using_quotes
|
||||
Developer.validates_uniqueness_of :name, :message=> "This string contains 'single' and \"double\" quotes"
|
||||
d = Developer.new
|
||||
d.name = "David"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
|
||||
repair_validations(Developer) do
|
||||
Developer.validates_uniqueness_of :name, :message=> "This string contains 'single' and \"double\" quotes"
|
||||
d = Developer.new
|
||||
d.name = "David"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
|
||||
end
|
||||
end
|
||||
|
||||
def test_validates_associated_with_custom_message_using_quotes
|
||||
Reply.validates_associated :topic, :message=> "This string contains 'single' and \"double\" quotes"
|
||||
Topic.validates_presence_of :content
|
||||
r = Reply.create("title" => "A reply", "content" => "with content!")
|
||||
r.topic = Topic.create("title" => "uhohuhoh")
|
||||
assert !r.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic).last
|
||||
repair_validations(Reply) do
|
||||
Reply.validates_associated :topic, :message=> "This string contains 'single' and \"double\" quotes"
|
||||
Topic.validates_presence_of :content
|
||||
r = Reply.create("title" => "A reply", "content" => "with content!")
|
||||
r.topic = Topic.create("title" => "uhohuhoh")
|
||||
assert !r.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic)
|
||||
end
|
||||
end
|
||||
|
||||
def test_if_validation_using_method_true
|
||||
@@ -1346,13 +1379,15 @@ class ValidationsTest < ActiveRecord::TestCase
|
||||
end
|
||||
|
||||
def test_validates_associated_missing
|
||||
Reply.validates_presence_of(:topic)
|
||||
r = Reply.create("title" => "A reply", "content" => "with content!")
|
||||
assert !r.valid?
|
||||
assert r.errors.on(:topic)
|
||||
repair_validations(Reply) do
|
||||
Reply.validates_presence_of(:topic)
|
||||
r = Reply.create("title" => "A reply", "content" => "with content!")
|
||||
assert !r.valid?
|
||||
assert r.errors.on(:topic)
|
||||
|
||||
r.topic = Topic.find :first
|
||||
assert r.valid?
|
||||
r.topic = Topic.find :first
|
||||
assert r.valid?
|
||||
end
|
||||
end
|
||||
|
||||
def test_errors_to_xml
|
||||
@@ -1364,14 +1399,14 @@ class ValidationsTest < ActiveRecord::TestCase
|
||||
assert xml.include?("<error>Content Empty</error>")
|
||||
end
|
||||
|
||||
def test_validation_order
|
||||
Topic.validates_presence_of :title
|
||||
Topic.validates_length_of :title, :minimum => 2
|
||||
def test_validation_order
|
||||
Topic.validates_presence_of :title
|
||||
Topic.validates_length_of :title, :minimum => 2
|
||||
|
||||
t = Topic.new("title" => "")
|
||||
assert !t.valid?
|
||||
assert_equal "can't be blank", t.errors.on("title").first
|
||||
end
|
||||
t = Topic.new("title" => "")
|
||||
assert !t.valid?
|
||||
assert_equal "can't be blank", t.errors.on("title").first
|
||||
end
|
||||
|
||||
# previous implementation of validates_presence_of eval'd the
|
||||
# string with the wrong binding, this regression test is to
|
||||
@@ -1423,11 +1458,7 @@ class ValidatesNumericalityTest < ActiveRecord::TestCase
|
||||
JUNK = ["not a number", "42 not a number", "0xdeadbeef", "00-1", "--3", "+-3", "+3-1", "-+019.0", "12.12.13.12", "123\nnot a number"]
|
||||
INFINITY = [1.0/0.0]
|
||||
|
||||
def setup
|
||||
Topic.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
||||
Topic.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
||||
Topic.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
||||
end
|
||||
repair_validations(Topic)
|
||||
|
||||
def test_default_validates_numericality_of
|
||||
Topic.validates_numericality_of :approved
|
||||
|
||||
6
activerecord/test/fixtures/member_types.yml
vendored
Normal file
6
activerecord/test/fixtures/member_types.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
founding:
|
||||
id: 1
|
||||
name: Founding
|
||||
provisional:
|
||||
id: 2
|
||||
name: Provisional
|
||||
4
activerecord/test/fixtures/members.yml
vendored
4
activerecord/test/fixtures/members.yml
vendored
@@ -1,4 +1,6 @@
|
||||
groucho:
|
||||
name: Groucho Marx
|
||||
member_type_id: 1
|
||||
some_other_guy:
|
||||
name: Englebert Humperdink
|
||||
name: Englebert Humperdink
|
||||
member_type_id: 2
|
||||
|
||||
@@ -8,4 +8,5 @@ class Member < ActiveRecord::Base
|
||||
has_one :sponsor_club, :through => :sponsor
|
||||
has_one :member_detail
|
||||
has_one :organization, :through => :member_detail
|
||||
belongs_to :member_type
|
||||
end
|
||||
@@ -1,4 +1,5 @@
|
||||
class MemberDetail < ActiveRecord::Base
|
||||
belongs_to :member
|
||||
belongs_to :organization
|
||||
has_one :member_type, :through => :member
|
||||
end
|
||||
|
||||
3
activerecord/test/models/member_type.rb
Normal file
3
activerecord/test/models/member_type.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
class MemberType < ActiveRecord::Base
|
||||
has_many :members
|
||||
end
|
||||
@@ -195,6 +195,7 @@ ActiveRecord::Schema.define do
|
||||
|
||||
create_table :members, :force => true do |t|
|
||||
t.string :name
|
||||
t.integer :member_type_id
|
||||
end
|
||||
|
||||
create_table :member_details, :force => true do |t|
|
||||
@@ -210,6 +211,10 @@ ActiveRecord::Schema.define do
|
||||
t.string :type
|
||||
end
|
||||
|
||||
create_table :member_types, :force => true do |t|
|
||||
t.string :name
|
||||
end
|
||||
|
||||
create_table :references, :force => true do |t|
|
||||
t.integer :person_id
|
||||
t.integer :job_id
|
||||
|
||||
@@ -20,15 +20,15 @@
|
||||
|
||||
* Fixed that to_param should be used and honored instead of hardcoding the id #11406 [gspiers]
|
||||
|
||||
* Improve documentation. [Radar, Jan De Poorter, chuyeow, xaviershay, danger, miloops, Xavier Noria, Sunny Ripert]
|
||||
* Improve documentation. [Ryan Bigg, Jan De Poorter, Cheah Chu Yeow, Xavier Shay, Jack Danger Canty, Emilio Tagua, Xavier Noria, Sunny Ripert]
|
||||
|
||||
* Use HEAD instead of GET in exists? [bscofield]
|
||||
|
||||
* Fix small documentation typo. Closes #10670 [l.guidi]
|
||||
* Fix small documentation typo. Closes #10670 [Luca Guidi]
|
||||
|
||||
* find_or_create_resource_for handles module nesting. #10646 [xavier]
|
||||
|
||||
* Allow setting ActiveResource::Base#format before #site. [rick]
|
||||
* Allow setting ActiveResource::Base#format before #site. [Rick Olson]
|
||||
|
||||
* Support agnostic formats when calling custom methods. Closes #10635 [joerichsen]
|
||||
|
||||
@@ -48,9 +48,9 @@
|
||||
|
||||
* Don't cache net/http object so that ActiveResource is more thread-safe. Closes #10142 [kou]
|
||||
|
||||
* Update XML documentation examples to include explicit type attributes. Closes #9754 [hasmanyjosh]
|
||||
* Update XML documentation examples to include explicit type attributes. Closes #9754 [Josh Susser]
|
||||
|
||||
* Added one-off declarations of mock behavior [DHH]. Example:
|
||||
* Added one-off declarations of mock behavior [David Heinemeier Hansson]. Example:
|
||||
|
||||
Before:
|
||||
ActiveResource::HttpMock.respond_to do |mock|
|
||||
@@ -60,7 +60,7 @@
|
||||
Now:
|
||||
ActiveResource::HttpMock.respond_to.get "/people/1.xml", {}, "<person><name>David</name></person>"
|
||||
|
||||
* Added ActiveResource.format= which defaults to :xml but can also be set to :json [DHH]. Example:
|
||||
* Added ActiveResource.format= which defaults to :xml but can also be set to :json [David Heinemeier Hansson]. Example:
|
||||
|
||||
class Person < ActiveResource::Base
|
||||
self.site = "http://app/"
|
||||
@@ -81,7 +81,7 @@
|
||||
|
||||
* Fix query methods on resources. [Cody Fauser]
|
||||
|
||||
* pass the prefix_options to the instantiated record when using find without a specific id. Closes #8544 [alloy]
|
||||
* pass the prefix_options to the instantiated record when using find without a specific id. Closes #8544 [Eloy Duran]
|
||||
|
||||
* Recognize and raise an exception on 405 Method Not Allowed responses. #7692 [Josh Peek]
|
||||
|
||||
@@ -89,15 +89,15 @@
|
||||
|
||||
Comment.find(:all, :params => { :article_id => 5, :page => 2 }) or Comment.find(:all, :params => { 'article_id' => 5, :page => 2 })
|
||||
|
||||
* Added find-one with symbol [DHH]. Example: Person.find(:one, :from => :leader) # => GET /people/leader.xml
|
||||
* Added find-one with symbol [David Heinemeier Hansson]. Example: Person.find(:one, :from => :leader) # => GET /people/leader.xml
|
||||
|
||||
* BACKWARDS INCOMPATIBLE: Changed the finder API to be more extensible with :params and more strict usage of scopes [DHH]. Changes:
|
||||
* BACKWARDS INCOMPATIBLE: Changed the finder API to be more extensible with :params and more strict usage of scopes [David Heinemeier Hansson]. Changes:
|
||||
|
||||
Person.find(:all, :title => "CEO") ...becomes: Person.find(:all, :params => { :title => "CEO" })
|
||||
Person.find(:managers) ...becomes: Person.find(:all, :from => :managers)
|
||||
Person.find("/companies/1/manager.xml") ...becomes: Person.find(:one, :from => "/companies/1/manager.xml")
|
||||
|
||||
* Add support for setting custom headers per Active Resource model [Rick]
|
||||
* Add support for setting custom headers per Active Resource model [Rick Olson]
|
||||
|
||||
class Project
|
||||
headers['X-Token'] = 'foo'
|
||||
@@ -106,13 +106,13 @@
|
||||
# makes the GET request with the custom X-Token header
|
||||
Project.find(:all)
|
||||
|
||||
* Added find-by-path options to ActiveResource::Base.find [DHH]. Examples:
|
||||
* Added find-by-path options to ActiveResource::Base.find [David Heinemeier Hansson]. Examples:
|
||||
|
||||
employees = Person.find(:all, :from => "/companies/1/people.xml") # => GET /companies/1/people.xml
|
||||
manager = Person.find("/companies/1/manager.xml") # => GET /companies/1/manager.xml
|
||||
|
||||
|
||||
* Added support for using classes from within a single nested module [DHH]. Example:
|
||||
* Added support for using classes from within a single nested module [David Heinemeier Hansson]. Example:
|
||||
|
||||
module Highrise
|
||||
class Note < ActiveResource::Base
|
||||
@@ -127,7 +127,7 @@
|
||||
assert_kind_of Highrise::Comment, Note.find(1).comments.first
|
||||
|
||||
|
||||
* Added load_attributes_from_response as a way of loading attributes from other responses than just create [DHH]
|
||||
* Added load_attributes_from_response as a way of loading attributes from other responses than just create [David Heinemeier Hansson]
|
||||
|
||||
class Highrise::Task < ActiveResource::Base
|
||||
def complete
|
||||
@@ -143,18 +143,18 @@
|
||||
Person.find(:managers) # => GET /people/managers.xml
|
||||
Kase.find(1).post(:close) # => POST /kases/1/close.xml
|
||||
|
||||
* Remove explicit prefix_options parameter for ActiveResource::Base#initialize. [Rick]
|
||||
* Remove explicit prefix_options parameter for ActiveResource::Base#initialize. [Rick Olson]
|
||||
ActiveResource splits the prefix_options from it automatically.
|
||||
|
||||
* Allow ActiveResource::Base.delete with custom prefix. [Rick]
|
||||
* Allow ActiveResource::Base.delete with custom prefix. [Rick Olson]
|
||||
|
||||
* Add ActiveResource::Base#dup [Rick]
|
||||
* Add ActiveResource::Base#dup [Rick Olson]
|
||||
|
||||
* Fixed constant warning when fetching the same object multiple times [DHH]
|
||||
* Fixed constant warning when fetching the same object multiple times [David Heinemeier Hansson]
|
||||
|
||||
* Added that saves which get a body response (and not just a 201) will use that response to update themselves [DHH]
|
||||
* Added that saves which get a body response (and not just a 201) will use that response to update themselves [David Heinemeier Hansson]
|
||||
|
||||
* Disregard namespaces from the default element name, so Highrise::Person will just try to fetch from "/people", not "/highrise/people" [DHH]
|
||||
* Disregard namespaces from the default element name, so Highrise::Person will just try to fetch from "/people", not "/highrise/people" [David Heinemeier Hansson]
|
||||
|
||||
* Allow array and hash query parameters. #7756 [Greg Spurrier]
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
|
||||
* Base#==, eql?, and hash methods. == returns true if its argument is identical to self or if it's an instance of the same class, is not new?, and has the same id. eql? is an alias for ==. hash delegates to id. [Jeremy Kemper]
|
||||
|
||||
* Allow subclassed resources to share the site info [Rick, Jeremy Kemper]
|
||||
* Allow subclassed resources to share the site info [Rick Olson, Jeremy Kemper]
|
||||
d
|
||||
class BeastResource < ActiveResource::Base
|
||||
self.site = 'http://beast.caboo.se'
|
||||
@@ -259,4 +259,4 @@ d
|
||||
|
||||
* Base.site= accepts URIs. 200...400 are valid response codes. PUT and POST request bodies default to ''. [Jeremy Kemper]
|
||||
|
||||
* Initial checkin: object-oriented client for restful HTTP resources which follow the Rails convention. [DHH]
|
||||
* Initial checkin: object-oriented client for restful HTTP resources which follow the Rails convention. [David Heinemeier Hansson]
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
|
||||
* Added ActiveSupport::OrderedHash#each_key and ActiveSupport::OrderedHash#each_value #1410 [Christoffer Sawicki]
|
||||
|
||||
* Added ActiveSupport::MessageVerifier and MessageEncryptor to aid users who need to store signed and/or encrypted messages. [Koz]
|
||||
* Added ActiveSupport::MessageVerifier and MessageEncryptor to aid users who need to store signed and/or encrypted messages. [Michael Koziarski]
|
||||
|
||||
* Added ActiveSupport::BacktraceCleaner to cut down on backtrace noise according to filters and silencers [DHH]
|
||||
* Added ActiveSupport::BacktraceCleaner to cut down on backtrace noise according to filters and silencers [David Heinemeier Hansson]
|
||||
|
||||
* Added Object#try. ( Taken from http://ozmm.org/posts/try.html ) [Chris Wanstrath]
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
* Fixed the option merging in Array#to_xml #1126 [Rudolf Gavlas]
|
||||
|
||||
* Make I18n::Backend::Simple reload its translations in development mode [DHH/Sven Fuchs]
|
||||
* Make I18n::Backend::Simple reload its translations in development mode [David Heinemeier Hansson/Sven Fuchs]
|
||||
|
||||
|
||||
*2.2.0 [RC1] (October 24th, 2008)*
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
* Time#advance recognizes fractional days and weeks. Deprecate Durations of fractional months and years #970 [Tom Lea]
|
||||
|
||||
* Add ActiveSupport::Rescuable module abstracting ActionController::Base rescue_from features. [Norbert Crombach, Pratik]
|
||||
* Add ActiveSupport::Rescuable module abstracting ActionController::Base rescue_from features. [Norbert Crombach, Pratik Naik]
|
||||
|
||||
* Switch from String#chars to String#mb_chars for the unicode proxy. [Manfred Stienstra]
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
|
||||
* Added Inflector#parameterize for easy slug generation ("Donald E. Knuth".parameterize => "donald-e-knuth") #713 [Matt Darby]
|
||||
|
||||
* Changed cache benchmarking to be reported in milliseconds [DHH]
|
||||
* Changed cache benchmarking to be reported in milliseconds [David Heinemeier Hansson]
|
||||
|
||||
* Fix Ruby's Time marshaling bug in pre-1.9 versions of Ruby: utc instances are now correctly unmarshaled with a utc zone instead of the system local zone [#900 state:resolved] [Luca Guidi, Geoff Buesing]
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
|
||||
* Move the test related core_ext stuff out of core_ext so it's only loaded by the test helpers. [Michael Koziarski]
|
||||
|
||||
* Add Inflection rules for String#humanize. #535 [dcmanges]
|
||||
* Add Inflection rules for String#humanize. #535 [Dan Manges]
|
||||
|
||||
ActiveSupport::Inflector.inflections do |inflect|
|
||||
inflect.human(/_cnt$/i, '\1_count')
|
||||
@@ -84,19 +84,19 @@
|
||||
|
||||
* Added TimeZone #=~, to support matching zones by regex in time_zone_select. #195 [Ernie Miller]
|
||||
|
||||
* Added Array#second through Array#fifth as aliases for Array#[1] through Array#[4] + Array#forty_two as alias for Array[41] [DHH]
|
||||
* Added Array#second through Array#fifth as aliases for Array#[1] through Array#[4] + Array#forty_two as alias for Array[41] [David Heinemeier Hansson]
|
||||
|
||||
* Added test/do declaration style testing to ActiveSupport::TestCase [DHH via Jay Fields]
|
||||
|
||||
* Added Object#present? which is equivalent to !Object#blank? [DHH]
|
||||
* Added Object#present? which is equivalent to !Object#blank? [David Heinemeier Hansson]
|
||||
|
||||
* Added Enumberable#many? to encapsulate collection.size > 1 [DHH/Damian Janowski]
|
||||
* Added Enumberable#many? to encapsulate collection.size > 1 [David Heinemeier Hansson/Damian Janowski]
|
||||
|
||||
* Add more standard Hash methods to ActiveSupport::OrderedHash [Steve Purcell]
|
||||
|
||||
* Namespace Inflector, Dependencies, OrderedOptions, and TimeZone under ActiveSupport [Josh Peek]
|
||||
|
||||
* Added StringInquirer for doing things like StringInquirer.new("production").production? # => true and StringInquirer.new("production").development? # => false [DHH]
|
||||
* Added StringInquirer for doing things like StringInquirer.new("production").production? # => true and StringInquirer.new("production").development? # => false [David Heinemeier Hansson]
|
||||
|
||||
* Fixed Date#end_of_quarter to not blow up on May 31st [#289 state:resolved] (Danger)
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
|
||||
* TimeWithZone respects config.active_support.use_standard_json_time_format [Geoff Buesing]
|
||||
|
||||
* Add config.active_support.escape_html_entities_in_json to allow disabling of html entity escaping. [rick]
|
||||
* Add config.active_support.escape_html_entities_in_json to allow disabling of html entity escaping. [Rick Olson]
|
||||
|
||||
* Improve documentation. [Xavier Noria]
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
|
||||
* TimeWithZone#method_missing: send to utc to advance with dst correctness, otherwise send to time. Adding tests for time calculations methods [Geoff Buesing]
|
||||
|
||||
* Add config.active_support.use_standard_json_time_format setting so that Times and Dates export to ISO 8601 dates. [rick]
|
||||
* Add config.active_support.use_standard_json_time_format setting so that Times and Dates export to ISO 8601 dates. [Rick Olson]
|
||||
|
||||
* TZInfo: Removing unneeded TimezoneProxy class [Geoff Buesing]
|
||||
|
||||
@@ -163,9 +163,9 @@
|
||||
|
||||
* TimeWithZone#usec returns 0 instead of error when DateTime is wrapped [Geoff Buesing]
|
||||
|
||||
* Improve documentation. [Radar, Jan De Poorter, chuyeow, xaviershay, danger, miloops, Xavier Noria, Sunny Ripert]
|
||||
* Improve documentation. [Ryan Bigg, Jan De Poorter, Cheah Chu Yeow, Xavier Shay, Jack Danger Canty, Emilio Tagua, Xavier Noria, Sunny Ripert]
|
||||
|
||||
* Ensure that TimeWithZone#to_yaml works when passed a YAML::Emitter. [rick]
|
||||
* Ensure that TimeWithZone#to_yaml works when passed a YAML::Emitter. [Rick Olson]
|
||||
|
||||
* Ensure correct TimeWithZone#to_date [Geoff Buesing]
|
||||
|
||||
@@ -197,7 +197,7 @@
|
||||
|
||||
* Adding TimeWithZone #marshal_dump and #marshal_load [Geoff Buesing]
|
||||
|
||||
* Add OrderedHash#to_hash [josh]
|
||||
* Add OrderedHash#to_hash [Josh Peek]
|
||||
|
||||
* Adding Time#end_of_day, _quarter, _week, and _year. #9312 [Juanjo Bazan, Tarmo Tänav, BigTitus]
|
||||
|
||||
@@ -207,9 +207,9 @@
|
||||
|
||||
* TimeWithZone #+ and #- behave consistently with numeric arguments regardless of whether wrapped time is a Time or DateTime; consistenty answers false to #acts_like?(:date) [Geoff Buesing]
|
||||
|
||||
* Add String#squish and String#squish! to remove consecutive chunks of whitespace. #11123 [jordi, Henrik N]
|
||||
* Add String#squish and String#squish! to remove consecutive chunks of whitespace. #11123 [Jordi Bunster, Henrik N]
|
||||
|
||||
* Serialize BigDecimals as Floats when using to_yaml. #8746 [ernesto.jimenez]
|
||||
* Serialize BigDecimals as Floats when using to_yaml. #8746 [Ernesto Jimenez]
|
||||
|
||||
* Adding TimeWithZone #to_yaml, #to_datetime, #eql? and method aliases for duck-typing compatibility with Time [Geoff Buesing]
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
* TimeZone#new method renamed #local; when used with Time.zone, constructor now reads: Time.zone.local() [Geoff Buesing]
|
||||
|
||||
* Added Base64.encode64s to encode values in base64 without the newlines. This makes the values immediately usable as URL parameters or memcache keys without further processing [DHH]
|
||||
* Added Base64.encode64s to encode values in base64 without the newlines. This makes the values immediately usable as URL parameters or memcache keys without further processing [David Heinemeier Hansson]
|
||||
|
||||
* Remove :nodoc: entries around the ActiveSupport test/unit assertions. #10946 [dancroak, jamesh]
|
||||
|
||||
@@ -235,7 +235,7 @@
|
||||
|
||||
* cache.fetch(key, :force => true) to force a cache miss. [Jeremy Kemper]
|
||||
|
||||
* Support retrieving TimeZones with a Duration. TimeZone[-28800] == TimeZone[-480.minutes]. [rick]
|
||||
* Support retrieving TimeZones with a Duration. TimeZone[-28800] == TimeZone[-480.minutes]. [Rick Olson]
|
||||
|
||||
* TimeWithZone#- added, so that #- can handle a Time or TimeWithZone argument correctly [Geoff Buesing]
|
||||
|
||||
@@ -283,21 +283,21 @@
|
||||
|
||||
* TestCase: introduce declared setup and teardown callbacks. Pass a list of methods and an optional block to call before setup or after teardown. Setup callbacks are run in the order declared; teardown callbacks are run in reverse. [Jeremy Kemper]
|
||||
|
||||
* Added ActiveSupport::Gzip.decompress/compress(source) as an easy wrapper for Zlib [Tobias Luetke]
|
||||
* Added ActiveSupport::Gzip.decompress/compress(source) as an easy wrapper for Zlib [Tobias Lütke]
|
||||
|
||||
* Included MemCache-Client to make the improved ActiveSupport::Cache::MemCacheStore work out of the box [Bob Cottrell, Eric Hodel]
|
||||
|
||||
* Added ActiveSupport::Cache::* framework as an extraction from ActionController::Caching::Fragments::* [DHH]
|
||||
* Added ActiveSupport::Cache::* framework as an extraction from ActionController::Caching::Fragments::* [David Heinemeier Hansson]
|
||||
|
||||
* Fixed String#titleize to work for strings with 's too #10571 [trek]
|
||||
|
||||
* Changed the implementation of Enumerable#group_by to use a double array approach instead of a hash such that the insert order is honored [DHH/Marcel]
|
||||
* Changed the implementation of Enumerable#group_by to use a double array approach instead of a hash such that the insert order is honored [David Heinemeier Hansson/Marcel Molina Jr.]
|
||||
|
||||
* remove multiple enumerations from ActiveSupport::JSON#convert_json_to_yaml when dealing with date/time values. [rick]
|
||||
* remove multiple enumerations from ActiveSupport::JSON#convert_json_to_yaml when dealing with date/time values. [Rick Olson]
|
||||
|
||||
* Hash#symbolize_keys skips keys that can't be symbolized. #10500 [Brad Greenlee]
|
||||
|
||||
* Ruby 1.9 compatibility. #1689, #10466, #10468, #10554, #10594, #10632 [Cheah Chu Yeow, Pratik Naik, Jeremy Kemper, Dirkjan Bussink, fxn]
|
||||
* Ruby 1.9 compatibility. #1689, #10466, #10468, #10554, #10594, #10632 [Cheah Chu Yeow, Pratik Naik, Jeremy Kemper, Dirkjan Bussink, Xavier Noria]
|
||||
|
||||
* TimeZone#to_s uses UTC rather than GMT. #1689 [Cheah Chu Yeow]
|
||||
|
||||
@@ -308,7 +308,7 @@
|
||||
|
||||
*2.0.1* (December 7th, 2007)
|
||||
|
||||
* Added Array#from and Array#to that behaves just from String#from and String#to [DHH]
|
||||
* Added Array#from and Array#to that behaves just from String#from and String#to [David Heinemeier Hansson]
|
||||
|
||||
* Fix that empty collections should be treated as empty arrays regardless of whitespace for Hash#from_xml #10255 [adamj]
|
||||
|
||||
@@ -316,17 +316,17 @@
|
||||
|
||||
* Change Time and DateTime #end_of_month to return last second of month instead of beginning of last day of month. Closes #10200 [Geoff Buesing]
|
||||
|
||||
* Speedup String#blank? [Jeremy Kemper, Koz]
|
||||
* Speedup String#blank? [Jeremy Kemper, Michael Koziarski]
|
||||
|
||||
* Add documentation for Hash#diff. Closes #9306 [Tarmo Tänav]
|
||||
|
||||
* Add new superclass_delegating_accessors. Similar to class inheritable attributes but with subtly different semantics. [Koz, Tarmo Tänav]
|
||||
* Add new superclass_delegating_accessors. Similar to class inheritable attributes but with subtly different semantics. [Michael Koziarski, Tarmo Tänav]
|
||||
|
||||
* Change JSON to encode %w(< > &) as 4 digit hex codes to be in compliance with the JSON spec. Closes #9975 [Josh Peek, Cheah Chu Yeow, Tim Pope]
|
||||
|
||||
* Fix JSON encoding/decoding bugs dealing with /'s. Closes #9990 [Rick, theamazingrando]
|
||||
* Fix JSON encoding/decoding bugs dealing with /'s. Closes #9990 [Rick Olson, theamazingrando]
|
||||
|
||||
* Introduce a base class for all test cases used by rails applications. ActiveSupport::TestCase [Koz]
|
||||
* Introduce a base class for all test cases used by rails applications. ActiveSupport::TestCase [Michael Koziarski]
|
||||
|
||||
The intention is to use this to reduce the amount of monkeypatching / overriding that
|
||||
is done to test/unit's classes.
|
||||
@@ -379,13 +379,13 @@
|
||||
|
||||
* Backport Object#instance_variable_defined? for Ruby < 1.8.6. [Jeremy Kemper]
|
||||
|
||||
* BufferedLogger#add converts the message to a string. #9702, #9724 [eigentone, DrMark, tomafro]
|
||||
* BufferedLogger#add converts the message to a string. #9702, #9724 [eigentone, DrMark, Tom Ward]
|
||||
|
||||
* Added ActiveSupport::BufferedLogger as a duck-typing alternative (albeit with no formatter) to the Ruby Logger, which provides a very nice speed bump (inspired by Ezra's buffered logger) [DHH]
|
||||
* Added ActiveSupport::BufferedLogger as a duck-typing alternative (albeit with no formatter) to the Ruby Logger, which provides a very nice speed bump (inspired by Ezra's buffered logger) [David Heinemeier Hansson]
|
||||
|
||||
* Object#instance_exec produces fewer garbage methods. [Mauricio Fernandez]
|
||||
|
||||
* Decode json strings as Dates/Times if they're using a YAML-compatible format. Closes #9614 [Rick]
|
||||
* Decode json strings as Dates/Times if they're using a YAML-compatible format. Closes #9614 [Rick Olson]
|
||||
|
||||
* Fixed cache_page to use the request url instead of the routing options when picking a save path. #8614 [Josh Peek]
|
||||
|
||||
@@ -393,11 +393,11 @@
|
||||
|
||||
* Fixed that pluralizing an empty string should return the same empty string, not "s". #7720 [Josh Peek]
|
||||
|
||||
* Added call to inspect on non-string classes for the logger #8533 [codahale]
|
||||
* Added call to inspect on non-string classes for the logger #8533 [Coda Hale]
|
||||
|
||||
* Deprecation: remove deprecated :mday option from Time, Date, and DateTime#change. [Jeremy Kemper]
|
||||
|
||||
* Fix JSON decoder with nested quotes and commas. #9579 [zdennis]
|
||||
* Fix JSON decoder with nested quotes and commas. #9579 [Zach Dennis]
|
||||
|
||||
* Hash#to_xml doesn't double-unescape. #8806 [Ezran]
|
||||
|
||||
@@ -409,27 +409,27 @@
|
||||
|
||||
* Deprecation: removed Reloadable. [Jeremy Kemper]
|
||||
|
||||
* Make the utf-handler return the correct value for non-matching regular expressions. Closes #9049 [manfred]
|
||||
* Make the utf-handler return the correct value for non-matching regular expressions. Closes #9049 [Manfred Stienstra]
|
||||
|
||||
* Add ljust, rjust and center to utf8-handler. Closes #9165 [manfred]
|
||||
* Add ljust, rjust and center to utf8-handler. Closes #9165 [Manfred Stienstra]
|
||||
|
||||
* Fix Time#advance bug when trying to advance a year from leap day. Closes #8655 [gbuesing]
|
||||
* Fix Time#advance bug when trying to advance a year from leap day. Closes #8655 [Geoff Buesing]
|
||||
|
||||
* Add support for []= on ActiveSupport::Multibyte::Chars. Closes #9142. [ewan, manfred]
|
||||
* Add support for []= on ActiveSupport::Multibyte::Chars. Closes #9142. [ewan, Manfred Stienstra]
|
||||
|
||||
* Added Array#extract_options! to encapsulate the pattern of getting an options hash out of a variable number of parameters. #8759 [Norbert Crombach]
|
||||
|
||||
* Let alias_attribute work with attributes with initial capital letters (legacy columns etc). Closes #8596 [mpalmer]
|
||||
|
||||
* Added Hash#except which is the inverse of Hash#slice -- return the hash except the keys that are specified [DHH]
|
||||
* Added Hash#except which is the inverse of Hash#slice -- return the hash except the keys that are specified [David Heinemeier Hansson]
|
||||
|
||||
* Added support for pluralization with a different starting letter than the singular version (cow/kine) #4929 [norri_b/hasmanyjosh]
|
||||
* Added support for pluralization with a different starting letter than the singular version (cow/kine) #4929 [norri_b/Josh Susser]
|
||||
|
||||
* Demote Hash#to_xml to use XmlSimple#xml_in_string so it can't read files or stdin. #8453 [candlerb, Jeremy Kemper]
|
||||
|
||||
* Backport clean_logger changes to support ruby 1.8.2 [mislav]
|
||||
* Backport clean_logger changes to support ruby 1.8.2 [Mislav Marohnić]
|
||||
|
||||
* Added proper handling of arrays #8537 [hasmanyjosh]
|
||||
* Added proper handling of arrays #8537 [Josh Susser]
|
||||
|
||||
Before:
|
||||
Hash.from_xml '<images></images>'
|
||||
@@ -463,7 +463,7 @@
|
||||
|
||||
* Move common DateTime calculations to Date. #8536 [Geoff Buesing]
|
||||
|
||||
* Added Date#change (like Time#change) [DHH]
|
||||
* Added Date#change (like Time#change) [David Heinemeier Hansson]
|
||||
|
||||
* DateTime#to_time converts to Time unless out of range. #8512 [Geoff Buesing]
|
||||
|
||||
@@ -471,7 +471,7 @@
|
||||
|
||||
* Time durations use since instead of + for accuracy. #8513 [Geoff Buesing]
|
||||
|
||||
* escape <'s and >'s in JSON strings. #8371 [Rick]
|
||||
* escape <'s and >'s in JSON strings. #8371 [Rick Olson]
|
||||
|
||||
* Inflections: MatrixTest -> MatrixTests instead of MatricesTest. #8496 [jbwiv]
|
||||
|
||||
@@ -487,11 +487,11 @@
|
||||
|
||||
* Simplify API of assert_difference by passing in an expression that is evaluated before and after the passed in block. See documenation for examples of new API. [Marcel Molina Jr.]
|
||||
|
||||
* Added assert_difference and assert_no_difference to test/unit assertions [Tobias Luetke]
|
||||
* Added assert_difference and assert_no_difference to test/unit assertions [Tobias Lütke]
|
||||
|
||||
* Removed breakpointer and Binding.of_caller in favor of relying on ruby-debug by Kent Sibilev since the breakpointer has been broken since Ruby 1.8.4 and will not be coming back [DHH]
|
||||
* Removed breakpointer and Binding.of_caller in favor of relying on ruby-debug by Kent Sibilev since the breakpointer has been broken since Ruby 1.8.4 and will not be coming back [David Heinemeier Hansson]
|
||||
|
||||
* Added parsing of file type in Hash.xml_in so you can easily do file uploads with base64 from an API [DHH]
|
||||
* Added parsing of file type in Hash.xml_in so you can easily do file uploads with base64 from an API [David Heinemeier Hansson]
|
||||
|
||||
<person>
|
||||
<name>David</name>
|
||||
@@ -511,9 +511,9 @@
|
||||
|
||||
* Use XSD-compatible type names for Hash#to_xml and make the converters extendable #8047 [Tim Pope]
|
||||
|
||||
* Added yielding of builder in Hash#to_xml [DHH]
|
||||
* Added yielding of builder in Hash#to_xml [David Heinemeier Hansson]
|
||||
|
||||
* Hash#with_indifferent_access now also converts hashes kept in arrays to indifferent access (makes it easier to treat HTML and XML parameters the same) [DHH]
|
||||
* Hash#with_indifferent_access now also converts hashes kept in arrays to indifferent access (makes it easier to treat HTML and XML parameters the same) [David Heinemeier Hansson]
|
||||
|
||||
* Hash#to_xml supports YAML attributes. #7502 [jonathan]
|
||||
|
||||
@@ -531,9 +531,9 @@
|
||||
|
||||
* Give DateTime correct .to_s implementations, lets it play nice with ActiveRecord quoting. #7649 [Geoff Buesing]
|
||||
|
||||
* Add File.atomic_write, allows you to write large files in an atomic manner, preventing users from seeing half written files. [Koz]
|
||||
* Add File.atomic_write, allows you to write large files in an atomic manner, preventing users from seeing half written files. [Michael Koziarski]
|
||||
|
||||
* Allow users to provide custom formatters to Logger. [aeden]
|
||||
* Allow users to provide custom formatters to Logger. [Anthony Eden]
|
||||
|
||||
* Hash#to_query CGI-escapes its keys. [Jeremy Kemper]
|
||||
|
||||
@@ -542,7 +542,7 @@
|
||||
* :db format for Date#to_s [Jeremy Kemper]
|
||||
Date.new(2007, 1, 27).to_s(:db) # => '2007-01-27'
|
||||
|
||||
* Added :instance_writer option to #mattr_writer/accessor, #cattr_writer/accessor, and #class_inheritable_writer to skip the creation of the instance writer. [Rick]
|
||||
* Added :instance_writer option to #mattr_writer/accessor, #cattr_writer/accessor, and #class_inheritable_writer to skip the creation of the instance writer. [Rick Olson]
|
||||
|
||||
* Added Hash#to_query to turn a hash of values into a form-encoded query string [Nicholas Seckar]
|
||||
|
||||
@@ -577,7 +577,7 @@ public for compatibility. [Jeremy Kemper]
|
||||
|
||||
* Optimize Class Inheritable Attributes so that unnecessary hashes are not created. Closes #7472 [Bruce Perens]
|
||||
|
||||
* Added :instance_writer option to #mattr_writer/accessor, #cattr_writer/accessor, and #class_inheritable_writer to skip the creation of the instance writer. [Rick]
|
||||
* Added :instance_writer option to #mattr_writer/accessor, #cattr_writer/accessor, and #class_inheritable_writer to skip the creation of the instance writer. [Rick Olson]
|
||||
|
||||
* Full test coverage for Inflector. #7228 [Dan Kubb]
|
||||
|
||||
@@ -602,13 +602,13 @@ public for compatibility. [Jeremy Kemper]
|
||||
|
||||
* Fixed Array#to_xml when it contains a series of hashes (each piece would get its own XML declaration) #6610 [thkarcher/cyu]
|
||||
|
||||
* Added Time#to_s(:time) which will just return H:M, like 17:44 [DHH]
|
||||
* Added Time#to_s(:time) which will just return H:M, like 17:44 [David Heinemeier Hansson]
|
||||
|
||||
* Add Module#attr_accessor_with_default to initialize value of attribute before setting it. Closes #6538. [Stuart Halloway, Marcel Molina Jr.]
|
||||
|
||||
* Hash#to_xml handles keys with the same name as Kernel methods. #6613 [Catfish]
|
||||
* Hash#to_xml handles keys with the same name as Kernel methods. #6613 [Jonathan del Strother]
|
||||
|
||||
* Added Time#end_of_day to get 23:59:59 of that day [DHH]
|
||||
* Added Time#end_of_day to get 23:59:59 of that day [David Heinemeier Hansson]
|
||||
|
||||
* Don't quote hash keys in Hash#to_json if they're valid JavaScript identifiers. Disable this with ActiveSupport::JSON.unquote_hash_key_identifiers = false if you need strict JSON compliance. [Sam Stephenson]
|
||||
|
||||
@@ -618,11 +618,11 @@ public for compatibility. [Jeremy Kemper]
|
||||
|
||||
* Fix unicode JSON regexp for Onigurama compatibility. #6494 [whitley]
|
||||
|
||||
* update XmlSimple to 1.0.10. Closes #6532. [nicksieger]
|
||||
* update XmlSimple to 1.0.10. Closes #6532. [Nick Sieger]
|
||||
|
||||
* Update dependencies to allow constants to be defined alongside their siblings. A common case for this is AR model classes with STI; user.rb might define User, Administrator and Guest for example. [Nicholas Seckar]
|
||||
|
||||
* next_week respects DST changes. #6483, #5617, #2353, #2509, #4551 [marclove, rabiedenharn, rails@roetzel.de, jsolson@damogran.org, drbrain@segment7.net]
|
||||
* next_week respects DST changes. #6483, #5617, #2353, #2509, #4551 [marclove, Rob Biedenharn, rails@roetzel.de, jsolson@damogran.org, drbrain@segment7.net]
|
||||
|
||||
* Expose methods added to Enumerable in the documentation, such as group_by. Closes #6170. [sergeykojin@gmail.com, Marcel Molina Jr.]
|
||||
|
||||
@@ -642,9 +642,9 @@ public for compatibility. [Jeremy Kemper]
|
||||
|
||||
* Hash#to_xml supports Bignum and BigDecimal. #6313 [edibiase]
|
||||
|
||||
* Don't undefine #class in OptionMerger [Rick]
|
||||
* Don't undefine #class in OptionMerger [Rick Olson]
|
||||
|
||||
* Hash.create_from_xml has been renamed to Hash.from_xml, alias will exist until Rails 2.0 [DHH]
|
||||
* Hash.create_from_xml has been renamed to Hash.from_xml, alias will exist until Rails 2.0 [David Heinemeier Hansson]
|
||||
|
||||
* alias_method_chain works with accessor= methods also. #6153 [Caio Chassot]
|
||||
|
||||
@@ -696,13 +696,13 @@ public for compatibility. [Jeremy Kemper]
|
||||
self.attr_internal_naming_format = '@%s__rofl'
|
||||
attr_internal :foo
|
||||
|
||||
* Raise fully qualified names upon name errors. #5533 [lars@pinds.com, Nicholas Seckar]
|
||||
* Raise fully qualified names upon name errors. #5533 [Lars Pind, Nicholas Seckar]
|
||||
|
||||
* Add extention to obtain the missing constant from NameError instances. [Nicholas Seckar]
|
||||
|
||||
* Thoroughly document inflections. #5700 [petermichaux@gmail.com]
|
||||
|
||||
* Added Module#alias_attribute [Jamis/DHH]. Example:
|
||||
* Added Module#alias_attribute [Jamis/David Heinemeier Hansson]. Example:
|
||||
|
||||
class Content < ActiveRecord::Base
|
||||
# has a title attribute
|
||||
@@ -724,23 +724,23 @@ public for compatibility. [Jeremy Kemper]
|
||||
Provide your own per-environment in e.g. config/environments/development.rb:
|
||||
ActiveSupport::Deprecation.behavior = Proc.new { |message| raise message }
|
||||
|
||||
* First cut of the Rails Deprecation system. [Koz]
|
||||
* First cut of the Rails Deprecation system. [Michael Koziarski]
|
||||
|
||||
* Strip boolean XML content before checking for 'true' [Rick Olson]
|
||||
|
||||
* Customize default BigDecimal formatting. References #5672 [dave@pragprog.com]
|
||||
* Customize default BigDecimal formatting. References #5672 [Dave Thomas]
|
||||
|
||||
* Correctly convert <foo nil="true"> to nil when using Hash.create_from_xml. [Rick]
|
||||
* Correctly convert <foo nil="true"> to nil when using Hash.create_from_xml. [Rick Olson]
|
||||
|
||||
* Optional identity for Enumerable#sum defaults to zero. #5657 [gensym@mac.com]
|
||||
|
||||
* HashWithIndifferentAccess shouldn't confuse false and nil. #5601 [shugo@ruby-lang.org]
|
||||
* HashWithIndifferentAccess shouldn't confuse false and nil. #5601 [Shugo Maeda]
|
||||
|
||||
* Fixed HashWithIndifferentAccess#default #5586 [chris@seagul.co.uk]
|
||||
|
||||
* More compatible Hash.create_from_xml. #5523 [nunemaker@gmail.com]
|
||||
|
||||
* Added Enumerable#sum for calculating a sum from the elements [DHH, jonathan@daikini.com]. Examples:
|
||||
* Added Enumerable#sum for calculating a sum from the elements [David Heinemeier Hansson, jonathan@daikini.com]. Examples:
|
||||
|
||||
[1, 2, 3].sum
|
||||
payments.sum { |p| p.price * p.tax_rate }
|
||||
@@ -762,7 +762,7 @@ public for compatibility. [Jeremy Kemper]
|
||||
{1 => "one", 2 => "two", 3 => "three"}.sort_by(&:first).map(&:last)
|
||||
#=> ["one", "two", "three"]
|
||||
|
||||
* Added Hash.create_from_xml(string) which will create a hash from a XML string and even typecast if possible [DHH]. Example:
|
||||
* Added Hash.create_from_xml(string) which will create a hash from a XML string and even typecast if possible [David Heinemeier Hansson]. Example:
|
||||
|
||||
Hash.create_from_xml <<-EOT
|
||||
<note>
|
||||
@@ -775,7 +775,7 @@ public for compatibility. [Jeremy Kemper]
|
||||
|
||||
{ :note => { :title => "This is a note", :created_at => Date.new(2004, 10, 10) } }
|
||||
|
||||
* Added Jim Weirich's excellent FlexMock class to vendor (Copyright 2003, 2004 by Jim Weirich (jim@weriichhouse.org)) -- it's not automatically required, though, so require 'flexmock' is still necessary [DHH]
|
||||
* Added Jim Weirich's excellent FlexMock class to vendor (Copyright 2003, 2004 by Jim Weirich (jim@weriichhouse.org)) -- it's not automatically required, though, so require 'flexmock' is still necessary [David Heinemeier Hansson]
|
||||
|
||||
* Fixed that Module#alias_method_chain should work with both foo? foo! and foo at the same time #4954 [anna@wota.jp]
|
||||
|
||||
@@ -783,13 +783,13 @@ public for compatibility. [Jeremy Kemper]
|
||||
|
||||
* Add OrderedHash#values. [Sam Stephenson]
|
||||
|
||||
* Added Array#to_s(:db) that'll produce a comma-separated list of ids [DHH]. Example:
|
||||
* Added Array#to_s(:db) that'll produce a comma-separated list of ids [David Heinemeier Hansson]. Example:
|
||||
|
||||
Purchase.find(:all, :conditions => "product_id IN (#{shops.products.to_s(:db)})"
|
||||
|
||||
* Normalize classify's argument to a String so that it plays nice with Symbols. [Marcel Molina Jr.]
|
||||
|
||||
* Strip out leading schema name in classify. References #5139. [schoenm@earthlink.net]
|
||||
* Strip out leading schema name in classify. References #5139. [Michael Schoen]
|
||||
|
||||
* Remove Enumerable#first_match since break(value) handles the use case well enough. [Nicholas Seckar]
|
||||
|
||||
@@ -821,7 +821,7 @@ public for compatibility. [Jeremy Kemper]
|
||||
|
||||
* Added Module#alias_method_chain [Jamis Buck]
|
||||
|
||||
* Updated to Builder 2.0 [DHH]
|
||||
* Updated to Builder 2.0 [David Heinemeier Hansson]
|
||||
|
||||
* Add Array#split for dividing arrays into one or more subarrays by value or block. [Sam Stephenson]
|
||||
|
||||
@@ -852,7 +852,7 @@ public for compatibility. [Jeremy Kemper]
|
||||
|
||||
* Added Fixnum#seconds for consistency, so you can say 5.minutes + 30.seconds instead of 5.minutes + 30 #4389 [François Beausoleil]
|
||||
|
||||
* Added option to String#camelize to generate lower-cased camel case by passing in :lower, like "super_man".camelize(:lower) # => "superMan" [DHH]
|
||||
* Added option to String#camelize to generate lower-cased camel case by passing in :lower, like "super_man".camelize(:lower) # => "superMan" [David Heinemeier Hansson]
|
||||
|
||||
* Added Hash#diff to show the difference between two hashes [Chris McGrath]
|
||||
|
||||
@@ -860,12 +860,12 @@ public for compatibility. [Jeremy Kemper]
|
||||
|
||||
* Enhance Inflector.underscore to convert '-' into '_' (as the inverse of Inflector.dasherize) [Jamis Buck]
|
||||
|
||||
* Switched to_xml to use the xml schema format for datetimes. This allows the encoding of time zones and should improve operability. [Koz]
|
||||
* Switched to_xml to use the xml schema format for datetimes. This allows the encoding of time zones and should improve operability. [Michael Koziarski]
|
||||
|
||||
* Added a note to the documentation for the Date related Numeric extensions to indicate that they're
|
||||
approximations and shouldn't be used for critical calculations. [Koz]
|
||||
approximations and shouldn't be used for critical calculations. [Michael Koziarski]
|
||||
|
||||
* Added Hash#to_xml and Array#to_xml that makes it much easier to produce XML from basic structures [DHH]. Examples:
|
||||
* Added Hash#to_xml and Array#to_xml that makes it much easier to produce XML from basic structures [David Heinemeier Hansson]. Examples:
|
||||
|
||||
{ :name => "David", :street_name => "Paulina", :age => 26, :moved_on => Date.new(2005, 11, 15) }.to_xml
|
||||
|
||||
@@ -878,7 +878,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
|
||||
<moved-on type="date">2005-11-15</moved-on>
|
||||
</person>
|
||||
|
||||
* Moved Jim Weirich's wonderful Builder from Action Pack to Active Support (it's simply too useful to be stuck in AP) [DHH]
|
||||
* Moved Jim Weirich's wonderful Builder from Action Pack to Active Support (it's simply too useful to be stuck in AP) [David Heinemeier Hansson]
|
||||
|
||||
* Fixed that Array#to_sentence will return "" on an empty array instead of ", and" #3842, #4031 [rubyonrails@beautifulpixel.com]
|
||||
|
||||
@@ -907,7 +907,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
|
||||
|
||||
[Marcel Molina Jr., Sam Stephenson]
|
||||
|
||||
* Added Kernel#daemonize to turn the current process into a daemon that can be killed with a TERM signal [DHH]
|
||||
* Added Kernel#daemonize to turn the current process into a daemon that can be killed with a TERM signal [David Heinemeier Hansson]
|
||||
|
||||
* Add 'around' methods to Logger, to make it easy to log before and after messages for a given block as requested in #3809. [Michael Koziarski] Example:
|
||||
|
||||
@@ -922,7 +922,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
|
||||
|
||||
* Make String#last return the string instead of nil when it is shorter than the limit [Scott Barron].
|
||||
|
||||
* Added delegation support to Module that allows multiple delegations at once (unlike Forwardable in the stdlib) [DHH]. Example:
|
||||
* Added delegation support to Module that allows multiple delegations at once (unlike Forwardable in the stdlib) [David Heinemeier Hansson]. Example:
|
||||
|
||||
class Account < ActiveRecord::Base
|
||||
has_one :subscription
|
||||
@@ -957,7 +957,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
|
||||
include Reloadable
|
||||
end
|
||||
|
||||
Reloading a class is done by removing its constant which will cause it to be loaded again on the next reference. [DHH]
|
||||
Reloading a class is done by removing its constant which will cause it to be loaded again on the next reference. [David Heinemeier Hansson]
|
||||
|
||||
* Added auto-loading support for classes in modules, so Conductor::Migration will look for conductor/migration.rb and Conductor::Database::Settings will look for conductor/database/settings.rb [Nicholas Seckar]
|
||||
|
||||
@@ -965,7 +965,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
|
||||
|
||||
* Add Proc#bind(object) for changing a proc or block's self by returning a Method bound to the given object. Based on why the lucky stiff's "cloaker" method. [Sam Stephenson]
|
||||
|
||||
* Fix merge and dup for hashes with indifferent access #3404 [kenneth.miller@bitfield.net]
|
||||
* Fix merge and dup for hashes with indifferent access #3404 [Ken Miller]
|
||||
|
||||
* Fix the requires in option_merger_test to unbreak AS tests. [Sam Stephenson]
|
||||
|
||||
@@ -1072,7 +1072,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
|
||||
|
||||
* Fixed clean logger to work with Ruby 1.8.3 Logger class #2245
|
||||
|
||||
* Fixed memory leak with Active Record classes when Dependencies.mechanism = :load #1704 [c.r.mcgrath@gmail.com]
|
||||
* Fixed memory leak with Active Record classes when Dependencies.mechanism = :load #1704 [Chris McGrath]
|
||||
|
||||
* Fixed Inflector.underscore for use with acronyms, so HTML becomes html instead of htm_l #2173 [k@v2studio.com]
|
||||
|
||||
@@ -1082,11 +1082,11 @@ approximations and shouldn't be used for critical calculations. [Koz]
|
||||
|
||||
* Added Hash#reverse_merge, Hash#reverse_merge!, and Hash#reverse_update to ease the use of default options
|
||||
|
||||
* Added Array#to_sentence that'll turn ['one', 'two', 'three'] into "one, two, and three" #2157 [m.stienstra@fngtps.com]
|
||||
* Added Array#to_sentence that'll turn ['one', 'two', 'three'] into "one, two, and three" #2157 [Manfred Stienstra]
|
||||
|
||||
* Added Kernel#silence_warnings to turn off warnings temporarily for the passed block
|
||||
|
||||
* Added String#starts_with? and String#ends_with? #2118 [thijs@vandervossen.net]
|
||||
* Added String#starts_with? and String#ends_with? #2118 [Thijs van der Vossen]
|
||||
|
||||
* Added easy extendability to the inflector through Inflector.inflections (using the Inflector::Inflections singleton class). Examples:
|
||||
|
||||
@@ -1129,7 +1129,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
|
||||
|
||||
* Added new rules to the Inflector to deal with more unusual plurals mouse/louse => mice/lice, information => information, ox => oxen, virus => viri, archive => archives #1571, #1583, #1490, #1599, #1608 [foamdino@gmail.com/others]
|
||||
|
||||
* Fixed memory leak with Object#remove_subclasses_of, which inflicted a Rails application running in development mode with a ~20KB leak per request #1289 [c.r.mcgrath@gmail.com]
|
||||
* Fixed memory leak with Object#remove_subclasses_of, which inflicted a Rails application running in development mode with a ~20KB leak per request #1289 [Chris McGrath]
|
||||
|
||||
* Made 1.year == 365.25.days to account for leap years. This allows you to do User.find(:all, :conditions => ['birthday > ?', 50.years.ago]) without losing a lot of days. #1488 [tuxie@dekadance.se]
|
||||
|
||||
@@ -1164,7 +1164,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
|
||||
|
||||
* Fixed that in some circumstances controllers outside of modules may have hidden ones inside modules. For example, admin/content might have been hidden by /content. #1075 [Nicholas Seckar]
|
||||
|
||||
* Fixed inflection of perspectives and similar words #1045 [thijs@vandervossen.net]
|
||||
* Fixed inflection of perspectives and similar words #1045 [Thijs van der Vossen]
|
||||
|
||||
* Added Fixnum#even? and Fixnum#odd?
|
||||
|
||||
@@ -1206,7 +1206,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
|
||||
|
||||
* Added inflection rules for "sh" words, like "wish" and "fish" #755 [phillip@pjbsoftware.com]
|
||||
|
||||
* Fixed an exception when using Ajax based requests from Safari because Safari appends a \000 to the post body. Symbols can't have \000 in them so indifferent access would throw an exception in the constructor. Indifferent hashes now use strings internally instead. #746 [Tobias Luetke]
|
||||
* Fixed an exception when using Ajax based requests from Safari because Safari appends a \000 to the post body. Symbols can't have \000 in them so indifferent access would throw an exception in the constructor. Indifferent hashes now use strings internally instead. #746 [Tobias Lütke]
|
||||
|
||||
* Added String#to_time and String#to_date for wrapping ParseDate
|
||||
|
||||
@@ -1272,7 +1272,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
|
||||
|
||||
* Added Inflections as an extension on String, so Inflector.pluralize(Inflector.classify(name)) becomes name.classify.pluralize #476 [Jeremy Kemper]
|
||||
|
||||
* Added Byte operations to Numeric, so 5.5.megabytes + 200.kilobytes #461 [Marcel Molina]
|
||||
* Added Byte operations to Numeric, so 5.5.megabytes + 200.kilobytes #461 [Marcel Molina Jr.]
|
||||
|
||||
* Fixed that Dependencies.reload can't load the same file twice #420 [Kent Sibilev]
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -84,7 +84,7 @@ else
|
||||
end
|
||||
|
||||
app = Rack::Builder.new {
|
||||
use Rails::Rack::Logger
|
||||
use Rails::Rack::LogTailer unless options[:detach]
|
||||
use Rails::Rack::Static
|
||||
use Rails::Rack::Debugger if options[:debugger]
|
||||
run inner_app
|
||||
|
||||
@@ -155,6 +155,8 @@ module Rails
|
||||
initialize_framework_settings
|
||||
initialize_framework_views
|
||||
|
||||
initialize_metal
|
||||
|
||||
add_support_load_paths
|
||||
|
||||
load_gems
|
||||
@@ -533,6 +535,10 @@ Run `rake gems:install` to install the missing gems.
|
||||
end
|
||||
end
|
||||
|
||||
def initialize_metal
|
||||
configuration.middleware.use Rails::Rack::Metal
|
||||
end
|
||||
|
||||
# Initializes framework-specific settings for each of the loaded frameworks
|
||||
# (Configuration#frameworks). The available settings map to the accessors
|
||||
# on each of the corresponding Base classes.
|
||||
@@ -915,6 +921,7 @@ Run `rake gems:install` to install the missing gems.
|
||||
# Followed by the standard includes.
|
||||
paths.concat %w(
|
||||
app
|
||||
app/metal
|
||||
app/models
|
||||
app/controllers
|
||||
app/helpers
|
||||
@@ -933,6 +940,7 @@ Run `rake gems:install` to install the missing gems.
|
||||
|
||||
def default_eager_load_paths
|
||||
%w(
|
||||
app/metal
|
||||
app/models
|
||||
app/controllers
|
||||
app/helpers
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
module Rails
|
||||
module Rack
|
||||
autoload :Debugger, "rails/rack/debugger"
|
||||
autoload :Logger, "rails/rack/logger"
|
||||
autoload :LogTailer, "rails/rack/log_tailer"
|
||||
autoload :Metal, "rails/rack/metal"
|
||||
autoload :Static, "rails/rack/static"
|
||||
end
|
||||
end
|
||||
|
||||
31
railties/lib/rails/rack/cascade.rb
Normal file
31
railties/lib/rails/rack/cascade.rb
Normal file
@@ -0,0 +1,31 @@
|
||||
require 'active_support/ordered_hash'
|
||||
|
||||
module Rails
|
||||
module Rack
|
||||
# Try a request on several apps; return the first non-404 response.
|
||||
class Cascade
|
||||
attr_reader :apps
|
||||
|
||||
def initialize(apps)
|
||||
@apps = ActiveSupport::OrderedHash.new
|
||||
apps.each { |app| add app }
|
||||
end
|
||||
|
||||
def call(env)
|
||||
@apps.keys.each do |app|
|
||||
result = app.call(env)
|
||||
return result unless result[0].to_i == 404
|
||||
end
|
||||
Metal::NotFoundResponse
|
||||
end
|
||||
|
||||
def add(app)
|
||||
@apps[app] = true
|
||||
end
|
||||
|
||||
def include?(app)
|
||||
@apps.include?(app)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
35
railties/lib/rails/rack/log_tailer.rb
Normal file
35
railties/lib/rails/rack/log_tailer.rb
Normal file
@@ -0,0 +1,35 @@
|
||||
module Rails
|
||||
module Rack
|
||||
class LogTailer
|
||||
EnvironmentLog = "#{File.expand_path(Rails.root)}/log/#{Rails.env}.log"
|
||||
|
||||
def initialize(app, log = nil)
|
||||
@app = app
|
||||
|
||||
path = Pathname.new(log || EnvironmentLog).cleanpath
|
||||
@cursor = ::File.size(path)
|
||||
@last_checked = Time.now.to_f
|
||||
|
||||
@file = ::File.open(path, 'r')
|
||||
end
|
||||
|
||||
def call(env)
|
||||
response = @app.call(env)
|
||||
tail_log
|
||||
response
|
||||
end
|
||||
|
||||
def tail_log
|
||||
@file.seek @cursor
|
||||
|
||||
mod = @file.mtime.to_f
|
||||
if mod > @last_checked
|
||||
contents = @file.read
|
||||
@last_checked = mod
|
||||
@cursor += contents.size
|
||||
$stdout.print contents
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,28 +0,0 @@
|
||||
module Rails
|
||||
module Rack
|
||||
class Logger
|
||||
EnvironmentLog = "#{File.expand_path(Rails.root)}/log/#{Rails.env}.log"
|
||||
|
||||
def initialize(app, log = nil)
|
||||
@app = app
|
||||
@path = Pathname.new(log || EnvironmentLog).cleanpath
|
||||
@cursor = ::File.size(@path)
|
||||
@last_checked = Time.now
|
||||
end
|
||||
|
||||
def call(env)
|
||||
response = @app.call(env)
|
||||
::File.open(@path, 'r') do |f|
|
||||
f.seek @cursor
|
||||
if f.mtime > @last_checked
|
||||
contents = f.read
|
||||
@last_checked = f.mtime
|
||||
@cursor += contents.length
|
||||
print contents
|
||||
end
|
||||
end
|
||||
response
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
27
railties/lib/rails/rack/metal.rb
Normal file
27
railties/lib/rails/rack/metal.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
require 'rails/rack/cascade'
|
||||
|
||||
module Rails
|
||||
module Rack
|
||||
module Metal
|
||||
NotFoundResponse = [404, {}, []].freeze
|
||||
NotFound = lambda { NotFoundResponse }
|
||||
|
||||
class << self
|
||||
def new(app)
|
||||
Cascade.new(builtins + [app])
|
||||
end
|
||||
|
||||
def builtins
|
||||
base = "#{Rails.root}/app/metal"
|
||||
matcher = /\A#{Regexp.escape(base)}\/(.*)\.rb\Z/
|
||||
|
||||
Dir["#{base}/**/*.rb"].sort.map do |file|
|
||||
file.sub!(matcher, '\1')
|
||||
require file
|
||||
file.classify.constantize
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,8 @@
|
||||
Description:
|
||||
Cast some metal!
|
||||
|
||||
Examples:
|
||||
`./script/generate metal poller`
|
||||
|
||||
This will create:
|
||||
Metal: app/metal/poller.rb
|
||||
@@ -0,0 +1,8 @@
|
||||
class MetalGenerator < Rails::Generator::NamedBase
|
||||
def manifest
|
||||
record do |m|
|
||||
m.directory 'app/metal'
|
||||
m.template 'metal.rb', File.join('app/metal', "#{file_name}.rb")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,12 @@
|
||||
# Allow the metal piece to run in isolation
|
||||
require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails)
|
||||
|
||||
class <%= class_name %>
|
||||
def self.call(env)
|
||||
if env["PATH_INFO"] =~ /^\/<%= file_name %>/
|
||||
[200, {"Content-Type" => "text/html"}, ["Hello, World!"]]
|
||||
else
|
||||
[404, {"Content-Type" => "text/html"}, ["Not Found"]]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
desc 'Prints out your Rack middleware stack'
|
||||
task :middleware => :environment do
|
||||
ActionController::Dispatcher.middleware.each do |middleware|
|
||||
ActionController::Dispatcher.middleware.active.each do |middleware|
|
||||
puts "use #{middleware.inspect}"
|
||||
end
|
||||
puts "run ActionController::Dispatcher.new"
|
||||
|
||||
Reference in New Issue
Block a user