Compare commits

...

84 Commits

Author SHA1 Message Date
Justin Collins
a6eb61b7e4 Fix SQL injection via nested hashes in conditions 2012-06-12 23:14:10 -07:00
Ryan Tomayko
fe11782158 Merge remote-tracking branch 'github/rack-1.x' into 2-3-github 2011-11-17 12:57:09 -08:00
Ryan Tomayko
899e99a025 pin to rack ~> 1.1 instead of ~> 1.1.0
Some pretty gnarly bugs and security issues are present in the
latest rack 1.1.x release. There are 1.2.x and 1.3.x releases that
correct these.

This changes the gem dependencies to allow for rack versions > 1.1.
At GitHub we're on 1.2.4 (latest 1.2.x release at present) and
should have some results from real world testing soon.
2011-11-17 12:51:32 -08:00
Aaron Patterson
e0774e4730 fixing utf8 escape vulerability 2011-08-16 14:58:39 -07:00
Aaron Patterson
60f783d9ce fixing strip tags vulnerability 2011-08-16 14:58:13 -07:00
Aaron Patterson
6b46d65597 fixing sql injection problem 2011-08-16 14:57:48 -07:00
Aaron Patterson
fb1588c5ff 2.3.14. yay. :'( 2011-08-16 14:57:05 -07:00
Aaron Patterson
dea5a10f71 bumping to 2.3.13 2011-08-16 14:34:14 -07:00
Aaron Patterson
11dafeaa75 fixing response splitting problem 2011-08-16 14:25:45 -07:00
Aaron Patterson
bb99aa1149 adding notification for rdoc 2011-08-16 14:24:44 -07:00
Aaron Patterson
b132992978 we should not ignore all gems in here 2011-08-04 16:34:20 -07:00
Xavier Noria
78a1fda7c8 contrib app minor tweak 2011-07-27 13:23:42 -07:00
José Valim
8d02083f23 Merge pull request #1740 from Antiarchitect/2-3-stable
Fix OrderedHash merging with block given.
2011-06-17 06:25:39 -07:00
Andrey Voronkov
b1c36b7088 Added tests for OrderedHash merging with block. 2011-06-16 23:56:39 -07:00
Andrey Voronkov
b2d4142fb7 Fix OrderedHash merging with block given. 2011-06-16 16:47:29 -07:00
Brian Cardarella
1aae5e70ef Remove deprecation warning for ActiveRecord::Errors#generate_message. This is the same API that ActiveModel ended up using and that won't be changing. 2011-06-09 14:59:33 -07:00
Aaron Patterson
a2a34133d8 find the spec from the source index, then activate it 2011-06-06 20:22:47 -07:00
Ryan Davis
79aa54d0c7 + Switched to newer rdoc and gem package tasks (and their requires).
+ Fixed deprecated usage in gemspecs.

Bumped the version to 2.3.12 so I could test locally with actual
installs. If this is bad form for this project, please beat me up and
I'll split them out.
2011-05-25 01:49:15 -07:00
Ryan Davis
3ad5fd1879 Removed the bulk of the deprecations by simply not calling refresh.
This may cause problems. I dunno.
The real solution is to get rid of all of this mess and use gem paths properly.
2011-05-12 16:02:41 -07:00
Ryan Davis
4c3725723f Fixed buggy gem activation. Don't pass a dependency to gem, pass the
name and requirement. Better, just activate the spec for the
dependency (1.8 only)
2011-05-12 16:01:56 -07:00
Ryan Davis
c20a4d18e3 Removed buggy GemDependency#requirement override. Overrides should NEVER change the semantics of the parent (returning nil if default). 2011-05-12 16:01:10 -07:00
Ryan Davis
01a9fbbcca Fix broken GemDependency#==. You should ALWAYS check the class! 2011-05-12 16:00:28 -07:00
Ryan Davis
8d4ca9edc6 Fix stupid emacsisms. Just makes things more readable. 2011-05-12 16:00:03 -07:00
José Valim
d793a56121 Merged pull request #198 from robdimarco/2-3-stable.
Patch for issue 6440 - Session Reset undefined method `destroy' for {}:Hash
2011-04-28 00:37:53 -07:00
José Valim
f424efe97f Merged pull request #331 from daphonz/2-3-stable.
Dynamic find_or_create_by_x_and_y always creates new records in Rails 2.3.11
2011-04-28 00:20:15 -07:00
Casey Dreier
9f7ff621bd Fixing dynamic finders on associations to properly send arguments to the find_by_* method. Closes issue #330.
Commit fdfc8e3b9 introduced a bugfix to prevent additional values passed
to a dynamic find_or_create_by_x methods from confusing the finder.
This patch also broke the essential behavior of this method on an
association by incorrectly sending arguments to the find_by_x methods.
The finder method would always see its inputs as a single array of
values instead of individual arguments, almost guaranteeing that the
finder call would be incorrect, and that we'd always create a new
record instead.

This patch adds a splat operator to the parameter array we send along to
the dynamic finder so that it receives its inputs correctly, and
includes an additional test to ensure that repeated calls to
find_or_create_by_x only creates one new record.
2011-04-27 21:57:24 -04:00
gmarik
b0be721dd9 respect :expire_after option
- it was broken after
[commit](e0eb8e9c65)
- there's also
[issue](https://rails.lighthouseapp.com/projects/8994/tickets/6634-railsrack-inconsistency-about-expires_afterexpires-cookie-option)

- also: maybe it worth making Rack understand :expire_after as we
duplicate same logic in [cookie_store](https://github.com/gmarik/rails/blob/v2.3.11/actionpack/lib/action_controller/session/cookie_store.rb#L114)

Signed-off-by: José Valim <jose.valim@gmail.com>
2011-04-14 13:48:35 +02:00
Rob Di Marco
8ca8ac379d Fixed bug 6440 by checking that destroy exists on the session 2011-02-28 22:54:03 -05:00
Rob Di Marco
589ce09564 Unit test that shows calling reset session twice results in an exception 2011-02-28 22:53:36 -05:00
Vijay Dev
6c42c142e2 fix incorrect version in deprecation message
Signed-off-by: Santiago Pastorino <santiago@wyeworks.com>
2011-02-20 13:32:18 -02:00
Aaron Patterson
abc06a2f76 rubygems 1.5.0 compatibility. Thanks Tim Serong 2011-02-09 15:24:14 -08:00
Michael Koziarski
b0c3d451a2 Prepare for the 2.3.11 release 2011-02-09 09:30:53 +13:00
Michael Koziarski
7e86f9b4d2 Change the CSRF whitelisting to only apply to get requests
Unfortunately the previous method of browser detection and XHR whitelisting is unable to prevent requests issued from some Flash animations and Java applets.  To ease the work required to include the CSRF token in ajax requests rails now supports providing the token in a custom http header:

 X-CSRF-Token: ...

This fixes CVE-2011-0447
2011-02-09 09:20:17 +13:00
Michael Koziarski
abe97736b8 Be sure to javascript_escape the email address to prevent apostrophes inadvertently causing javascript errors.
This fixes CVE-2011-0446
2011-02-09 09:20:16 +13:00
Aaron Patterson
7e0f60d2ed fixing invalid yaml [#4418 state:resolved]
Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2011-02-01 13:30:58 -08:00
Jamis Buck
3afa5385c9 Revert "make TestCaseTest work for pre-1.9 rubies, too"
This reverts commit 8378a44ff9.
2011-01-19 15:57:16 -07:00
Jamis Buck
c545331f9e Revert "scrub instance variables from test cases on teardown"
This reverts commit b5cf2b4b82.
2011-01-19 15:57:16 -07:00
Jamis Buck
cd0ecff00b Revert "rein in GC during tests by making them run (at most) once per second"
This reverts commit a0c761dc6b.
2011-01-19 15:57:14 -07:00
Jamis Buck
a0c761dc6b rein in GC during tests by making them run (at most) once per second
this can provide a significant performance boost during testing, by
preventing the GC from running too frequently.
2011-01-19 10:27:53 -07:00
Jamis Buck
b5cf2b4b82 scrub instance variables from test cases on teardown
this prevents test state from accumulating, resulting in leaked
objects and slow tests due to overactive GC.
2011-01-19 10:12:18 -07:00
Jamis Buck
8378a44ff9 make TestCaseTest work for pre-1.9 rubies, too 2011-01-19 10:08:02 -07:00
Johnathan Ritzi
4f0c8ef9f1 Fix doc for #check_box [#6311 state:resolved]
Signed-off-by: Xavier Noria <fxn@hashref.com>
2011-01-19 08:47:19 +01:00
Jeremy Kemper
bc302f2aec Revert "use Object#class instead of Object#type"
This reverts commit 08d94d3f7e.
2011-01-10 14:14:25 -08:00
Tomasz Pajor
08d94d3f7e use Object#class instead of Object#type 2011-01-09 15:12:25 -08:00
Mikel Lindsaar
10ec012f58 Updating documentation on ActionMailer base to show a multipart email with attachments 2011-01-02 11:13:44 +11:00
Mikel Lindsaar
92fd824480 Correcting actionmailer guide for Rails 2.3 2011-01-02 11:08:44 +11:00
Michael Koziarski
6d916329b8 Require thread explicitly rather than relying on rubygems to do it. 2010-12-20 11:16:55 +13:00
Michael Koziarski
84465a2cc1 Revert "In nested_attributes when association is not loaded and association record is saved then in memory record attributes should be saved"
This reverts commit 12bbc34aca.

It caused errors when combined with attr_accessible, piggy back attributes fetched by :select, etc.  Leaving it in 3.0, but removing from 2.3
2010-12-08 09:48:54 +13:00
Will Bryant
0fee359278 Don't add non-new records back to the target array after loading targets on associations, as that makes destroy_all destroy any created records that don't match the scope destroy_all is called on
Signed-off-by: Michael Koziarski <michael@koziarski.com>
2010-12-08 09:48:16 +13:00
Pascal Friederich
e0eb8e9c65 Let Rack::Utils.set_cookie_header! create the Set-Cookie header instead of manually fiddling with the response headers [#4941 state:resolved]
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-12-01 11:58:45 +01:00
José Valim
2826324e56 Revert "Fix AbstractStore so that it preserves Set-Cookie header as an array, rather than as newline separated strings"
This reverts commit 36b91e34f4.

Conflicts:

	actionpack/test/activerecord/active_record_store_test.rb
2010-12-01 11:48:31 +01:00
Alexandru Catighera
1681ede605 Fix ActiveRecord calculations when grouped by multiple fields 2010-11-16 11:06:49 -08:00
Tom Stuart
44db47c63e Backport BlankSlate removal from ActiveSupport::BasicObject [#5911 state:resolved]
This is a backport of dd15a3fee0.

Signed-off-by: Andrew White <andyw@pixeltrix.co.uk>
2010-11-03 11:03:38 +00:00
Andrew White
25139ac92c Don't write out secure cookies unless the request is secure 2010-10-27 15:04:29 +01:00
Andrew White
0e52a609fd Don't create a deprecation proxy object if the variable was passed in local_assigns [#1671 state:resolved] 2010-10-26 12:57:21 +01:00
Aaron Patterson
df78de2bc8 removing space errors 2010-10-21 10:30:18 -07:00
Omar Qureshi
36b91e34f4 Fix AbstractStore so that it preserves Set-Cookie header as an array, rather than as newline separated strings 2010-10-21 10:28:54 -07:00
toby cabot
bdfddb09d7 bug 1108: yield to block provided to find_or_create_by_x
Starting in 2.3.8 we stopped yielding to blocks passed in to
find_or_create_by_x methods.  This patch restores that behavior and
adds a case to test it.
2010-10-20 17:23:54 -07:00
toby cabot
fdfc8e3b9c bug 1108: fix a bug with find_or_create_by and additional values
There was a bug with find_or_create_by_x introduced in 2.3.9 - if you
included extra parameters for the create() then those parameters would
confuse the find() so you'd never get to the create().  This patch
filters the parameters so we only pass to find() the subset that it's
interested in.  The code for the filtering was modelled on the code in
base.rb's method_missing().
2010-10-20 17:23:54 -07:00
Michael Koziarski
f5ed5c317e Prepare for the 2.3.10 release 2010-10-15 08:41:59 +13:00
Michael Koziarski
96183e0f28 Revert 7d2173ec5c which introduced a security vulnerability.
This addresses  CVE-2010-3933
2010-10-15 08:30:34 +13:00
Geoff Buesing
f2e32e4fd7 require 'uri' in action_controller/url_rewriter [#5555 state:resolved]
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-10-12 00:58:29 +02:00
Aaron Patterson
8beb84fa33 calling correct method on minitest for test name when teardown callback fails 2010-10-04 09:29:37 -07:00
Aaron Patterson
a448e74661 [#5406 state:resolved] calling the correct method on minitest to obtain the test name 2010-10-04 09:28:21 -07:00
Aaron Patterson
fb526a0470 fixing space errors 2010-09-30 10:29:46 -07:00
Marcelo Giorgi
96c19ff7cc AssociationCollection#include? working properly for objects added with build method [#3472 state:resolved] 2010-09-30 10:28:25 -07:00
Marcelo Giorgi
9b78af95be Remove duplication of conditions generated for associations when used in conjunction with named_scopes [#4634 state: resolved] 2010-09-30 09:04:04 -07:00
Emilio Tagua
5a63df211d Add examples to performance script that were included in version 3. 2010-09-30 09:54:01 -03:00
Emilio Tagua
1851596db5 Use detect instead select to avoid sh [..] command not found. 2010-09-30 09:52:47 -03:00
Marcelo Giorgi
0665182950 Preserving :include options for hmt association with an order but without conditions [#5262 state:resolved] 2010-09-28 09:56:10 -07:00
Ryan Wallace
515917f5d8 Add test to demonstrate failure with eager loading hmt where the association has an order. 2010-09-28 09:54:32 -07:00
Étienne Barrié
bc52d81306 Fix add_index with a symbol #4891 2010-09-27 10:26:01 -07:00
Michael Koziarski
dbbf2fd19c Revert "Makes form_helper use overriden model accessors backport"
This change introduced breakages and test failures.

This reverts commit 8141f0894e.
2010-09-27 12:20:54 +13:00
Andrew Kaspick
9476d628a3 memoized protected methods should remain protected
Signed-off-by: Michael Koziarski <michael@koziarski.com>
2010-09-27 11:50:54 +13:00
Colin Casey
7240e8af6a Fix for imposed version number as last part of gem directory name for frozen gems
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-09-24 13:16:51 +02:00
Colin Casey
f2990620d7 Test for imposed version number as last part of gem directory name for frozen gems
[#4295 state:resolved]

Signed-off-by: José Valim <jose.valim@gmail.com>
2010-09-24 13:16:50 +02:00
W. Andrew Loe III
17f2fb44c0 Only send secure cookies over SSL. 2010-09-14 11:52:40 -07:00
Emilio Tagua
8c049c6b20 Add more examples in performance script.
[#5610 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2010-09-10 13:15:46 -07:00
Jeremy Kemper
761c9cd5db Ruby 1.9 compat: convert Pathname to string 2010-09-10 12:23:41 -07:00
Andrew Kaspick
a159fd0b8c Fix fixtures in integration test sessions
Signed-off-by: Michael Koziarski <michael@koziarski.com>
2010-09-10 10:45:23 +12:00
Erik Michaels-Ober
e8b84ab1b4 Add support for mysql2 adapter 2010-09-10 02:13:56 +08:00
Erik Michaels-Ober
383ea02e38 Fix typo in deprecation warning
Object#returning should be Kernel#returning
2010-09-10 02:13:56 +08:00
Mikel Lindsaar
597fb1da94 Adding documentation to redirect_to and status code option references 2010-09-09 14:00:09 +10:00
Mislav Marohnić
c6e33d30c1 fix setting session cookie with activerecord and memcache store
Commit f8f3653 broke setting the session ID cookie for requests without 'HTTP_COOKIE' header
when using activerecord or memcache store. Integration tests didn't catch this because they
always set the HTTP_COOKIE header for mock requests, so now this is changed to only set the
header if there are cookies.

[#5581 state:committed]

Signed-off-by: Santiago Pastorino <santiago@wyeworks.com>
2010-09-08 12:59:48 -03:00
98 changed files with 1034 additions and 469 deletions

1
.gitignore vendored
View File

@@ -1,4 +1,3 @@
*.gem
pkg
.bundle
debug.log

View File

@@ -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"

View File

@@ -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)*

View File

@@ -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

View File

@@ -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
#

View File

@@ -2,7 +2,7 @@ module ActionMailer
module VERSION #:nodoc:
MAJOR = 2
MINOR = 3
TINY = 9
TINY = 14
STRING = [MAJOR, MINOR, TINY].join('.')
end

View File

@@ -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]

View File

@@ -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

View File

@@ -31,7 +31,7 @@ rescue LoadError
end
end
gem 'rack', '~> 1.1.0'
gem 'rack', '~> 1.1'
require 'rack'
require 'action_controller/cgi_ext'

View File

@@ -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?

View File

@@ -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:

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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)

View File

@@ -1,6 +1,6 @@
begin
require_library_or_gem 'memcache'
require 'thread'
module ActionController
module Session
class MemCacheStore < AbstractStore

View File

@@ -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.

View File

@@ -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

View File

@@ -2,7 +2,7 @@ module ActionPack #:nodoc:
module VERSION #:nodoc:
MAJOR = 2
MINOR = 3
TINY = 9
TINY = 14
STRING = [MAJOR, MINOR, TINY].join('.')
end

View File

@@ -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

View File

@@ -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:

View 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

View File

@@ -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

View File

@@ -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>"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"))

View File

@@ -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

View File

@@ -1,4 +1,5 @@
require 'abstract_unit'
require 'thread'
class ReloaderTests < ActiveSupport::TestCase
Reloader = ActionController::Reloader

View File

@@ -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

View File

@@ -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&lt;=?"/>), @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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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")

View File

@@ -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=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">&#109;&#101;&#40;&#97;&#116;&#41;&#100;&#111;&#109;&#97;&#105;&#110;&#46;&#99;&#111;&#109;</a>", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)")
assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%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=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">&#109;&#101;&#40;&#97;&#116;&#41;&#100;&#111;&#109;&#97;&#105;&#110;&#40;&#100;&#111;&#116;&#41;&#99;&#111;&#109;</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?

View File

@@ -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'

View File

@@ -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)*

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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 ')

View File

@@ -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)

View File

@@ -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

View File

@@ -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:

View File

@@ -162,7 +162,7 @@ module ActiveRecord
end
def quote_column_name(name) #:nodoc:
%Q("#{name}")
%Q("#{name.to_s.gsub('"', '""')}")
end

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -2,7 +2,7 @@ module ActiveRecord
module VERSION #:nodoc:
MAJOR = 2
MINOR = 3
TINY = 9
TINY = 14
STRING = [MAJOR, MINOR, TINY].join('.')
end

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)}

View File

@@ -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?

View File

@@ -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]

View File

@@ -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" })

View File

@@ -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'

View File

@@ -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?

View File

@@ -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

View File

@@ -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

View File

@@ -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)*

View File

@@ -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

View File

@@ -2,7 +2,7 @@ module ActiveResource
module VERSION #:nodoc:
MAJOR = 2
MINOR = 3
TINY = 9
TINY = 14
STRING = [MAJOR, MINOR, TINY].join('.')
end

View File

@@ -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}.

View File

@@ -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

View File

@@ -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

View File

@@ -1,3 +1,5 @@
require 'thread'
module ActiveSupport
# Inspired by the buffered logger idea by Ezra
class BufferedLogger

View File

@@ -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

View File

@@ -19,7 +19,7 @@ class ERB
if s.html_safe?
s
else
s.gsub(/[&"><]/) { |special| HTML_ESCAPE[special] }.html_safe
s.to_s.gsub(/&/, "&amp;").gsub(/\"/, "&quot;").gsub(/>/, "&gt;").gsub(/</, "&lt;").html_safe
end
end

View File

@@ -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:

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -2,7 +2,7 @@ module ActiveSupport
module VERSION #:nodoc:
MAJOR = 2
MINOR = 3
TINY = 9
TINY = 14
STRING = [MAJOR, MINOR, TINY].join('.')
end

View File

@@ -1,4 +1,5 @@
require 'abstract_unit'
require 'thread'
class SynchronizationTest < Test::Unit::TestCase
def setup

View File

@@ -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 + "&lt;"
assert_equal expected, ERB::Util.html_escape(string)
end
def test_pluralize
SingularToPlural.each do |singular, plural|
assert_equal(plural, singular.pluralize)

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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]

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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)

View File

@@ -2,7 +2,7 @@ module Rails
module VERSION #:nodoc:
MAJOR = 2
MINOR = 3
TINY = 9
TINY = 14
STRING = [MAJOR, MINOR, TINY].join('.')
end

View File

@@ -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'

View File

@@ -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/

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View 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

View File

@@ -0,0 +1 @@
DUMMY_GEM_L_VERSION="1.0.0"