mirror of
https://github.com/github/rails.git
synced 2026-01-13 08:38:05 -05:00
Compare commits
84 Commits
v2.3.9
...
2.3.14.git
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6eb61b7e4 | ||
|
|
fe11782158 | ||
|
|
899e99a025 | ||
|
|
e0774e4730 | ||
|
|
60f783d9ce | ||
|
|
6b46d65597 | ||
|
|
fb1588c5ff | ||
|
|
dea5a10f71 | ||
|
|
11dafeaa75 | ||
|
|
bb99aa1149 | ||
|
|
b132992978 | ||
|
|
78a1fda7c8 | ||
|
|
8d02083f23 | ||
|
|
b1c36b7088 | ||
|
|
b2d4142fb7 | ||
|
|
1aae5e70ef | ||
|
|
a2a34133d8 | ||
|
|
79aa54d0c7 | ||
|
|
3ad5fd1879 | ||
|
|
4c3725723f | ||
|
|
c20a4d18e3 | ||
|
|
01a9fbbcca | ||
|
|
8d4ca9edc6 | ||
|
|
d793a56121 | ||
|
|
f424efe97f | ||
|
|
9f7ff621bd | ||
|
|
b0be721dd9 | ||
|
|
8ca8ac379d | ||
|
|
589ce09564 | ||
|
|
6c42c142e2 | ||
|
|
abc06a2f76 | ||
|
|
b0c3d451a2 | ||
|
|
7e86f9b4d2 | ||
|
|
abe97736b8 | ||
|
|
7e0f60d2ed | ||
|
|
3afa5385c9 | ||
|
|
c545331f9e | ||
|
|
cd0ecff00b | ||
|
|
a0c761dc6b | ||
|
|
b5cf2b4b82 | ||
|
|
8378a44ff9 | ||
|
|
4f0c8ef9f1 | ||
|
|
bc302f2aec | ||
|
|
08d94d3f7e | ||
|
|
10ec012f58 | ||
|
|
92fd824480 | ||
|
|
6d916329b8 | ||
|
|
84465a2cc1 | ||
|
|
0fee359278 | ||
|
|
e0eb8e9c65 | ||
|
|
2826324e56 | ||
|
|
1681ede605 | ||
|
|
44db47c63e | ||
|
|
25139ac92c | ||
|
|
0e52a609fd | ||
|
|
df78de2bc8 | ||
|
|
36b91e34f4 | ||
|
|
bdfddb09d7 | ||
|
|
fdfc8e3b9c | ||
|
|
f5ed5c317e | ||
|
|
96183e0f28 | ||
|
|
f2e32e4fd7 | ||
|
|
8beb84fa33 | ||
|
|
a448e74661 | ||
|
|
fb526a0470 | ||
|
|
96c19ff7cc | ||
|
|
9b78af95be | ||
|
|
5a63df211d | ||
|
|
1851596db5 | ||
|
|
0665182950 | ||
|
|
515917f5d8 | ||
|
|
bc52d81306 | ||
|
|
dbbf2fd19c | ||
|
|
9476d628a3 | ||
|
|
7240e8af6a | ||
|
|
f2990620d7 | ||
|
|
17f2fb44c0 | ||
|
|
8c049c6b20 | ||
|
|
761c9cd5db | ||
|
|
a159fd0b8c | ||
|
|
e8b84ab1b4 | ||
|
|
383ea02e38 | ||
|
|
597fb1da94 | ||
|
|
c6e33d30c1 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,3 @@
|
||||
*.gem
|
||||
pkg
|
||||
.bundle
|
||||
debug.log
|
||||
|
||||
4
Rakefile
4
Rakefile
@@ -1,5 +1,5 @@
|
||||
require 'rake'
|
||||
require 'rake/rdoctask'
|
||||
require 'rdoc/task'
|
||||
|
||||
env = %(PKG_BUILD="#{ENV['PKG_BUILD']}") if ENV['PKG_BUILD']
|
||||
|
||||
@@ -23,7 +23,7 @@ end
|
||||
|
||||
|
||||
desc "Generate documentation for the Rails framework"
|
||||
Rake::RDocTask.new do |rdoc|
|
||||
RDoc::Task.new do |rdoc|
|
||||
rdoc.rdoc_dir = 'doc/rdoc'
|
||||
rdoc.title = "Ruby on Rails Documentation"
|
||||
rdoc.main = "railties/README"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
*2.3.11 (February 9, 2011)*
|
||||
*2.3.10 (October 15, 2010)*
|
||||
*2.3.9 (September 4, 2010)*
|
||||
*2.3.8 (May 24, 2010)*
|
||||
*2.3.7 (May 24, 2010)*
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
require 'rubygems'
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
require 'rdoc/task'
|
||||
require 'rake/packagetask'
|
||||
require 'rake/gempackagetask'
|
||||
require 'rubygems/package_task'
|
||||
require File.join(File.dirname(__FILE__), 'lib', 'action_mailer', 'version')
|
||||
|
||||
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
||||
@@ -29,7 +29,7 @@ Rake::TestTask.new { |t|
|
||||
|
||||
|
||||
# Generate the RDoc documentation
|
||||
Rake::RDocTask.new { |rdoc|
|
||||
RDoc::Task.new { |rdoc|
|
||||
rdoc.rdoc_dir = 'doc'
|
||||
rdoc.title = "Action Mailer -- Easy email delivery and testing"
|
||||
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
||||
@@ -54,19 +54,17 @@ spec = Gem::Specification.new do |s|
|
||||
s.rubyforge_project = "actionmailer"
|
||||
s.homepage = "http://www.rubyonrails.org"
|
||||
|
||||
s.add_dependency('actionpack', '= 2.3.9' + PKG_BUILD)
|
||||
s.add_dependency('actionpack', '= 2.3.14' + PKG_BUILD)
|
||||
|
||||
s.has_rdoc = true
|
||||
s.requirements << 'none'
|
||||
s.require_path = 'lib'
|
||||
s.autorequire = 'action_mailer'
|
||||
|
||||
s.files = [ "Rakefile", "install.rb", "README", "CHANGELOG", "MIT-LICENSE" ]
|
||||
s.files = s.files + Dir.glob( "lib/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
|
||||
s.files = s.files + Dir.glob( "test/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
|
||||
end
|
||||
|
||||
Rake::GemPackageTask.new(spec) do |p|
|
||||
Gem::PackageTask.new(spec) do |p|
|
||||
p.gem_spec = spec
|
||||
p.need_tar = true
|
||||
p.need_zip = true
|
||||
|
||||
@@ -195,6 +195,39 @@ module ActionMailer #:nodoc:
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# = Multipart Emails with Attachments
|
||||
#
|
||||
# Multipart emails that also have attachments can be created by nesting a "multipart/alternative" part
|
||||
# within an email that has its content type set to "multipart/mixed". This would also need two templates
|
||||
# in place within +app/views/mailer+ called "welcome_email.text.html.erb" and "welcome_email.text.plain.erb"
|
||||
#
|
||||
# class ApplicationMailer < ActionMailer::Base
|
||||
# def signup_notification(recipient)
|
||||
# recipients recipient.email_address_with_name
|
||||
# subject "New account information"
|
||||
# from "system@example.com"
|
||||
# content_type "multipart/mixed"
|
||||
#
|
||||
# part "multipart/alternative" do |alternative|
|
||||
#
|
||||
# alternative.part "text/html" do |html|
|
||||
# html.body = render_message("welcome_email.text.html", :message => "<h1>HTML content</h1>")
|
||||
# end
|
||||
#
|
||||
# alternative.part "text/plain" do |plain|
|
||||
# plain.body = render_message("welcome_email.text.plain", :message => "text content")
|
||||
# end
|
||||
#
|
||||
# end
|
||||
#
|
||||
# attachment :content_type => "image/png",
|
||||
# :body => File.read(File.join(RAILS_ROOT, 'public/images/rails.png'))
|
||||
#
|
||||
# attachment "application/pdf" do |a|
|
||||
# a.body = File.read('/Users/mikel/Code/mail/spec/fixtures/attachments/test.pdf')
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# = Configuration options
|
||||
#
|
||||
|
||||
@@ -2,7 +2,7 @@ module ActionMailer
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 3
|
||||
TINY = 9
|
||||
TINY = 14
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
*2.3.11 (February 9, 2011)*
|
||||
|
||||
* Two security fixes. CVE-2011-0446, CVE-2011-0447
|
||||
|
||||
*2.3.10 (October 15, 2010)*
|
||||
|
||||
*2.3.9 (September 4, 2010)*
|
||||
|
||||
* Version bump.
|
||||
@@ -1929,7 +1935,7 @@ superclass' view_paths. [Rick Olson]
|
||||
|
||||
* Update documentation for erb trim syntax. #5651 [matt@mattmargolis.net]
|
||||
|
||||
* Pass :id => nil or :class => nil to error_messages_for to supress that html attribute. #3586 [olivier_ansaldi@yahoo.com, sebastien@goetzilla.info]
|
||||
* Pass :id => nil or :class => nil to error_messages_for to supress that html attribute. #3586 [olivier_ansaldi@yahoo.com]
|
||||
|
||||
* Reset @html_document between requests so assert_tag works. #4810 [Jarkko Laine, easleydp@gmail.com]
|
||||
|
||||
@@ -2526,7 +2532,7 @@ superclass' view_paths. [Rick Olson]
|
||||
|
||||
* Provide support for decimal columns to form helpers. Closes #5672. [Dave Thomas]
|
||||
|
||||
* Pass :id => nil or :class => nil to error_messages_for to supress that html attribute. #3586 [olivier_ansaldi@yahoo.com, sebastien@goetzilla.info]
|
||||
* Pass :id => nil or :class => nil to error_messages_for to supress that html attribute. #3586 [olivier_ansaldi@yahoo.com]
|
||||
|
||||
* Reset @html_document between requests so assert_tag works. #4810 [Jarkko Laine, easleydp@gmail.com]
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
require 'rubygems'
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
require 'rdoc/task'
|
||||
require 'rake/packagetask'
|
||||
require 'rake/gempackagetask'
|
||||
require 'rubygems/package_task'
|
||||
require File.join(File.dirname(__FILE__), 'lib', 'action_pack', 'version')
|
||||
|
||||
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
||||
@@ -45,7 +45,7 @@ end
|
||||
|
||||
# Genereate the RDoc documentation
|
||||
|
||||
Rake::RDocTask.new { |rdoc|
|
||||
RDoc::Task.new { |rdoc|
|
||||
rdoc.rdoc_dir = 'doc'
|
||||
rdoc.title = "Action Pack -- On rails from request to response"
|
||||
rdoc.options << '--line-numbers' << '--inline-source'
|
||||
@@ -76,14 +76,12 @@ spec = Gem::Specification.new do |s|
|
||||
s.rubyforge_project = "actionpack"
|
||||
s.homepage = "http://www.rubyonrails.org"
|
||||
|
||||
s.has_rdoc = true
|
||||
s.requirements << 'none'
|
||||
|
||||
s.add_dependency('activesupport', '= 2.3.9' + PKG_BUILD)
|
||||
s.add_dependency('rack', '~> 1.1.0')
|
||||
s.add_dependency('activesupport', '= 2.3.14' + PKG_BUILD)
|
||||
s.add_dependency('rack', '~> 1.1')
|
||||
|
||||
s.require_path = 'lib'
|
||||
s.autorequire = 'action_controller'
|
||||
|
||||
s.files = [ "Rakefile", "install.rb", "README", "RUNNING_UNIT_TESTS", "CHANGELOG", "MIT-LICENSE" ]
|
||||
dist_dirs.each do |dir|
|
||||
@@ -91,7 +89,7 @@ spec = Gem::Specification.new do |s|
|
||||
end
|
||||
end
|
||||
|
||||
Rake::GemPackageTask.new(spec) do |p|
|
||||
Gem::PackageTask.new(spec) do |p|
|
||||
p.gem_spec = spec
|
||||
p.need_tar = true
|
||||
p.need_zip = true
|
||||
|
||||
@@ -31,7 +31,7 @@ rescue LoadError
|
||||
end
|
||||
end
|
||||
|
||||
gem 'rack', '~> 1.1.0'
|
||||
gem 'rack', '~> 1.1'
|
||||
require 'rack'
|
||||
require 'action_controller/cgi_ext'
|
||||
|
||||
|
||||
@@ -1088,6 +1088,9 @@ module ActionController #:nodoc:
|
||||
# redirect_to post_url(@post), :status => 301
|
||||
# redirect_to :action=>'atom', :status => 302
|
||||
#
|
||||
# The status code can either be a standard {HTTP Status code}[http://www.iana.org/assignments/http-status-codes] as an
|
||||
# integer, or a symbol representing the downcased, underscored and symbolized description.
|
||||
#
|
||||
# It is also possible to assign a flash message as part of the redirection. There are two special accessors for commonly used the flash names
|
||||
# +alert+ and +notice+ as well as a general purpose +flash+ bucket.
|
||||
#
|
||||
@@ -1097,8 +1100,7 @@ module ActionController #:nodoc:
|
||||
# redirect_to post_url(@post), :status => 301, :flash => { :updated_post_id => @post.id }
|
||||
# redirect_to { :action=>'atom' }, :alert => "Something serious happened"
|
||||
#
|
||||
# When using <tt>redirect_to :back</tt>, if there is no referrer,
|
||||
# RedirectBackError will be raised. You may specify some fallback
|
||||
# When using <tt>redirect_to :back</tt>, if there is no referrer, 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?
|
||||
|
||||
@@ -60,7 +60,7 @@ module ActionController #:nodoc:
|
||||
attr_reader :controller
|
||||
|
||||
def initialize(controller)
|
||||
@controller, @cookies = controller, controller.request.cookies
|
||||
@controller, @cookies, @secure = controller, controller.request.cookies, controller.request.ssl?
|
||||
super()
|
||||
update(@cookies)
|
||||
end
|
||||
@@ -81,7 +81,7 @@ module ActionController #:nodoc:
|
||||
|
||||
options[:path] = "/" unless options.has_key?(:path)
|
||||
super(key.to_s, options[:value])
|
||||
@controller.response.set_cookie(key, options)
|
||||
@controller.response.set_cookie(key, options) if write_cookie?(options)
|
||||
end
|
||||
|
||||
# Removes the cookie on the client machine by setting the value to an empty string
|
||||
@@ -126,6 +126,12 @@ module ActionController #:nodoc:
|
||||
def signed
|
||||
@signed ||= SignedCookieJar.new(self)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def write_cookie?(cookie)
|
||||
@secure || !cookie[:secure] || defined?(Rails.env) && Rails.env.development?
|
||||
end
|
||||
end
|
||||
|
||||
class PermanentCookieJar < CookieJar #:nodoc:
|
||||
|
||||
@@ -287,7 +287,6 @@ module ActionController
|
||||
"REMOTE_ADDR" => remote_addr,
|
||||
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
|
||||
"CONTENT_LENGTH" => data ? data.length.to_s : nil,
|
||||
"HTTP_COOKIE" => encode_cookies,
|
||||
"HTTP_ACCEPT" => accept,
|
||||
|
||||
"rack.version" => [0,1],
|
||||
@@ -298,6 +297,8 @@ module ActionController
|
||||
"rack.run_once" => false
|
||||
)
|
||||
|
||||
env['HTTP_COOKIE'] = encode_cookies if cookies.any?
|
||||
|
||||
(headers || {}).each do |key, value|
|
||||
key = key.to_s.upcase.gsub(/-/, "_")
|
||||
key = "HTTP_#{key}" unless env.has_key?(key) || key =~ /^HTTP_/
|
||||
@@ -535,7 +536,7 @@ EOF
|
||||
if self.class.respond_to?(:fixture_table_names)
|
||||
self.class.fixture_table_names.each do |table_name|
|
||||
name = table_name.tr(".", "_")
|
||||
next unless respond_to?(name)
|
||||
next unless respond_to?(name, true)
|
||||
extras.__send__(:define_method, name) { |*args|
|
||||
delegate.send(name, *args)
|
||||
}
|
||||
|
||||
@@ -446,7 +446,9 @@ EOM
|
||||
end
|
||||
|
||||
def reset_session
|
||||
session.destroy if session
|
||||
# session may be a hash, if so, we do not want to call destroy
|
||||
# fixes issue 6440
|
||||
session.destroy if session and session.respond_to?(:destroy)
|
||||
self.session = {}
|
||||
end
|
||||
|
||||
|
||||
@@ -76,7 +76,11 @@ module ActionController #:nodoc:
|
||||
protected
|
||||
# The actual before_filter that is used. Modify this to change how you handle unverified requests.
|
||||
def verify_authenticity_token
|
||||
verified_request? || raise(ActionController::InvalidAuthenticityToken)
|
||||
verified_request? || handle_unverified_request
|
||||
end
|
||||
|
||||
def handle_unverified_request
|
||||
reset_session
|
||||
end
|
||||
|
||||
# Returns true or false if a request is verified. Checks:
|
||||
@@ -85,11 +89,10 @@ module ActionController #:nodoc:
|
||||
# * is it a GET request? Gets should be safe and idempotent
|
||||
# * Does the form_authenticity_token match the given token value from the params?
|
||||
def verified_request?
|
||||
!protect_against_forgery? ||
|
||||
request.method == :get ||
|
||||
request.xhr? ||
|
||||
!verifiable_request_format? ||
|
||||
form_authenticity_token == form_authenticity_param
|
||||
!protect_against_forgery? ||
|
||||
request.get? ||
|
||||
form_authenticity_token == form_authenticity_param ||
|
||||
form_authenticity_token == request.headers['X-CSRF-Token']
|
||||
end
|
||||
|
||||
def form_authenticity_param
|
||||
|
||||
@@ -64,12 +64,13 @@ module ActionController # :nodoc:
|
||||
# the character set information will also be included in the content type
|
||||
# information.
|
||||
def content_type=(mime_type)
|
||||
self.headers["Content-Type"] =
|
||||
new_content_type =
|
||||
if mime_type =~ /charset/ || (c = charset).nil?
|
||||
mime_type.to_s
|
||||
else
|
||||
"#{mime_type}; charset=#{c}"
|
||||
end
|
||||
self.headers["Content-Type"] = URI.escape(new_content_type, "\r\n")
|
||||
end
|
||||
|
||||
# Returns the response's content MIME type, or nil if content type has been set.
|
||||
|
||||
@@ -180,6 +180,10 @@ module ActionController
|
||||
options = env[ENV_SESSION_OPTIONS_KEY]
|
||||
|
||||
if !session_data.is_a?(AbstractStore::SessionHash) || session_data.loaded? || options[:expire_after]
|
||||
request = ActionController::Request.new(env)
|
||||
|
||||
return response if (options[:secure] && !request.ssl?)
|
||||
|
||||
session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.loaded?
|
||||
|
||||
sid = options[:id] || generate_sid
|
||||
@@ -188,23 +192,12 @@ module ActionController
|
||||
return response
|
||||
end
|
||||
|
||||
if (env["rack.request.cookie_hash"] && env["rack.request.cookie_hash"][@key] != sid) || options[:expire_after]
|
||||
cookie = Rack::Utils.escape(@key) + '=' + Rack::Utils.escape(sid)
|
||||
cookie << "; domain=#{options[:domain]}" if options[:domain]
|
||||
cookie << "; path=#{options[:path]}" if options[:path]
|
||||
if options[:expire_after]
|
||||
expiry = Time.now + options[:expire_after]
|
||||
cookie << "; expires=#{expiry.httpdate}"
|
||||
end
|
||||
cookie << "; Secure" if options[:secure]
|
||||
cookie << "; HttpOnly" if options[:httponly]
|
||||
request_cookies = env["rack.request.cookie_hash"]
|
||||
|
||||
headers = response[1]
|
||||
unless headers[SET_COOKIE].blank?
|
||||
headers[SET_COOKIE] << "\n#{cookie}"
|
||||
else
|
||||
headers[SET_COOKIE] = cookie
|
||||
end
|
||||
if (request_cookies.nil? || request_cookies[@key] != sid) || options[:expire_after]
|
||||
cookie = {:value => sid}
|
||||
cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
|
||||
Rack::Utils.set_cookie_header!(response[1], @key, cookie.merge(options))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -52,7 +52,6 @@ module ActionController
|
||||
|
||||
ENV_SESSION_KEY = "rack.session".freeze
|
||||
ENV_SESSION_OPTIONS_KEY = "rack.session.options".freeze
|
||||
HTTP_SET_COOKIE = "Set-Cookie".freeze
|
||||
|
||||
# Raised when storing more than 4K of session data.
|
||||
class CookieOverflow < StandardError; end
|
||||
@@ -101,8 +100,9 @@ module ActionController
|
||||
|
||||
session_data = env[ENV_SESSION_KEY]
|
||||
options = env[ENV_SESSION_OPTIONS_KEY]
|
||||
|
||||
if !session_data.is_a?(AbstractStore::SessionHash) || session_data.loaded? || options[:expire_after]
|
||||
request = ActionController::Request.new(env)
|
||||
|
||||
if !(options[:secure] && !request.ssl?) && (!session_data.is_a?(AbstractStore::SessionHash) || session_data.loaded? || options[:expire_after])
|
||||
session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.loaded?
|
||||
|
||||
persistent_session_id!(session_data)
|
||||
@@ -115,9 +115,7 @@ module ActionController
|
||||
cookie[:expires] = Time.now + options[:expire_after]
|
||||
end
|
||||
|
||||
cookie = build_cookie(@key, cookie.merge(options))
|
||||
headers[HTTP_SET_COOKIE] = [] if headers[HTTP_SET_COOKIE].blank?
|
||||
headers[HTTP_SET_COOKIE] << cookie
|
||||
Rack::Utils.set_cookie_header!(headers, @key, cookie.merge(options))
|
||||
end
|
||||
|
||||
[status, headers, body]
|
||||
@@ -129,26 +127,6 @@ module ActionController
|
||||
env[ENV_SESSION_KEY] = AbstractStore::SessionHash.new(self, env)
|
||||
env[ENV_SESSION_OPTIONS_KEY] = AbstractStore::OptionsHash.new(self, env, @default_options)
|
||||
end
|
||||
|
||||
# Should be in Rack::Utils soon
|
||||
def build_cookie(key, value)
|
||||
case value
|
||||
when Hash
|
||||
domain = "; domain=" + value[:domain] if value[:domain]
|
||||
path = "; path=" + value[:path] if value[:path]
|
||||
# According to RFC 2109, we need dashes here.
|
||||
# N.B.: cgi.rb uses spaces...
|
||||
expires = "; expires=" + value[:expires].clone.gmtime.
|
||||
strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
|
||||
secure = "; secure" if value[:secure]
|
||||
httponly = "; HttpOnly" if value[:httponly]
|
||||
value = value[:value]
|
||||
end
|
||||
value = [value] unless Array === value
|
||||
cookie = Rack::Utils.escape(key) + "=" +
|
||||
value.map { |v| Rack::Utils.escape(v) }.join("&") +
|
||||
"#{domain}#{path}#{expires}#{secure}#{httponly}"
|
||||
end
|
||||
|
||||
def load_session(env)
|
||||
data = unpacked_cookie_data(env)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
begin
|
||||
require_library_or_gem 'memcache'
|
||||
|
||||
require 'thread'
|
||||
module ActionController
|
||||
module Session
|
||||
class MemCacheStore < AbstractStore
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require 'uri'
|
||||
|
||||
module ActionController
|
||||
# In <b>routes.rb</b> one defines URL-to-controller mappings, but the reverse
|
||||
# is also possible: an URL can be generated from one of your routing definitions.
|
||||
|
||||
@@ -162,7 +162,7 @@ module HTML #:nodoc:
|
||||
end
|
||||
|
||||
closing = ( scanner.scan(/\//) ? :close : nil )
|
||||
return Text.new(parent, line, pos, content) unless name = scanner.scan(/[-:\w\x00-\x09\x0b-\x0c\x0e-\x1f]+/)
|
||||
return Text.new(parent, line, pos, content) unless name = scanner.scan(/[^\s!>\/]+/)
|
||||
name.downcase!
|
||||
|
||||
unless closing
|
||||
|
||||
@@ -2,7 +2,7 @@ module ActionPack #:nodoc:
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 3
|
||||
TINY = 9
|
||||
TINY = 14
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
||||
@@ -6,6 +6,7 @@ module ActionView #:nodoc:
|
||||
autoload :BenchmarkHelper, 'action_view/helpers/benchmark_helper'
|
||||
autoload :CacheHelper, 'action_view/helpers/cache_helper'
|
||||
autoload :CaptureHelper, 'action_view/helpers/capture_helper'
|
||||
autoload :CsrfHelper, 'action_view/helpers/csrf_helper'
|
||||
autoload :DateHelper, 'action_view/helpers/date_helper'
|
||||
autoload :DebugHelper, 'action_view/helpers/debug_helper'
|
||||
autoload :FormHelper, 'action_view/helpers/form_helper'
|
||||
@@ -38,6 +39,7 @@ module ActionView #:nodoc:
|
||||
include BenchmarkHelper
|
||||
include CacheHelper
|
||||
include CaptureHelper
|
||||
include CsrfHelper
|
||||
include DateHelper
|
||||
include DebugHelper
|
||||
include FormHelper
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
require 'cgi'
|
||||
require 'action_view/helpers/url_helper'
|
||||
require 'action_view/helpers/tag_helper'
|
||||
require 'thread'
|
||||
|
||||
module ActionView
|
||||
module Helpers #:nodoc:
|
||||
|
||||
14
actionpack/lib/action_view/helpers/csrf_helper.rb
Normal file
14
actionpack/lib/action_view/helpers/csrf_helper.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
module ActionView
|
||||
# = Action View CSRF Helper
|
||||
module Helpers
|
||||
module CsrfHelper
|
||||
# Returns a meta tag with the cross-site request forgery protection token
|
||||
# for forms to use. Place this in your head.
|
||||
def csrf_meta_tag
|
||||
if protect_against_forgery?
|
||||
%(<meta name="csrf-param" content="#{h(request_forgery_protection_token)}"/>\n<meta name="csrf-token" content="#{h(form_authenticity_token)}"/>).html_safe
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -665,7 +665,7 @@ module ActionView
|
||||
#
|
||||
# The HTML specification says unchecked check boxes are not successful, and
|
||||
# thus web browsers do not send them. Unfortunately this introduces a gotcha:
|
||||
# if an Invoice model has a +paid+ flag, and in the form that edits a paid
|
||||
# if an +Invoice+ model has a +paid+ flag, and in the form that edits a paid
|
||||
# invoice the user unchecks its check box, no +paid+ parameter is sent. So,
|
||||
# any mass-assignment idiom like
|
||||
#
|
||||
@@ -673,12 +673,15 @@ module ActionView
|
||||
#
|
||||
# wouldn't update the flag.
|
||||
#
|
||||
# To prevent this the helper generates a hidden field with the same name as
|
||||
# the checkbox after the very check box. So, the client either sends only the
|
||||
# hidden field (representing the check box is unchecked), or both fields.
|
||||
# Since the HTML specification says key/value pairs have to be sent in the
|
||||
# same order they appear in the form and Rails parameters extraction always
|
||||
# gets the first occurrence of any given key, that works in ordinary forms.
|
||||
# To prevent this the helper generates an auxiliary hidden field before
|
||||
# the very check box. The hidden field has the same name and its
|
||||
# attributes mimick an unchecked check box.
|
||||
#
|
||||
# This way, the client either sends only the hidden field (representing
|
||||
# the check box is unchecked), or both fields. Since the HTML specification
|
||||
# says key/value pairs have to be sent in the same order they appear in the
|
||||
# form, and parameters extraction gets the last occurrence of any repeated
|
||||
# key in the query string, that works for ordinary forms.
|
||||
#
|
||||
# Unfortunately that workaround does not work when the check box goes
|
||||
# within an array-like parameter, as in
|
||||
@@ -689,22 +692,26 @@ module ActionView
|
||||
# <% end %>
|
||||
#
|
||||
# because parameter name repetition is precisely what Rails seeks to distinguish
|
||||
# the elements of the array.
|
||||
# the elements of the array. For each item with a checked check box you
|
||||
# get an extra ghost item with only that attribute, assigned to "0".
|
||||
#
|
||||
# In that case it is preferable to either use +check_box_tag+ or to use
|
||||
# hashes instead of arrays.
|
||||
#
|
||||
# ==== Examples
|
||||
# # Let's say that @post.validated? is 1:
|
||||
# check_box("post", "validated")
|
||||
# # => <input type="checkbox" id="post_validated" name="post[validated]" value="1" />
|
||||
# # <input name="post[validated]" type="hidden" value="0" />
|
||||
# # => <input name="post[validated]" type="hidden" value="0" />
|
||||
# # <input type="checkbox" id="post_validated" name="post[validated]" value="1" />
|
||||
#
|
||||
# # Let's say that @puppy.gooddog is "no":
|
||||
# check_box("puppy", "gooddog", {}, "yes", "no")
|
||||
# # => <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
|
||||
# # <input name="puppy[gooddog]" type="hidden" value="no" />
|
||||
# # => <input name="puppy[gooddog]" type="hidden" value="no" />
|
||||
# # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
|
||||
#
|
||||
# check_box("eula", "accepted", { :class => 'eula_check' }, "yes", "no")
|
||||
# # => <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
|
||||
# # <input name="eula[accepted]" type="hidden" value="no" />
|
||||
# # => <input name="eula[accepted]" type="hidden" value="no" />
|
||||
# # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
|
||||
#
|
||||
def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
|
||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_check_box_tag(options, checked_value, unchecked_value)
|
||||
@@ -877,9 +884,9 @@ module ActionView
|
||||
|
||||
def value_before_type_cast(object, method_name)
|
||||
unless object.nil?
|
||||
object.respond_to?(method_name) ?
|
||||
object.send(method_name) :
|
||||
object.send(method_name + "_before_type_cast")
|
||||
object.respond_to?(method_name + "_before_type_cast") ?
|
||||
object.send(method_name + "_before_type_cast") :
|
||||
object.send(method_name)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -471,7 +471,8 @@ module ActionView
|
||||
email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.has_key?("replace_dot")
|
||||
|
||||
if encode == "javascript"
|
||||
"document.write('#{content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');".each_byte do |c|
|
||||
html = content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge({ "href" => "mailto:"+html_escape(email_address)+extras }))
|
||||
"document.write('#{escape_javascript(html)}');".each_byte do |c|
|
||||
string << sprintf("%%%x", c)
|
||||
end
|
||||
"<script type=\"#{Mime::JS}\">eval(decodeURIComponent('#{string}'))</script>"
|
||||
|
||||
@@ -27,7 +27,7 @@ module ActionView
|
||||
def render_partial(view, object = nil, local_assigns = {}, as = nil)
|
||||
object ||= local_assigns[:object] || local_assigns[variable_name]
|
||||
|
||||
if object.nil? && view.respond_to?(:controller)
|
||||
if object.nil? && !local_assigns_key?(local_assigns) && view.respond_to?(:controller)
|
||||
ivar = :"@#{variable_name}"
|
||||
object =
|
||||
if view.controller.instance_variable_defined?(ivar)
|
||||
@@ -43,5 +43,11 @@ module ActionView
|
||||
|
||||
render_template(view, local_assigns)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def local_assigns_key?(local_assigns)
|
||||
local_assigns.key?(:object) || local_assigns.key?(variable_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -46,6 +46,11 @@ class ContentTypeController < ActionController::Base
|
||||
format.rss { render :text => "hello world!", :content_type => Mime::XML }
|
||||
end
|
||||
end
|
||||
|
||||
def render_content_type_from_user_input
|
||||
response.content_type= params[:hello]
|
||||
render :text=>"hello"
|
||||
end
|
||||
|
||||
def rescue_action(e) raise end
|
||||
end
|
||||
@@ -129,6 +134,11 @@ class ContentTypeTest < ActionController::TestCase
|
||||
assert_equal Mime::HTML, @response.content_type
|
||||
assert_equal "utf-8", @response.charset
|
||||
end
|
||||
|
||||
def test_user_supplied_value
|
||||
get :render_content_type_from_user_input, :hello=>"hello/world\r\nAttack: true"
|
||||
assert_equal "hello/world%0D%0AAttack: true", @response.content_type
|
||||
end
|
||||
end
|
||||
|
||||
class AcceptBasedContentTypeTest < ActionController::TestCase
|
||||
|
||||
@@ -42,6 +42,10 @@ class CookieTest < ActionController::TestCase
|
||||
cookies["user_name"] = { :value => "david", :httponly => true }
|
||||
end
|
||||
|
||||
def authenticate_with_secure
|
||||
cookies["user_name"] = { :value => "david", :secure => true }
|
||||
end
|
||||
|
||||
def set_permanent_cookie
|
||||
cookies.permanent[:user_name] = "Jamie"
|
||||
end
|
||||
@@ -94,6 +98,27 @@ class CookieTest < ActionController::TestCase
|
||||
assert_equal ["user_name=david; path=/; HttpOnly"], @response.headers["Set-Cookie"]
|
||||
assert_equal({"user_name" => "david"}, @response.cookies)
|
||||
end
|
||||
|
||||
def test_setting_cookie_with_secure
|
||||
@request.env["HTTPS"] = "on"
|
||||
get :authenticate_with_secure
|
||||
assert_equal ["user_name=david; path=/; secure"], @response.headers["Set-Cookie"]
|
||||
assert_equal({"user_name" => "david"}, @response.cookies)
|
||||
end
|
||||
|
||||
def test_setting_cookie_with_secure_in_development
|
||||
with_environment(:development) do
|
||||
get :authenticate_with_secure
|
||||
assert_equal ["user_name=david; path=/; secure"], @response.headers["Set-Cookie"]
|
||||
assert_equal({"user_name" => "david"}, @response.cookies)
|
||||
end
|
||||
end
|
||||
|
||||
def test_not_setting_cookie_with_secure
|
||||
get :authenticate_with_secure
|
||||
assert_not_equal ["user_name=david; path=/; secure"], @response.headers["Set-Cookie"]
|
||||
assert_not_equal({"user_name" => "david"}, @response.cookies)
|
||||
end
|
||||
|
||||
def test_multiple_cookies
|
||||
get :set_multiple_cookies
|
||||
@@ -167,4 +192,17 @@ class CookieTest < ActionController::TestCase
|
||||
assert_match %r(#{20.years.from_now.year}), @response.headers["Set-Cookie"].first
|
||||
assert_equal 100, @controller.send(:cookies).signed[:remember_me]
|
||||
end
|
||||
|
||||
private
|
||||
def with_environment(enviroment)
|
||||
old_rails = Object.const_get(:Rails) rescue nil
|
||||
mod = Object.const_set(:Rails, Module.new)
|
||||
(class << mod; self; end).instance_eval do
|
||||
define_method(:env) { @_env ||= ActiveSupport::StringInquirer.new(enviroment.to_s) }
|
||||
end
|
||||
yield
|
||||
ensure
|
||||
Object.module_eval { remove_const(:Rails) } if defined?(Rails)
|
||||
Object.const_set(:Rails, old_rails) if old_rails
|
||||
end
|
||||
end
|
||||
@@ -5,6 +5,13 @@ class SanitizerTest < ActionController::TestCase
|
||||
@sanitizer = nil # used by assert_sanitizer
|
||||
end
|
||||
|
||||
def test_strip_tags_with_quote
|
||||
sanitizer = HTML::FullSanitizer.new
|
||||
string = '<" <img src="trollface.gif" onload="alert(1)"> hi'
|
||||
|
||||
assert_equal ' hi', sanitizer.sanitize(string)
|
||||
end
|
||||
|
||||
def test_strip_tags
|
||||
sanitizer = HTML::FullSanitizer.new
|
||||
assert_equal("<<<bad html", sanitizer.sanitize("<<<bad html"))
|
||||
|
||||
@@ -227,6 +227,24 @@ class IntegrationTestTest < Test::Unit::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
require 'active_record_unit'
|
||||
# Tests that fixtures are accessible in the integration test sessions
|
||||
class IntegrationTestWithFixtures < ActiveRecordTestCase
|
||||
include ActionController::Integration::Runner
|
||||
|
||||
fixtures :companies
|
||||
|
||||
def test_fixtures_in_new_session
|
||||
sym = :thirty_seven_signals
|
||||
# fixtures are accessible in main session
|
||||
assert_not_nil companies(sym)
|
||||
|
||||
# create a new session and the fixtures should be accessible in it as well
|
||||
session1 = open_session { |sess| }
|
||||
assert_not_nil session1.companies(sym)
|
||||
end
|
||||
end
|
||||
|
||||
# Tests that integration tests don't call Controller test methods for processing.
|
||||
# Integration tests have their own setup and teardown.
|
||||
class IntegrationTestUsesCorrectClass < ActionController::IntegrationTest
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
require 'abstract_unit'
|
||||
require 'thread'
|
||||
|
||||
class ReloaderTests < ActiveSupport::TestCase
|
||||
Reloader = ActionController::Reloader
|
||||
|
||||
@@ -716,6 +716,11 @@ class TestController < ActionController::Base
|
||||
render :partial => "customer"
|
||||
end
|
||||
|
||||
def partial_with_implicit_local_assignment_and_nil_local
|
||||
@customer = Customer.new("Marcel")
|
||||
render :partial => "customer", :locals => { :customer => nil }
|
||||
end
|
||||
|
||||
def render_call_to_partial_with_layout
|
||||
render :action => "calling_partial_with_layout"
|
||||
end
|
||||
@@ -1543,6 +1548,13 @@ class RenderTest < ActionController::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
def test_partial_with_implicit_local_assignment_and_nil_local
|
||||
assert_not_deprecated do
|
||||
get :partial_with_implicit_local_assignment_and_nil_local
|
||||
assert_equal "Hello: Anonymous", @response.body
|
||||
end
|
||||
end
|
||||
|
||||
def test_render_missing_partial_template
|
||||
assert_raise(ActionView::MissingTemplate) do
|
||||
get :missing_partial
|
||||
|
||||
@@ -23,6 +23,10 @@ module RequestForgeryProtectionActions
|
||||
render :text => 'pwn'
|
||||
end
|
||||
|
||||
def meta
|
||||
render :inline => "<%= csrf_meta_tag %>"
|
||||
end
|
||||
|
||||
def rescue_action(e) raise e end
|
||||
end
|
||||
|
||||
@@ -32,6 +36,16 @@ class RequestForgeryProtectionController < ActionController::Base
|
||||
protect_from_forgery :only => :index
|
||||
end
|
||||
|
||||
class RequestForgeryProtectionControllerUsingOldBehaviour < ActionController::Base
|
||||
include RequestForgeryProtectionActions
|
||||
protect_from_forgery :only => %w(index meta)
|
||||
|
||||
def handle_unverified_request
|
||||
raise(ActionController::InvalidAuthenticityToken)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class FreeCookieController < RequestForgeryProtectionController
|
||||
self.allow_forgery_protection = false
|
||||
|
||||
@@ -54,158 +68,92 @@ end
|
||||
# common test methods
|
||||
|
||||
module RequestForgeryProtectionTests
|
||||
def teardown
|
||||
ActionController::Base.request_forgery_protection_token = nil
|
||||
def setup
|
||||
@token = "cf50faa3fe97702ca1ae"
|
||||
|
||||
ActiveSupport::SecureRandom.stubs(:base64).returns(@token)
|
||||
ActionController::Base.request_forgery_protection_token = :authenticity_token
|
||||
end
|
||||
|
||||
|
||||
|
||||
def test_should_render_form_with_token_tag
|
||||
get :index
|
||||
assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
|
||||
end
|
||||
|
||||
def test_should_render_button_to_with_token_tag
|
||||
get :show_button
|
||||
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
|
||||
end
|
||||
|
||||
def test_should_allow_post_without_token_on_unsafe_action
|
||||
post :unsafe
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_not_allow_html_post_without_token
|
||||
@request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
|
||||
assert_raise(ActionController::InvalidAuthenticityToken) { post :index, :format => :html }
|
||||
end
|
||||
|
||||
def test_should_not_allow_html_put_without_token
|
||||
@request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
|
||||
assert_raise(ActionController::InvalidAuthenticityToken) { put :index, :format => :html }
|
||||
end
|
||||
|
||||
def test_should_not_allow_html_delete_without_token
|
||||
@request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
|
||||
assert_raise(ActionController::InvalidAuthenticityToken) { delete :index, :format => :html }
|
||||
end
|
||||
|
||||
def test_should_allow_api_formatted_post_without_token
|
||||
assert_nothing_raised do
|
||||
post :index, :format => 'xml'
|
||||
assert_not_blocked do
|
||||
get :index
|
||||
end
|
||||
assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
|
||||
end
|
||||
|
||||
def test_should_not_allow_api_formatted_put_without_token
|
||||
assert_nothing_raised do
|
||||
put :index, :format => 'xml'
|
||||
def test_should_render_button_to_with_token_tag
|
||||
assert_not_blocked do
|
||||
get :show_button
|
||||
end
|
||||
assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
|
||||
end
|
||||
|
||||
def test_should_allow_api_formatted_delete_without_token
|
||||
assert_nothing_raised do
|
||||
delete :index, :format => 'xml'
|
||||
end
|
||||
def test_should_allow_get
|
||||
assert_not_blocked { get :index }
|
||||
end
|
||||
|
||||
def test_should_not_allow_api_formatted_post_sent_as_url_encoded_form_without_token
|
||||
assert_raise(ActionController::InvalidAuthenticityToken) do
|
||||
@request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
|
||||
post :index, :format => 'xml'
|
||||
end
|
||||
def test_should_allow_post_without_token_on_unsafe_action
|
||||
assert_not_blocked { post :unsafe }
|
||||
end
|
||||
|
||||
def test_should_not_allow_api_formatted_put_sent_as_url_encoded_form_without_token
|
||||
assert_raise(ActionController::InvalidAuthenticityToken) do
|
||||
@request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
|
||||
put :index, :format => 'xml'
|
||||
end
|
||||
def test_should_not_allow_post_without_token
|
||||
assert_blocked { post :index }
|
||||
end
|
||||
|
||||
def test_should_not_allow_api_formatted_delete_sent_as_url_encoded_form_without_token
|
||||
assert_raise(ActionController::InvalidAuthenticityToken) do
|
||||
@request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
|
||||
delete :index, :format => 'xml'
|
||||
end
|
||||
def test_should_not_allow_post_without_token_irrespective_of_format
|
||||
assert_blocked { post :index, :format=>'xml' }
|
||||
end
|
||||
|
||||
def test_should_not_allow_api_formatted_post_sent_as_multipart_form_without_token
|
||||
assert_raise(ActionController::InvalidAuthenticityToken) do
|
||||
@request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s
|
||||
post :index, :format => 'xml'
|
||||
end
|
||||
def test_should_not_allow_put_without_token
|
||||
assert_blocked { put :index }
|
||||
end
|
||||
|
||||
def test_should_not_allow_api_formatted_put_sent_as_multipart_form_without_token
|
||||
assert_raise(ActionController::InvalidAuthenticityToken) do
|
||||
@request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s
|
||||
put :index, :format => 'xml'
|
||||
end
|
||||
def test_should_not_allow_delete_without_token
|
||||
assert_blocked { delete :index }
|
||||
end
|
||||
|
||||
def test_should_not_allow_api_formatted_delete_sent_as_multipart_form_without_token
|
||||
assert_raise(ActionController::InvalidAuthenticityToken) do
|
||||
@request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s
|
||||
delete :index, :format => 'xml'
|
||||
end
|
||||
def test_should_not_allow_xhr_post_without_token
|
||||
assert_blocked { xhr :post, :index }
|
||||
end
|
||||
|
||||
def test_should_allow_xhr_post_without_token
|
||||
assert_nothing_raised { xhr :post, :index }
|
||||
end
|
||||
|
||||
def test_should_allow_xhr_put_without_token
|
||||
assert_nothing_raised { xhr :put, :index }
|
||||
end
|
||||
|
||||
def test_should_allow_xhr_delete_without_token
|
||||
assert_nothing_raised { xhr :delete, :index }
|
||||
end
|
||||
|
||||
def test_should_allow_xhr_post_with_encoded_form_content_type_without_token
|
||||
@request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
|
||||
assert_nothing_raised { xhr :post, :index }
|
||||
end
|
||||
|
||||
|
||||
def test_should_allow_post_with_token
|
||||
post :index, :authenticity_token => @token
|
||||
assert_response :success
|
||||
assert_not_blocked { post :index, :authenticity_token => @token }
|
||||
end
|
||||
|
||||
def test_should_allow_put_with_token
|
||||
put :index, :authenticity_token => @token
|
||||
assert_response :success
|
||||
assert_not_blocked { put :index, :authenticity_token => @token }
|
||||
end
|
||||
|
||||
def test_should_allow_delete_with_token
|
||||
delete :index, :authenticity_token => @token
|
||||
assert_not_blocked { delete :index, :authenticity_token => @token }
|
||||
end
|
||||
|
||||
def test_should_allow_post_with_token_in_header
|
||||
@request.env['HTTP_X_CSRF_TOKEN'] = @token
|
||||
assert_not_blocked { post :index }
|
||||
end
|
||||
|
||||
def test_should_allow_delete_with_token_in_header
|
||||
@request.env['HTTP_X_CSRF_TOKEN'] = @token
|
||||
assert_not_blocked { delete :index }
|
||||
end
|
||||
|
||||
def test_should_allow_put_with_token_in_header
|
||||
@request.env['HTTP_X_CSRF_TOKEN'] = @token
|
||||
assert_not_blocked { put :index }
|
||||
end
|
||||
|
||||
def assert_blocked
|
||||
session[:something_like_user_id] = 1
|
||||
yield
|
||||
assert_nil session[:something_like_user_id], "session values are still present"
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_allow_post_with_xml
|
||||
@request.env['CONTENT_TYPE'] = Mime::XML.to_s
|
||||
post :index, :format => 'xml'
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_allow_put_with_xml
|
||||
@request.env['CONTENT_TYPE'] = Mime::XML.to_s
|
||||
put :index, :format => 'xml'
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_allow_delete_with_xml
|
||||
@request.env['CONTENT_TYPE'] = Mime::XML.to_s
|
||||
delete :index, :format => 'xml'
|
||||
def assert_not_blocked
|
||||
assert_nothing_raised { yield }
|
||||
assert_response :success
|
||||
end
|
||||
end
|
||||
@@ -214,15 +162,20 @@ end
|
||||
|
||||
class RequestForgeryProtectionControllerTest < ActionController::TestCase
|
||||
include RequestForgeryProtectionTests
|
||||
def setup
|
||||
@controller = RequestForgeryProtectionController.new
|
||||
@request = ActionController::TestRequest.new
|
||||
@request.format = :html
|
||||
@response = ActionController::TestResponse.new
|
||||
@token = "cf50faa3fe97702ca1ae"
|
||||
|
||||
ActiveSupport::SecureRandom.stubs(:base64).returns(@token)
|
||||
ActionController::Base.request_forgery_protection_token = :authenticity_token
|
||||
test 'should emit a csrf-token meta tag' do
|
||||
ActiveSupport::SecureRandom.stubs(:base64).returns(@token + '<=?')
|
||||
get :meta
|
||||
assert_equal %(<meta name="csrf-param" content="authenticity_token"/>\n<meta name="csrf-token" content="cf50faa3fe97702ca1ae<=?"/>), @response.body
|
||||
end
|
||||
end
|
||||
|
||||
class RequestForgeryProtectionControllerUsingOldBehaviourTest < ActionController::TestCase
|
||||
include RequestForgeryProtectionTests
|
||||
def assert_blocked
|
||||
assert_raises(ActionController::InvalidAuthenticityToken) do
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -251,15 +204,30 @@ class FreeCookieControllerTest < ActionController::TestCase
|
||||
assert_nothing_raised { send(method, :index)}
|
||||
end
|
||||
end
|
||||
|
||||
test 'should not emit a csrf-token meta tag' do
|
||||
get :meta
|
||||
assert_blank @response.body
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class CustomAuthenticityParamControllerTest < ActionController::TestCase
|
||||
def setup
|
||||
ActionController::Base.request_forgery_protection_token = :custom_token_name
|
||||
super
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActionController::Base.request_forgery_protection_token = :authenticity_token
|
||||
super
|
||||
end
|
||||
|
||||
def test_should_allow_custom_token
|
||||
post :index, :authenticity_token => 'foobar'
|
||||
post :index, :custom_token_name => 'foobar'
|
||||
assert_response :ok
|
||||
end
|
||||
end
|
||||
|
||||
64
actionpack/test/controller/session/abstract_store_test.rb
Normal file
64
actionpack/test/controller/session/abstract_store_test.rb
Normal file
@@ -0,0 +1,64 @@
|
||||
require 'abstract_unit'
|
||||
|
||||
# You need to start a memcached server inorder to run these tests
|
||||
class AbstractStoreTest < ActionController::IntegrationTest
|
||||
SessionKey = '_myapp_session'
|
||||
DispatcherApp = ActionController::Dispatcher.new
|
||||
|
||||
class TestController < ActionController::Base
|
||||
def get_session
|
||||
session[:test] = 'test'
|
||||
head :ok
|
||||
end
|
||||
end
|
||||
|
||||
def test_expiry_after
|
||||
with_test_route_set(:expire_after => 5 * 60) do
|
||||
get 'get_session'
|
||||
assert_response :success
|
||||
assert_match /expires=\S+/, headers['Set-Cookie']
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def with_test_route_set(options = {})
|
||||
with_routing do |set|
|
||||
set.draw do |map|
|
||||
map.with_options :controller => "abstract_store_test/test" do |c|
|
||||
c.connect "/:action"
|
||||
end
|
||||
end
|
||||
|
||||
options = { :key => SessionKey, :secret => 'SessionSecret' }.merge!(options)
|
||||
@integration_session = open_session(TestStore.new(DispatcherApp, options))
|
||||
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
class TestStore < ActionController::Session::AbstractStore
|
||||
def initialize(app, options = {})
|
||||
super
|
||||
@_store = Hash.new({})
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_session(env, sid)
|
||||
sid ||= generate_sid
|
||||
session = @_store[sid]
|
||||
[sid, session]
|
||||
end
|
||||
|
||||
def set_session(env, sid, session_data)
|
||||
@_store[sid] = session_data
|
||||
end
|
||||
|
||||
def destroy(env)
|
||||
@_store.delete(sid)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -6,7 +6,6 @@ class CookieStoreTest < ActionController::IntegrationTest
|
||||
SessionSecret = 'b3c631c314c0bbca50c1b2843150fe33'
|
||||
|
||||
DispatcherApp = ActionController::Dispatcher.new
|
||||
CookieStoreApp = ActionController::Session::CookieStore.new(DispatcherApp, :key => SessionKey, :secret => SessionSecret)
|
||||
|
||||
Verifier = ActiveSupport::MessageVerifier.new(SessionSecret, 'SHA1')
|
||||
|
||||
@@ -43,6 +42,12 @@ class CookieStoreTest < ActionController::IntegrationTest
|
||||
head :ok
|
||||
end
|
||||
|
||||
def call_reset_session_twice
|
||||
reset_session
|
||||
reset_session
|
||||
head :ok
|
||||
end
|
||||
|
||||
def call_reset_session
|
||||
reset_session
|
||||
head :ok
|
||||
@@ -62,10 +67,6 @@ class CookieStoreTest < ActionController::IntegrationTest
|
||||
def rescue_action(e) raise end
|
||||
end
|
||||
|
||||
def setup
|
||||
@integration_session = open_session(CookieStoreApp)
|
||||
end
|
||||
|
||||
def test_raises_argument_error_if_missing_session_key
|
||||
assert_raise(ArgumentError, nil.inspect) {
|
||||
ActionController::Session::CookieStore.new(nil,
|
||||
@@ -111,7 +112,7 @@ class CookieStoreTest < ActionController::IntegrationTest
|
||||
with_test_route_set do
|
||||
get '/set_session_value'
|
||||
assert_response :success
|
||||
assert_equal ["_myapp_session=#{response.body}; path=/; HttpOnly"],
|
||||
assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly",
|
||||
headers['Set-Cookie']
|
||||
end
|
||||
end
|
||||
@@ -152,6 +153,23 @@ class CookieStoreTest < ActionController::IntegrationTest
|
||||
end
|
||||
end
|
||||
|
||||
def test_does_not_set_secure_cookies_over_http
|
||||
with_test_route_set(:secure => true) do
|
||||
get '/set_session_value'
|
||||
assert_response :success
|
||||
assert_equal nil, headers['Set-Cookie']
|
||||
end
|
||||
end
|
||||
|
||||
def test_does_set_secure_cookies_over_https
|
||||
with_test_route_set(:secure => true) do
|
||||
get '/set_session_value', nil, 'HTTPS' => 'on'
|
||||
assert_response :success
|
||||
assert_equal "_myapp_session=#{response.body}; path=/; secure; HttpOnly",
|
||||
headers['Set-Cookie']
|
||||
end
|
||||
end
|
||||
|
||||
def test_close_raises_when_data_overflows
|
||||
with_test_route_set do
|
||||
assert_raise(ActionController::Session::CookieStore::CookieOverflow) {
|
||||
@@ -178,17 +196,55 @@ class CookieStoreTest < ActionController::IntegrationTest
|
||||
end
|
||||
end
|
||||
|
||||
def test_calling_session_reset_twice
|
||||
with_test_route_set do
|
||||
get '/set_session_value'
|
||||
assert_response :success
|
||||
session_payload = response.body
|
||||
assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly",
|
||||
headers['Set-Cookie']
|
||||
|
||||
get '/call_reset_session_twice'
|
||||
assert_response :success
|
||||
assert_not_equal "", headers['Set-Cookie']
|
||||
assert_not_equal session_payload, cookies[SessionKey]
|
||||
|
||||
get '/get_session_value'
|
||||
assert_response :success
|
||||
assert_equal 'foo: nil', response.body
|
||||
end
|
||||
end
|
||||
|
||||
def test_setting_session_value_after_session_reset
|
||||
with_test_route_set do
|
||||
get '/set_session_value'
|
||||
assert_response :success
|
||||
session_payload = response.body
|
||||
assert_equal ["_myapp_session=#{response.body}; path=/; HttpOnly"],
|
||||
assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly",
|
||||
headers['Set-Cookie']
|
||||
|
||||
get '/call_reset_session'
|
||||
assert_response :success
|
||||
assert_not_equal [], headers['Set-Cookie']
|
||||
assert_not_equal "", headers['Set-Cookie']
|
||||
assert_not_equal session_payload, cookies[SessionKey]
|
||||
|
||||
get '/get_session_value'
|
||||
assert_response :success
|
||||
assert_equal 'foo: nil', response.body
|
||||
end
|
||||
end
|
||||
|
||||
def test_setting_session_value_after_session_reset
|
||||
with_test_route_set do
|
||||
get '/set_session_value'
|
||||
assert_response :success
|
||||
session_payload = response.body
|
||||
assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly",
|
||||
headers['Set-Cookie']
|
||||
|
||||
get '/call_reset_session'
|
||||
assert_response :success
|
||||
assert_not_equal "", headers['Set-Cookie']
|
||||
assert_not_equal session_payload, cookies[SessionKey]
|
||||
|
||||
get '/get_session_value'
|
||||
@@ -202,7 +258,7 @@ class CookieStoreTest < ActionController::IntegrationTest
|
||||
get '/set_session_value'
|
||||
assert_response :success
|
||||
session_payload = response.body
|
||||
assert_equal ["_myapp_session=#{response.body}; path=/; HttpOnly"],
|
||||
assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly",
|
||||
headers['Set-Cookie']
|
||||
|
||||
get '/call_session_clear'
|
||||
@@ -272,13 +328,17 @@ class CookieStoreTest < ActionController::IntegrationTest
|
||||
end
|
||||
|
||||
private
|
||||
def with_test_route_set
|
||||
def with_test_route_set(options = {})
|
||||
with_routing do |set|
|
||||
set.draw do |map|
|
||||
map.with_options :controller => "cookie_store_test/test" do |c|
|
||||
c.connect "/:action"
|
||||
end
|
||||
end
|
||||
|
||||
options = { :key => SessionKey, :secret => SessionSecret }.merge!(options)
|
||||
@integration_session = open_session(ActionController::Session::CookieStore.new(DispatcherApp, options))
|
||||
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
@@ -37,13 +37,6 @@ class MemCacheStoreTest < ActionController::IntegrationTest
|
||||
|
||||
begin
|
||||
DispatcherApp = ActionController::Dispatcher.new
|
||||
MemCacheStoreApp = ActionController::Session::MemCacheStore.new(
|
||||
DispatcherApp, :key => '_session_id')
|
||||
|
||||
|
||||
def setup
|
||||
@integration_session = open_session(MemCacheStoreApp)
|
||||
end
|
||||
|
||||
def test_setting_and_getting_session_value
|
||||
with_test_route_set do
|
||||
@@ -177,14 +170,18 @@ class MemCacheStoreTest < ActionController::IntegrationTest
|
||||
end
|
||||
|
||||
private
|
||||
def with_test_route_set
|
||||
def with_test_route_set(options = {})
|
||||
with_routing do |set|
|
||||
set.draw do |map|
|
||||
map.with_options :controller => "mem_cache_store_test/test" do |c|
|
||||
c.connect "/:action"
|
||||
end
|
||||
end
|
||||
|
||||
options = { :key => '_session_id' }.merge!(options)
|
||||
@integration_session = open_session(ActionController::Session::MemCacheStore.new(DispatcherApp, options))
|
||||
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -91,16 +91,6 @@ end
|
||||
class FormHelperTest < ActionView::TestCase
|
||||
tests ActionView::Helpers::FormHelper
|
||||
|
||||
class Developer
|
||||
def name_before_type_cast
|
||||
"David"
|
||||
end
|
||||
|
||||
def name
|
||||
"Santiago"
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
super
|
||||
|
||||
@@ -266,13 +256,6 @@ class FormHelperTest < ActionView::TestCase
|
||||
assert_equal object_name, "post[]"
|
||||
end
|
||||
|
||||
def test_text_field_from_a_user_defined_method
|
||||
@developer = Developer.new
|
||||
assert_dom_equal(
|
||||
'<input id="developer_name" name="developer[name]" size="30" type="text" value="Santiago" />', text_field("developer", "name")
|
||||
)
|
||||
end
|
||||
|
||||
def test_hidden_field
|
||||
assert_dom_equal '<input id="post_title" name="post[title]" type="hidden" value="Hello World" />',
|
||||
hidden_field("post", "title")
|
||||
|
||||
@@ -333,11 +333,11 @@ class UrlHelperTest < ActionView::TestCase
|
||||
end
|
||||
|
||||
def test_mail_to_with_javascript
|
||||
assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript")
|
||||
assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%5c%22%3e%4d%79%20%65%6d%61%69%6c%3c%5c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript")
|
||||
end
|
||||
|
||||
def test_mail_to_with_javascript_unicode
|
||||
assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%75%6e%69%63%6f%64%65%40%65%78%61%6d%70%6c%65%2e%63%6f%6d%22%3e%c3%ba%6e%69%63%6f%64%65%3c%2f%61%3e%27%29%3b'))</script>", mail_to("unicode@example.com", "únicode", :encode => "javascript")
|
||||
assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%75%6e%69%63%6f%64%65%40%65%78%61%6d%70%6c%65%2e%63%6f%6d%5c%22%3e%c3%ba%6e%69%63%6f%64%65%3c%5c%2f%61%3e%27%29%3b'))</script>", mail_to("unicode@example.com", "únicode", :encode => "javascript")
|
||||
end
|
||||
|
||||
def test_mail_with_options
|
||||
@@ -361,8 +361,8 @@ class UrlHelperTest < ActionView::TestCase
|
||||
assert_dom_equal "<a href=\"mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">me(at)domain.com</a>", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)")
|
||||
assert_dom_equal "<a href=\"mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">My email</a>", mail_to("me@domain.com", "My email", :encode => "hex", :replace_at => "(at)")
|
||||
assert_dom_equal "<a href=\"mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">me(at)domain(dot)com</a>", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)", :replace_dot => "(dot)")
|
||||
assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
|
||||
assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%6d%65%28%61%74%29%64%6f%6d%61%69%6e%28%64%6f%74%29%63%6f%6d%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", nil, :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
|
||||
assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%5c%22%3e%4d%79%20%65%6d%61%69%6c%3c%5c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
|
||||
assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%5c%22%3e%6d%65%28%61%74%29%64%6f%6d%61%69%6e%28%64%6f%74%29%63%6f%6d%3c%5c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", nil, :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
|
||||
end
|
||||
|
||||
def protect_against_forgery?
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env ruby
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
require 'rdoc/task'
|
||||
|
||||
task :default => :test
|
||||
|
||||
@@ -13,7 +13,7 @@ Rake::TestTask.new do |t|
|
||||
end
|
||||
|
||||
# Generate the RDoc documentation
|
||||
Rake::RDocTask.new do |rdoc|
|
||||
RDoc::Task.new do |rdoc|
|
||||
rdoc.rdoc_dir = 'doc'
|
||||
rdoc.title = "Active Model"
|
||||
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
*2.3.11 (February 9, 2011)*
|
||||
|
||||
*2.3.10 (October 15, 2010)*
|
||||
|
||||
* Security Release to fix CVE-2010-3933
|
||||
|
||||
*2.3.9 (September 4, 2010)*
|
||||
*2.3.8 (May 24, 2010)*
|
||||
*2.3.7 (May 24, 2010)*
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
require 'rubygems'
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
require 'rdoc/task'
|
||||
require 'rake/packagetask'
|
||||
require 'rake/gempackagetask'
|
||||
require 'rubygems/package_task'
|
||||
|
||||
require File.join(File.dirname(__FILE__), 'lib', 'active_record', 'version')
|
||||
require File.expand_path(File.dirname(__FILE__)) + "/test/config"
|
||||
@@ -157,7 +157,7 @@ task :rebuild_frontbase_databases => 'frontbase:rebuild_databases'
|
||||
|
||||
# Generate the RDoc documentation
|
||||
|
||||
Rake::RDocTask.new { |rdoc|
|
||||
RDoc::Task.new { |rdoc|
|
||||
rdoc.rdoc_dir = 'doc'
|
||||
rdoc.title = "Active Record -- Object-relation mapping put on rails"
|
||||
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
||||
@@ -192,16 +192,14 @@ 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.3.9' + PKG_BUILD)
|
||||
s.add_dependency('activesupport', '= 2.3.14' + PKG_BUILD)
|
||||
|
||||
s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite"
|
||||
s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite"
|
||||
s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite3"
|
||||
s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite3"
|
||||
s.require_path = 'lib'
|
||||
s.autorequire = 'active_record'
|
||||
|
||||
s.has_rdoc = true
|
||||
s.extra_rdoc_files = %w( README )
|
||||
s.rdoc_options.concat ['--main', 'README']
|
||||
|
||||
@@ -211,7 +209,7 @@ spec = Gem::Specification.new do |s|
|
||||
s.rubyforge_project = "activerecord"
|
||||
end
|
||||
|
||||
Rake::GemPackageTask.new(spec) do |p|
|
||||
Gem::PackageTask.new(spec) do |p|
|
||||
p.gem_spec = spec
|
||||
p.need_tar = true
|
||||
p.need_zip = true
|
||||
|
||||
@@ -27,7 +27,7 @@ conn[:socket] = Pathname.glob(%w[
|
||||
/tmp/mysql.sock
|
||||
/var/mysql/mysql.sock
|
||||
/var/run/mysqld/mysqld.sock
|
||||
]).find { |path| path.socket? }
|
||||
]).find { |path| path.socket? }.to_s
|
||||
|
||||
ActiveRecord::Base.establish_connection(conn)
|
||||
|
||||
@@ -60,7 +60,7 @@ end
|
||||
sqlfile = "#{__DIR__}/performance.sql"
|
||||
|
||||
if File.exists?(sqlfile)
|
||||
mysql_bin = %w[mysql mysql5].select { |bin| `which #{bin}`.length > 0 }
|
||||
mysql_bin = %w[mysql mysql5].detect { |bin| `which #{bin}`.length > 0 }
|
||||
`#{mysql_bin} -u #{conn[:username]} #{"-p#{conn[:password]}" unless conn[:password].blank?} #{conn[:database]} < #{sqlfile}`
|
||||
else
|
||||
puts 'Generating data...'
|
||||
@@ -90,7 +90,7 @@ else
|
||||
)
|
||||
end
|
||||
|
||||
mysqldump_bin = %w[mysqldump mysqldump5].select { |bin| `which #{bin}`.length > 0 }
|
||||
mysqldump_bin = %w[mysqldump mysqldump5].detect { |bin| `which #{bin}`.length > 0 }
|
||||
`#{mysqldump_bin} -u #{conn[:username]} #{"-p#{conn[:password]}" unless conn[:password].blank?} #{conn[:database]} exhibits users > #{sqlfile}`
|
||||
end
|
||||
|
||||
@@ -157,6 +157,40 @@ RBench.run(TIMES) do
|
||||
ar { Exhibit.transaction { Exhibit.new } }
|
||||
end
|
||||
|
||||
report 'Model.find(id)' do
|
||||
id = Exhibit.first.id
|
||||
ar { Exhibit.find(id) }
|
||||
end
|
||||
|
||||
report 'Model.find_by_sql' do
|
||||
ar { Exhibit.find_by_sql("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}").first }
|
||||
end
|
||||
|
||||
report 'Model.log', (TIMES * 10) do
|
||||
ar { Exhibit.connection.send(:log, "hello", "world") {} }
|
||||
end
|
||||
|
||||
report 'AR.execute(query)', (TIMES / 2) do
|
||||
ar { ActiveRecord::Base.connection.execute("Select * from exhibits where id = #{(rand * 1000 + 1).to_i}") }
|
||||
end
|
||||
|
||||
report 'Model.find(id)' do
|
||||
id = Exhibit.first.id
|
||||
ar { Exhibit.find(id) }
|
||||
end
|
||||
|
||||
report 'Model.find_by_sql' do
|
||||
ar { Exhibit.find_by_sql("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}").first }
|
||||
end
|
||||
|
||||
report 'Model.log', (TIMES * 10) do
|
||||
ar { Exhibit.connection.send(:log, "hello", "world") {} }
|
||||
end
|
||||
|
||||
report 'AR.execute(query)', (TIMES / 2) do
|
||||
ar { ActiveRecord::Base.connection.execute("Select * from exhibits where id = #{(rand * 1000 + 1).to_i}") }
|
||||
end
|
||||
|
||||
summary 'Total'
|
||||
end
|
||||
|
||||
|
||||
@@ -283,7 +283,7 @@ module ActiveRecord
|
||||
through_records.flatten!
|
||||
else
|
||||
options = {}
|
||||
options[:include] = reflection.options[:include] || reflection.options[:source] if reflection.options[:conditions]
|
||||
options[:include] = reflection.options[:include] || reflection.options[:source] if reflection.options[:conditions] || reflection.options[:order]
|
||||
options[:order] = reflection.options[:order]
|
||||
options[:conditions] = reflection.options[:conditions]
|
||||
records.first.class.preload_associations(records, through_association, options)
|
||||
|
||||
@@ -332,6 +332,7 @@ module ActiveRecord
|
||||
|
||||
def include?(record)
|
||||
return false unless record.is_a?(@reflection.klass)
|
||||
return include_in_memory?(record) if record.new_record?
|
||||
load_target if @reflection.options[:finder_sql] && !loaded?
|
||||
return @target.include?(record) if loaded?
|
||||
exists?(record)
|
||||
@@ -352,15 +353,14 @@ module ActiveRecord
|
||||
if @target.is_a?(Array) && @target.any?
|
||||
@target = find_target.map do |f|
|
||||
i = @target.index(f)
|
||||
if i
|
||||
@target.delete_at(i).tap do |t|
|
||||
keys = ["id"] + t.changes.keys + (f.attribute_names - t.attribute_names)
|
||||
t.attributes = f.attributes.except(*keys)
|
||||
end
|
||||
t = @target.delete_at(i) if i
|
||||
if t && t.changed?
|
||||
t
|
||||
else
|
||||
f.mark_for_destruction if t && t.marked_for_destruction?
|
||||
f
|
||||
end
|
||||
end + @target
|
||||
end + @target.find_all {|t| t.new_record?}
|
||||
else
|
||||
@target = find_target
|
||||
end
|
||||
@@ -374,16 +374,17 @@ module ActiveRecord
|
||||
target
|
||||
end
|
||||
|
||||
def method_missing(method, *args)
|
||||
def method_missing(method, *args, &block)
|
||||
case method.to_s
|
||||
when 'find_or_create'
|
||||
return find(:first, :conditions => args.first) || create(args.first)
|
||||
when /^find_or_create_by_(.*)$/
|
||||
rest = $1
|
||||
return send("find_by_#{rest}", *args) ||
|
||||
method_missing("create_by_#{rest}", *args)
|
||||
find_args = pull_finder_args_from(DynamicFinderMatch.match(method).attribute_names, *args)
|
||||
return send("find_by_#{rest}", *find_args) ||
|
||||
method_missing("create_by_#{rest}", *args, &block)
|
||||
when /^create_by_(.*)$/
|
||||
return create($1.split('_and_').zip(args).inject({}) { |h,kv| k,v=kv ; h[k] = v ; h })
|
||||
return create($1.split('_and_').zip(args).inject({}) { |h,kv| k,v=kv ; h[k] = v ; h }, &block)
|
||||
end
|
||||
|
||||
if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
|
||||
@@ -433,20 +434,32 @@ module ActiveRecord
|
||||
callback(:before_add, record)
|
||||
yield(record) if block_given?
|
||||
@target ||= [] unless loaded?
|
||||
index = @target.index(record)
|
||||
unless @reflection.options[:uniq] && index
|
||||
if index
|
||||
@target[index] = record
|
||||
else
|
||||
@target << record
|
||||
end
|
||||
end
|
||||
@target << record unless @reflection.options[:uniq] && @target.include?(record)
|
||||
callback(:after_add, record)
|
||||
set_inverse_instance(record, @owner)
|
||||
record
|
||||
end
|
||||
|
||||
private
|
||||
# Separate the "finder" args from the "create" args given to a
|
||||
# find_or_create_by_ call. Returns an array with the
|
||||
# parameter values in the same order as the keys in the
|
||||
# "names" array. This code was based on code in base.rb's
|
||||
# method_missing method.
|
||||
def pull_finder_args_from(names, *args)
|
||||
attributes = names.collect { |name| name.intern }
|
||||
attribute_hash = {}
|
||||
args.each_with_index do |arg, i|
|
||||
if arg.is_a?(Hash)
|
||||
attribute_hash.merge! arg
|
||||
else
|
||||
attribute_hash[attributes[i]] = arg
|
||||
end
|
||||
end
|
||||
attribute_hash = attribute_hash.with_indifferent_access
|
||||
attributes.collect { |attr| attribute_hash[attr] }
|
||||
end
|
||||
|
||||
def create_record(attrs)
|
||||
attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
|
||||
ensure_owner_is_not_new
|
||||
@@ -491,8 +504,8 @@ module ActiveRecord
|
||||
def callbacks_for(callback_name)
|
||||
full_callback_name = "#{callback_name}_for_#{@reflection.name}"
|
||||
@owner.class.read_inheritable_attribute(full_callback_name.to_sym) || []
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def ensure_owner_is_not_new
|
||||
if @owner.new_record?
|
||||
raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
|
||||
@@ -503,6 +516,18 @@ module ActiveRecord
|
||||
args.first.kind_of?(Hash) || !(loaded? || @owner.new_record? || @reflection.options[:finder_sql] ||
|
||||
@target.any? { |record| record.new_record? } || args.first.kind_of?(Integer))
|
||||
end
|
||||
|
||||
def include_in_memory?(record)
|
||||
if @reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
||||
@owner.send(proxy_reflection.through_reflection.name.to_sym).each do |source|
|
||||
source_reflection_target = source.send(proxy_reflection.source_reflection.name)
|
||||
return true if source_reflection_target.respond_to?(:include?) ? source_reflection_target.include?(record) : source_reflection_target == record
|
||||
end
|
||||
false
|
||||
else
|
||||
@target.include?(record)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1286,7 +1286,7 @@ module ActiveRecord #:nodoc:
|
||||
|
||||
# Turns the +table_name+ back into a class name following the reverse rules of +table_name+.
|
||||
def class_name(table_name = table_name) # :nodoc:
|
||||
ActiveSupport::Deprecation.warn("ActiveRecord::Base#class_name is deprecated and will be removed in Rails 2.3.9.", caller)
|
||||
ActiveSupport::Deprecation.warn("ActiveRecord::Base#class_name is deprecated and will be removed in Rails 3.", caller)
|
||||
|
||||
# remove any prefix and/or suffix from the table name
|
||||
class_name = table_name[table_name_prefix.length..-(table_name_suffix.length + 1)].camelize
|
||||
@@ -2333,17 +2333,17 @@ module ActiveRecord #:nodoc:
|
||||
# And for value objects on a composed_of relationship:
|
||||
# { :address => Address.new("123 abc st.", "chicago") }
|
||||
# # => "address_street='123 abc st.' and address_city='chicago'"
|
||||
def sanitize_sql_hash_for_conditions(attrs, default_table_name = quoted_table_name)
|
||||
def sanitize_sql_hash_for_conditions(attrs, default_table_name = quoted_table_name, top_level = true)
|
||||
attrs = expand_hash_conditions_for_aggregates(attrs)
|
||||
|
||||
conditions = attrs.map do |attr, value|
|
||||
table_name = default_table_name
|
||||
|
||||
unless value.is_a?(Hash)
|
||||
if not value.is_a?(Hash)
|
||||
attr = attr.to_s
|
||||
|
||||
# Extract table name from qualified attribute names.
|
||||
if attr.include?('.')
|
||||
if attr.include?('.') and top_level
|
||||
attr_table_name, attr = attr.split('.', 2)
|
||||
attr_table_name = connection.quote_table_name(attr_table_name)
|
||||
else
|
||||
@@ -2351,8 +2351,10 @@ module ActiveRecord #:nodoc:
|
||||
end
|
||||
|
||||
attribute_condition("#{attr_table_name}.#{connection.quote_column_name(attr)}", value)
|
||||
elsif top_level
|
||||
sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s), false)
|
||||
else
|
||||
sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s))
|
||||
raise ActiveRecord::StatementInvalid
|
||||
end
|
||||
end.join(' AND ')
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ module ActiveRecord
|
||||
# A (slower) workaround if we're using a backend, like sqlite, that doesn't support COUNT DISTINCT.
|
||||
sql = "SELECT COUNT(*) AS #{aggregate_alias}" if use_workaround
|
||||
|
||||
sql << ", #{options[:group_field]} AS #{options[:group_alias]}" if options[:group]
|
||||
options[:group_fields].each_index{|i| sql << ", #{options[:group_fields][i]} AS #{options[:group_aliases][i]}" } if options[:group]
|
||||
if options[:from]
|
||||
sql << " FROM #{options[:from]} "
|
||||
elsif scope && scope[:from] && !use_workaround
|
||||
@@ -211,8 +211,8 @@ module ActiveRecord
|
||||
add_limited_ids_condition!(sql, options, join_dependency) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
|
||||
|
||||
if options[:group]
|
||||
group_key = connection.adapter_name == 'FrontBase' ? :group_alias : :group_field
|
||||
sql << " GROUP BY #{options[group_key]} "
|
||||
group_key = connection.adapter_name == 'FrontBase' ? :group_aliases : :group_fields
|
||||
sql << " GROUP BY #{options[group_key].join(',')} "
|
||||
end
|
||||
|
||||
if options[:group] && options[:having]
|
||||
@@ -239,24 +239,31 @@ module ActiveRecord
|
||||
end
|
||||
|
||||
def execute_grouped_calculation(operation, column_name, column, options) #:nodoc:
|
||||
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 ? 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))
|
||||
group_attr = options[:group]
|
||||
association = reflect_on_association(group_attr.to_s.to_sym)
|
||||
associated = association && association.macro == :belongs_to # only count belongs_to associations
|
||||
group_fields = Array(associated ? association.primary_key_name : group_attr)
|
||||
group_aliases = []
|
||||
group_columns = {}
|
||||
|
||||
group_fields.each do |field|
|
||||
group_aliases << column_alias_for(field)
|
||||
group_columns[column_alias_for(field)] = column_for(field)
|
||||
end
|
||||
|
||||
sql = construct_calculation_sql(operation, column_name, options.merge(:group_fields => group_fields, :group_aliases => group_aliases))
|
||||
calculated_data = connection.select_all(sql)
|
||||
aggregate_alias = column_alias_for(operation, column_name)
|
||||
|
||||
if association
|
||||
key_ids = calculated_data.collect { |row| row[group_alias] }
|
||||
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
||||
key_records = association.klass.base_class.find(key_ids)
|
||||
key_records = key_records.inject({}) { |hsh, r| hsh.merge(r.id => r) }
|
||||
end
|
||||
|
||||
calculated_data.inject(ActiveSupport::OrderedHash.new) do |all, row|
|
||||
key = type_cast_calculated_value(row[group_alias], group_column)
|
||||
key = group_aliases.map{|group_alias| type_cast_calculated_value(row[group_alias], group_columns[group_alias])}
|
||||
key = key.first if key.size == 1
|
||||
key = key_records[key] if associated
|
||||
value = row[aggregate_alias]
|
||||
all[key] = type_cast_calculated_value(value, column, operation)
|
||||
|
||||
@@ -274,7 +274,7 @@ module ActiveRecord
|
||||
|
||||
if Hash === options # legacy support, since this param was a string
|
||||
index_type = options[:unique] ? "UNIQUE" : ""
|
||||
index_name = options[:name] || index_name
|
||||
index_name = options[:name].to_s if options[:name]
|
||||
else
|
||||
index_type = options
|
||||
end
|
||||
@@ -347,6 +347,7 @@ module ActiveRecord
|
||||
# as there's no way to determine the correct answer in that case.
|
||||
def index_exists?(table_name, index_name, default)
|
||||
return default unless respond_to?(:indexes)
|
||||
index_name = index_name.to_s
|
||||
indexes(table_name).detect { |i| i.name == index_name }
|
||||
end
|
||||
|
||||
|
||||
@@ -238,7 +238,7 @@ module ActiveRecord
|
||||
end
|
||||
|
||||
def quote_column_name(name) #:nodoc:
|
||||
@quoted_column_names[name] ||= "`#{name}`"
|
||||
@quoted_column_names[name] ||= "`#{name.to_s.gsub('`', '``')}`"
|
||||
end
|
||||
|
||||
def quote_table_name(name) #:nodoc:
|
||||
|
||||
@@ -162,7 +162,7 @@ module ActiveRecord
|
||||
end
|
||||
|
||||
def quote_column_name(name) #:nodoc:
|
||||
%Q("#{name}")
|
||||
%Q("#{name.to_s.gsub('"', '""')}")
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ module ActiveRecord
|
||||
options ||= {}
|
||||
[options[:extend]].flatten.each { |extension| extend extension } if options[:extend]
|
||||
extend Module.new(&block) if block_given?
|
||||
unless Scope === proxy_scope
|
||||
unless (Scope === proxy_scope || ActiveRecord::Associations::AssociationCollection === proxy_scope)
|
||||
@current_scoped_methods_when_defined = proxy_scope.send(:current_scoped_methods)
|
||||
end
|
||||
@proxy_scope, @proxy_options = proxy_scope, options.except(:extend)
|
||||
|
||||
@@ -286,9 +286,7 @@ module ActiveRecord
|
||||
assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy])
|
||||
|
||||
elsif attributes['id']
|
||||
existing_record = self.class.reflect_on_association(association_name).klass.find(attributes['id'])
|
||||
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
|
||||
self.send(association_name.to_s+'=', existing_record)
|
||||
raise_nested_attributes_record_not_found(association_name, attributes['id'])
|
||||
|
||||
elsif !reject_new_record?(association_name, attributes)
|
||||
method = "build_#{association_name}"
|
||||
@@ -358,16 +356,11 @@ module ActiveRecord
|
||||
unless reject_new_record?(association_name, attributes)
|
||||
association.build(attributes.except(*UNASSIGNABLE_KEYS))
|
||||
end
|
||||
|
||||
elsif existing_records.size == 0 # Existing record but not yet associated
|
||||
existing_record = self.class.reflect_on_association(association_name).klass.find(attributes['id'])
|
||||
association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded?
|
||||
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
|
||||
|
||||
elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
|
||||
association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded?
|
||||
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
|
||||
|
||||
else
|
||||
raise_nested_attributes_record_not_found(association_name, attributes['id'])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -387,7 +380,7 @@ module ActiveRecord
|
||||
ConnectionAdapters::Column.value_to_boolean(hash['_destroy'])
|
||||
end
|
||||
|
||||
# Determines if a new record should be built by checking for
|
||||
# Determines if a new record should be build by checking for
|
||||
# has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
|
||||
# association and evaluates to +true+.
|
||||
def reject_new_record?(association_name, attributes)
|
||||
@@ -403,5 +396,9 @@ module ActiveRecord
|
||||
end
|
||||
end
|
||||
|
||||
def raise_nested_attributes_record_not_found(association_name, record_id)
|
||||
reflection = self.class.reflect_on_association(association_name)
|
||||
raise RecordNotFound, "Couldn't find #{reflection.klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -333,7 +333,6 @@ module ActiveRecord
|
||||
end
|
||||
|
||||
def generate_message(attribute, message = :invalid, options = {})
|
||||
ActiveSupport::Deprecation.warn("ActiveRecord::Errors#generate_message has been deprecated. Please use ActiveRecord::Error.new().to_s.")
|
||||
Error.new(@base, attribute, message, options).to_s
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@ module ActiveRecord
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 3
|
||||
TINY = 9
|
||||
TINY = 14
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
||||
@@ -363,7 +363,12 @@ class EagerAssociationTest < ActiveRecord::TestCase
|
||||
assert_equal post_tags, eager_post_tags
|
||||
end
|
||||
|
||||
|
||||
def test_eager_with_has_many_through_association_with_order
|
||||
author_comments = Author.find(authors(:david).id).comments_desc
|
||||
eager_author_comments = Author.find(authors(:david).id, :include => :comments_desc).comments_desc
|
||||
assert_equal eager_author_comments, author_comments
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_through_join_model_with_include
|
||||
author_comments = Author.find(authors(:david).id, :include => :comments_with_include).comments_with_include.to_a
|
||||
assert_no_queries do
|
||||
|
||||
@@ -819,4 +819,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
|
||||
assert_queries(0) { david.projects.columns; david.projects.columns }
|
||||
end
|
||||
|
||||
def test_include_method_in_has_and_belongs_to_many_association_should_return_true_for_instance_added_with_build
|
||||
project = Project.new
|
||||
developer = project.developers.build
|
||||
assert project.developers.include?(developer)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -65,6 +65,38 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
||||
assert_equal person, person.readers.first.person
|
||||
end
|
||||
|
||||
def test_find_or_create_by_with_additional_parameters
|
||||
post = Post.create! :title => 'test_find_or_create_by_with_additional_parameters', :body => 'this is the body'
|
||||
comment = post.comments.create! :body => 'test comment body', :type => 'test'
|
||||
|
||||
assert_equal comment, post.comments.find_or_create_by_body('test comment body')
|
||||
|
||||
post.comments.find_or_create_by_body(:body => 'other test comment body', :type => 'test')
|
||||
assert_equal 2, post.comments.count
|
||||
assert_equal 2, post.comments.length
|
||||
post.comments.find_or_create_by_body('other other test comment body', :type => 'test')
|
||||
assert_equal 3, post.comments.count
|
||||
assert_equal 3, post.comments.length
|
||||
post.comments.find_or_create_by_body_and_type('3rd test comment body', 'test')
|
||||
assert_equal 4, post.comments.count
|
||||
assert_equal 4, post.comments.length
|
||||
end
|
||||
|
||||
def test_find_or_create_by_with_same_parameters_creates_a_single_record
|
||||
author = Author.first
|
||||
assert_difference "Post.count", +1 do
|
||||
2.times do
|
||||
author.posts.find_or_create_by_body_and_title('one', 'two')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_find_or_create_by_with_block
|
||||
post = Post.create! :title => 'test_find_or_create_by_with_additional_parameters', :body => 'this is the body'
|
||||
comment = post.comments.find_or_create_by_body('other test comment body') { |comment| comment.type = 'test' }
|
||||
assert_equal 'test', comment.type
|
||||
end
|
||||
|
||||
def test_find_or_create
|
||||
person = Person.create! :first_name => 'tenderlove'
|
||||
post = Post.find :first
|
||||
@@ -843,6 +875,17 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
||||
assert companies(:first_firm).clients_of_firm(true).empty?, "37signals has no clients after destroy all and refresh"
|
||||
end
|
||||
|
||||
def test_destroy_all_with_creates_and_scope_that_doesnt_match_created_records
|
||||
company = companies(:first_firm)
|
||||
unloaded_client_matching_scope = companies(:second_client)
|
||||
created_client_matching_scope = company.clients_of_firm.create!(:name => "Somesoft")
|
||||
created_client_not_matching_scope = company.clients_of_firm.create!(:name => "OtherCo")
|
||||
destroyed = company.clients_of_firm.with_oft_in_name.destroy_all
|
||||
assert destroyed.include?(unloaded_client_matching_scope), "unloaded clients matching the scope destroy_all on should have been destroyed"
|
||||
assert destroyed.include?(created_client_matching_scope), "loaded clients matching the scope destroy_all on should have been destroyed"
|
||||
assert !destroyed.include?(created_client_not_matching_scope), "loaded clients not matching the scope destroy_all on should not have been destroyed"
|
||||
end
|
||||
|
||||
def test_dependence
|
||||
firm = companies(:first_firm)
|
||||
assert_equal 2, firm.clients.size
|
||||
@@ -1221,5 +1264,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
||||
end
|
||||
EOF
|
||||
end
|
||||
end
|
||||
|
||||
def test_include_method_in_has_many_association_should_return_true_for_instance_added_with_build
|
||||
post = Post.new
|
||||
comment = post.comments.build
|
||||
assert post.comments.include?(comment)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -343,4 +343,18 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
|
||||
lambda { authors(:david).very_special_comments.delete(authors(:david).very_special_comments.first) },
|
||||
].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
|
||||
end
|
||||
|
||||
def test_include_method_in_association_through_should_return_true_for_instance_added_with_build
|
||||
person = Person.new
|
||||
reference = person.references.build
|
||||
job = reference.build_job
|
||||
assert person.jobs.include?(job)
|
||||
end
|
||||
|
||||
def test_include_method_in_association_through_should_return_true_for_instance_added_with_nested_builds
|
||||
author = Author.new
|
||||
post = author.posts.build
|
||||
comment = post.comments.build
|
||||
assert author.comments.include?(comment)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -18,8 +18,6 @@ require 'models/person'
|
||||
require 'models/reader'
|
||||
require 'models/parrot'
|
||||
require 'models/pirate'
|
||||
require 'models/ship'
|
||||
require 'models/ship_part'
|
||||
require 'models/treasure'
|
||||
require 'models/price_estimate'
|
||||
require 'models/club'
|
||||
@@ -31,23 +29,6 @@ class AssociationsTest < ActiveRecord::TestCase
|
||||
fixtures :accounts, :companies, :developers, :projects, :developers_projects,
|
||||
:computers, :people, :readers
|
||||
|
||||
def test_loading_the_association_target_should_keep_child_records_marked_for_destruction
|
||||
ship = Ship.create!(:name => "The good ship Dollypop")
|
||||
part = ship.parts.create!(:name => "Mast")
|
||||
part.mark_for_destruction
|
||||
ship.parts.send(:load_target)
|
||||
assert ship.parts[0].marked_for_destruction?
|
||||
end
|
||||
|
||||
def test_loading_the_association_target_should_load_most_recent_attributes_for_child_records_marked_for_destruction
|
||||
ship = Ship.create!(:name => "The good ship Dollypop")
|
||||
part = ship.parts.create!(:name => "Mast")
|
||||
part.mark_for_destruction
|
||||
ShipPart.find(part.id).update_attribute(:name, 'Deck')
|
||||
ship.parts.send(:load_target)
|
||||
assert_equal 'Deck', ship.parts[0].name
|
||||
end
|
||||
|
||||
def test_include_with_order_works
|
||||
assert_nothing_raised {Account.find(:first, :order => 'id', :include => :firm)}
|
||||
assert_nothing_raised {Account.find(:first, :order => :id, :include => :firm)}
|
||||
|
||||
@@ -79,6 +79,23 @@ end
|
||||
class BasicsTest < ActiveRecord::TestCase
|
||||
fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts
|
||||
|
||||
def test_column_names_are_escaped
|
||||
conn = ActiveRecord::Base.connection
|
||||
classname = conn.class.name[/[^:]*$/]
|
||||
badchar = {
|
||||
'SQLite3Adapter' => '"',
|
||||
'MysqlAdapter' => '`',
|
||||
'Mysql2Adapter' => '`',
|
||||
'PostgreSQLAdapter' => '"',
|
||||
'OracleAdapter' => '"',
|
||||
}.fetch(classname) {
|
||||
raise "need a bad char for #{classname}"
|
||||
}
|
||||
|
||||
quoted = conn.quote_column_name "foo#{badchar}bar"
|
||||
assert_equal("#{badchar}foo#{badchar * 2}bar#{badchar}", quoted)
|
||||
end
|
||||
|
||||
def test_table_exists
|
||||
assert !NonExistentTable.table_exists?
|
||||
assert Topic.table_exists?
|
||||
|
||||
@@ -58,6 +58,19 @@ class CalculationsTest < ActiveRecord::TestCase
|
||||
[1,6,2].each { |firm_id| assert c.keys.include?(firm_id) }
|
||||
end
|
||||
|
||||
def test_should_group_by_multiple_fields
|
||||
c = Account.count(:all, :group => ['firm_id', :credit_limit])
|
||||
[ [nil, 50], [1, 50], [6, 50], [6, 55], [9, 53], [2, 60] ].each { |firm_and_limit| assert c.keys.include?(firm_and_limit) }
|
||||
end
|
||||
|
||||
def test_should_group_by_multiple_fields_having_functions
|
||||
c = Topic.count(:all, :group => [:author_name, 'COALESCE(type, title)'])
|
||||
assert_equal 1, c[["Nick", "The Third Topic of the day"]]
|
||||
assert_equal 1, c[["Mary", "Reply"]]
|
||||
assert_equal 1, c[["David", "The First Topic"]]
|
||||
assert_equal 1, c[["Carl", "Reply"]]
|
||||
end
|
||||
|
||||
def test_should_group_by_summed_field
|
||||
c = Account.sum(:credit_limit, :group => :firm_id)
|
||||
assert_equal 50, c[1]
|
||||
|
||||
@@ -363,6 +363,22 @@ class FinderTest < ActiveRecord::TestCase
|
||||
}
|
||||
end
|
||||
|
||||
def test_hash_condition_find_with_improper_nested_hashes
|
||||
assert_raise(ActiveRecord::StatementInvalid) {
|
||||
Company.find(:first, :conditions => { :name => { :companies => { :id => 1 }}})
|
||||
}
|
||||
end
|
||||
|
||||
def test_hash_condition_find_with_dot_in_nested_column_name
|
||||
assert_raise(ActiveRecord::StatementInvalid) {
|
||||
Company.find(:first, :conditions => { :name => { "companies.id" => 1 }})
|
||||
}
|
||||
end
|
||||
|
||||
def test_hash_condition_find_with_dot_in_column_name_okay
|
||||
assert Company.find(:first, :conditions => { "companies.id" => 1 })
|
||||
end
|
||||
|
||||
def test_hash_condition_find_with_escaped_characters
|
||||
Company.create("name" => "Ain't noth'n like' \#stuff")
|
||||
assert Company.find(:first, :conditions => { :name => "Ain't noth'n like' \#stuff" })
|
||||
|
||||
@@ -119,6 +119,13 @@ if ActiveRecord::Base.connection.supports_migrations?
|
||||
end
|
||||
end
|
||||
|
||||
def test_index_symbol_names
|
||||
assert_nothing_raised { Person.connection.add_index :people, :primary_contact_id, :name => :symbol_index_name }
|
||||
assert Person.connection.index_exists?(:people, :symbol_index_name, true)
|
||||
assert_nothing_raised { Person.connection.remove_index :people, :name => :symbol_index_name }
|
||||
assert_equal true, !Person.connection.index_exists?(:people, :symbol_index_name, false)
|
||||
end
|
||||
|
||||
def test_add_index_length_limit
|
||||
good_index_name = 'x' * Person.connection.index_name_length
|
||||
too_long_index_name = good_index_name + 'x'
|
||||
|
||||
@@ -146,6 +146,12 @@ class NamedScopeTest < ActiveRecord::TestCase
|
||||
assert_equal authors(:david).posts & Post.containing_the_letter_a, authors(:david).posts.containing_the_letter_a
|
||||
end
|
||||
|
||||
def test_nested_named_scopes_doesnt_duplicate_conditions_on_child_scopes
|
||||
comments_scope = posts(:welcome).comments.send(:construct_sql)
|
||||
named_scope_sql_conditions = posts(:welcome).comments.containing_the_letter_e.send(:current_scoped_methods)[:find][:conditions]
|
||||
assert_no_match /#{comments_scope}.*#{comments_scope}/i, named_scope_sql_conditions
|
||||
end
|
||||
|
||||
def test_has_many_through_associations_have_access_to_named_scopes
|
||||
assert_not_equal Comment.containing_the_letter_e, authors(:david).comments
|
||||
assert !Comment.containing_the_letter_e.empty?
|
||||
|
||||
@@ -175,6 +175,12 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
|
||||
assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
|
||||
end
|
||||
|
||||
def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
|
||||
assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find Ship with ID=1234567890 for Pirate with ID=#{@pirate.id}" do
|
||||
@pirate.ship_attributes = { :id => 1234567890 }
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
|
||||
@pirate.reload.ship_attributes = { 'id' => @ship.id, 'name' => 'Davy Jones Gold Dagger' }
|
||||
|
||||
@@ -324,13 +330,10 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
|
||||
assert_equal 'Arr', @ship.pirate.catchphrase
|
||||
end
|
||||
|
||||
def test_should_associate_with_record_if_parent_record_is_not_saved
|
||||
@ship.destroy
|
||||
@pirate = Pirate.create(:catchphrase => 'Arr')
|
||||
@ship = Ship.new(:name => 'Nights Dirty Lightning', :pirate_attributes => { :id => @pirate.id, :catchphrase => @pirate.catchphrase})
|
||||
|
||||
assert_equal @ship.name, 'Nights Dirty Lightning'
|
||||
assert_equal @pirate, @ship.pirate
|
||||
def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
|
||||
assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find Pirate with ID=1234567890 for Ship with ID=#{@ship.id}" do
|
||||
@ship.pirate_attributes = { :id => 1234567890 }
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
|
||||
@@ -434,11 +437,6 @@ module NestedAttributesOnACollectionAssociationTests
|
||||
assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.reload.name, @child_2.reload.name]
|
||||
end
|
||||
|
||||
def test_should_assign_existing_children_if_parent_is_new
|
||||
@pirate = Pirate.new({:catchphrase => "Don' botharr talkin' like one, savvy?"}.merge(@alternate_params))
|
||||
assert_equal ['Grace OMalley', 'Privateers Greed'], [@pirate.send(@association_name)[0].name, @pirate.send(@association_name)[1].name]
|
||||
end
|
||||
|
||||
def test_should_take_an_array_and_assign_the_attributes_to_the_associated_models
|
||||
@pirate.send(association_setter, @alternate_params[association_getter].values)
|
||||
@pirate.save
|
||||
@@ -508,8 +506,8 @@ module NestedAttributesOnACollectionAssociationTests
|
||||
assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.name, @child_2.name]
|
||||
end
|
||||
|
||||
def test_should_not_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
|
||||
assert_nothing_raised ActiveRecord::RecordNotFound do
|
||||
def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
|
||||
assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find #{@child_1.class.name} with ID=1234567890 for Pirate with ID=#{@pirate.id}" do
|
||||
@pirate.attributes = { association_getter => [{ :id => 1234567890 }] }
|
||||
end
|
||||
end
|
||||
@@ -810,13 +808,7 @@ class TestHasManyAutosaveAssoictaionWhichItselfHasAutosaveAssociations < ActiveR
|
||||
@part = @ship.parts.create!(:name => "Mast")
|
||||
@trinket = @part.trinkets.create!(:name => "Necklace")
|
||||
end
|
||||
|
||||
test "if association is not loaded and association record is saved and then in memory record attributes should be saved" do
|
||||
@ship.parts_attributes=[{:id => @part.id,:name =>'Deck'}]
|
||||
assert_equal 1, @ship.parts.proxy_target.size
|
||||
assert_equal 'Deck', @ship.parts[0].name
|
||||
end
|
||||
|
||||
|
||||
test "when grandchild changed in memory, saving parent should save grandchild" do
|
||||
@trinket.name = "changed"
|
||||
@ship.save
|
||||
|
||||
@@ -12,6 +12,8 @@ class Company < AbstractCompany
|
||||
has_many :contracts
|
||||
has_many :developers, :through => :contracts
|
||||
|
||||
named_scope :with_oft_in_name, :conditions => "name LIKE '%oft%'"
|
||||
|
||||
def arbitrary_method
|
||||
"I am Jack's profound disappointment"
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
*2.3.11 (February 9, 2011)*
|
||||
*2.3.10 (October 15, 2010)*
|
||||
*2.3.9 (September 4, 2010)*
|
||||
*2.3.8 (May 24, 2010)*
|
||||
*2.3.7 (May 24, 2010)*
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
require 'rubygems'
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
require 'rdoc/task'
|
||||
require 'rake/packagetask'
|
||||
require 'rake/gempackagetask'
|
||||
require 'rubygems/package_task'
|
||||
|
||||
require File.join(File.dirname(__FILE__), 'lib', 'active_resource', 'version')
|
||||
|
||||
@@ -38,7 +38,7 @@ Rake::TestTask.new { |t|
|
||||
|
||||
# Generate the RDoc documentation
|
||||
|
||||
Rake::RDocTask.new { |rdoc|
|
||||
RDoc::Task.new { |rdoc|
|
||||
rdoc.rdoc_dir = 'doc'
|
||||
rdoc.title = "Active Resource -- Object-oriented REST services"
|
||||
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
||||
@@ -66,12 +66,10 @@ 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.3.9' + PKG_BUILD)
|
||||
s.add_dependency('activesupport', '= 2.3.14' + PKG_BUILD)
|
||||
|
||||
s.require_path = 'lib'
|
||||
s.autorequire = 'active_resource'
|
||||
|
||||
s.has_rdoc = true
|
||||
s.extra_rdoc_files = %w( README )
|
||||
s.rdoc_options.concat ['--main', 'README']
|
||||
|
||||
@@ -81,7 +79,7 @@ spec = Gem::Specification.new do |s|
|
||||
s.rubyforge_project = "activeresource"
|
||||
end
|
||||
|
||||
Rake::GemPackageTask.new(spec) do |p|
|
||||
Gem::PackageTask.new(spec) do |p|
|
||||
p.gem_spec = spec
|
||||
p.need_tar = true
|
||||
p.need_zip = true
|
||||
|
||||
@@ -2,7 +2,7 @@ module ActiveResource
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 3
|
||||
TINY = 9
|
||||
TINY = 14
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
*2.3.11 (February 9, 2011)*
|
||||
|
||||
*2.3.10 (October 15, 2010)*
|
||||
|
||||
|
||||
*2.3.9 (September 4, 2010)*
|
||||
|
||||
* i18n: bundle i18n 0.4.1 for forward compatibility with Rails 3. Deprecates {{foo}} interpolation syntax in favor of 1.9-native %{foo}.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
require 'rake/gempackagetask'
|
||||
require 'rdoc/task'
|
||||
require 'rubygems/package_task'
|
||||
|
||||
require File.join(File.dirname(__FILE__), 'lib', 'active_support', 'version')
|
||||
|
||||
@@ -27,7 +27,7 @@ dist_dirs = [ "lib", "test"]
|
||||
|
||||
# Genereate the RDoc documentation
|
||||
|
||||
Rake::RDocTask.new { |rdoc|
|
||||
RDoc::Task.new { |rdoc|
|
||||
rdoc.rdoc_dir = 'doc'
|
||||
rdoc.title = "Active Support -- Utility classes and standard library extensions from Rails"
|
||||
rdoc.options << '--line-numbers' << '--inline-source'
|
||||
@@ -48,7 +48,6 @@ spec = Gem::Specification.new do |s|
|
||||
|
||||
s.files = [ "CHANGELOG", "README" ] + Dir.glob( "lib/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
|
||||
s.require_path = 'lib'
|
||||
s.has_rdoc = true
|
||||
|
||||
s.author = "David Heinemeier Hansson"
|
||||
s.email = "david@loudthinking.com"
|
||||
@@ -56,7 +55,7 @@ spec = Gem::Specification.new do |s|
|
||||
s.rubyforge_project = "activesupport"
|
||||
end
|
||||
|
||||
Rake::GemPackageTask.new(spec) do |p|
|
||||
Gem::PackageTask.new(spec) do |p|
|
||||
p.gem_spec = spec
|
||||
p.need_tar = true
|
||||
p.need_zip = true
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
# A base class with no predefined methods that tries to behave like Builder's
|
||||
# BlankSlate in Ruby 1.9. In Ruby pre-1.9, this is actually the
|
||||
# Builder::BlankSlate class.
|
||||
#
|
||||
# Ruby 1.9 introduces BasicObject which differs slightly from Builder's
|
||||
# BlankSlate that has been used so far. ActiveSupport::BasicObject provides a
|
||||
# barebones base class that emulates Builder::BlankSlate while still relying on
|
||||
# Ruby 1.9's BasicObject in Ruby 1.9.
|
||||
module ActiveSupport
|
||||
if defined? ::BasicObject
|
||||
# A class with no predefined methods that behaves similarly to Builder's
|
||||
# BlankSlate. Used for proxy classes.
|
||||
class BasicObject < ::BasicObject
|
||||
undef_method :==
|
||||
undef_method :equal?
|
||||
@@ -18,7 +12,10 @@ module ActiveSupport
|
||||
end
|
||||
end
|
||||
else
|
||||
require 'blankslate'
|
||||
BasicObject = BlankSlate
|
||||
class BasicObject #:nodoc:
|
||||
instance_methods.each do |m|
|
||||
undef_method(m) if m.to_s !~ /(?:^__|^nil\?$|^send$|^object_id$)/
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require 'thread'
|
||||
|
||||
module ActiveSupport
|
||||
# Inspired by the buffered logger idea by Ezra
|
||||
class BufferedLogger
|
||||
|
||||
@@ -38,7 +38,7 @@ class Object
|
||||
#
|
||||
# foo # => ['bar', 'baz']
|
||||
def returning(value)
|
||||
ActiveSupport::Deprecation.warn('Object#returning has been deprecated in favor of Object#tap.', caller)
|
||||
ActiveSupport::Deprecation.warn('Kernel#returning has been deprecated in favor of Object#tap.', caller)
|
||||
yield(value)
|
||||
value
|
||||
end
|
||||
|
||||
@@ -19,7 +19,7 @@ class ERB
|
||||
if s.html_safe?
|
||||
s
|
||||
else
|
||||
s.gsub(/[&"><]/) { |special| HTML_ESCAPE[special] }.html_safe
|
||||
s.to_s.gsub(/&/, "&").gsub(/\"/, """).gsub(/>/, ">").gsub(/</, "<").html_safe
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -15,7 +15,10 @@ en:
|
||||
month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
|
||||
abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
|
||||
# Used in date_select and datime_select.
|
||||
order: [ :year, :month, :day ]
|
||||
order:
|
||||
- :year
|
||||
- :month
|
||||
- :day
|
||||
|
||||
time:
|
||||
formats:
|
||||
|
||||
@@ -92,6 +92,8 @@ module ActiveSupport
|
||||
#
|
||||
if private_method_defined?(#{original_method.inspect}) # if private_method_defined?(:_unmemoized_mime_type)
|
||||
private #{symbol.inspect} # private :mime_type
|
||||
elsif protected_method_defined?(#{original_method.inspect}) # elsif protected_method_defined?(:_unmemoized_mime_type)
|
||||
protected #{symbol.inspect} # protected :mime_type
|
||||
end # end
|
||||
EOS
|
||||
end
|
||||
|
||||
@@ -130,14 +130,18 @@ module ActiveSupport
|
||||
end
|
||||
|
||||
def merge!(other_hash)
|
||||
other_hash.each {|k,v| self[k] = v }
|
||||
if block_given?
|
||||
other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v }
|
||||
else
|
||||
other_hash.each { |k, v| self[k] = v }
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
alias_method :update, :merge!
|
||||
|
||||
def merge(other_hash)
|
||||
dup.merge!(other_hash)
|
||||
def merge(other_hash, &block)
|
||||
dup.merge!(other_hash, &block)
|
||||
end
|
||||
|
||||
# When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not.
|
||||
|
||||
@@ -23,12 +23,12 @@ module ActiveSupport
|
||||
run_callbacks :setup
|
||||
result = super
|
||||
rescue Exception => e
|
||||
result = runner.puke(self.class, self.name, e)
|
||||
result = runner.puke(self.class, __name__, e)
|
||||
ensure
|
||||
begin
|
||||
run_callbacks :teardown, :enumerator => :reverse_each
|
||||
rescue Exception => e
|
||||
result = runner.puke(self.class, self.name, e)
|
||||
result = runner.puke(self.class, __name__, e)
|
||||
end
|
||||
end
|
||||
result
|
||||
|
||||
@@ -2,7 +2,7 @@ module ActiveSupport
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 3
|
||||
TINY = 9
|
||||
TINY = 14
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
require 'abstract_unit'
|
||||
require 'thread'
|
||||
|
||||
class SynchronizationTest < Test::Unit::TestCase
|
||||
def setup
|
||||
|
||||
@@ -2,10 +2,17 @@
|
||||
require 'date'
|
||||
require 'abstract_unit'
|
||||
require 'inflector_test_cases'
|
||||
require 'active_support/core_ext/string/output_safety'
|
||||
|
||||
class StringInflectionsTest < Test::Unit::TestCase
|
||||
include InflectorTestCases
|
||||
|
||||
def test_erb_escape
|
||||
string = [192, 60].pack('CC')
|
||||
expected = 192.chr + "<"
|
||||
assert_equal expected, ERB::Util.html_escape(string)
|
||||
end
|
||||
|
||||
def test_pluralize
|
||||
SingularToPlural.each do |singular, plural|
|
||||
assert_equal(plural, singular.pluralize)
|
||||
|
||||
@@ -34,6 +34,13 @@ class MemoizableTest < Test::Unit::TestCase
|
||||
|
||||
memoize :name, :age
|
||||
|
||||
protected
|
||||
|
||||
def memoize_protected_test
|
||||
'protected'
|
||||
end
|
||||
memoize :memoize_protected_test
|
||||
|
||||
private
|
||||
|
||||
def is_developer?
|
||||
@@ -234,6 +241,13 @@ class MemoizableTest < Test::Unit::TestCase
|
||||
assert_raise(RuntimeError) { company.memoize :name }
|
||||
end
|
||||
|
||||
def test_protected_method_memoization
|
||||
person = Person.new
|
||||
|
||||
assert_raise(NoMethodError) { person.memoize_protected_test }
|
||||
assert_equal "protected", person.send(:memoize_protected_test)
|
||||
end
|
||||
|
||||
def test_private_method_memoization
|
||||
person = Person.new
|
||||
|
||||
|
||||
@@ -147,6 +147,32 @@ class OrderedHashTest < Test::Unit::TestCase
|
||||
assert_equal @ordered_hash.keys, merged.keys
|
||||
end
|
||||
|
||||
def test_merge_with_block
|
||||
hash = ActiveSupport::OrderedHash.new
|
||||
hash[:a] = 0
|
||||
hash[:b] = 0
|
||||
merged = hash.merge(:b => 2, :c => 7) do |key, old_value, new_value|
|
||||
new_value + 1
|
||||
end
|
||||
|
||||
assert_equal 0, merged[:a]
|
||||
assert_equal 3, merged[:b]
|
||||
assert_equal 7, merged[:c]
|
||||
end
|
||||
|
||||
def test_merge_bang_with_block
|
||||
hash = ActiveSupport::OrderedHash.new
|
||||
hash[:a] = 0
|
||||
hash[:b] = 0
|
||||
hash.merge!(:a => 1, :c => 7) do |key, old_value, new_value|
|
||||
new_value + 3
|
||||
end
|
||||
|
||||
assert_equal 4, hash[:a]
|
||||
assert_equal 0, hash[:b]
|
||||
assert_equal 7, hash[:c]
|
||||
end
|
||||
|
||||
def test_shift
|
||||
pair = @ordered_hash.shift
|
||||
assert_equal [@keys.first, @values.first], pair
|
||||
|
||||
57
activesupport/test/test_case_test.rb
Normal file
57
activesupport/test/test_case_test.rb
Normal file
@@ -0,0 +1,57 @@
|
||||
require 'abstract_unit'
|
||||
|
||||
module ActiveSupport
|
||||
class TestCaseTest < ActiveSupport::TestCase
|
||||
class FakeRunner
|
||||
attr_reader :puked
|
||||
|
||||
def initialize
|
||||
@puked = []
|
||||
end
|
||||
|
||||
def puke(klass, name, e)
|
||||
@puked << [klass, name, e]
|
||||
end
|
||||
end
|
||||
|
||||
if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions
|
||||
def test_callback_with_exception
|
||||
tc = Class.new(TestCase) do
|
||||
setup :bad_callback
|
||||
def bad_callback; raise 'oh noes' end
|
||||
def test_true; assert true end
|
||||
end
|
||||
|
||||
test_name = 'test_true'
|
||||
fr = FakeRunner.new
|
||||
|
||||
test = tc.new test_name
|
||||
test.run fr
|
||||
klass, name, exception = *fr.puked.first
|
||||
|
||||
assert_equal tc, klass
|
||||
assert_equal test_name, name
|
||||
assert_equal 'oh noes', exception.message
|
||||
end
|
||||
|
||||
def test_teardown_callback_with_exception
|
||||
tc = Class.new(TestCase) do
|
||||
teardown :bad_callback
|
||||
def bad_callback; raise 'oh noes' end
|
||||
def test_true; assert true end
|
||||
end
|
||||
|
||||
test_name = 'test_true'
|
||||
fr = FakeRunner.new
|
||||
|
||||
test = tc.new test_name
|
||||
test.run fr
|
||||
klass, name, exception = *fr.puked.first
|
||||
|
||||
assert_equal tc, klass
|
||||
assert_equal test_name, name
|
||||
assert_equal 'oh noes', exception.message
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,7 @@
|
||||
*2.3.11 (February 9, 2011)*
|
||||
|
||||
*2.3.10 (October 15, 2010)*
|
||||
|
||||
*2.3.9 (September 4, 2010)*
|
||||
|
||||
* Deprecates config.load_(once_)paths in favor of autolaod_(once_)paths. [fxn]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
require 'rake/gempackagetask'
|
||||
require 'rdoc/task'
|
||||
require 'rubygems/package_task'
|
||||
|
||||
require 'date'
|
||||
require 'rbconfig'
|
||||
@@ -267,7 +267,7 @@ task :generate_app_doc do
|
||||
system %{cd #{PKG_DESTINATION}; rake doc:app}
|
||||
end
|
||||
|
||||
Rake::RDocTask.new { |rdoc|
|
||||
RDoc::Task.new { |rdoc|
|
||||
rdoc.rdoc_dir = 'doc'
|
||||
rdoc.title = "Railties -- Gluing the Engine to the Rails"
|
||||
rdoc.options << '--line-numbers' << '--inline-source' << '--accessor' << 'cattr_accessor=object'
|
||||
@@ -313,20 +313,18 @@ spec = Gem::Specification.new do |s|
|
||||
EOF
|
||||
|
||||
s.add_dependency('rake', '>= 0.8.3')
|
||||
s.add_dependency('activesupport', '= 2.3.9' + PKG_BUILD)
|
||||
s.add_dependency('activerecord', '= 2.3.9' + PKG_BUILD)
|
||||
s.add_dependency('actionpack', '= 2.3.9' + PKG_BUILD)
|
||||
s.add_dependency('actionmailer', '= 2.3.9' + PKG_BUILD)
|
||||
s.add_dependency('activeresource', '= 2.3.9' + PKG_BUILD)
|
||||
s.add_dependency('activesupport', '= 2.3.14' + PKG_BUILD)
|
||||
s.add_dependency('activerecord', '= 2.3.14' + PKG_BUILD)
|
||||
s.add_dependency('actionpack', '= 2.3.14' + PKG_BUILD)
|
||||
s.add_dependency('actionmailer', '= 2.3.14' + PKG_BUILD)
|
||||
s.add_dependency('activeresource', '= 2.3.14' + PKG_BUILD)
|
||||
|
||||
s.rdoc_options << '--exclude' << '.'
|
||||
s.has_rdoc = false
|
||||
|
||||
s.files = PKG_FILES
|
||||
s.require_path = 'lib'
|
||||
s.bindir = "bin" # Use these for applications.
|
||||
s.executables = ["rails"]
|
||||
s.default_executable = "rails"
|
||||
|
||||
s.author = "David Heinemeier Hansson"
|
||||
s.email = "david@loudthinking.com"
|
||||
@@ -334,7 +332,7 @@ spec = Gem::Specification.new do |s|
|
||||
s.rubyforge_project = "rails"
|
||||
end
|
||||
|
||||
Rake::GemPackageTask.new(spec) do |pkg|
|
||||
Gem::PackageTask.new(spec) do |pkg|
|
||||
pkg.gem_spec = spec
|
||||
end
|
||||
|
||||
|
||||
@@ -218,7 +218,9 @@ If you set a default +:host+ for your mailers you need to pass +:only_path => fa
|
||||
|
||||
h4. Sending Multipart Emails
|
||||
|
||||
Action Mailer will automatically send multipart emails if you have different templates for the same action. So, for our UserMailer example, if you have +welcome_email.text.plain.erb+ and +welcome_email.text.html.erb+ in +app/views/user_mailer+, Action Mailer will automatically send a multipart email with the HTML and text versions setup as different parts.
|
||||
Action Mailer will automatically send multipart emails if you have different templates for the same action. So, for our UserMailer example, if you have +welcome_email.text.plain.erb+ and +welcome_email.text.html.erb+ in +app/views/user_mailer+, Action Mailer will automatically send a "multipart/alternative" email with the HTML and text versions setup as different parts.
|
||||
|
||||
A "multipart/alternative" content type tells your email client that there are several representations of the same content available and that the email client is free to choose any one to display to the user. In this case we are giving a plain text and HTML version of the same message. But this could also be a text version, and a recording of someone speaking the the same message. It is important to use "multipart/alternative" only when each part has the same content.
|
||||
|
||||
To explicitly specify multipart messages, you can do something like:
|
||||
|
||||
@@ -242,7 +244,11 @@ end
|
||||
|
||||
h4. Sending Emails with Attachments
|
||||
|
||||
Attachments can be added by using the +attachment+ method:
|
||||
Attachments can be added by using the +attachment+ method. The +attachment+ method has two variations, you can either pass the body in as an option, or create it within a block.
|
||||
|
||||
Usually you will use the variation shown below for the "image/jpeg" attachment, here you just pass in the content type and body as a options hash to the attachment method. However, if you need to do some processing to create the attachment, such as with the PDF below, then the block variation can be used.
|
||||
|
||||
This email uses the "multipart/mixed" content type because each part is a different block of content. This indicates to the email client that it must show all the parts that it can display to the end user.
|
||||
|
||||
<ruby>
|
||||
class UserMailer < ActionMailer::Base
|
||||
@@ -250,13 +256,14 @@ class UserMailer < ActionMailer::Base
|
||||
recipients user.email_address
|
||||
subject "New account information"
|
||||
from "system@example.com"
|
||||
content_type "multipart/alternative"
|
||||
content_type "multipart/mixed"
|
||||
|
||||
attachment :content_type => "image/jpeg",
|
||||
:body => File.read("an-image.jpg")
|
||||
|
||||
attachment "application/pdf" do |a|
|
||||
a.body = generate_your_pdf_here()
|
||||
pdf = generate_your_pdf_here(:name => user)
|
||||
a.body = pdf
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -266,7 +273,11 @@ h4. Sending Multipart Emails with Attachments
|
||||
|
||||
Once you use the +attachment+ method, ActionMailer will no longer automagically use the correct template based on the filename. You must declare which template you are using for each content type via the +part+ method.
|
||||
|
||||
In the following example, there would be two template files, +welcome_email_html.erb+ and +welcome_email_plain.erb+ in the +app/views/user_mailer+ folder.
|
||||
Here we are making the email "multipart/mixed" with three top level parts, a "multipart/alternative", an "image/jpeg" and an "application/pdf". Within the "multipart/alternative" we are nesting a "text/html" and "text/plain" version of the same message.
|
||||
|
||||
This tells the email client that each of the top level parts should be shown to the end user, however, the first part has the content type "multipart/alternative" and provides two versions of the same message, a plain text and HTML version.
|
||||
|
||||
In the following example, there would be two template files, +welcome_email.text.html.erb+ and +welcome_email.text.plain.erb+ in the +app/views/user_mailer+ folder.
|
||||
|
||||
<ruby>
|
||||
class UserMailer < ActionMailer::Base
|
||||
@@ -274,22 +285,28 @@ class UserMailer < ActionMailer::Base
|
||||
recipients user.email_address
|
||||
subject "New account information"
|
||||
from "system@example.com"
|
||||
content_type "multipart/alternative"
|
||||
content_type "multipart/mixed"
|
||||
|
||||
part "text/html" do |p|
|
||||
p.body = render_message("welcome_email_html", :message => "<h1>HTML content</h1>")
|
||||
end
|
||||
part "multipart/alternative" do |alternative|
|
||||
|
||||
alternative.part "text/html" do |html|
|
||||
html.body = render_message("welcome_email.text.html", :message => "<h1>HTML content</h1>")
|
||||
end
|
||||
|
||||
alternative.part "text/plain" do |plain|
|
||||
plain.body = render_message("welcome_email.text.plain", :message => "text content")
|
||||
end
|
||||
|
||||
part "text/plain" do |p|
|
||||
p.body = render_message("welcome_email_plain", :message => "text content")
|
||||
end
|
||||
|
||||
attachment :content_type => "image/jpeg",
|
||||
:body => File.read("an-image.jpg")
|
||||
|
||||
attachment "application/pdf" do |a|
|
||||
a.body = generate_your_pdf_here()
|
||||
pdf = generate_your_pdf_here(:name => user)
|
||||
a.body = pdf
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
</ruby>
|
||||
|
||||
@@ -31,12 +31,14 @@ module Rails
|
||||
|
||||
def self.from_directory_name(directory_name, load_spec=true)
|
||||
directory_name_parts = File.basename(directory_name).split('-')
|
||||
name = directory_name_parts[0..-2].join('-')
|
||||
version = directory_name_parts.last
|
||||
|
||||
version = directory_name_parts.find { |s| s.match(/^\d(\.\d|\.\w+)*$/) }
|
||||
name = directory_name_parts[0..directory_name_parts.index(version)-1].join('-') if version
|
||||
|
||||
result = self.new(name, :version => version)
|
||||
spec_filename = File.join(directory_name, '.specification')
|
||||
if load_spec
|
||||
raise "Missing specification file in #{File.dirname(spec_filename)}. Perhaps you need to do a 'rake gems:refresh_specs'?" unless File.exists?(spec_filename)
|
||||
raise "Missing specification file in #{File.dirname(spec_filename)}. Perhaps you need to do a 'rake gems:refresh_specs'\?" unless File.exists?(spec_filename)
|
||||
spec = YAML::load_file(spec_filename)
|
||||
result.specification = spec
|
||||
end
|
||||
@@ -70,7 +72,15 @@ module Rails
|
||||
@load_paths_added = @loaded = @frozen = true
|
||||
return
|
||||
end
|
||||
gem self
|
||||
|
||||
begin
|
||||
dep = Gem::Dependency.new(name, requirement)
|
||||
spec = Gem.source_index.find { |_,s| s.satisfies_requirement?(dep) }.last
|
||||
spec.activate # a way that exists
|
||||
rescue
|
||||
gem self.name, self.requirement # < 1.8 unhappy way
|
||||
end
|
||||
|
||||
@spec = Gem.loaded_specs[name]
|
||||
@frozen = @spec.loaded_from.include?(self.class.unpacked_path) if @spec
|
||||
@load_paths_added = true
|
||||
@@ -115,18 +125,6 @@ module Rails
|
||||
@spec = s
|
||||
end
|
||||
|
||||
if method_defined?(:requirement)
|
||||
def requirement
|
||||
req = super
|
||||
req unless req == Gem::Requirement.default
|
||||
end
|
||||
else
|
||||
def requirement
|
||||
req = version_requirements
|
||||
req unless req == Gem::Requirement.default
|
||||
end
|
||||
end
|
||||
|
||||
def built?
|
||||
return false unless frozen?
|
||||
|
||||
@@ -272,9 +270,10 @@ module Rails
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
self.name == other.name && self.requirement == other.requirement
|
||||
Gem::Dependency === other.class &&
|
||||
self.name == other.name && self.requirement == other.requirement
|
||||
end
|
||||
alias_method :"eql?", :"=="
|
||||
alias_method :eql?, :"=="
|
||||
|
||||
private
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ module Rails
|
||||
|
||||
def refresh!
|
||||
# reload the installed gems
|
||||
@installed_source_index.refresh!
|
||||
# HACK: I don't think this is needed: @installed_source_index.refresh!
|
||||
vendor_gems = {}
|
||||
|
||||
# handle vendor Rails gems - they are identified by having loaded_from set to ""
|
||||
@@ -101,8 +101,8 @@ module Rails
|
||||
end
|
||||
|
||||
def version_for_dir(d)
|
||||
matches = /-([^-]+)$/.match(d)
|
||||
Gem::Version.new(matches[1]) if matches
|
||||
version = d.split('-').find { |s| s.match(/^\d(\.\d|\.\w+)*$/) }
|
||||
Gem::Version.new(version)
|
||||
end
|
||||
|
||||
def load_specification(gem_dir)
|
||||
|
||||
@@ -2,7 +2,7 @@ module Rails
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 3
|
||||
TINY = 9
|
||||
TINY = 14
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
require 'rdoc/task'
|
||||
|
||||
desc 'Default: run unit tests.'
|
||||
task :default => :test
|
||||
@@ -14,7 +14,7 @@ Rake::TestTask.new(:test) do |t|
|
||||
end
|
||||
|
||||
desc 'Generate documentation for the <%= file_name %> plugin.'
|
||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||
RDoc::Task.new(:rdoc) do |rdoc|
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = '<%= class_name %>'
|
||||
rdoc.options << '--line-numbers' << '--inline-source'
|
||||
|
||||
@@ -46,14 +46,14 @@ namespace :db do
|
||||
$stderr.puts "Couldn't create database for #{config.inspect}"
|
||||
end
|
||||
end
|
||||
return # Skip the else clause of begin/rescue
|
||||
return # Skip the else clause of begin/rescue
|
||||
else
|
||||
ActiveRecord::Base.establish_connection(config)
|
||||
ActiveRecord::Base.connection
|
||||
end
|
||||
rescue
|
||||
case config['adapter']
|
||||
when 'mysql'
|
||||
when /^mysql/
|
||||
@charset = ENV['CHARSET'] || 'utf8'
|
||||
@collation = ENV['COLLATION'] || 'utf8_unicode_ci'
|
||||
begin
|
||||
@@ -159,7 +159,7 @@ namespace :db do
|
||||
task :charset => :environment do
|
||||
config = ActiveRecord::Base.configurations[RAILS_ENV || 'development']
|
||||
case config['adapter']
|
||||
when 'mysql'
|
||||
when /^mysql/
|
||||
ActiveRecord::Base.establish_connection(config)
|
||||
puts ActiveRecord::Base.connection.charset
|
||||
when 'postgresql'
|
||||
@@ -174,7 +174,7 @@ namespace :db do
|
||||
task :collation => :environment do
|
||||
config = ActiveRecord::Base.configurations[RAILS_ENV || 'development']
|
||||
case config['adapter']
|
||||
when 'mysql'
|
||||
when /^mysql/
|
||||
ActiveRecord::Base.establish_connection(config)
|
||||
puts ActiveRecord::Base.connection.collation
|
||||
else
|
||||
@@ -274,7 +274,7 @@ namespace :db do
|
||||
task :dump => :environment do
|
||||
abcs = ActiveRecord::Base.configurations
|
||||
case abcs[RAILS_ENV]["adapter"]
|
||||
when "mysql", "oci", "oracle"
|
||||
when /^mysql/, "oci", "oracle"
|
||||
ActiveRecord::Base.establish_connection(abcs[RAILS_ENV])
|
||||
File.open("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump }
|
||||
when "postgresql"
|
||||
@@ -320,7 +320,7 @@ namespace :db do
|
||||
task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do
|
||||
abcs = ActiveRecord::Base.configurations
|
||||
case abcs["test"]["adapter"]
|
||||
when "mysql"
|
||||
when /^mysql/
|
||||
ActiveRecord::Base.establish_connection(:test)
|
||||
ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0')
|
||||
IO.readlines("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql").join.split("\n\n").each do |table|
|
||||
@@ -354,14 +354,14 @@ namespace :db do
|
||||
task :purge => :environment do
|
||||
abcs = ActiveRecord::Base.configurations
|
||||
case abcs["test"]["adapter"]
|
||||
when "mysql"
|
||||
when /^mysql/
|
||||
ActiveRecord::Base.establish_connection(:test)
|
||||
ActiveRecord::Base.connection.recreate_database(abcs["test"]["database"], abcs["test"])
|
||||
when "postgresql"
|
||||
ActiveRecord::Base.clear_active_connections!
|
||||
drop_database(abcs['test'])
|
||||
create_database(abcs['test'])
|
||||
when "sqlite","sqlite3"
|
||||
when "sqlite", "sqlite3"
|
||||
dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"]
|
||||
File.delete(dbfile) if File.exist?(dbfile)
|
||||
when "sqlserver"
|
||||
@@ -408,7 +408,7 @@ end
|
||||
def drop_database(config)
|
||||
begin
|
||||
case config['adapter']
|
||||
when 'mysql'
|
||||
when /^mysql/
|
||||
ActiveRecord::Base.establish_connection(config)
|
||||
ActiveRecord::Base.connection.drop_database config['database']
|
||||
when /^sqlite/
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
begin
|
||||
require 'rdoc/task'
|
||||
namespace :doc do
|
||||
desc "Generate documentation for the application. Set custom template with TEMPLATE=/path/to/rdoc/template.rb or title with TITLE=\"Custom Title\""
|
||||
Rake::RDocTask.new("app") { |rdoc|
|
||||
RDoc::Task.new("app") { |rdoc|
|
||||
rdoc.rdoc_dir = 'doc/app'
|
||||
rdoc.template = ENV['template'] if ENV['template']
|
||||
rdoc.title = ENV['title'] || "Rails Application Documentation"
|
||||
@@ -12,7 +14,7 @@ namespace :doc do
|
||||
}
|
||||
|
||||
desc "Generate documentation for the Rails framework"
|
||||
Rake::RDocTask.new("rails") { |rdoc|
|
||||
RDoc::Task.new("rails") { |rdoc|
|
||||
rdoc.rdoc_dir = 'doc/api'
|
||||
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
||||
rdoc.title = "Rails Framework Documentation"
|
||||
@@ -86,3 +88,6 @@ namespace :doc do
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue LoadError
|
||||
$stderr.puts 'Please install RDoc 2.4.2+ to generate documentation.'
|
||||
end
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace :rails do
|
||||
begin
|
||||
chdir("vendor/rails") do
|
||||
rails.dependencies.select { |g| deps.include? g.name }.each do |g|
|
||||
Gem::GemRunner.new.run(["unpack", g.name, "--version", g.version_requirements.to_s])
|
||||
Gem::GemRunner.new.run(["unpack", g.name, "--version", g.respond_to?(:requirement) ? g.requirement.to_s : g.version_requirements.to_s])
|
||||
mv(Dir.glob("#{g.name}*").first, g.name)
|
||||
end
|
||||
|
||||
|
||||
@@ -113,6 +113,14 @@ class GemDependencyTest < Test::Unit::TestCase
|
||||
assert_not_nil DUMMY_GEM_C_VERSION
|
||||
assert_equal '0.6.0', DUMMY_GEM_C_VERSION
|
||||
end
|
||||
|
||||
def test_gem_load_frozen_when_platform_string_is_present
|
||||
dummy_gem = Rails::GemDependency.new "dummy-gem-l"
|
||||
dummy_gem.add_load_paths
|
||||
dummy_gem.load
|
||||
assert_not_nil DUMMY_GEM_L_VERSION
|
||||
assert_equal "1.0.0", DUMMY_GEM_L_VERSION
|
||||
end
|
||||
|
||||
def test_gem_load_missing_specification
|
||||
dummy_gem = Rails::GemDependency.new "dummy-gem-d"
|
||||
|
||||
28
railties/test/vendor/gems/dummy-gem-l-1.0.0-x86-mswin32/.specification
vendored
Normal file
28
railties/test/vendor/gems/dummy-gem-l-1.0.0-x86-mswin32/.specification
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
--- !ruby/object:Gem::Specification
|
||||
name: dummy-gem-l
|
||||
version: !ruby/object:Gem::Version
|
||||
version: 1.0.0
|
||||
platform: mswin32
|
||||
authors:
|
||||
- "Nobody"
|
||||
date: 2008-10-03 00:00:00 -04:00
|
||||
files:
|
||||
- lib
|
||||
- lib/dummy-gem-l.rb
|
||||
require_paths:
|
||||
- lib
|
||||
required_ruby_version: !ruby/object:Gem::Requirement
|
||||
requirements:
|
||||
- - ">="
|
||||
- !ruby/object:Gem::Version
|
||||
version: "0"
|
||||
version:
|
||||
required_rubygems_version: !ruby/object:Gem::Requirement
|
||||
requirements:
|
||||
- - ">="
|
||||
- !ruby/object:Gem::Version
|
||||
version: "0"
|
||||
version:
|
||||
requirements: []
|
||||
specification_version: 2
|
||||
summary: Dummy Gem L
|
||||
1
railties/test/vendor/gems/dummy-gem-l-1.0.0-x86-mswin32/lib/dummy-gem-l.rb
vendored
Normal file
1
railties/test/vendor/gems/dummy-gem-l-1.0.0-x86-mswin32/lib/dummy-gem-l.rb
vendored
Normal file
@@ -0,0 +1 @@
|
||||
DUMMY_GEM_L_VERSION="1.0.0"
|
||||
Reference in New Issue
Block a user