mirror of
https://github.com/github/rails.git
synced 2026-01-12 08:08:31 -05:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44d721c193 | ||
|
|
642da856d5 | ||
|
|
63edc022f1 | ||
|
|
2c96f509a8 | ||
|
|
1c207dfbfc | ||
|
|
5a5b0b8b3b | ||
|
|
b96db52878 | ||
|
|
0e57097437 | ||
|
|
0a1deae366 | ||
|
|
833d3e46ab | ||
|
|
6a386ea706 | ||
|
|
9d03813e91 | ||
|
|
5b714e7197 | ||
|
|
f0000281ff | ||
|
|
41adf8710e | ||
|
|
2dbceab361 | ||
|
|
a550d2aadb | ||
|
|
a77e57f4e3 | ||
|
|
e1b3a441ed | ||
|
|
8b63dd09c0 | ||
|
|
0dad92a790 | ||
|
|
e3a39ca91d | ||
|
|
8e556e5d74 | ||
|
|
a9113b8895 | ||
|
|
b1aa67410a | ||
|
|
291dad2411 | ||
|
|
0f225c0f3e | ||
|
|
75aef09ee1 | ||
|
|
c71de03306 | ||
|
|
81991d6913 | ||
|
|
68477ad16d | ||
|
|
569a78cbe0 | ||
|
|
2a07886be3 | ||
|
|
36fa00a1eb | ||
|
|
c31a04aba3 | ||
|
|
f09a529138 | ||
|
|
6997918a9d | ||
|
|
f3b382692a | ||
|
|
1752dccb34 | ||
|
|
207473bed8 | ||
|
|
3a90e72e6f | ||
|
|
ca66f4471f | ||
|
|
e2ce901a78 | ||
|
|
3006870c1a | ||
|
|
3328171a40 | ||
|
|
09b1021b99 | ||
|
|
9b31893668 | ||
|
|
002e73a1ee | ||
|
|
b2eca732ba | ||
|
|
274ef21104 | ||
|
|
24abd43d56 | ||
|
|
083b0b7f3f | ||
|
|
fa7e937e67 | ||
|
|
131acec685 | ||
|
|
c48a1fc213 | ||
|
|
9bd987f28b |
@@ -1,3 +1,12 @@
|
||||
*2.0.3* (12th May 2008)
|
||||
|
||||
* 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]
|
||||
|
||||
* Fixed that you don't have to call super in ActionMailer::TestCase#setup #10406 [jamesgolick]
|
||||
|
||||
|
||||
*2.0.2* (December 16th, 2007)
|
||||
|
||||
* Included in Rails 2.0.2
|
||||
|
||||
@@ -55,7 +55,7 @@ spec = Gem::Specification.new do |s|
|
||||
s.rubyforge_project = "actionmailer"
|
||||
s.homepage = "http://www.rubyonrails.org"
|
||||
|
||||
s.add_dependency('actionpack', '= 2.0.2' + PKG_BUILD)
|
||||
s.add_dependency('actionpack', '= 2.0.3' + PKG_BUILD)
|
||||
|
||||
s.has_rdoc = true
|
||||
s.requirements << 'none'
|
||||
|
||||
@@ -463,7 +463,10 @@ module ActionMailer #:nodoc:
|
||||
# no alternate has been given as the parameter, this will fail.
|
||||
def deliver!(mail = @mail)
|
||||
raise "no mail object available for delivery!" unless mail
|
||||
logger.info "Sent mail:\n #{mail.encoded}" unless logger.nil?
|
||||
unless logger.nil?
|
||||
logger.info "Sent mail to #{recipients.to_a.join(', ')}"
|
||||
logger.debug "\n#{mail.encoded}"
|
||||
end
|
||||
|
||||
begin
|
||||
__send__("perform_delivery_#{delivery_method}", mail) if perform_deliveries
|
||||
|
||||
@@ -33,7 +33,7 @@ module ActionMailer
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
def setup_with_mailer
|
||||
ActionMailer::Base.delivery_method = :test
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.deliveries = []
|
||||
@@ -42,6 +42,19 @@ module ActionMailer
|
||||
@expected.set_content_type "text", "plain", { "charset" => charset }
|
||||
@expected.mime_version = '1.0'
|
||||
end
|
||||
alias_method :setup, :setup_with_mailer
|
||||
|
||||
def self.method_added(method)
|
||||
if method.to_s == 'setup'
|
||||
unless method_defined?(:setup_without_mailer)
|
||||
alias_method :setup_without_mailer, :setup
|
||||
define_method(:setup) do
|
||||
setup_with_mailer
|
||||
setup_without_mailer
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def charset
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
require 'rubygems'
|
||||
|
||||
begin
|
||||
gem 'tmail', '~> 1.1.0'
|
||||
gem 'tmail', '~> 1.2.1'
|
||||
rescue Gem::LoadError
|
||||
$:.unshift "#{File.dirname(__FILE__)}/vendor/tmail-1.1.0"
|
||||
$:.unshift "#{File.dirname(__FILE__)}/vendor/tmail-1.2.1"
|
||||
end
|
||||
|
||||
begin
|
||||
|
||||
@@ -1,540 +0,0 @@
|
||||
=begin rdoc
|
||||
|
||||
= Facade.rb Provides an interface to the TMail object
|
||||
|
||||
=end
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
require 'tmail/utils'
|
||||
|
||||
module TMail
|
||||
|
||||
class Mail
|
||||
|
||||
def header_string( name, default = nil )
|
||||
h = @header[name.downcase] or return default
|
||||
h.to_s
|
||||
end
|
||||
|
||||
###
|
||||
### attributes
|
||||
###
|
||||
|
||||
include TextUtils
|
||||
|
||||
def set_string_array_attr( key, strs )
|
||||
strs.flatten!
|
||||
if strs.empty?
|
||||
@header.delete key.downcase
|
||||
else
|
||||
store key, strs.join(', ')
|
||||
end
|
||||
strs
|
||||
end
|
||||
private :set_string_array_attr
|
||||
|
||||
def set_string_attr( key, str )
|
||||
if str
|
||||
store key, str
|
||||
else
|
||||
@header.delete key.downcase
|
||||
end
|
||||
str
|
||||
end
|
||||
private :set_string_attr
|
||||
|
||||
def set_addrfield( name, arg )
|
||||
if arg
|
||||
h = HeaderField.internal_new(name, @config)
|
||||
h.addrs.replace [arg].flatten
|
||||
@header[name] = h
|
||||
else
|
||||
@header.delete name
|
||||
end
|
||||
arg
|
||||
end
|
||||
private :set_addrfield
|
||||
|
||||
def addrs2specs( addrs )
|
||||
return nil unless addrs
|
||||
list = addrs.map {|addr|
|
||||
if addr.address_group?
|
||||
then addr.map {|a| a.spec }
|
||||
else addr.spec
|
||||
end
|
||||
}.flatten
|
||||
return nil if list.empty?
|
||||
list
|
||||
end
|
||||
private :addrs2specs
|
||||
|
||||
#
|
||||
# date time
|
||||
#
|
||||
|
||||
def date( default = nil )
|
||||
if h = @header['date']
|
||||
h.date
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def date=( time )
|
||||
if time
|
||||
store 'Date', time2str(time)
|
||||
else
|
||||
@header.delete 'date'
|
||||
end
|
||||
time
|
||||
end
|
||||
|
||||
def strftime( fmt, default = nil )
|
||||
if t = date
|
||||
t.strftime(fmt)
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# destination
|
||||
#
|
||||
|
||||
def to_addrs( default = nil )
|
||||
if h = @header['to']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def cc_addrs( default = nil )
|
||||
if h = @header['cc']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def bcc_addrs( default = nil )
|
||||
if h = @header['bcc']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def to_addrs=( arg )
|
||||
set_addrfield 'to', arg
|
||||
end
|
||||
|
||||
def cc_addrs=( arg )
|
||||
set_addrfield 'cc', arg
|
||||
end
|
||||
|
||||
def bcc_addrs=( arg )
|
||||
set_addrfield 'bcc', arg
|
||||
end
|
||||
|
||||
def to( default = nil )
|
||||
addrs2specs(to_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def cc( default = nil )
|
||||
addrs2specs(cc_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def bcc( default = nil )
|
||||
addrs2specs(bcc_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def to=( *strs )
|
||||
set_string_array_attr 'To', strs
|
||||
end
|
||||
|
||||
def cc=( *strs )
|
||||
set_string_array_attr 'Cc', strs
|
||||
end
|
||||
|
||||
def bcc=( *strs )
|
||||
set_string_array_attr 'Bcc', strs
|
||||
end
|
||||
|
||||
#
|
||||
# originator
|
||||
#
|
||||
|
||||
def from_addrs( default = nil )
|
||||
if h = @header['from']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def from_addrs=( arg )
|
||||
set_addrfield 'from', arg
|
||||
end
|
||||
|
||||
def from( default = nil )
|
||||
addrs2specs(from_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def from=( *strs )
|
||||
set_string_array_attr 'From', strs
|
||||
end
|
||||
|
||||
def friendly_from( default = nil )
|
||||
h = @header['from']
|
||||
a, = h.addrs
|
||||
return default unless a
|
||||
return a.phrase if a.phrase
|
||||
return h.comments.join(' ') unless h.comments.empty?
|
||||
a.spec
|
||||
end
|
||||
|
||||
|
||||
def reply_to_addrs( default = nil )
|
||||
if h = @header['reply-to']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def reply_to_addrs=( arg )
|
||||
set_addrfield 'reply-to', arg
|
||||
end
|
||||
|
||||
def reply_to( default = nil )
|
||||
addrs2specs(reply_to_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def reply_to=( *strs )
|
||||
set_string_array_attr 'Reply-To', strs
|
||||
end
|
||||
|
||||
|
||||
def sender_addr( default = nil )
|
||||
f = @header['sender'] or return default
|
||||
f.addr or return default
|
||||
end
|
||||
|
||||
def sender_addr=( addr )
|
||||
if addr
|
||||
h = HeaderField.internal_new('sender', @config)
|
||||
h.addr = addr
|
||||
@header['sender'] = h
|
||||
else
|
||||
@header.delete 'sender'
|
||||
end
|
||||
addr
|
||||
end
|
||||
|
||||
def sender( default )
|
||||
f = @header['sender'] or return default
|
||||
a = f.addr or return default
|
||||
a.spec
|
||||
end
|
||||
|
||||
def sender=( str )
|
||||
set_string_attr 'Sender', str
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# subject
|
||||
#
|
||||
|
||||
def subject( default = nil )
|
||||
if h = @header['subject']
|
||||
h.body
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
alias quoted_subject subject
|
||||
|
||||
def subject=( str )
|
||||
set_string_attr 'Subject', str
|
||||
end
|
||||
|
||||
#
|
||||
# identity & threading
|
||||
#
|
||||
|
||||
def message_id( default = nil )
|
||||
if h = @header['message-id']
|
||||
h.id || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def message_id=( str )
|
||||
set_string_attr 'Message-Id', str
|
||||
end
|
||||
|
||||
def in_reply_to( default = nil )
|
||||
if h = @header['in-reply-to']
|
||||
h.ids
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def in_reply_to=( *idstrs )
|
||||
set_string_array_attr 'In-Reply-To', idstrs
|
||||
end
|
||||
|
||||
def references( default = nil )
|
||||
if h = @header['references']
|
||||
h.refs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def references=( *strs )
|
||||
set_string_array_attr 'References', strs
|
||||
end
|
||||
|
||||
#
|
||||
# MIME headers
|
||||
#
|
||||
|
||||
def mime_version( default = nil )
|
||||
if h = @header['mime-version']
|
||||
h.version || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def mime_version=( m, opt = nil )
|
||||
if opt
|
||||
if h = @header['mime-version']
|
||||
h.major = m
|
||||
h.minor = opt
|
||||
else
|
||||
store 'Mime-Version', "#{m}.#{opt}"
|
||||
end
|
||||
else
|
||||
store 'Mime-Version', m
|
||||
end
|
||||
m
|
||||
end
|
||||
|
||||
def content_type( default = nil )
|
||||
if h = @header['content-type']
|
||||
h.content_type || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def main_type( default = nil )
|
||||
if h = @header['content-type']
|
||||
h.main_type || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def sub_type( default = nil )
|
||||
if h = @header['content-type']
|
||||
h.sub_type || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def set_content_type( str, sub = nil, param = nil )
|
||||
if sub
|
||||
main, sub = str, sub
|
||||
else
|
||||
main, sub = str.split(%r</>, 2)
|
||||
raise ArgumentError, "sub type missing: #{str.inspect}" unless sub
|
||||
end
|
||||
if h = @header['content-type']
|
||||
h.main_type = main
|
||||
h.sub_type = sub
|
||||
h.params.clear
|
||||
else
|
||||
store 'Content-Type', "#{main}/#{sub}"
|
||||
end
|
||||
@header['content-type'].params.replace param if param
|
||||
str
|
||||
end
|
||||
|
||||
alias content_type= set_content_type
|
||||
|
||||
def type_param( name, default = nil )
|
||||
if h = @header['content-type']
|
||||
h[name] || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def charset( default = nil )
|
||||
if h = @header['content-type']
|
||||
h['charset'] or default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def charset=( str )
|
||||
if str
|
||||
if h = @header[ 'content-type' ]
|
||||
h['charset'] = str
|
||||
else
|
||||
store 'Content-Type', "text/plain; charset=#{str}"
|
||||
end
|
||||
end
|
||||
str
|
||||
end
|
||||
|
||||
def transfer_encoding( default = nil )
|
||||
if h = @header['content-transfer-encoding']
|
||||
h.encoding || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def transfer_encoding=( str )
|
||||
set_string_attr 'Content-Transfer-Encoding', str
|
||||
end
|
||||
|
||||
alias encoding transfer_encoding
|
||||
alias encoding= transfer_encoding=
|
||||
alias content_transfer_encoding transfer_encoding
|
||||
alias content_transfer_encoding= transfer_encoding=
|
||||
|
||||
def disposition( default = nil )
|
||||
if h = @header['content-disposition']
|
||||
h.disposition || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
alias content_disposition disposition
|
||||
|
||||
def set_disposition( str, params = nil )
|
||||
if h = @header['content-disposition']
|
||||
h.disposition = str
|
||||
h.params.clear
|
||||
else
|
||||
store('Content-Disposition', str)
|
||||
h = @header['content-disposition']
|
||||
end
|
||||
h.params.replace params if params
|
||||
end
|
||||
|
||||
alias disposition= set_disposition
|
||||
alias set_content_disposition set_disposition
|
||||
alias content_disposition= set_disposition
|
||||
|
||||
def disposition_param( name, default = nil )
|
||||
if h = @header['content-disposition']
|
||||
h[name] || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
###
|
||||
### utils
|
||||
###
|
||||
|
||||
def create_reply
|
||||
mail = TMail::Mail.parse('')
|
||||
mail.subject = 'Re: ' + subject('').sub(/\A(?:\[[^\]]+\])?(?:\s*Re:)*\s*/i, '')
|
||||
mail.to_addrs = reply_addresses([])
|
||||
mail.in_reply_to = [message_id(nil)].compact
|
||||
mail.references = references([]) + [message_id(nil)].compact
|
||||
mail.mime_version = '1.0'
|
||||
mail
|
||||
end
|
||||
|
||||
def base64_encode
|
||||
store 'Content-Transfer-Encoding', 'Base64'
|
||||
self.body = Base64.folding_encode(self.body)
|
||||
end
|
||||
|
||||
def base64_decode
|
||||
if /base64/i === self.transfer_encoding('')
|
||||
store 'Content-Transfer-Encoding', '8bit'
|
||||
self.body = Base64.decode(self.body, @config.strict_base64decode?)
|
||||
end
|
||||
end
|
||||
|
||||
def destinations( default = nil )
|
||||
ret = []
|
||||
%w( to cc bcc ).each do |nm|
|
||||
if h = @header[nm]
|
||||
h.addrs.each {|i| ret.push i.address }
|
||||
end
|
||||
end
|
||||
ret.empty? ? default : ret
|
||||
end
|
||||
|
||||
def each_destination( &block )
|
||||
destinations([]).each do |i|
|
||||
if Address === i
|
||||
yield i
|
||||
else
|
||||
i.each(&block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
alias each_dest each_destination
|
||||
|
||||
def reply_addresses( default = nil )
|
||||
reply_to_addrs(nil) or from_addrs(nil) or default
|
||||
end
|
||||
|
||||
def error_reply_addresses( default = nil )
|
||||
if s = sender(nil)
|
||||
[s]
|
||||
else
|
||||
from_addrs(default)
|
||||
end
|
||||
end
|
||||
|
||||
def multipart?
|
||||
main_type('').downcase == 'multipart'
|
||||
end
|
||||
|
||||
end # class Mail
|
||||
|
||||
end # module TMail
|
||||
@@ -1,36 +1,35 @@
|
||||
=begin rdoc
|
||||
|
||||
= Text Encoding class
|
||||
|
||||
=end
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
# = TITLE:
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
# Text Encoding class
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
# = COPYRIGHT:
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
|
||||
require 'nkf'
|
||||
require 'tmail/base64.rb'
|
||||
require 'tmail/base64'
|
||||
require 'tmail/stringio'
|
||||
require 'tmail/utils'
|
||||
|
||||
@@ -52,25 +51,25 @@ module TMail
|
||||
end
|
||||
end
|
||||
module_function :create_dest
|
||||
|
||||
|
||||
def encoded( eol = "\r\n", charset = 'j', dest = nil )
|
||||
accept_strategy Encoder, eol, charset, dest
|
||||
end
|
||||
|
||||
|
||||
def decoded( eol = "\n", charset = 'e', dest = nil )
|
||||
# Turn the E-Mail into a string and return it with all
|
||||
# encoded characters decoded. alias for to_s
|
||||
accept_strategy Decoder, eol, charset, dest
|
||||
end
|
||||
|
||||
|
||||
alias to_s decoded
|
||||
|
||||
|
||||
def accept_strategy( klass, eol, charset, dest = nil )
|
||||
dest ||= ''
|
||||
accept klass.new( create_dest(dest), charset, eol )
|
||||
dest
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -121,7 +120,7 @@ module TMail
|
||||
def header_body( str )
|
||||
@f << decode(str)
|
||||
end
|
||||
|
||||
|
||||
def space
|
||||
@f << ' '
|
||||
end
|
||||
@@ -131,7 +130,7 @@ module TMail
|
||||
def lwsp( str )
|
||||
@f << str
|
||||
end
|
||||
|
||||
|
||||
def meta( str )
|
||||
@f << str
|
||||
end
|
||||
@@ -182,7 +181,8 @@ module TMail
|
||||
end
|
||||
|
||||
SPACER = "\t"
|
||||
MAX_LINE_LEN = 70
|
||||
MAX_LINE_LEN = 78
|
||||
RFC_2822_MAX_LENGTH = 998
|
||||
|
||||
OPTIONS = {
|
||||
'EUC' => '-Ej -m0',
|
||||
@@ -202,7 +202,7 @@ module TMail
|
||||
def preserve_quotes=( bool )
|
||||
@preserve_quotes
|
||||
end
|
||||
|
||||
|
||||
def preserve_quotes
|
||||
@preserve_quotes
|
||||
end
|
||||
@@ -378,7 +378,7 @@ module TMail
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
METHOD_ID = {
|
||||
?j => :extract_J,
|
||||
?e => :extract_E,
|
||||
@@ -451,31 +451,73 @@ module TMail
|
||||
# puts '---- lwsp -------------------------------------'
|
||||
# puts "+ #{lwsp.inspect}"
|
||||
fold if restsize() <= 0
|
||||
flush
|
||||
flush(@folded)
|
||||
@lwsp = lwsp
|
||||
end
|
||||
|
||||
def flush
|
||||
def flush(folded = false)
|
||||
# puts '---- flush ----'
|
||||
# puts "spc >>>#{@lwsp.inspect}<<<"
|
||||
# puts "txt >>>#{@text.inspect}<<<"
|
||||
@f << @lwsp << @text
|
||||
@curlen += (@lwsp.size + @text.size)
|
||||
if folded
|
||||
@curlen = 0
|
||||
else
|
||||
@curlen += (@lwsp.size + @text.size)
|
||||
end
|
||||
@text = ''
|
||||
@lwsp = ''
|
||||
end
|
||||
|
||||
def fold
|
||||
# puts '---- fold ----'
|
||||
@f << @eol
|
||||
unless @f.string =~ /^.*?:$/
|
||||
@f << @eol
|
||||
@lwsp = SPACER
|
||||
else
|
||||
fold_header
|
||||
@folded = true
|
||||
end
|
||||
@curlen = 0
|
||||
@lwsp = SPACER
|
||||
end
|
||||
|
||||
def fold_header
|
||||
# Called because line is too long - so we need to wrap.
|
||||
# First look for whitespace in the text
|
||||
# if it has text, fold there
|
||||
# check the remaining text, if too long, fold again
|
||||
# if it doesn't, then don't fold unless the line goes beyond 998 chars
|
||||
|
||||
# Check the text to see if there is whitespace, or if not
|
||||
@wrapped_text = []
|
||||
until @text == ''
|
||||
fold_the_string
|
||||
end
|
||||
@text = @wrapped_text.join("#{@eol}#{SPACER}")
|
||||
end
|
||||
|
||||
def fold_the_string
|
||||
whitespace_location = @text =~ /\s/ || @text.length
|
||||
# Is the location of the whitespace shorter than the RCF_2822_MAX_LENGTH?
|
||||
# if there is no whitespace in the string, then this
|
||||
unless mazsize(whitespace_location) <= 0
|
||||
@wrapped_text << @text.slice!(0...whitespace_location)
|
||||
# If it is not less, we have to wrap it destructively
|
||||
else
|
||||
slice_point = RFC_2822_MAX_LENGTH - @curlen - @lwsp.length
|
||||
@wrapped_text << @text.slice!(0...slice_point)
|
||||
end
|
||||
end
|
||||
|
||||
def restsize
|
||||
MAX_LINE_LEN - (@curlen + @lwsp.size + @text.size)
|
||||
end
|
||||
|
||||
def mazsize(whitespace_location)
|
||||
# Per RFC2822, the maximum length of a line is 998 chars
|
||||
RFC_2822_MAX_LENGTH - (@curlen + @lwsp.size + whitespace_location)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end # module TMail
|
||||
@@ -54,8 +54,40 @@ module TMail
|
||||
klass.newobj body, conf
|
||||
end
|
||||
|
||||
# Returns a HeaderField object matching the header you specify in the "name" param.
|
||||
# Requires an initialized TMail::Port to be passed in.
|
||||
#
|
||||
# The method searches the header of the Port you pass into it to find a match on
|
||||
# the header line you pass. Once a match is found, it will unwrap the matching line
|
||||
# as needed to return an initialized HeaderField object.
|
||||
#
|
||||
# If you want to get the Envelope sender of the email object, pass in "EnvelopeSender",
|
||||
# if you want the From address of the email itself, pass in 'From'.
|
||||
#
|
||||
# This is because a mailbox doesn't have the : after the From that designates the
|
||||
# beginning of the envelope sender (which can be different to the from address of
|
||||
# the emial)
|
||||
#
|
||||
# Other fields can be passed as normal, "Reply-To", "Received" etc.
|
||||
#
|
||||
# Note: Change of behaviour in 1.2.1 => returns nil if it does not find the specified
|
||||
# header field, otherwise returns an instantiated object of the correct header class
|
||||
#
|
||||
# For example:
|
||||
# port = TMail::FilePort.new("/test/fixtures/raw_email_simple")
|
||||
# h = TMail::HeaderField.new_from_port(port, "From")
|
||||
# h.addrs.to_s #=> "Mikel Lindsaar <mikel@nowhere.com>"
|
||||
# h = TMail::HeaderField.new_from_port(port, "EvelopeSender")
|
||||
# h.addrs.to_s #=> "mike@anotherplace.com.au"
|
||||
# h = TMail::HeaderField.new_from_port(port, "SomeWeirdHeaderField")
|
||||
# h #=> nil
|
||||
def new_from_port( port, name, conf = DEFAULT_CONFIG )
|
||||
re = Regep.new('\A(' + Regexp.quote(name) + '):', 'i')
|
||||
if name == "EnvelopeSender"
|
||||
name = "From"
|
||||
re = Regexp.new('\A(From) ', 'i')
|
||||
else
|
||||
re = Regexp.new('\A(' + Regexp.quote(name) + '):', 'i')
|
||||
end
|
||||
str = nil
|
||||
port.ropen {|f|
|
||||
f.each do |line|
|
||||
@@ -66,7 +98,7 @@ module TMail
|
||||
end
|
||||
end
|
||||
}
|
||||
new(name, str, Config.to_config(conf))
|
||||
new(name, str, Config.to_config(conf)) if str
|
||||
end
|
||||
|
||||
def internal_new( name, conf )
|
||||
1123
actionmailer/lib/action_mailer/vendor/tmail-1.2.1/tmail/interface.rb
vendored
Normal file
1123
actionmailer/lib/action_mailer/vendor/tmail-1.2.1/tmail/interface.rb
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -28,6 +28,23 @@
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
# == TMail::Mail
|
||||
# === Class Methods
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
require 'tmail/interface'
|
||||
require 'tmail/encode'
|
||||
@@ -162,6 +179,14 @@ module TMail
|
||||
@header.dup
|
||||
end
|
||||
|
||||
# Returns a TMail::AddressHeader object of the field you are querying.
|
||||
# Examples:
|
||||
# @mail['from'] #=> #<TMail::AddressHeader "mikel@test.com.au">
|
||||
# @mail['to'] #=> #<TMail::AddressHeader "mikel@test.com.au">
|
||||
#
|
||||
# You can get the string value of this by passing "to_s" to the query:
|
||||
# Example:
|
||||
# @mail['to'].to_s #=> "mikel@test.com.au"
|
||||
def []( key )
|
||||
@header[key.downcase]
|
||||
end
|
||||
@@ -172,6 +197,19 @@ module TMail
|
||||
|
||||
alias fetch []
|
||||
|
||||
# Allows you to set or delete TMail header objects at will.
|
||||
# Eamples:
|
||||
# @mail = TMail::Mail.new
|
||||
# @mail['to'].to_s # => 'mikel@test.com.au'
|
||||
# @mail['to'] = 'mikel@elsewhere.org'
|
||||
# @mail['to'].to_s # => 'mikel@elsewhere.org'
|
||||
# @mail.encoded # => "To: mikel@elsewhere.org\r\n\r\n"
|
||||
# @mail['to'] = nil
|
||||
# @mail['to'].to_s # => nil
|
||||
# @mail.encoded # => "\r\n"
|
||||
#
|
||||
# Note: setting mail[] = nil actualy deletes the header field in question from the object,
|
||||
# it does not just set the value of the hash to nil
|
||||
def []=( key, val )
|
||||
dkey = key.downcase
|
||||
|
||||
@@ -203,7 +241,14 @@ module TMail
|
||||
end
|
||||
|
||||
alias store []=
|
||||
|
||||
|
||||
# Allows you to loop through each header in the TMail::Mail object in a block
|
||||
# Example:
|
||||
# @mail['to'] = 'mikel@elsewhere.org'
|
||||
# @mail['from'] = 'me@me.com'
|
||||
# @mail.each_header { |k,v| puts "#{k} = #{v}" }
|
||||
# # => from = me@me.com
|
||||
# # => to = mikel@elsewhere.org
|
||||
def each_header
|
||||
@header.each do |key, val|
|
||||
[val].flatten.each {|v| yield key, v }
|
||||
@@ -213,13 +213,13 @@ module TMail
|
||||
fromaddr(), TextUtils.time2str(File.mtime(port.filename))
|
||||
end
|
||||
|
||||
def UNIXMbox.fromaddr
|
||||
def UNIXMbox.fromaddr(port)
|
||||
h = HeaderField.new_from_port(port, 'Return-Path') ||
|
||||
HeaderField.new_from_port(port, 'From') or return 'nobody'
|
||||
HeaderField.new_from_port(port, 'From') ||
|
||||
HeaderField.new_from_port(port, 'EnvelopeSender') or return 'nobody'
|
||||
a = h.addrs[0] or return 'nobody'
|
||||
a.spec
|
||||
end
|
||||
private_class_method :fromaddr
|
||||
|
||||
def close
|
||||
return if @closed
|
||||
@@ -128,41 +128,6 @@ module TMail
|
||||
'using C.T.Encoding with multipart mail is not permitted'
|
||||
end
|
||||
end
|
||||
|
||||
def create_empty_mail
|
||||
self.class.new(StringPort.new(''), @config)
|
||||
end
|
||||
|
||||
def create_reply
|
||||
setup_reply create_empty_mail()
|
||||
end
|
||||
|
||||
def setup_reply( m )
|
||||
if tmp = reply_addresses(nil)
|
||||
m.to_addrs = tmp
|
||||
end
|
||||
|
||||
mid = message_id(nil)
|
||||
tmp = references(nil) || []
|
||||
tmp.push mid if mid
|
||||
m.in_reply_to = [mid] if mid
|
||||
m.references = tmp unless tmp.empty?
|
||||
m.subject = 'Re: ' + subject('').sub(/\A(?:\s*re:)+/i, '')
|
||||
|
||||
m
|
||||
end
|
||||
|
||||
def create_forward
|
||||
setup_forward create_empty_mail()
|
||||
end
|
||||
|
||||
def setup_forward( mail )
|
||||
m = Mail.new(StringPort.new(''))
|
||||
m.body = decoded
|
||||
m.set_content_type 'message', 'rfc822'
|
||||
m.encoding = encoding('7bit')
|
||||
mail.parts.push m
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -13,12 +13,12 @@ module TMail
|
||||
from_charset = sub_header("content-type", "charset")
|
||||
case (content_transfer_encoding || "7bit").downcase
|
||||
when "quoted-printable"
|
||||
# the default charset is set to iso-8859-1 instead of 'us-ascii'.
|
||||
# the default charset is set to iso-8859-1 instead of 'us-ascii'.
|
||||
# This is needed as many mailer do not set the charset but send in ISO. This is only used if no charset is set.
|
||||
if !from_charset.blank? && from_charset.downcase == 'us-ascii'
|
||||
from_charset = 'iso-8859-1'
|
||||
end
|
||||
|
||||
|
||||
Unquoter.unquote_quoted_printable_and_convert_to(quoted_body,
|
||||
to_charset, from_charset, true)
|
||||
when "base64"
|
||||
@@ -35,9 +35,9 @@ module TMail
|
||||
|
||||
def body(to_charset = 'utf-8', &block)
|
||||
attachment_presenter = block || Proc.new { |file_name| "Attachment: #{file_name}\n" }
|
||||
|
||||
|
||||
if multipart?
|
||||
parts.collect { |part|
|
||||
parts.collect { |part|
|
||||
header = part["content-type"]
|
||||
|
||||
if part.multipart?
|
||||
@@ -81,13 +81,13 @@ module TMail
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def unquote_quoted_printable_and_convert_to(text, to, from, preserve_underscores=false)
|
||||
text = text.gsub(/_/, " ") unless preserve_underscores
|
||||
text = text.gsub(/\r\n|\r/, "\n") # normalize newlines
|
||||
convert_to(text.unpack("M*").first, to, from)
|
||||
end
|
||||
|
||||
|
||||
def unquote_base64_and_convert_to(text, to, from)
|
||||
convert_to(Base64.decode(text), to, from)
|
||||
end
|
||||
@@ -116,27 +116,3 @@ module TMail
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if __FILE__ == $0
|
||||
require 'test/unit'
|
||||
|
||||
class TC_Unquoter < Test::Unit::TestCase
|
||||
def test_unquote_quoted_printable
|
||||
a ="=?ISO-8859-1?Q?[166417]_Bekr=E6ftelse_fra_Rejsefeber?="
|
||||
b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
|
||||
assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
|
||||
end
|
||||
|
||||
def test_unquote_base64
|
||||
a ="=?ISO-8859-1?B?WzE2NjQxN10gQmVrcuZmdGVsc2UgZnJhIFJlanNlZmViZXI=?="
|
||||
b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
|
||||
assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
|
||||
end
|
||||
|
||||
def test_unquote_without_charset
|
||||
a ="[166417]_Bekr=E6ftelse_fra_Rejsefeber"
|
||||
b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
|
||||
assert_equal "[166417]_Bekr=E6ftelse_fra_Rejsefeber", b
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -30,14 +30,18 @@
|
||||
#++
|
||||
|
||||
require 'tmail/utils'
|
||||
require 'tmail/config'
|
||||
|
||||
module TMail
|
||||
require 'tmail/scanner_r.rb'
|
||||
# NOTE: It woiuld be nice if these two libs could boith be called "tmailscanner", and
|
||||
# the native extension would have precedence. However RubyGems boffs that up b/c
|
||||
# it does not gaurantee load_path order.
|
||||
begin
|
||||
raise LoadError, 'Turn off Ruby extention by user choice' if ENV['NORUBYEXT']
|
||||
require 'tmail/scanner_c.so'
|
||||
Scanner = Scanner_C
|
||||
raise LoadError, 'Turned off native extentions by user choice' if ENV['NORUBYEXT']
|
||||
require('tmail/tmailscanner') # c extension
|
||||
Scanner = TMailScanner
|
||||
rescue LoadError
|
||||
Scanner = Scanner_R
|
||||
require 'tmail/scanner_r'
|
||||
Scanner = TMailScanner
|
||||
end
|
||||
end
|
||||
@@ -29,12 +29,11 @@
|
||||
|
||||
require 'tmail/config'
|
||||
|
||||
|
||||
module TMail
|
||||
|
||||
class Scanner_R
|
||||
class TMailScanner
|
||||
|
||||
Version = '0.10.7'
|
||||
Version = '0.11.0'
|
||||
Version.freeze
|
||||
|
||||
MIME_HEADERS = {
|
||||
@@ -46,7 +45,6 @@ module TMail
|
||||
alnum = 'a-zA-Z0-9'
|
||||
atomsyms = %q[ _#!$%&`'*+-{|}~^/=? ].strip
|
||||
tokensyms = %q[ _#!$%&`'*+-{|}~^@. ].strip
|
||||
|
||||
atomchars = alnum + Regexp.quote(atomsyms)
|
||||
tokenchars = alnum + Regexp.quote(tokensyms)
|
||||
iso2022str = '\e(?!\(B)..(?:[^\e]+|\e(?!\(B)..)*\e\(B'
|
||||
@@ -56,7 +56,7 @@ module TMail
|
||||
|
||||
module TextUtils
|
||||
# Defines characters per RFC that are OK for TOKENs, ATOMs, PHRASEs and CONTROL characters.
|
||||
|
||||
|
||||
aspecial = '()<>[]:;.\\,"'
|
||||
tspecial = '()<>[];:\\,"/?='
|
||||
lwsp = " \t\r\n"
|
||||
@@ -248,8 +248,7 @@ module TMail
|
||||
def decode_RFC2231( str )
|
||||
m = RFC2231_ENCODED.match(str) or return str
|
||||
begin
|
||||
NKF.nkf(NKF_FLAGS[$KCODE],
|
||||
m.post_match.gsub(/%[\da-f]{2}/in) {|s| s[1,2].hex.chr })
|
||||
to_kcode(m.post_match.gsub(/%[\da-f]{2}/in) {|s| s[1,2].hex.chr })
|
||||
rescue
|
||||
m.post_match.gsub(/%[\da-f]{2}/in, "")
|
||||
end
|
||||
@@ -263,7 +262,7 @@ module TMail
|
||||
preamble = $1
|
||||
remainder = $2
|
||||
if remainder =~ /;/
|
||||
remainder =~ /^(.*)(;.*)$/m
|
||||
remainder =~ /^(.*?)(;.*)$/m
|
||||
boundary_text = $1
|
||||
post = $2.chomp
|
||||
else
|
||||
@@ -30,8 +30,8 @@
|
||||
module TMail #:nodoc:
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 1
|
||||
MINOR = 1
|
||||
TINY = 1
|
||||
MINOR = 2
|
||||
TINY = 0
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
@@ -2,7 +2,7 @@ module ActionMailer
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 0
|
||||
TINY = 2
|
||||
TINY = 3
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
||||
@@ -534,7 +534,8 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
def test_delivery_logs_sent_mail
|
||||
mail = TestMailer.create_signed_up(@recipient)
|
||||
logger = mock()
|
||||
logger.expects(:info).with("Sent mail:\n #{mail.encoded}")
|
||||
logger.expects(:info).with("Sent mail to #{@recipient}")
|
||||
logger.expects(:debug).with("\n#{mail.encoded}")
|
||||
TestMailer.logger = logger
|
||||
TestMailer.deliver_signed_up(@recipient)
|
||||
end
|
||||
@@ -838,7 +839,7 @@ EOF
|
||||
fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email8")
|
||||
mail = TMail::Mail.parse(fixture)
|
||||
attachment = mail.attachments.last
|
||||
assert_equal "01QuienTeDijat.Pitbull.mp3", attachment.original_filename
|
||||
assert_equal "01 Quien Te Dij\212at. Pitbull.mp3", attachment.original_filename
|
||||
end
|
||||
|
||||
def test_wrong_mail_header
|
||||
|
||||
@@ -115,3 +115,21 @@ class TestHelperMailerTest < ActionMailer::TestCase
|
||||
assert_match /0 .* but 1/, error.message
|
||||
end
|
||||
end
|
||||
|
||||
class AnotherTestHelperMailerTest < ActionMailer::TestCase
|
||||
|
||||
tests TestHelperMailer
|
||||
|
||||
def setup
|
||||
# Should not override ActionMailer setup methods
|
||||
@test_var = "a value"
|
||||
end
|
||||
|
||||
def test_should_still_setup_mailer
|
||||
assert @expected.is_a?(TMail::Mail)
|
||||
end
|
||||
|
||||
def test_should_run_overridden_setup_method
|
||||
assert @test_var
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,42 @@
|
||||
*SVN*
|
||||
*2.0.3* (12th May 2008)
|
||||
|
||||
* Avoid remote_ip spoofing. [Brian Candler]
|
||||
|
||||
* Correct inconsistencies in RequestForgeryProtection docs. #11032 [mislav]
|
||||
|
||||
* Make assert_routing aware of the HTTP method used. #8039 [mpalmer]
|
||||
e.g. assert_routing({ :method => 'put', :path => '/product/321' }, { :controller => "product", :action => "update", :id => "321" })
|
||||
|
||||
* Remove ERB trim variables from trace template in case ActionView::Base.erb_trim_mode is changed in the application. #10098 [tpope, kampers]
|
||||
|
||||
* Fix typo in form_helper documentation. #10650 [xaviershay, kampers]
|
||||
|
||||
* Fix bug with setting Request#format= after the getter has cached the value. #10889 [cch1]
|
||||
|
||||
* Add label_tag helper for generating elements. #10802 [DefV]
|
||||
|
||||
* TestSession supports indifferent access. #7372 [tamc, Arsen7, mhackett, julik, jean.helou]
|
||||
|
||||
* UrlWriter respects relative_url_root. #10748 [Cheah Chu Yeow]
|
||||
|
||||
* Support render :text => nil. #6684 [tjennings, PotatoSalad, Cheah Chu Yeow]
|
||||
|
||||
* assert_response failures include the exception message. #10688 [Seth Rasmussen]
|
||||
|
||||
* Fixed rendering of partials with layout when done from site layout #9209 [antramm]
|
||||
|
||||
* Fix atom_feed_helper to comply with the atom spec. Closes #10672 [xaviershay]
|
||||
|
||||
* The tags created do not contain a date (http://feedvalidator.org/docs/error/InvalidTAG.html)
|
||||
* IDs are not guaranteed unique
|
||||
* A default self link was not provided, contrary to the documentation
|
||||
* NOTE: This changes tags for existing atom entries, but at least they validate now.
|
||||
|
||||
* Correct indentation in tests. Closes #10671 [l.guidi]
|
||||
|
||||
* Fix that auto_link looks for ='s in url paths (Amazon urls have them). Closes #10640 [bgreenlee]
|
||||
|
||||
* Ensure that test case setup is run even if overridden. #10382 [Josh Peek]
|
||||
|
||||
* Fix HTML Sanitizer to allow trailing spaces in CSS style attributes. Closes #10566 [wesley.moxam]
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
require 'rake/packagetask'
|
||||
require 'rake/gempackagetask'
|
||||
require 'rake/contrib/rubyforgepublisher'
|
||||
require File.join(File.dirname(__FILE__), 'lib', 'action_pack', 'version')
|
||||
|
||||
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
||||
@@ -76,7 +75,7 @@ spec = Gem::Specification.new do |s|
|
||||
s.has_rdoc = true
|
||||
s.requirements << 'none'
|
||||
|
||||
s.add_dependency('activesupport', '= 2.0.2' + PKG_BUILD)
|
||||
s.add_dependency('activesupport', '= 2.0.3' + PKG_BUILD)
|
||||
|
||||
s.require_path = 'lib'
|
||||
s.autorequire = 'action_controller'
|
||||
@@ -144,6 +143,7 @@ end
|
||||
desc "Publish the release files to RubyForge."
|
||||
task :release => [ :package ] do
|
||||
require 'rubyforge'
|
||||
require 'rake/contrib/rubyforgepublisher'
|
||||
|
||||
packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
|
||||
|
||||
|
||||
@@ -33,7 +33,11 @@ module ActionController
|
||||
elsif type.is_a?(Symbol) && @response.response_code == ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[type]
|
||||
assert_block("") { true } # to count the assertion
|
||||
else
|
||||
assert_block(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code)) { false }
|
||||
if @response.error?
|
||||
assert_block(build_message(message, "Expected response to be a <?>, but was <?>\n<?>", type, @response.response_code, @response.template.instance_variable_get(:@exception).message)) { false }
|
||||
else
|
||||
assert_block(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code)) { false }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -91,7 +95,7 @@ module ActionController
|
||||
value['controller'] = value['controller'].to_s
|
||||
if key == :actual && value['controller'].first != '/' && !value['controller'].include?('/')
|
||||
new_controller_path = ActionController::Routing.controller_relative_to(value['controller'], @controller.class.controller_path)
|
||||
value['controller'] = new_controller_path if value['controller'] != new_controller_path && ActionController::Routing.possible_controllers.include?(new_controller_path)
|
||||
value['controller'] = new_controller_path if value['controller'] != new_controller_path && ActionController::Routing.possible_controllers.include?(new_controller_path) && @response.redirected_to.is_a?(Hash)
|
||||
end
|
||||
value['controller'] = value['controller'][1..-1] if value['controller'].first == '/' # strip leading hash
|
||||
end
|
||||
|
||||
@@ -114,6 +114,9 @@ module ActionController
|
||||
#
|
||||
# # Tests a route, providing a defaults hash
|
||||
# assert_routing 'controller/action/9', {:id => "9", :item => "square"}, {:controller => "controller", :action => "action"}, {}, {:item => "square"}
|
||||
#
|
||||
# # Tests a route with a HTTP method
|
||||
# assert_routing({ :method => 'put', :path => '/product/321' }, { :controller => "product", :action => "update", :id => "321" })
|
||||
def assert_routing(path, options, defaults={}, extras={}, message=nil)
|
||||
assert_recognizes(options, path, extras, message)
|
||||
|
||||
@@ -122,7 +125,7 @@ module ActionController
|
||||
options[:controller] = "/#{controller}"
|
||||
end
|
||||
|
||||
assert_generates(path, options, defaults, extras, message)
|
||||
assert_generates(path.is_a?(Hash) ? path[:path] : path, options, defaults, extras, message)
|
||||
end
|
||||
|
||||
private
|
||||
@@ -140,4 +143,4 @@ module ActionController
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -850,8 +850,8 @@ module ActionController #:nodoc:
|
||||
response.headers["Location"] = url_for(location)
|
||||
end
|
||||
|
||||
if text = options[:text]
|
||||
render_for_text(text, options[:status])
|
||||
if options.has_key?(:text)
|
||||
render_for_text(options[:text], options[:status])
|
||||
|
||||
else
|
||||
if file = options[:file]
|
||||
@@ -1029,7 +1029,8 @@ module ActionController #:nodoc:
|
||||
# RedirectBackError will be raised. You may specify some fallback
|
||||
# behavior for this case by rescuing RedirectBackError.
|
||||
def redirect_to(options = {}, response_status = {}) #:doc:
|
||||
|
||||
raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
|
||||
|
||||
if options.is_a?(Hash) && options[:status]
|
||||
status = options.delete(:status)
|
||||
elsif response_status[:status]
|
||||
|
||||
@@ -89,7 +89,7 @@ class CGI #:nodoc:
|
||||
cookies = Hash.new([])
|
||||
|
||||
if raw_cookie
|
||||
raw_cookie.split(/[;,]\s?/).each do |pairs|
|
||||
raw_cookie.split(/;\s?/).each do |pairs|
|
||||
name, values = pairs.split('=',2)
|
||||
next unless name and values
|
||||
name = CGI::unescape(name)
|
||||
|
||||
@@ -583,10 +583,12 @@ module ActionController #:nodoc:
|
||||
when filter.respond_to?(:call)
|
||||
if filter.is_a?(Method)
|
||||
MethodFilter
|
||||
elsif filter.arity == 1
|
||||
ProcFilter
|
||||
else
|
||||
ProcWithCallFilter
|
||||
case filter.arity
|
||||
when 1; ProcFilter
|
||||
when 2; ProcWithCallFilter
|
||||
else raise ArgumentError, 'Filter blocks must take one or two arguments.'
|
||||
end
|
||||
end
|
||||
when filter.respond_to?(:filter)
|
||||
ClassFilter
|
||||
|
||||
@@ -145,7 +145,10 @@ module Mime
|
||||
end
|
||||
|
||||
def ==(mime_type)
|
||||
(@synonyms + [ self ]).any? { |synonym| synonym.to_s == mime_type.to_s } if mime_type
|
||||
return false unless mime_type
|
||||
(@synonyms + [ self ]).any? do |synonym|
|
||||
synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -1,8 +1,75 @@
|
||||
module ActionController
|
||||
# Polymorphic URL helpers are methods for smart resolution to a named route call when
|
||||
# given an ActiveRecord model instance. They are to be used in combination with
|
||||
# ActionController::Resources.
|
||||
#
|
||||
# These methods are useful when you want to generate correct URL or path to a RESTful
|
||||
# resource without having to know the exact type of the record in question.
|
||||
#
|
||||
# Nested resources and/or namespaces are also supported, as illustrated in the example:
|
||||
#
|
||||
# polymorphic_url([:admin, @article, @comment])
|
||||
# #-> results in:
|
||||
# admin_article_comment_url(@article, @comment)
|
||||
#
|
||||
# == Usage within the framework
|
||||
#
|
||||
# Polymorphic URL helpers are used in a number of places throughout the Rails framework:
|
||||
#
|
||||
# * <tt>url_for</tt>, so you can use it with a record as the argument, e.g.
|
||||
# <tt>url_for(@article)</tt>;
|
||||
# * ActionView::Helpers::FormHelper uses <tt>polymorphic_path</tt>, so you can write
|
||||
# <tt>form_for(@article)</tt> without having to specify :url parameter for the form
|
||||
# action;
|
||||
# * <tt>redirect_to</tt> (which, in fact, uses <tt>url_for</tt>) so you can write
|
||||
# <tt>redirect_to(post)</tt> in your controllers;
|
||||
# * ActionView::Helpers::AtomFeedHelper, so you don't have to explicitly specify URLs
|
||||
# for feed entries.
|
||||
#
|
||||
# == Prefixed polymorphic helpers
|
||||
#
|
||||
# In addition to <tt>polymorphic_url</tt> and <tt>polymorphic_path</tt> methods, a
|
||||
# number of prefixed helpers are available as a shorthand to <tt>:action => "..."</tt>
|
||||
# in options. Those are:
|
||||
#
|
||||
# * <tt>edit_polymorphic_url</tt>, <tt>edit_polymorphic_path</tt>
|
||||
# * <tt>new_polymorphic_url</tt>, <tt>new_polymorphic_path</tt>
|
||||
# * <tt>formatted_polymorphic_url</tt>, <tt>formatted_polymorphic_path</tt>
|
||||
#
|
||||
# Example usage:
|
||||
#
|
||||
# edit_polymorphic_path(@post)
|
||||
# #=> /posts/1/edit
|
||||
#
|
||||
# formatted_polymorphic_path([@post, :pdf])
|
||||
# #=> /posts/1.pdf
|
||||
module PolymorphicRoutes
|
||||
# Constructs a call to a named RESTful route for the given record and returns the
|
||||
# resulting URL string. For example:
|
||||
#
|
||||
# polymorphic_url(post)
|
||||
# # calls post_url(post) #=> "http://example.com/posts/1"
|
||||
#
|
||||
# ==== Options
|
||||
# * <tt>:action</tt> -- specifies the action prefix for the named route:
|
||||
# <tt>:new</tt>, <tt>:edit</tt> or <tt>:formatted</tt>. Default is no prefix.
|
||||
# * <tt>:routing_type</tt> -- <tt>:path</tt> or <tt>:url</tt> (default <tt>:url</tt>).
|
||||
#
|
||||
# ==== Examples
|
||||
#
|
||||
# # an Article record
|
||||
# polymorphic_url(record) #-> article_url(record)
|
||||
#
|
||||
# # a Comment record
|
||||
# polymorphic_url(record) #-> comment_url(record)
|
||||
#
|
||||
# # it recognizes new records and maps to the collection
|
||||
# record = Comment.new
|
||||
# polymorphic_url(record) #-> comments_url()
|
||||
#
|
||||
def polymorphic_url(record_or_hash_or_array, options = {})
|
||||
record = extract_record(record_or_hash_or_array)
|
||||
|
||||
record = extract_record(record_or_hash_or_array)
|
||||
format = (options[:action].to_s == "formatted" and record_or_hash_or_array.pop)
|
||||
namespace = extract_namespace(record_or_hash_or_array)
|
||||
|
||||
args = case record_or_hash_or_array
|
||||
@@ -11,9 +78,11 @@ module ActionController
|
||||
else [ record_or_hash_or_array ]
|
||||
end
|
||||
|
||||
args << format if format
|
||||
|
||||
inflection =
|
||||
case
|
||||
when options[:action] == "new"
|
||||
when options[:action].to_s == "new"
|
||||
args.pop
|
||||
:singular
|
||||
when record.respond_to?(:new_record?) && record.new_record?
|
||||
@@ -27,8 +96,11 @@ module ActionController
|
||||
send!(named_route, *args)
|
||||
end
|
||||
|
||||
def polymorphic_path(record_or_hash_or_array)
|
||||
polymorphic_url(record_or_hash_or_array, :routing_type => :path)
|
||||
# Returns the path component of a URL for the given record. It uses
|
||||
# <tt>polymorphic_url</tt> with <tt>:routing_type => :path</tt>.
|
||||
def polymorphic_path(record_or_hash_or_array, options = {})
|
||||
options[:routing_type] = :path
|
||||
polymorphic_url(record_or_hash_or_array, options)
|
||||
end
|
||||
|
||||
%w(edit new formatted).each do |action|
|
||||
@@ -43,26 +115,29 @@ module ActionController
|
||||
EOT
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
def action_prefix(options)
|
||||
options[:action] ? "#{options[:action]}_" : ""
|
||||
end
|
||||
|
||||
def routing_type(options)
|
||||
"#{options[:routing_type] || "url"}"
|
||||
options[:routing_type] || :url
|
||||
end
|
||||
|
||||
def build_named_route_call(records, namespace, inflection, options = {})
|
||||
records = Array.new([extract_record(records)]) unless records.is_a?(Array)
|
||||
base_segment = "#{RecordIdentifier.send!("#{inflection}_class_name", records.pop)}_"
|
||||
|
||||
method_root = records.reverse.inject(base_segment) do |string, name|
|
||||
segment = "#{RecordIdentifier.send!("singular_class_name", name)}_"
|
||||
segment << string
|
||||
unless records.is_a?(Array)
|
||||
record = extract_record(records)
|
||||
route = ''
|
||||
else
|
||||
record = records.pop
|
||||
route = records.inject("") do |string, parent|
|
||||
string << "#{RecordIdentifier.send!("singular_class_name", parent)}_"
|
||||
end
|
||||
end
|
||||
|
||||
action_prefix(options) + namespace + method_root + routing_type(options)
|
||||
route << "#{RecordIdentifier.send!("#{inflection}_class_name", record)}_"
|
||||
|
||||
action_prefix(options) + namespace + route + routing_type(options).to_s
|
||||
end
|
||||
|
||||
def extract_record(record_or_hash_or_array)
|
||||
@@ -78,7 +153,7 @@ module ActionController
|
||||
if record_or_hash_or_array.is_a?(Array)
|
||||
record_or_hash_or_array.delete_if do |record_or_namespace|
|
||||
if record_or_namespace.is_a?(String) || record_or_namespace.is_a?(Symbol)
|
||||
namespace << "#{record_or_namespace.to_s}_"
|
||||
namespace << "#{record_or_namespace}_"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -53,15 +53,15 @@ module ActionController
|
||||
[ prefix, singular_class_name(record_or_class) ].compact * '_'
|
||||
end
|
||||
|
||||
# The DOM class convention is to use the singular form of an object or class with the id following an underscore.
|
||||
# The DOM id convention is to use the singular form of an object or class with the id following an underscore.
|
||||
# If no id is found, prefix with "new_" instead. Examples:
|
||||
#
|
||||
# dom_class(Post.new(:id => 45)) # => "post_45"
|
||||
# dom_class(Post.new) # => "new_post"
|
||||
# dom_id(Post.new(:id => 45)) # => "post_45"
|
||||
# dom_id(Post.new) # => "new_post"
|
||||
#
|
||||
# If you need to address multiple instances of the same class in the same view, you can prefix the dom_id:
|
||||
#
|
||||
# dom_class(Post.new(:id => 45), :edit) # => "edit_post_45"
|
||||
# dom_id(Post.new(:id => 45), :edit) # => "edit_post_45"
|
||||
def dom_id(record, prefix = nil)
|
||||
prefix ||= 'new' unless record.id
|
||||
[ prefix, singular_class_name(record), record.id ].compact * '_'
|
||||
|
||||
@@ -111,7 +111,7 @@ module ActionController
|
||||
# end
|
||||
def format=(extension)
|
||||
parameters[:format] = extension.to_s
|
||||
format
|
||||
@format = Mime::Type.lookup_by_extension(parameters[:format])
|
||||
end
|
||||
|
||||
# Returns true if the request's "X-Requested-With" header contains
|
||||
@@ -122,26 +122,41 @@ module ActionController
|
||||
end
|
||||
alias xhr? :xml_http_request?
|
||||
|
||||
# Which IP addresses are "trusted proxies" that can be stripped from
|
||||
# the right-hand-side of X-Forwarded-For
|
||||
TRUSTED_PROXIES = /^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i
|
||||
|
||||
# Determine originating IP address. REMOTE_ADDR is the standard
|
||||
# but will fail if the user is behind a proxy. HTTP_CLIENT_IP and/or
|
||||
# HTTP_X_FORWARDED_FOR are set by proxies so check for these before
|
||||
# falling back to REMOTE_ADDR. HTTP_X_FORWARDED_FOR may be a comma-
|
||||
# delimited list in the case of multiple chained proxies; the first is
|
||||
# the originating IP.
|
||||
#
|
||||
# Security note: do not use if IP spoofing is a concern for your
|
||||
# application. Since remote_ip checks HTTP headers for addresses forwarded
|
||||
# by proxies, the client may send any IP. remote_addr can't be spoofed but
|
||||
# also doesn't work behind a proxy, since it's always the proxy's IP.
|
||||
# HTTP_X_FORWARDED_FOR are set by proxies so check for these if
|
||||
# REMOTE_ADDR is a proxy. HTTP_X_FORWARDED_FOR may be a comma-
|
||||
# delimited list in the case of multiple chained proxies; the last
|
||||
# address which is not trusted is the originating IP.
|
||||
|
||||
def remote_ip
|
||||
return @env['HTTP_CLIENT_IP'] if @env.include? 'HTTP_CLIENT_IP'
|
||||
if TRUSTED_PROXIES !~ @env['REMOTE_ADDR']
|
||||
return @env['REMOTE_ADDR']
|
||||
end
|
||||
|
||||
if @env.include? 'HTTP_CLIENT_IP'
|
||||
if @env.include? 'HTTP_X_FORWARDED_FOR'
|
||||
# We don't know which came from the proxy, and which from the user
|
||||
raise ActionControllerError.new(<<EOM)
|
||||
IP spoofing attack?!
|
||||
HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}
|
||||
HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}
|
||||
EOM
|
||||
end
|
||||
return @env['HTTP_CLIENT_IP']
|
||||
end
|
||||
|
||||
if @env.include? 'HTTP_X_FORWARDED_FOR' then
|
||||
remote_ips = @env['HTTP_X_FORWARDED_FOR'].split(',').reject do |ip|
|
||||
ip.strip =~ /^unknown$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i
|
||||
remote_ips = @env['HTTP_X_FORWARDED_FOR'].split(',')
|
||||
while remote_ips.size > 1 && TRUSTED_PROXIES =~ remote_ips.last.strip
|
||||
remote_ips.pop
|
||||
end
|
||||
|
||||
return remote_ips.first.strip unless remote_ips.empty?
|
||||
return remote_ips.last.strip
|
||||
end
|
||||
|
||||
@env['REMOTE_ADDR']
|
||||
|
||||
@@ -13,33 +13,46 @@ module ActionController #:nodoc:
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
# Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current web application, not a
|
||||
# forged link from another site, is done by embedding a token based on the session (which an attacker wouldn't know) in all
|
||||
# forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only
|
||||
# HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication
|
||||
# scheme there anyway). Also, GET requests are not protected as these should be indempotent anyway.
|
||||
#
|
||||
# This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an
|
||||
# ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the error message in
|
||||
# production by editing public/422.html. A call to this method in ApplicationController is generated by default in post-Rails 2.0
|
||||
# applications.
|
||||
#
|
||||
# The token parameter is named <tt>authenticity_token</tt> by default. If you are generating an HTML form manually (without the
|
||||
# use of Rails' <tt>form_for</tt>, <tt>form_tag</tt> or other helpers), you have to include a hidden field named like that and
|
||||
# set its value to what is returned by <tt>form_authenticity_token</tt>. Same applies to manually constructed Ajax requests. To
|
||||
# make the token available through a global variable to scripts on a certain page, you could add something like this to a view:
|
||||
#
|
||||
# <%= javascript_tag "window._token = '#{form_authenticity_token}'" %>
|
||||
#
|
||||
# Request forgery protection is disabled by default in test environment. If you are upgrading from Rails 1.x, add this to
|
||||
# config/environments/test.rb:
|
||||
#
|
||||
# # Disable request forgery protection in test environment
|
||||
# config.action_controller.allow_forgery_protection = false
|
||||
#
|
||||
# == Learn more about CSRF (Cross-Site Request Forgery) attacks
|
||||
#
|
||||
# Here are some resources:
|
||||
# * http://isc.sans.org/diary.html?storyid=1750
|
||||
# * http://en.wikipedia.org/wiki/Cross-site_request_forgery
|
||||
#
|
||||
# Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
|
||||
# There are a few guidelines you should follow:
|
||||
#
|
||||
# * Keep your GET requests safe and idempotent. More reading material:
|
||||
# * http://www.xml.com/pub/a/2002/04/24/deviant.html
|
||||
# * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
|
||||
# * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look for "Expires: at end of session"
|
||||
#
|
||||
module ClassMethods
|
||||
# Protect a controller's actions from CSRF attacks by ensuring that all forms are coming from the current web application, not
|
||||
# a forged link from another site. This is done by embedding a token based on the session (which an attacker wouldn't know) in
|
||||
# all forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only
|
||||
# HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication
|
||||
# scheme there anyway). Also, GET requests are not protected as these should be indempotent anyway.
|
||||
#
|
||||
# You turn this on with the #protect_from_forgery method, which will perform the check and raise
|
||||
# an ActionController::InvalidAuthenticityToken if the token doesn't match what was expected. And it will add
|
||||
# a _authenticity_token parameter to all forms that are automatically generated by Rails. You can customize the error message
|
||||
# given through public/422.html.
|
||||
#
|
||||
# Learn more about CSRF (Cross-Site Request Forgery) attacks:
|
||||
#
|
||||
# * http://isc.sans.org/diary.html?storyid=1750
|
||||
# * http://en.wikipedia.org/wiki/Cross-site_request_forgery
|
||||
#
|
||||
# Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
|
||||
# There are a few guidelines you should follow:
|
||||
#
|
||||
# * Keep your GET requests safe and idempotent. More reading material:
|
||||
# * http://www.xml.com/pub/a/2002/04/24/deviant.html
|
||||
# * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
|
||||
# * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look for "Expires: at end of session"
|
||||
#
|
||||
# If you need to construct a request yourself, but still want to take advantage of forgery protection, you can grab the
|
||||
# authenticity_token using the form_authenticity_token helper method and make it part of the parameters yourself.
|
||||
# Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
@@ -54,16 +67,10 @@ module ActionController #:nodoc:
|
||||
# skip_before_filter :verify_authenticity_token
|
||||
# end
|
||||
#
|
||||
# If you are upgrading from Rails 1.x, disable forgery protection to
|
||||
# simplify your tests. Add this to config/environments/test.rb:
|
||||
#
|
||||
# # Disable request forgery protection in test environment
|
||||
# config.action_controller.allow_forgery_protection = false
|
||||
#
|
||||
# Valid Options:
|
||||
#
|
||||
# * <tt>:only/:except</tt> - passed to the before_filter call. Set which actions are verified.
|
||||
# * <tt>:secret</tt> - Custom salt used to generate the form_authenticity_token.
|
||||
# * <tt>:only/:except</tt> - passed to the <tt>before_filter</tt> call. Set which actions are verified.
|
||||
# * <tt>:secret</tt> - Custom salt used to generate the <tt>form_authenticity_token</tt>.
|
||||
# Leave this off if you are using the cookie session store.
|
||||
# * <tt>:digest</tt> - Message digest used for hashing. Defaults to 'SHA1'
|
||||
def protect_from_forgery(options = {})
|
||||
|
||||
@@ -306,8 +306,8 @@ module ActionController
|
||||
gsub(%r{(.)[\\/]$}, '\1') # drop final / or \ if path ends with it
|
||||
|
||||
# eliminate .. paths where possible
|
||||
re = %r{\w+[/\\]\.\.[/\\]}
|
||||
path.gsub!(%r{\w+[/\\]\.\.[/\\]}, "") while path.match(re)
|
||||
re = %r{[^/\\]+[/\\]\.\.[/\\]}
|
||||
path.gsub!(re, "") while path.match(re)
|
||||
path
|
||||
end
|
||||
|
||||
@@ -1216,7 +1216,7 @@ module ActionController
|
||||
opts = if args.empty? || Hash === args.first
|
||||
args.first || {}
|
||||
else
|
||||
options = args.last.is_a?(Hash) ? args.pop : {}
|
||||
options = args.extract_options!
|
||||
args = args.zip(#{route.segment_keys.inspect}).inject({}) do |h, (v, k)|
|
||||
h[k] = v
|
||||
h
|
||||
|
||||
@@ -10,17 +10,17 @@
|
||||
<p><code>RAILS_ROOT: <%= defined?(RAILS_ROOT) ? RAILS_ROOT : "unset" %></code></p>
|
||||
|
||||
<div id="traces">
|
||||
<% names.each do |name| -%>
|
||||
<% names.each do |name| %>
|
||||
<%
|
||||
show = "document.getElementById('#{name.gsub /\s/, '-'}').style.display='block';"
|
||||
hide = (names - [name]).collect {|hide_name| "document.getElementById('#{hide_name.gsub /\s/, '-'}').style.display='none';"}
|
||||
%>
|
||||
<a href="#" onclick="<%= hide %><%= show %>; return false;"><%= name %></a> <%= '|' unless names.last == name %>
|
||||
<% end -%>
|
||||
<% end %>
|
||||
|
||||
<% traces.each do |name, trace| -%>
|
||||
<% traces.each do |name, trace| %>
|
||||
<div id="<%= name.gsub /\s/, '-' %>" style="display: <%= name == "Application Trace" ? 'block' : 'none' %>;">
|
||||
<pre><code><%= trace.join "\n" %></code></pre>
|
||||
</div>
|
||||
<% end -%>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -3,9 +3,15 @@ require 'active_support/test_case'
|
||||
module ActionController
|
||||
class NonInferrableControllerError < ActionControllerError
|
||||
def initialize(name)
|
||||
@name = name
|
||||
super "Unable to determine the controller to test from #{name}. " +
|
||||
"You'll need to specify it using 'tests YourController' in your " +
|
||||
"test case definition"
|
||||
"test case definition. This could mean that #{inferred_controller_name} does not exist " +
|
||||
"or it contains syntax errors"
|
||||
end
|
||||
|
||||
def inferred_controller_name
|
||||
@name.sub(/Test$/, '')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -44,10 +50,24 @@ module ActionController
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
def setup_with_controller
|
||||
@controller = self.class.controller_class.new
|
||||
@request = TestRequest.new
|
||||
@response = TestResponse.new
|
||||
end
|
||||
end
|
||||
end
|
||||
alias_method :setup, :setup_with_controller
|
||||
|
||||
def self.method_added(method)
|
||||
if method.to_s == 'setup'
|
||||
unless method_defined?(:setup_without_controller)
|
||||
alias_method :setup_without_controller, :setup
|
||||
define_method(:setup) do
|
||||
setup_with_fixtures if respond_to?(:setup_with_fixtures)
|
||||
setup_with_controller
|
||||
setup_without_controller
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -286,7 +286,7 @@ module ActionController #:nodoc:
|
||||
|
||||
def initialize(attributes = nil)
|
||||
@session_id = ''
|
||||
@attributes = attributes
|
||||
@attributes = attributes.nil? ? nil : attributes.stringify_keys
|
||||
@saved_attributes = nil
|
||||
end
|
||||
|
||||
@@ -295,11 +295,11 @@ module ActionController #:nodoc:
|
||||
end
|
||||
|
||||
def [](key)
|
||||
data[key]
|
||||
data[key.to_s]
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
data[key] = value
|
||||
data[key.to_s] = value
|
||||
end
|
||||
|
||||
def update
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
module ActionController
|
||||
module ActionController
|
||||
# Write URLs from arbitrary places in your codebase, such as your mailers.
|
||||
#
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
#
|
||||
# class MyMailer
|
||||
# include ActionController::UrlWriter
|
||||
# default_url_options[:host] = 'www.basecamphq.com'
|
||||
#
|
||||
#
|
||||
# def signup_url(token)
|
||||
# url_for(:controller => 'signup', action => 'index', :token => token)
|
||||
# end
|
||||
# end
|
||||
#
|
||||
#
|
||||
# In addition to providing +url_for+, named routes are also accessible after
|
||||
# including UrlWriter.
|
||||
module UrlWriter
|
||||
@@ -19,64 +19,65 @@ module ActionController
|
||||
# is provided.
|
||||
mattr_accessor :default_url_options
|
||||
self.default_url_options = {}
|
||||
|
||||
|
||||
def self.included(base) #:nodoc:
|
||||
ActionController::Routing::Routes.install_helpers base
|
||||
ActionController::Routing::Routes.install_helpers(base)
|
||||
base.mattr_accessor :default_url_options
|
||||
base.default_url_options ||= default_url_options
|
||||
end
|
||||
|
||||
# Generate a url based on the options provided, default_url_options and the
|
||||
|
||||
# Generate a url based on the options provided, default_url_options and the
|
||||
# routes defined in routes.rb. The following options are supported:
|
||||
#
|
||||
#
|
||||
# * <tt>:only_path</tt> If true, the relative url is returned. Defaults to false.
|
||||
# * <tt>:protocol</tt> The protocol to connect to. Defaults to 'http'.
|
||||
# * <tt>:host</tt> Specifies the host the link should be targetted at. If <tt>:only_path</tt> is false, this option must be
|
||||
# provided either explicitly, or via default_url_options.
|
||||
# * <tt>:host</tt> Specifies the host the link should be targetted at. If <tt>:only_path</tt> is false, this option must be
|
||||
# provided either explicitly, or via default_url_options.
|
||||
# * <tt>:port</tt> Optionally specify the port to connect to.
|
||||
# * <tt>:anchor</tt> An anchor name to be appended to the path.
|
||||
#
|
||||
# * <tt>:skip_relative_url_root</tt> If true, the url is not constructed using the relative_url_root set in <tt>ActionController::AbstractRequest.relative_url_root</tt>.
|
||||
#
|
||||
# Any other key(:controller, :action, etc...) given to <tt>url_for</tt> is forwarded to the Routes module.
|
||||
#
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
#
|
||||
# url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080' # => 'http://somehost.org:8080/tasks/testing'
|
||||
# url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok'
|
||||
# url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33'
|
||||
#
|
||||
def url_for(options)
|
||||
options = self.class.default_url_options.merge(options)
|
||||
|
||||
|
||||
url = ''
|
||||
|
||||
unless options.delete :only_path
|
||||
unless options.delete(:only_path)
|
||||
url << (options.delete(:protocol) || 'http')
|
||||
url << '://' unless url.match("://") #dont add separator if its already been specified in :protocol
|
||||
|
||||
url << '://' unless url.match("://")
|
||||
|
||||
raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless options[:host]
|
||||
|
||||
url << options.delete(:host)
|
||||
url << ":#{options.delete(:port)}" if options.key?(:port)
|
||||
else
|
||||
# Delete the unused options to prevent their appearance in the query string
|
||||
[:protocol, :host, :port].each { |k| options.delete k }
|
||||
# Delete the unused options to prevent their appearance in the query string.
|
||||
[:protocol, :host, :port, :skip_relative_url_root].each { |k| options.delete(k) }
|
||||
end
|
||||
|
||||
anchor = "##{CGI.escape options.delete(:anchor).to_param.to_s}" if options.key?(:anchor)
|
||||
url << ActionController::AbstractRequest.relative_url_root.to_s unless options[:skip_relative_url_root]
|
||||
anchor = "##{CGI.escape options.delete(:anchor).to_param.to_s}" if options[:anchor]
|
||||
url << Routing::Routes.generate(options, {})
|
||||
url << anchor if anchor
|
||||
|
||||
return url
|
||||
end
|
||||
url
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Rewrites URLs for Base.redirect_to and Base.url_for in the controller.
|
||||
class UrlRewriter #:nodoc:
|
||||
RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash, :skip_relative_url_root]
|
||||
def initialize(request, parameters)
|
||||
@request, @parameters = request, parameters
|
||||
end
|
||||
|
||||
|
||||
def rewrite(options = {})
|
||||
rewrite_url(options)
|
||||
end
|
||||
@@ -123,7 +124,7 @@ module ActionController
|
||||
# Generates the query string, too
|
||||
Routing::Routes.generate(options, @request.symbolized_path_parameters)
|
||||
end
|
||||
|
||||
|
||||
def rewrite_authentication(options)
|
||||
if options[:user] && options[:password]
|
||||
"#{CGI.escape(options.delete(:user))}:#{CGI.escape(options.delete(:password))}@"
|
||||
|
||||
@@ -43,72 +43,88 @@ module ActionController #:nodoc:
|
||||
# the user is redirected to a different action. The +options+ parameter
|
||||
# is a hash consisting of the following key/value pairs:
|
||||
#
|
||||
# * <tt>:params</tt> - a single key or an array of keys that must
|
||||
# be in the <tt>params</tt> hash in order for the action(s) to be safely
|
||||
# called.
|
||||
# * <tt>:session</tt> - a single key or an array of keys that must
|
||||
# be in the <tt>session</tt> in order for the action(s) to be safely called.
|
||||
# * <tt>:flash</tt> - a single key or an array of keys that must
|
||||
# be in the flash in order for the action(s) to be safely called.
|
||||
# * <tt>:method</tt> - a single key or an array of keys--any one of which
|
||||
# must match the current request method in order for the action(s) to
|
||||
# be safely called. (The key should be a symbol: <tt>:get</tt> or
|
||||
# <tt>:post</tt>, for example.)
|
||||
# * <tt>:xhr</tt> - true/false option to ensure that the request is coming
|
||||
# from an Ajax call or not.
|
||||
# * <tt>:add_flash</tt> - a hash of name/value pairs that should be merged
|
||||
# into the session's flash if the prerequisites cannot be satisfied.
|
||||
# * <tt>:add_headers</tt> - a hash of name/value pairs that should be
|
||||
# merged into the response's headers hash if the prerequisites cannot
|
||||
# be satisfied.
|
||||
# * <tt>:redirect_to</tt> - the redirection parameters to be used when
|
||||
# redirecting if the prerequisites cannot be satisfied. You can
|
||||
# redirect either to named route or to the action in some controller.
|
||||
# * <tt>:render</tt> - the render parameters to be used when
|
||||
# the prerequisites cannot be satisfied.
|
||||
# * <tt>:only</tt> - only apply this verification to the actions specified
|
||||
# in the associated array (may also be a single value).
|
||||
# * <tt>:except</tt> - do not apply this verification to the actions
|
||||
# specified in the associated array (may also be a single value).
|
||||
# <tt>:params</tt>::
|
||||
# a single key or an array of keys that must be in the <tt>params</tt>
|
||||
# hash in order for the action(s) to be safely called.
|
||||
# <tt>:session</tt>::
|
||||
# a single key or an array of keys that must be in the <tt>session</tt>
|
||||
# in order for the action(s) to be safely called.
|
||||
# <tt>:flash</tt>::
|
||||
# a single key or an array of keys that must be in the flash in order
|
||||
# for the action(s) to be safely called.
|
||||
# <tt>:method</tt>::
|
||||
# a single key or an array of keys--any one of which must match the
|
||||
# current request method in order for the action(s) to be safely called.
|
||||
# (The key should be a symbol: <tt>:get</tt> or <tt>:post</tt>, for
|
||||
# example.)
|
||||
# <tt>:xhr</tt>::
|
||||
# true/false option to ensure that the request is coming from an Ajax
|
||||
# call or not.
|
||||
# <tt>:add_flash</tt>::
|
||||
# a hash of name/value pairs that should be merged into the session's
|
||||
# flash if the prerequisites cannot be satisfied.
|
||||
# <tt>:add_headers</tt>::
|
||||
# a hash of name/value pairs that should be merged into the response's
|
||||
# headers hash if the prerequisites cannot be satisfied.
|
||||
# <tt>:redirect_to</tt>::
|
||||
# the redirection parameters to be used when redirecting if the
|
||||
# prerequisites cannot be satisfied. You can redirect either to named
|
||||
# route or to the action in some controller.
|
||||
# <tt>:render</tt>::
|
||||
# the render parameters to be used when the prerequisites cannot be satisfied.
|
||||
# <tt>:only</tt>::
|
||||
# only apply this verification to the actions specified in the associated
|
||||
# array (may also be a single value).
|
||||
# <tt>:except</tt>::
|
||||
# do not apply this verification to the actions specified in the associated
|
||||
# array (may also be a single value).
|
||||
def verify(options={})
|
||||
filter_opts = { :only => options[:only], :except => options[:except] }
|
||||
before_filter(filter_opts) do |c|
|
||||
before_filter :only => options[:only], :except => options[:except] do |c|
|
||||
c.send! :verify_action, options
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def verify_action(options) #:nodoc:
|
||||
prereqs_invalid =
|
||||
[*options[:params] ].find { |v| params[v].nil? } ||
|
||||
[*options[:session]].find { |v| session[v].nil? } ||
|
||||
[*options[:flash] ].find { |v| flash[v].nil? }
|
||||
|
||||
if !prereqs_invalid && options[:method]
|
||||
prereqs_invalid ||=
|
||||
[*options[:method]].all? { |v| request.method != v.to_sym }
|
||||
end
|
||||
|
||||
prereqs_invalid ||= (request.xhr? != options[:xhr]) unless options[:xhr].nil?
|
||||
|
||||
if prereqs_invalid
|
||||
flash.update(options[:add_flash]) if options[:add_flash]
|
||||
response.headers.update(options[:add_headers]) if options[:add_headers]
|
||||
private
|
||||
|
||||
unless performed?
|
||||
case
|
||||
when options[:render]
|
||||
render(options[:render])
|
||||
when options[:redirect_to]
|
||||
options[:redirect_to] = self.send!(options[:redirect_to]) if options[:redirect_to].is_a?(Symbol)
|
||||
redirect_to(options[:redirect_to])
|
||||
else
|
||||
head(:bad_request)
|
||||
end
|
||||
end
|
||||
def verify_action(options) #:nodoc:
|
||||
if prereqs_invalid?(options)
|
||||
flash.update(options[:add_flash]) if options[:add_flash]
|
||||
response.headers.update(options[:add_headers]) if options[:add_headers]
|
||||
apply_remaining_actions(options) unless performed?
|
||||
end
|
||||
end
|
||||
|
||||
def prereqs_invalid?(options) # :nodoc:
|
||||
verify_presence_of_keys_in_hash_flash_or_params(options) ||
|
||||
verify_method(options) ||
|
||||
verify_request_xhr_status(options)
|
||||
end
|
||||
|
||||
def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc:
|
||||
[*options[:params] ].find { |v| params[v].nil? } ||
|
||||
[*options[:session]].find { |v| session[v].nil? } ||
|
||||
[*options[:flash] ].find { |v| flash[v].nil? }
|
||||
end
|
||||
|
||||
def verify_method(options) # :nodoc:
|
||||
[*options[:method]].all? { |v| request.method != v.to_sym } if options[:method]
|
||||
end
|
||||
|
||||
def verify_request_xhr_status(options) # :nodoc:
|
||||
request.xhr? != options[:xhr] unless options[:xhr].nil?
|
||||
end
|
||||
|
||||
def apply_redirect_to(redirect_to_option) # :nodoc:
|
||||
redirect_to_option.is_a?(Symbol) ? self.send!(redirect_to_option) : redirect_to_option
|
||||
end
|
||||
|
||||
def apply_remaining_actions(options) # :nodoc:
|
||||
case
|
||||
when options[:render] ; render(options[:render])
|
||||
when options[:redirect_to] ; redirect_to(apply_redirect_to(options[:redirect_to]))
|
||||
else head(:bad_request)
|
||||
end
|
||||
end
|
||||
|
||||
private :verify_action
|
||||
end
|
||||
end
|
||||
@@ -2,7 +2,7 @@ module ActionPack #:nodoc:
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 0
|
||||
TINY = 2
|
||||
TINY = 3
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
||||
@@ -157,6 +157,8 @@ module ActionView #:nodoc:
|
||||
attr_reader :logger, :response, :headers
|
||||
attr_internal :cookies, :flash, :headers, :params, :request, :response, :session
|
||||
|
||||
delegate :logger, :action_name, :to => :controller
|
||||
|
||||
attr_writer :template_format
|
||||
|
||||
# Specify trim mode for the ERB compiler. Defaults to '-'.
|
||||
@@ -338,11 +340,13 @@ If you are rendering a subtemplate, you must now use controller-like partial syn
|
||||
path, partial_name = partial_pieces(options.delete(:layout))
|
||||
|
||||
if block_given?
|
||||
@content_for_layout = capture(&block)
|
||||
concat(render(options.merge(:partial => "#{path}/#{partial_name}")), block.binding)
|
||||
wrap_content_for_layout capture(&block) do
|
||||
concat(render(options.merge(:partial => "#{path}/#{partial_name}")), block.binding)
|
||||
end
|
||||
else
|
||||
@content_for_layout = render(options)
|
||||
render(options.merge(:partial => "#{path}/#{partial_name}"))
|
||||
wrap_content_for_layout render(options) do
|
||||
render(options.merge(:partial => "#{path}/#{partial_name}"))
|
||||
end
|
||||
end
|
||||
elsif options[:file]
|
||||
render_file(options[:file], options[:use_full_path], options[:locals])
|
||||
@@ -441,6 +445,12 @@ If you are rendering a subtemplate, you must now use controller-like partial syn
|
||||
end
|
||||
|
||||
private
|
||||
def wrap_content_for_layout(content)
|
||||
original_content_for_layout = @content_for_layout
|
||||
@content_for_layout = content
|
||||
returning(yield) { @content_for_layout = original_content_for_layout }
|
||||
end
|
||||
|
||||
def find_full_template_path(template_path, extension)
|
||||
file_name = "#{template_path}.#{extension}"
|
||||
base_path = find_base_path_for(file_name)
|
||||
@@ -516,10 +526,18 @@ If you are rendering a subtemplate, you must now use controller-like partial syn
|
||||
def template_handler_is_compilable?(handler)
|
||||
handler.new(self).respond_to?(:compile)
|
||||
end
|
||||
|
||||
|
||||
# Assigns instance variables from the controller to the view.
|
||||
def assign_variables_from_controller
|
||||
@assigns.each { |key, value| instance_variable_set("@#{key}", value) }
|
||||
@assigns.each do |key, value|
|
||||
if ['logger'].include?(key)
|
||||
instance_variable_set("@#{key}", ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, key.to_sym))
|
||||
elsif ['action_name'].include?(key)
|
||||
instance_variable_set("@#{key}", ActiveSupport::Deprecation::DeprecatedInstanceVariable.new(value, key))
|
||||
else
|
||||
instance_variable_set("@#{key}", value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ module ActionView
|
||||
# <script type="text/javascript" src="/javascripts/cart.js"></script>
|
||||
# <script type="text/javascript" src="/javascripts/checkout.js"></script>
|
||||
#
|
||||
# javascript_include_tag "prototype", "cart", "checkout", :cache => "shop" # when ActionController::Base.perform_caching is false =>
|
||||
# javascript_include_tag "prototype", "cart", "checkout", :cache => "shop" # when ActionController::Base.perform_caching is true =>
|
||||
# <script type="text/javascript" src="/javascripts/shop.js"></script>
|
||||
def javascript_include_tag(*sources)
|
||||
options = sources.extract_options!.stringify_keys
|
||||
@@ -435,9 +435,11 @@ module ActionView
|
||||
else
|
||||
source = "/#{dir}/#{source}" unless source[0] == ?/
|
||||
if has_request
|
||||
source = "#{@controller.request.relative_url_root}#{source}"
|
||||
unless source =~ %r{^#{@controller.request.relative_url_root}/}
|
||||
source = "#{@controller.request.relative_url_root}#{source}"
|
||||
end
|
||||
end
|
||||
rewrite_asset_path!(source)
|
||||
source = rewrite_asset_path(source)
|
||||
|
||||
if include_host
|
||||
host = compute_asset_host(source)
|
||||
@@ -484,11 +486,15 @@ module ActionView
|
||||
end
|
||||
end
|
||||
|
||||
# Break out the asset path rewrite so you wish to put the asset id
|
||||
# Break out the asset path rewrite in case plugins wish to put the asset id
|
||||
# someplace other than the query string.
|
||||
def rewrite_asset_path!(source)
|
||||
def rewrite_asset_path(source)
|
||||
asset_id = rails_asset_id(source)
|
||||
source << "?#{asset_id}" if !asset_id.blank?
|
||||
if asset_id.blank?
|
||||
source
|
||||
else
|
||||
source + "?#{asset_id}"
|
||||
end
|
||||
end
|
||||
|
||||
def javascript_src_tag(source, options)
|
||||
|
||||
@@ -26,7 +26,7 @@ module ActionView
|
||||
# end
|
||||
#
|
||||
# app/views/posts/index.atom.builder:
|
||||
# atom_feed do |feed|
|
||||
# atom_feed(:tag_uri => "2008") do |feed|
|
||||
# feed.title("My great blog!")
|
||||
# feed.updated((@posts.first.created_at))
|
||||
#
|
||||
@@ -44,31 +44,35 @@ module ActionView
|
||||
#
|
||||
# The options are for atom_feed are:
|
||||
#
|
||||
# * <tt>:schema_date</tt>: Required. The date at which the tag scheme for the feed was first used. A good default is the year you created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information.
|
||||
# * <tt>:language</tt>: Defaults to "en-US".
|
||||
# * <tt>:root_url</tt>: The HTML alternative that this feed is doubling for. Defaults to / on the current host.
|
||||
# * <tt>:url</tt>: The URL for this feed. Defaults to the current URL.
|
||||
#
|
||||
# atom_feed yields a AtomFeedBuilder instance.
|
||||
# atom_feed yields an AtomFeedBuilder instance.
|
||||
def atom_feed(options = {}, &block)
|
||||
if options[:schema_date].blank?
|
||||
logger.warn("You must provide the :schema_date option to atom_feed for your feed to be valid. A good default is the year you first created this feed.") unless logger.nil?
|
||||
else
|
||||
options[:schema_date] = options[:schema_date].strftime("%Y-%m-%d") if options[:schema_date].respond_to?(:strftime)
|
||||
end
|
||||
|
||||
xml = options[:xml] || eval("xml", block.binding)
|
||||
xml.instruct!
|
||||
|
||||
xml.feed "xml:lang" => options[:language] || "en-US", "xmlns" => 'http://www.w3.org/2005/Atom' do
|
||||
xml.id("tag:#{request.host}:#{request.request_uri.split(".")[0].gsub("/", "")}")
|
||||
xml.id("tag:#{request.host},#{options[:schema_date]}:#{request.request_uri.split(".")[0]}")
|
||||
xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:root_url] || (request.protocol + request.host_with_port))
|
||||
|
||||
if options[:url]
|
||||
xml.link(:rel => 'self', :type => 'application/atom+xml', :href => options[:url] || request.url)
|
||||
end
|
||||
|
||||
yield AtomFeedBuilder.new(xml, self)
|
||||
xml.link(:rel => 'self', :type => 'application/atom+xml', :href => options[:url] || request.url)
|
||||
|
||||
yield AtomFeedBuilder.new(xml, self, options)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class AtomFeedBuilder
|
||||
def initialize(xml, view)
|
||||
@xml, @view = xml, view
|
||||
def initialize(xml, view, feed_options = {})
|
||||
@xml, @view, @feed_options = xml, view, feed_options
|
||||
end
|
||||
|
||||
# Accepts a Date or Time object and inserts it in the proper format. If nil is passed, current time in UTC is used.
|
||||
@@ -80,12 +84,12 @@ module ActionView
|
||||
#
|
||||
# Options:
|
||||
#
|
||||
# * <tt>:updated</tt>: Time of update. Defaults to the created_at attribute on the record if one such exists.
|
||||
# * <tt>:published</tt>: Time first published. Defaults to the updated_at attribute on the record if one such exists.
|
||||
# * <tt>:published</tt>: Time first published. Defaults to the created_at attribute on the record if one such exists.
|
||||
# * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists.
|
||||
# * <tt>:url</tt>: The URL for this entry. Defaults to the polymorphic_url for the record.
|
||||
def entry(record, options = {})
|
||||
@xml.entry do
|
||||
@xml.id("tag:#{@view.request.host_with_port}:#{record.class}#{record.id}")
|
||||
@xml.id("tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}")
|
||||
|
||||
if options[:published] || (record.respond_to?(:created_at) && record.created_at)
|
||||
@xml.published((options[:published] || record.created_at).xmlschema)
|
||||
@@ -102,10 +106,10 @@ module ActionView
|
||||
end
|
||||
|
||||
private
|
||||
def method_missing(method, *arguments)
|
||||
@xml.__send__(method, *arguments)
|
||||
def method_missing(method, *arguments, &block)
|
||||
@xml.__send__(method, *arguments, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -155,8 +155,9 @@ module ActionView
|
||||
#
|
||||
# In many cases you will want to wrap the above in another helper, so you could do something like the following:
|
||||
#
|
||||
# def labelled_form_for(name, object, options, &proc)
|
||||
# form_for(name, object, options.merge(:builder => LabellingFormBuiler), &proc)
|
||||
# def labelled_form_for(record_or_name_or_array, *args, &proc)
|
||||
# options = args.extract_options!
|
||||
# form_for(record_or_name_or_array, *(args << options.merge(:builder => LabellingFormBuilder)), &proc)
|
||||
# end
|
||||
#
|
||||
# If you don't need to attach a form to a model instance, then check out FormTagHelper#form_tag.
|
||||
|
||||
@@ -114,6 +114,24 @@ module ActionView
|
||||
tag :input, { "type" => "text", "name" => name, "id" => name, "value" => value }.update(options.stringify_keys)
|
||||
end
|
||||
|
||||
# Creates a label field
|
||||
#
|
||||
# ==== Options
|
||||
# * Creates standard HTML attributes for the tag.
|
||||
#
|
||||
# ==== Examples
|
||||
# label_tag 'name'
|
||||
# # => <label for="name">Name</label>
|
||||
#
|
||||
# label_tag 'name', 'Your name'
|
||||
# # => <label for="name">Your Name</label>
|
||||
#
|
||||
# label_tag 'name', nil, :class => 'small_label'
|
||||
# # => <label for="name" class="small_label">Name</label>
|
||||
def label_tag(name, text = nil, options = {})
|
||||
content_tag :label, text || name.humanize, { "for" => name }.update(options.stringify_keys)
|
||||
end
|
||||
|
||||
# Creates a hidden form input field used to transmit data that would be lost due to HTTP's statelessness or
|
||||
# data that should be hidden from the user.
|
||||
#
|
||||
|
||||
@@ -170,7 +170,7 @@ module ActionView
|
||||
when size < 1.gigabyte; "%.#{precision}f MB" % (size / 1.0.megabyte)
|
||||
when size < 1.terabyte; "%.#{precision}f GB" % (size / 1.0.gigabyte)
|
||||
else "%.#{precision}f TB" % (size / 1.0.terabyte)
|
||||
end.sub(/([0-9])\.?0+ /, '\1 ' )
|
||||
end.sub(/([0-9]\.\d*?)0+ /, '\1 ' ).sub(/\. /,' ')
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
||||
@@ -1019,7 +1019,7 @@ module ActionView
|
||||
js_options['parameters'] = options[:with]
|
||||
end
|
||||
|
||||
if protect_against_forgery?
|
||||
if protect_against_forgery? && !options[:form]
|
||||
if js_options['parameters']
|
||||
js_options['parameters'] << " + '&"
|
||||
else
|
||||
|
||||
@@ -436,7 +436,7 @@ module ActionView
|
||||
[-\w]+ # subdomain or domain
|
||||
(?:\.[-\w]+)* # remaining subdomains or domain
|
||||
(?::\d+)? # port
|
||||
(?:/(?:(?:[~\w\+@%-]|(?:[,.;:][^\s$]))+)?)* # path
|
||||
(?:/(?:(?:[~\w\+@%=-]|(?:[,.;:][^\s$]))+)?)* # path
|
||||
(?:\?[\w\+@%&=.;-]+)? # query string
|
||||
(?:\#[\w\-]*)? # trailing anchor
|
||||
)
|
||||
|
||||
@@ -10,6 +10,7 @@ module ActionView
|
||||
@base_path, @assigns, @source, @original_exception =
|
||||
base_path, assigns.dup, source, original_exception
|
||||
@file_path = file_path
|
||||
@backtrace = compute_backtrace
|
||||
end
|
||||
|
||||
def message
|
||||
@@ -72,14 +73,20 @@ module ActionView
|
||||
"#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n"
|
||||
end
|
||||
|
||||
# don't do anything nontrivial here. Any raised exception from here becomes fatal
|
||||
# (and can't be rescued).
|
||||
def backtrace
|
||||
[
|
||||
"#{source_location.capitalize}\n\n#{source_extract(4)}\n " +
|
||||
clean_backtrace.join("\n ")
|
||||
]
|
||||
@backtrace
|
||||
end
|
||||
|
||||
private
|
||||
def compute_backtrace
|
||||
[
|
||||
"#{source_location.capitalize}\n\n#{source_extract(4)}\n " +
|
||||
clean_backtrace.join("\n ")
|
||||
]
|
||||
end
|
||||
|
||||
def strip_base_path(path)
|
||||
stripped_path = File.expand_path(path).gsub(@base_path, "")
|
||||
stripped_path.gsub!(/^#{Regexp.escape File.expand_path(RAILS_ROOT)}/, '') if defined?(RAILS_ROOT)
|
||||
|
||||
24
actionpack/test/activerecord/fixtures_test.rb
Normal file
24
actionpack/test/activerecord/fixtures_test.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
require File.dirname(__FILE__) + '/../active_record_unit'
|
||||
require "action_controller/test_case"
|
||||
|
||||
class ActionController::TestCase
|
||||
self.fixture_path = File.dirname(__FILE__) + '/../fixtures'
|
||||
self.use_transactional_fixtures = false
|
||||
end
|
||||
|
||||
class DeveloperController < ActionController::Base
|
||||
end
|
||||
|
||||
class DeveloperControllerTest < ActionController::TestCase
|
||||
fixtures :developers
|
||||
|
||||
def setup
|
||||
@david = developers(:david)
|
||||
end
|
||||
|
||||
def test_should_have_loaded_fixtures
|
||||
assert_kind_of(Developer, @david)
|
||||
assert_kind_of(Developer, developers(:jamis))
|
||||
assert_equal(@developers.size, Developer.count)
|
||||
end
|
||||
end
|
||||
@@ -124,6 +124,18 @@ class ActionPackAssertionsController < ActionController::Base
|
||||
def rescue_action(e) raise; end
|
||||
end
|
||||
|
||||
# Used to test that assert_response includes the exception message
|
||||
# in the failure message when an action raises and assert_response
|
||||
# is expecting something other than an error.
|
||||
class AssertResponseWithUnexpectedErrorController < ActionController::Base
|
||||
def index
|
||||
raise 'FAIL'
|
||||
end
|
||||
end
|
||||
|
||||
class UserController < ActionController::Base
|
||||
end
|
||||
|
||||
module Admin
|
||||
class InnerModuleController < ActionController::Base
|
||||
def index
|
||||
@@ -161,7 +173,7 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
|
||||
# let's get this party started
|
||||
def setup
|
||||
ActionController::Routing::Routes.reload
|
||||
ActionController::Routing.use_controllers!(%w(action_pack_assertions admin/inner_module content admin/user))
|
||||
ActionController::Routing.use_controllers!(%w(action_pack_assertions admin/inner_module user content admin/user))
|
||||
@controller = ActionPackAssertionsController.new
|
||||
@request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
|
||||
end
|
||||
@@ -255,7 +267,7 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
|
||||
assert_redirected_to admin_inner_module_path
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_assert_redirected_to_top_level_named_route_from_nested_controller
|
||||
with_routing do |set|
|
||||
set.draw do |map|
|
||||
@@ -269,6 +281,20 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_assert_redirected_to_top_level_named_route_with_same_controller_name_in_both_namespaces
|
||||
with_routing do |set|
|
||||
set.draw do |map|
|
||||
# this controller exists in the admin namespace as well which is the only difference from previous test
|
||||
map.top_level '/user/:id', :controller => 'user', :action => 'index'
|
||||
map.connect ':controller/:action/:id'
|
||||
end
|
||||
@controller = Admin::InnerModuleController.new
|
||||
process :redirect_to_top_level_named_route
|
||||
assert_redirected_to "/user/foo"
|
||||
end
|
||||
end
|
||||
|
||||
# -- standard request/response object testing --------------------------------
|
||||
|
||||
# make sure that the template objects exist
|
||||
@@ -465,6 +491,15 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
|
||||
rescue Test::Unit::AssertionFailedError => e
|
||||
end
|
||||
end
|
||||
|
||||
def test_assert_response_uses_exception_message
|
||||
@controller = AssertResponseWithUnexpectedErrorController.new
|
||||
get :index
|
||||
assert_response :success
|
||||
flunk 'Expected non-success response'
|
||||
rescue Test::Unit::AssertionFailedError => e
|
||||
assert e.message.include?('FAIL')
|
||||
end
|
||||
end
|
||||
|
||||
class ActionPackHeaderTest < Test::Unit::TestCase
|
||||
|
||||
@@ -87,7 +87,10 @@ class ControllerInstanceTests < Test::Unit::TestCase
|
||||
# Mocha adds some public instance methods to Object that would be
|
||||
# considered actions, so explicitly hide_action them.
|
||||
def hide_mocha_methods_from_controller(controller)
|
||||
mocha_methods = [:expects, :metaclass, :mocha, :mocha_inspect, :reset_mocha, :stubba_object, :stubba_method, :stubs, :verify, :__metaclass__, :__is_a__]
|
||||
mocha_methods = [
|
||||
:expects, :mocha, :mocha_inspect, :reset_mocha, :stubba_object,
|
||||
:stubba_method, :stubs, :verify, :__metaclass__, :__is_a__, :to_matcher,
|
||||
]
|
||||
controller.class.send!(:hide_action, *mocha_methods)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,8 +4,9 @@ require 'action_controller/cgi_process'
|
||||
class BaseCgiTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@request_hash = {"HTTP_MAX_FORWARDS"=>"10", "SERVER_NAME"=>"glu.ttono.us:8007", "FCGI_ROLE"=>"RESPONDER", "HTTP_X_FORWARDED_HOST"=>"glu.ttono.us", "HTTP_ACCEPT_ENCODING"=>"gzip, deflate", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1", "PATH_INFO"=>"", "HTTP_ACCEPT_LANGUAGE"=>"en", "HTTP_HOST"=>"glu.ttono.us:8007", "SERVER_PROTOCOL"=>"HTTP/1.1", "REDIRECT_URI"=>"/dispatch.fcgi", "SCRIPT_NAME"=>"/dispatch.fcgi", "SERVER_ADDR"=>"207.7.108.53", "REMOTE_ADDR"=>"207.7.108.53", "SERVER_SOFTWARE"=>"lighttpd/1.4.5", "HTTP_COOKIE"=>"_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes", "HTTP_X_FORWARDED_SERVER"=>"glu.ttono.us", "REQUEST_URI"=>"/admin", "DOCUMENT_ROOT"=>"/home/kevinc/sites/typo/public", "SERVER_PORT"=>"8007", "QUERY_STRING"=>"", "REMOTE_PORT"=>"63137", "GATEWAY_INTERFACE"=>"CGI/1.1", "HTTP_X_FORWARDED_FOR"=>"65.88.180.234", "HTTP_ACCEPT"=>"*/*", "SCRIPT_FILENAME"=>"/home/kevinc/sites/typo/public/dispatch.fcgi", "REDIRECT_STATUS"=>"200", "REQUEST_METHOD"=>"GET"}
|
||||
# cookie as returned by some Nokia phone browsers (no space after semicolon separator)
|
||||
@alt_cookie_fmt_request_hash = {"HTTP_COOKIE"=>"_session_id=c84ace84796670c052c6ceb2451fb0f2;is_admin=yes"}
|
||||
# some Nokia phone browsers omit the space after the semicolon separator.
|
||||
# some developers have grown accustomed to using comma in cookie values.
|
||||
@alt_cookie_fmt_request_hash = {"HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes"}
|
||||
@fake_cgi = Struct.new(:env_table).new(@request_hash)
|
||||
@request = ActionController::CgiRequest.new(@fake_cgi)
|
||||
end
|
||||
@@ -76,7 +77,7 @@ class CgiRequestTest < BaseCgiTest
|
||||
assert_equal ["yes"], cookies["is_admin"], cookies.inspect
|
||||
|
||||
alt_cookies = CGI::Cookie::parse(@alt_cookie_fmt_request_hash["HTTP_COOKIE"]);
|
||||
assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], alt_cookies["_session_id"], alt_cookies.inspect
|
||||
assert_equal ["c84ace847,96670c052c6ceb2451fb0f2"], alt_cookies["_session_id"], alt_cookies.inspect
|
||||
assert_equal ["yes"], alt_cookies["is_admin"], alt_cookies.inspect
|
||||
end
|
||||
end
|
||||
|
||||
@@ -696,10 +696,6 @@ class ControllerWithProcFilter < PostsController
|
||||
end
|
||||
end
|
||||
|
||||
class ControllerWithWrongFilterType < PostsController
|
||||
around_filter lambda { yield }, :only => :no_raise
|
||||
end
|
||||
|
||||
class ControllerWithNestedFilters < ControllerWithSymbolAsFilter
|
||||
around_filter :raise_before, :raise_after, :without_exception, :only => :raises_both
|
||||
end
|
||||
@@ -746,14 +742,15 @@ class YieldingAroundFiltersTest < Test::Unit::TestCase
|
||||
assert_equal 1, ControllerWithFilterClass.filter_chain.size
|
||||
assert_equal 1, ControllerWithFilterInstance.filter_chain.size
|
||||
assert_equal 3, ControllerWithSymbolAsFilter.filter_chain.size
|
||||
assert_equal 1, ControllerWithWrongFilterType.filter_chain.size
|
||||
assert_equal 6, ControllerWithNestedFilters.filter_chain.size
|
||||
assert_equal 4, ControllerWithAllTypesOfFilters.filter_chain.size
|
||||
end
|
||||
|
||||
def test_wrong_filter_type
|
||||
assert_raise(ActionController::ActionControllerError) do
|
||||
test_process(ControllerWithWrongFilterType,'no_raise')
|
||||
assert_raise ArgumentError do
|
||||
Class.new PostsController do
|
||||
around_filter lambda { yield }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -204,9 +204,9 @@ class SanitizerTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_should_sanitize_with_trailing_space
|
||||
raw = "display:block; "
|
||||
expected = "display: block;"
|
||||
assert_equal expected, sanitize_css(raw)
|
||||
raw = "display:block; "
|
||||
expected = "display: block;"
|
||||
assert_equal expected, sanitize_css(raw)
|
||||
end
|
||||
|
||||
def test_should_sanitize_xul_style_attributes
|
||||
|
||||
@@ -39,6 +39,11 @@ class MimeTypeTest < Test::Unit::TestCase
|
||||
Mime.module_eval { remove_const :GIF if const_defined?(:GIF) }
|
||||
end
|
||||
|
||||
def test_type_should_be_equal_to_symbol
|
||||
assert_equal Mime::HTML, 'application/xhtml+xml'
|
||||
assert_equal Mime::HTML, :html
|
||||
end
|
||||
|
||||
def test_type_convenience_methods
|
||||
types = [:html, :xml, :png, :pdf, :yaml, :url_encoded_form]
|
||||
types.each do |type|
|
||||
|
||||
@@ -361,10 +361,18 @@ class NewRenderTestController < ActionController::Base
|
||||
render :action => "calling_partial_with_layout"
|
||||
end
|
||||
|
||||
def render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout
|
||||
render :action => "calling_partial_with_layout"
|
||||
end
|
||||
|
||||
def render_using_layout_around_block
|
||||
render :action => "using_layout_around_block"
|
||||
end
|
||||
|
||||
def render_using_layout_around_block_in_main_layout_and_within_content_for_layout
|
||||
render :action => "using_layout_around_block"
|
||||
end
|
||||
|
||||
def rescue_action(e) raise end
|
||||
|
||||
private
|
||||
@@ -387,6 +395,10 @@ class NewRenderTestController < ActionController::Base
|
||||
"layouts/builder"
|
||||
when "action_talk_to_layout", "layout_overriding_layout"
|
||||
"layouts/talk_from_action"
|
||||
when "render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout"
|
||||
"layouts/partial_with_layout"
|
||||
when "render_using_layout_around_block_in_main_layout_and_within_content_for_layout"
|
||||
"layouts/block_with_layout"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -824,9 +836,20 @@ EOS
|
||||
get :render_call_to_partial_with_layout
|
||||
assert_equal "Before (David)\nInside from partial (David)\nAfter", @response.body
|
||||
end
|
||||
|
||||
|
||||
def test_render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout
|
||||
get :render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout
|
||||
assert_equal "Before (Anthony)\nInside from partial (Anthony)\nAfter\nBefore (David)\nInside from partial (David)\nAfter\nBefore (Ramm)\nInside from partial (Ramm)\nAfter", @response.body
|
||||
end
|
||||
|
||||
def test_using_layout_around_block
|
||||
get :using_layout_around_block
|
||||
get :render_using_layout_around_block
|
||||
assert_equal "Before (David)\nInside from block\nAfter", @response.body
|
||||
end
|
||||
|
||||
def test_using_layout_around_block_in_main_layout_and_within_content_for_layout
|
||||
get :render_using_layout_around_block_in_main_layout_and_within_content_for_layout
|
||||
assert_equal "Before (Anthony)\nInside from first block in layout\nAfter\nBefore (David)\nInside from block\nAfter\nBefore (Ramm)\nInside from second block in layout\nAfter\n", @response.body
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -5,94 +5,125 @@ class Article
|
||||
def save; @id = 1 end
|
||||
def new_record?; @id.nil? end
|
||||
def name
|
||||
@id.nil? ? 'new post' : "post ##{@id}"
|
||||
model = self.class.name.downcase
|
||||
@id.nil? ? "new #{model}" : "#{model} ##{@id}"
|
||||
end
|
||||
end
|
||||
|
||||
class Comment
|
||||
attr_reader :id
|
||||
class Comment < Article
|
||||
def post_id; 1 end
|
||||
def save; @id = 1 end
|
||||
def new_record?; @id.nil? end
|
||||
def name
|
||||
@id.nil? ? 'new comment' : "comment ##{@id}"
|
||||
end
|
||||
end
|
||||
|
||||
class Tag < Article
|
||||
def comment_id; 1 end
|
||||
end
|
||||
|
||||
# TODO: test nested models
|
||||
class Comment::Nested < Comment; end
|
||||
|
||||
class Test::Unit::TestCase
|
||||
protected
|
||||
def articles_url
|
||||
'http://www.example.com/articles'
|
||||
end
|
||||
alias_method :new_article_url, :articles_url
|
||||
|
||||
def article_url(article)
|
||||
"http://www.example.com/articles/#{article.id}"
|
||||
end
|
||||
uses_mocha 'polymorphic URL helpers' do
|
||||
class PolymorphicRoutesTest < Test::Unit::TestCase
|
||||
|
||||
def article_comments_url(article)
|
||||
"http://www.example.com/articles/#{article.id}/comments"
|
||||
end
|
||||
include ActionController::PolymorphicRoutes
|
||||
|
||||
def setup
|
||||
@article = Article.new
|
||||
@comment = Comment.new
|
||||
end
|
||||
|
||||
def article_comment_url(article, comment)
|
||||
"http://www.example.com/articles/#{article.id}/comments/#{comment.id}"
|
||||
end
|
||||
|
||||
def admin_articles_url
|
||||
"http://www.example.com/admin/articles"
|
||||
end
|
||||
alias_method :new_admin_article_url, :admin_articles_url
|
||||
|
||||
def admin_article_url(article)
|
||||
"http://www.example.com/admin/articles/#{article.id}"
|
||||
end
|
||||
|
||||
def admin_article_comments_url(article)
|
||||
"http://www.example.com/admin/articles/#{article.id}/comments"
|
||||
end
|
||||
|
||||
def admin_article_comment_url(article, comment)
|
||||
"http://www.example.com/admin/test/articles/#{article.id}/comments/#{comment.id}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class PolymorphicRoutesTest < Test::Unit::TestCase
|
||||
include ActionController::PolymorphicRoutes
|
||||
|
||||
def setup
|
||||
@article = Article.new
|
||||
@comment = Comment.new
|
||||
end
|
||||
|
||||
def test_with_record
|
||||
assert_equal(articles_url, polymorphic_url(@article, :action => 'new'))
|
||||
assert_equal(articles_url, polymorphic_url(@article))
|
||||
@article.save
|
||||
assert_equal(article_url(@article), polymorphic_url(@article))
|
||||
end
|
||||
|
||||
# TODO: Needs to be updated to correctly know about whether the object is in a hash or not
|
||||
def xtest_with_hash
|
||||
@article.save
|
||||
assert_equal(article_url(@article), polymorphic_url(:id => @article))
|
||||
end
|
||||
|
||||
def test_with_array
|
||||
assert_equal(article_comments_url(@article), polymorphic_url([@article, @comment]))
|
||||
@comment.save
|
||||
assert_equal(article_comment_url(@article, @comment), polymorphic_url([@article, @comment]))
|
||||
end
|
||||
|
||||
def test_with_array_and_namespace
|
||||
assert_equal(admin_articles_url, polymorphic_url([:admin, @article], :action => 'new'))
|
||||
assert_equal(admin_articles_url, polymorphic_url([:admin, @article]))
|
||||
@article.save
|
||||
assert_equal(admin_article_url(@article), polymorphic_url([:admin, @article]))
|
||||
assert_equal(admin_article_comments_url(@article), polymorphic_url([:admin, @article, @comment]))
|
||||
@comment.save
|
||||
assert_equal(admin_article_comment_url(@article, @comment), polymorphic_url([:admin, @article, @comment]))
|
||||
def test_with_record
|
||||
@article.save
|
||||
expects(:article_url).with(@article)
|
||||
polymorphic_url(@article)
|
||||
end
|
||||
|
||||
def test_with_new_record
|
||||
expects(:articles_url).with()
|
||||
@article.expects(:new_record?).returns(true)
|
||||
polymorphic_url(@article)
|
||||
end
|
||||
|
||||
def test_with_record_and_action
|
||||
expects(:new_article_url).with()
|
||||
@article.expects(:new_record?).never
|
||||
polymorphic_url(@article, :action => 'new')
|
||||
end
|
||||
|
||||
def test_url_helper_prefixed_with_new
|
||||
expects(:new_article_url).with()
|
||||
new_polymorphic_url(@article)
|
||||
end
|
||||
|
||||
def test_url_helper_prefixed_with_edit
|
||||
@article.save
|
||||
expects(:edit_article_url).with(@article)
|
||||
edit_polymorphic_url(@article)
|
||||
end
|
||||
|
||||
def test_formatted_url_helper
|
||||
expects(:formatted_article_url).with(@article, :pdf)
|
||||
formatted_polymorphic_url([@article, :pdf])
|
||||
end
|
||||
|
||||
# TODO: should this work?
|
||||
def xtest_format_option
|
||||
@article.save
|
||||
expects(:article_url).with(@article, :format => :pdf)
|
||||
polymorphic_url(@article, :format => :pdf)
|
||||
end
|
||||
|
||||
def test_with_nested
|
||||
@comment.save
|
||||
expects(:article_comment_url).with(@article, @comment)
|
||||
polymorphic_url([@article, @comment])
|
||||
end
|
||||
|
||||
def test_with_nested_unsaved
|
||||
expects(:article_comments_url).with(@article)
|
||||
polymorphic_url([@article, @comment])
|
||||
end
|
||||
|
||||
def test_new_with_array_and_namespace
|
||||
expects(:new_admin_article_url).with()
|
||||
polymorphic_url([:admin, @article], :action => 'new')
|
||||
end
|
||||
|
||||
def test_unsaved_with_array_and_namespace
|
||||
expects(:admin_articles_url).with()
|
||||
polymorphic_url([:admin, @article])
|
||||
end
|
||||
|
||||
def test_nested_unsaved_with_array_and_namespace
|
||||
@article.save
|
||||
expects(:admin_article_url).with(@article)
|
||||
polymorphic_url([:admin, @article])
|
||||
expects(:admin_article_comments_url).with(@article)
|
||||
polymorphic_url([:admin, @article, @comment])
|
||||
end
|
||||
|
||||
def test_nested_with_array_and_namespace
|
||||
@comment.save
|
||||
expects(:admin_article_comment_url).with(@article, @comment)
|
||||
polymorphic_url([:admin, @article, @comment])
|
||||
|
||||
# a ridiculously long named route tests correct ordering of namespaces and nesting:
|
||||
@tag = Tag.new
|
||||
@tag.save
|
||||
expects(:site_admin_article_comment_tag_url).with(@article, @comment, @tag)
|
||||
polymorphic_url([:site, :admin, @article, @comment, @tag])
|
||||
end
|
||||
|
||||
# TODO: Needs to be updated to correctly know about whether the object is in a hash or not
|
||||
def xtest_with_hash
|
||||
expects(:article_url).with(@article)
|
||||
@article.save
|
||||
polymorphic_url(:id => @article)
|
||||
end
|
||||
|
||||
def test_polymorphic_path_accepts_options
|
||||
expects(:new_article_path).with()
|
||||
polymorphic_path(@article, :action => :new)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -77,6 +77,10 @@ class RedirectController < ActionController::Base
|
||||
redirect_to Workshop.new(5, true)
|
||||
end
|
||||
|
||||
def redirect_to_nil
|
||||
redirect_to nil
|
||||
end
|
||||
|
||||
def rescue_errors(e) raise e end
|
||||
|
||||
def rescue_action(e) raise end
|
||||
@@ -215,6 +219,13 @@ class RedirectTest < Test::Unit::TestCase
|
||||
get :redirect_to_new_record
|
||||
assert_equal "http://test.host/workshops", redirect_to_url
|
||||
end
|
||||
|
||||
def test_redirect_to_nil
|
||||
assert_raises(ActionController::ActionControllerError) do
|
||||
get :redirect_to_nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module ModuleTest
|
||||
|
||||
@@ -57,6 +57,14 @@ class TestController < ActionController::Base
|
||||
render :text => "hello world", :status => 404
|
||||
end
|
||||
|
||||
def render_text_with_nil
|
||||
render :text => nil
|
||||
end
|
||||
|
||||
def render_text_with_false
|
||||
render :text => false
|
||||
end
|
||||
|
||||
def render_nothing_with_appendix
|
||||
render :text => "appended"
|
||||
end
|
||||
@@ -263,6 +271,17 @@ class RenderTest < Test::Unit::TestCase
|
||||
assert_equal 'hello world', @response.body
|
||||
end
|
||||
|
||||
def test_render_text_with_nil
|
||||
get :render_text_with_nil
|
||||
assert_response 200
|
||||
assert_equal '', @response.body
|
||||
end
|
||||
|
||||
def test_render_text_with_false
|
||||
get :render_text_with_false
|
||||
assert_equal 'false', @response.body
|
||||
end
|
||||
|
||||
def test_render_nothing_with_appendix
|
||||
get :render_nothing_with_appendix
|
||||
assert_response 200
|
||||
|
||||
@@ -22,6 +22,10 @@ module RequestForgeryProtectionActions
|
||||
render :inline => "<%= button_to('New', '/') {} %>"
|
||||
end
|
||||
|
||||
def remote_form
|
||||
render :inline => "<% form_remote_tag(:url => '/') {} %>"
|
||||
end
|
||||
|
||||
def unsafe
|
||||
render :text => 'pwn'
|
||||
end
|
||||
@@ -75,6 +79,11 @@ module RequestForgeryProtectionTests
|
||||
assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
|
||||
end
|
||||
|
||||
def test_should_render_remote_form_with_only_one_token_parameter
|
||||
get :remote_form
|
||||
assert_equal 1, @response.body.scan(@token).size
|
||||
end
|
||||
|
||||
def test_should_allow_get
|
||||
get :index
|
||||
assert_response :success
|
||||
|
||||
@@ -13,9 +13,17 @@ class RequestTest < Test::Unit::TestCase
|
||||
assert_equal '1.2.3.4', @request.remote_ip
|
||||
|
||||
@request.env['HTTP_CLIENT_IP'] = '2.3.4.5'
|
||||
assert_equal '1.2.3.4', @request.remote_ip
|
||||
|
||||
@request.remote_addr = '192.168.0.1'
|
||||
assert_equal '2.3.4.5', @request.remote_ip
|
||||
@request.env.delete 'HTTP_CLIENT_IP'
|
||||
|
||||
@request.remote_addr = '1.2.3.4'
|
||||
@request.env['HTTP_X_FORWARDED_FOR'] = '3.4.5.6'
|
||||
assert_equal '1.2.3.4', @request.remote_ip
|
||||
|
||||
@request.remote_addr = '127.0.0.1'
|
||||
@request.env['HTTP_X_FORWARDED_FOR'] = '3.4.5.6'
|
||||
assert_equal '3.4.5.6', @request.remote_ip
|
||||
|
||||
@@ -35,10 +43,23 @@ class RequestTest < Test::Unit::TestCase
|
||||
assert_equal '3.4.5.6', @request.remote_ip
|
||||
|
||||
@request.env['HTTP_X_FORWARDED_FOR'] = '127.0.0.1,3.4.5.6'
|
||||
assert_equal '127.0.0.1', @request.remote_ip
|
||||
assert_equal '3.4.5.6', @request.remote_ip
|
||||
|
||||
@request.env['HTTP_X_FORWARDED_FOR'] = 'unknown,192.168.0.1'
|
||||
assert_equal '1.2.3.4', @request.remote_ip
|
||||
assert_equal 'unknown', @request.remote_ip
|
||||
|
||||
@request.env['HTTP_X_FORWARDED_FOR'] = '9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4'
|
||||
assert_equal '3.4.5.6', @request.remote_ip
|
||||
|
||||
@request.env['HTTP_CLIENT_IP'] = '8.8.8.8'
|
||||
e = assert_raises(ActionController::ActionControllerError) {
|
||||
@request.remote_ip
|
||||
}
|
||||
assert_match /IP spoofing attack/, e.message
|
||||
assert_match /HTTP_X_FORWARDED_FOR="9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4"/, e.message
|
||||
assert_match /HTTP_CLIENT_IP="8.8.8.8"/, e.message
|
||||
|
||||
@request.env.delete 'HTTP_CLIENT_IP'
|
||||
@request.env.delete 'HTTP_X_FORWARDED_FOR'
|
||||
end
|
||||
|
||||
@@ -371,6 +392,13 @@ class RequestTest < Test::Unit::TestCase
|
||||
assert_equal Mime::HTML, @request.content_type
|
||||
end
|
||||
|
||||
def test_format_assignment_should_set_format
|
||||
@request.instance_eval { self.format = :txt }
|
||||
assert !@request.format.xml?
|
||||
@request.instance_eval { self.format = :xml }
|
||||
assert @request.format.xml?
|
||||
end
|
||||
|
||||
def test_content_no_type
|
||||
assert_equal nil, @request.content_type
|
||||
end
|
||||
|
||||
@@ -2119,15 +2119,15 @@ class RoutingTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_normalize_unix_paths
|
||||
load_paths = %w(. config/../app/controllers config/../app//helpers script/../config/../vendor/rails/actionpack/lib vendor/rails/railties/builtin/rails_info app/models lib script/../config/../foo/bar/../../app/models)
|
||||
load_paths = %w(. config/../app/controllers config/../app//helpers script/../config/../vendor/rails/actionpack/lib vendor/rails/railties/builtin/rails_info app/models lib script/../config/../foo/bar/../../app/models .foo/../.bar foo.bar/../config)
|
||||
paths = ActionController::Routing.normalize_paths(load_paths)
|
||||
assert_equal %w(vendor/rails/railties/builtin/rails_info vendor/rails/actionpack/lib app/controllers app/helpers app/models lib .), paths
|
||||
assert_equal %w(vendor/rails/railties/builtin/rails_info vendor/rails/actionpack/lib app/controllers app/helpers app/models config .bar lib .), paths
|
||||
end
|
||||
|
||||
def test_normalize_windows_paths
|
||||
load_paths = %w(. config\\..\\app\\controllers config\\..\\app\\\\helpers script\\..\\config\\..\\vendor\\rails\\actionpack\\lib vendor\\rails\\railties\\builtin\\rails_info app\\models lib script\\..\\config\\..\\foo\\bar\\..\\..\\app\\models)
|
||||
load_paths = %w(. config\\..\\app\\controllers config\\..\\app\\\\helpers script\\..\\config\\..\\vendor\\rails\\actionpack\\lib vendor\\rails\\railties\\builtin\\rails_info app\\models lib script\\..\\config\\..\\foo\\bar\\..\\..\\app\\models .foo\\..\\.bar foo.bar\\..\\config)
|
||||
paths = ActionController::Routing.normalize_paths(load_paths)
|
||||
assert_equal %w(vendor\\rails\\railties\\builtin\\rails_info vendor\\rails\\actionpack\\lib app\\controllers app\\helpers app\\models lib .), paths
|
||||
assert_equal %w(vendor\\rails\\railties\\builtin\\rails_info vendor\\rails\\actionpack\\lib app\\controllers app\\helpers app\\models config .bar lib .), paths
|
||||
end
|
||||
|
||||
def test_routing_helper_module
|
||||
|
||||
@@ -4,11 +4,21 @@ require "action_controller/test_case"
|
||||
|
||||
class TestTest < Test::Unit::TestCase
|
||||
class TestController < ActionController::Base
|
||||
def no_op
|
||||
render :text => 'dummy'
|
||||
end
|
||||
|
||||
def set_flash
|
||||
flash["test"] = ">#{flash["test"]}<"
|
||||
render :text => 'ignore me'
|
||||
end
|
||||
|
||||
def set_session
|
||||
session['string'] = 'A wonder'
|
||||
session[:symbol] = 'it works'
|
||||
render :text => 'Success'
|
||||
end
|
||||
|
||||
def render_raw_post
|
||||
raise Test::Unit::AssertionFailedError, "#raw_post is blank" if request.raw_post.blank?
|
||||
render :text => request.raw_post
|
||||
@@ -136,6 +146,22 @@ XML
|
||||
assert_equal '>value<', flash['test']
|
||||
end
|
||||
|
||||
def test_process_with_session
|
||||
process :set_session
|
||||
assert_equal 'A wonder', session['string'], "A value stored in the session should be available by string key"
|
||||
assert_equal 'A wonder', session[:string], "Test session hash should allow indifferent access"
|
||||
assert_equal 'it works', session['symbol'], "Test session hash should allow indifferent access"
|
||||
assert_equal 'it works', session[:symbol], "Test session hash should allow indifferent access"
|
||||
end
|
||||
|
||||
def test_process_with_session_arg
|
||||
process :no_op, nil, { 'string' => 'value1', :symbol => 'value2' }
|
||||
assert_equal 'value1', session['string']
|
||||
assert_equal 'value1', session[:string]
|
||||
assert_equal 'value2', session['symbol']
|
||||
assert_equal 'value2', session[:symbol]
|
||||
end
|
||||
|
||||
def test_process_with_request_uri_with_no_params
|
||||
process :test_uri
|
||||
assert_equal "/test_test/test/test_uri", @response.body
|
||||
@@ -375,6 +401,13 @@ XML
|
||||
assert_routing 'content', :controller => 'content', :action => 'index'
|
||||
end
|
||||
|
||||
def test_assert_routing_with_method
|
||||
with_routing do |set|
|
||||
set.draw { |map| map.resources(:content) }
|
||||
assert_routing({ :method => 'post', :path => 'content' }, { :controller => 'content', :action => 'create' })
|
||||
end
|
||||
end
|
||||
|
||||
def test_assert_routing_in_module
|
||||
assert_routing 'admin/user', :controller => 'admin/user', :action => 'index'
|
||||
end
|
||||
@@ -614,10 +647,19 @@ class InferringClassNameTest < Test::Unit::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
class ContentControllerTest < ActionController::TestCase
|
||||
def setup
|
||||
# Should not override ActionController setup methods
|
||||
end
|
||||
|
||||
def test_should_still_setup_controller
|
||||
assert_kind_of(ContentController, @controller)
|
||||
end
|
||||
end
|
||||
|
||||
class CrazyNameTest < ActionController::TestCase
|
||||
tests ContentController
|
||||
def test_controller_class_can_be_set_manually_not_just_inferred
|
||||
assert_equal ContentController, self.class.controller_class
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -149,27 +149,57 @@ class UrlWriterTests < Test::Unit::TestCase
|
||||
)
|
||||
end
|
||||
|
||||
def test_named_route
|
||||
def test_relative_url_root_is_respected
|
||||
orig_relative_url_root = ActionController::AbstractRequest.relative_url_root
|
||||
ActionController::AbstractRequest.relative_url_root = '/subdir'
|
||||
|
||||
add_host!
|
||||
assert_equal('https://www.basecamphq.com/subdir/c/a/i',
|
||||
W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https')
|
||||
)
|
||||
ensure
|
||||
ActionController::AbstractRequest.relative_url_root = orig_relative_url_root
|
||||
end
|
||||
|
||||
def test_named_routes
|
||||
ActionController::Routing::Routes.draw do |map|
|
||||
map.no_args '/this/is/verbose', :controller => 'home', :action => 'index'
|
||||
map.home '/home/sweet/home/:user', :controller => 'home', :action => 'index'
|
||||
map.connect ':controller/:action/:id'
|
||||
end
|
||||
|
||||
|
||||
# We need to create a new class in order to install the new named route.
|
||||
kls = Class.new { include ActionController::UrlWriter }
|
||||
controller = kls.new
|
||||
assert controller.respond_to?(:home_url)
|
||||
assert_equal 'http://www.basecamphq.com/home/sweet/home/again',
|
||||
controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again')
|
||||
|
||||
|
||||
assert_equal("/home/sweet/home/alabama", controller.send(:home_path, :user => 'alabama', :host => 'unused'))
|
||||
assert_equal("http://www.basecamphq.com/home/sweet/home/alabama", controller.send(:home_url, :user => 'alabama', :host => 'www.basecamphq.com'))
|
||||
assert_equal("http://www.basecamphq.com/this/is/verbose", controller.send(:no_args_url, :host=>'www.basecamphq.com'))
|
||||
ensure
|
||||
ActionController::Routing::Routes.load!
|
||||
end
|
||||
|
||||
|
||||
def test_relative_url_root_is_respected_for_named_routes
|
||||
orig_relative_url_root = ActionController::AbstractRequest.relative_url_root
|
||||
ActionController::AbstractRequest.relative_url_root = '/subdir'
|
||||
|
||||
ActionController::Routing::Routes.draw do |map|
|
||||
map.home '/home/sweet/home/:user', :controller => 'home', :action => 'index'
|
||||
end
|
||||
|
||||
kls = Class.new { include ActionController::UrlWriter }
|
||||
controller = kls.new
|
||||
|
||||
assert_equal 'http://www.basecamphq.com/subdir/home/sweet/home/again',
|
||||
controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again')
|
||||
ensure
|
||||
ActionController::Routing::Routes.load!
|
||||
ActionController::AbstractRequest.relative_url_root = orig_relative_url_root
|
||||
end
|
||||
|
||||
def test_only_path
|
||||
ActionController::Routing::Routes.draw do |map|
|
||||
map.home '/home/sweet/home/:user', :controller => 'home', :action => 'index'
|
||||
|
||||
3
actionpack/test/fixtures/layouts/block_with_layout.erb
vendored
Normal file
3
actionpack/test/fixtures/layouts/block_with_layout.erb
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<% render(:layout => "layout_for_partial", :locals => { :name => "Anthony" }) do %>Inside from first block in layout<% end %>
|
||||
<%= yield %>
|
||||
<% render(:layout => "layout_for_partial", :locals => { :name => "Ramm" }) do %>Inside from second block in layout<% end %>
|
||||
3
actionpack/test/fixtures/layouts/partial_with_layout.erb
vendored
Normal file
3
actionpack/test/fixtures/layouts/partial_with_layout.erb
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<%= render( :layout => "layout_for_partial", :partial => "partial_for_use_in_layout", :locals => {:name => 'Anthony' } ) %>
|
||||
<%= yield %>
|
||||
<%= render( :layout => "layout_for_partial", :partial => "partial_for_use_in_layout", :locals => {:name => 'Ramm' } ) %>
|
||||
@@ -392,6 +392,8 @@ class AssetTagHelperNonVhostTest < Test::Unit::TestCase
|
||||
assert_dom_equal(%(/collaboration/hieraki/javascripts/xmlhr.js), javascript_path("xmlhr"))
|
||||
assert_dom_equal(%(/collaboration/hieraki/stylesheets/style.css), stylesheet_path("style"))
|
||||
assert_dom_equal(%(/collaboration/hieraki/images/xml.png), image_path("xml.png"))
|
||||
assert_dom_equal(%(<img alt="Mouse" onmouseover="this.src='/collaboration/hieraki/images/mouse_over.png'" onmouseout="this.src='/collaboration/hieraki/images/mouse.png'" src="/collaboration/hieraki/images/mouse.png" />), image_tag("mouse.png", :mouseover => "/images/mouse_over.png"))
|
||||
assert_dom_equal(%(<img alt="Mouse2" onmouseover="this.src='/collaboration/hieraki/images/mouse_over2.png'" onmouseout="this.src='/collaboration/hieraki/images/mouse2.png'" src="/collaboration/hieraki/images/mouse2.png" />), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png")))
|
||||
end
|
||||
|
||||
def test_should_ignore_relative_root_path_on_complete_url
|
||||
@@ -404,6 +406,8 @@ class AssetTagHelperNonVhostTest < Test::Unit::TestCase
|
||||
assert_dom_equal(%(http://assets.example.com/collaboration/hieraki/javascripts/xmlhr.js), javascript_path("xmlhr"))
|
||||
assert_dom_equal(%(http://assets.example.com/collaboration/hieraki/stylesheets/style.css), stylesheet_path("style"))
|
||||
assert_dom_equal(%(http://assets.example.com/collaboration/hieraki/images/xml.png), image_path("xml.png"))
|
||||
assert_dom_equal(%(<img alt="Mouse" onmouseover="this.src='http://assets.example.com/collaboration/hieraki/images/mouse_over.png'" onmouseout="this.src='http://assets.example.com/collaboration/hieraki/images/mouse.png'" src="http://assets.example.com/collaboration/hieraki/images/mouse.png" />), image_tag("mouse.png", :mouseover => "/images/mouse_over.png"))
|
||||
assert_dom_equal(%(<img alt="Mouse2" onmouseover="this.src='http://assets.example.com/collaboration/hieraki/images/mouse_over2.png'" onmouseout="this.src='http://assets.example.com/collaboration/hieraki/images/mouse2.png'" src="http://assets.example.com/collaboration/hieraki/images/mouse2.png" />), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png")))
|
||||
ensure
|
||||
ActionController::Base.asset_host = ""
|
||||
end
|
||||
|
||||
@@ -5,7 +5,7 @@ Scroll = Struct.new(:id, :to_param, :title, :body, :updated_at, :created_at)
|
||||
class ScrollsController < ActionController::Base
|
||||
FEEDS = {}
|
||||
FEEDS["defaults"] = <<-EOT
|
||||
atom_feed do |feed|
|
||||
atom_feed(:schema_date => '2008') do |feed|
|
||||
feed.title("My great blog!")
|
||||
feed.updated((@scrolls.first.created_at))
|
||||
|
||||
@@ -38,7 +38,23 @@ class ScrollsController < ActionController::Base
|
||||
end
|
||||
end
|
||||
EOT
|
||||
FEEDS["xml_block"] = <<-EOT
|
||||
atom_feed do |feed|
|
||||
feed.title("My great blog!")
|
||||
feed.updated((@scrolls.first.created_at))
|
||||
|
||||
feed.author do |author|
|
||||
author.name("DHH")
|
||||
end
|
||||
|
||||
for scroll in @scrolls
|
||||
feed.entry(scroll, :url => "/otherstuff/" + scroll.to_param, :updated => Time.utc(2007, 1, scroll.id)) do |entry|
|
||||
entry.title(scroll.title)
|
||||
entry.content(scroll.body, :type => 'html')
|
||||
end
|
||||
end
|
||||
end
|
||||
EOT
|
||||
def index
|
||||
@scrolls = [
|
||||
Scroll.new(1, "1", "Hello One", "Something <i>COOL!</i>", Time.utc(2007, 12, 12, 15), Time.utc(2007, 12, 12, 15)),
|
||||
@@ -47,6 +63,12 @@ class ScrollsController < ActionController::Base
|
||||
|
||||
render :inline => FEEDS[params[:id]], :type => :builder
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def rescue_action(e)
|
||||
raise(e)
|
||||
end
|
||||
end
|
||||
|
||||
class AtomFeedTest < Test::Unit::TestCase
|
||||
@@ -88,6 +110,34 @@ class AtomFeedTest < Test::Unit::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
def test_self_url_should_default_to_current_request_url
|
||||
with_restful_routing(:scrolls) do
|
||||
get :index, :id => "defaults"
|
||||
assert_select "link[rel=self][href=http://www.nextangle.com/scrolls?id=defaults]"
|
||||
end
|
||||
end
|
||||
|
||||
def test_feed_id_should_be_a_valid_tag
|
||||
with_restful_routing(:scrolls) do
|
||||
get :index, :id => "defaults"
|
||||
assert_select "id", :text => "tag:www.nextangle.com,2008:/scrolls?id=defaults"
|
||||
end
|
||||
end
|
||||
|
||||
def test_entry_id_should_be_a_valid_tag
|
||||
with_restful_routing(:scrolls) do
|
||||
get :index, :id => "defaults"
|
||||
assert_select "entry id", :text => "tag:www.nextangle.com,2008:Scroll/1"
|
||||
assert_select "entry id", :text => "tag:www.nextangle.com,2008:Scroll/2"
|
||||
end
|
||||
end
|
||||
|
||||
def test_feed_should_allow_nested_xml_blocks
|
||||
with_restful_routing(:scrolls) do
|
||||
get :index, :id => "xml_block"
|
||||
assert_select "author name", :text => "DHH"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def with_restful_routing(resources)
|
||||
@@ -98,4 +148,4 @@ class AtomFeedTest < Test::Unit::TestCase
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1395,7 +1395,7 @@ class DateHelperTest < Test::Unit::TestCase
|
||||
|
||||
expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
|
||||
expected << %(<option value=""></option>\n)
|
||||
2002.upto(2012) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
|
||||
(Time.now.year - 5).upto(Time.now.year + 5) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
|
||||
expected << "</select>\n"
|
||||
expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
|
||||
expected << %(<option value=""></option>\n)
|
||||
|
||||
51
actionpack/test/template/deprecate_ivars_test.rb
Normal file
51
actionpack/test/template/deprecate_ivars_test.rb
Normal file
@@ -0,0 +1,51 @@
|
||||
require File.dirname(__FILE__) + '/../abstract_unit'
|
||||
|
||||
class DeprecateIvars < ActionController::Base
|
||||
def use_logger
|
||||
render :inline => "<%= logger.class -%>"
|
||||
end
|
||||
|
||||
def use_old_logger
|
||||
render :inline => "<%= @logger.class -%>"
|
||||
end
|
||||
|
||||
def use_action_name
|
||||
render :inline => "<%= action_name -%>"
|
||||
end
|
||||
|
||||
def use_old_action_name
|
||||
render :inline => "<%= @action_name -%>"
|
||||
end
|
||||
end
|
||||
|
||||
class DeprecateIvarsTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
|
||||
@controller = DeprecateIvars.new
|
||||
@controller.logger = Logger.new(nil)
|
||||
|
||||
@request.host = "rubyonrails.com"
|
||||
end
|
||||
|
||||
def test_logger
|
||||
assert_not_deprecated { get :use_logger }
|
||||
assert_equal "Logger", @response.body
|
||||
end
|
||||
|
||||
def test_deprecated_logger
|
||||
assert_deprecated { get :use_old_logger }
|
||||
assert_equal "Logger", @response.body
|
||||
end
|
||||
|
||||
def test_action_name
|
||||
assert_not_deprecated { get :use_action_name }
|
||||
assert_equal "use_action_name", @response.body
|
||||
end
|
||||
|
||||
def test_deprecated_action_name
|
||||
assert_deprecated { get :use_old_action_name }
|
||||
assert_equal "use_old_action_name", @response.body
|
||||
end
|
||||
end
|
||||
@@ -188,6 +188,24 @@ class FormTagHelperTest < Test::Unit::TestCase
|
||||
assert_dom_equal expected, actual
|
||||
end
|
||||
|
||||
def test_label_tag_without_text
|
||||
actual = label_tag "title"
|
||||
expected = %(<label for="title">Title</label>)
|
||||
assert_dom_equal expected, actual
|
||||
end
|
||||
|
||||
def test_label_tag_with_text
|
||||
actual = label_tag "title", "My Title"
|
||||
expected = %(<label for="title">My Title</label>)
|
||||
assert_dom_equal expected, actual
|
||||
end
|
||||
|
||||
def test_label_tag_class_string
|
||||
actual = label_tag "title", "My Title", "class" => "small_label"
|
||||
expected = %(<label for="title" class="small_label">My Title</label>)
|
||||
assert_dom_equal expected, actual
|
||||
end
|
||||
|
||||
def test_boolean_optios
|
||||
assert_dom_equal %(<input checked="checked" disabled="disabled" id="admin" name="admin" readonly="readonly" type="checkbox" value="1" />), check_box_tag("admin", 1, true, 'disabled' => true, :readonly => "yes")
|
||||
assert_dom_equal %(<input checked="checked" id="admin" name="admin" type="checkbox" value="1" />), check_box_tag("admin", 1, true, :disabled => false, :readonly => nil)
|
||||
|
||||
@@ -87,6 +87,7 @@ class NumberHelperTest < Test::Unit::TestCase
|
||||
assert_equal '1.01 KB', number_to_human_size(1.0100.kilobytes, 4)
|
||||
assert_equal '10 KB', number_to_human_size(10.000.kilobytes, 4)
|
||||
assert_equal '1 Byte', number_to_human_size(1.1)
|
||||
assert_equal '10 Bytes', number_to_human_size(10)
|
||||
assert_nil number_to_human_size('x')
|
||||
assert_nil number_to_human_size(nil)
|
||||
end
|
||||
|
||||
@@ -175,6 +175,7 @@ class TextHelperTest < Test::Unit::TestCase
|
||||
http://www.rubyonrails.com/~minam/contact;new?with=query&string=params
|
||||
http://en.wikipedia.org/wiki/Wikipedia:Today%27s_featured_picture_%28animation%29/January_20%2C_2007
|
||||
http://www.mail-archive.com/rails@lists.rubyonrails.org/
|
||||
http://www.amazon.com/Testing-Equal-Sign-In-Path/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1198861734&sr=8-1
|
||||
)
|
||||
|
||||
urls.each do |url|
|
||||
|
||||
@@ -1,4 +1,20 @@
|
||||
*SVN*
|
||||
*2.0.3* (12th May 2008)
|
||||
|
||||
* Migrations: create_table supports primary_key_prefix_type. #10314 [student, thechrisoshow]
|
||||
|
||||
* Ensure that ActiveRecord::Calculations disambiguates field names with the table name. #11027 [cavalle]
|
||||
|
||||
* Ensure that modifying has_and_belongs_to_many actions clear the query cache. Closes #10840 [john.andrews]
|
||||
|
||||
* Fix issue where Table#references doesn't pass a :null option to a *_type attribute for polymorphic associations. Closes #10753 [railsjitsu]
|
||||
|
||||
* update_all ignores scoped :order and :limit, so post.comments.update_all doesn't try to include the comment order in the update statement. #10686 [Brendan Ribera]
|
||||
|
||||
* Added by parameter to increment, decrement, and their bang varieties so you can do player1.increment!(:points, 5) #10542 [Sam]
|
||||
|
||||
* Optimize ActiveRecord::Base#exists? to use #select_all instead of #find. Closes #10605 [jamesh, fcheung, protocool]
|
||||
|
||||
* Don't unnecessarily load has_many associations in after_update callbacks. Closes #6822 [stopdropandrew, canadaduane]
|
||||
|
||||
* Eager belongs_to :include infers the foreign key from the association name rather than the class name. #10517 [Jonathan Viney]
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
require 'rake/packagetask'
|
||||
require 'rake/gempackagetask'
|
||||
require 'rake/contrib/rubyforgepublisher'
|
||||
require File.join(File.dirname(__FILE__), 'lib', 'active_record', 'version')
|
||||
|
||||
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
||||
@@ -173,7 +172,7 @@ spec = Gem::Specification.new do |s|
|
||||
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
|
||||
end
|
||||
|
||||
s.add_dependency('activesupport', '= 2.0.2' + PKG_BUILD)
|
||||
s.add_dependency('activesupport', '= 2.0.3' + PKG_BUILD)
|
||||
|
||||
s.files.delete "test/fixtures/fixture_database.sqlite"
|
||||
s.files.delete "test/fixtures/fixture_database_2.sqlite"
|
||||
@@ -239,6 +238,7 @@ end
|
||||
desc "Publish the release files to RubyForge."
|
||||
task :release => [ :package ] do
|
||||
require 'rubyforge'
|
||||
require 'rake/contrib/rubyforgepublisher'
|
||||
|
||||
packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ module ActiveRecord
|
||||
# #others<< | X | X | X
|
||||
# #others.push | X | X | X
|
||||
# #others.concat | X | X | X
|
||||
# #others.build(attributes={}) | X | X | X
|
||||
# #others.build(attributes={}) | X | X |
|
||||
# #others.create(attributes={}) | X | X |
|
||||
# #others.create!(attributes={}) | X | X | X
|
||||
# #others.size | X | X | X
|
||||
@@ -487,6 +487,22 @@ module ActiveRecord
|
||||
# When eager loaded, conditions are interpolated in the context of the model class, not the model instance. Conditions are lazily interpolated
|
||||
# before the actual model exists.
|
||||
#
|
||||
# Eager loading is not possible with polymorphic associations. Given
|
||||
#
|
||||
# class Address < ActiveRecord::Base
|
||||
# belongs_to :addressable, :polymorphic => true
|
||||
# end
|
||||
#
|
||||
# a call that tries to eager load the addressable model
|
||||
#
|
||||
# Address.find(:all, :include => :addressable) # INVALID
|
||||
#
|
||||
# will raise <tt>ActiveRecord::EagerLoadPolymorphicError</tt>. The reason is that the parent model's type
|
||||
# is a column value so its corresponding table name cannot be put in the FROM/JOIN clauses of that early query.
|
||||
#
|
||||
# It does work the other way around though: if the <tt>User</tt> model is <tt>addressable</tt> you can eager load
|
||||
# their addresses with <tt>:include</tt> just fine, every piece needed to construct the query is known beforehand.
|
||||
#
|
||||
# == Table Aliasing
|
||||
#
|
||||
# ActiveRecord uses table aliasing in the case that a table is referenced multiple times in a join. If a table is referenced only once,
|
||||
@@ -783,6 +799,7 @@ module ActiveRecord
|
||||
# a column name instead of a +true+/+false+ value to this option (e.g., <tt>:counter_cache => :my_custom_counter</tt>.)
|
||||
# Note: Specifying a counter_cache will add it to that model's list of readonly attributes using #attr_readonly.
|
||||
# * <tt>:include</tt> - specify second-order associations that should be eager loaded when this object is loaded.
|
||||
# Not allowed if the association is polymorphic.
|
||||
# * <tt>:polymorphic</tt> - specify this association is a polymorphic association by passing +true+.
|
||||
# Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
|
||||
# to the attr_readonly list in the associated classes (e.g. class Post; attr_readonly :comments_count; end).
|
||||
@@ -1079,8 +1096,10 @@ module ActiveRecord
|
||||
if association.respond_to?(:loaded?)
|
||||
if new_record?
|
||||
association
|
||||
else
|
||||
elsif association.loaded?
|
||||
association.select { |record| record.new_record? }
|
||||
else
|
||||
association.target.select { |record| record.new_record? }
|
||||
end.each do |record|
|
||||
errors.add "#{association_name}" unless record.valid?
|
||||
end
|
||||
@@ -1097,6 +1116,8 @@ module ActiveRecord
|
||||
association
|
||||
elsif association.respond_to?(:loaded?) && association.loaded?
|
||||
association.select { |record| record.new_record? }
|
||||
elsif association.respond_to?(:loaded?) && !association.loaded?
|
||||
association.target.select { |record| record.new_record? }
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
@@ -14,7 +14,7 @@ module ActiveRecord
|
||||
def create(attributes = {})
|
||||
create_record(attributes) { |record| insert_record(record) }
|
||||
end
|
||||
|
||||
|
||||
def create!(attributes = {})
|
||||
create_record(attributes) { |record| insert_record(record, true) }
|
||||
end
|
||||
@@ -80,7 +80,7 @@ module ActiveRecord
|
||||
end
|
||||
|
||||
if @reflection.options[:insert_sql]
|
||||
@owner.connection.execute(interpolate_sql(@reflection.options[:insert_sql], record))
|
||||
@owner.connection.insert(interpolate_sql(@reflection.options[:insert_sql], record))
|
||||
else
|
||||
columns = @owner.connection.columns(@reflection.options[:join_table], "#{@reflection.options[:join_table]} Columns")
|
||||
|
||||
@@ -103,7 +103,7 @@ module ActiveRecord
|
||||
"INSERT INTO #{@reflection.options[:join_table]} (#{@owner.send(:quoted_column_names, attributes).join(', ')}) " +
|
||||
"VALUES (#{attributes.values.join(', ')})"
|
||||
|
||||
@owner.connection.execute(sql)
|
||||
@owner.connection.insert(sql)
|
||||
end
|
||||
|
||||
return true
|
||||
@@ -111,11 +111,11 @@ module ActiveRecord
|
||||
|
||||
def delete_records(records)
|
||||
if sql = @reflection.options[:delete_sql]
|
||||
records.each { |record| @owner.connection.execute(interpolate_sql(sql, record)) }
|
||||
records.each { |record| @owner.connection.delete(interpolate_sql(sql, record)) }
|
||||
else
|
||||
ids = quoted_record_ids(records)
|
||||
sql = "DELETE FROM #{@reflection.options[:join_table]} WHERE #{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.association_foreign_key} IN (#{ids})"
|
||||
@owner.connection.execute(sql)
|
||||
sql = "DELETE FROM #{@owner.connection.quote_table_name @reflection.options[:join_table]} WHERE #{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.association_foreign_key} IN (#{ids})"
|
||||
@owner.connection.delete(sql)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -548,8 +548,14 @@ module ActiveRecord #:nodoc:
|
||||
# Person.exists?(:name => "David")
|
||||
# Person.exists?(['name LIKE ?', "%#{query}%"])
|
||||
def exists?(id_or_conditions)
|
||||
!find(:first, :select => "#{quoted_table_name}.#{primary_key}",
|
||||
:conditions => expand_id_conditions(id_or_conditions)).nil?
|
||||
connection.select_all(
|
||||
construct_finder_sql(
|
||||
:select => "#{quoted_table_name}.#{primary_key}",
|
||||
:conditions => expand_id_conditions(id_or_conditions),
|
||||
:limit => 1
|
||||
),
|
||||
"#{name} Exists"
|
||||
).size > 0
|
||||
end
|
||||
|
||||
# Creates an object (or multiple objects) and saves it to the database, if validations pass.
|
||||
@@ -674,8 +680,8 @@ module ActiveRecord #:nodoc:
|
||||
sql = "UPDATE #{table_name} SET #{sanitize_sql_for_assignment(updates)} "
|
||||
scope = scope(:find)
|
||||
add_conditions!(sql, conditions, scope)
|
||||
add_order!(sql, options[:order], scope)
|
||||
add_limit!(sql, options, scope)
|
||||
add_order!(sql, options[:order], nil)
|
||||
add_limit!(sql, options, nil)
|
||||
connection.update(sql, "#{name} Update")
|
||||
end
|
||||
|
||||
@@ -960,14 +966,19 @@ module ActiveRecord #:nodoc:
|
||||
end
|
||||
|
||||
def reset_primary_key #:nodoc:
|
||||
key = get_primary_key(base_class.name)
|
||||
set_primary_key(key)
|
||||
key
|
||||
end
|
||||
|
||||
def get_primary_key(base_name) #:nodoc:
|
||||
key = 'id'
|
||||
case primary_key_prefix_type
|
||||
when :table_name
|
||||
key = Inflector.foreign_key(base_class.name, false)
|
||||
key = Inflector.foreign_key(base_name, false)
|
||||
when :table_name_with_underscore
|
||||
key = Inflector.foreign_key(base_class.name)
|
||||
key = Inflector.foreign_key(base_name)
|
||||
end
|
||||
set_primary_key(key)
|
||||
key
|
||||
end
|
||||
|
||||
@@ -1487,7 +1498,7 @@ module ActiveRecord #:nodoc:
|
||||
|
||||
self.class_eval %{
|
||||
def self.#{method_id}(*args)
|
||||
options = args.last.is_a?(Hash) ? args.pop : {}
|
||||
options = args.extract_options!
|
||||
attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args)
|
||||
finder_options = { :conditions => attributes }
|
||||
validate_find_options(options)
|
||||
@@ -2046,28 +2057,28 @@ module ActiveRecord #:nodoc:
|
||||
save!
|
||||
end
|
||||
|
||||
# Initializes the +attribute+ to zero if nil and adds one. Only makes sense for number-based attributes. Returns self.
|
||||
def increment(attribute)
|
||||
# Initializes the +attribute+ to zero if nil and adds the value passed as +by+ (default is one). Only makes sense for number-based attributes. Returns self.
|
||||
def increment(attribute, by = 1)
|
||||
self[attribute] ||= 0
|
||||
self[attribute] += 1
|
||||
self[attribute] += by
|
||||
self
|
||||
end
|
||||
|
||||
# Increments the +attribute+ and saves the record.
|
||||
def increment!(attribute)
|
||||
increment(attribute).update_attribute(attribute, self[attribute])
|
||||
def increment!(attribute, by = 1)
|
||||
increment(attribute, by).update_attribute(attribute, self[attribute])
|
||||
end
|
||||
|
||||
# Initializes the +attribute+ to zero if nil and subtracts one. Only makes sense for number-based attributes. Returns self.
|
||||
def decrement(attribute)
|
||||
# Initializes the +attribute+ to zero if nil and subtracts the value passed as +by+ (default is one). Only makes sense for number-based attributes. Returns self.
|
||||
def decrement(attribute, by = 1)
|
||||
self[attribute] ||= 0
|
||||
self[attribute] -= 1
|
||||
self[attribute] -= by
|
||||
self
|
||||
end
|
||||
|
||||
# Decrements the +attribute+ and saves the record.
|
||||
def decrement!(attribute)
|
||||
decrement(attribute).update_attribute(attribute, self[attribute])
|
||||
def decrement!(attribute, by = 1)
|
||||
decrement(attribute, by).update_attribute(attribute, self[attribute])
|
||||
end
|
||||
|
||||
# Turns an +attribute+ that's currently true into false and vice versa. Returns self.
|
||||
@@ -2134,6 +2145,7 @@ module ActiveRecord #:nodoc:
|
||||
if options.nil?
|
||||
attributes
|
||||
else
|
||||
ActiveSupport::Deprecation.warn "Passing options to Base#attributes is deprecated and will be removed in Rails 2.1. Please use Hash#slice or Hash#except instead"
|
||||
if except = options[:except]
|
||||
except = Array(except).collect { |attribute| attribute.to_s }
|
||||
except.each { |attribute_name| attributes.delete(attribute_name) }
|
||||
@@ -2385,6 +2397,10 @@ module ActiveRecord #:nodoc:
|
||||
end
|
||||
|
||||
# Includes an ugly hack for Time.local instead of Time.new because the latter is reserved by Time itself.
|
||||
def instantiate_time_object(*values)
|
||||
@@default_timezone == :utc ? Time.utc(*values) : Time.local(*values)
|
||||
end
|
||||
|
||||
def execute_callstack_for_multiparameter_attributes(callstack)
|
||||
errors = []
|
||||
callstack.each do |name, values|
|
||||
@@ -2393,7 +2409,19 @@ module ActiveRecord #:nodoc:
|
||||
send(name + "=", nil)
|
||||
else
|
||||
begin
|
||||
send(name + "=", Time == klass ? (@@default_timezone == :utc ? klass.utc(*values) : klass.local(*values)) : klass.new(*values))
|
||||
value = if Time == klass
|
||||
instantiate_time_object(*values)
|
||||
elsif Date == klass
|
||||
begin
|
||||
Date.new(*values)
|
||||
rescue ArgumentError => ex # if Date.new raises an exception on an invalid date
|
||||
instantiate_time_object(*values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
|
||||
end
|
||||
else
|
||||
klass.new(*values)
|
||||
end
|
||||
|
||||
send(name + "=", value)
|
||||
rescue => ex
|
||||
errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
|
||||
end
|
||||
|
||||
@@ -155,6 +155,7 @@ module ActiveRecord
|
||||
scope = scope(:find)
|
||||
merged_includes = merge_includes(scope ? scope[:include] : [], options[:include])
|
||||
aggregate_alias = column_alias_for(operation, column_name)
|
||||
column_name = "#{connection.quote_table_name(table_name)}.#{column_name}" unless column_name == "*" || column_name.to_s.include?('.')
|
||||
|
||||
if operation == 'count'
|
||||
if merged_includes.any?
|
||||
@@ -213,7 +214,7 @@ module ActiveRecord
|
||||
group_attr = options[:group].to_s
|
||||
association = reflect_on_association(group_attr.to_sym)
|
||||
associated = association && association.macro == :belongs_to # only count belongs_to associations
|
||||
group_field = (associated ? "#{options[:group]}_id" : options[:group]).to_s
|
||||
group_field = associated ? association.primary_key_name : group_attr
|
||||
group_alias = column_alias_for(group_field)
|
||||
group_column = column_for group_field
|
||||
sql = construct_calculation_sql(operation, column_name, options.merge(:group_field => group_field, :group_alias => group_alias))
|
||||
|
||||
@@ -453,9 +453,7 @@ module ActiveRecord
|
||||
polymorphic = options.delete(:polymorphic)
|
||||
args.each do |col|
|
||||
column("#{col}_id", :integer, options)
|
||||
unless polymorphic.nil?
|
||||
column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : {})
|
||||
end
|
||||
column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
|
||||
end
|
||||
end
|
||||
alias :belongs_to :references
|
||||
|
||||
@@ -89,7 +89,7 @@ module ActiveRecord
|
||||
# See also TableDefinition#column for details on how to create columns.
|
||||
def create_table(table_name, options = {})
|
||||
table_definition = TableDefinition.new(self)
|
||||
table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false
|
||||
table_definition.primary_key(options[:primary_key] || Base.get_primary_key(table_name)) unless options[:id] == false
|
||||
|
||||
yield table_definition
|
||||
|
||||
|
||||
@@ -386,8 +386,8 @@ module ActiveRecord
|
||||
|
||||
# Executes an INSERT query and returns the new record's ID
|
||||
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
||||
table = sql.split(" ", 4)[2]
|
||||
super || last_insert_id(table, sequence_name || default_sequence_name(table, pk))
|
||||
table = sql.split(" ", 4)[2].gsub('"', '')
|
||||
super || pk && last_insert_id(table, sequence_name || default_sequence_name(table, pk))
|
||||
end
|
||||
|
||||
# Queries the database and returns the results in an Array or nil otherwise.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user