Compare commits

..

200 Commits

Author SHA1 Message Date
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
Jeremy Kemper
a61a39ecd4 Rails 2.3.9 2010-09-04 14:36:40 -07:00
Ken Collins
b64d1fe637 Conversion of a two dimensional array that is ruby 1.8.6 safe. Fix paren warnings too.
Signed-off-by: Michael Koziarski <michael@koziarski.com>
2010-09-03 14:38:28 +12:00
Jeremy Kemper
6f17422ca7 require 'thread' for Mutex dependency 2010-08-31 16:38:52 -07:00
Mikel Lindsaar
bac12fa5fc Adding option to ActiveResource to allow you to not reset the previously stored requests and responses by passing false to respond_to
Backport of commit 2a1b23f851ea3d4634fc68b74fe6b1afed23d3ef on rails/master
2010-08-29 22:55:02 -07:00
Mikel Lindsaar
56fdfeb265 Back porting HttpMock test from Rails 3 master 2010-08-29 22:55:02 -07:00
Mikel Lindsaar
881712cf50 Updating documentation on ActiveResource HTTP Mock and also adding test coverage 2010-08-29 22:55:02 -07:00
Jeremy Kemper
b2c91983dc Prepare for Rails 2.3.9. Release 2.3.9.pre gems. 2010-08-29 20:19:05 -07:00
Jeremy Kemper
bdace5d6aa Exclude guides from gem to keep file size small 2010-08-29 17:54:13 -07:00
Mikel Lindsaar
0fcb4302e1 Make ActiveResource::InvalidRequestError more user friendly
Signed-off-by: Xavier Noria <fxn@hashref.com>
2010-08-25 10:11:06 +02:00
Xavier Noria
11361a9e79 restores railties/README as home page of the API 2010-08-20 04:19:34 +02:00
Xavier Noria
add3ccbca6 revises guides generation 2010-08-20 04:13:15 +02:00
Jeff Lawson
d35a67bba3 Bug Fix -- clean up connection after stored procedure [#3151 state:resolved] for 2-3-stable 2010-08-18 08:33:47 -07:00
Jeff Lawson
7e79889d1c Bug Fix -- clean up connection after stored procedure [#3151 state:resolved] for 2-3-stable 2010-08-18 08:33:47 -07:00
Santiago Pastorino
43e2bbe28e Making time_zone_options_for_select return a html_safe string master backport 2010-08-15 10:07:38 -03:00
Michael Koziarski
b154b97ea4 Revert "Ruby 1.9.2: explicitly raise NoMethodError for attempts at explicit coercion"
This reverts commit 64082b350c.

This change broke compatibility with 1.8.6 and was only needed for older 1.9.2 versions

Conflicts:

	activerecord/lib/active_record/attribute_methods.rb
2010-08-11 10:53:06 +12:00
Xavier Noria
15cafbe267 it is no longer true that load_paths are going to be removed in final 2010-08-05 00:04:43 +02:00
Subba Rao Pasupuleti
12bbc34aca In nested_attributes when association is not loaded and association record is saved then in memory record attributes should be saved
[#5053 state:resolved]

Signed-off-by: José Valim <jose.valim@gmail.com>
2010-08-03 10:56:51 +02:00
Santiago Pastorino
8141f0894e Makes form_helper use overriden model accessors backport
[#3374]
2010-08-01 19:49:45 -03:00
Leigh Caplan
27651c1fad Test to ensure that falsy objects aren't wrapped by deprecation proxies 2010-07-26 09:54:26 -07:00
Leigh Caplan
a9ef2fd56c Override new on proxy objects so that they never wrap nil or false. 2010-07-26 09:54:26 -07:00
Santiago Pastorino
ae63d5c90d Changes Object#returning with Object#tap on guides 2010-07-25 17:10:37 -07:00
Santiago Pastorino
6f3896751a Changelog update for Object#responding deprecation
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-07-25 23:08:50 +02:00
Santiago Pastorino
5b0f839054 Deprecates Object#returning in favor of Object#tap
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-07-25 22:49:10 +02:00
Santiago Pastorino
a5d8c95a7c Changes the usage of Object#returning with Object#tap
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-07-25 22:49:06 +02:00
Subba Rao Pasupuleti
dec2c4f4e3 renaming test name to fix accidently override [#5076 state:resolved]
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-07-18 11:20:25 +02:00
Neeraj Singh
99cdea7cbe update_attribute and updated_attributes! are now wrapped in a transaction
[#922 state:resolved]

Signed-off-by: José Valim <jose.valim@gmail.com>
2010-07-18 11:20:25 +02:00
Aaron Patterson
c2d13a9a53 changing fixtures back to superclass_delegating_accessor until we can convert them to class_attributes 2010-07-17 18:57:24 -07:00
Jon Yurek
fb615cd7fd Fix for integration tests not serializing arrays in multipart forms correctly.
Signed-off-by: wycats <wycats@gmail.com>
2010-07-17 13:01:50 -05:00
Aaron Patterson
4ae4828953 backporting a couple missing files. sorry folks! 2010-07-16 08:41:53 -07:00
Michael Koziarski
f57ca87729 Only skip eager loading the code if dependency_loading is still enabled.
Otherwise rake tasks which depend on environment will get errors about missing constants.
2010-07-16 17:06:32 +12:00
Aaron Patterson
7b6383f263 fixing performance regression from 2.3.5 -> 2.3.8 2010-07-15 15:59:19 -07:00
Michael Lovitt
257a29d3cc Sessions should not be created until written to and session data should be destroyed on reset. [#4938 state:resolved]
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-07-14 08:04:37 +02:00
Jacob Lewallen
8298bef72e Set destroyed=true in opt locking's destroy [#5058 state:resolved]
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-07-14 08:03:02 +02:00
Mike Breen
046c900df2 A generated plugin's test are not run by 'rake test'
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-07-08 23:08:22 +02:00
Ken Collins
504f7cfbb3 Fix the #using_limitable_reflections? helper to work correctly by not examining the length of an array which contains false/true, hence always passing.
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-07-08 23:08:11 +02:00
Grant Ammons
0963774c0a fixes #2362, eager loading :through associations will join the :source model if there are :conditions
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-07-08 23:08:05 +02:00
Mislav Marohnić
2d3bc99b0d test that ActiveRecord destroy and destroy_all return destroyed records
Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2010-07-08 11:35:56 -07:00
Mislav Marohnić
ba9c469113 add missing require to ActiveRecord "base_test.rb"
Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2010-07-08 11:35:46 -07:00
José Valim
bfbdeeae30 Use bind instead of instance_exec cause it may be causing memory leaks. Also, provide a simpler and sane implementation for scoped. [#5044 state:resolved] 2010-07-04 16:19:40 +02:00
Aaron Patterson
67e18c523c fisting Session::AbstractStore#clear to actually clear the session. [#5030 state:resolved]
Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2010-07-01 14:47:28 -07:00
James Le Cuirot
526f1e5f15 Don't remove scheduled destroys when loading an association.
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-07-01 01:07:24 +02:00
Jan Berkel
f8f4872fcc Backported patch from [#4762]
URL fragments should not have safe characters escaped. Ref: Appendix A,
  http://tools.ietf.org/rfc/rfc3986.txt

Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-30 13:27:28 +02:00
Aaron Patterson
fad166c152 AssociationCollection#create_by_*, find_or_create_by_* work properly now. [#1108 state:resolved]
Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2010-06-29 16:33:12 -07:00
Leigh Caplan
78e4d88c70 Rewrite the clause to pluck the existing value from zones_map before performing a lookup. [#4942 state:resolved]
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-29 17:54:18 +02:00
David Trasbo
ac42e6951f Deprecate ActiveRecord::Base#class_name [#379 state:committed]
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-29 17:54:05 +02:00
David Trasbo
d0d10f51d7 Only tell users that the Rails gem is missing if it's actually the case [#2901 state:committed]
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-29 17:53:57 +02:00
Prem Sichanugrist
69c4e4ce65 Fix [54a5088cd5] where the i18n gem was wrongly updated to 0.4.1.
I've tested and confirm that `2-3-stable` will use the vendored `i18n` gem if there's no `i18n` gem with version >= 0.4.1 installed

Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-29 17:53:48 +02:00
Leigh Caplan
80473e035a test that unknown zones don't store mapping keys
[#4942]

Signed-off-by: Santiago Pastorino <santiago@wyeworks.com>
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-28 22:20:34 +02:00
Santiago Pastorino
70af7efa16 Don't store incorrect values in zones_map backport
[#4942 state:committed]

Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-28 22:20:34 +02:00
Evgeniy Dolzhenko
56b35afbdd Add module_eval missing file_name and line_number args
[#4712 state:resolved]

Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-27 08:49:54 +02:00
José Valim
0e9190c902 Tidy up tests in previous commit since they did not assure an OrderedHash is returned (the test would pass for an array and would pass by chance for hashes).
[#4875 state:resolved]
2010-06-26 18:16:09 +02:00
chaitanyav
449cf50d85 Add OrderedHash#invert to preserve order in ruby 1.8
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-26 18:13:45 +02:00
Paul Mucur
05defcd63a Alias ActiveSupport::OrderedHash#update to ActiveSupport::OrderedHash.merge!
This ensures that an OrderedHash's keys are set up appropriately when using update.

[#4973 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2010-06-25 15:18:39 -07:00
Prem Sichanugrist
f8f365346e Make sure that Rails doesn't resent session_id cookie over and over again if it's already there [#2485 state:resolved]
This apply to only Active Record store and Memcached store, as they both store only the session_id, which will be unchanged, in the cookie.

Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-25 09:47:56 +02:00
Xavier Noria
4a745ca670 deprecates load_(once_)paths in dependencies and app config in favor of autolaod_(once_)paths 2010-06-24 00:00:45 +02:00
José Valim
68bfd8a392 Use size for Ruby 1.8.6 compatibility. 2010-06-23 17:53:02 +02:00
Neeraj Singh
549b2ad77c fixes to the tests for patch #4909
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-23 17:34:45 +02:00
kane
09a23d2290 quote scoped columns in validates_uniqueness_of [#4909 state:resolved]
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-23 17:34:40 +02:00
George Montana Harkin
7d2173ec5c Fixes #2415 by creating a new instance of the Model when saving attributes to that model and the associated attributes already exist. Tests included. [#2415 state:resolved]
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-23 17:31:22 +02:00
Neeraj Singh
cc53229378 Fragment cache not generating the proper cache key in log
[#4827 state:resolved]

Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-23 09:11:48 +02:00
Maxim Chernyak
844da12ba6 Fix eager loading of polymorphic has_one associations nested-included under polymorphic belongs_to associations. [#3233 state:resolved]
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-23 09:09:46 +02:00
Neeraj Singh
a9c69f3bb0 test for #1570
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-23 09:08:55 +02:00
Paweł Kondzior
687d7f52c4 STI should identify itself inside named_scope
[#1570 state:resovled]

Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-23 09:07:46 +02:00
Michael Koziarski
cbf36cf57c Revert "make text_field and hidden_field omit the value attribute if the developer explicitly passes in :value => nil [#4839 state:reopened]"
This reverts commit 52c922fad1
2010-06-23 16:54:05 +12:00
Michael Koziarski
52c922fad1 make text_field and hidden_field omit the value attribute if the developer explicitly passes in :value => nil [#4839 state:resolved]
Signed-off-by: Michael Koziarski <michael@koziarski.com>

Conflicts:

	actionpack/lib/action_view/helpers/form_helper.rb
2010-06-23 16:25:19 +12:00
Jeff Dean
da93d69bcb remove_column should raise an ArgumentError when no columns are passed [#4803 state:resolved]
Signed-off-by: Michael Koziarski <michael@koziarski.com>
2010-06-23 16:23:03 +12:00
Jeremy Kemper
e703fc101b CI: add i18n gem 2010-06-22 14:11:14 -07:00
Jesse Storimer
85b6d79d8a CookieStore should preserve the Set-Cookie header Array [#4743 state:resolved]
Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2010-06-22 11:25:32 -07:00
Prem Sichanugrist
5ed6a8447b Change all i18n interpolations from {{...}} to %{...}
This will silent all warning if there's a i18n version 0.4.x gem install on user's machine.

[#4913 state:resolved]

Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-21 01:14:36 +02:00
Prem Sichanugrist
54a5088cd5 Update bundled i18n gem to 0.4.1 to make sure every project will be warn about using deprecated %{..} interpolation.
This will also make sure that by changing {{..}} into %{..} won't break any Rails 2.3.x application, since it would load the vendored version if it's not satisfy the version requirement.

Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-21 01:13:37 +02:00
James Le Cuirot
08302d2feb When not overwriting unsaved updates in nested attributes, allow already-saved records to be refreshed.
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-20 00:33:39 +02:00
Maxime RETY
c7e875abdb Fix Yajl backend discovery in ActiveSupport::JSON
[#4897]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2010-06-18 20:12:16 -07:00
Master Lambaster
1ac00a6844 Fix test which prevents connection reset on failing and remove hardcoded connection
[#4689 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2010-06-18 15:28:56 -07:00
Pratik Naik
e4accdec0c Fix AR perf script 2010-06-10 00:00:17 +05:30
James Le Cuirot
b41c3ba154 Don't overwrite unsaved updates when loading an association but preserve the order of the loaded records. [#4642 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2010-06-09 18:20:15 +05:30
Prem Sichanugrist
0f44d37d04 Make sure that rails recognized the full notation of IPv6 loopback address, and recognize 127.0.0.0/8 in IPv4
[#3257 state:resolved]

Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-08 19:47:18 +02:00
Xavier Noria
ed8cabcec2 deprecates Array#random_element in favor of Array#sample, backported from Ruby 1.9, thanks to Marc-Andre Lafortune 2010-06-05 02:11:33 +02:00
Andrew
3d6ed50187 Don't rewrap system level exceptions with StatementInvalid
Signed-off-by: Michael Koziarski <michael@koziarski.com>
[#896 state:committed]
2010-05-29 16:00:54 +12:00
Michael Koziarski
b760d699a8 Merge commit 'mislav/counter_cache_2-3-stable' into 2-3-stable 2010-05-29 14:05:31 +12:00
Michael Koziarski
5796a92433 Merge commit 'mislav/auto_link_2-3-stable' into 2-3-stable 2010-05-29 14:05:21 +12:00
Santiago Pastorino
b1a97a4998 removes an unneeded alias
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-05-26 16:10:31 +02:00
Jeremy Kemper
a815f0c5a3 Shift SafeBuffer#concat responsibility over to rails_xss 2010-05-25 14:27:40 -07:00
Jeremy Kemper
9da7ff8842 Bump 2-3-stable to 2.3.9 2010-05-25 09:50:34 -07:00
Xavier Noria
2ed893bdce get railties/README back to the home page of the API 2010-05-25 17:18:50 +02:00
Santiago Pastorino
240f4e944c SQLite: forward compatibility with future driver releases
[#4633]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2010-05-24 20:53:26 -07:00
Jeremy Kemper
f7e27bd078 i18n: t() handles single keys returning an Array, also 2010-05-24 20:41:28 -07:00
Jeremy Kemper
6a9e188c0c HTML safety: fix textarea with nil content 2010-05-24 20:13:07 -07:00
Jeremy Kemper
aa449141b4 Work around strange Ruby 1.9 autoload issue by using absolute load paths for tests (for Active Model too) 2010-05-24 16:18:29 -07:00
Santiago Pastorino
a9032c885f Error messages for asserts
Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2010-05-24 16:05:39 -07:00
Jeremy Kemper
e8ba5265e0 Work around strange Ruby 1.9 autoload issue by using absolute load paths for tests (ditto for other components' tests) 2010-05-24 16:05:34 -07:00
José Valim
50f3754525 Ensure translations work with symbols. 2010-05-24 23:38:49 +02:00
Santiago Pastorino
4986d5ed04 translate helper method using an array is deprecated
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-05-24 23:38:48 +02:00
Jeremy Kemper
4fef5af9c3 2.3.7.pre1: fixes HTML escaping when *not* using rails_xss 2010-05-24 14:02:38 -07:00
Jeremy Kemper
e5af56abfe Add global gem task 2010-05-24 13:48:13 -07:00
Jeremy Kemper
9d3bd87045 Work around strange Ruby 1.9 autoload issue by using absolute load paths for tests 2010-05-24 12:16:33 -07:00
Santiago Pastorino
6b0616d1b8 translation method of TranslationHelper module returns a SafeBuffer Array backport
[#4675 state:committed]

Signed-off-by: José Valim <jose.valim@gmail.com>
2010-05-24 20:56:44 +02:00
Santiago Pastorino
d3da1a2c66 Revert "translation method of TranslationHelper module returns always SafeBuffer [#4194 status:resolved]"
This reverts commit 2310aef29b.

Signed-off-by: José Valim <jose.valim@gmail.com>
2010-05-24 20:56:44 +02:00
wycats
8e6a044b2b Needs to work on 1.8 too 2010-05-24 11:29:12 -07:00
wycats
50b7c0c104 Give the ERB String the encoding of the original template 2010-05-24 11:18:20 -07:00
Jeremy Kemper
c66013e2c5 Fix that captured content (e.g. with form_for or div_for) would be HTML-escaped even without the rails_xss plugin installed. Rails 2.3.7, we barely knew ya... 2010-05-24 09:12:00 -07:00
Lance Ivy
9e08e196fa Ensure auto_link does not ignore multiple trailing punctuations
[#2504 state:resolved]
2010-05-24 11:47:36 +02:00
Mislav Marohnić
17b4fd25e4 avoid auto_linking already linked emails; more robust detection of linked URLs
References #1523  [#1862 state:resolved]  [#3591 state:resolved]

Add test that shows how link text can contain HTML if needed:
the trick is using block form in combination with `raw`.
Let link text be automatically HTML-escaped

[#2017 state:resolved]
2010-05-24 11:18:20 +02:00
Mislav Marohnić
bd9ca9aed0 auto_link: support arbitrary URI schemes like "ftp:" and "file:"
recognizes all URI scheme allowed characters, such as colon and period.

[#3494 state:resolved]
2010-05-24 11:18:20 +02:00
Jeremy Kemper
f97da34b4f Bump 2-3-stable to 2.3.8 2010-05-24 01:39:19 -07:00
Jeremy Kemper
326188c25e 2.3.7 release: fix rails_xss compatibility 2010-05-24 00:04:41 -07:00
Jeremy Kemper
60e82a3ca2 Move tests for deprecated String#html_safe! to plugin 2010-05-23 22:07:47 -07:00
Jeremy Kemper
3ff921a65a rails_xss handles deprecated String html safety, when installed 2010-05-23 21:30:43 -07:00
Santiago Pastorino
b10bf834b7 Make use of safe_concat on TextHelper concat
Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2010-05-23 16:51:52 -07:00
Jeremy Kemper
86f0287993 Revert "Don't always mark the argument to #concat as HTML-safe."
This reverts commit e53791f8c0.
2010-05-23 16:51:47 -07:00
Jeremy Kemper
ab2d7c8b5d Use a non-XSS-protected output buffer for view tests 2010-05-23 16:21:44 -07:00
Jeremy Kemper
ca5f5d97b9 Fix test rendering unmarked but safe HTML 2010-05-23 15:28:40 -07:00
Nathan Weizenbaum
e3f14d12cd Don't incompatibly monkeypatch ERB.
Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2010-05-23 15:02:24 -07:00
Nathan Weizenbaum
e53791f8c0 Don't always mark the argument to #concat as HTML-safe.
Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2010-05-23 15:00:14 -07:00
Nathan Weizenbaum
48fbe7b0d8 Mark all raw HTML being concatted as HTML-safe.
Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2010-05-23 14:57:45 -07:00
Jeremy Kemper
55e88eeee4 Bump 2-3-stable to 2.3.7 2010-05-23 01:39:45 -07:00
Mislav Marohnić
8be3e09fcf fix reset_counters to work even with complex class names
e.g. it guesses that a belongs_to association to Namespace::MyModel is
named "my_model", unlike before where it would look up an association
named "namespace::mymodel" and fail.
2010-04-17 03:13:13 +02:00
Mislav Marohnić
ef0591efc2 cleanup update/reset_counters; refactor tests 2010-04-17 03:13:00 +02:00
263 changed files with 4125 additions and 1721 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']
@@ -12,7 +12,7 @@ end
desc 'Run all tests by default'
task :default => :test
%w(test rdoc pgem package release).each do |task_name|
%w(test rdoc pgem package release gem).each do |task_name|
desc "Run #{task_name} task for all projects"
task task_name do
PROJECTS.each do |project|
@@ -23,13 +23,15 @@ 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"
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.options << '-A cattr_accessor=object'
rdoc.options << '--charset' << 'utf-8'
rdoc.options << '--main' << 'railties/README'
rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : './doc/template/horo'

View File

@@ -1,3 +1,12 @@
*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)*
* Version bump.
*2.3.6 (May 23, 2010)*
* Upgrade TMail from 1.2.3 to 1.2.7. [Mikel Lindsaar]

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.6' + 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
#
@@ -278,7 +311,7 @@ module ActionMailer #:nodoc:
@@raise_delivery_errors = true
cattr_accessor :raise_delivery_errors
superclass_delegating_accessor :delivery_method
class_attribute :delivery_method
self.delivery_method = :smtp
@@perform_deliveries = true

View File

@@ -105,7 +105,7 @@ module ActionMailer
private
# Extend the template class instance with our controller's helper module.
def initialize_template_class_with_helper(assigns)
returning(template = initialize_template_class_without_helper(assigns)) do
initialize_template_class_without_helper(assigns).tap do |template|
template.extend self.class.master_helper_module
end
end

View File

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

View File

@@ -1,9 +1,9 @@
require 'rubygems'
require 'test/unit'
$:.unshift "#{File.dirname(__FILE__)}/../lib"
$:.unshift "#{File.dirname(__FILE__)}/../../activesupport/lib"
$:.unshift "#{File.dirname(__FILE__)}/../../actionpack/lib"
$:.unshift File.expand_path('../../lib', __FILE__)
$:.unshift File.expand_path('../../../activesupport/lib', __FILE__)
$:.unshift File.expand_path('../../../actionpack/lib', __FILE__)
require 'action_mailer'
require 'action_mailer/test_case'

View File

@@ -1,3 +1,24 @@
*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.
*2.3.8 (May 24, 2010)*
* HTML safety: fix compatibility *without* the optional rails_xss plugin.
*2.3.7 (May 24, 2010)*
* HTML safety: fix compatibility with the optional rails_xss plugin. [Nathan Weizenbaum, Santiago Pastorino]
*2.3.6 (May 23, 2010)*
* JSON: set Base.include_root_in_json = true to include a root value in the JSON: {"post": {"title": ...}}. Mirrors the Active Record option. #2584 [Matthew Moore, Joe Martinez, Elad Meidar, Santiago Pastorino]
@@ -1914,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]
@@ -2511,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.6' + 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

@@ -65,8 +65,8 @@ module ActionController #:nodoc:
def read_fragment(key, options = nil)
return unless cache_configured?
key = fragment_cache_key(key)
self.class.benchmark "Cached fragment hit: #{key}" do
key = fragment_cache_key(key)
result = cache_store.read(key, options)
result.respond_to?(:html_safe) ? result.html_safe : result
end

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_/
@@ -414,15 +415,25 @@ module ActionController
end
def multipart_requestify(params, first=true)
returning Hash.new do |p|
Array.new.tap do |p|
params.each do |key, value|
k = first ? key.to_s : "[#{key.to_s}]"
if Hash === value
multipart_requestify(value, false).each do |subkey, subvalue|
p[k + subkey] = subvalue
p << [k + subkey, subvalue]
end
elsif Array === value
value.each do |element|
if Hash === element || Array === element
multipart_requestify(element, false).each do |subkey, subvalue|
p << ["#{k}[]#{subkey}", subvalue]
end
else
p << ["#{k}[]", element]
end
end
else
p[k] = value
p << [k, value]
end
end
end
@@ -453,6 +464,7 @@ EOF
end
end.join("")+"--#{boundary}--\r"
end
end
# A module used to extend ActionController::Base, so that integration tests
@@ -500,7 +512,7 @@ EOF
reset! unless @integration_session
# reset the html_document variable, but only for new get/post calls
@html_document = nil unless %w(cookies assigns).include?(method)
returning @integration_session.__send__(method, *args) do
@integration_session.__send__(method, *args).tap do
copy_session_variables!
end
end
@@ -524,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)
}
@@ -556,7 +568,7 @@ EOF
def method_missing(sym, *args, &block)
reset! unless @integration_session
if @integration_session.respond_to?(sym)
returning @integration_session.__send__(sym, *args, &block) do
@integration_session.__send__(sym, *args, &block).tap do
copy_session_variables!
end
else

View File

@@ -446,8 +446,10 @@ EOM
end
def reset_session
@env['rack.session.options'].delete(:id)
@env['rack.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
def session_options

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

@@ -15,7 +15,7 @@ module ActionController #:nodoc:
# behavior is achieved by overriding the <tt>rescue_action_in_public</tt>
# and <tt>rescue_action_locally</tt> methods.
module Rescue
LOCALHOST = ['127.0.0.1', '::1'].freeze
LOCALHOST = [/^127\.0\.0\.\d{1,3}$/, /^::1$/, /^0:0:0:0:0:0:0:1(%.*)?$/].freeze
DEFAULT_RESCUE_RESPONSE = :internal_server_error
DEFAULT_RESCUE_RESPONSES = {
@@ -122,7 +122,7 @@ module ActionController #:nodoc:
# method if you wish to redefine the meaning of a local request to
# include remote IP addresses or other criteria.
def local_request? #:doc:
LOCALHOST.any?{ |local_ip| request.remote_addr == local_ip && request.remote_ip == local_ip }
LOCALHOST.any?{ |local_ip| request.remote_addr =~ local_ip && request.remote_ip =~ local_ip }
end
# Render detailed diagnostics for unhandled exceptions rescued from

View File

@@ -659,7 +659,7 @@ module ActionController
end
def add_conditions_for(conditions, method)
returning({:conditions => conditions.dup}) do |options|
({:conditions => conditions.dup}).tap do |options|
options[:conditions][:method] = method unless method == :any
end
end

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

@@ -377,7 +377,7 @@ module ActionController
ActiveSupport::Inflector.module_eval do
# Ensures that routes are reloaded when Rails inflections are updated.
def inflections_with_route_reloading(&block)
returning(inflections_without_route_reloading(&block)) {
(inflections_without_route_reloading(&block)).tap {
ActionController::Routing::Routes.reload! if block_given?
}
end

View File

@@ -65,7 +65,7 @@ module ActionController
# map.connect '/page/:id', :controller => 'pages', :action => 'show', :id => /\d+/
#
def parameter_shell
@parameter_shell ||= returning({}) do |shell|
@parameter_shell ||= {}.tap do |shell|
requirements.each do |key, requirement|
shell[key] = requirement unless requirement.is_a? Regexp
end
@@ -76,7 +76,7 @@ module ActionController
# includes keys that appear inside the path, and keys that have requirements
# placed upon them.
def significant_keys
@significant_keys ||= returning([]) do |sk|
@significant_keys ||= [].tap do |sk|
segments.each { |segment| sk << segment.key if segment.respond_to? :key }
sk.concat requirements.keys
sk.uniq!
@@ -86,7 +86,7 @@ module ActionController
# Return a hash of key/value pairs representing the keys in the route that
# have defaults, or which are specified by non-regexp requirements.
def defaults
@defaults ||= returning({}) do |hash|
@defaults ||= {}.tap do |hash|
segments.each do |segment|
next unless segment.respond_to? :default
hash[segment.key] = segment.default unless segment.default.nil?

View File

@@ -2,13 +2,42 @@ require 'rack/utils'
module ActionController
module Session
class AbstractStore
class AbstractStore
ENV_SESSION_KEY = 'rack.session'.freeze
ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
HTTP_COOKIE = 'HTTP_COOKIE'.freeze
SET_COOKIE = 'Set-Cookie'.freeze
# thin wrapper around Hash that allows us to lazily
# load session id into session_options
class OptionsHash < Hash
def initialize(by, env, default_options)
@by = by
@env = env
@session_id_loaded = false
merge!(default_options)
end
def [](key)
if key == :id
load_session_id! unless super(:id) || has_session_id?
end
super(key)
end
private
def has_session_id?
@session_id_loaded
end
def load_session_id!
self[:id] = @by.send(:extract_session_id, @env)
@session_id_loaded = true
end
end
class SessionHash < Hash
def initialize(by, env)
super()
@@ -25,21 +54,42 @@ module ActionController
end
def [](key)
load! unless @loaded
load_for_read!
super
end
def has_key?(key)
load_for_read!
super
end
def []=(key, value)
load! unless @loaded
load_for_write!
super
end
def clear
load_for_write!
super
end
def to_hash
load_for_read!
h = {}.replace(self)
h.delete_if { |k,v| v.nil? }
h
end
def update(hash)
load_for_write!
super
end
def delete(key)
load_for_write!
super
end
def data
ActiveSupport::Deprecation.warn(
"ActionController::Session::AbstractStore::SessionHash#data " +
@@ -48,40 +98,43 @@ module ActionController
end
def inspect
load! unless @loaded
load_for_read!
super
end
def exists?
return @exists if instance_variable_defined?(:@exists)
@exists = @by.send(:exists?, @env)
end
def loaded?
@loaded
end
def destroy
clear
@by.send(:destroy, @env) if @by
@env[ENV_SESSION_OPTIONS_KEY][:id] = nil if @env && @env[ENV_SESSION_OPTIONS_KEY]
@loaded = false
end
private
def loaded?
@loaded
def load_for_read!
load! if !loaded? && exists?
end
def load_for_write!
load! unless loaded?
end
def load!
stale_session_check! do
id, session = @by.send(:load_session, @env)
(@env[ENV_SESSION_OPTIONS_KEY] ||= {})[:id] = id
replace(session)
@loaded = true
end
id, session = @by.send(:load_session, @env)
@env[ENV_SESSION_OPTIONS_KEY][:id] = id
replace(session)
@loaded = true
end
def stale_session_check!
yield
rescue ArgumentError => argument_error
if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
begin
# Note that the regexp does not allow $1 to end with a ':'
$1.constantize
rescue LoadError, NameError => const_error
raise ActionController::SessionRestoreError, "Session contains objects whose class definition isn\\'t available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: \#{const_error.message} [\#{const_error.class}])\n"
end
retry
else
raise
end
end
end
DEFAULT_OPTIONS = {
@@ -120,18 +173,18 @@ module ActionController
end
def call(env)
session = SessionHash.new(self, env)
env[ENV_SESSION_KEY] = session
env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup
prepare!(env)
response = @app.call(env)
session_data = env[ENV_SESSION_KEY]
options = env[ENV_SESSION_OPTIONS_KEY]
if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after]
session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?)
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
@@ -139,21 +192,12 @@ module ActionController
return response
end
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
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
@@ -161,18 +205,39 @@ module ActionController
end
private
def prepare!(env)
env[ENV_SESSION_KEY] = SessionHash.new(self, env)
env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options)
end
def generate_sid
ActiveSupport::SecureRandom.hex(16)
end
def load_session(env)
request = Rack::Request.new(env)
sid = request.cookies[@key]
unless @cookie_only
sid ||= request.params[@key]
stale_session_check! do
sid = current_session_id(env)
sid, session = get_session(env, sid)
[sid, session]
end
sid, session = get_session(env, sid)
[sid, session]
end
def extract_session_id(env)
stale_session_check! do
request = Rack::Request.new(env)
sid = request.cookies[@key]
sid ||= request.params[@key] unless @cookie_only
sid
end
end
def current_session_id(env)
env[ENV_SESSION_OPTIONS_KEY][:id]
end
def exists?(env)
current_session_id(env).present?
end
def get_session(env, sid)
@@ -182,6 +247,30 @@ module ActionController
def set_session(env, sid, session_data)
raise '#set_session needs to be implemented.'
end
def destroy(env)
raise '#destroy needs to be implemented.'
end
module SessionUtils
private
def stale_session_check!
yield
rescue ArgumentError => argument_error
if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
begin
# Note that the regexp does not allow $1 to end with a ':'
$1.constantize
rescue LoadError, NameError => const_error
raise ActionController::SessionRestoreError, "Session contains objects whose class definition isn\\'t available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: \#{const_error.message} [\#{const_error.class}])\n"
end
retry
else
raise
end
end
end
include SessionUtils
end
end
end

View File

@@ -36,6 +36,8 @@ module ActionController
#
# Note that changing digest or secret invalidates all existing sessions!
class CookieStore
include AbstractStore::SessionUtils
# Cookies can typically store 4096 bytes.
MAX = 4096
SECRET_MIN_LENGTH = 30 # characters
@@ -50,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
@@ -93,73 +94,81 @@ module ActionController
end
def call(env)
env[ENV_SESSION_KEY] = AbstractStore::SessionHash.new(self, env)
env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup
prepare!(env)
status, headers, body = @app.call(env)
session_data = env[ENV_SESSION_KEY]
options = env[ENV_SESSION_OPTIONS_KEY]
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?
if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after]
session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?)
persistent_session_id!(session_data)
session_data = marshal(session_data.to_hash)
raise CookieOverflow if session_data.size > MAX
cookie = Hash.new
cookie[:value] = session_data
unless options[:expire_after].nil?
cookie[:expires] = Time.now + options[:expire_after]
end
cookie = build_cookie(@key, cookie.merge(options))
unless headers[HTTP_SET_COOKIE].blank?
headers[HTTP_SET_COOKIE] << "\n#{cookie}"
else
headers[HTTP_SET_COOKIE] = cookie
end
Rack::Utils.set_cookie_header!(headers, @key, cookie.merge(options))
end
[status, headers, body]
end
private
# 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}"
def prepare!(env)
env[ENV_SESSION_KEY] = AbstractStore::SessionHash.new(self, env)
env[ENV_SESSION_OPTIONS_KEY] = AbstractStore::OptionsHash.new(self, env, @default_options)
end
def load_session(env)
request = Rack::Request.new(env)
session_data = request.cookies[@key]
data = unmarshal(session_data) || persistent_session_id!({})
data = unpacked_cookie_data(env)
data = persistent_session_id!(data)
[data[:session_id], data]
end
def extract_session_id(env)
if data = unpacked_cookie_data(env)
persistent_session_id!(data) unless data.empty?
data[:session_id]
else
nil
end
end
def current_session_id(env)
env[ENV_SESSION_OPTIONS_KEY][:id]
end
def exists?(env)
current_session_id(env).present?
end
def unpacked_cookie_data(env)
env["action_dispatch.request.unsigned_session_cookie"] ||= begin
stale_session_check! do
request = Rack::Request.new(env)
session_data = request.cookies[@key]
unmarshal(session_data) || {}
end
end
end
# Marshal a session hash into safe cookie data. Include an integrity hash.
def marshal(session)
@verifier.generate(persistent_session_id!(session))
@verifier.generate(session)
end
# Unmarshal cookie data to a hash and verify its integrity.
def unmarshal(cookie)
persistent_session_id!(@verifier.verify(cookie)) if cookie
@verifier.verify(cookie) if cookie
rescue ActiveSupport::MessageVerifier::InvalidSignature
nil
end
@@ -207,6 +216,10 @@ module ActionController
ActiveSupport::SecureRandom.hex(16)
end
def destroy(env)
# session data is stored on client; nothing to do here
end
def persistent_session_id!(data)
(data ||= {}).merge!(inject_persistent_session_id(data))
end

View File

@@ -1,6 +1,6 @@
begin
require_library_or_gem 'memcache'
require 'thread'
module ActionController
module Session
class MemCacheStore < AbstractStore
@@ -43,6 +43,15 @@ begin
rescue MemCache::MemCacheError, Errno::ECONNREFUSED
return false
end
def destroy(env)
if sid = current_session_id(env)
@pool.delete(sid)
end
rescue MemCache::MemCacheError, Errno::ECONNREFUSED
false
end
end
end
end

View File

@@ -450,7 +450,7 @@ module ActionController #:nodoc:
def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
@request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
@request.env['HTTP_ACCEPT'] = [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
returning __send__(request_method, action, parameters, session, flash) do
__send__(request_method, action, parameters, session, flash).tap do
@request.env.delete 'HTTP_X_REQUESTED_WITH'
@request.env.delete 'HTTP_ACCEPT'
end

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.
@@ -92,6 +94,14 @@ module ActionController
# end
# end
module UrlWriter
RESERVED_PCHAR = ':@&=+$,;%'
SAFE_PCHAR = "#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}"
if RUBY_VERSION >= '1.9'
UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false).freeze
else
UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false, 'N').freeze
end
def self.included(base) #:nodoc:
ActionController::Routing::Routes.install_helpers(base)
base.mattr_accessor :default_url_options
@@ -142,7 +152,7 @@ module ActionController
end
trailing_slash = options.delete(:trailing_slash) if options.key?(:trailing_slash)
url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
anchor = "##{CGI.escape options.delete(:anchor).to_param.to_s}" if options[:anchor]
anchor = "##{URI.escape(options.delete(:anchor).to_param.to_s, UNSAFE_PCHAR)}" if options[:anchor]
generated = Routing::Routes.generate(options, {})
url << (trailing_slash ? generated.sub(/\?|\z/) { "/" + $& } : generated)
url << anchor if anchor

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 = 6
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

@@ -124,7 +124,7 @@ module ActionView
# Use an alternate output buffer for the duration of the block.
# Defaults to a new empty string.
def with_output_buffer(buf = ActiveSupport::SafeBuffer.new) #:nodoc:
def with_output_buffer(buf = '') #:nodoc:
self.output_buffer, old_buffer = buf, output_buffer
yield
output_buffer

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

@@ -280,7 +280,7 @@ module ActionView
concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}))
fields_for(object_name, *(args << options), &proc)
concat('</form>')
concat('</form>'.html_safe)
end
def apply_form_for_options!(object_or_array, options) #:nodoc:
@@ -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)

View File

@@ -481,7 +481,7 @@ module ActionView
end
zone_options += options_for_select(convert_zones[zones], selected)
zone_options
zone_options.html_safe
end
private

View File

@@ -265,7 +265,7 @@ module ActionView
escape = options.key?("escape") ? options.delete("escape") : true
content = html_escape(content) if escape
content_tag :textarea, content.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
content_tag :textarea, content.to_s.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
end
# Creates a check box form input tag.
@@ -435,12 +435,12 @@ module ActionView
concat(tag(:fieldset, options, true))
concat(content_tag(:legend, legend)) unless legend.blank?
concat(content)
concat("</fieldset>")
concat("</fieldset>".html_safe)
end
private
def html_options_for_form(url_for_options, options, *parameters_for_url)
returning options.stringify_keys do |html_options|
options.stringify_keys.tap do |html_options|
html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart")
html_options["action"] = url_for(url_for_options, *parameters_for_url)
end
@@ -469,7 +469,7 @@ module ActionView
content = capture(&block)
concat(form_tag_html(html_options))
concat(content)
concat("</form>")
concat("</form>".html_safe)
end
def token_tag

View File

@@ -393,7 +393,7 @@ module ActionView
concat(form_remote_tag(options))
fields_for(object_name, *(args << options), &proc)
concat('</form>')
concat('</form>'.html_safe)
end
alias_method :form_remote_for, :remote_form_for
@@ -653,7 +653,7 @@ module ActionView
# <script> tag.
module GeneratorMethods
def to_s #:nodoc:
returning javascript = @lines * $/ do
(@lines * $/).tap do |javascript|
if ActionView::Base.debug_rjs
source = javascript.dup
javascript.replace "try {\n#{source}\n} catch (e) "
@@ -981,8 +981,8 @@ module ActionView
end
def record(line)
returning line = "#{line.to_s.chomp.gsub(/\;\z/, '')};" do
self << line
"#{line.to_s.chomp.gsub(/\;\z/, '')};".tap do |_line|
self << _line
end
end

View File

@@ -532,9 +532,14 @@ module ActionView
end
AUTO_LINK_RE = %r{
( https?:// | www\. )
(?: ([\w+.:-]+:)// | www\. )
[^\s<]+
}x unless const_defined?(:AUTO_LINK_RE)
}x
# regexps for determining context, used high-volume
AUTO_LINK_CRE = [/<[^>]+$/, /^[^>]*>/, /<a\b.*?>/i, /<\/a>/i]
AUTO_EMAIL_RE = /[\w.!#\$%+-]+@[\w-]+(?:\.[\w-]+)+/
BRACKETS = { ']' => '[', ')' => '(', '}' => '{' }
@@ -543,26 +548,26 @@ module ActionView
def auto_link_urls(text, html_options = {})
link_attributes = html_options.stringify_keys
text.gsub(AUTO_LINK_RE) do
href = $&
punctuation = ''
left, right = $`, $'
# detect already linked URLs and URLs in the middle of a tag
if left =~ /<[^>]+$/ && right =~ /^[^>]*>/
scheme, href = $1, $&
punctuation = []
if auto_linked?($`, $')
# do not change string; URL is already linked
href
else
# don't include trailing punctuation character as part of the URL
if href.sub!(/[^\w\/-]$/, '') and punctuation = $& and opening = BRACKETS[punctuation]
if href.scan(opening).size > href.scan(punctuation).size
href << punctuation
punctuation = ''
while href.sub!(/[^\w\/-]$/, '')
punctuation.push $&
if opening = BRACKETS[punctuation.last] and href.scan(opening).size > href.scan(punctuation.last).size
href << punctuation.pop
break
end
end
link_text = block_given?? yield(href) : href
href = 'http://' + href unless href =~ %r{^[a-z]+://}i
href = 'http://' + href unless scheme
content_tag(:a, h(link_text), link_attributes.merge('href' => href)) + punctuation
content_tag(:a, h(link_text), link_attributes.merge('href' => href)) + punctuation.reverse.join('')
end
end
end
@@ -570,11 +575,10 @@ module ActionView
# Turns all email addresses into clickable links. If a block is given,
# each email is yielded and the result is used as the link text.
def auto_link_email_addresses(text, html_options = {})
body = text.dup
text.gsub(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
text = $1
text.gsub(AUTO_EMAIL_RE) do
text = $&
if body.match(/<a\b[^>]*>(.*)(#{Regexp.escape(text)})(.*)<\/a>/)
if auto_linked?($`, $')
text
else
display_text = (block_given?) ? yield(text) : text
@@ -582,6 +586,12 @@ module ActionView
end
end
end
# Detects already linked context or position in the middle of a tag
def auto_linked?(left, right)
(left =~ AUTO_LINK_CRE[0] and right =~ AUTO_LINK_CRE[1]) or
(left.rindex(AUTO_LINK_CRE[2]) and $' !~ AUTO_LINK_CRE[3])
end
end
end
end

View File

@@ -3,17 +3,30 @@ require 'action_view/helpers/tag_helper'
module ActionView
module Helpers
module TranslationHelper
# Delegates to I18n#translate but also performs two additional functions. First, it'll catch MissingTranslationData exceptions
# Delegates to I18n#translate but also performs two additional functions. First, it'll catch MissingTranslationData exceptions
# and turn them into inline spans that contains the missing key, such that you can see in a view what is missing where.
#
# Second, it'll scope the key by the current partial if the key starts with a period. So if you call translate(".foo") from the
# people/index.html.erb template, you'll actually be calling I18n.translate("people.index.foo"). This makes it less repetitive
# to translate many keys within the same partials and gives you a simple framework for scoping them consistently. If you don't
# prepend the key with a period, nothing is converted.
def translate(key, options = {})
def translate(keys, options = {})
if multiple_keys = keys.is_a?(Array)
ActiveSupport::Deprecation.warn "Giving an array to translate is deprecated, please give a symbol or a string instead", caller
end
options[:raise] = true
translation = I18n.translate(scope_key_by_partial(key), options)
(translation.respond_to?(:join) ? translation.join : translation).html_safe
keys = scope_keys_by_partial(keys)
translations = I18n.translate(keys, options)
translations = [translations] if !multiple_keys && translations.size > 1
translations = html_safe_translation_keys(keys, translations)
if multiple_keys || translations.size > 1
translations
else
translations.first
end
rescue I18n::MissingTranslationData => e
keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope])
content_tag('span', keys.join(', '), :class => 'translation_missing')
@@ -28,12 +41,25 @@ module ActionView
private
def scope_key_by_partial(key)
strkey = key.respond_to?(:join) ? key.join : key.to_s
if strkey.first == "."
template.path_without_format_and_extension.gsub(%r{/_?}, ".") + strkey
else
key
def scope_keys_by_partial(keys)
Array.wrap(keys).map do |key|
key = key.to_s
if key.first == "."
template.path_without_format_and_extension.gsub(%r{/_?}, ".") + key
else
key
end
end
end
def html_safe_translation_keys(keys, translations)
keys.zip(translations).map do |key, translation|
if key =~ /(\b|_|\.)html$/ && translation.respond_to?(:html_safe)
translation.html_safe
else
translation
end
end
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

@@ -63,37 +63,37 @@
half_a_minute: "half a minute"
less_than_x_seconds:
one: "less than 1 second"
other: "less than {{count}} seconds"
other: "less than %{count} seconds"
x_seconds:
one: "1 second"
other: "{{count}} seconds"
other: "%{count} seconds"
less_than_x_minutes:
one: "less than a minute"
other: "less than {{count}} minutes"
other: "less than %{count} minutes"
x_minutes:
one: "1 minute"
other: "{{count}} minutes"
other: "%{count} minutes"
about_x_hours:
one: "about 1 hour"
other: "about {{count}} hours"
other: "about %{count} hours"
x_days:
one: "1 day"
other: "{{count}} days"
other: "%{count} days"
about_x_months:
one: "about 1 month"
other: "about {{count}} months"
other: "about %{count} months"
x_months:
one: "1 month"
other: "{{count}} months"
other: "%{count} months"
about_x_years:
one: "about 1 year"
other: "about {{count}} years"
other: "about %{count} years"
over_x_years:
one: "over 1 year"
other: "over {{count}} years"
other: "over %{count} years"
almost_x_years:
one: "almost 1 year"
other: "almost {{count}} years"
other: "almost %{count} years"
prompts:
year: "Year"
month: "Month"
@@ -106,12 +106,12 @@
errors:
template:
header:
one: "1 error prohibited this {{model}} from being saved"
other: "{{count}} errors prohibited this {{model}} from being saved"
one: "1 error prohibited this %{model} from being saved"
other: "%{count} errors prohibited this %{model} from being saved"
# The variable :count is also available
body: "There were problems with the following fields:"
support:
select:
# default value for :prompt => true in FormOptionsHelper
prompt: "Please select"
prompt: "Please select"

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

@@ -45,8 +45,8 @@ module ActionView #:nodoc:
end
def self.new_and_loaded(path)
returning new(path) do |path|
path.load!
new(path).tap do |_path|
_path.load!
end
end

View File

@@ -13,6 +13,11 @@ module ActionView
def compile(template)
magic = $1 if template.source =~ /\A(<%#.*coding[:=]\s*(\S+)\s*-?%>)/
erb = "#{magic}<% __in_erb_template=true %>#{template.source}"
if erb.respond_to?(:force_encoding)
erb.force_encoding(template.source.encoding)
end
::ERB.new(erb, nil, erb_trim_mode, '@output_buffer').src
end
end

View File

@@ -53,7 +53,7 @@ module ActionView
setup :setup_with_controller
def setup_with_controller
@controller = TestController.new
@output_buffer = ActiveSupport::SafeBuffer.new
@output_buffer = ''
@rendered = ''
self.class.send(:include_helper_modules!)

View File

@@ -1,7 +1,7 @@
$:.unshift(File.dirname(__FILE__) + '/../lib')
$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib')
$:.unshift(File.dirname(__FILE__) + '/fixtures/helpers')
$:.unshift(File.dirname(__FILE__) + '/fixtures/alternate_helpers')
$:.unshift File.expand_path('../../lib', __FILE__)
$:.unshift File.expand_path('../../../activesupport/lib', __FILE__)
$:.unshift File.expand_path('../fixtures/helpers', __FILE__)
$:.unshift File.expand_path('../fixtures/alternate_helpers', __FILE__)
require 'rubygems'
require 'yaml'
@@ -58,4 +58,21 @@ class DummyMutex
end
end
ActionController::Reloader.default_lock = DummyMutex.new
class ActionController::IntegrationTest < ActiveSupport::TestCase
def with_autoload_path(path)
path = File.join(File.dirname(__FILE__), "fixtures", path)
if ActiveSupport::Dependencies.autoload_paths.include?(path)
yield
else
begin
ActiveSupport::Dependencies.autoload_paths << path
yield
ensure
ActiveSupport::Dependencies.autoload_paths.reject! {|p| p == path}
ActiveSupport::Dependencies.clear
end
end
end
end
ActionController::Reloader.default_lock = DummyMutex.new

View File

@@ -16,7 +16,7 @@ if defined?(ActiveRecord) && defined?(Fixtures)
else
$stderr.print 'Attempting to load Active Record... '
begin
PATH_TO_AR = "#{File.dirname(__FILE__)}/../../activerecord/lib"
PATH_TO_AR = File.expand_path('../../../activerecord/lib', __FILE__)
raise LoadError, "#{PATH_TO_AR} doesn't exist" unless File.directory?(PATH_TO_AR)
$LOAD_PATH.unshift PATH_TO_AR
require 'active_record'

View File

@@ -22,7 +22,6 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest
end
def get_session_id
session[:foo]
render :text => "#{request.session_options[:id]}"
end
@@ -45,23 +44,27 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest
ActiveRecord::SessionStore.session_class.drop_table!
end
def test_setting_and_getting_session_value
with_test_route_set do
get '/set_session_value'
assert_response :success
assert cookies['_session_id']
%w{ session sql_bypass }.each do |class_name|
define_method("test_setting_and_getting_session_value_with_#{class_name}_store") do
with_store class_name do
with_test_route_set do
get '/set_session_value'
assert_response :success
assert cookies['_session_id']
get '/get_session_value'
assert_response :success
assert_equal 'foo: "bar"', response.body
get '/get_session_value'
assert_response :success
assert_equal 'foo: "bar"', response.body
get '/set_session_value', :foo => "baz"
assert_response :success
assert cookies['_session_id']
get '/set_session_value', :foo => "baz"
assert_response :success
assert cookies['_session_id']
get '/get_session_value'
assert_response :success
assert_equal 'foo: "baz"', response.body
get '/get_session_value'
assert_response :success
assert_equal 'foo: "baz"', response.body
end
end
end
end
@@ -107,6 +110,38 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest
end
end
def test_getting_session_value
with_test_route_set do
get '/set_session_value'
assert_response :success
assert cookies['_session_id']
get '/get_session_value'
assert_response :success
assert_equal nil, headers['Set-Cookie'], "should not resend the cookie again if session_id cookie is already exists"
session_id = cookies["_session_id"]
get '/call_reset_session'
assert_response :success
assert_not_equal [], headers['Set-Cookie']
cookies["_session_id"] = session_id # replace our new session_id with our old, pre-reset session_id
get '/get_session_value'
assert_response :success
assert_equal 'foo: nil', response.body, "data for this session should have been obliterated from the database"
end
end
def test_getting_from_nonexistent_session
with_test_route_set do
get '/get_session_value'
assert_response :success
assert_equal 'foo: nil', response.body
assert_nil cookies['_session_id'], "should only create session on write, not read"
end
end
def test_prevents_session_fixation
with_test_route_set do
get '/set_session_value'
@@ -171,4 +206,16 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest
yield
end
end
def with_store(class_name)
begin
session_class = ActiveRecord::SessionStore.session_class
ActiveRecord::SessionStore.session_class = "ActiveRecord::SessionStore::#{class_name.camelize}".constantize
yield
rescue
ActiveRecord::SessionStore.session_class = session_class
raise
end
end
end

View File

@@ -210,13 +210,13 @@ class AssertSelectTest < ActionController::TestCase
assert_nothing_raised { assert_select "div", "foo" }
assert_nothing_raised { assert_select "div", "bar" }
assert_nothing_raised { assert_select "div", /\w*/ }
assert_nothing_raised { assert_select "div", /\w*/, :count=>2 }
assert_raise(Assertion) { assert_select "div", :text=>"foo", :count=>2 }
assert_nothing_raised { assert_select "div", :text => /\w*/, :count=>2 }
assert_raise(Assertion) { assert_select "div", :text=>"foo", :count=>2 }
assert_nothing_raised { assert_select "div", :html=>"<span>bar</span>" }
assert_nothing_raised { assert_select "div", :html=>"<span>bar</span>" }
assert_nothing_raised { assert_select "div", :html=>/\w*/ }
assert_nothing_raised { assert_select "div", :html=>/\w*/, :count=>2 }
assert_raise(Assertion) { assert_select "div", :html=>"<span>foo</span>", :count=>2 }
assert_raise(Assertion) { assert_select "div", :html=>"<span>foo</span>", :count=>2 }
end
end
@@ -268,8 +268,8 @@ class AssertSelectTest < ActionController::TestCase
def test_css_select
render_html %Q{<div id="1"></div><div id="2"></div>}
assert 2, css_select("div").size
assert 0, css_select("p").size
assert_equal 2, css_select("div").size
assert_equal 0, css_select("p").size
end
def test_nested_css_select

View File

@@ -36,7 +36,7 @@ class CaptureTest < ActionController::TestCase
def test_simple_capture
get :capturing
assert_equal "Dreamy days", @response.body.strip
assert_equal "<p>Dreamy days</p>", @response.body.strip
end
def test_content_for

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

@@ -87,7 +87,7 @@ class HelperTest < Test::Unit::TestCase
assert_nothing_raised {
@controller_class.helper { include HelperTest::TestHelper }
}
assert [], missing_methods
assert_equal [], missing_methods
end
def test_helper_method

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
@@ -266,6 +284,14 @@ class IntegrationProcessTest < ActionController::IntegrationTest
render :text => "foo(1i): #{params[:"foo(1i)"]}, foo(2i): #{params[:"foo(2i)"]}, filesize: #{params[:file].size}", :status => 200
end
def multipart_post_with_nested_params
render :text => "foo: #{params[:foo][0]}, #{params[:foo][1]}; [filesize: #{params[:file_list][0][:content].size}, filesize: #{params[:file_list][1][:content].size}]", :status => 200
end
def multipart_post_with_multiparameter_complex_params
render :text => "foo(1i): #{params[:"foo(1i)"]}, foo(2i): #{params[:"foo(2i)"]}, [filesize: #{params[:file_list][0][:content].size}, filesize: #{params[:file_list][1][:content].size}]", :status => 200
end
def post
render :text => "Created", :status => 201
end
@@ -405,6 +431,24 @@ class IntegrationProcessTest < ActionController::IntegrationTest
end
end
def test_multipart_post_with_nested_params
with_test_route_set do
post '/multipart_post_with_nested_params', :"foo" => ['a', 'b'], :file_list => [{:content => fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")}, {:content => fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")}]
assert_equal 200, status
assert_equal "foo: a, b; [filesize: 159528, filesize: 159528]", response.body
end
end
def test_multipart_post_with_multiparameter_complex_attribute_parameters
with_test_route_set do
post '/multipart_post_with_multiparameter_complex_params', :"foo(1i)" => "bar", :"foo(2i)" => "baz", :file_list => [{:content => fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")}, {:content => fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")}]
assert_equal 200, status
assert_equal "foo(1i): bar, foo(2i): baz, [filesize: 159528, filesize: 159528]", response.body
end
end
def test_head
with_test_route_set do
head '/get'

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

@@ -14,6 +14,10 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest
def read
render :text => "File: #{params[:uploaded_data].read}"
end
def read_complex
render :text => "File: #{params[:level0][:level1][0][:file_data].read}"
end
end
FIXTURE_PATH = File.dirname(__FILE__) + '/../../fixtures/multipart'
@@ -133,6 +137,17 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest
end
end
test "uploads and reads file in complex parameter" do
with_test_routing do
post '/read_complex',
:level0 => {
:level1 => [ { :file_data => fixture_file_upload(FIXTURE_PATH + "/hello.txt", "text/plain") }
]
}
assert_equal "File: Hello", response.body
end
end
private
def fixture(name)
File.open(File.join(FIXTURE_PATH, name), 'rb') do |file|

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

@@ -281,12 +281,11 @@ class RescueControllerTest < ActionController::TestCase
end
def test_local_request_when_remote_addr_is_localhost
@controller.expects(:request).returns(@request).at_least(4)
with_remote_addr '127.0.0.1' do
assert @controller.send(:local_request?)
end
with_remote_addr '::1' do
assert @controller.send(:local_request?)
@controller.expects(:request).returns(@request).at_least(10)
['127.0.0.1', '127.0.0.127', '::1', '0:0:0:0:0:0:0:1', '0:0:0:0:0:0:0:1%0'].each do |ip_address|
with_remote_addr ip_address do
assert @controller.send(:local_request?)
end
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')
@@ -34,6 +33,21 @@ class CookieStoreTest < ActionController::IntegrationTest
render :text => "foo: #{session[:foo].inspect}; id: #{request.session_options[:id]}"
end
def get_session_id_only
render :text => "id: #{request.session_options[:id]}"
end
def call_session_clear
session.clear
head :ok
end
def call_reset_session_twice
reset_session
reset_session
head :ok
end
def call_reset_session
reset_session
head :ok
@@ -44,11 +58,13 @@ class CookieStoreTest < ActionController::IntegrationTest
head :ok
end
def rescue_action(e) raise end
end
def set_session_value_and_cookie
cookies["foo"] = "bar"
session[:foo] = "bar"
render :text => Rack::Utils.escape(Verifier.generate(session.to_hash))
end
def setup
@integration_session = open_session(CookieStoreApp)
def rescue_action(e) raise end
end
def test_raises_argument_error_if_missing_session_key
@@ -121,6 +137,10 @@ class CookieStoreTest < ActionController::IntegrationTest
get '/get_session_id'
assert_response :success
assert_equal "foo: \"bar\"; id: #{session_id}", response.body
get '/get_session_id_only'
assert_response :success
assert_equal "id: #{session_id}", response.body, "should be able to read session id without accessing the session hash"
end
end
@@ -133,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) {
@@ -159,6 +196,25 @@ 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'
@@ -169,7 +225,7 @@ class CookieStoreTest < ActionController::IntegrationTest
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'
@@ -178,6 +234,76 @@ class CookieStoreTest < ActionController::IntegrationTest
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'
assert_response :success
assert_equal 'foo: nil', response.body
end
end
def test_setting_session_value_after_session_clear
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_session_clear'
assert_response :success
get '/get_session_value'
assert_response :success
assert_equal 'foo: nil', response.body
end
end
def test_getting_from_nonexistent_session
with_test_route_set do
get '/get_session_value'
assert_response :success
assert_equal 'foo: nil', response.body
assert_nil headers['Set-Cookie'], "should only create session on write, not read"
end
end
# {:foo=>#<SessionAutoloadTest::Foo bar:"baz">, :session_id=>"ce8b0752a6ab7c7af3cdb8a80e6b9e46"}
SignedSerializedCookie = "BAh7BzoIZm9vbzodU2Vzc2lvbkF1dG9sb2FkVGVzdDo6Rm9vBjoJQGJhciIIYmF6Og9zZXNzaW9uX2lkIiVjZThiMDc1MmE2YWI3YzdhZjNjZGI4YTgwZTZiOWU0Ng==--2bf3af1ae8bd4e52b9ac2099258ace0c380e601c"
def test_deserializes_unloaded_classes_on_get_id
with_test_route_set do
with_autoload_path "session_autoload_test" do
cookies[SessionKey] = SignedSerializedCookie
get '/get_session_id_only'
assert_response :success
assert_equal 'id: ce8b0752a6ab7c7af3cdb8a80e6b9e46', response.body, "should auto-load unloaded class"
end
end
end
def test_deserializes_unloaded_classes_on_get_value
with_test_route_set do
with_autoload_path "session_autoload_test" do
cookies[SessionKey] = SignedSerializedCookie
get '/get_session_value'
assert_response :success
assert_equal 'foo: #<SessionAutoloadTest::Foo bar:"baz">', response.body, "should auto-load unloaded class"
end
end
end
def test_persistent_session_id
with_test_route_set do
cookies[SessionKey] = SignedBar
@@ -193,14 +319,26 @@ class CookieStoreTest < ActionController::IntegrationTest
end
end
def test_setting_session_value_and_cookie
with_test_route_set do
get '/set_session_value_and_cookie'
assert_response :success
assert_equal({"_myapp_session" => response.body, "foo" => "bar"}, cookies)
end
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

@@ -12,12 +12,16 @@ class MemCacheStoreTest < ActionController::IntegrationTest
head :ok
end
def set_serialized_session_value
session[:foo] = SessionAutoloadTest::Foo.new
head :ok
end
def get_session_value
render :text => "foo: #{session[:foo].inspect}"
end
def get_session_id
session[:foo]
render :text => "#{request.session_options[:id]}"
end
@@ -33,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
@@ -82,6 +79,34 @@ class MemCacheStoreTest < ActionController::IntegrationTest
end
end
def test_getting_session_value_after_session_reset
with_test_route_set do
get '/set_session_value'
assert_response :success
assert cookies['_session_id']
session_id = cookies["_session_id"]
get '/call_reset_session'
assert_response :success
assert_not_equal [], headers['Set-Cookie']
cookies["_session_id"] = session_id # replace our new session_id with our old, pre-reset session_id
get '/get_session_value'
assert_response :success
assert_equal 'foo: nil', response.body, "data for this session should have been obliterated from memcached"
end
end
def test_getting_from_nonexistent_session
with_test_route_set do
get '/get_session_value'
assert_response :success
assert_equal 'foo: nil', response.body
assert_nil cookies['_session_id'], "should only create session on write, not read"
end
end
def test_getting_session_id
with_test_route_set do
get '/set_session_value'
@@ -91,7 +116,38 @@ class MemCacheStoreTest < ActionController::IntegrationTest
get '/get_session_id'
assert_response :success
assert_equal session_id, response.body
assert_equal session_id, response.body, "should be able to read session id without accessing the session hash"
end
end
def test_doesnt_write_session_cookie_if_session_id_is_already_exists
with_test_route_set do
get '/set_session_value'
assert_response :success
assert cookies['_session_id']
get '/get_session_value'
assert_response :success
assert_equal nil, headers['Set-Cookie'], "should not resend the cookie again if session_id cookie is already exists"
end
end
def test_deserializes_unloaded_class
with_test_route_set do
with_autoload_path "session_autoload_test" do
get '/set_serialized_session_value'
assert_response :success
assert cookies['_session_id']
end
with_autoload_path "session_autoload_test" do
get '/get_session_id'
assert_response :success
end
with_autoload_path "session_autoload_test" do
get '/get_session_value'
assert_response :success
assert_equal 'foo: #<SessionAutoloadTest::Foo bar:"baz">', response.body, "should auto-load unloaded class"
end
end
end
@@ -114,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

@@ -134,9 +134,15 @@ class UrlWriterTests < ActionController::TestCase
)
end
def test_anchor_should_be_cgi_escaped
assert_equal('/c/a#anc%2Fhor',
W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('anc/hor'))
def test_anchor_should_escape_unsafe_pchar
assert_equal('/c/a#%23anchor',
W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('#anchor'))
)
end
def test_anchor_should_not_escape_safe_pchar
assert_equal('/c/a#name=user&email=user@domain.com',
W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('name=user&email=user@domain.com'))
)
end

View File

@@ -0,0 +1,10 @@
module SessionAutoloadTest
class Foo
def initialize(bar='baz')
@bar = bar
end
def inspect
"#<#{self.class} bar:#{@bar.inspect}>"
end
end
end

View File

@@ -1 +1 @@
<%= t(['foo', 'bar']) %>
<% translation = t(['foo', 'bar', 'baz_html']) %><%= translation.first %>, <%= translation.second %>, <%= translation.third %>

View File

@@ -1,4 +1,4 @@
<% days = capture do %>
Dreamy days
<p>Dreamy days</p>
<% end %>
<%= days %>
<%= days %>

View File

@@ -1 +1 @@
<%= t(['.foo', '.bar']) %>
<%= t(['.foo', '.bar']).join(", ") %>

View File

@@ -0,0 +1 @@
<%= t('.helper') %>

View File

@@ -280,6 +280,10 @@ class FormOptionsHelperTest < ActionView::TestCase
opts
end
def test_time_zone_options_returns_html_safe_string
assert time_zone_options_for_select.html_safe?
end
def test_select
@post = Post.new
@post.category = "<mus>"

View File

@@ -176,6 +176,12 @@ class FormTagHelperTest < ActionView::TestCase
assert_dom_equal expected, actual
end
def test_text_area_tag_unescaped_nil_content
actual = text_area_tag "body", nil, :escape => false
expected = %(<textarea id="body" name="body"></textarea>)
assert_dom_equal expected, actual
end
def test_text_field_tag
actual = text_field_tag "title", "Hello!"
expected = %(<input id="title" name="title" type="text" value="Hello!" />)

View File

@@ -296,6 +296,7 @@ class TextHelperTest < ActionView::TestCase
assert_equal %(<p>Link #{link_result_with_options}</p>), auto_link("<p>Link #{link_raw}</p>", :all, {:target => "_blank"})
assert_equal %(Go to #{link_result}.), auto_link(%(Go to #{link_raw}.))
assert_equal %(<p>Go to #{link_result}, then say hello to #{email_result}.</p>), auto_link(%(<p>Go to #{link_raw}, then say hello to #{email_raw}.</p>))
assert_equal %(#{link_result} #{link_result}), auto_link(%(#{link_result} #{link_raw}))
email2_raw = '+david@loudthinking.com'
email2_result = %{<a href="mailto:#{email2_raw}">#{email2_raw}</a>}
@@ -368,24 +369,38 @@ class TextHelperTest < ActionView::TestCase
end
def test_auto_link_other_protocols
silence_warnings do
begin
old_re_value = ActionView::Helpers::TextHelper::AUTO_LINK_RE
ActionView::Helpers::TextHelper.const_set :AUTO_LINK_RE, %r{(ftp://)[^\s<]+}
link_raw = 'ftp://example.com/file.txt'
link_result = generate_result(link_raw)
assert_equal %(Download #{link_result}), auto_link("Download #{link_raw}")
ensure
ActionView::Helpers::TextHelper.const_set :AUTO_LINK_RE, old_re_value
end
end
ftp_raw = 'ftp://example.com/file.txt'
assert_equal %(Download #{generate_result(ftp_raw)}), auto_link("Download #{ftp_raw}")
file_scheme = 'file:///home/username/RomeoAndJuliet.pdf'
z39_scheme = 'z39.50r://host:696/db'
chrome_scheme = 'chrome://package/section/path'
view_source = 'view-source:http://en.wikipedia.org/wiki/URI_scheme'
assert_equal generate_result(z39_scheme), auto_link(z39_scheme)
assert_equal generate_result(chrome_scheme), auto_link(chrome_scheme)
assert_equal generate_result(view_source), auto_link(view_source)
end
def test_auto_link_already_linked
linked1 = generate_result('Ruby On Rails', 'http://www.rubyonrails.com')
linked2 = generate_result('www.rubyonrails.com', 'http://www.rubyonrails.com')
linked2 = %('<a href="http://www.example.com">www.example.com</a>')
linked3 = %('<a href="http://www.example.com" rel="nofollow">www.example.com</a>')
linked4 = %('<a href="http://www.example.com"><b>www.example.com</b></a>')
linked5 = %('<a href="#close">close</a> <a href="http://www.example.com"><b>www.example.com</b></a>')
assert_equal linked1, auto_link(linked1)
assert_equal linked2, auto_link(linked2)
assert_equal linked3, auto_link(linked3)
assert_equal linked4, auto_link(linked4)
assert_equal linked5, auto_link(linked5)
linked_email = %Q(<a href="mailto:david@loudthinking.com">Mail me</a>)
assert_equal linked_email, auto_link(linked_email)
end
def test_auto_link_within_tags
link_raw = 'http://www.rubyonrails.org/images/rails.png'
link_result = %Q(<img src="#{link_raw}" />)
assert_equal link_result, auto_link(link_result)
end
def test_auto_link_with_brackets
@@ -405,12 +420,6 @@ class TextHelperTest < ActionView::TestCase
assert_equal "{link: #{link3_result}}", auto_link("{link: #{link3_raw}}")
end
def test_auto_link_in_tags
link_raw = 'http://www.rubyonrails.org/images/rails.png'
link_result = %Q(<img src="#{link_raw}" />)
assert_equal link_result, auto_link(link_result)
end
def test_auto_link_at_eol
url1 = "http://api.rubyonrails.com/Foo.html"
url2 = "http://www.ruby-doc.org/core/Bar.html"
@@ -424,12 +433,32 @@ class TextHelperTest < ActionView::TestCase
assert_equal %(<p><a href="#{url}">#{url[0...7]}...</a><br /><a href="mailto:#{email}">#{email[0...7]}...</a><br /></p>), auto_link("<p>#{url}<br />#{email}<br /></p>") { |url| truncate(url, :length => 10) }
end
def test_auto_link_with_block_with_html
pic = "http://example.com/pic.png"
url = "http://example.com/album?a&b=c"
assert_equal %(My pic: <a href="#{pic}"><img src="#{pic}" width="160px"></a> -- full album here #{generate_result(url)}), auto_link("My pic: #{pic} -- full album here #{url}") { |link|
if link =~ /\.(jpg|gif|png|bmp|tif)$/i
raw %(<img src="#{link}" width="160px">)
else
link
end
}
end
def test_auto_link_with_options_hash
assert_dom_equal 'Welcome to my new blog at <a href="http://www.myblog.com/" class="menu" target="_blank">http://www.myblog.com/</a>. Please e-mail me at <a href="mailto:me@email.com" class="menu" target="_blank">me@email.com</a>.',
auto_link("Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com.",
:link => :all, :html => { :class => "menu", :target => "_blank" })
end
def test_auto_link_with_multiple_trailing_punctuations
url = "http://youtube.com"
url_result = generate_result(url)
assert_equal url_result, auto_link(url)
assert_equal "(link: #{url_result}).", auto_link("(link: #{url}).")
end
def test_cycle_class
value = Cycle.new("one", 2, "3")

View File

@@ -1,33 +1,50 @@
require 'abstract_unit'
class TranslationHelperTest < Test::Unit::TestCase
class TranslationHelperTest < ActiveSupport::TestCase
include ActionView::Helpers::TagHelper
include ActionView::Helpers::TranslationHelper
attr_reader :request
def setup
end
def test_delegates_to_i18n_setting_the_raise_option
I18n.expects(:translate).with(:foo, :locale => 'en', :raise => true).returns("")
I18n.expects(:translate).with(['foo'], :locale => 'en', :raise => true).returns([""])
translate :foo, :locale => 'en'
end
def test_returns_missing_translation_message_wrapped_into_span
expected = '<span class="translation_missing">en, foo</span>'
assert_equal expected, translate(:foo)
end
def test_translation_returning_an_array
I18n.expects(:translate).with(["foo"], :raise => true).returns(["foo", "bar"])
assert_equal ["foo", "bar"], translate(:foo)
end
def test_translation_of_an_array
I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(["foo", "bar"])
assert_equal "foobar", translate(["foo", "bar"])
assert_deprecated do
I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(["foo", "bar"])
assert_equal ["foo", "bar"], translate(["foo", "bar"])
end
end
def test_translation_of_an_array_returning_an_array
assert_deprecated do
I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(["foo", ["bar", "baz"]])
assert_equal ["foo", ["bar", "baz"]], translate(["foo", "bar"])
end
end
def test_translation_of_an_array_with_html
expected = '<a href="#">foo</a><a href="#">bar</a>'
I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(['<a href="#">foo</a>', '<a href="#">bar</a>'])
@view = ActionView::Base.new(ActionController::Base.view_paths, {})
assert_equal expected, @view.render(:file => "test/array_translation")
assert_deprecated do
translate_expected = ['<a href="#">foo</a>', '<a href="#">bar</a>', '<a href="#">baz</a>']
I18n.expects(:translate).with(["foo", "bar", "baz_html"], :raise => true).returns(translate_expected)
@view = ActionView::Base.new(ActionController::Base.view_paths, {})
expected = '<a href="#">foo</a>, <a href="#">bar</a>, <a href="#">baz</a>'
assert_equal expected, @view.render(:file => "test/array_translation")
end
end
def test_delegates_localize_to_i18n
@@ -35,16 +52,44 @@ class TranslationHelperTest < Test::Unit::TestCase
I18n.expects(:localize).with(@time)
localize @time
end
def test_scoping_by_partial
expects(:template).returns(stub(:path_without_format_and_extension => "people/index"))
I18n.expects(:translate).with("people.index.foo", :locale => 'en', :raise => true).returns("")
translate ".foo", :locale => 'en'
I18n.expects(:translate).with(["test.translation.helper"], :raise => true).returns(["helper"])
@view = ActionView::Base.new(ActionController::Base.view_paths, {})
assert_equal "helper", @view.render(:file => "test/translation")
end
def test_scoping_by_partial_of_an_array
I18n.expects(:translate).with("test.scoped_array_translation.foo.bar", :raise => true).returns(["foo", "bar"])
@view = ActionView::Base.new(ActionController::Base.view_paths, {})
assert_equal "foobar", @view.render(:file => "test/scoped_array_translation")
assert_deprecated do
I18n.expects(:translate).with(["test.scoped_array_translation.foo", "test.scoped_array_translation.bar"], :raise => true).returns(["foo", "bar"])
@view = ActionView::Base.new(ActionController::Base.view_paths, {})
assert_equal "foo, bar", @view.render(:file => "test/scoped_array_translation")
end
end
def test_translate_works_with_symbols
I18n.expects(:translate).with(["hello"], :raise => true).returns(["Hello World"])
assert_equal "Hello World", translate(:hello)
end
def test_translate_does_not_mark_plain_text_as_safe_html
I18n.expects(:translate).with(["hello"], :raise => true).returns(["Hello World"])
assert_equal false, translate("hello").html_safe?
end
def test_translate_marks_translations_named_html_as_safe_html
I18n.expects(:translate).with(["html"], :raise => true).returns(["<a>Hello World</a>"])
assert translate("html").html_safe?
end
def test_translate_marks_translations_with_a_html_suffix_as_safe_html
I18n.expects(:translate).with(["hello_html"], :raise => true).returns(["<a>Hello World</a>"])
assert translate("hello_html").html_safe?
end
def test_translation_returning_an_array_ignores_html_suffix
I18n.expects(:translate).with(["foo_html"], :raise => true).returns(["foo", "bar"])
assert_equal ["foo", "bar"], translate(:foo_html)
end
end

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

@@ -159,7 +159,7 @@ module ActionView
class AssertionsTest < ActionView::TestCase
def render_from_helper
form_tag('/foo') do
concat render(:text => '<ul><li>foo</li></ul>')
concat render(:text => '<ul><li>foo</li></ul>'.html_safe)
end
end
helper_method :render_from_helper
@@ -173,4 +173,4 @@ module ActionView
end
end
end
end
end

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

@@ -7,7 +7,7 @@ require 'mocha'
require 'active_model'
require 'active_model/state_machine'
$:.unshift File.dirname(__FILE__) + "/../../activesupport/lib"
$:.unshift File.expand_path('../../../activesupport/lib', __FILE__)
require 'active_support'
require 'active_support/test_case'

View File

@@ -1,3 +1,16 @@
*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)*
* Version bump.
*2.3.6 (May 23, 2010)*
* Add index length support for MySQL. #1852 [Emili Parreno, Pratik Naik]

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.6' + 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

@@ -12,6 +12,8 @@ require 'rbench'
__DIR__ = File.dirname(__FILE__)
$:.unshift "#{__DIR__}/../lib"
$:.unshift "#{__DIR__}/../../activesupport/lib"
require 'active_record'
conn = { :adapter => 'mysql',
@@ -25,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)
@@ -58,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...'
@@ -88,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
@@ -155,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

@@ -282,7 +282,11 @@ module ActiveRecord
end
through_records.flatten!
else
records.first.class.preload_associations(records, through_association)
options = {}
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)
through_records = records.map {|record| record.send(through_association)}.flatten
end
through_records.compact!
@@ -357,7 +361,13 @@ module ActiveRecord
table_name = reflection.klass.quoted_table_name
if interface = reflection.options[:as]
conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} #{in_or_equals_for_ids(ids)} and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{self.base_class.sti_name}'"
parent_type = if reflection.active_record.abstract_class?
self.base_class.sti_name
else
reflection.active_record.sti_name
end
conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} #{in_or_equals_for_ids(ids)} and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{parent_type}'"
else
foreign_key = reflection.primary_key_name
conditions = "#{reflection.klass.quoted_table_name}.#{foreign_key} #{in_or_equals_for_ids(ids)}"

View File

@@ -1782,7 +1782,7 @@ module ActiveRecord
end
def using_limitable_reflections?(reflections)
reflections.collect(&:collection?).length.zero?
reflections.none?(&:collection?)
end
def column_aliases(join_dependency)

View File

@@ -234,8 +234,9 @@ module ActiveRecord
# See destroy for more info.
def destroy_all
load_target
destroy(@target)
reset_target!
destroy(@target).tap do
reset_target!
end
end
def create(attrs = {})
@@ -331,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)
@@ -349,7 +351,16 @@ module ActiveRecord
begin
if !loaded?
if @target.is_a?(Array) && @target.any?
@target = find_target + @target.find_all {|t| t.new_record? }
@target = find_target.map do |f|
i = @target.index(f)
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.find_all {|t| t.new_record?}
else
@target = find_target
end
@@ -363,7 +374,19 @@ 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
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 }, &block)
end
if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
if block_given?
super { |*block_args| yield(*block_args) }
@@ -418,6 +441,25 @@ module ActiveRecord
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
@@ -462,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"
@@ -474,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

@@ -230,10 +230,6 @@ module ActiveRecord
# It's also possible to instantiate related objects, so a Client class belonging to the clients
# table with a +master_id+ foreign key can instantiate master through Client#master.
def method_missing(method_id, *args, &block)
if method_id == :to_ary || method_id == :to_str
raise NoMethodError, "undefined method `#{method_id}' for #{inspect}:#{self.class}"
end
method_name = method_id.to_s
if self.class.private_method_defined?(method_name)

View File

@@ -146,12 +146,12 @@ module ActiveRecord
# add_autosave_association_callbacks(reflect_on_association(name))
# end
ASSOCIATION_TYPES.each do |type|
module_eval %{
module_eval <<-CODE, __FILE__, __LINE__ + 1
def #{type}(name, options = {})
super
add_autosave_association_callbacks(reflect_on_association(name))
end
}
CODE
end
# Adds a validate and save callback for the association as specified by

View File

@@ -1,5 +1,6 @@
require 'yaml'
require 'set'
require 'active_support/core_ext/class/attribute'
module ActiveRecord #:nodoc:
# Generic Active Record exception class.
@@ -515,7 +516,7 @@ module ActiveRecord #:nodoc:
@@timestamped_migrations = true
# Determine whether to store the full constant name including namespace when using STI
superclass_delegating_accessor :store_full_sti_class
class_attribute :store_full_sti_class
self.store_full_sti_class = false
# Stores the default scope for the class
@@ -935,11 +936,18 @@ module ActiveRecord #:nodoc:
def reset_counters(id, *counters)
object = find(id)
counters.each do |association|
child_class = reflect_on_association(association).klass
counter_name = child_class.reflect_on_association(self.name.downcase.to_sym).counter_cache_column
child_class = reflect_on_association(association.to_sym).klass
belongs_name = self.name.demodulize.underscore.to_sym
counter_name = child_class.reflect_on_association(belongs_name).counter_cache_column
value = object.send(association).count
connection.update("UPDATE #{quoted_table_name} SET #{connection.quote_column_name(counter_name)} = #{object.send(association).count} WHERE #{connection.quote_column_name(primary_key)} = #{quote_value(object.id)}", "#{name} UPDATE")
connection.update(<<-CMD, "#{name} UPDATE")
UPDATE #{quoted_table_name}
SET #{connection.quote_column_name(counter_name)} = #{value}
WHERE #{connection.quote_column_name(primary_key)} = #{quote_value(object.id)}
CMD
end
return true
end
# A generic "counter updater" implementation, intended primarily to be
@@ -972,19 +980,13 @@ module ActiveRecord #:nodoc:
# # SET comment_count = comment_count + 1,
# # WHERE id IN (10, 15)
def update_counters(id, counters)
updates = counters.inject([]) { |list, (counter_name, increment)|
sign = increment < 0 ? "-" : "+"
list << "#{connection.quote_column_name(counter_name)} = COALESCE(#{connection.quote_column_name(counter_name)}, 0) #{sign} #{increment.abs}"
}.join(", ")
if id.is_a?(Array)
ids_list = id.map {|i| quote_value(i)}.join(', ')
condition = "IN (#{ids_list})"
else
condition = "= #{quote_value(id)}"
updates = counters.map do |counter_name, value|
operator = value < 0 ? '-' : '+'
quoted_column = connection.quote_column_name(counter_name)
"#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
end
update_all(updates, "#{connection.quote_column_name(primary_key)} #{condition}")
update_all(updates.join(', '), primary_key => id )
end
# Increment a number field by one, usually representing a count.
@@ -1284,6 +1286,8 @@ 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 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
class_name = class_name.singularize if pluralize_table_names
@@ -2642,7 +2646,7 @@ module ActiveRecord #:nodoc:
# Note: The new instance will share a link to the same attributes as the original class. So any change to the attributes in either
# instance will affect the other.
def becomes(klass)
returning klass.new do |became|
klass.new.tap do |became|
became.instance_variable_set("@attributes", @attributes)
became.instance_variable_set("@attributes_cache", @attributes_cache)
became.instance_variable_set("@new_record", new_record?)
@@ -2660,12 +2664,20 @@ module ActiveRecord #:nodoc:
# Updates all the attributes from the passed-in Hash and saves the record. If the object is invalid, the saving will
# fail and false will be returned.
def update_attributes(attributes)
with_transaction_returning_status(:update_attributes_inside_transaction, attributes)
end
def update_attributes_inside_transaction(attributes) #:nodoc:
self.attributes = attributes
save
end
# Updates an object just like Base.update_attributes but calls save! instead of save so an exception is raised if the record is invalid.
def update_attributes!(attributes)
with_transaction_returning_status(:update_attributes_inside_transaction!, attributes)
end
def update_attributes_inside_transaction!(attributes) #:nodoc:
self.attributes = attributes
save!
end

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

@@ -10,7 +10,7 @@ module ActiveRecord
##
# :singleton-method:
# The connection handler
superclass_delegating_accessor :connection_handler
class_attribute :connection_handler
self.connection_handler = ConnectionAdapters::ConnectionHandler.new
# Returns the connection currently associated with the class. This can

View File

@@ -195,6 +195,7 @@ module ActiveRecord
# remove_column(:suppliers, :qualification)
# remove_columns(:suppliers, :qualification, :experience)
def remove_column(table_name, *column_names)
raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
column_names.flatten.each do |column_name|
execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
end
@@ -273,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
@@ -346,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

@@ -211,6 +211,12 @@ module ActiveRecord
log_info(sql, name, 0)
nil
end
rescue SystemExit, SignalException, NoMemoryError => e
# Don't re-wrap these exceptions. They are probably not being caused by invalid
# sql, but rather some external stimulus beyond the responsibilty of this code.
# Additionaly, wrapping these exceptions with StatementInvalid would lead to
# meaningful loss of data, such as losing SystemExit#status.
raise e
rescue Exception => e
# Log message and raise exception.
# Set last_verification to 0, so that connection gets verified

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:
@@ -315,6 +315,7 @@ module ActiveRecord
rows = []
result.each { |row| rows << row }
result.free
@connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
rows
end
@@ -638,6 +639,7 @@ module ActiveRecord
result = execute(sql, name)
rows = result.all_hashes
result.free
@connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
rows
end

View File

@@ -25,7 +25,7 @@ module ActiveRecord
module ConnectionAdapters #:nodoc:
class SQLite3Adapter < SQLiteAdapter # :nodoc:
def table_structure(table_name)
returning structure = @connection.table_info(quote_table_name(table_name)) do
@connection.table_info(quote_table_name(table_name)).tap do |structure|
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
end
end

View File

@@ -162,7 +162,7 @@ module ActiveRecord
end
def quote_column_name(name) #:nodoc:
%Q("#{name}")
%Q("#{name.to_s.gsub('"', '""')}")
end
@@ -220,20 +220,20 @@ module ActiveRecord
SQL
execute(sql, name).map do |row|
row[0]
row['name']
end
end
def columns(table_name, name = nil) #:nodoc:
table_structure(table_name).map do |field|
SQLiteColumn.new(field['name'], field['dflt_value'], field['type'], field['notnull'] == "0")
SQLiteColumn.new(field['name'], field['dflt_value'], field['type'], field['notnull'].to_i == 0)
end
end
def indexes(table_name, name = nil) #:nodoc:
execute("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row|
index = IndexDefinition.new(table_name, row['name'])
index.unique = row['unique'] != '0'
index.unique = row['unique'].to_i != 0
index.columns = execute("PRAGMA index_info('#{index.name}')").map { |col| col['name'] }
index
end
@@ -269,6 +269,7 @@ module ActiveRecord
end
def remove_column(table_name, *column_names) #:nodoc:
raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
column_names.flatten.each do |column_name|
alter_table(table_name) do |definition|
definition.columns.delete(definition[column_name])
@@ -329,7 +330,7 @@ module ActiveRecord
end
def table_structure(table_name)
returning structure = execute("PRAGMA table_info(#{quote_table_name(table_name)})") do
execute("PRAGMA table_info(#{quote_table_name(table_name)})").tap do |structure|
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
end
end

View File

@@ -44,7 +44,7 @@ module ActiveRecord
base.alias_method_chain :update, :dirty
base.alias_method_chain :reload, :dirty
base.superclass_delegating_accessor :partial_updates
base.class_attribute :partial_updates
base.partial_updates = true
base.send(:extend, ClassMethods)

View File

@@ -11,23 +11,23 @@ en:
accepted: "must be accepted"
empty: "can't be empty"
blank: "can't be blank"
too_long: "is too long (maximum is {{count}} characters)"
too_short: "is too short (minimum is {{count}} characters)"
wrong_length: "is the wrong length (should be {{count}} characters)"
too_long: "is too long (maximum is %{count} characters)"
too_short: "is too short (minimum is %{count} characters)"
wrong_length: "is the wrong length (should be %{count} characters)"
taken: "has already been taken"
not_a_number: "is not a number"
greater_than: "must be greater than {{count}}"
greater_than_or_equal_to: "must be greater than or equal to {{count}}"
equal_to: "must be equal to {{count}}"
less_than: "must be less than {{count}}"
less_than_or_equal_to: "must be less than or equal to {{count}}"
greater_than: "must be greater than %{count}"
greater_than_or_equal_to: "must be greater than or equal to %{count}"
equal_to: "must be equal to %{count}"
less_than: "must be less than %{count}"
less_than_or_equal_to: "must be less than or equal to %{count}"
odd: "must be odd"
even: "must be even"
record_invalid: "Validation failed: {{errors}}"
record_invalid: "Validation failed: %{errors}"
# Append your own errors here or at the model/attributes scope.
full_messages:
format: "{{attribute}} {{message}}"
format: "%{attribute} %{message}"
# You can define own errors for models or model attributes.
# The values :model, :attribute and :value are always available for interpolation.
@@ -35,7 +35,7 @@ en:
# For example,
# models:
# user:
# blank: "This is a custom blank message for {{model}}: {{attribute}}"
# blank: "This is a custom blank message for %{model}: %{attribute}"
# attributes:
# login:
# blank: "This is a custom blank message for User login"

View File

@@ -128,6 +128,7 @@ module ActiveRecord
end
end
@destroyed = true
freeze
end

View File

@@ -516,7 +516,7 @@ module ActiveRecord
raise DuplicateMigrationNameError.new(name.camelize)
end
klasses << returning(MigrationProxy.new) do |migration|
klasses << (MigrationProxy.new).tap do |migration|
migration.name = name.camelize
migration.version = version
migration.filename = file

View File

@@ -8,10 +8,7 @@ module ActiveRecord
#
# You can define a scope that applies to all finders using ActiveRecord::Base.default_scope.
def self.included(base)
base.class_eval do
extend ClassMethods
named_scope :scoped, lambda { |scope| scope }
end
base.extend ClassMethods
end
module ClassMethods
@@ -19,6 +16,10 @@ module ActiveRecord
read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
end
def scoped(scope, &block)
Scope.new(self, scope, &block)
end
# Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query,
# such as <tt>:conditions => {:color => :red}, :select => 'shirts.*', :include => :washing_instructions</tt>.
#
@@ -84,18 +85,22 @@ module ActiveRecord
# assert_equal expected_options, Shirt.colored('red').proxy_options
def named_scope(name, options = {}, &block)
name = name.to_sym
scopes[name] = lambda do |parent_scope, *args|
Scope.new(parent_scope, case options
when Hash
options
when Proc
options.call(*args)
if self.model_name != parent_scope.model_name
options.bind(parent_scope).call(*args)
else
options.call(*args)
end
end, &block)
end
(class << self; self end).instance_eval do
define_method name do |*args|
scopes[name].call(self, *args)
end
singleton_class.send :define_method, name do |*args|
scopes[name].call(self, *args)
end
end
end
@@ -115,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

@@ -74,7 +74,7 @@ module ActiveRecord #:nodoc:
end
def serializable_record
returning(serializable_record = {}) do
{}.tap do |serializable_record|
serializable_names.each { |name| serializable_record[name] = @record.send(name) }
add_includes do |association, records, opts|
if records.is_a?(Enumerable)

View File

@@ -184,7 +184,7 @@ module ActiveRecord
# Look up a session by id and unmarshal its data if found.
def find_by_session_id(session_id)
if record = @@connection.select_one("SELECT * FROM #{@@table_name} WHERE #{@@session_id_column}=#{@@connection.quote(session_id)}")
if record = connection.select_one("SELECT * FROM #{@@table_name} WHERE #{@@session_id_column}=#{connection.quote(session_id)}")
new(:session_id => session_id, :marshaled_data => record['data'])
end
end
@@ -310,6 +310,14 @@ module ActiveRecord
return true
end
def destroy(env)
if sid = current_session_id(env)
Base.silence do
get_session_model(env, sid).destroy
end
end
end
def get_session_model(env, sid)
if env[ENV_SESSION_OPTIONS_KEY][:id].nil?
env[SESSION_RECORD_KEY] = find_session(sid)

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